diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts index 16ef7986ef1..679389277b5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts @@ -7,8 +7,9 @@ import {NodePath} from '@babel/core'; import * as t from '@babel/types'; -import {PluginOptions} from './Options'; import {CompilerError} from '../CompilerError'; +import {ProgramContext} from './Imports'; +import {ExternalFunction} from '..'; /** * Gating rewrite for function declarations which are referenced before their @@ -34,7 +35,8 @@ import {CompilerError} from '../CompilerError'; function insertAdditionalFunctionDeclaration( fnPath: NodePath, compiled: t.FunctionDeclaration, - gating: NonNullable, + programContext: ProgramContext, + gatingFunctionIdentifierName: string, ): void { const originalFnName = fnPath.node.id; const originalFnParams = fnPath.node.params; @@ -57,14 +59,14 @@ function insertAdditionalFunctionDeclaration( loc: fnPath.node.loc ?? null, }); - const gatingCondition = fnPath.scope.generateUidIdentifier( - `${gating.importSpecifierName}_result`, + const gatingCondition = t.identifier( + programContext.newUid(`${gatingFunctionIdentifierName}_result`), ); - const unoptimizedFnName = fnPath.scope.generateUidIdentifier( - `${originalFnName.name}_unoptimized`, + const unoptimizedFnName = t.identifier( + programContext.newUid(`${originalFnName.name}_unoptimized`), ); - const optimizedFnName = fnPath.scope.generateUidIdentifier( - `${originalFnName.name}_optimized`, + const optimizedFnName = t.identifier( + programContext.newUid(`${originalFnName.name}_optimized`), ); /** * Step 1: rename existing functions @@ -115,7 +117,7 @@ function insertAdditionalFunctionDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( gatingCondition, - t.callExpression(t.identifier(gating.importSpecifierName), []), + t.callExpression(t.identifier(gatingFunctionIdentifierName), []), ), ]), ); @@ -129,19 +131,26 @@ export function insertGatedFunctionDeclaration( | t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression, - gating: NonNullable, + programContext: ProgramContext, + gating: ExternalFunction, referencedBeforeDeclaration: boolean, ): void { + const gatingImportedName = programContext.addImportSpecifier(gating).name; if (referencedBeforeDeclaration && fnPath.isFunctionDeclaration()) { CompilerError.invariant(compiled.type === 'FunctionDeclaration', { reason: 'Expected compiled node type to match input type', description: `Got ${compiled.type} but expected FunctionDeclaration`, loc: fnPath.node.loc ?? null, }); - insertAdditionalFunctionDeclaration(fnPath, compiled, gating); + insertAdditionalFunctionDeclaration( + fnPath, + compiled, + programContext, + gatingImportedName, + ); } else { const gatingExpression = t.conditionalExpression( - t.callExpression(t.identifier(gating.importSpecifierName), []), + t.callExpression(t.identifier(gatingImportedName), []), buildFunctionExpression(compiled), buildFunctionExpression(fnPath.node), ); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts index 1a19d019766..704f696b3b8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts @@ -7,9 +7,19 @@ import {NodePath} from '@babel/core'; import * as t from '@babel/types'; +import {Scope as BabelScope} from '@babel/traverse'; + import {CompilerError, ErrorSeverity} from '../CompilerError'; -import {EnvironmentConfig, ExternalFunction, GeneratedSource} from '../HIR'; -import {getOrInsertDefault} from '../Utils/utils'; +import { + EnvironmentConfig, + GeneratedSource, + NonLocalImportSpecifier, +} from '../HIR'; +import {getOrInsertWith} from '../Utils/utils'; +import {ExternalFunction, isHookName} from '../HIR/Environment'; +import {Err, Ok, Result} from '../Utils/Result'; +import {CompilerReactTarget} from './Options'; +import {getReactCompilerRuntimeModule} from './Program'; export function validateRestrictedImports( path: NodePath, @@ -42,159 +52,226 @@ export function validateRestrictedImports( } } -export function addImportsToProgram( - path: NodePath, - importList: Array, -): void { - const identifiers: Set = new Set(); - const sortedImports: Map> = new Map(); - for (const {importSpecifierName, source} of importList) { - /* - * Codegen currently does not rename import specifiers, so we do additional - * validation here +export class ProgramContext { + /* Program and environment context */ + scope: BabelScope; + reactRuntimeModule: string; + hookPattern: string | null; + + // known generated or referenced identifiers in the program + knownReferencedNames: Set = new Set(); + // generated imports + imports: Map> = new Map(); + + constructor( + program: NodePath, + reactRuntimeModule: CompilerReactTarget, + hookPattern: string | null, + ) { + this.hookPattern = hookPattern; + this.scope = program.scope; + this.reactRuntimeModule = getReactCompilerRuntimeModule(reactRuntimeModule); + } + + isHookName(name: string): boolean { + if (this.hookPattern == null) { + return isHookName(name); + } else { + const match = new RegExp(this.hookPattern).exec(name); + return ( + match != null && typeof match[1] === 'string' && isHookName(match[1]) + ); + } + } + + hasReference(name: string): boolean { + return ( + this.knownReferencedNames.has(name) || + this.scope.hasBinding(name) || + this.scope.hasGlobal(name) || + this.scope.hasReference(name) + ); + } + + newUid(name: string): string { + /** + * Don't call babel's generateUid for known hook imports, as + * InferTypes might eventually type `HookKind` based on callee naming + * convention and `_useFoo` is not named as a hook. + * + * Local uid generation is susceptible to check-before-use bugs since we're + * checking for naming conflicts / references long before we actually insert + * the import. (see similar logic in HIRBuilder:resolveBinding) */ - CompilerError.invariant(identifiers.has(importSpecifierName) === false, { - reason: `Encountered conflicting import specifier for ${importSpecifierName} in Forget config.`, - description: null, - loc: GeneratedSource, - suggestions: null, - }); - CompilerError.invariant( - path.scope.hasBinding(importSpecifierName) === false, + let uid; + if (this.isHookName(name)) { + uid = name; + let i = 0; + while (this.hasReference(uid)) { + this.knownReferencedNames.add(uid); + uid = `${name}_${i++}`; + } + } else if (!this.hasReference(name)) { + uid = name; + } else { + uid = this.scope.generateUid(name); + } + this.knownReferencedNames.add(uid); + return uid; + } + + addMemoCacheImport(): NonLocalImportSpecifier { + return this.addImportSpecifier( { - reason: `Encountered conflicting import specifiers for ${importSpecifierName} in generated program.`, - description: null, - loc: GeneratedSource, - suggestions: null, + source: this.reactRuntimeModule, + importSpecifierName: 'c', }, + '_c', ); - identifiers.add(importSpecifierName); - - const importSpecifierNameList = getOrInsertDefault( - sortedImports, - source, - [], - ); - importSpecifierNameList.push(importSpecifierName); } - const stmts: Array = []; - for (const [source, importSpecifierNameList] of sortedImports) { - const importSpecifiers = importSpecifierNameList.map(name => { - const id = t.identifier(name); - return t.importSpecifier(id, id); - }); + /** + * + * @param externalFunction + * @param nameHint if defined, will be used as the name of the import specifier + * @returns + */ + addImportSpecifier( + {source: module, importSpecifierName: specifier}: ExternalFunction, + nameHint?: string, + ): NonLocalImportSpecifier { + const maybeBinding = this.imports.get(module)?.get(specifier); + if (maybeBinding != null) { + return {...maybeBinding}; + } - stmts.push(t.importDeclaration(importSpecifiers, t.stringLiteral(source))); + const binding: NonLocalImportSpecifier = { + kind: 'ImportSpecifier', + name: this.newUid(nameHint ?? specifier), + module, + imported: specifier, + }; + getOrInsertWith(this.imports, module, () => new Map()).set(specifier, { + ...binding, + }); + return binding; } - path.unshiftContainer('body', stmts); -} - -/* - * Matches `import { ... } from ;` - * but not `import * as React from ;` - */ -function isNonNamespacedImport( - importDeclPath: NodePath, - moduleName: string, -): boolean { - return ( - importDeclPath.get('source').node.value === moduleName && - importDeclPath - .get('specifiers') - .every(specifier => specifier.isImportSpecifier()) && - importDeclPath.node.importKind !== 'type' && - importDeclPath.node.importKind !== 'typeof' - ); -} -function hasExistingNonNamespacedImportOfModule( - program: NodePath, - moduleName: string, -): boolean { - let hasExistingImport = false; - program.traverse({ - ImportDeclaration(importDeclPath) { - if (isNonNamespacedImport(importDeclPath, moduleName)) { - hasExistingImport = true; - } - }, - }); + addNewReference(name: string): void { + this.knownReferencedNames.add(name); + } - return hasExistingImport; + assertGlobalBinding( + name: string, + localScope?: BabelScope, + ): Result { + const scope = localScope ?? this.scope; + if (!scope.hasReference(name) && !scope.hasBinding(name)) { + return Ok(undefined); + } + const error = new CompilerError(); + error.push({ + severity: ErrorSeverity.Todo, + reason: 'Encountered conflicting global in generated program', + description: `Conflict from local binding ${name}`, + loc: scope.getBinding(name)?.path.node.loc ?? null, + suggestions: null, + }); + return Err(error); + } } -/* - * If an existing import of React exists (ie `import { ... } from ''`), inject useMemoCache - * into the list of destructured variables. - */ -function addMemoCacheFunctionSpecifierToExistingImport( +function getExistingImports( program: NodePath, - moduleName: string, - identifierName: string, -): boolean { - let didInsertUseMemoCache = false; +): Map> { + const existingImports = new Map>(); program.traverse({ - ImportDeclaration(importDeclPath) { - if ( - !didInsertUseMemoCache && - isNonNamespacedImport(importDeclPath, moduleName) - ) { - importDeclPath.pushContainer( - 'specifiers', - t.importSpecifier(t.identifier(identifierName), t.identifier('c')), - ); - didInsertUseMemoCache = true; + ImportDeclaration(path) { + if (isNonNamespacedImport(path)) { + existingImports.set(path.node.source.value, path); } }, }); - return didInsertUseMemoCache; + return existingImports; } -export function updateMemoCacheFunctionImport( - program: NodePath, - moduleName: string, - useMemoCacheIdentifier: string, +export function addImportsToProgram( + path: NodePath, + programContext: ProgramContext, ): void { - /* - * If there isn't already an import of * as React, insert it so useMemoCache doesn't - * throw - */ - const hasExistingImport = hasExistingNonNamespacedImportOfModule( - program, - moduleName, + const existingImports = getExistingImports(path); + const stmts: Array = []; + const sortedModules = [...programContext.imports.entries()].sort(([a], [b]) => + a.localeCompare(b), ); + for (const [moduleName, importsMap] of sortedModules) { + for (const [specifierName, loweredImport] of importsMap) { + /** + * Assert that the import identifier hasn't already be declared in the program. + * Note: we use getBinding here since `Scope.hasBinding` pessimistically returns true + * for all allocated uids (from `Scope.getUid`) + */ + CompilerError.invariant( + path.scope.getBinding(loweredImport.name) == null, + { + reason: + 'Encountered conflicting import specifiers in generated program', + description: `Conflict from import ${loweredImport.module}:(${loweredImport.imported} as ${loweredImport.name}).`, + loc: GeneratedSource, + suggestions: null, + }, + ); + CompilerError.invariant( + loweredImport.module === moduleName && + loweredImport.imported === specifierName, + { + reason: + 'Found inconsistent import specifier. This is an internal bug.', + description: `Expected import ${moduleName}:${specifierName} but found ${loweredImport.module}:${loweredImport.imported}`, + loc: GeneratedSource, + }, + ); + } + const sortedImport: Array = [ + ...importsMap.values(), + ].sort(({imported: a}, {imported: b}) => a.localeCompare(b)); + const importSpecifiers = sortedImport.map(specifier => { + return t.importSpecifier( + t.identifier(specifier.name), + t.identifier(specifier.imported), + ); + }); - if (hasExistingImport) { - const didUpdateImport = addMemoCacheFunctionSpecifierToExistingImport( - program, - moduleName, - useMemoCacheIdentifier, - ); - if (!didUpdateImport) { - throw new Error( - `Expected an ImportDeclaration of \`${moduleName}\` in order to update ImportSpecifiers with useMemoCache`, + /** + * If an existing import of this module exists (ie `import { ... } from + * ''`), inject new imported specifiers into the list of + * destructured variables. + */ + const maybeExistingImports = existingImports.get(moduleName); + if (maybeExistingImports != null) { + maybeExistingImports.pushContainer('specifiers', importSpecifiers); + } else { + stmts.push( + t.importDeclaration(importSpecifiers, t.stringLiteral(moduleName)), ); } - } else { - addMemoCacheFunctionImportDeclaration( - program, - moduleName, - useMemoCacheIdentifier, - ); } + path.unshiftContainer('body', stmts); } -function addMemoCacheFunctionImportDeclaration( - program: NodePath, - moduleName: string, - localName: string, -): void { - program.unshiftContainer( - 'body', - t.importDeclaration( - [t.importSpecifier(t.identifier(localName), t.identifier('c'))], - t.stringLiteral(moduleName), - ), +/* + * Matches `import { ... } from ;` + * but not `import * as React from ;` + * `import type { Foo } from ;` + */ +function isNonNamespacedImport( + importDeclPath: NodePath, +): boolean { + return ( + importDeclPath + .get('specifiers') + .every(specifier => specifier.isImportSpecifier()) && + importDeclPath.node.importKind !== 'type' && + importDeclPath.node.importKind !== 'typeof' ); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index ed41ce2eedc..61f57f68cfe 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -8,7 +8,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; import prettyFormat from 'pretty-format'; -import {Logger} from '.'; +import {Logger, ProgramContext} from '.'; import { HIRFunction, ReactiveFunction, @@ -117,7 +117,7 @@ function run( config: EnvironmentConfig, fnType: ReactFunctionType, mode: CompilerMode, - useMemoCacheIdentifier: string, + programContext: ProgramContext, logger: Logger | null, filename: string | null, code: string | null, @@ -132,7 +132,7 @@ function run( logger, filename, code, - useMemoCacheIdentifier, + programContext, ); env.logger?.debugLogIRs?.({ kind: 'debug', @@ -552,7 +552,7 @@ export function compileFn( config: EnvironmentConfig, fnType: ReactFunctionType, mode: CompilerMode, - useMemoCacheIdentifier: string, + programContext: ProgramContext, logger: Logger | null, filename: string | null, code: string | null, @@ -562,7 +562,7 @@ export function compileFn( config, fnType, mode, - useMemoCacheIdentifier, + programContext, logger, filename, code, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index ec1a2d2935f..c00c672b2cb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -12,11 +12,7 @@ import { CompilerErrorDetail, ErrorSeverity, } from '../CompilerError'; -import { - EnvironmentConfig, - ExternalFunction, - ReactFunctionType, -} from '../HIR/Environment'; +import {EnvironmentConfig, ReactFunctionType} from '../HIR/Environment'; import {CodegenFunction} from '../ReactiveScopes'; import {isComponentDeclaration} from '../Utils/ComponentDeclaration'; import {isHookDeclaration} from '../Utils/HookDeclaration'; @@ -24,10 +20,10 @@ import {assertExhaustive} from '../Utils/utils'; import {insertGatedFunctionDeclaration} from './Gating'; import { addImportsToProgram, - updateMemoCacheFunctionImport, + ProgramContext, validateRestrictedImports, } from './Imports'; -import {PluginOptions} from './Options'; +import {CompilerReactTarget, PluginOptions} from './Options'; import {compileFn} from './Pipeline'; import { filterSuppressionsThatAffectFunction, @@ -299,8 +295,12 @@ export function compileProgram( handleError(restrictedImportsErr, pass, null); return null; } - const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c'); + const programContext = new ProgramContext( + program, + pass.opts.target, + environment.hookPattern, + ); /* * Record lint errors and critical errors as depending on Forget's config, * we may still need to run Forget's analysis on every function (even if we @@ -410,7 +410,7 @@ export function compileProgram( environment, fnType, 'all_features', - useMemoCacheIdentifier.name, + programContext, pass.opts.logger, pass.filename, pass.code, @@ -445,7 +445,7 @@ export function compileProgram( environment, fnType, 'no_inferred_memo', - useMemoCacheIdentifier.name, + programContext, pass.opts.logger, pass.filename, pass.code, @@ -453,7 +453,7 @@ export function compileProgram( }; if ( !compileResult.compiledFn.hasFireRewrite && - !compileResult.compiledFn.hasLoweredContextAccess + !compileResult.compiledFn.hasInferredEffect ) { return null; } @@ -554,79 +554,29 @@ export function compileProgram( if (moduleScopeOptOutDirectives.length > 0) { return null; } - let gating: null | { - gatingFn: ExternalFunction; - referencedBeforeDeclared: Set; - } = null; - if (pass.opts.gating != null) { - gating = { - gatingFn: pass.opts.gating, - referencedBeforeDeclared: - getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns), - }; - } - - const hasLoweredContextAccess = compiledFns.some( - c => c.compiledFn.hasLoweredContextAccess, - ); - const externalFunctions: Array = []; - try { - // TODO: check for duplicate import specifiers - if (gating != null) { - externalFunctions.push(gating.gatingFn); - } - - const lowerContextAccess = environment.lowerContextAccess; - if (lowerContextAccess && hasLoweredContextAccess) { - externalFunctions.push(lowerContextAccess); - } - - const enableEmitInstrumentForget = environment.enableEmitInstrumentForget; - if (enableEmitInstrumentForget != null) { - externalFunctions.push(enableEmitInstrumentForget.fn); - if (enableEmitInstrumentForget.gating != null) { - externalFunctions.push(enableEmitInstrumentForget.gating); - } - } - - if (environment.enableEmitFreeze != null) { - externalFunctions.push(environment.enableEmitFreeze); - } - - if (environment.enableEmitHookGuards != null) { - externalFunctions.push(environment.enableEmitHookGuards); - } - - if (environment.enableChangeDetectionForDebugging != null) { - externalFunctions.push(environment.enableChangeDetectionForDebugging); - } - - const hasFireRewrite = compiledFns.some(c => c.compiledFn.hasFireRewrite); - if (environment.enableFire && hasFireRewrite) { - externalFunctions.push({ - source: getReactCompilerRuntimeModule(pass.opts), - importSpecifierName: 'useFire', - }); - } - } catch (err) { - handleError(err, pass, null); - return null; - } - /* * Only insert Forget-ified functions if we have not encountered a critical * error elsewhere in the file, regardless of bailout mode. */ + const referencedBeforeDeclared = + pass.opts.gating != null + ? getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns) + : null; for (const result of compiledFns) { const {kind, originalFn, compiledFn} = result; const transformedFn = createNewFunctionNode(originalFn, compiledFn); - if (gating != null && kind === 'original') { + if (referencedBeforeDeclared != null && kind === 'original') { + CompilerError.invariant(pass.opts.gating != null, { + reason: "Expected 'gating' import to be present", + loc: null, + }); insertGatedFunctionDeclaration( originalFn, transformedFn, - gating.gatingFn, - gating.referencedBeforeDeclared.has(result), + programContext, + pass.opts.gating, + referencedBeforeDeclared.has(result), ); } else { originalFn.replaceWith(transformedFn); @@ -635,22 +585,7 @@ export function compileProgram( // Forget compiled the component, we need to update existing imports of useMemoCache if (compiledFns.length > 0) { - let needsMemoCacheFunctionImport = false; - for (const fn of compiledFns) { - if (fn.compiledFn.memoSlotsUsed > 0) { - needsMemoCacheFunctionImport = true; - break; - } - } - - if (needsMemoCacheFunctionImport) { - updateMemoCacheFunctionImport( - program, - getReactCompilerRuntimeModule(pass.opts), - useMemoCacheIdentifier.name, - ); - } - addImportsToProgram(program, externalFunctions); + addImportsToProgram(program, programContext); } return {retryErrors}; } @@ -683,7 +618,7 @@ function shouldSkipCompilation( if ( hasMemoCacheFunctionImport( program, - getReactCompilerRuntimeModule(pass.opts), + getReactCompilerRuntimeModule(pass.opts.target), ) ) { return true; @@ -1177,16 +1112,18 @@ function getFunctionReferencedBeforeDeclarationAtTopLevel( return referencedBeforeDeclaration; } -function getReactCompilerRuntimeModule(opts: PluginOptions): string { - if (opts.target === '19') { +export function getReactCompilerRuntimeModule( + target: CompilerReactTarget, +): string { + if (target === '19') { return 'react/compiler-runtime'; // from react namespace - } else if (opts.target === '17' || opts.target === '18') { + } else if (target === '17' || target === '18') { return 'react-compiler-runtime'; // npm package } else { CompilerError.invariant( - opts.target != null && - opts.target.kind === 'donotuse_meta_internal' && - typeof opts.target.runtimeModule === 'string', + target != null && + target.kind === 'donotuse_meta_internal' && + typeof target.runtimeModule === 'string', { reason: 'Expected target to already be validated', description: null, @@ -1194,6 +1131,6 @@ function getReactCompilerRuntimeModule(opts: PluginOptions): string { suggestions: null, }, ); - return opts.target.runtimeModule; + return target.runtimeModule; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index a8f5b15dbd0..2594ac31c6b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -15,6 +15,7 @@ import { PanicThresholdOptions, parsePluginOptions, PluginOptions, + ProgramContext, } from '../Entrypoint'; import {Err, Ok, Result} from '../Utils/Result'; import { @@ -84,6 +85,8 @@ export const InstrumentationSchema = z ); export type ExternalFunction = z.infer; +export const USE_FIRE_FUNCTION_NAME = 'useFire'; +export const EMIT_FREEZE_GLOBAL_GATING = '__DEV__'; export const MacroMethodSchema = z.union([ z.object({type: z.literal('wildcard')}), @@ -846,9 +849,9 @@ export class Environment { config: EnvironmentConfig; fnType: ReactFunctionType; compilerMode: CompilerMode; - useMemoCacheIdentifier: string; - hasLoweredContextAccess: boolean; + programContext: ProgramContext; hasFireRewrite: boolean; + hasInferredEffect: boolean; #contextIdentifiers: Set; #hoistedIdentifiers: Set; @@ -862,7 +865,7 @@ export class Environment { logger: Logger | null, filename: string | null, code: string | null, - useMemoCacheIdentifier: string, + programContext: ProgramContext, ) { this.#scope = scope; this.fnType = fnType; @@ -871,11 +874,11 @@ export class Environment { this.filename = filename; this.code = code; this.logger = logger; - this.useMemoCacheIdentifier = useMemoCacheIdentifier; + this.programContext = programContext; this.#shapes = new Map(DEFAULT_SHAPES); this.#globals = new Map(DEFAULT_GLOBALS); - this.hasLoweredContextAccess = false; this.hasFireRewrite = false; + this.hasInferredEffect = false; if ( config.disableMemoizationForDebugging && @@ -937,6 +940,10 @@ export class Environment { return makeScopeId(this.#nextScope++); } + get scope(): BabelScope { + return this.#scope; + } + logErrors(errors: Result): void { if (errors.isOk() || this.logger == null) { return; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index f58cdfbb081..3a8cb89ca0f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -1167,18 +1167,21 @@ export type VariableBinding = // bindings declard outside the current component/hook | NonLocalBinding; +// `import {bar as baz} from 'foo'`: name=baz, module=foo, imported=bar +export type NonLocalImportSpecifier = { + kind: 'ImportSpecifier'; + name: string; + module: string; + imported: string; +}; + export type NonLocalBinding = // `import Foo from 'foo'`: name=Foo, module=foo | {kind: 'ImportDefault'; name: string; module: string} // `import * as Foo from 'foo'`: name=Foo, module=foo | {kind: 'ImportNamespace'; name: string; module: string} - // `import {bar as baz} from 'foo'`: name=baz, module=foo, imported=bar - | { - kind: 'ImportSpecifier'; - name: string; - module: string; - imported: string; - } + // `import {bar as baz} from 'foo'` + | NonLocalImportSpecifier // let, const, function, etc declared in the module but outside the current component/hook | {kind: 'ModuleLocal'; name: string} // an unresolved binding diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts index 9202f2145f2..44dd34b7d6c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts @@ -331,6 +331,7 @@ export default class HIRBuilder { type: makeType(), loc: node.loc ?? GeneratedSource, }; + this.#env.programContext.addNewReference(name); this.#bindings.set(name, {node, identifier}); return identifier; } else if (mapping.node === node) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts index 4d295aad060..85cb0236659 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts @@ -249,6 +249,7 @@ export function inferEffectDependencies(fn: HIRFunction): void { // Renumber instructions and fix scope ranges markInstructionIds(fn.body); fixScopeAndIdentifierRanges(fn.body); + fn.env.hasInferredEffect = true; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts index b636c7b1718..834f60195af 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts @@ -18,6 +18,7 @@ import { Instruction, LoadGlobal, LoadLocal, + NonLocalImportSpecifier, Place, PropertyLoad, isUseContextHookType, @@ -35,7 +36,7 @@ import {inferTypes} from '../TypeInference'; export function lowerContextAccess( fn: HIRFunction, - loweredContextCallee: ExternalFunction, + loweredContextCalleeConfig: ExternalFunction, ): void { const contextAccess: Map = new Map(); const contextKeys: Map> = new Map(); @@ -79,6 +80,8 @@ export function lowerContextAccess( } } + let importLoweredContextCallee: NonLocalImportSpecifier | null = null; + if (contextAccess.size > 0 && contextKeys.size > 0) { for (const [, block] of fn.body.blocks) { let nextInstructions: Array | null = null; @@ -91,9 +94,13 @@ export function lowerContextAccess( isUseContextHookType(value.callee.identifier) && contextKeys.has(lvalue.identifier.id) ) { + importLoweredContextCallee ??= + fn.env.programContext.addImportSpecifier( + loweredContextCalleeConfig, + ); const loweredContextCalleeInstr = emitLoadLoweredContextCallee( fn.env, - loweredContextCallee, + importLoweredContextCallee, ); if (nextInstructions === null) { @@ -122,21 +129,16 @@ export function lowerContextAccess( } markInstructionIds(fn.body); inferTypes(fn); - fn.env.hasLoweredContextAccess = true; } } function emitLoadLoweredContextCallee( env: Environment, - loweredContextCallee: ExternalFunction, + importedLowerContextCallee: NonLocalImportSpecifier, ): Instruction { const loadGlobal: LoadGlobal = { kind: 'LoadGlobal', - binding: { - kind: 'ImportNamespace', - module: loweredContextCallee.source, - name: loweredContextCallee.importSpecifierName, - }, + binding: {...importedLowerContextCallee}, loc: GeneratedSource, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts index a6b94075cce..d35c4d77362 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts @@ -196,7 +196,7 @@ function process( return null; } - const props = collectProps(jsx); + const props = collectProps(fn.env, jsx); if (!props) return null; const outlinedTag = fn.env.generateGloballyUniqueIdentifierName(null).value; @@ -217,6 +217,7 @@ type OutlinedJsxAttribute = { }; function collectProps( + env: Environment, instructions: Array, ): Array | null { let id = 1; @@ -227,6 +228,7 @@ function collectProps( newName = `${oldName}${id++}`; } seen.add(newName); + env.programContext.addNewReference(newName); return newName; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index ce535a9b386..b90e4e417c6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -52,7 +52,8 @@ import {assertExhaustive} from '../Utils/utils'; import {buildReactiveFunction} from './BuildReactiveFunction'; import {SINGLE_CHILD_FBT_TAGS} from './MemoizeFbtAndMacroOperandsInSameScope'; import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors'; -import {ReactFunctionType} from '../HIR/Environment'; +import {EMIT_FREEZE_GLOBAL_GATING, ReactFunctionType} from '../HIR/Environment'; +import {ProgramContext} from '../Entrypoint'; export const MEMO_CACHE_SENTINEL = 'react.memo_cache_sentinel'; export const EARLY_RETURN_SENTINEL = 'react.early_return_sentinel'; @@ -100,9 +101,9 @@ export type CodegenFunction = { }>; /** - * This is true if the compiler has the lowered useContext calls. + * This is true if the compiler has compiled inferred effect dependencies */ - hasLoweredContextAccess: boolean; + hasInferredEffect: boolean; /** * This is true if the compiler has compiled a fire to a useFire call @@ -160,6 +161,7 @@ export function codegenFunction( compiled.body = t.blockStatement([ createHookGuard( hookGuard, + fn.env.programContext, compiled.body.body, GuardKind.PushHookGuard, GuardKind.PopHookGuard, @@ -170,13 +172,15 @@ export function codegenFunction( const cacheCount = compiled.memoSlotsUsed; if (cacheCount !== 0) { const preface: Array = []; + const useMemoCacheIdentifier = + fn.env.programContext.addMemoCacheImport().name; // The import declaration for `useMemoCache` is inserted in the Babel plugin preface.push( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(cx.synthesizeName('$')), - t.callExpression(t.identifier(fn.env.useMemoCacheIdentifier), [ + t.callExpression(t.identifier(useMemoCacheIdentifier), [ t.numericLiteral(cacheCount), ]), ), @@ -259,34 +263,54 @@ export function codegenFunction( * Technically, this is a conditional hook call. However, we expect * __DEV__ and gating identifier to be runtime constants */ - let gating: t.Expression; - if ( - emitInstrumentForget.gating != null && + const gating = + emitInstrumentForget.gating != null + ? t.identifier( + fn.env.programContext.addImportSpecifier( + emitInstrumentForget.gating, + ).name, + ) + : null; + + const globalGating = emitInstrumentForget.globalGating != null - ) { - gating = t.logicalExpression( - '&&', - t.identifier(emitInstrumentForget.globalGating), - t.identifier(emitInstrumentForget.gating.importSpecifierName), + ? t.identifier(emitInstrumentForget.globalGating) + : null; + + if (emitInstrumentForget.globalGating != null) { + const assertResult = fn.env.programContext.assertGlobalBinding( + emitInstrumentForget.globalGating, ); - } else if (emitInstrumentForget.gating != null) { - gating = t.identifier(emitInstrumentForget.gating.importSpecifierName); + if (assertResult.isErr()) { + return assertResult; + } + } + + let ifTest: t.Expression; + if (gating != null && globalGating != null) { + ifTest = t.logicalExpression('&&', globalGating, gating); + } else if (gating != null) { + ifTest = gating; } else { - CompilerError.invariant(emitInstrumentForget.globalGating != null, { + CompilerError.invariant(globalGating != null, { reason: 'Bad config not caught! Expected at least one of gating or globalGating', loc: null, suggestions: null, }); - gating = t.identifier(emitInstrumentForget.globalGating); + ifTest = globalGating; } + + const instrumentFnIdentifier = fn.env.programContext.addImportSpecifier( + emitInstrumentForget.fn, + ).name; const test: t.IfStatement = t.ifStatement( - gating, + ifTest, t.expressionStatement( - t.callExpression( - t.identifier(emitInstrumentForget.fn.importSpecifierName), - [t.stringLiteral(fn.id), t.stringLiteral(fn.env.filename ?? '')], - ), + t.callExpression(t.identifier(instrumentFnIdentifier), [ + t.stringLiteral(fn.id), + t.stringLiteral(fn.env.filename ?? ''), + ]), ), ); compiled.body.body.unshift(test); @@ -363,8 +387,8 @@ function codegenReactiveFunction( prunedMemoBlocks: countMemoBlockVisitor.prunedMemoBlocks, prunedMemoValues: countMemoBlockVisitor.prunedMemoValues, outlined: [], - hasLoweredContextAccess: fn.env.hasLoweredContextAccess, hasFireRewrite: fn.env.hasFireRewrite, + hasInferredEffect: fn.env.hasInferredEffect, }); } @@ -553,13 +577,18 @@ function codegenBlockNoReset( function wrapCacheDep(cx: Context, value: t.Expression): t.Expression { if (cx.env.config.enableEmitFreeze != null && cx.env.isInferredMemoEnabled) { - // The import declaration for emitFreeze is inserted in the Babel plugin + const emitFreezeIdentifier = cx.env.programContext.addImportSpecifier( + cx.env.config.enableEmitFreeze, + ).name; + cx.env.programContext + .assertGlobalBinding(EMIT_FREEZE_GLOBAL_GATING, cx.env.scope) + .unwrap(); return t.conditionalExpression( - t.identifier('__DEV__'), - t.callExpression( - t.identifier(cx.env.config.enableEmitFreeze.importSpecifierName), - [value, t.stringLiteral(cx.fnName)], - ), + t.identifier(EMIT_FREEZE_GLOBAL_GATING), + t.callExpression(t.identifier(emitFreezeIdentifier), [ + value, + t.stringLiteral(cx.fnName), + ]), value, ); } else { @@ -713,16 +742,14 @@ function codegenReactiveScope( let computationBlock = codegenBlock(cx, block); let memoStatement; - if ( - cx.env.config.enableChangeDetectionForDebugging != null && - changeExpressions.length > 0 - ) { + const detectionFunction = cx.env.config.enableChangeDetectionForDebugging; + if (detectionFunction != null && changeExpressions.length > 0) { const loc = typeof scope.loc === 'symbol' ? 'unknown location' : `(${scope.loc.start.line}:${scope.loc.end.line})`; - const detectionFunction = - cx.env.config.enableChangeDetectionForDebugging.importSpecifierName; + const importedDetectionFunctionIdentifier = + cx.env.programContext.addImportSpecifier(detectionFunction).name; const cacheLoadOldValueStatements: Array = []; const changeDetectionStatements: Array = []; const idempotenceDetectionStatements: Array = []; @@ -744,7 +771,7 @@ function codegenReactiveScope( ); changeDetectionStatements.push( t.expressionStatement( - t.callExpression(t.identifier(detectionFunction), [ + t.callExpression(t.identifier(importedDetectionFunctionIdentifier), [ t.identifier(loadName), t.cloneNode(name, true), t.stringLiteral(name.name), @@ -756,7 +783,7 @@ function codegenReactiveScope( ); idempotenceDetectionStatements.push( t.expressionStatement( - t.callExpression(t.identifier(detectionFunction), [ + t.callExpression(t.identifier(importedDetectionFunctionIdentifier), [ t.cloneNode(slot, true), t.cloneNode(name, true), t.stringLiteral(name.name), @@ -1518,15 +1545,15 @@ const createStringLiteral = withLoc(t.stringLiteral); function createHookGuard( guard: ExternalFunction, + context: ProgramContext, stmts: Array, before: GuardKind, after: GuardKind, ): t.TryStatement { + const guardFnName = context.addImportSpecifier(guard).name; function createHookGuardImpl(kind: number): t.ExpressionStatement { return t.expressionStatement( - t.callExpression(t.identifier(guard.importSpecifierName), [ - t.numericLiteral(kind), - ]), + t.callExpression(t.identifier(guardFnName), [t.numericLiteral(kind)]), ); } @@ -1576,6 +1603,7 @@ function createCallExpression( t.blockStatement([ createHookGuard( hookGuard, + env.programContext, [t.returnStatement(callExpr)], GuardKind.AllowHook, GuardKind.DisallowHook, diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts index f84965b92e6..5b39055e7d6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {ProgramContext} from '..'; import {CompilerError} from '../CompilerError'; import { DeclarationId, @@ -47,7 +48,7 @@ import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors'; */ export function renameVariables(fn: ReactiveFunction): Set { const globals = collectReferencedGlobals(fn); - const scopes = new Scopes(globals); + const scopes = new Scopes(globals, fn.env.programContext); renameVariablesImpl(fn, new Visitor(), scopes); return new Set([...scopes.names, ...globals]); } @@ -124,10 +125,12 @@ class Scopes { #seen: Map = new Map(); #stack: Array> = [new Map()]; #globals: Set; + #programContext: ProgramContext; names: Set = new Set(); - constructor(globals: Set) { + constructor(globals: Set, programContext: ProgramContext) { this.#globals = globals; + this.#programContext = programContext; } visit(identifier: Identifier): void { @@ -156,6 +159,7 @@ class Scopes { name = `${originalName.value}$${id++}`; } } + this.#programContext.addNewReference(name); const identifierName = makeIdentifierName(name); identifier.name = identifierName; this.#seen.set(identifier.declarationId, identifierName); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts b/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts index a480b5d7c78..943b6b8eca2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts @@ -28,6 +28,7 @@ import { isUseEffectHookType, LoadLocal, makeInstructionId, + NonLocalImportSpecifier, Place, promoteTemporary, } from '../HIR'; @@ -36,6 +37,7 @@ import {getOrInsertWith} from '../Utils/utils'; import {BuiltInFireId, DefaultNonmutatingHook} from '../HIR/ObjectShape'; import {eachInstructionOperand} from '../HIR/visitors'; import {printSourceLocationLine} from '../HIR/PrintHIR'; +import {USE_FIRE_FUNCTION_NAME} from '../HIR/Environment'; /* * TODO(jmbrown): @@ -56,6 +58,7 @@ export function transformFire(fn: HIRFunction): void { } function replaceFireFunctions(fn: HIRFunction, context: Context): void { + let importedUseFire: NonLocalImportSpecifier | null = null; let hasRewrite = false; for (const [, block] of fn.body.blocks) { const rewriteInstrs = new Map>(); @@ -87,7 +90,15 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void { ] of capturedCallees.entries()) { if (!context.hasCalleeWithInsertedFire(fireCalleePlace)) { context.addCalleeWithInsertedFire(fireCalleePlace); - const loadUseFireInstr = makeLoadUseFireInstruction(fn.env); + + importedUseFire ??= fn.env.programContext.addImportSpecifier({ + source: fn.env.programContext.reactRuntimeModule, + importSpecifierName: USE_FIRE_FUNCTION_NAME, + }); + const loadUseFireInstr = makeLoadUseFireInstruction( + fn.env, + importedUseFire, + ); const loadFireCalleeInstr = makeLoadFireCalleeInstruction( fn.env, fireCalleeInfo.capturedCalleeIdentifier, @@ -404,18 +415,16 @@ function ensureNoMoreFireUses(fn: HIRFunction, context: Context): void { } } -function makeLoadUseFireInstruction(env: Environment): Instruction { +function makeLoadUseFireInstruction( + env: Environment, + importedLoadUseFire: NonLocalImportSpecifier, +): Instruction { const useFirePlace = createTemporaryPlace(env, GeneratedSource); useFirePlace.effect = Effect.Read; useFirePlace.identifier.type = DefaultNonmutatingHook; const instrValue: InstructionValue = { kind: 'LoadGlobal', - binding: { - kind: 'ImportSpecifier', - name: 'useFire', - module: 'react', - imported: 'useFire', - }, + binding: {...importedLoadUseFire}, loc: GeneratedSource, }; return { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md new file mode 100644 index 00000000000..5bb87a2b032 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md @@ -0,0 +1,73 @@ + +## Input + +```javascript +import * as React from 'react'; +import {someImport} from 'react/compiler-runtime'; +import {calculateExpensiveNumber} from 'shared-runtime'; + +function Component(props) { + const [x] = React.useState(0); + const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]); + + return ( +
+ {expensiveNumber} + {`${someImport}`} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], +}; + +``` + +## Code + +```javascript +import * as React from "react"; +import { someImport, c as _c } from "react/compiler-runtime"; +import { calculateExpensiveNumber } from "shared-runtime"; + +function Component(props) { + const $ = _c(4); + const [x] = React.useState(0); + let t0; + let t1; + if ($[0] !== x) { + t1 = calculateExpensiveNumber(x); + $[0] = x; + $[1] = t1; + } else { + t1 = $[1]; + } + t0 = t1; + const expensiveNumber = t0; + let t2; + if ($[2] !== expensiveNumber) { + t2 = ( +
+ {expensiveNumber} + {`${someImport}`} +
+ ); + $[2] = expensiveNumber; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], +}; + +``` + +### Eval output +(kind: ok)
0undefined
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js new file mode 100644 index 00000000000..80a2006dd12 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js @@ -0,0 +1,20 @@ +import * as React from 'react'; +import {someImport} from 'react/compiler-runtime'; +import {calculateExpensiveNumber} from 'shared-runtime'; + +function Component(props) { + const [x] = React.useState(0); + const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]); + + return ( +
+ {expensiveNumber} + {`${someImport}`} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md index 161b42dc6d7..6ec8520f9ec 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md @@ -14,9 +14,9 @@ function useFoo(props) { ```javascript import { - useRenderCounter, - shouldInstrument, makeReadOnly, + shouldInstrument, + useRenderCounter, } from "react-compiler-runtime"; import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @enableEmitInstrumentForget diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md index 319de18794f..8c2f0b94ef8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md @@ -23,7 +23,7 @@ function Foo(props) { ## Code ```javascript -import { useRenderCounter, shouldInstrument } from "react-compiler-runtime"; +import { shouldInstrument, useRenderCounter } from "react-compiler-runtime"; import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget @compilationMode(annotation) function Bar(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md new file mode 100644 index 00000000000..a2df134b2a8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md @@ -0,0 +1,97 @@ + +## Input + +```javascript +// @enableEmitInstrumentForget @compilationMode(annotation) + +import {identity} from 'shared-runtime'; + +function Bar(props) { + 'use forget'; + const shouldInstrument = identity(null); + const _shouldInstrument = identity(null); + const _x2 = () => { + const _shouldInstrument2 = 'hello world'; + return identity({_shouldInstrument2}); + }; + return ( +
+ {props.bar} +
+ ); +} + +function Foo(props) { + 'use forget'; + return {props.bar}; +} + +``` + +## Code + +```javascript +import { + shouldInstrument as _shouldInstrument3, + useRenderCounter, +} from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget @compilationMode(annotation) + +import { identity } from "shared-runtime"; + +function Bar(props) { + "use forget"; + if (DEV && _shouldInstrument3) + useRenderCounter("Bar", "/conflict-codegen-instrument-forget.ts"); + const $ = _c(4); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = identity(null); + $[0] = t0; + } else { + t0 = $[0]; + } + const shouldInstrument = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = identity(null); + $[1] = t1; + } else { + t1 = $[1]; + } + const _shouldInstrument = t1; + let t2; + if ($[2] !== props.bar) { + t2 = ( +
+ {props.bar} +
+ ); + $[2] = props.bar; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +function Foo(props) { + "use forget"; + if (DEV && _shouldInstrument3) + useRenderCounter("Foo", "/conflict-codegen-instrument-forget.ts"); + const $ = _c(2); + let t0; + if ($[0] !== props.bar) { + t0 = {props.bar}; + $[0] = props.bar; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js new file mode 100644 index 00000000000..88ffefe220d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js @@ -0,0 +1,23 @@ +// @enableEmitInstrumentForget @compilationMode(annotation) + +import {identity} from 'shared-runtime'; + +function Bar(props) { + 'use forget'; + const shouldInstrument = identity(null); + const _shouldInstrument = identity(null); + const _x2 = () => { + const _shouldInstrument2 = 'hello world'; + return identity({_shouldInstrument2}); + }; + return ( +
+ {props.bar} +
+ ); +} + +function Foo(props) { + 'use forget'; + return {props.bar}; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md new file mode 100644 index 00000000000..9b30c1b8cbb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md @@ -0,0 +1,37 @@ + +## Input + +```javascript +// @enableEmitFreeze @instrumentForget + +let makeReadOnly = 'conflicting identifier'; +function useFoo(props) { + return foo(props.x); +} + +``` + +## Code + +```javascript +import { makeReadOnly as _makeReadOnly } from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @instrumentForget + +let makeReadOnly = "conflicting identifier"; +function useFoo(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.x) { + t0 = foo(props.x); + $[0] = props.x; + $[1] = __DEV__ ? _makeReadOnly(t0, "useFoo") : t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md new file mode 100644 index 00000000000..ee18d3d1a2d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + return foo(props.x, __DEV__); +} + +``` + +## Code + +```javascript +import { makeReadOnly } from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @instrumentForget +function useFoo(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.x) { + t0 = foo(props.x, __DEV__); + $[0] = props.x; + $[1] = __DEV__ ? makeReadOnly(t0, "useFoo") : t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js new file mode 100644 index 00000000000..62c313b67d3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js @@ -0,0 +1,4 @@ +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + return foo(props.x, __DEV__); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md deleted file mode 100644 index 2bd0eee1b1f..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md +++ /dev/null @@ -1,21 +0,0 @@ - -## Input - -```javascript -// @enableEmitFreeze @instrumentForget - -let makeReadOnly = 'conflicting identifier'; -function useFoo(props) { - return foo(props.x); -} - -``` - - -## Error - -``` -Invariant: Encountered conflicting import specifiers for makeReadOnly in generated program. -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md new file mode 100644 index 00000000000..a54cc98708f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + const __DEV__ = 'conflicting global'; + console.log(__DEV__); + return foo(props.x); +} + +``` + + +## Error + +``` + 1 | // @enableEmitFreeze @instrumentForget + 2 | function useFoo(props) { +> 3 | const __DEV__ = 'conflicting global'; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Todo: Encountered conflicting global in generated program. Conflict from local binding __DEV__ (3:3) + 4 | console.log(__DEV__); + 5 | return foo(props.x); + 6 | } +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js new file mode 100644 index 00000000000..4391ad76e70 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js @@ -0,0 +1,6 @@ +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + const __DEV__ = 'conflicting global'; + console.log(__DEV__); + return foo(props.x); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md index 659e2c9ff9e..298b4d59dfc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md @@ -18,8 +18,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() ? (t0) => { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md index fe85e38e106..6256e5ec90f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md @@ -36,9 +36,9 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { useRenderCounter, shouldInstrument } from "react-compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget @compilationMode(annotation) @gating +import { shouldInstrument, useRenderCounter } from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @enableEmitInstrumentForget @compilationMode(annotation) @gating const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md index 25215580c81..410b6512446 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md @@ -20,14 +20,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { Stringify } from "shared-runtime"; import * as React from "react"; const Foo = React.forwardRef(Foo_withRef); -const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); -function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { +const isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { const $ = _c(2); let t0; if ($[0] !== ref) { @@ -39,16 +39,15 @@ function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { } return t0; } -function _Foo_withRef_unoptimized( +function Foo_withRef_unoptimized( _$$empty_props_placeholder$$: $ReadOnly<{}>, ref: React.RefSetter, ): React.Node { return ; } function Foo_withRef(arg0, arg1) { - if (_isForgetEnabled_Fixtures_result) - return _Foo_withRef_optimized(arg0, arg1); - else return _Foo_withRef_unoptimized(arg0, arg1); + if (isForgetEnabled_Fixtures_result) return Foo_withRef_optimized(arg0, arg1); + else return Foo_withRef_unoptimized(arg0, arg1); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md new file mode 100644 index 00000000000..ca6f518032b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +// @gating + +export const isForgetEnabled_Fixtures = () => { + 'use no forget'; + return false; +}; + +export function Bar(props) { + 'use forget'; + return
{props.bar}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures as _isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating + +export const isForgetEnabled_Fixtures = () => { + "use no forget"; + return false; +}; + +export const Bar = _isForgetEnabled_Fixtures() + ? function Bar(props) { + "use forget"; + const $ = _c(2); + let t0; + if ($[0] !== props.bar) { + t0 =
{props.bar}
; + $[0] = props.bar; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; + } + : function Bar(props) { + "use forget"; + return
{props.bar}
; + }; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + +``` + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js new file mode 100644 index 00000000000..3e5757dc915 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js @@ -0,0 +1,16 @@ +// @gating + +export const isForgetEnabled_Fixtures = () => { + 'use no forget'; + return false; +}; + +export function Bar(props) { + 'use forget'; + return
{props.bar}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md index e056b6fa1c0..9b3633472d6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md @@ -18,8 +18,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating const Component = isForgetEnabled_Fixtures() ? function Component() { const $ = _c(1); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md index a8f2a8dc58b..40791a2454b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md @@ -24,8 +24,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { identity, useHook as useRenamed } from "shared-runtime"; const _ = { useHook: isForgetEnabled_Fixtures() ? () => {} : () => {}, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md index 053fff651e5..cc852d25384 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md @@ -26,8 +26,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating const Component = isForgetEnabled_Fixtures() ? function Component() { const $ = _c(1); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md index 4e4a35c3755..ae7896e7a5e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md @@ -27,8 +27,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md index 95c629aa11f..4c368a8254d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md @@ -34,8 +34,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md index ab31d2939d4..c3e2e4a0428 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md @@ -27,8 +27,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) export const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md index e68e4840cf5..8ccce567134 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md @@ -27,8 +27,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md index d09c98dffd9..9730119c250 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md @@ -21,14 +21,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { createRef, forwardRef } from "react"; import { Stringify } from "shared-runtime"; const Foo = forwardRef(Foo_withRef); -const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); -function _Foo_withRef_optimized(props, ref) { +const isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function Foo_withRef_optimized(props, ref) { const $ = _c(3); let t0; if ($[0] !== props || $[1] !== ref) { @@ -41,13 +41,12 @@ function _Foo_withRef_optimized(props, ref) { } return t0; } -function _Foo_withRef_unoptimized(props, ref) { +function Foo_withRef_unoptimized(props, ref) { return ; } function Foo_withRef(arg0, arg1) { - if (_isForgetEnabled_Fixtures_result) - return _Foo_withRef_optimized(arg0, arg1); - else return _Foo_withRef_unoptimized(arg0, arg1); + if (isForgetEnabled_Fixtures_result) return Foo_withRef_optimized(arg0, arg1); + else return Foo_withRef_unoptimized(arg0, arg1); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md index 0bbfc967567..937af490f1d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md @@ -22,14 +22,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { memo } from "react"; import { Stringify } from "shared-runtime"; export default memo(Foo); -const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); -function _Foo_optimized(t0) { +const isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function Foo_optimized(t0) { "use memo"; const $ = _c(3); const { prop1, prop2 } = t0; @@ -44,13 +44,13 @@ function _Foo_optimized(t0) { } return t1; } -function _Foo_unoptimized({ prop1, prop2 }) { +function Foo_unoptimized({ prop1, prop2 }) { "use memo"; return ; } function Foo(arg0) { - if (_isForgetEnabled_Fixtures_result) return _Foo_optimized(arg0); - else return _Foo_unoptimized(arg0); + if (isForgetEnabled_Fixtures_result) return Foo_optimized(arg0); + else return Foo_unoptimized(arg0); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md index 26c6e510d14..f81680970c4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md @@ -23,8 +23,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { memo } from "react"; type Props = React.ElementConfig; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md index 4931b87b017..aff21ee0bd3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md @@ -13,8 +13,8 @@ export default React.forwardRef(function notNamedLikeAComponent(props) { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(infer) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(infer) import React from "react"; export default React.forwardRef( isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md index 47b58453ca0..cdf9acc2b65 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md @@ -23,8 +23,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import * as React from "react"; let Foo; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md index b3dc5011f9c..1300bc8982d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md @@ -19,8 +19,8 @@ export default props => ( ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md index ea2a1c15412..c8905939391 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md @@ -24,8 +24,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md index cc5195edb22..74be7ac5666 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md @@ -26,8 +26,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md index 5f18d98491f..267da62b89a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md @@ -31,8 +31,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import * as React from "react"; /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md new file mode 100644 index 00000000000..c6e179a6e7f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md @@ -0,0 +1,66 @@ + +## Input + +```javascript +// @lowerContextAccess @enableEmitHookGuards +function App() { + const {foo} = useContext(MyContext); + const {bar} = useContext(MyContext); + return ; +} + +``` + +## Code + +```javascript +import { + $dispatcherGuard, + useContext_withSelector, +} from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @lowerContextAccess @enableEmitHookGuards +function App() { + const $ = _c(3); + try { + $dispatcherGuard(0); + const { foo } = (function () { + try { + $dispatcherGuard(2); + return useContext_withSelector(MyContext, _temp); + } finally { + $dispatcherGuard(3); + } + })(); + const { bar } = (function () { + try { + $dispatcherGuard(2); + return useContext_withSelector(MyContext, _temp2); + } finally { + $dispatcherGuard(3); + } + })(); + let t0; + if ($[0] !== bar || $[1] !== foo) { + t0 = ; + $[0] = bar; + $[1] = foo; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; + } finally { + $dispatcherGuard(1); + } +} +function _temp2(t0) { + return [t0.bar]; +} +function _temp(t0) { + return [t0.foo]; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js new file mode 100644 index 00000000000..da881ea124a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js @@ -0,0 +1,6 @@ +// @lowerContextAccess @enableEmitHookGuards +function App() { + const {foo} = useContext(MyContext); + const {bar} = useContext(MyContext); + return ; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md index fecc28bb001..7ba4ee28115 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md @@ -43,8 +43,7 @@ function FireComponent(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) import { fire } from "react"; /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md index 8d8bc179a24..36146cff00f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md @@ -21,8 +21,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md index a335fea8867..de003f7007f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md @@ -30,8 +30,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md index 5767ff0746c..20260bd5e69 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md @@ -21,8 +21,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire @inferEffectDependencies +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @inferEffectDependencies import { fire, useEffect } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md new file mode 100644 index 00000000000..d94cce55884 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md @@ -0,0 +1,72 @@ + +## Input + +```javascript +// @enableFire @enableEmitHookGuards +import {fire} from 'react'; + +function Component(props) { + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return null; +} + +``` + +## Code + +```javascript +import { $dispatcherGuard } from "react-compiler-runtime"; +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @enableEmitHookGuards +import { fire } from "react"; + +function Component(props) { + const $ = _c(3); + try { + $dispatcherGuard(0); + const foo = _temp; + const t0 = (function () { + try { + $dispatcherGuard(2); + return useFire(foo); + } finally { + $dispatcherGuard(3); + } + })(); + let t1; + if ($[0] !== props || $[1] !== t0) { + t1 = () => { + t0(props); + }; + $[0] = props; + $[1] = t0; + $[2] = t1; + } else { + t1 = $[2]; + } + (function () { + try { + $dispatcherGuard(2); + return useEffect(t1); + } finally { + $dispatcherGuard(3); + } + })(); + return null; + } finally { + $dispatcherGuard(1); + } +} +function _temp(props_0) { + console.log(props_0); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js new file mode 100644 index 00000000000..bc0b1a2d7ce --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js @@ -0,0 +1,13 @@ +// @enableFire @enableEmitHookGuards +import {fire} from 'react'; + +function Component(props) { + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return null; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md index 02f39351712..796c4397eec 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md @@ -29,8 +29,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md index 1734ca3ab45..e528823550f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md @@ -22,8 +22,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md index 5a27845f079..c7ed50ceba2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md @@ -23,7 +23,6 @@ function Component(props, useDynamicHook) { ## Code ```javascript -import { $dispatcherGuard } from "react-compiler-runtime"; import { useFire } from "react/compiler-runtime"; import { useEffect, fire } from "react"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md index ae71f603932..e569536ad3a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md @@ -21,8 +21,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md index 9b689b31c7b..92dbf9843ad 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md @@ -26,8 +26,7 @@ function Component({bar, baz}) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(t0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/index.ts b/compiler/packages/babel-plugin-react-compiler/src/index.ts index fa330f5582f..3310581462d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/index.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/index.ts @@ -19,6 +19,7 @@ export { parsePluginOptions, OPT_OUT_DIRECTIVES, OPT_IN_DIRECTIVES, + ProgramContext, findDirectiveEnablingMemoization, findDirectiveDisablingMemoization, type CompilerPipelineValue,