Workaround serious Babel bug
I ran into the same issue that @poteto and @gsathya (and probably @mofeiZ) have run into: "Duplicate declaration of '$'" caused by Babel visiting a function twice despite our calling `skip()`. This PR keeps a set of nodes that we have already visited to avoid visiting them again, as a workaround for skip not working. # Test Plan Synced to www and confirmed that the previous bug no longer reproduces, and the compiled output looks sane.
Joe Savona committed
Aug 30, 2023 at 13:21 UTC
87903245629715e99a95e546acce9a20b89d5129
1 file changed
+19
-1
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts
+19
-1
@@ -65,6 +65,10 @@ function compileAndInsertNewFunctionDeclaration(
65
>,
66
pass: CompilerPass
67
): boolean {
68
+ if (ALREADY_COMPILED.has(fnPath.node)) {
69
+ return false;
70
+ }
71
+
72
let compiledFn: CodegenFunction | null = null;
73
let hasForgetMutatedOriginalSource = false;
74
try {
@@ -100,7 +104,10 @@ function compileAndInsertNewFunctionDeclaration(
104
// traversal will loop infinitely.
105
fnPath.skip();
106
103
- let transformedFunction;
107
+ let transformedFunction:
108
+ | t.FunctionDeclaration
109
+ | t.ArrowFunctionExpression
110
+ | t.FunctionExpression;
111
switch (fnPath.node.type) {
112
case "FunctionDeclaration": {
113
const fn: t.FunctionDeclaration = {
@@ -143,6 +150,12 @@ function compileAndInsertNewFunctionDeclaration(
150
}
151
}
152
153
+ // Ensure we avoid visiting the original function again (since we move it
154
+ // within the AST in gating mode)
155
+ ALREADY_COMPILED.add(fnPath);
156
+ // And avoid visiting the new version as well
157
+ ALREADY_COMPILED.add(transformedFunction);
158
+
159
insertNewFunctionDeclaration(fnPath, transformedFunction, pass);
160
hasForgetMutatedOriginalSource = true;
161
}
@@ -176,6 +189,11 @@ function insertNewFunctionDeclaration(
189
}
190
}
191
192
+// This is a hack to work around what seems to be a Babel bug. Babel doesn't
193
+// consistently respect the `skip()` function to avoid revisiting a node within
194
+// a pass, so we use this set to track nodes that we have compiled.
195
+const ALREADY_COMPILED: WeakSet<object> | Set<object> = new (WeakSet ?? Set)();
196
+
197
export function compileProgram(
198
program: NodePath<t.Program>,
199
pass: CompilerPass