-
Notifications
You must be signed in to change notification settings - Fork 47.9k
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
[compiler] Run compiler pipeline on 'use no forget' #30720
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@@ -102,7 +103,7 @@ export type CompileResult = { | |||
compiledFn: CodegenFunction; | |||
}; | |||
|
|||
function handleError( | |||
function logError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split this up so we can decide to only log in some cases instead of throwing (eg a file contains at least one function that was compiled although there were opt outs + errors in others)
// Opt-outs disable compilation regardless of mode | ||
const useNoForget = findDirectiveDisablingMemoization( | ||
fn.node.body.directives, | ||
pass.opts, | ||
); | ||
if (useNoForget != null) { | ||
pass.opts.logger?.logEvent(pass.filename, { | ||
kind: 'CompileError', | ||
fnLoc: fn.node.body.loc ?? null, | ||
detail: { | ||
severity: ErrorSeverity.Todo, | ||
reason: 'Skipped due to "use no forget" directive.', | ||
loc: useNoForget.loc ?? null, | ||
suggestions: null, | ||
}, | ||
}); | ||
return null; | ||
} | ||
// Otherwise opt-ins enable compilation regardless of mode | ||
if (findDirectiveEnablingMemoization(fn.node.body.directives) != null) { | ||
if (findDirectiveEnablingMemoization(fn.node.body.directives).length > 0) | ||
return getComponentOrHookLike(fn, hookPattern) ?? 'Other'; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic really shouldn't have been in this function which is responsible for categorization of the function (eg "Component" or "Hook". This has been moved into processFn instead.
// Top level "use no forget", skip this file entirely | ||
const useNoForget = findDirectiveDisablingMemoization( | ||
program.node.directives, | ||
pass.opts, | ||
); | ||
if (useNoForget != null) { | ||
pass.opts.logger?.logEvent(pass.filename, { | ||
kind: 'CompileError', | ||
fnLoc: null, | ||
detail: { | ||
severity: ErrorSeverity.Todo, | ||
reason: 'Skipped due to "use no forget" directive.', | ||
loc: useNoForget.loc ?? null, | ||
suggestions: null, | ||
}, | ||
}); | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously this would skip visiting the function at all, so no validations could be checked.
const optInDirectives = findDirectiveEnablingMemoization( | ||
fn.node.body.directives, | ||
); | ||
const optOutDirectives = findDirectiveDisablingMemoization( | ||
fn.node.body.directives, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could probably just calculate these once at the top of processFn, then reference the result here and above in the error case?
``` | ||
7 | 'use no forget'; | ||
8 | const ref = useRef(null); | ||
> 9 | // eslint-disable-next-line react-hooks/rules-of-hooks | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. eslint-disable-next-line react-hooks/rules-of-hooks (9:9) | ||
10 | ref.current = 'bad'; | ||
11 | return <button ref={ref} />; | ||
12 | } | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, shouldn't this not be reported since the component is opted out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. eslint-disable-next-line react-hooks/rules-of-hooks (6:6) | ||
7 | ref.current = 'bad'; | ||
8 | return <button ref={ref} />; | ||
9 | } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
if (fn.node.body.type === 'BlockStatement') { | ||
const optOutDirectives = findDirectiveDisablingMemoization( | ||
fn.node.body.directives, | ||
); | ||
if (optOutDirectives.length > 0) { | ||
logError(err, pass, fn.node.loc ?? null); | ||
return null; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think you need similar logic to this above on https://github.com/facebook/react/pull/30720/files#diff-def62b77d903a117d9a493b69a884f79317eb8c8beac025622265c99f1c7189fR403 (ugh, why doesn't GH let you comment on lines that weren't changed by a PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh yeah, thanks for catching this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice nice
This PR updates the babel plugin to continue the compilation pipeline as normal on components/hooks that have been opted out using a directive. Instead, we no longer emit the compiled function when the directive is present. Previously, we would skip over the entire pipeline. By continuing to enter the pipeline, we'll be able to detect if there are unused directives. The end result is: - (no change) 'use forget' will always opt into compilation - (new) 'use no forget' will opt out of compilation but continue to log errors without throwing them. This means that a Program containing multiple functions (some of which are opted out) will continue to compile correctly ghstack-source-id: 5bd85df2f81350cb2c1998a8761b8ed3fec32a40 Pull Request resolved: #30720
**breaking change for canary users: Bumps peer dependency of React from `19.0.0-rc-1eaccd82-20240816` to `19.0.0-rc-eb3ad065-20240822`** No changes required in Next.js it seems. [diff facebook/react@1eaccd82...eb3ad065](facebook/react@1eaccd8...eb3ad06) <details> <summary>React upstream changes</summary> - facebook/react#30761 - facebook/react#30779 - facebook/react#30775 - facebook/react#30770 - facebook/react#30756 - facebook/react#30755 - facebook/react#30768 - facebook/react#30760 - facebook/react#30732 - facebook/react#30757 - facebook/react#30750 - facebook/react#30751 - facebook/react#30753 - facebook/react#30740 - facebook/react#30748 - facebook/react#30746 - facebook/react#30747 - facebook/react#30731 - facebook/react#30725 - facebook/react#30741 - facebook/react#30730 - facebook/react#30726 - facebook/react#30717 - facebook/react#30729 - facebook/react#30721 - facebook/react#30720 - facebook/react#30705 </details>
Stack from ghstack (oldest at bottom):
This PR updates the babel plugin to continue the compilation pipeline as
normal on components/hooks that have been opted out using a directive.
Instead, we no longer emit the compiled function when the directive is
present.
Previously, we would skip over the entire pipeline. By continuing to
enter the pipeline, we'll be able to detect if there are unused
directives.
The end result is:
errors without throwing them. This means that a Program containing
multiple functions (some of which are opted out) will continue to
compile correctly