Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type ProgramContextOptions = {
opts: PluginOptions;
filename: string | null;
code: string | null;
hasModuleScopeOptOut: boolean;
};
export class ProgramContext {
/**
Expand All @@ -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
Expand All @@ -94,13 +96,15 @@ export class ProgramContext {
opts,
filename,
code,
hasModuleScopeOptOut,
}: ProgramContextOptions) {
this.scope = program.scope;
this.opts = opts;
this.filename = filename;
this.code = code;
this.reactRuntimeModule = getReactCompilerRuntimeModule(opts.target);
this.suppressions = suppressions;
this.hasModuleScopeOptOut = hasModuleScopeOptOut;
}

isHookName(name: string): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 &&
Expand All @@ -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) {
/**
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ function Foo() {
function Foo() {
return <button onClick={() => alert("hello!")}>Click me!</button>;
}
function _temp() {
return alert("hello!");
}

```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Loading