@samitouri / QOS-React / commits / 50e7ec8a69

[compiler] Deprecate noEmit, add outputMode (#35112)

This deprecates the `noEmit: boolean` flag and adds `outputMode: 'client' | 'client-no-memo' | 'ssr' | 'lint'` as the replacement. OutputMode defaults to null and takes precedence if specified, otherwise we use 'client' mode for noEmit=false and 'lint' mode for noEmit=true. Key points: * Retrying failed compilation switches from 'client' mode to 'client-no-memo' * Validations are enabled behind Environment.proto.shouldEnableValidations, enabled for all modes except 'client-no-memo'. Similar for dropping manual memoization. * OptimizeSSR is now gated by the outputMode==='ssr', not a feature flag * Creation of reactive scopes, and related codegen logic, is now gated by outputMode==='client'

Joseph Savona committed Nov 20, 2025 at 15:12 UTC 50e7ec8a694072fd6fcd52182df8a75211bf084d
30 files changed +514 -216
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts
+26 -1
@@ -102,14 +102,25 @@ export type PluginOptions = Partial<{
102
103 panicThreshold: PanicThresholdOptions;
104
105 - /*
105 + /**
106 + * @deprecated
107 + *
108 * When enabled, Forget will continue statically analyzing and linting code, but skip over codegen
109 * passes.
110 *
111 + * NOTE: ignored if `outputMode` is specified
112 + *
113 * Defaults to false
114 */
115 noEmit: boolean;
116
117 + /**
118 + * If specified, overrides `noEmit` and controls the output mode of the compiler.
119 + *
120 + * Defaults to null
121 + */
122 + outputMode: CompilerOutputMode | null;
123 +
124 /*
125 * Determines the strategy for determining which functions to compile. Note that regardless of
126 * which mode is enabled, a component can be opted out by adding the string literal
@@ -212,6 +223,19 @@ const CompilationModeSchema = z.enum([
223
224 export type CompilationMode = z.infer<typeof CompilationModeSchema>;
225
226 +const CompilerOutputModeSchema = z.enum([
227 + // Build optimized for SSR, with client features removed
228 + 'ssr',
229 + // Build optimized for the client, with auto memoization
230 + 'client',
231 + // Build optimized for the client without auto memo
232 + 'client-no-memo',
233 + // Lint mode, the output is unused but validations should run
234 + 'lint',
235 +]);
236 +
237 +export type CompilerOutputMode = z.infer<typeof CompilerOutputModeSchema>;
238 +
239 /**
240 * Represents 'events' that may occur during compilation. Events are only
241 * recorded when a logger is set (through the config).
@@ -293,6 +317,7 @@ export const defaultOptions: ParsedPluginOptions = {
317 logger: null,
318 gating: null,
319 noEmit: false,
320 + outputMode: null,
321 dynamicGating: null,
322 eslintSuppressionRules: null,
323 flowSuppressions: true,
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+15 -18
@@ -8,7 +8,7 @@
8 import {NodePath} from '@babel/traverse';
9 import * as t from '@babel/types';
10 import prettyFormat from 'pretty-format';
11 -import {Logger, ProgramContext} from '.';
11 +import {CompilerOutputMode, Logger, ProgramContext} from '.';
12 import {
13 HIRFunction,
14 ReactiveFunction,
@@ -24,7 +24,6 @@ import {
24 pruneUnusedLabelsHIR,
25 } from '../HIR';
26 import {
27 - CompilerMode,
27 Environment,
28 EnvironmentConfig,
29 ReactFunctionType,
@@ -120,7 +119,7 @@ function run(
119 >,
120 config: EnvironmentConfig,
121 fnType: ReactFunctionType,
123 - mode: CompilerMode,
122 + mode: CompilerOutputMode,
123 programContext: ProgramContext,
124 logger: Logger | null,
125 filename: string | null,
@@ -170,7 +169,7 @@ function runWithEnvironment(
169 validateUseMemo(hir).unwrap();
170
171 if (
173 - env.isInferredMemoEnabled &&
172 + env.enableDropManualMemoization &&
173 !env.config.enablePreserveExistingManualUseMemo &&
174 !env.config.disableMemoizationForDebugging &&
175 !env.config.enableChangeDetectionForDebugging
@@ -206,7 +205,7 @@ function runWithEnvironment(
205 inferTypes(hir);
206 log({kind: 'hir', name: 'InferTypes', value: hir});
207
209 - if (env.isInferredMemoEnabled) {
208 + if (env.enableValidations) {
209 if (env.config.validateHooksUsage) {
210 validateHooksUsage(hir).unwrap();
211 }
@@ -232,13 +231,13 @@ function runWithEnvironment(
231
232 const mutabilityAliasingErrors = inferMutationAliasingEffects(hir);
233 log({kind: 'hir', name: 'InferMutationAliasingEffects', value: hir});
235 - if (env.isInferredMemoEnabled) {
234 + if (env.enableValidations) {
235 if (mutabilityAliasingErrors.isErr()) {
236 throw mutabilityAliasingErrors.unwrapErr();
237 }
238 }
239
241 - if (env.config.enableOptimizeForSSR) {
240 + if (env.outputMode === 'ssr') {
241 optimizeForSSR(hir);
242 log({kind: 'hir', name: 'OptimizeForSSR', value: hir});
243 }
@@ -259,14 +258,14 @@ function runWithEnvironment(
258 isFunctionExpression: false,
259 });
260 log({kind: 'hir', name: 'InferMutationAliasingRanges', value: hir});
262 - if (env.isInferredMemoEnabled) {
261 + if (env.enableValidations) {
262 if (mutabilityAliasingRangeErrors.isErr()) {
263 throw mutabilityAliasingRangeErrors.unwrapErr();
264 }
265 validateLocalsNotReassignedAfterRender(hir);
266 }
267
269 - if (env.isInferredMemoEnabled) {
268 + if (env.enableValidations) {
269 if (env.config.assertValidMutableRanges) {
270 assertValidMutableRanges(hir);
271 }
@@ -310,20 +309,18 @@ function runWithEnvironment(
309 value: hir,
310 });
311
313 - if (env.isInferredMemoEnabled) {
314 - if (env.config.validateStaticComponents) {
315 - env.logErrors(validateStaticComponents(hir));
316 - }
312 + if (env.enableValidations && env.config.validateStaticComponents) {
313 + env.logErrors(validateStaticComponents(hir));
314 + }
315
316 + if (env.enableMemoization) {
317 /**
318 * Only create reactive scopes (which directly map to generated memo blocks)
319 * if inferred memoization is enabled. This makes all later passes which
320 * transform reactive-scope labeled instructions no-ops.
321 */
323 - if (!env.config.enableOptimizeForSSR) {
324 - inferReactiveScopeVariables(hir);
325 - log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
326 - }
322 + inferReactiveScopeVariables(hir);
323 + log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
324 }
325
326 const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
@@ -588,7 +585,7 @@ export function compileFn(
585 >,
586 config: EnvironmentConfig,
587 fnType: ReactFunctionType,
591 - mode: CompilerMode,
588 + mode: CompilerOutputMode,
589 programContext: ProgramContext,
590 logger: Logger | null,
591 filename: string | null,
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+28 -8
@@ -24,6 +24,7 @@ import {
24 validateRestrictedImports,
25 } from './Imports';
26 import {
27 + CompilerOutputMode,
28 CompilerReactTarget,
29 ParsedPluginOptions,
30 PluginOptions,
@@ -421,9 +422,17 @@ export function compileProgram(
422 );
423 const compiledFns: Array<CompileResult> = [];
424
425 + // outputMode takes precedence if specified
426 + const outputMode: CompilerOutputMode =
427 + pass.opts.outputMode ?? (pass.opts.noEmit ? 'lint' : 'client');
428 while (queue.length !== 0) {
429 const current = queue.shift()!;
426 - const compiled = processFn(current.fn, current.fnType, programContext);
430 + const compiled = processFn(
431 + current.fn,
432 + current.fnType,
433 + programContext,
434 + outputMode,
435 + );
436
437 if (compiled != null) {
438 for (const outlined of compiled.outlined) {
@@ -581,6 +590,7 @@ function processFn(
590 fn: BabelFn,
591 fnType: ReactFunctionType,
592 programContext: ProgramContext,
593 + outputMode: CompilerOutputMode,
594 ): null | CodegenFunction {
595 let directives: {
596 optIn: t.Directive | null;
@@ -616,18 +626,27 @@ function processFn(
626 }
627
628 let compiledFn: CodegenFunction;
619 - const compileResult = tryCompileFunction(fn, fnType, programContext);
629 + const compileResult = tryCompileFunction(
630 + fn,
631 + fnType,
632 + programContext,
633 + outputMode,
634 + );
635 if (compileResult.kind === 'error') {
636 if (directives.optOut != null) {
637 logError(compileResult.error, programContext, fn.node.loc ?? null);
638 } else {
639 handleError(compileResult.error, programContext, fn.node.loc ?? null);
640 }
626 - const retryResult = retryCompileFunction(fn, fnType, programContext);
627 - if (retryResult == null) {
641 + if (outputMode === 'client') {
642 + const retryResult = retryCompileFunction(fn, fnType, programContext);
643 + if (retryResult == null) {
644 + return null;
645 + }
646 + compiledFn = retryResult;
647 + } else {
648 return null;
649 }
630 - compiledFn = retryResult;
650 } else {
651 compiledFn = compileResult.compiledFn;
652 }
@@ -663,7 +682,7 @@ function processFn(
682
683 if (programContext.hasModuleScopeOptOut) {
684 return null;
666 - } else if (programContext.opts.noEmit) {
685 + } else if (programContext.opts.outputMode === 'lint') {
686 /**
687 * inferEffectDependencies + noEmit is currently only used for linting. In
688 * this mode, add source locations for where the compiler *can* infer effect
@@ -693,6 +712,7 @@ function tryCompileFunction(
712 fn: BabelFn,
713 fnType: ReactFunctionType,
714 programContext: ProgramContext,
715 + outputMode: CompilerOutputMode,
716 ):
717 | {kind: 'compile'; compiledFn: CodegenFunction}
718 | {kind: 'error'; error: unknown} {
@@ -719,7 +739,7 @@ function tryCompileFunction(
739 fn,
740 programContext.opts.environment,
741 fnType,
722 - 'all_features',
742 + outputMode,
743 programContext,
744 programContext.opts.logger,
745 programContext.filename,
@@ -757,7 +777,7 @@ function retryCompileFunction(
777 fn,
778 environment,
779 fnType,
760 - 'no_inferred_memo',
780 + 'client-no-memo',
781 programContext,
782 programContext.opts.logger,
783 programContext.filename,
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+64 -8
@@ -9,7 +9,7 @@ import * as t from '@babel/types';
9 import {ZodError, z} from 'zod/v4';
10 import {fromZodError} from 'zod-validation-error/v4';
11 import {CompilerError} from '../CompilerError';
12 -import {Logger, ProgramContext} from '../Entrypoint';
12 +import {CompilerOutputMode, Logger, ProgramContext} from '../Entrypoint';
13 import {Err, Ok, Result} from '../Utils/Result';
14 import {
15 DEFAULT_GLOBALS,
@@ -51,6 +51,7 @@ import {Scope as BabelScope, NodePath} from '@babel/traverse';
51 import {TypeSchema} from './TypeSchema';
52 import {FlowTypeEnv} from '../Flood/Types';
53 import {defaultModuleTypeProvider} from './DefaultModuleTypeProvider';
54 +import {assertExhaustive} from '../Utils/utils';
55
56 export const ReactElementSymbolSchema = z.object({
57 elementSymbol: z.union([
@@ -691,8 +692,6 @@ export const EnvironmentConfigSchema = z.object({
692 * by React to only execute in response to events, not during render.
693 */
694 enableInferEventHandlers: z.boolean().default(false),
694 -
695 - enableOptimizeForSSR: z.boolean().default(false),
695 });
696
697 export type EnvironmentConfig = z.infer<typeof EnvironmentConfigSchema>;
@@ -732,7 +731,7 @@ export class Environment {
731 code: string | null;
732 config: EnvironmentConfig;
733 fnType: ReactFunctionType;
735 - compilerMode: CompilerMode;
734 + outputMode: CompilerOutputMode;
735 programContext: ProgramContext;
736 hasFireRewrite: boolean;
737 hasInferredEffect: boolean;
@@ -747,7 +746,7 @@ export class Environment {
746 constructor(
747 scope: BabelScope,
748 fnType: ReactFunctionType,
750 - compilerMode: CompilerMode,
749 + outputMode: CompilerOutputMode,
750 config: EnvironmentConfig,
751 contextIdentifiers: Set<t.Identifier>,
752 parentFunction: NodePath<t.Function>, // the outermost function being compiled
@@ -758,7 +757,7 @@ export class Environment {
757 ) {
758 this.#scope = scope;
759 this.fnType = fnType;
761 - this.compilerMode = compilerMode;
760 + this.outputMode = outputMode;
761 this.config = config;
762 this.filename = filename;
763 this.code = code;
@@ -854,8 +853,65 @@ export class Environment {
853 return this.#flowTypeEnvironment;
854 }
855
857 - get isInferredMemoEnabled(): boolean {
858 - return this.compilerMode !== 'no_inferred_memo';
856 + get enableDropManualMemoization(): boolean {
857 + switch (this.outputMode) {
858 + case 'lint': {
859 + // linting drops to be more compatible with compiler analysis
860 + return true;
861 + }
862 + case 'client':
863 + case 'ssr': {
864 + return true;
865 + }
866 + case 'client-no-memo': {
867 + return false;
868 + }
869 + default: {
870 + assertExhaustive(
871 + this.outputMode,
872 + `Unexpected output mode '${this.outputMode}'`,
873 + );
874 + }
875 + }
876 + }
877 +
878 + get enableMemoization(): boolean {
879 + switch (this.outputMode) {
880 + case 'client':
881 + case 'lint': {
882 + // linting also enables memoization so that we can check if manual memoization is preserved
883 + return true;
884 + }
885 + case 'ssr':
886 + case 'client-no-memo': {
887 + return false;
888 + }
889 + default: {
890 + assertExhaustive(
891 + this.outputMode,
892 + `Unexpected output mode '${this.outputMode}'`,
893 + );
894 + }
895 + }
896 + }
897 +
898 + get enableValidations(): boolean {
899 + switch (this.outputMode) {
900 + case 'client':
901 + case 'lint':
902 + case 'ssr': {
903 + return true;
904 + }
905 + case 'client-no-memo': {
906 + return false;
907 + }
908 + default: {
909 + assertExhaustive(
910 + this.outputMode,
911 + `Unexpected output mode '${this.outputMode}'`,
912 + );
913 + }
914 + }
915 }
916
917 get nextIdentifierId(): IdentifierId {
compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
+1 -1
@@ -2452,7 +2452,7 @@ function computeEffectsForLegacySignature(
2452 }),
2453 });
2454 }
2455 - if (signature.knownIncompatible != null && state.env.isInferredMemoEnabled) {
2455 + if (signature.knownIncompatible != null && state.env.enableValidations) {
2456 const errors = new CompilerError();
2457 errors.pushDiagnostic(
2458 CompilerDiagnostic.create({
compiler/packages/babel-plugin-react-compiler/src/Optimization/DeadCodeElimination.ts
+1 -1
@@ -319,7 +319,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
319 }
320 case 'CallExpression':
321 case 'MethodCall': {
322 - if (state.env.config.enableOptimizeForSSR) {
322 + if (state.env.outputMode === 'ssr') {
323 const calleee =
324 value.kind === 'CallExpression' ? value.callee : value.property;
325 const hookKind = getHookKind(state.env, calleee.identifier);
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+7 -4
@@ -159,7 +159,7 @@ export function codegenFunction(
159 const compiled = compileResult.unwrap();
160
161 const hookGuard = fn.env.config.enableEmitHookGuards;
162 - if (hookGuard != null && fn.env.isInferredMemoEnabled) {
162 + if (hookGuard != null && fn.env.outputMode === 'client') {
163 compiled.body = t.blockStatement([
164 createHookGuard(
165 hookGuard,
@@ -259,7 +259,7 @@ export function codegenFunction(
259 if (
260 emitInstrumentForget != null &&
261 fn.id != null &&
262 - fn.env.isInferredMemoEnabled
262 + fn.env.outputMode === 'client'
263 ) {
264 /*
265 * Technically, this is a conditional hook call. However, we expect
@@ -591,7 +591,10 @@ function codegenBlockNoReset(
591 }
592
593 function wrapCacheDep(cx: Context, value: t.Expression): t.Expression {
594 - if (cx.env.config.enableEmitFreeze != null && cx.env.isInferredMemoEnabled) {
594 + if (
595 + cx.env.config.enableEmitFreeze != null &&
596 + cx.env.outputMode === 'client'
597 + ) {
598 const emitFreezeIdentifier = cx.env.programContext.addImportSpecifier(
599 cx.env.config.enableEmitFreeze,
600 ).name;
@@ -1772,7 +1775,7 @@ function createCallExpression(
1775 }
1776
1777 const hookGuard = env.config.enableEmitHookGuards;
1775 - if (hookGuard != null && isHook && env.isInferredMemoEnabled) {
1778 + if (hookGuard != null && isHook && env.outputMode === 'client') {
1779 const iife = t.functionExpression(
1780 null,
1781 [],
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.expect.md
+2 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @dynamicGating:{"source":"shared-runtime"} @noEmit
5 +// @dynamicGating:{"source":"shared-runtime"} @outputMode:"lint"
6
7 function Foo() {
8 'use memo if(getTrue)';
@@ -19,7 +19,7 @@ export const FIXTURE_ENTRYPOINT = {
19 ## Code
20
21 ```javascript
22 -// @dynamicGating:{"source":"shared-runtime"} @noEmit
22 +// @dynamicGating:{"source":"shared-runtime"} @outputMode:"lint"
23
24 function Foo() {
25 "use memo if(getTrue)";
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/dynamic-gating-noemit.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @dynamicGating:{"source":"shared-runtime"} @noEmit
1 +// @dynamicGating:{"source":"shared-runtime"} @outputMode:"lint"
2
3 function Foo() {
4 'use memo if(getTrue)';
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/no-emit/retry-no-emit.expect.md deleted
-66
@@ -1,66 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
6 -import {print} from 'shared-runtime';
7 -import useEffectWrapper from 'useEffectWrapper';
8 -import {AUTODEPS} from 'react';
9 -
10 -function Foo({propVal}) {
11 - const arr = [propVal];
12 - useEffectWrapper(() => print(arr), AUTODEPS);
13 -
14 - const arr2 = [];
15 - useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
16 - arr2.push(2);
17 - return {arr, arr2};
18 -}
19 -
20 -export const FIXTURE_ENTRYPOINT = {
21 - fn: Foo,
22 - params: [{propVal: 1}],
23 - sequentialRenders: [{propVal: 1}, {propVal: 2}],
24 -};
25 -
26 -```
27 -
28 -## Code
29 -
30 -```javascript
31 -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
32 -import { print } from "shared-runtime";
33 -import useEffectWrapper from "useEffectWrapper";
34 -import { AUTODEPS } from "react";
35 -
36 -function Foo({ propVal }) {
37 - const arr = [propVal];
38 - useEffectWrapper(() => print(arr), AUTODEPS);
39 -
40 - const arr2 = [];
41 - useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
42 - arr2.push(2);
43 - return { arr, arr2 };
44 -}
45 -
46 -export const FIXTURE_ENTRYPOINT = {
47 - fn: Foo,
48 - params: [{ propVal: 1 }],
49 - sequentialRenders: [{ propVal: 1 }, { propVal: 2 }],
50 -};
51 -
52 -```
53 -
54 -## Logs
55 -
56 -```
57 -{"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"}]}}}
58 -{"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"}]}
59 -{"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"}]}
60 -{"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}
61 -```
62 -
63 -### Eval output
64 -(kind: ok) {"arr":[1],"arr2":[2]}
65 -{"arr":[2],"arr2":[2]}
66 -logs: [[ 1 ],[ 2 ]]
\ No newline at end of file
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
+48
@@ -0,0 +1,48 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none"
6 +import {print} from 'shared-runtime';
7 +import useEffectWrapper from 'useEffectWrapper';
8 +import {AUTODEPS} from 'react';
9 +
10 +function Foo({propVal}) {
11 + const arr = [propVal];
12 + useEffectWrapper(() => print(arr), AUTODEPS);
13 +
14 + const arr2 = [];
15 + useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
16 + arr2.push(2);
17 + return {arr, arr2};
18 +}
19 +
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: Foo,
22 + params: [{propVal: 1}],
23 + sequentialRenders: [{propVal: 1}, {propVal: 2}],
24 +};
25 +
26 +```
27 +
28 +
29 +## Error
30 +
31 +```
32 +Found 1 error:
33 +
34 +Error: Cannot infer dependencies of this effect. This will break your build!
35 +
36 +To resolve, either pass a dependency array or fix reported compiler bailout diagnostics.
37 +
38 +error.infer-effect-deps-with-rule-violation--lint.ts:8:2
39 + 6 | function Foo({propVal}) {
40 + 7 | const arr = [propVal];
41 +> 8 | useEffectWrapper(() => print(arr), AUTODEPS);
42 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot infer dependencies
43 + 9 |
44 + 10 | const arr2 = [];
45 + 11 | useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
46 +```
47 +
48 +
\ No newline at end of file
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 renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
1 +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none"
2 import {print} from 'shared-runtime';
3 import useEffectWrapper from 'useEffectWrapper';
4 import {AUTODEPS} from 'react';
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
+49
@@ -0,0 +1,49 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none"
6 +import {print} from 'shared-runtime';
7 +import useEffectWrapper from 'useEffectWrapper';
8 +import {AUTODEPS} from 'react';
9 +
10 +function Foo({propVal}) {
11 + 'use memo';
12 + const arr = [propVal];
13 + useEffectWrapper(() => print(arr), AUTODEPS);
14 +
15 + const arr2 = [];
16 + useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
17 + arr2.push(2);
18 + return {arr, arr2};
19 +}
20 +
21 +export const FIXTURE_ENTRYPOINT = {
22 + fn: Foo,
23 + params: [{propVal: 1}],
24 + sequentialRenders: [{propVal: 1}, {propVal: 2}],
25 +};
26 +
27 +```
28 +
29 +
30 +## Error
31 +
32 +```
33 +Found 1 error:
34 +
35 +Error: Cannot infer dependencies of this effect. This will break your build!
36 +
37 +To resolve, either pass a dependency array or fix reported compiler bailout diagnostics.
38 +
39 +error.infer-effect-deps-with-rule-violation-use-memo-opt-in--lint.ts:9:2
40 + 7 | 'use memo';
41 + 8 | const arr = [propVal];
42 +> 9 | useEffectWrapper(() => print(arr), AUTODEPS);
43 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot infer dependencies
44 + 10 |
45 + 11 | const arr2 = [];
46 + 12 | useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
47 +```
48 +
49 +
\ No newline at end of file
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 renamed
+2 -3
@@ -1,7 +1,7 @@
1 -// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit
1 +// @inferEffectDependencies @outputMode:"lint" @panicThreshold:"none"
2 import {print} from 'shared-runtime';
3 -import {AUTODEPS} from 'react';
3 import useEffectWrapper from 'useEffectWrapper';
4 +import {AUTODEPS} from 'react';
5
6 function Foo({propVal}) {
7 'use memo';
@@ -11,7 +11,6 @@ function Foo({propVal}) {
11 const arr2 = [];
12 useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
13 arr2.push(2);
14 -
14 return {arr, arr2};
15 }
16
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
+58
@@ -0,0 +1,58 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @inferEffectDependencies @panicThreshold:"none"
6 +import {print} from 'shared-runtime';
7 +import useEffectWrapper from 'useEffectWrapper';
8 +import {AUTODEPS} from 'react';
9 +
10 +function Foo({propVal}) {
11 + const arr = [propVal];
12 + useEffectWrapper(() => print(arr), AUTODEPS);
13 +
14 + const arr2 = [];
15 + useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
16 + arr2.push(2);
17 + return {arr, arr2};
18 +}
19 +
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: Foo,
22 + params: [{propVal: 1}],
23 + sequentialRenders: [{propVal: 1}, {propVal: 2}],
24 +};
25 +
26 +```
27 +
28 +## Code
29 +
30 +```javascript
31 +// @inferEffectDependencies @panicThreshold:"none"
32 +import { print } from "shared-runtime";
33 +import useEffectWrapper from "useEffectWrapper";
34 +import { AUTODEPS } from "react";
35 +
36 +function Foo(t0) {
37 + const { propVal } = t0;
38 + const arr = [propVal];
39 + useEffectWrapper(() => print(arr), [arr]);
40 +
41 + const arr2 = [];
42 + useEffectWrapper(() => arr2.push(propVal), [arr2, propVal]);
43 + arr2.push(2);
44 + return { arr, arr2 };
45 +}
46 +
47 +export const FIXTURE_ENTRYPOINT = {
48 + fn: Foo,
49 + params: [{ propVal: 1 }],
50 + sequentialRenders: [{ propVal: 1 }, { propVal: 2 }],
51 +};
52 +
53 +```
54 +
55 +### Eval output
56 +(kind: ok) {"arr":[1],"arr2":[2]}
57 +{"arr":[2],"arr2":[2]}
58 +logs: [[ 1 ],[ 2 ]]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/infer-effect-deps-with-rule-violation--compile.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
1 +// @inferEffectDependencies @panicThreshold:"none"
2 import {print} from 'shared-runtime';
3 import useEffectWrapper from 'useEffectWrapper';
4 import {AUTODEPS} from 'react';
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 renamed
+9 -9
@@ -2,10 +2,10 @@
2 ## Input
3
4 ```javascript
5 -// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit
5 +// @inferEffectDependencies @panicThreshold:"none"
6 import {print} from 'shared-runtime';
7 -import {AUTODEPS} from 'react';
7 import useEffectWrapper from 'useEffectWrapper';
8 +import {AUTODEPS} from 'react';
9
10 function Foo({propVal}) {
11 'use memo';
@@ -15,7 +15,6 @@ function Foo({propVal}) {
15 const arr2 = [];
16 useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
17 arr2.push(2);
18 -
18 return {arr, arr2};
19 }
20
@@ -30,20 +29,21 @@ export const FIXTURE_ENTRYPOINT = {
29 ## Code
30
31 ```javascript
33 -// @compilationMode:"all" @inferEffectDependencies @panicThreshold:"none" @noEmit
32 +// @inferEffectDependencies @panicThreshold:"none"
33 import { print } from "shared-runtime";
35 -import { AUTODEPS } from "react";
34 import useEffectWrapper from "useEffectWrapper";
35 +import { AUTODEPS } from "react";
36
38 -function Foo({ propVal }) {
37 +function Foo(t0) {
38 "use memo";
39 + const { propVal } = t0;
40 +
41 const arr = [propVal];
41 - useEffectWrapper(() => print(arr), AUTODEPS);
42 + useEffectWrapper(() => print(arr), [arr]);
43
44 const arr2 = [];
44 - useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
45 + useEffectWrapper(() => arr2.push(propVal), [arr2, propVal]);
46 arr2.push(2);
46 -
47 return { arr, arr2 };
48 }
49
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
+21
@@ -0,0 +1,21 @@
1 +// @inferEffectDependencies @panicThreshold:"none"
2 +import {print} from 'shared-runtime';
3 +import useEffectWrapper from 'useEffectWrapper';
4 +import {AUTODEPS} from 'react';
5 +
6 +function Foo({propVal}) {
7 + 'use memo';
8 + const arr = [propVal];
9 + useEffectWrapper(() => print(arr), AUTODEPS);
10 +
11 + const arr2 = [];
12 + useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
13 + arr2.push(2);
14 + return {arr, arr2};
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: Foo,
19 + params: [{propVal: 1}],
20 + sequentialRenders: [{propVal: 1}, {propVal: 2}],
21 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.expect.md renamed
+2 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @inferEffectDependencies @noEmit
5 +// @inferEffectDependencies @outputMode:"lint"
6 import {print} from 'shared-runtime';
7 import useEffectWrapper from 'useEffectWrapper';
8 import {AUTODEPS} from 'react';
@@ -17,7 +17,7 @@ function ReactiveVariable({propVal}) {
17 ## Code
18
19 ```javascript
20 -// @inferEffectDependencies @noEmit
20 +// @inferEffectDependencies @outputMode:"lint"
21 import { print } from "shared-runtime";
22 import useEffectWrapper from "useEffectWrapper";
23 import { AUTODEPS } from "react";
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/retry-lint-comparison/lint-repro.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @inferEffectDependencies @noEmit
1 +// @inferEffectDependencies @outputMode:"lint"
2 import {print} from 'shared-runtime';
3 import useEffectWrapper from 'useEffectWrapper';
4 import {AUTODEPS} from 'react';
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.expect.md deleted
-66
@@ -1,66 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
6 -import {print} from 'shared-runtime';
7 -import useEffectWrapper from 'useEffectWrapper';
8 -import {AUTODEPS} from 'react';
9 -
10 -function Foo({propVal}) {
11 - const arr = [propVal];
12 - useEffectWrapper(() => print(arr), AUTODEPS);
13 -
14 - const arr2 = [];
15 - useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
16 - arr2.push(2);
17 - return {arr, arr2};
18 -}
19 -
20 -export const FIXTURE_ENTRYPOINT = {
21 - fn: Foo,
22 - params: [{propVal: 1}],
23 - sequentialRenders: [{propVal: 1}, {propVal: 2}],
24 -};
25 -
26 -```
27 -
28 -## Code
29 -
30 -```javascript
31 -// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
32 -import { print } from "shared-runtime";
33 -import useEffectWrapper from "useEffectWrapper";
34 -import { AUTODEPS } from "react";
35 -
36 -function Foo({ propVal }) {
37 - const arr = [propVal];
38 - useEffectWrapper(() => print(arr), AUTODEPS);
39 -
40 - const arr2 = [];
41 - useEffectWrapper(() => arr2.push(propVal), AUTODEPS);
42 - arr2.push(2);
43 - return { arr, arr2 };
44 -}
45 -
46 -export const FIXTURE_ENTRYPOINT = {
47 - fn: Foo,
48 - params: [{ propVal: 1 }],
49 - sequentialRenders: [{ propVal: 1 }, { propVal: 2 }],
50 -};
51 -
52 -```
53 -
54 -## Logs
55 -
56 -```
57 -{"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"}]}}}
58 -{"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"}]}
59 -{"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"}]}
60 -{"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}
61 -```
62 -
63 -### Eval output
64 -(kind: ok) {"arr":[1],"arr2":[2]}
65 -{"arr":[2],"arr2":[2]}
66 -logs: [[ 1 ],[ 2 ]]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/optimize-ssr.expect.md
+33 -3
@@ -20,10 +20,40 @@ function Component() {
20 ## Code
21
22 ```javascript
23 -// @enableOptimizeForSSR
23 +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR
24 function Component() {
25 - const state = 0;
26 - return <input value={state} />;
25 + const $ = _c(4);
26 + const [state, setState] = useState(0);
27 + const ref = useRef(null);
28 + let t0;
29 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 + t0 = (e) => {
31 + setState(e.target.value);
32 + };
33 + $[0] = t0;
34 + } else {
35 + t0 = $[0];
36 + }
37 + const onChange = t0;
38 + let t1;
39 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
40 + t1 = () => {
41 + log(ref.current.value);
42 + };
43 + $[1] = t1;
44 + } else {
45 + t1 = $[1];
46 + }
47 + useEffect(t1);
48 + let t2;
49 + if ($[2] !== state) {
50 + t2 = <input value={state} onChange={onChange} ref={ref} />;
51 + $[2] = state;
52 + $[3] = t2;
53 + } else {
54 + t2 = $[3];
55 + }
56 + return t2;
57 }
58
59 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-setState.expect.md
+32 -4
@@ -22,12 +22,40 @@ function Component() {
22 ## Code
23
24 ```javascript
25 -// @enableOptimizeForSSR
25 +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR
26 function Component() {
27 - const state = 0;
27 + const $ = _c(4);
28 + const [state, setState] = useState(0);
29 const ref = useRef(null);
29 - const onChange = undefined;
30 - return <CustomInput value={state} onChange={onChange} ref={ref} />;
30 + let t0;
31 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32 + t0 = (e) => {
33 + setState(e.target.value);
34 + };
35 + $[0] = t0;
36 + } else {
37 + t0 = $[0];
38 + }
39 + const onChange = t0;
40 + let t1;
41 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
42 + t1 = () => {
43 + log(ref.current.value);
44 + };
45 + $[1] = t1;
46 + } else {
47 + t1 = $[1];
48 + }
49 + useEffect(t1);
50 + let t2;
51 + if ($[2] !== state) {
52 + t2 = <CustomInput value={state} onChange={onChange} ref={ref} />;
53 + $[2] = state;
54 + $[3] = t2;
55 + } else {
56 + t2 = $[3];
57 + }
58 + return t2;
59 }
60
61 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-infer-event-handlers-from-startTransition.expect.md
+35 -5
@@ -25,13 +25,43 @@ function Component() {
25 ## Code
26
27 ```javascript
28 -// @enableOptimizeForSSR
28 +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR
29 function Component() {
30 - useTransition();
31 - const state = 0;
30 + const $ = _c(4);
31 + const [, startTransition] = useTransition();
32 + const [state, setState] = useState(0);
33 const ref = useRef(null);
33 - const onChange = undefined;
34 - return <CustomInput value={state} onChange={onChange} ref={ref} />;
34 + let t0;
35 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
36 + t0 = (e) => {
37 + startTransition(() => {
38 + setState.call(null, e.target.value);
39 + });
40 + };
41 + $[0] = t0;
42 + } else {
43 + t0 = $[0];
44 + }
45 + const onChange = t0;
46 + let t1;
47 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
48 + t1 = () => {
49 + log(ref.current.value);
50 + };
51 + $[1] = t1;
52 + } else {
53 + t1 = $[1];
54 + }
55 + useEffect(t1);
56 + let t2;
57 + if ($[2] !== state) {
58 + t2 = <CustomInput value={state} onChange={onChange} ref={ref} />;
59 + $[2] = state;
60 + $[3] = t2;
61 + } else {
62 + t2 = $[3];
63 + }
64 + return t2;
65 }
66
67 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer-initializer.expect.md
+36 -3
@@ -25,7 +25,7 @@ function Component() {
25 ## Code
26
27 ```javascript
28 -// @enableOptimizeForSSR
28 +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR
29
30 import { useReducer } from "react";
31
@@ -34,8 +34,41 @@ const initializer = (x) => {
34 };
35
36 function Component() {
37 - const state = initializer(0);
38 - return <input value={state} />;
37 + const $ = _c(4);
38 + const [state, dispatch] = useReducer(_temp, 0, initializer);
39 + const ref = useRef(null);
40 + let t0;
41 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
42 + t0 = (e) => {
43 + dispatch(e.target.value);
44 + };
45 + $[0] = t0;
46 + } else {
47 + t0 = $[0];
48 + }
49 + const onChange = t0;
50 + let t1;
51 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
52 + t1 = () => {
53 + log(ref.current.value);
54 + };
55 + $[1] = t1;
56 + } else {
57 + t1 = $[1];
58 + }
59 + useEffect(t1);
60 + let t2;
61 + if ($[2] !== state) {
62 + t2 = <input value={state} onChange={onChange} ref={ref} />;
63 + $[2] = state;
64 + $[3] = t2;
65 + } else {
66 + t2 = $[3];
67 + }
68 + return t2;
69 +}
70 +function _temp(_, next) {
71 + return next;
72 }
73
74 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ssr/ssr-use-reducer.expect.md
+36 -3
@@ -23,13 +23,46 @@ function Component() {
23 ## Code
24
25 ```javascript
26 -// @enableOptimizeForSSR
26 +import { c as _c } from "react/compiler-runtime"; // @enableOptimizeForSSR
27
28 import { useReducer } from "react";
29
30 function Component() {
31 - const state = 0;
32 - return <input value={state} />;
31 + const $ = _c(4);
32 + const [state, dispatch] = useReducer(_temp, 0);
33 + const ref = useRef(null);
34 + let t0;
35 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
36 + t0 = (e) => {
37 + dispatch(e.target.value);
38 + };
39 + $[0] = t0;
40 + } else {
41 + t0 = $[0];
42 + }
43 + const onChange = t0;
44 + let t1;
45 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
46 + t1 = () => {
47 + log(ref.current.value);
48 + };
49 + $[1] = t1;
50 + } else {
51 + t1 = $[1];
52 + }
53 + useEffect(t1);
54 + let t2;
55 + if ($[2] !== state) {
56 + t2 = <input value={state} onChange={onChange} ref={ref} />;
57 + $[2] = state;
58 + $[3] = t2;
59 + } else {
60 + t2 = $[3];
61 + }
62 + return t2;
63 +}
64 +function _temp(_, next) {
65 + return next;
66 }
67
68 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.expect.md
+2 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @noEmit
5 +// @outputMode:"lint"
6
7 function Foo() {
8 'use memo';
@@ -19,7 +19,7 @@ export const FIXTURE_ENTRYPOINT = {
19 ## Code
20
21 ```javascript
22 -// @noEmit
22 +// @outputMode:"lint"
23
24 function Foo() {
25 "use memo";
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-noemit.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @noEmit
1 +// @outputMode:"lint"
2
3 function Foo() {
4 'use memo';
compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts
+1 -1
@@ -21,7 +21,7 @@ import {isDeepStrictEqual} from 'util';
21 import type {ParseResult} from '@babel/parser';
22
23 const COMPILER_OPTIONS: PluginOptions = {
24 - noEmit: true,
24 + outputMode: 'lint',
25 panicThreshold: 'none',
26 // Don't emit errors on Flow suppressions--Flow already gave a signal
27 flowSuppressions: false,
packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts
+1 -1
@@ -22,7 +22,7 @@ import {isDeepStrictEqual} from 'util';
22 import type {ParseResult} from '@babel/parser';
23
24 const COMPILER_OPTIONS: PluginOptions = {
25 - noEmit: true,
25 + outputMode: 'lint',
26 panicThreshold: 'none',
27 // Don't emit errors on Flow suppressions--Flow already gave a signal
28 flowSuppressions: false,