【工具】检测无用代码

对给定 JavaScript 代码的静态分析,找出其中未被使用的函数和变量,并输出到控制台。

相关文章

检测无用代码


功能清单

  • 代码解析: 使用 Babel 的解析器 @babel/parser 将输入的 JavaScript 代码字符串解析为抽象语法树(AST),以便后续分析。
  • 函数和变量定义收集: 在 AST 中遍历,收集代码中定义的函数和变量名称,并将它们分别存储在集合中。
  • 函数调用和变量引用收集: 继续遍历 AST,收集代码中使用的函数和变量名称,并将它们分别存储在集合中。
  • 未被使用的函数和变量查找: 通过比较定义的函数和变量集合与使用的函数和变量集合,找出未被使用的函数和变量。
  • 结果输出: 将找出的未被使用的函数和变量输出到控制台,以供开发者查看和分析。

一、解析代码为 AST(抽象语法树)

使用适当的工具(如 Babel、@babel/parser、@vue/parser 等)将代码解析为 AST。这样可以方便地分析代码的结构和关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const { parse } = require('@babel/parser'); // 导入从字符串解析代码并生成 AST 的函数 parse

// 示例代码
const code = `export function a(x) {
return x * x;
}
function b(x) {
return x * x * x;
}

function c(x) {
return x * x * x;
}

export const d = 3.14;
let e = 5;
var f = 5;

b(e);
`;

// 解析代码为 AST
const ast = parse(code, {
sourceType: 'module', // 设置源代码类型为模块
});

二、收集函数和变量的定义

遍历 AST,收集函数和变量的定义。这可以通过查找函数声明、变量声明等节点来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 收集函数和变量定义
const definedFunctions = new Set(); // 用于存储定义的函数名称的集合
const definedVariables = new Set(); // 用于存储定义的变量名称的集合

// 遍历 AST,收集函数和变量的定义
traverse(ast, {
FunctionDeclaration(path) { // 处理函数声明节点
definedFunctions.add(path.node.id.name); // 将函数名添加到已定义函数的集合中
},
VariableDeclarator(path) { // 处理变量声明节点
definedVariables.add(path.node.id.name); // 将变量名添加到已定义变量的集合中
},
});

三、收集函数调用和变量引用

继续遍历 AST,收集函数调用和变量引用。这可以通过查找函数调用、变量引用等节点来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 收集函数调用和变量引用
const usedFunctions = new Set(); // 用于存储使用的函数名称的集合
const usedVariables = new Set(); // 用于存储使用的变量名称的集合

// 遍历 AST,收集函数调用和变量引用
traverse(ast, {
CallExpression(path) { // 处理函数调用节点
if (path.node.callee.type === 'Identifier') { // 如果调用者是标识符类型
usedFunctions.add(path.node.callee.name); // 将函数名添加到已使用函数的集合中
}
},
Identifier(path) { // 处理标识符节点(变量引用)
const parent = path.findParent(parentPath => { // 查找父节点
return parentPath.isVariableDeclarator() || parentPath.isFunctionDeclaration(); // 判断父节点是否是变量声明或函数声明
});
if (!parent) { // 如果父节点不是变量声明或函数声明
usedVariables.add(path.node.name); // 将变量名添加到已使用变量的集合中
}
},
});

四、分析使用情况

根据收集到的函数和变量的定义,以及它们的调用和引用情况,分析代码中哪些函数和变量是被使用的,哪些是未被使用的。

1
2
3
4
5
6
// 找出未被使用的函数和变量
const unusedFunctions = Array.from(definedFunctions).filter(name => !usedFunctions.has(name)); // 找出未被使用的函数
const unusedVariables = Array.from(definedVariables).filter(name => !usedVariables.has(name)); // 找出未被使用的变量

console.log('Unused functions:', unusedFunctions); // 输出未被使用的函数
console.log('Unused variables:', unusedVariables); // 输出未被使用的变量

五、完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const { parse } = require('@babel/parser'); // 导入从字符串解析代码并生成 AST 的函数 parse
const traverse = require('@babel/traverse').default; // 导入用于遍历 AST 的函数 traverse

const code = `export function a(x) {
return x * x;
}
function b(x) {
return x * x * x;
}

function c(x) {
return x * x * x;
}

export const d = 3.14;
let e = 5;
var f = 5;

b(e);
`;

// 解析代码为 AST
const ast = parse(code, {
sourceType: 'module', // 设置源代码类型为模块
plugins: ['jsx'], // 使用 jsx 插件
});

// 收集函数和变量定义
const definedFunctions = new Set(); // 用于存储定义的函数名称的集合
const definedVariables = new Set(); // 用于存储定义的变量名称的集合

// 遍历 AST,收集函数和变量的定义
traverse(ast, {
FunctionDeclaration(path) { // 处理函数声明节点
definedFunctions.add(path.node.id.name); // 将函数名添加到已定义函数的集合中
},
VariableDeclarator(path) { // 处理变量声明节点
definedVariables.add(path.node.id.name); // 将变量名添加到已定义变量的集合中
},
});

// 收集函数调用和变量引用
const usedFunctions = new Set(); // 用于存储使用的函数名称的集合
const usedVariables = new Set(); // 用于存储使用的变量名称的集合

// 遍历 AST,收集函数调用和变量引用
traverse(ast, {
CallExpression(path) { // 处理函数调用节点
if (path.node.callee.type === 'Identifier') { // 如果调用者是标识符类型
usedFunctions.add(path.node.callee.name); // 将函数名添加到已使用函数的集合中
}
},
Identifier(path) { // 处理标识符节点(变量引用)
const parent = path.findParent(parentPath => { // 查找父节点
return parentPath.isVariableDeclarator() || parentPath.isFunctionDeclaration(); // 判断父节点是否是变量声明或函数声明
});
if (!parent) { // 如果父节点不是变量声明或函数声明
usedVariables.add(path.node.name); // 将变量名添加到已使用变量的集合中
}
},
});

// 找出未被使用的函数和变量
const unusedFunctions = Array.from(definedFunctions).filter(name => !usedFunctions.has(name)); // 找出未被使用的函数
const unusedVariables = Array.from(definedVariables).filter(name => !usedVariables.has(name)); // 找出未被使用的变量

console.log('Unused functions:', unusedFunctions); // 输出未被使用的函数
console.log('Unused variables:', unusedVariables); // 输出未被使用的变量

输出结果

1
2
Unused functions: [ 'a', 'c' ]
Unused variables: [ 'd', 'f' ]

喜欢这篇文章?打赏一下支持一下作者吧!
【工具】检测无用代码
https://www.cccccl.com/20240201/工具/检测无用代码/
作者
Jeffrey
发布于
2024年2月1日
许可协议