Skip to content

Commit c61e75b

Browse files
authored
[compiler] Avoid failing builds when import specifiers conflict or shadow vars (facebook#32663)
Avoid failing builds when imported function specifiers conflict by using babel's `generateUid`. Failing a build is very disruptive, as it usually presents to developers similar to a javascript parse error. ```js import {logRender as _logRender} from 'instrument-runtime'; const logRender = () => { /* local conflicting implementation */ } function Component_optimized() { _logRender(); // inserted by compiler } ``` Currently, we fail builds (even in `panicThreshold:none` cases) when import specifiers are detected to conflict with existing local variables. The reason we destructively throw (instead of bailing out) is because (1) we first generate identifier references to the conflicting name in compiled functions, (2) replaced original functions with compiled functions, and then (3) finally check for conflicts. When we finally check for conflicts, it's too late to bail out. ```js // import {logRender} from 'instrument-runtime'; const logRender = () => { /* local conflicting implementation */ } function Component_optimized() { logRender(); // inserted by compiler } ```
1 parent 7c908bc commit c61e75b

File tree

61 files changed

+1013
-409
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1013
-409
lines changed

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77

88
import {NodePath} from '@babel/core';
99
import * as t from '@babel/types';
10-
import {PluginOptions} from './Options';
1110
import {CompilerError} from '../CompilerError';
11+
import {ProgramContext} from './Imports';
12+
import {ExternalFunction} from '..';
1213

1314
/**
1415
* Gating rewrite for function declarations which are referenced before their
@@ -34,7 +35,8 @@ import {CompilerError} from '../CompilerError';
3435
function insertAdditionalFunctionDeclaration(
3536
fnPath: NodePath<t.FunctionDeclaration>,
3637
compiled: t.FunctionDeclaration,
37-
gating: NonNullable<PluginOptions['gating']>,
38+
programContext: ProgramContext,
39+
gatingFunctionIdentifierName: string,
3840
): void {
3941
const originalFnName = fnPath.node.id;
4042
const originalFnParams = fnPath.node.params;
@@ -57,14 +59,14 @@ function insertAdditionalFunctionDeclaration(
5759
loc: fnPath.node.loc ?? null,
5860
});
5961

60-
const gatingCondition = fnPath.scope.generateUidIdentifier(
61-
`${gating.importSpecifierName}_result`,
62+
const gatingCondition = t.identifier(
63+
programContext.newUid(`${gatingFunctionIdentifierName}_result`),
6264
);
63-
const unoptimizedFnName = fnPath.scope.generateUidIdentifier(
64-
`${originalFnName.name}_unoptimized`,
65+
const unoptimizedFnName = t.identifier(
66+
programContext.newUid(`${originalFnName.name}_unoptimized`),
6567
);
66-
const optimizedFnName = fnPath.scope.generateUidIdentifier(
67-
`${originalFnName.name}_optimized`,
68+
const optimizedFnName = t.identifier(
69+
programContext.newUid(`${originalFnName.name}_optimized`),
6870
);
6971
/**
7072
* Step 1: rename existing functions
@@ -115,7 +117,7 @@ function insertAdditionalFunctionDeclaration(
115117
t.variableDeclaration('const', [
116118
t.variableDeclarator(
117119
gatingCondition,
118-
t.callExpression(t.identifier(gating.importSpecifierName), []),
120+
t.callExpression(t.identifier(gatingFunctionIdentifierName), []),
119121
),
120122
]),
121123
);
@@ -129,19 +131,26 @@ export function insertGatedFunctionDeclaration(
129131
| t.FunctionDeclaration
130132
| t.ArrowFunctionExpression
131133
| t.FunctionExpression,
132-
gating: NonNullable<PluginOptions['gating']>,
134+
programContext: ProgramContext,
135+
gating: ExternalFunction,
133136
referencedBeforeDeclaration: boolean,
134137
): void {
138+
const gatingImportedName = programContext.addImportSpecifier(gating).name;
135139
if (referencedBeforeDeclaration && fnPath.isFunctionDeclaration()) {
136140
CompilerError.invariant(compiled.type === 'FunctionDeclaration', {
137141
reason: 'Expected compiled node type to match input type',
138142
description: `Got ${compiled.type} but expected FunctionDeclaration`,
139143
loc: fnPath.node.loc ?? null,
140144
});
141-
insertAdditionalFunctionDeclaration(fnPath, compiled, gating);
145+
insertAdditionalFunctionDeclaration(
146+
fnPath,
147+
compiled,
148+
programContext,
149+
gatingImportedName,
150+
);
142151
} else {
143152
const gatingExpression = t.conditionalExpression(
144-
t.callExpression(t.identifier(gating.importSpecifierName), []),
153+
t.callExpression(t.identifier(gatingImportedName), []),
145154
buildFunctionExpression(compiled),
146155
buildFunctionExpression(fnPath.node),
147156
);

0 commit comments

Comments
 (0)