-
Notifications
You must be signed in to change notification settings - Fork 425
Expand file tree
/
Copy pathremove-unused-variables.ts
More file actions
70 lines (65 loc) · 2.05 KB
/
Copy pathremove-unused-variables.ts
File metadata and controls
70 lines (65 loc) · 2.05 KB
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
69
70
import type * as babel from "@babel/core";
import * as t from "@babel/types";
import { isPathValid } from "./paths.ts";
function isInvalidForRemoval(path: babel.NodePath) {
if (isPathValid(path, t.isCatchClause)) {
// This case is for `catch (error)` blocks
return true;
}
// This one is for destructured variables
let target = path;
if (isPathValid(path, t.isVariableDeclarator)) {
target = path.get('id');
}
return isPathValid(target, t.isObjectPattern) || isPathValid(target, t.isArrayPattern);
}
export function removeUnusedVariables(program: babel.NodePath<t.Program>) {
// TODO(Alexis):
// This implementation is simple but slow
// We repeat removing unused variables from each pass
// until no potential unused variables are left.
// There might be a simpler implementation.
let dirty = true;
while (dirty) {
dirty = false;
program.traverse({
BindingIdentifier(path) {
const binding = path.scope.getBinding(path.node.name);
if (binding) {
switch (binding.kind) {
case "const":
case "let":
case "var":
case "hoisted":
case "module":
if (binding.references === 0 && !binding.path.removed) {
const parent = binding.path.parentPath;
if (isPathValid(parent, t.isImportDeclaration)) {
if (parent.node.specifiers.length === 1) {
parent.remove();
} else {
binding.path.remove();
}
dirty = true;
} else if (!(isInvalidForRemoval(binding.path))) {
binding.path.remove();
dirty = true;
}
}
break;
case "local":
case "param":
case "unknown":
break;
}
}
},
VariableDeclaration(path) {
if (path.node.declarations.length === 0) {
path.remove();
}
},
});
program.scope.crawl();
}
}