@samitouri / QOS-React-2 / commits / 8790324562

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