-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbabel_v1_2.js
198 lines (167 loc) · 7.17 KB
/
babel_v1_2.js
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// ChatGPT Refs:
// https://chat.openai.com/c/e65996fc-6607-4209-a082-6bc086c4f043
// https://chat.openai.com/c/b8596908-5e17-4aac-941b-aa95962de9c2
// https://chat.openai.com/c/4fc379b2-2760-43ef-9b1b-9b0d2e25768b
// https://chat.openai.com/c/174c4c91-8a4f-4acf-b605-59f22ce03bad
// Refs:
// - https://babeljs.io/
// - https://babeljs.io/docs/babel-parser
// - > The Babel parser (previously Babylon) is a JavaScript parser used in Babel
// - > Heavily based on `acorn` and `acorn-jsx`
// - https://babeljs.io/docs/babel-parser#api
// - https://babeljs.io/docs/babel-parser#output
// - > The Babel parser generates AST according to Babel AST format. It is based on ESTree spec with the following deviations...
// - https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md
// - > AST for JSX code is based on Facebook JSX AST
// - https://github.com/facebook/jsx/blob/main/AST.md
// - https://babeljs.io/docs/babel-parser#plugins
// - https://babeljs.io/docs/babel-parser#language-extensions
// - > Language extensions
// - https://babeljs.io/docs/babel-parser#ecmascript-proposals
// - > ECMAScript proposals
// - https://babeljs.io/docs/babel-parser#latest-ecmascript-features
// - > The following features are already enabled on the latest version of `@babel/parser`, and cannot be disabled because they are part of the language. You should enable these features only if you are using an older version.
// - https://babeljs.io/docs/babel-traverse
// - > We can use it alongside the `babel` parser to traverse and update nodes
// - https://babeljs.io/docs/babel-types
// - https://babeljs.io/docs/babel-types#aliases
// - https://babeljs.io/docs/babel-types#scopable
// - > A cover of `FunctionParent` and `BlockParent`.
// - https://babeljs.io/docs/babel-types#functionparent
// - > A cover of AST nodes that start an execution context with new `VariableEnvironment`. In other words, they define the scope of `var` declarations. `FunctionParent` did not include `Program` since Babel 7.
// - https://babeljs.io/docs/babel-types#blockparent
// - > A cover of AST nodes that start an execution context with new `LexicalEnvironment`. In other words, they define the scope of `let` and `const` declarations.
const babel = require("@babel/core");
const generate = require("@babel/generator").default;
const code = `
function foo(a) {
var b = a + 1;
return b;
}
function bar(a) {
var c = a - 1;
return c;
}
`;
const ast = babel.parse(code);
let id = 0;
// -------------------------------------
// Only Processes Binding Renames Once
// -------------------------------------
// const processedBindings = new Set();
// babel.traverse(ast, {
// Scopable(path) {
// const bindings = path.scope.getAllBindings();
// Object.keys(bindings).forEach((name) => {
// const binding = bindings[name];
// // Skip bindings from parent scopes.
// if (binding.scope !== path.scope) {
// console.log(`Skipping bindings from different scope uid=${path.scope.uid} (${path.scope.path.type})`);
// return;
// }
// // Generate a unique key for each binding with its scope.
// const uniqueKey = `${name}_${path.scope.uid}`;
// // Skip bindings that have already been processed
// if (processedBindings.has(uniqueKey)) {
// console.log(`Skipping binding as we have already processed it name=${name} (${binding.scope.path.type})`);
// return;
// }
// console.group("Binding Details"); // Start a console group
// console.log("***************************");
// console.log("Old name:", name);
// console.log("Scope type:", path.type);
// console.log("Scope uid:", path.scope.uid);
// console.log("Reference count:", binding.referencePaths.length);
// const newName = "var" + id++;
// binding.identifier.name = newName;
// console.log("New name:", newName);
// console.log("***************************");
// console.log(
// `Renaming refPaths of ${name} (old) / ${newName} (new) from scope ${binding.scope.uid} (${binding.scope.path.type}):`
// );
// binding.referencePaths.forEach((refPath) => {
// console.log(`${refPath.node.name} in scope ${refPath.scope.uid}`);
// refPath.node.name = newName;
// });
// console.log("***************************");
// console.groupEnd(); // End the console group
// // Mark this binding as processed
// processedBindings.add(uniqueKey);
// });
// },
// });
// -------------------------------------
// WIP to rename more structurally
// -------------------------------------
const prefixCounts = new Map();
const processedBindings = new Set();
const getTypePrefix = (type) => {
switch (type) {
case 'Program':
return '';
case 'FunctionDeclaration':
return 'func';
case 'BlockStatement':
'';
case 'VariableDeclaration':
return 'var';
case 'Identifier':
return 'arg';
default:
return type;
}
};
function getPrefix(path) {
const parentPrefix = path.parentPath ? getPrefix(path.parentPath) : '';
const typePrefix = getTypePrefix(path.type);
const combinedPrefix = parentPrefix ? `${parentPrefix}_${typePrefix}` : typePrefix;
// const combinedPrefix = parentPrefix && typePrefix ? `${parentPrefix}_${typePrefix}` : `${parentPrefix}${typePrefix}`;
const count = (prefixCounts.get(combinedPrefix) || 0) + 1;
prefixCounts.set(combinedPrefix, count);
const combinedPrefixWithCount = combinedPrefix ? `${combinedPrefix}_${count}` : '';
// TODO: remove debug log
console.log(
'[DEBUG] getPrefix',
{ parentPrefix, typePrefix, combinedPrefix, combinedPrefixWithCount }
);
return combinedPrefixWithCount;
}
// TODO: make this capable of properly traversing all of the bindings/references like we used to be able to do.. as well as ensuring that we only process a scope/identifier once
// TODO: Can we just use the same Scopable implementation we did earlier + the new getPrefix logic?
babel.traverse(ast, {
FunctionDeclaration(path) {
const prefix = getPrefix(path);
path.node.id.name = prefix;
path.traverse({
Identifier(path) {
// TODO: I think this part is wrong based on our rules.. we don't want to use the existing node name at all.. we want to use the generated index name for it again..
if (path.node.name !== prefix && path.scope.hasBinding(path.node.name)) {
path.node.name = prefix + "_" + path.node.name;
}
},
FunctionDeclaration(path) {
path.skip();
}
});
},
VariableDeclaration(path) {
if (path.parentPath.type !== 'FunctionDeclaration') {
const prefix = getPrefix(path);
path.traverse({
Identifier(path) {
if (path.scope.hasBinding(path.node.name)) {
path.node.name = prefix + "_" + path.node.name;
}
}
});
}
},
});
const output = generate(ast, {}, code);
console.log('------------');
console.group('Before:');
console.log(code);
console.groupEnd();
console.group('After:');
console.log(output.code);
console.groupEnd();