-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.ts
124 lines (117 loc) · 2.99 KB
/
plugin.ts
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { NodePath, PluginObj, types } from '@babel/core';
import generate from '@babel/generator';
import {
ArrowFunctionExpression,
BlockStatement,
FunctionDeclaration,
FunctionExpression,
SwitchCase,
VariableDeclarator,
} from '@babel/types';
import { highlight } from 'cli-highlight';
type BabelTypes = typeof types;
interface PluginOptions {
opts: {
localPath: string;
unusedRanges: { start: number; end: number }[];
};
}
export default function ({
types: t,
}: {
types: BabelTypes;
}): PluginObj<PluginOptions> {
return {
name: 'babel-plugin-doei',
visitor: {
BlockStatement(path, state) {
if (
isUnused(path, state.opts.unusedRanges) &&
!path.findParent((p) => p.isFor())
) {
logNode(path, state.opts.localPath);
path.replaceWith(emptyBody(t));
}
},
ArrowFunctionExpression(path, state) {
if (isUnused(path, state.opts.unusedRanges)) {
logNode(path, state.opts.localPath);
path.node.params = [];
path.node.body = emptyBody(t);
}
},
FunctionExpression(path, state) {
if (isUnused(path, state.opts.unusedRanges)) {
logNode(path, state.opts.localPath);
path.node.params = [];
path.node.body = emptyBody(t);
}
},
FunctionDeclaration(path, state) {
if (isUnused(path, state.opts.unusedRanges)) {
logNode(path, state.opts.localPath);
path.node.params = [];
path.node.body = emptyBody(t);
}
},
SwitchCase(path, state) {
if (isUnused(path, state.opts.unusedRanges)) {
logNode(path, state.opts.localPath);
path.remove();
}
},
Program: {
exit() {},
},
},
};
}
function emptyBody(t: BabelTypes): types.BlockStatement {
return t.blockStatement([
t.expressionStatement(
t.callExpression(
t.memberExpression(t.identifier('console'), t.identifier('log'), false),
[t.stringLiteral('"unused" code called')],
),
),
]);
}
function isUnused(
path: NodePath<
| ArrowFunctionExpression
| BlockStatement
| FunctionDeclaration
| FunctionExpression
| SwitchCase
| VariableDeclarator
>,
unusedRanges: PluginOptions['opts']['unusedRanges'],
): boolean {
const { start, end } = path.node;
return (
typeof start === 'number' &&
typeof end === 'number' &&
unusedRanges.some((range) => start >= range.start && end <= range.end)
);
}
function logCode(code: string): void {
console.log(
highlight(code, { language: 'javascript', ignoreIllegals: true }),
);
}
function logNode(path: NodePath<any>, localPath: string): void {
const { node, type } = path;
const { start, end } = node;
const name = (node as any).id?.name;
const bytes = end - start;
console.log(
'Removed %s byte %s "%s" at %s-%s in %s',
bytes,
type,
name,
start,
end,
localPath,
);
logCode(generate(node).code);
}