Skip to content

Commit 5069e18

Browse files
authored
[compiler][be] Make program traversal more readable (facebook#33147)
React Compiler's program traversal logic is pretty lengthy and complex as we've added a lot of features piecemeal. `compileProgram` is 300+ lines long and has confusing control flow (defining helpers inline, invoking visitors, mutating-asts-while-iterating, mutating global `ALREADY_COMPILED` state). - Moved more stuff to `ProgramContext` - Separated `compileProgram` into a bunch of helpers Tested by syncing this stack to a Meta codebase and observing no compilation output changes (D74487851, P1806855669, P1806855379) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33147). * facebook#33149 * facebook#33148 * __->__ facebook#33147
1 parent 21fdf30 commit 5069e18

File tree

9 files changed

+521
-250
lines changed

9 files changed

+521
-250
lines changed

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

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import {
1818
import {getOrInsertWith} from '../Utils/utils';
1919
import {ExternalFunction, isHookName} from '../HIR/Environment';
2020
import {Err, Ok, Result} from '../Utils/Result';
21-
import {CompilerReactTarget} from './Options';
22-
import {getReactCompilerRuntimeModule} from './Program';
21+
import {LoggerEvent, PluginOptions} from './Options';
22+
import {BabelFn, getReactCompilerRuntimeModule} from './Program';
23+
import {SuppressionRange} from './Suppression';
2324

2425
export function validateRestrictedImports(
2526
path: NodePath<t.Program>,
@@ -52,32 +53,61 @@ export function validateRestrictedImports(
5253
}
5354
}
5455

56+
type ProgramContextOptions = {
57+
program: NodePath<t.Program>;
58+
suppressions: Array<SuppressionRange>;
59+
opts: PluginOptions;
60+
filename: string | null;
61+
code: string | null;
62+
};
5563
export class ProgramContext {
56-
/* Program and environment context */
64+
/**
65+
* Program and environment context
66+
*/
5767
scope: BabelScope;
68+
opts: PluginOptions;
69+
filename: string | null;
70+
code: string | null;
5871
reactRuntimeModule: string;
59-
hookPattern: string | null;
72+
suppressions: Array<SuppressionRange>;
6073

74+
/*
75+
* This is a hack to work around what seems to be a Babel bug. Babel doesn't
76+
* consistently respect the `skip()` function to avoid revisiting a node within
77+
* a pass, so we use this set to track nodes that we have compiled.
78+
*/
79+
alreadyCompiled: WeakSet<object> | Set<object> = new (WeakSet ?? Set)();
6180
// known generated or referenced identifiers in the program
6281
knownReferencedNames: Set<string> = new Set();
6382
// generated imports
6483
imports: Map<string, Map<string, NonLocalImportSpecifier>> = new Map();
6584

66-
constructor(
67-
program: NodePath<t.Program>,
68-
reactRuntimeModule: CompilerReactTarget,
69-
hookPattern: string | null,
70-
) {
71-
this.hookPattern = hookPattern;
85+
/**
86+
* Metadata from compilation
87+
*/
88+
retryErrors: Array<{fn: BabelFn; error: CompilerError}> = [];
89+
inferredEffectLocations: Set<t.SourceLocation> = new Set();
90+
91+
constructor({
92+
program,
93+
suppressions,
94+
opts,
95+
filename,
96+
code,
97+
}: ProgramContextOptions) {
7298
this.scope = program.scope;
73-
this.reactRuntimeModule = getReactCompilerRuntimeModule(reactRuntimeModule);
99+
this.opts = opts;
100+
this.filename = filename;
101+
this.code = code;
102+
this.reactRuntimeModule = getReactCompilerRuntimeModule(opts.target);
103+
this.suppressions = suppressions;
74104
}
75105

76106
isHookName(name: string): boolean {
77-
if (this.hookPattern == null) {
107+
if (this.opts.environment.hookPattern == null) {
78108
return isHookName(name);
79109
} else {
80-
const match = new RegExp(this.hookPattern).exec(name);
110+
const match = new RegExp(this.opts.environment.hookPattern).exec(name);
81111
return (
82112
match != null && typeof match[1] === 'string' && isHookName(match[1])
83113
);
@@ -179,6 +209,12 @@ export class ProgramContext {
179209
});
180210
return Err(error);
181211
}
212+
213+
logEvent(event: LoggerEvent): void {
214+
if (this.opts.logger != null) {
215+
this.opts.logger.logEvent(this.filename, event);
216+
}
217+
}
182218
}
183219

184220
function getExistingImports(

0 commit comments

Comments
 (0)