Compare commits

...

1 Commits

Author SHA1 Message Date
Mofei Zhang
1b658717e9 [compiler][entrypoint] Fix edgecases for noEmit and opt-outs
Title
2025-05-09 13:30:42 -04:00
5 changed files with 39 additions and 35 deletions

View File

@@ -59,6 +59,7 @@ type ProgramContextOptions = {
opts: PluginOptions;
filename: string | null;
code: string | null;
hasModuleScopeOptOut: boolean;
};
export class ProgramContext {
/**
@@ -70,6 +71,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
@@ -94,6 +96,7 @@ export class ProgramContext {
opts,
filename,
code,
hasModuleScopeOptOut,
}: ProgramContextOptions) {
this.scope = program.scope;
this.opts = opts;
@@ -101,6 +104,7 @@ export class ProgramContext {
this.code = code;
this.reactRuntimeModule = getReactCompilerRuntimeModule(opts.target);
this.suppressions = suppressions;
this.hasModuleScopeOptOut = hasModuleScopeOptOut;
}
isHookName(name: string): boolean {

View File

@@ -325,6 +325,8 @@ export function compileProgram(
filename: pass.filename,
code: pass.code,
suppressions,
hasModuleScopeOptOut:
findDirectiveDisablingMemoization(program.node.directives) != null,
});
const queue: Array<CompileSource> = findFunctionsToCompile(
@@ -368,7 +370,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;
}
@@ -491,9 +505,10 @@ function processFn(
}
/**
* Otherwise if 'use no forget/memo' is present, we still run the code through the compiler
* for validation but we don't mutate the babel AST. This allows us to flag if there is an
* unused 'use no forget/memo' directive.
* If 'use no forget/memo' is present and we still ran the code through the
* compiler for validation, log a skip event and don't mutate the babel AST.
* This allows us to flag if there is an unused 'use no forget/memo'
* directive.
*/
if (
programContext.opts.ignoreUseNoForget === false &&
@@ -518,16 +533,7 @@ 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.
*/
if (programContext.hasModuleScopeOptOut) {
return null;
} else if (programContext.opts.noEmit) {
/**
@@ -541,6 +547,15 @@ function processFn(
}
}
return null;
} else if (
programContext.opts.compilationMode === 'annotation' &&
directives.optIn == null
) {
/**
* 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;
}

View File

@@ -33,16 +33,15 @@ export const FIXTURE_ENTRYPOINT = {
import { print } from "shared-runtime";
import useEffectWrapper from "useEffectWrapper";
function Foo(t0) {
function Foo({ propVal }) {
"use memo";
const { propVal } = t0;
const arr = [propVal];
useEffectWrapper(() => print(arr), [arr]);
useEffectWrapper(() => print(arr));
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal), [arr2, propVal]);
useEffectWrapper(() => arr2.push(propVal));
arr2.push(2);
return { arr, arr2 };
}

View File

@@ -20,9 +20,6 @@ function Foo() {
function Foo() {
return <button onClick={() => alert("hello!")}>Click me!</button>;
}
function _temp() {
return alert("hello!");
}
```

View File

@@ -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 = {