[compiler][entrypoint] Fix edgecases for noEmit and opt-outs

Title
This commit is contained in:
Mofei Zhang
2025-05-08 11:27:18 -04:00
parent 3cdf44994e
commit d5358f8af6
4 changed files with 40 additions and 34 deletions
@@ -19,7 +19,11 @@ import {getOrInsertWith} from '../Utils/utils';
import {ExternalFunction, isHookName} from '../HIR/Environment';
import {Err, Ok, Result} from '../Utils/Result';
import {LoggerEvent, PluginOptions} from './Options';
import {BabelFn, getReactCompilerRuntimeModule} from './Program';
import {
BabelFn,
findDirectiveDisablingMemoization,
getReactCompilerRuntimeModule,
} from './Program';
import {SuppressionRange} from './Suppression';
export function validateRestrictedImports(
@@ -70,6 +74,7 @@ export class ProgramContext {
code: string | null;
reactRuntimeModule: string;
suppressions: Array<SuppressionRange>;
hasModuleScopeOptOut: boolean;
/*
* This is a hack to work around what seems to be a Babel bug. Babel doesn't
@@ -101,6 +106,8 @@ export class ProgramContext {
this.code = code;
this.reactRuntimeModule = getReactCompilerRuntimeModule(opts.target);
this.suppressions = suppressions;
this.hasModuleScopeOptOut =
findDirectiveDisablingMemoization(program.node.directives) != null;
}
isHookName(name: string): boolean {
@@ -368,7 +368,19 @@ export function compileProgram(
}
// Avoid modifying the program if we find a program level opt-out
if (findDirectiveDisablingMemoization(program.node.directives) != null) {
if (programContext.hasModuleScopeOptOut) {
if (compiledFns.length > 0) {
const error = new CompilerError();
error.pushErrorDetail(
new CompilerErrorDetail({
reason:
'Unexpected compiled functions when module scope opt-out is present',
severity: ErrorSeverity.Invariant,
loc: null,
}),
);
handleError(error, programContext, null);
}
return null;
}
@@ -513,21 +525,6 @@ function processFn(
prunedMemoValues: compiledFn.prunedMemoValues,
});
/**
* Always compile functions with opt in directives.
*/
if (directives.optIn != null) {
return compiledFn;
} else if (programContext.opts.compilationMode === 'annotation') {
/**
* If no opt-in directive is found and the compiler is configured in
* annotation mode, don't insert the compiled function.
*/
return null;
} else if (!programContext.opts.noEmit) {
return compiledFn;
}
/**
* inferEffectDependencies + noEmit is currently only used for linting. In
* this mode, add source locations for where the compiler *can* infer effect
@@ -538,7 +535,23 @@ function processFn(
programContext.inferredEffectLocations.add(loc);
}
}
return null;
/**
* Always compile functions with opt in directives.
*/
if (programContext.hasModuleScopeOptOut || programContext.opts.noEmit) {
return null;
} else if (directives.optIn != null) {
return compiledFn;
} else if (programContext.opts.compilationMode === 'annotation') {
/**
* If no opt-in directive is found and the compiler is configured in
* annotation mode, don't insert the compiled function.
*/
return null;
} else {
return compiledFn;
}
}
function tryCompileFunction(
@@ -20,9 +20,6 @@ function Foo() {
function Foo() {
return <button onClick={() => alert("hello!")}>Click me!</button>;
}
function _temp() {
return alert("hello!");
}
```
@@ -19,22 +19,11 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @noEmit
// @noEmit
function Foo() {
"use memo";
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <button onClick={_temp}>Click me!</button>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
function _temp() {
return alert("hello!");
return <button onClick={() => alert("hello!")}>Click me!</button>;
}
export const FIXTURE_ENTRYPOINT = {