diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index 2a117b4661..b758d7b024 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -102,14 +102,25 @@ export type PluginOptions = Partial<{ panicThreshold: PanicThresholdOptions; - /* + /** + * @deprecated + * * When enabled, Forget will continue statically analyzing and linting code, but skip over codegen * passes. * + * NOTE: ignored if `outputMode` is specified + * * Defaults to false */ noEmit: boolean; + /** + * If specified, overrides `noEmit` and controls the output mode of the compiler. + * + * Defaults to null + */ + outputMode: CompilerOutputMode | null; + /* * Determines the strategy for determining which functions to compile. Note that regardless of * which mode is enabled, a component can be opted out by adding the string literal @@ -212,6 +223,19 @@ const CompilationModeSchema = z.enum([ export type CompilationMode = z.infer; +const CompilerOutputModeSchema = z.enum([ + // Build optimized for SSR, with client features removed + 'ssr', + // Build optimized for the client, with auto memoization + 'client', + // Build optimized for the client without auto memo + 'client-no-memo', + // Lint mode, the output is unused but validations should run + 'lint', +]); + +export type CompilerOutputMode = z.infer; + /** * Represents 'events' that may occur during compilation. Events are only * recorded when a logger is set (through the config). @@ -293,6 +317,7 @@ export const defaultOptions: ParsedPluginOptions = { logger: null, gating: null, noEmit: false, + outputMode: null, dynamicGating: null, eslintSuppressionRules: null, flowSuppressions: true, 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 c01aceb6e8..68b609e7f8 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, ProgramContext} from '.'; +import {CompilerOutputMode, Logger, ProgramContext} from '.'; import { HIRFunction, ReactiveFunction, @@ -24,7 +24,6 @@ import { pruneUnusedLabelsHIR, } from '../HIR'; import { - CompilerMode, Environment, EnvironmentConfig, ReactFunctionType, @@ -120,7 +119,7 @@ function run( >, config: EnvironmentConfig, fnType: ReactFunctionType, - mode: CompilerMode, + mode: CompilerOutputMode, programContext: ProgramContext, logger: Logger | null, filename: string | null, @@ -170,7 +169,7 @@ function runWithEnvironment( validateUseMemo(hir).unwrap(); if ( - env.isInferredMemoEnabled && + env.enableDropManualMemoization && !env.config.enablePreserveExistingManualUseMemo && !env.config.disableMemoizationForDebugging && !env.config.enableChangeDetectionForDebugging @@ -206,7 +205,7 @@ function runWithEnvironment( inferTypes(hir); log({kind: 'hir', name: 'InferTypes', value: hir}); - if (env.isInferredMemoEnabled) { + if (env.enableValidations) { if (env.config.validateHooksUsage) { validateHooksUsage(hir).unwrap(); } @@ -232,13 +231,13 @@ function runWithEnvironment( const mutabilityAliasingErrors = inferMutationAliasingEffects(hir); log({kind: 'hir', name: 'InferMutationAliasingEffects', value: hir}); - if (env.isInferredMemoEnabled) { + if (env.enableValidations) { if (mutabilityAliasingErrors.isErr()) { throw mutabilityAliasingErrors.unwrapErr(); } } - if (env.config.enableOptimizeForSSR) { + if (env.outputMode === 'ssr') { optimizeForSSR(hir); log({kind: 'hir', name: 'OptimizeForSSR', value: hir}); } @@ -259,14 +258,14 @@ function runWithEnvironment( isFunctionExpression: false, }); log({kind: 'hir', name: 'InferMutationAliasingRanges', value: hir}); - if (env.isInferredMemoEnabled) { + if (env.enableValidations) { if (mutabilityAliasingRangeErrors.isErr()) { throw mutabilityAliasingRangeErrors.unwrapErr(); } validateLocalsNotReassignedAfterRender(hir); } - if (env.isInferredMemoEnabled) { + if (env.enableValidations) { if (env.config.assertValidMutableRanges) { assertValidMutableRanges(hir); } @@ -310,20 +309,18 @@ function runWithEnvironment( value: hir, }); - if (env.isInferredMemoEnabled) { - if (env.config.validateStaticComponents) { - env.logErrors(validateStaticComponents(hir)); - } + if (env.enableValidations && env.config.validateStaticComponents) { + env.logErrors(validateStaticComponents(hir)); + } + if (env.enableMemoization) { /** * Only create reactive scopes (which directly map to generated memo blocks) * if inferred memoization is enabled. This makes all later passes which * transform reactive-scope labeled instructions no-ops. */ - if (!env.config.enableOptimizeForSSR) { - inferReactiveScopeVariables(hir); - log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir}); - } + inferReactiveScopeVariables(hir); + log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir}); } const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir); @@ -588,7 +585,7 @@ export function compileFn( >, config: EnvironmentConfig, fnType: ReactFunctionType, - mode: CompilerMode, + mode: CompilerOutputMode, programContext: ProgramContext, logger: Logger | null, filename: string | null, 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 accecc91a2..7d12e05437 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -24,6 +24,7 @@ import { validateRestrictedImports, } from './Imports'; import { + CompilerOutputMode, CompilerReactTarget, ParsedPluginOptions, PluginOptions, @@ -421,9 +422,17 @@ export function compileProgram( ); const compiledFns: Array = []; + // outputMode takes precedence if specified + const outputMode: CompilerOutputMode = + pass.opts.outputMode ?? (pass.opts.noEmit ? 'lint' : 'client'); while (queue.length !== 0) { const current = queue.shift()!; - const compiled = processFn(current.fn, current.fnType, programContext); + const compiled = processFn( + current.fn, + current.fnType, + programContext, + outputMode, + ); if (compiled != null) { for (const outlined of compiled.outlined) { @@ -581,6 +590,7 @@ function processFn( fn: BabelFn, fnType: ReactFunctionType, programContext: ProgramContext, + outputMode: CompilerOutputMode, ): null | CodegenFunction { let directives: { optIn: t.Directive | null; @@ -616,18 +626,27 @@ function processFn( } let compiledFn: CodegenFunction; - const compileResult = tryCompileFunction(fn, fnType, programContext); + const compileResult = tryCompileFunction( + fn, + fnType, + programContext, + outputMode, + ); if (compileResult.kind === 'error') { if (directives.optOut != null) { logError(compileResult.error, programContext, fn.node.loc ?? null); } else { handleError(compileResult.error, programContext, fn.node.loc ?? null); } - const retryResult = retryCompileFunction(fn, fnType, programContext); - if (retryResult == null) { + if (outputMode === 'client') { + const retryResult = retryCompileFunction(fn, fnType, programContext); + if (retryResult == null) { + return null; + } + compiledFn = retryResult; + } else { return null; } - compiledFn = retryResult; } else { compiledFn = compileResult.compiledFn; } @@ -663,7 +682,7 @@ function processFn( if (programContext.hasModuleScopeOptOut) { return null; - } else if (programContext.opts.noEmit) { + } else if (programContext.opts.outputMode === 'lint') { /** * inferEffectDependencies + noEmit is currently only used for linting. In * this mode, add source locations for where the compiler *can* infer effect @@ -693,6 +712,7 @@ function tryCompileFunction( fn: BabelFn, fnType: ReactFunctionType, programContext: ProgramContext, + outputMode: CompilerOutputMode, ): | {kind: 'compile'; compiledFn: CodegenFunction} | {kind: 'error'; error: unknown} { @@ -719,7 +739,7 @@ function tryCompileFunction( fn, programContext.opts.environment, fnType, - 'all_features', + outputMode, programContext, programContext.opts.logger, programContext.filename, @@ -757,7 +777,7 @@ function retryCompileFunction( fn, environment, fnType, - 'no_inferred_memo', + 'client-no-memo', programContext, programContext.opts.logger, programContext.filename, 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 0e3654dcca..2a0266881b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -9,7 +9,7 @@ import * as t from '@babel/types'; import {ZodError, z} from 'zod/v4'; import {fromZodError} from 'zod-validation-error/v4'; import {CompilerError} from '../CompilerError'; -import {Logger, ProgramContext} from '../Entrypoint'; +import {CompilerOutputMode, Logger, ProgramContext} from '../Entrypoint'; import {Err, Ok, Result} from '../Utils/Result'; import { DEFAULT_GLOBALS, @@ -51,6 +51,7 @@ import {Scope as BabelScope, NodePath} from '@babel/traverse'; import {TypeSchema} from './TypeSchema'; import {FlowTypeEnv} from '../Flood/Types'; import {defaultModuleTypeProvider} from './DefaultModuleTypeProvider'; +import {assertExhaustive} from '../Utils/utils'; export const ReactElementSymbolSchema = z.object({ elementSymbol: z.union([ @@ -691,8 +692,6 @@ export const EnvironmentConfigSchema = z.object({ * by React to only execute in response to events, not during render. */ enableInferEventHandlers: z.boolean().default(false), - - enableOptimizeForSSR: z.boolean().default(false), }); export type EnvironmentConfig = z.infer; @@ -732,7 +731,7 @@ export class Environment { code: string | null; config: EnvironmentConfig; fnType: ReactFunctionType; - compilerMode: CompilerMode; + outputMode: CompilerOutputMode; programContext: ProgramContext; hasFireRewrite: boolean; hasInferredEffect: boolean; @@ -747,7 +746,7 @@ export class Environment { constructor( scope: BabelScope, fnType: ReactFunctionType, - compilerMode: CompilerMode, + outputMode: CompilerOutputMode, config: EnvironmentConfig, contextIdentifiers: Set, parentFunction: NodePath, // the outermost function being compiled @@ -758,7 +757,7 @@ export class Environment { ) { this.#scope = scope; this.fnType = fnType; - this.compilerMode = compilerMode; + this.outputMode = outputMode; this.config = config; this.filename = filename; this.code = code; @@ -854,8 +853,65 @@ export class Environment { return this.#flowTypeEnvironment; } - get isInferredMemoEnabled(): boolean { - return this.compilerMode !== 'no_inferred_memo'; + get enableDropManualMemoization(): boolean { + switch (this.outputMode) { + case 'lint': { + // linting drops to be more compatible with compiler analysis + return true; + } + case 'client': + case 'ssr': { + return true; + } + case 'client-no-memo': { + return false; + } + default: { + assertExhaustive( + this.outputMode, + `Unexpected output mode '${this.outputMode}'`, + ); + } + } + } + + get enableMemoization(): boolean { + switch (this.outputMode) { + case 'client': + case 'lint': { + // linting also enables memoization so that we can check if manual memoization is preserved + return true; + } + case 'ssr': + case 'client-no-memo': { + return false; + } + default: { + assertExhaustive( + this.outputMode, + `Unexpected output mode '${this.outputMode}'`, + ); + } + } + } + + get enableValidations(): boolean { + switch (this.outputMode) { + case 'client': + case 'lint': + case 'ssr': { + return true; + } + case 'client-no-memo': { + return false; + } + default: { + assertExhaustive( + this.outputMode, + `Unexpected output mode '${this.outputMode}'`, + ); + } + } } get nextIdentifierId(): IdentifierId { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts index b894eb2898..4a027b87b6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts @@ -2452,7 +2452,7 @@ function computeEffectsForLegacySignature( }), }); } - if (signature.knownIncompatible != null && state.env.isInferredMemoEnabled) { + if (signature.knownIncompatible != null && state.env.enableValidations) { const errors = new CompilerError(); errors.pushDiagnostic( CompilerDiagnostic.create({ diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/DeadCodeElimination.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/DeadCodeElimination.ts index 0dbc8c471b..8b251e9966 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/DeadCodeElimination.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/DeadCodeElimination.ts @@ -319,7 +319,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean { } case 'CallExpression': case 'MethodCall': { - if (state.env.config.enableOptimizeForSSR) { + if (state.env.outputMode === 'ssr') { const calleee = value.kind === 'CallExpression' ? value.callee : value.property; const hookKind = getHookKind(state.env, calleee.identifier); 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 f81c962edf..0ab7934a1a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -159,7 +159,7 @@ export function codegenFunction( const compiled = compileResult.unwrap(); const hookGuard = fn.env.config.enableEmitHookGuards; - if (hookGuard != null && fn.env.isInferredMemoEnabled) { + if (hookGuard != null && fn.env.outputMode === 'client') { compiled.body = t.blockStatement([ createHookGuard( hookGuard, @@ -259,7 +259,7 @@ export function codegenFunction( if ( emitInstrumentForget != null && fn.id != null && - fn.env.isInferredMemoEnabled + fn.env.outputMode === 'client' ) { /* * Technically, this is a conditional hook call. However, we expect @@ -591,7 +591,10 @@ function codegenBlockNoReset( } function wrapCacheDep(cx: Context, value: t.Expression): t.Expression { - if (cx.env.config.enableEmitFreeze != null && cx.env.isInferredMemoEnabled) { + if ( + cx.env.config.enableEmitFreeze != null && + cx.env.outputMode === 'client' + ) { const emitFreezeIdentifier = cx.env.programContext.addImportSpecifier( cx.env.config.enableEmitFreeze, ).name; @@ -1772,7 +1775,7 @@ function createCallExpression( } const hookGuard = env.config.enableEmitHookGuards; - if (hookGuard != null && isHook && env.isInferredMemoEnabled) { + if (hookGuard != null && isHook && env.outputMode === 'client') { const iife = t.functionExpression( null, [], diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.expect.md index 81ebd6dd9f..c00e78b6e7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @dynamicGating:{"source":"shared-runtime"} @noEmit +// @dynamicGating:{"source":"shared-runtime"} @outputMode:"lint" function Foo() { 'use memo if(getTrue)'; @@ -19,7 +19,7 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -// @dynamicGating:{"source":"shared-runtime"} @noEmit +// @dynamicGating:{"source":"shared-runtime"} @outputMode:"lint" function Foo() { "use memo if(getTrue)"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.js index 97cf777a55..901a1dd3ea 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.js @@ -1,4 +1,4 @@ -// @dynamicGating:{"source":"shared-runtime"} @noEmit +// @dynamicGating:{"source":"shared-runtime"} @outputMode:"lint" function Foo() { 'use memo if(getTrue)'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.expect.md deleted file mode 100644 index 55a6aa5c46..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.expect.md +++ /dev/null @@ -1,66 +0,0 @@ - -## Input - -```javascript -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly -import {print} from 'shared-runtime'; -import useEffectWrapper from 'useEffectWrapper'; -import {AUTODEPS} from 'react'; - -function Foo({propVal}) { - const arr = [propVal]; - useEffectWrapper(() => print(arr), AUTODEPS); - - const arr2 = []; - useEffectWrapper(() => arr2.push(propVal), AUTODEPS); - arr2.push(2); - return {arr, arr2}; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{propVal: 1}], - sequentialRenders: [{propVal: 1}, {propVal: 2}], -}; - -``` - -## Code - -```javascript -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly -import { print } from "shared-runtime"; -import useEffectWrapper from "useEffectWrapper"; -import { AUTODEPS } from "react"; - -function Foo({ propVal }) { - const arr = [propVal]; - useEffectWrapper(() => print(arr), AUTODEPS); - - const arr2 = []; - useEffectWrapper(() => arr2.push(propVal), AUTODEPS); - arr2.push(2); - return { arr, arr2 }; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{ propVal: 1 }], - sequentialRenders: [{ propVal: 1 }, { propVal: 2 }], -}; - -``` - -## Logs - -``` -{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook","details":[{"kind":"error","loc":{"start":{"line":12,"column":2,"index":372},"end":{"line":12,"column":6,"index":376},"filename":"retry-no-emit.ts","identifierName":"arr2"},"message":"value cannot be modified"}]}}} -{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":248},"end":{"line":8,"column":46,"index":292},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":8,"column":31,"index":277},"end":{"line":8,"column":34,"index":280},"filename":"retry-no-emit.ts","identifierName":"arr"}]} -{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":11,"column":2,"index":316},"end":{"line":11,"column":54,"index":368},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":11,"column":25,"index":339},"end":{"line":11,"column":29,"index":343},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":25,"index":339},"end":{"line":11,"column":29,"index":343},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":35,"index":349},"end":{"line":11,"column":42,"index":356},"filename":"retry-no-emit.ts","identifierName":"propVal"}]} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":195},"end":{"line":14,"column":1,"index":409},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0} -``` - -### Eval output -(kind: ok) {"arr":[1],"arr2":[2]} -{"arr":[2],"arr2":[2]} -logs: [[ 1 ],[ 2 ]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation--lint.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation--lint.expect.md new file mode 100644 index 0000000000..b8d213b637 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation--lint.expect.md @@ -0,0 +1,48 @@ + +## Input + +```javascript +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none" +import {print} from 'shared-runtime'; +import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; + +function Foo({propVal}) { + const arr = [propVal]; + useEffectWrapper(() => print(arr), AUTODEPS); + + const arr2 = []; + useEffectWrapper(() => arr2.push(propVal), AUTODEPS); + arr2.push(2); + return {arr, arr2}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{propVal: 1}], + sequentialRenders: [{propVal: 1}, {propVal: 2}], +}; + +``` + + +## Error + +``` +Found 1 error: + +Error: Cannot infer dependencies of this effect. This will break your build! + +To resolve, either pass a dependency array or fix reported compiler bailout diagnostics. + +error.infer-effect-deps-with-rule-violation--lint.ts:8:2 + 6 | function Foo({propVal}) { + 7 | const arr = [propVal]; +> 8 | useEffectWrapper(() => print(arr), AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot infer dependencies + 9 | + 10 | const arr2 = []; + 11 | useEffectWrapper(() => arr2.push(propVal), AUTODEPS); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation--lint.js similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation--lint.js index 692653b417..7df86cdfd2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation--lint.js @@ -1,4 +1,4 @@ -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none" import {print} from 'shared-runtime'; import useEffectWrapper from 'useEffectWrapper'; import {AUTODEPS} from 'react'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.expect.md new file mode 100644 index 0000000000..80d8af00e6 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.expect.md @@ -0,0 +1,49 @@ + +## Input + +```javascript +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none" +import {print} from 'shared-runtime'; +import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; + +function Foo({propVal}) { + 'use memo'; + const arr = [propVal]; + useEffectWrapper(() => print(arr), AUTODEPS); + + const arr2 = []; + useEffectWrapper(() => arr2.push(propVal), AUTODEPS); + arr2.push(2); + return {arr, arr2}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{propVal: 1}], + sequentialRenders: [{propVal: 1}, {propVal: 2}], +}; + +``` + + +## Error + +``` +Found 1 error: + +Error: Cannot infer dependencies of this effect. This will break your build! + +To resolve, either pass a dependency array or fix reported compiler bailout diagnostics. + +error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.ts:9:2 + 7 | 'use memo'; + 8 | const arr = [propVal]; +> 9 | useEffectWrapper(() => print(arr), AUTODEPS); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot infer dependencies + 10 | + 11 | const arr2 = []; + 12 | useEffectWrapper(() => arr2.push(propVal), AUTODEPS); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-opt-in--no-emit.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.js similarity index 85% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-opt-in--no-emit.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.js index e4ec1a545f..42bbf4c994 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-opt-in--no-emit.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.js @@ -1,7 +1,7 @@ -// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none" import {print} from 'shared-runtime'; -import {AUTODEPS} from 'react'; import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; function Foo({propVal}) { 'use memo'; @@ -11,7 +11,6 @@ function Foo({propVal}) { const arr2 = []; useEffectWrapper(() => arr2.push(propVal), AUTODEPS); arr2.push(2); - return {arr, arr2}; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.expect.md new file mode 100644 index 0000000000..47de4a1d19 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.expect.md @@ -0,0 +1,58 @@ + +## Input + +```javascript +// @inferEffectDependencies @panicThreshold:"none" +import {print} from 'shared-runtime'; +import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; + +function Foo({propVal}) { + const arr = [propVal]; + useEffectWrapper(() => print(arr), AUTODEPS); + + const arr2 = []; + useEffectWrapper(() => arr2.push(propVal), AUTODEPS); + arr2.push(2); + return {arr, arr2}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{propVal: 1}], + sequentialRenders: [{propVal: 1}, {propVal: 2}], +}; + +``` + +## Code + +```javascript +// @inferEffectDependencies @panicThreshold:"none" +import { print } from "shared-runtime"; +import useEffectWrapper from "useEffectWrapper"; +import { AUTODEPS } from "react"; + +function Foo(t0) { + const { propVal } = t0; + const arr = [propVal]; + useEffectWrapper(() => print(arr), [arr]); + + const arr2 = []; + useEffectWrapper(() => arr2.push(propVal), [arr2, propVal]); + arr2.push(2); + return { arr, arr2 }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ propVal: 1 }], + sequentialRenders: [{ propVal: 1 }, { propVal: 2 }], +}; + +``` + +### Eval output +(kind: ok) {"arr":[1],"arr2":[2]} +{"arr":[2],"arr2":[2]} +logs: [[ 1 ],[ 2 ]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.js similarity index 81% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.js index 2815a9a28d..6cca9a833d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.js @@ -1,4 +1,4 @@ -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel +// @inferEffectDependencies @panicThreshold:"none" import {print} from 'shared-runtime'; import useEffectWrapper from 'useEffectWrapper'; import {AUTODEPS} from 'react'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-opt-in--no-emit.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation-use-memo-opt-in--compile.expect.md similarity index 77% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-opt-in--no-emit.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation-use-memo-opt-in--compile.expect.md index a2d5610acc..610ad34890 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-opt-in--no-emit.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation-use-memo-opt-in--compile.expect.md @@ -2,10 +2,10 @@ ## Input ```javascript -// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit +// @inferEffectDependencies @panicThreshold:"none" import {print} from 'shared-runtime'; -import {AUTODEPS} from 'react'; import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; function Foo({propVal}) { 'use memo'; @@ -15,7 +15,6 @@ function Foo({propVal}) { const arr2 = []; useEffectWrapper(() => arr2.push(propVal), AUTODEPS); arr2.push(2); - return {arr, arr2}; } @@ -30,20 +29,21 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit +// @inferEffectDependencies @panicThreshold:"none" import { print } from "shared-runtime"; -import { AUTODEPS } from "react"; import useEffectWrapper from "useEffectWrapper"; +import { AUTODEPS } from "react"; -function Foo({ propVal }) { +function Foo(t0) { "use memo"; + const { propVal } = t0; + const arr = [propVal]; - useEffectWrapper(() => print(arr), AUTODEPS); + useEffectWrapper(() => print(arr), [arr]); const arr2 = []; - useEffectWrapper(() => arr2.push(propVal), AUTODEPS); + useEffectWrapper(() => arr2.push(propVal), [arr2, propVal]); arr2.push(2); - return { arr, arr2 }; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation-use-memo-opt-in--compile.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation-use-memo-opt-in--compile.js new file mode 100644 index 0000000000..efa5db1940 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation-use-memo-opt-in--compile.js @@ -0,0 +1,21 @@ +// @inferEffectDependencies @panicThreshold:"none" +import {print} from 'shared-runtime'; +import useEffectWrapper from 'useEffectWrapper'; +import {AUTODEPS} from 'react'; + +function Foo({propVal}) { + 'use memo'; + const arr = [propVal]; + useEffectWrapper(() => print(arr), AUTODEPS); + + const arr2 = []; + useEffectWrapper(() => arr2.push(propVal), AUTODEPS); + arr2.push(2); + return {arr, arr2}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{propVal: 1}], + sequentialRenders: [{propVal: 1}, {propVal: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/no-emit-lint-repro.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/no-emit-lint-repro.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.expect.md index 7c8142f436..1d767ce3db 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/no-emit-lint-repro.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @inferEffectDependencies @noEmit +// @inferEffectDependencies @outputMode:"lint" import {print} from 'shared-runtime'; import useEffectWrapper from 'useEffectWrapper'; import {AUTODEPS} from 'react'; @@ -17,7 +17,7 @@ function ReactiveVariable({propVal}) { ## Code ```javascript -// @inferEffectDependencies @noEmit +// @inferEffectDependencies @outputMode:"lint" import { print } from "shared-runtime"; import useEffectWrapper from "useEffectWrapper"; import { AUTODEPS } from "react"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/no-emit-lint-repro.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.js similarity index 83% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/no-emit-lint-repro.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.js index 0df8291afd..011cc535c2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/no-emit-lint-repro.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.js @@ -1,4 +1,4 @@ -// @inferEffectDependencies @noEmit +// @inferEffectDependencies @outputMode:"lint" import {print} from 'shared-runtime'; import useEffectWrapper from 'useEffectWrapper'; import {AUTODEPS} from 'react'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.expect.md deleted file mode 100644 index 9529a5c31d..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.expect.md +++ /dev/null @@ -1,66 +0,0 @@ - -## Input - -```javascript -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel -import {print} from 'shared-runtime'; -import useEffectWrapper from 'useEffectWrapper'; -import {AUTODEPS} from 'react'; - -function Foo({propVal}) { - const arr = [propVal]; - useEffectWrapper(() => print(arr), AUTODEPS); - - const arr2 = []; - useEffectWrapper(() => arr2.push(propVal), AUTODEPS); - arr2.push(2); - return {arr, arr2}; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{propVal: 1}], - sequentialRenders: [{propVal: 1}, {propVal: 2}], -}; - -``` - -## Code - -```javascript -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel -import { print } from "shared-runtime"; -import useEffectWrapper from "useEffectWrapper"; -import { AUTODEPS } from "react"; - -function Foo({ propVal }) { - const arr = [propVal]; - useEffectWrapper(() => print(arr), AUTODEPS); - - const arr2 = []; - useEffectWrapper(() => arr2.push(propVal), AUTODEPS); - arr2.push(2); - return { arr, arr2 }; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{ propVal: 1 }], - sequentialRenders: [{ propVal: 1 }, { propVal: 2 }], -}; - -``` - -## Logs - -``` -{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":227},"end":{"line":14,"column":1,"index":441},"filename":"retry-no-emit.ts"},"detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook","details":[{"kind":"error","loc":{"start":{"line":12,"column":2,"index":404},"end":{"line":12,"column":6,"index":408},"filename":"retry-no-emit.ts","identifierName":"arr2"},"message":"value cannot be modified"}]}}} -{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":280},"end":{"line":8,"column":46,"index":324},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":8,"column":31,"index":309},"end":{"line":8,"column":34,"index":312},"filename":"retry-no-emit.ts","identifierName":"arr"}]} -{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":11,"column":2,"index":348},"end":{"line":11,"column":54,"index":400},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":11,"column":25,"index":371},"end":{"line":11,"column":29,"index":375},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":25,"index":371},"end":{"line":11,"column":29,"index":375},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":11,"column":35,"index":381},"end":{"line":11,"column":42,"index":388},"filename":"retry-no-emit.ts","identifierName":"propVal"}]} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":227},"end":{"line":14,"column":1,"index":441},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0} -``` - -### Eval output -(kind: ok) {"arr":[1],"arr2":[2]} -{"arr":[2],"arr2":[2]} -logs: [[ 1 ],[ 2 ]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/optimize-ssr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/optimize-ssr.expect.md index 3508aab535..48a0a92be7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/optimize-ssr.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/optimize-ssr.expect.md @@ -20,10 +20,40 @@ function Component() { ## Code ```javascript -// @enableOptimizeForSSR +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR function Component() { - const state = 0; - return ; + const $ = _c(4); + const [state, setState] = useState(0); + const ref = useRef(null); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = (e) => { + setState(e.target.value); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const onChange = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + log(ref.current.value); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + let t2; + if ($[2] !== state) { + t2 = ; + $[2] = state; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-setState.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-setState.expect.md index 0aeb890c26..80884d8453 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-setState.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-setState.expect.md @@ -22,12 +22,40 @@ function Component() { ## Code ```javascript -// @enableOptimizeForSSR +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR function Component() { - const state = 0; + const $ = _c(4); + const [state, setState] = useState(0); const ref = useRef(null); - const onChange = undefined; - return ; + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = (e) => { + setState(e.target.value); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const onChange = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + log(ref.current.value); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + let t2; + if ($[2] !== state) { + t2 = ; + $[2] = state; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-startTransition.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-startTransition.expect.md index 53cf10a678..ccfdccb288 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-startTransition.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-startTransition.expect.md @@ -25,13 +25,43 @@ function Component() { ## Code ```javascript -// @enableOptimizeForSSR +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR function Component() { - useTransition(); - const state = 0; + const $ = _c(4); + const [, startTransition] = useTransition(); + const [state, setState] = useState(0); const ref = useRef(null); - const onChange = undefined; - return ; + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = (e) => { + startTransition(() => { + setState.call(null, e.target.value); + }); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const onChange = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + log(ref.current.value); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + let t2; + if ($[2] !== state) { + t2 = ; + $[2] = state; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer-initializer.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer-initializer.expect.md index ead89e1288..780e1f3963 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer-initializer.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer-initializer.expect.md @@ -25,7 +25,7 @@ function Component() { ## Code ```javascript -// @enableOptimizeForSSR +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR import { useReducer } from "react"; @@ -34,8 +34,41 @@ const initializer = (x) => { }; function Component() { - const state = initializer(0); - return ; + const $ = _c(4); + const [state, dispatch] = useReducer(_temp, 0, initializer); + const ref = useRef(null); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = (e) => { + dispatch(e.target.value); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const onChange = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + log(ref.current.value); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + let t2; + if ($[2] !== state) { + t2 = ; + $[2] = state; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} +function _temp(_, next) { + return next; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer.expect.md index 2bf6a02f0b..3c48b27f86 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer.expect.md @@ -23,13 +23,46 @@ function Component() { ## Code ```javascript -// @enableOptimizeForSSR +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR import { useReducer } from "react"; function Component() { - const state = 0; - return ; + const $ = _c(4); + const [state, dispatch] = useReducer(_temp, 0); + const ref = useRef(null); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = (e) => { + dispatch(e.target.value); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const onChange = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + log(ref.current.value); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + let t2; + if ($[2] !== state) { + t2 = ; + $[2] = state; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} +function _temp(_, next) { + return next; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.expect.md index c47501945b..2068971957 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @noEmit +// @outputMode:"lint" function Foo() { 'use memo'; @@ -19,7 +19,7 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -// @noEmit +// @outputMode:"lint" function Foo() { "use memo"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.js index 04ec880761..b12668f15a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.js @@ -1,4 +1,4 @@ -// @noEmit +// @outputMode:"lint" function Foo() { 'use memo'; diff --git a/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts b/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts index 419dc3841c..aa55c64237 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts @@ -21,7 +21,7 @@ import {isDeepStrictEqual} from 'util'; import type {ParseResult} from '@babel/parser'; const COMPILER_OPTIONS: PluginOptions = { - noEmit: true, + outputMode: 'lint', panicThreshold: 'none', // Don't emit errors on Flow suppressions--Flow already gave a signal flowSuppressions: false, diff --git a/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts b/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts index 02fd68badf..f2062496fe 100644 --- a/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts +++ b/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts @@ -22,7 +22,7 @@ import {isDeepStrictEqual} from 'util'; import type {ParseResult} from '@babel/parser'; const COMPILER_OPTIONS: PluginOptions = { - noEmit: true, + outputMode: 'lint', panicThreshold: 'none', // Don't emit errors on Flow suppressions--Flow already gave a signal flowSuppressions: false,