diff --git a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts index 58167194249e5..18946703a03e4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts @@ -12,6 +12,7 @@ import { pipelineUsesReanimatedPlugin, } from '../Entrypoint/Reanimated'; import validateNoUntransformedReferences from '../Entrypoint/ValidateNoUntransformedReferences'; +import {CompilerError} from '..'; const ENABLE_REACT_COMPILER_TIMINGS = process.env['ENABLE_REACT_COMPILER_TIMINGS'] === '1'; @@ -34,51 +35,58 @@ export default function BabelPluginReactCompiler( */ Program: { enter(prog, pass): void { - const filename = pass.filename ?? 'unknown'; - if (ENABLE_REACT_COMPILER_TIMINGS === true) { - performance.mark(`${filename}:start`, { - detail: 'BabelPlugin:Program:start', - }); - } - let opts = parsePluginOptions(pass.opts); - const isDev = - (typeof __DEV__ !== 'undefined' && __DEV__ === true) || - process.env['NODE_ENV'] === 'development'; - if ( - opts.enableReanimatedCheck === true && - pipelineUsesReanimatedPlugin(pass.file.opts.plugins) - ) { - opts = injectReanimatedFlag(opts); - } - if ( - opts.environment.enableResetCacheOnSourceFileChanges !== false && - isDev - ) { - opts = { - ...opts, - environment: { - ...opts.environment, - enableResetCacheOnSourceFileChanges: true, - }, - }; - } - const result = compileProgram(prog, { - opts, - filename: pass.filename ?? null, - comments: pass.file.ast.comments ?? [], - code: pass.file.code, - }); - validateNoUntransformedReferences( - prog, - pass.filename ?? null, - opts.logger, - opts.environment, - result, - ); - if (ENABLE_REACT_COMPILER_TIMINGS === true) { - performance.mark(`${filename}:end`, { - detail: 'BabelPlugin:Program:end', + try { + const filename = pass.filename ?? 'unknown'; + if (ENABLE_REACT_COMPILER_TIMINGS === true) { + performance.mark(`${filename}:start`, { + detail: 'BabelPlugin:Program:start', + }); + } + let opts = parsePluginOptions(pass.opts); + const isDev = + (typeof __DEV__ !== 'undefined' && __DEV__ === true) || + process.env['NODE_ENV'] === 'development'; + if ( + opts.enableReanimatedCheck === true && + pipelineUsesReanimatedPlugin(pass.file.opts.plugins) + ) { + opts = injectReanimatedFlag(opts); + } + if ( + opts.environment.enableResetCacheOnSourceFileChanges !== false && + isDev + ) { + opts = { + ...opts, + environment: { + ...opts.environment, + enableResetCacheOnSourceFileChanges: true, + }, + }; + } + const result = compileProgram(prog, { + opts, + filename: pass.filename ?? null, + comments: pass.file.ast.comments ?? [], + code: pass.file.code, }); + validateNoUntransformedReferences( + prog, + pass.filename ?? null, + opts.logger, + opts.environment, + result, + ); + if (ENABLE_REACT_COMPILER_TIMINGS === true) { + performance.mark(`${filename}:end`, { + detail: 'BabelPlugin:Program:end', + }); + } + } catch (e) { + if (e instanceof CompilerError) { + throw new Error(e.printErrorMessage(pass.file.code)); + } + throw e; } }, exit(_, pass): void { diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index 75e01abaefa8f..cac897a9269be 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {codeFrameColumns} from '@babel/code-frame'; import type {SourceLocation} from './HIR'; import {Err, Ok, Result} from './Utils/Result'; import {assertExhaustive} from './Utils/utils'; @@ -44,6 +45,24 @@ export enum ErrorSeverity { Invariant = 'Invariant', } +export type CompilerDiagnosticOptions = { + severity: ErrorSeverity; + category: string; + description: string; + details: Array; + suggestions?: Array | null | undefined; +}; + +export type CompilerDiagnosticDetail = + /** + * A/the source of the error + */ + { + kind: 'error'; + loc: SourceLocation; + message: string; + }; + export enum CompilerSuggestionOperation { InsertBefore, InsertAfter, @@ -74,6 +93,94 @@ export type CompilerErrorDetailOptions = { suggestions?: Array | null | undefined; }; +export class CompilerDiagnostic { + options: CompilerDiagnosticOptions; + + constructor(options: CompilerDiagnosticOptions) { + this.options = options; + } + + get category(): CompilerDiagnosticOptions['category'] { + return this.options.category; + } + get description(): CompilerDiagnosticOptions['description'] { + return this.options.description; + } + get severity(): CompilerDiagnosticOptions['severity'] { + return this.options.severity; + } + get suggestions(): CompilerDiagnosticOptions['suggestions'] { + return this.options.suggestions; + } + + primaryLocation(): SourceLocation | null { + return this.options.details.filter(d => d.kind === 'error')[0]?.loc ?? null; + } + + printErrorMessage(source: string): string { + const buffer = [ + printErrorSummary(this.severity, this.category), + '\n\n', + this.description, + ]; + for (const detail of this.options.details) { + switch (detail.kind) { + case 'error': { + const loc = detail.loc; + if (typeof loc === 'symbol') { + continue; + } + let codeFrame: string; + try { + codeFrame = codeFrameColumns( + source, + { + start: { + line: loc.start.line, + column: loc.start.column + 1, + }, + end: { + line: loc.end.line, + column: loc.end.column + 1, + }, + }, + { + message: detail.message, + }, + ); + } catch (e) { + codeFrame = detail.message; + } + buffer.push( + `\n\n${loc.filename}:${loc.start.line}:${loc.start.column}\n`, + ); + buffer.push(codeFrame); + break; + } + default: { + assertExhaustive( + detail.kind, + `Unexpected detail kind ${(detail as any).kind}`, + ); + } + } + } + return buffer.join(''); + } + + toString(): string { + const buffer = [printErrorSummary(this.severity, this.category)]; + if (this.description != null) { + buffer.push(`. ${this.description}.`); + } + const loc = this.primaryLocation(); + if (loc != null && typeof loc !== 'symbol') { + buffer.push(` (${loc.start.line}:${loc.start.column})`); + } + return buffer.join(''); + } +} + /* * Each bailout or invariant in HIR lowering creates an {@link CompilerErrorDetail}, which is then * aggregated into a single {@link CompilerError} later. @@ -101,24 +208,62 @@ export class CompilerErrorDetail { return this.options.suggestions; } - printErrorMessage(): string { - const buffer = [`${this.severity}: ${this.reason}`]; + primaryLocation(): SourceLocation | null { + return this.loc; + } + + printErrorMessage(source: string): string { + const buffer = [printErrorSummary(this.severity, this.reason)]; if (this.description != null) { - buffer.push(`. ${this.description}`); + buffer.push(`\n\n${this.description}.`); } - if (this.loc != null && typeof this.loc !== 'symbol') { - buffer.push(` (${this.loc.start.line}:${this.loc.end.line})`); + const loc = this.loc; + if (loc != null && typeof loc !== 'symbol') { + let codeFrame: string; + try { + codeFrame = codeFrameColumns( + source, + { + start: { + line: loc.start.line, + column: loc.start.column + 1, + }, + end: { + line: loc.end.line, + column: loc.end.column + 1, + }, + }, + { + message: this.reason, + }, + ); + } catch (e) { + codeFrame = ''; + } + buffer.push( + `\n\n${loc.filename}:${loc.start.line}:${loc.start.column}\n`, + ); + buffer.push(codeFrame); + buffer.push('\n\n'); } return buffer.join(''); } toString(): string { - return this.printErrorMessage(); + const buffer = [printErrorSummary(this.severity, this.reason)]; + if (this.description != null) { + buffer.push(`. ${this.description}.`); + } + const loc = this.loc; + if (loc != null && typeof loc !== 'symbol') { + buffer.push(` (${loc.start.line}:${loc.start.column})`); + } + return buffer.join(''); } } export class CompilerError extends Error { - details: Array = []; + details: Array = []; static invariant( condition: unknown, @@ -136,6 +281,12 @@ export class CompilerError extends Error { } } + static throwDiagnostic(options: CompilerDiagnosticOptions): never { + const errors = new CompilerError(); + errors.pushDiagnostic(new CompilerDiagnostic(options)); + throw errors; + } + static throwTodo( options: Omit, ): never { @@ -210,6 +361,21 @@ export class CompilerError extends Error { return this.name; } + printErrorMessage(source: string): string { + return ( + `Found ${this.details.length} error${this.details.length === 1 ? '' : 's'}:\n` + + this.details.map(detail => detail.printErrorMessage(source)).join('\n') + ); + } + + merge(other: CompilerError): void { + this.details.push(...other.details); + } + + pushDiagnostic(diagnostic: CompilerDiagnostic): void { + this.details.push(diagnostic); + } + push(options: CompilerErrorDetailOptions): CompilerErrorDetail { const detail = new CompilerErrorDetail({ reason: options.reason, @@ -260,3 +426,32 @@ export class CompilerError extends Error { }); } } + +function printErrorSummary(severity: ErrorSeverity, message: string): string { + let severityCategory: string; + switch (severity) { + case ErrorSeverity.InvalidConfig: + case ErrorSeverity.InvalidJS: + case ErrorSeverity.InvalidReact: + case ErrorSeverity.UnsupportedJS: { + severityCategory = 'Error'; + break; + } + case ErrorSeverity.CannotPreserveMemoization: { + severityCategory = 'Memoization'; + break; + } + case ErrorSeverity.Invariant: { + severityCategory = 'Invariant'; + break; + } + case ErrorSeverity.Todo: { + severityCategory = 'Todo'; + break; + } + default: { + assertExhaustive(severity, `Unexpected severity '${severity}'`); + } + } + return `${severityCategory}: ${message}`; +} 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 0c23ceb345296..c13940ed10a21 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -7,7 +7,12 @@ import * as t from '@babel/types'; import {z} from 'zod'; -import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError'; +import { + CompilerDiagnostic, + CompilerError, + CompilerErrorDetail, + CompilerErrorDetailOptions, +} from '../CompilerError'; import { EnvironmentConfig, ExternalFunction, @@ -224,7 +229,7 @@ export type LoggerEvent = export type CompileErrorEvent = { kind: 'CompileError'; fnLoc: t.SourceLocation | null; - detail: CompilerErrorDetailOptions; + detail: CompilerErrorDetail | CompilerDiagnostic; }; export type CompileDiagnosticEvent = { kind: 'CompileDiagnostic'; 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 c5ca3434b1b54..648ff01ba713c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -94,7 +94,7 @@ import {validateLocalsNotReassignedAfterRender} from '../Validation/ValidateLoca import {outlineFunctions} from '../Optimization/OutlineFunctions'; import {propagatePhiTypes} from '../TypeInference/PropagatePhiTypes'; import {lowerContextAccess} from '../Optimization/LowerContextAccess'; -import {validateNoSetStateInPassiveEffects} from '../Validation/ValidateNoSetStateInPassiveEffects'; +import {validateNoSetStateInEffects} from '../Validation/ValidateNoSetStateInEffects'; import {validateNoJSXInTryStatement} from '../Validation/ValidateNoJSXInTryStatement'; import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHIR'; import {outlineJSX} from '../Optimization/OutlineJsx'; @@ -292,8 +292,8 @@ function runWithEnvironment( validateNoSetStateInRender(hir).unwrap(); } - if (env.config.validateNoSetStateInPassiveEffects) { - env.logErrors(validateNoSetStateInPassiveEffects(hir)); + if (env.config.validateNoSetStateInEffects) { + env.logErrors(validateNoSetStateInEffects(hir)); } if (env.config.validateNoJSXInTryStatements) { 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 de8d16fb12a6e..79bbee37a565c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -181,7 +181,7 @@ function logError( context.opts.logger.logEvent(context.filename, { kind: 'CompileError', fnLoc, - detail: detail.options, + detail, }); } } else { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts index d363e11831d3d..16d7c3713c6dd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts @@ -8,32 +8,27 @@ import {NodePath} from '@babel/core'; import * as t from '@babel/types'; -import { - CompilerError, - CompilerErrorDetailOptions, - EnvironmentConfig, - ErrorSeverity, - Logger, -} from '..'; +import {CompilerError, EnvironmentConfig, ErrorSeverity, Logger} from '..'; import {getOrInsertWith} from '../Utils/utils'; -import {Environment} from '../HIR'; +import {Environment, GeneratedSource} from '../HIR'; import {DEFAULT_EXPORT} from '../HIR/Environment'; import {CompileProgramMetadata} from './Program'; +import {CompilerDiagnostic, CompilerDiagnosticOptions} from '../CompilerError'; function throwInvalidReact( - options: Omit, + options: Omit, {logger, filename}: TraversalState, ): never { - const detail: CompilerErrorDetailOptions = { - ...options, + const detail: CompilerDiagnosticOptions = { severity: ErrorSeverity.InvalidReact, + ...options, }; logger?.logEvent(filename, { kind: 'CompileError', fnLoc: null, - detail, + detail: new CompilerDiagnostic(detail), }); - CompilerError.throw(detail); + CompilerError.throwDiagnostic(detail); } function isAutodepsSigil( @@ -97,14 +92,18 @@ function assertValidEffectImportReference( */ throwInvalidReact( { - reason: - '[InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. ' + - 'This will break your build! ' + - 'To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.', - description: maybeErrorDiagnostic - ? `(Bailout reason: ${maybeErrorDiagnostic})` - : null, - loc: parent.node.loc ?? null, + category: + 'Cannot infer dependencies of this effect. This will break your build!', + description: + 'To resolve, either pass a dependency array or fix reported compiler bailout diagnostics.' + + (maybeErrorDiagnostic ? ` ${maybeErrorDiagnostic}` : ''), + details: [ + { + kind: 'error', + message: 'Cannot infer dependencies', + loc: parent.node.loc ?? GeneratedSource, + }, + ], }, context, ); @@ -124,13 +123,20 @@ function assertValidFireImportReference( ); throwInvalidReact( { - reason: - '[Fire] Untransformed reference to compiler-required feature. ' + - 'Either remove this `fire` call or ensure it is successfully transformed by the compiler', - description: maybeErrorDiagnostic - ? `(Bailout reason: ${maybeErrorDiagnostic})` - : null, - loc: paths[0].node.loc ?? null, + category: + '[Fire] Untransformed reference to compiler-required feature.', + description: + 'Either remove this `fire` call or ensure it is successfully transformed by the compiler' + + maybeErrorDiagnostic + ? ` ${maybeErrorDiagnostic}` + : '', + details: [ + { + kind: 'error', + message: 'Untransformed `fire` call', + loc: paths[0].node.loc ?? GeneratedSource, + }, + ], }, context, ); diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index cd7d6a7a35b35..5b5702f19c62b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -2271,11 +2271,17 @@ function lowerExpression( }); for (const [name, locations] of Object.entries(fbtLocations)) { if (locations.length > 1) { - CompilerError.throwTodo({ - reason: `Support <${tagName}> tags with multiple <${tagName}:${name}> values`, - loc: locations.at(-1) ?? GeneratedSource, - description: null, - suggestions: null, + CompilerError.throwDiagnostic({ + severity: ErrorSeverity.Todo, + category: 'Support duplicate fbt tags', + description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`, + details: locations.map(loc => { + return { + kind: 'error', + message: `Multiple \`<${tagName}:${name}>\` tags found`, + loc, + }; + }), }); } } @@ -3503,9 +3509,8 @@ function lowerFunction( ); let loweredFunc: HIRFunction; if (lowering.isErr()) { - lowering - .unwrapErr() - .details.forEach(detail => builder.errors.pushErrorDetail(detail)); + const functionErrors = lowering.unwrapErr(); + builder.errors.merge(functionErrors); return null; } loweredFunc = lowering.unwrap(); 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 a552803171ad5..5b5b78fc5202c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -318,10 +318,10 @@ export const EnvironmentConfigSchema = z.object({ validateNoSetStateInRender: z.boolean().default(true), /** - * Validates that setState is not called directly within a passive effect (useEffect). + * Validates that setState is not called synchronously within an effect (useEffect and friends). * Scheduling a setState (with an event listener, subscription, etc) is valid. */ - validateNoSetStateInPassiveEffects: z.boolean().default(false), + validateNoSetStateInEffects: z.boolean().default(false), /** * Validates against creating JSX within a try block and recommends using an error boundary diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts index c3a6c18d3aaff..81959ea361564 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts @@ -7,7 +7,7 @@ import {Binding, NodePath} from '@babel/traverse'; import * as t from '@babel/types'; -import {CompilerError} from '../CompilerError'; +import {CompilerError, ErrorSeverity} from '../CompilerError'; import {Environment} from './Environment'; import { BasicBlock, @@ -308,9 +308,18 @@ export default class HIRBuilder { resolveBinding(node: t.Identifier): Identifier { if (node.name === 'fbt') { - CompilerError.throwTodo({ - reason: 'Support local variables named "fbt"', - loc: node.loc ?? null, + CompilerError.throwDiagnostic({ + severity: ErrorSeverity.Todo, + category: 'Support local variables named `fbt`', + description: + 'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported', + details: [ + { + kind: 'error', + message: 'Rename to avoid conflict with fbt plugin', + loc: node.loc ?? GeneratedSource, + }, + ], }); } const originalName = node.name; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts similarity index 92% rename from compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts rename to compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts index a36c347faa001..e9b0fdb8870ee 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts @@ -11,20 +11,22 @@ import { IdentifierId, isSetStateType, isUseEffectHookType, + isUseInsertionEffectHookType, + isUseLayoutEffectHookType, Place, } from '../HIR'; import {eachInstructionValueOperand} from '../HIR/visitors'; import {Result} from '../Utils/Result'; /** - * Validates against calling setState in the body of a *passive* effect (useEffect), + * Validates against calling setState in the body of an effect (useEffect and friends), * while allowing calling setState in callbacks scheduled by the effect. * * Calling setState during execution of a useEffect triggers a re-render, which is * often bad for performance and frequently has more efficient and straightforward * alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples. */ -export function validateNoSetStateInPassiveEffects( +export function validateNoSetStateInEffects( fn: HIRFunction, ): Result { const setStateFunctions: Map = new Map(); @@ -79,7 +81,11 @@ export function validateNoSetStateInPassiveEffects( instr.value.kind === 'MethodCall' ? instr.value.receiver : instr.value.callee; - if (isUseEffectHookType(callee.identifier)) { + if ( + isUseEffectHookType(callee.identifier) || + isUseLayoutEffectHookType(callee.identifier) || + isUseInsertionEffectHookType(callee.identifier) + ) { const arg = instr.value.args[0]; if (arg !== undefined && arg.kind === 'Identifier') { const setState = setStateFunctions.get(arg.identifier.id); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts index 500edab8f20f5..f8a6330977fe8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts @@ -20,7 +20,7 @@ describe('parseConfigPragma()', () => { validateHooksUsage: 1, } as any); }).toThrowErrorMatchingInlineSnapshot( - `"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: Expected boolean, received number at "validateHooksUsage""`, + `"Error: Could not validate environment config. Update React Compiler config to fix the error. Validation error: Expected boolean, received number at "validateHooksUsage"."`, ); }); @@ -38,7 +38,7 @@ describe('parseConfigPragma()', () => { ], } as any); }).toThrowErrorMatchingInlineSnapshot( - `"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: autodepsIndex must be > 0 at "inferEffectDependencies[0].autodepsIndex""`, + `"Error: Could not validate environment config. Update React Compiler config to fix the error. Validation error: autodepsIndex must be > 0 at "inferEffectDependencies[0].autodepsIndex"."`, ); }); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error._todo.computed-lval-in-destructure.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error._todo.computed-lval-in-destructure.expect.md index f44ae83b2cee0..eaa480f7c5f62 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error._todo.computed-lval-in-destructure.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error._todo.computed-lval-in-destructure.expect.md @@ -15,13 +15,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Todo: (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern + +error._todo.computed-lval-in-destructure.ts:3:9 1 | function Component(props) { 2 | const computedKey = props.key; > 3 | const {[computedKey]: x} = props.val; - | ^^^^^^^^^^^^^^^^ Todo: (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern (3:3) + | ^^^^^^^^^^^^^^^^ (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern 4 | 5 | return x; 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-component-tag-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-component-tag-function.expect.md index 5553f235a0847..d15aba19d12ee 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-component-tag-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-component-tag-function.expect.md @@ -15,13 +15,19 @@ function Component() { ## Error ``` +Found 1 error: +Error: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) + +error.assign-global-in-component-tag-function.ts:3:4 1 | function Component() { 2 | const Foo = () => { > 3 | someGlobal = true; - | ^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3) + | ^^^^^^^^^^ Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) 4 | }; 5 | return ; 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-children.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-children.expect.md index d380137836cc1..634d98394c3da 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-children.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-children.expect.md @@ -18,13 +18,19 @@ function Component() { ## Error ``` +Found 1 error: +Error: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) + +error.assign-global-in-jsx-children.ts:3:4 1 | function Component() { 2 | const foo = () => { > 3 | someGlobal = true; - | ^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3) + | ^^^^^^^^^^ Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) 4 | }; 5 | // Children are generally access/called during render, so 6 | // modifying a global in a children function is almost + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-spread-attribute.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-spread-attribute.expect.md index 3f0b5530ee2d2..2bcf5a49f8e6e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-spread-attribute.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.assign-global-in-jsx-spread-attribute.expect.md @@ -16,13 +16,19 @@ function Component() { ## Error ``` +Found 1 error: +Error: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) + +error.assign-global-in-jsx-spread-attribute.ts:4:4 2 | function Component() { 3 | const foo = () => { > 4 | someGlobal = true; - | ^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4) + | ^^^^^^^^^^ Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) 5 | }; 6 | return
; 7 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-flow-suppression.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-flow-suppression.expect.md index 1d5b4abdf7d42..988a8dbab8d03 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-flow-suppression.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-flow-suppression.expect.md @@ -16,13 +16,21 @@ function Foo(props) { ## Error ``` +Found 1 error: +Error: React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior + +$FlowFixMe[react-rule-hook]. + +error.bailout-on-flow-suppression.ts:4:2 2 | 3 | function Foo(props) { > 4 | // $FlowFixMe[react-rule-hook] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. $FlowFixMe[react-rule-hook] (4:4) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior 5 | useX(); 6 | return null; 7 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-suppression-of-custom-rule.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-suppression-of-custom-rule.expect.md index d74ebd119c345..c6653177a7a53 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-suppression-of-custom-rule.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bailout-on-suppression-of-custom-rule.expect.md @@ -19,15 +19,35 @@ function lowercasecomponent() { ## Error ``` +Found 2 errors: +Error: 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 my-app/react-rule. + +error.bailout-on-suppression-of-custom-rule.ts:3:0 1 | // @eslintSuppressionRules:["my-app","react-rule"] 2 | > 3 | /* eslint-disable my-app/react-rule */ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 my-app/react-rule (3:3) - -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 my-app/react-rule (7:7) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 4 | function lowercasecomponent() { 5 | 'use forget'; 6 | const x = []; + + +Error: 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 my-app/react-rule. + +error.bailout-on-suppression-of-custom-rule.ts:7:2 + 5 | 'use forget'; + 6 | const x = []; +> 7 | // eslint-disable-next-line my-app/react-rule + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 + 8 | return
{x}
; + 9 | } + 10 | /* eslint-enable my-app/react-rule */ + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md index e1cebb00df56f..84370796a6724 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md @@ -36,6 +36,10 @@ function Component() { ## Error ``` +Found 2 errors: +Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead + +error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:20:12 18 | ); 19 | const ref = useRef(null); > 20 | useEffect(() => { @@ -47,12 +51,24 @@ function Component() { > 23 | } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 24 | }, [update]); - | ^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (20:24) - -InvalidReact: The function modifies a local variable here (14:14) + | ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead 25 | 26 | return 'ok'; 27 | } + + +Error: The function modifies a local variable here + +error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:14:6 + 12 | ...partialParams, + 13 | }; +> 14 | nextParams.param = 'value'; + | ^^^^^^^^^^ The function modifies a local variable here + 15 | console.log(nextParams); + 16 | }, + 17 | [params] + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.call-args-destructuring-asignment-complex.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.call-args-destructuring-asignment-complex.expect.md index cb2ce1a20df0a..fea112547e445 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.call-args-destructuring-asignment-complex.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.call-args-destructuring-asignment-complex.expect.md @@ -14,13 +14,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Invariant: Const declaration cannot be referenced as an expression + +error.call-args-destructuring-asignment-complex.ts:3:9 1 | function Component(props) { 2 | let x = makeObject(); > 3 | x.foo(([[x]] = makeObject())); - | ^^^^^ Invariant: Const declaration cannot be referenced as an expression (3:3) + | ^^^^^ Const declaration cannot be referenced as an expression 4 | return x; 5 | } 6 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call-aliased.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call-aliased.expect.md index 94b3ae1035ea2..dad64bcbd8dc4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call-aliased.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call-aliased.expect.md @@ -14,12 +14,20 @@ function Foo() { ## Error ``` +Found 1 error: +Error: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config + +Bar may be a component.. + +error.capitalized-function-call-aliased.ts:4:2 2 | function Foo() { 3 | let x = Bar; > 4 | x(); // ERROR - | ^^^ InvalidReact: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config. Bar may be a component. (4:4) + | ^^^ Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config 5 | } 6 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call.expect.md index d8b0f8facfed0..e2894b6efd21b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-function-call.expect.md @@ -15,13 +15,21 @@ function Component() { ## Error ``` +Found 1 error: +Error: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config + +SomeFunc may be a component.. + +error.capitalized-function-call.ts:3:12 1 | // @validateNoCapitalizedCalls 2 | function Component() { > 3 | const x = SomeFunc(); - | ^^^^^^^^^^ InvalidReact: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config. SomeFunc may be a component. (3:3) + | ^^^^^^^^^^ Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config 4 | 5 | return x; 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-method-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-method-call.expect.md index 39dc43e4a563c..ecc0303692ea5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-method-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capitalized-method-call.expect.md @@ -15,13 +15,21 @@ function Component() { ## Error ``` +Found 1 error: +Error: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config + +SomeFunc may be a component.. + +error.capitalized-method-call.ts:3:12 1 | // @validateNoCapitalizedCalls 2 | function Component() { > 3 | const x = someGlobal.SomeFunc(); - | ^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config. SomeFunc may be a component. (3:3) + | ^^^^^^^^^^^^^^^^^^^^^ Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config 4 | 5 | return x; 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-mutation.expect.md index cff34e3449376..9c9cd94dbd1f5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-mutation.expect.md @@ -32,19 +32,55 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 4 errors: +Error: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.capture-ref-for-mutation.ts:12:13 10 | }; 11 | const moveLeft = { > 12 | handler: handleKey('left')(), - | ^^^^^^^^^^^^^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12) + | ^^^^^^^^^^^^^^^^^ This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) + 13 | }; + 14 | const moveRight = { + 15 | handler: handleKey('right')(), -InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12) -InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15) +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) -InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15) +error.capture-ref-for-mutation.ts:12:13 + 10 | }; + 11 | const moveLeft = { +> 12 | handler: handleKey('left')(), + | ^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) 13 | }; 14 | const moveRight = { 15 | handler: handleKey('right')(), + + +Error: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.capture-ref-for-mutation.ts:15:13 + 13 | }; + 14 | const moveRight = { +> 15 | handler: handleKey('right')(), + | ^^^^^^^^^^^^^^^^^^ This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) + 16 | }; + 17 | return [moveLeft, moveRight]; + 18 | } + + +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.capture-ref-for-mutation.ts:15:13 + 13 | }; + 14 | const moveRight = { +> 15 | handler: handleKey('right')(), + | ^^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + 16 | }; + 17 | return [moveLeft, moveRight]; + 18 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md index 7ea8ae9809370..86af804221926 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hook-unknown-hook-react-namespace.expect.md @@ -16,13 +16,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +error.conditional-hook-unknown-hook-react-namespace.ts:4:8 2 | let x = null; 3 | if (props.cond) { > 4 | x = React.useNonexistentHook(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) + | ^^^^^^^^^^^^^^^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 5 | } 6 | return x; 7 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hooks-as-method-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hooks-as-method-call.expect.md index c2ad547414aa9..427a573dc789c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hooks-as-method-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.conditional-hooks-as-method-call.expect.md @@ -16,13 +16,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +error.conditional-hooks-as-method-call.ts:4:8 2 | let x = null; 3 | if (props.cond) { > 4 | x = Foo.useFoo(); - | ^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (4:4) + | ^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 5 | } 6 | return x; 7 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md index 0318fa9525fda..de50b21543742 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md @@ -28,13 +28,21 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + +Variable `x` cannot be reassigned after render. + +error.context-variable-only-chained-assign.ts:10:19 8 | }; 9 | const fn2 = () => { > 10 | const copy2 = (x = 4); - | ^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `x` cannot be reassigned after render (10:10) + | ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead 11 | return [invoke(fn1), copy2, identity(copy2)]; 12 | }; 13 | return invoke(fn2); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md index 2a6dce11f242e..6823db842d2a1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md @@ -17,13 +17,21 @@ function Component() { ## Error ``` +Found 1 error: +Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + +Variable `x` cannot be reassigned after render. + +error.declare-reassign-variable-in-function-declaration.ts:4:4 2 | let x = null; 3 | function foo() { > 4 | x = 9; - | ^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `x` cannot be reassigned after render (4:4) + | ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead 5 | } 6 | const y = bar(foo); 7 | return ; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.default-param-accesses-local.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.default-param-accesses-local.expect.md index dbf084466d80d..02e06c7a82fda 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.default-param-accesses-local.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.default-param-accesses-local.expect.md @@ -22,6 +22,10 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `ArrowFunctionExpression` cannot be safely reordered + +error.default-param-accesses-local.ts:3:6 1 | function Component( 2 | x, > 3 | y = () => { @@ -29,10 +33,12 @@ export const FIXTURE_ENTRYPOINT = { > 4 | return x; | ^^^^^^^^^^^^^ > 5 | } - | ^^^^ Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `ArrowFunctionExpression` cannot be safely reordered (3:5) + | ^^^^ (BuildHIR::node.lowerReorderableExpression) Expression type `ArrowFunctionExpression` cannot be safely reordered 6 | ) { 7 | return y(); 8 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md index b08d151be64c4..c0bd287e12e87 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md @@ -19,13 +19,21 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Todo: [hoisting] EnterSSA: Expected identifier to be defined before being used + +Identifier x$1 is undefined. + +error.dont-hoist-inline-reference.ts:3:2 1 | import {identity} from 'shared-runtime'; 2 | function useInvalid() { > 3 | const x = identity(x); - | ^^^^^^^^^^^^^^^^^^^^^^ Todo: [hoisting] EnterSSA: Expected identifier to be defined before being used. Identifier x$1 is undefined (3:3) + | ^^^^^^^^^^^^^^^^^^^^^^ [hoisting] EnterSSA: Expected identifier to be defined before being used 4 | return x; 5 | } 6 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md index a54cc98708f1e..d1e1476535335 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md @@ -15,13 +15,21 @@ function useFoo(props) { ## Error ``` +Found 1 error: +Todo: Encountered conflicting global in generated program + +Conflict from local binding __DEV__. + +error.emit-freeze-conflicting-global.ts:3:8 1 | // @enableEmitFreeze @instrumentForget 2 | function useFoo(props) { > 3 | const __DEV__ = 'conflicting global'; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Todo: Encountered conflicting global in generated program. Conflict from local binding __DEV__ (3:3) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Encountered conflicting global in generated program 4 | console.log(__DEV__); 5 | return foo(props.x); 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md index 76ac6d77a2774..47af99524836a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md @@ -15,13 +15,21 @@ function Component() { ## Error ``` +Found 1 error: +Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + +Variable `callback` cannot be reassigned after render. + +error.function-expression-references-variable-its-assigned-to.ts:3:4 1 | function Component() { 2 | let callback = () => { > 3 | callback = null; - | ^^^^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `callback` cannot be reassigned after render (3:3) + | ^^^^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead 4 | }; 5 | return
; 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md index 048fee7ee1d58..dcde3a9f83f57 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md @@ -24,6 +24,12 @@ function Component(props) { ## Error ``` +Found 1 error: +Memoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected + +The inferred dependency was `props.items`, but the source dependencies were [props?.items, props.cond]. Inferred different dependency than source. + +error.hoist-optional-member-expression-with-conditional-optional.ts:4:23 2 | import {ValidateMemoization} from 'shared-runtime'; 3 | function Component(props) { > 4 | const data = useMemo(() => { @@ -41,10 +47,12 @@ function Component(props) { > 10 | return x; | ^^^^^^^^^^^^^^^^^ > 11 | }, [props?.items, props.cond]); - | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `props.items`, but the source dependencies were [props?.items, props.cond]. Inferred different dependency than source (4:11) + | ^^^^ React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected 12 | return ( 13 | 14 | ); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md index ca3ee2ae1388e..ea6683fd0ad80 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md @@ -24,6 +24,12 @@ function Component(props) { ## Error ``` +Found 1 error: +Memoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected + +The inferred dependency was `props.items`, but the source dependencies were [props?.items, props.cond]. Inferred different dependency than source. + +error.hoist-optional-member-expression-with-conditional.ts:4:23 2 | import {ValidateMemoization} from 'shared-runtime'; 3 | function Component(props) { > 4 | const data = useMemo(() => { @@ -41,10 +47,12 @@ function Component(props) { > 10 | return x; | ^^^^^^^^^^^^^^^^^ > 11 | }, [props?.items, props.cond]); - | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `props.items`, but the source dependencies were [props?.items, props.cond]. Inferred different dependency than source (4:11) + | ^^^^ React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected 12 | return ( 13 | 14 | ); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md index 1ba0d59e17265..c3ab81ba38beb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md @@ -24,6 +24,10 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Todo: Support functions with unreachable code that may contain hoisted declarations + +error.hoisting-simple-function-declaration.ts:6:2 4 | } 5 | return baz(); // OK: FuncDecls are HoistableDeclarations that have both declaration and value hoisting > 6 | function baz() { @@ -31,10 +35,12 @@ export const FIXTURE_ENTRYPOINT = { > 7 | return bar(); | ^^^^^^^^^^^^^^^^^ > 8 | } - | ^^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (6:8) + | ^^^^ Support functions with unreachable code that may contain hoisted declarations 9 | } 10 | 11 | export const FIXTURE_ENTRYPOINT = { + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md index 5e0a9886272bc..7174acc43d2b5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md @@ -29,13 +29,19 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Error: Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook + +error.hook-call-freezes-captured-identifier.ts:13:2 11 | }); 12 | > 13 | x.value += count; - | ^ InvalidReact: Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook (13:13) + | ^ Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook 14 | return ; 15 | } 16 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md index c5af59d642426..7a969400a33f7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md @@ -29,13 +29,19 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Error: Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook + +error.hook-call-freezes-captured-memberexpr.ts:13:2 11 | }); 12 | > 13 | x.value += count; - | ^ InvalidReact: Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook (13:13) + | ^ Updating a value previously passed as an argument to a hook is not allowed. Consider moving the mutation before calling the hook 14 | return ; 15 | } 16 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md index 0949fb3072fcb..f3716d810c5a0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-property-load-local-hook.expect.md @@ -23,15 +23,31 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 2 errors: +Error: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values + +error.hook-property-load-local-hook.ts:7:12 5 | 6 | function Foo() { > 7 | let bar = useFoo.useBar; - | ^^^^^^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (7:7) - -InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (8:8) + | ^^^^^^^^^^^^^ Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values 8 | return bar(); 9 | } 10 | + + +Error: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values + +error.hook-property-load-local-hook.ts:8:9 + 6 | function Foo() { + 7 | let bar = useFoo.useBar; +> 8 | return bar(); + | ^^^ Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values + 9 | } + 10 | + 11 | export const FIXTURE_ENTRYPOINT = { + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-ref-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-ref-value.expect.md index d92d918fe9f3c..abf18e43e3f3d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-ref-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-ref-value.expect.md @@ -20,15 +20,31 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 2 errors: +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.hook-ref-value.ts:5:23 3 | function Component(props) { 4 | const ref = useRef(); > 5 | useEffect(() => {}, [ref.current]); - | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (5:5) + | ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + 6 | } + 7 | + 8 | export const FIXTURE_ENTRYPOINT = { + + +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) -InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (5:5) +error.hook-ref-value.ts:5:23 + 3 | function Component(props) { + 4 | const ref = useRef(); +> 5 | useEffect(() => {}, [ref.current]); + | ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) 6 | } 7 | 8 | export const FIXTURE_ENTRYPOINT = { + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md index db616600e80dc..1c5c92d2c305a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md @@ -15,16 +15,22 @@ function component(a, b) { ## Error ``` +Found 1 error: +Error: useMemo callbacks may not be async or generator functions + +error.invalid-ReactUseMemo-async-callback.ts:2:24 1 | function component(a, b) { > 2 | let x = React.useMemo(async () => { | ^^^^^^^^^^^^^ > 3 | await a; | ^^^^^^^^^^^^ > 4 | }, []); - | ^^^^ InvalidReact: useMemo callbacks may not be async or generator functions (2:4) + | ^^^^ useMemo callbacks may not be async or generator functions 5 | return x; 6 | } 7 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-during-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-during-render.expect.md index 02748366456fb..d3dd7317ef90e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-during-render.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-during-render.expect.md @@ -15,13 +15,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.invalid-access-ref-during-render.ts:4:16 2 | function Component(props) { 3 | const ref = useRef(null); > 4 | const value = ref.current; - | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4) + | ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) 5 | return value; 6 | } 7 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-aliased-ref-in-callback-invoked-during-render-.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-aliased-ref-in-callback-invoked-during-render-.expect.md index e2ce2cceae3b9..7d7a0dafcee74 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-aliased-ref-in-callback-invoked-during-render-.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-aliased-ref-in-callback-invoked-during-render-.expect.md @@ -19,12 +19,18 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.invalid-aliased-ref-in-callback-invoked-during-render-.ts:9:33 7 | return ; 8 | }; > 9 | return {props.items.map(item => renderItem(item))}; - | ^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (9:9) + | ^^^^^^^^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) 10 | } 11 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-array-push-frozen.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-array-push-frozen.expect.md index 0440117adbfc7..137d29cbc2916 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-array-push-frozen.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-array-push-frozen.expect.md @@ -15,13 +15,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX + +error.invalid-array-push-frozen.ts:4:2 2 | const x = []; 3 |
{x}
; > 4 | x.push(props.value); - | ^ InvalidReact: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX (4:4) + | ^ Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX 5 | return x; 6 | } 7 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md index a4327cf961bfc..6abdb5b2efc04 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-assign-hook-to-local.expect.md @@ -14,12 +14,18 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values + +error.invalid-assign-hook-to-local.ts:2:12 1 | function Component(props) { > 2 | const x = useState; - | ^^^^^^^^ InvalidReact: Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values (2:2) + | ^^^^^^^^ Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values 3 | const state = x(null); 4 | return state[0]; 5 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-computed-store-to-frozen-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-computed-store-to-frozen-value.expect.md index 2318d38feb80f..7391ae00490e7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-computed-store-to-frozen-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-computed-store-to-frozen-value.expect.md @@ -16,13 +16,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX + +error.invalid-computed-store-to-frozen-value.ts:5:2 3 | // freeze 4 |
{x}
; > 5 | x[0] = true; - | ^ InvalidReact: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX (5:5) + | ^ Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX 6 | return x; 7 | } 8 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-hook-import.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-hook-import.expect.md index 14bf83054607e..0f2a99872b37b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-hook-import.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-hook-import.expect.md @@ -18,13 +18,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +error.invalid-conditional-call-aliased-hook-import.ts:6:11 4 | let data; 5 | if (props.cond) { > 6 | data = readFragment(); - | ^^^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (6:6) + | ^^^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 7 | } 8 | return data; 9 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-react-hook.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-react-hook.expect.md index 6c81f3d2be5b0..8ac4baa899ff6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-react-hook.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-aliased-react-hook.expect.md @@ -18,13 +18,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +error.invalid-conditional-call-aliased-react-hook.ts:6:10 4 | let s; 5 | if (props.cond) { > 6 | [s] = state(); - | ^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (6:6) + | ^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 7 | } 8 | return s; 9 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-non-hook-imported-as-hook.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-non-hook-imported-as-hook.expect.md index d0fb92e751c86..8b70421efd28d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-non-hook-imported-as-hook.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-call-non-hook-imported-as-hook.expect.md @@ -18,13 +18,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +error.invalid-conditional-call-non-hook-imported-as-hook.ts:6:11 4 | let data; 5 | if (props.cond) { > 6 | data = useArray(); - | ^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (6:6) + | ^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 7 | } 8 | return data; 9 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md index f1666cc4013f7..5af5db112fb1b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md @@ -22,15 +22,31 @@ function Component({item, cond}) { ## Error ``` +Found 2 errors: +Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + +error.invalid-conditional-setState-in-useMemo.ts:7:6 5 | useMemo(() => { 6 | if (cond) { > 7 | setPrevItem(item); - | ^^^^^^^^^^^ InvalidReact: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) (7:7) - -InvalidReact: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) (8:8) + | ^^^^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) 8 | setState(0); 9 | } 10 | }, [cond, key, init]); + + +Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + +error.invalid-conditional-setState-in-useMemo.ts:8:6 + 6 | if (cond) { + 7 | setPrevItem(item); +> 8 | setState(0); + | ^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + 9 | } + 10 | }, [cond, key, init]); + 11 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-computed-property-of-frozen-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-computed-property-of-frozen-value.expect.md index 7116e4d197154..363d4137f45ae 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-computed-property-of-frozen-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-computed-property-of-frozen-value.expect.md @@ -16,13 +16,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX + +error.invalid-delete-computed-property-of-frozen-value.ts:5:9 3 | // freeze 4 |
{x}
; > 5 | delete x[y]; - | ^ InvalidReact: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX (5:5) + | ^ Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX 6 | return x; 7 | } 8 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-property-of-frozen-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-property-of-frozen-value.expect.md index c6176d1afc5d9..ccea30731bdc8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-property-of-frozen-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-delete-property-of-frozen-value.expect.md @@ -16,13 +16,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX + +error.invalid-delete-property-of-frozen-value.ts:5:9 3 | // freeze 4 |
{x}
; > 5 | delete x.y; - | ^ InvalidReact: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX (5:5) + | ^ Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX 6 | return x; 7 | } 8 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-assignment-to-global.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-assignment-to-global.expect.md index b3471873eb079..7454d40695221 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-assignment-to-global.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-assignment-to-global.expect.md @@ -13,12 +13,18 @@ function useFoo(props) { ## Error ``` +Found 1 error: +Error: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) + +error.invalid-destructure-assignment-to-global.ts:2:3 1 | function useFoo(props) { > 2 | [x] = props; - | ^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (2:2) + | ^ Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) 3 | return {x}; 4 | } 5 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-to-local-global-variables.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-to-local-global-variables.expect.md index b3303fa189a33..dcb4a7af2fd82 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-to-local-global-variables.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-destructure-to-local-global-variables.expect.md @@ -15,13 +15,19 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) + +error.invalid-destructure-to-local-global-variables.ts:3:6 1 | function Component(props) { 2 | let a; > 3 | [a, b] = props.value; - | ^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3) + | ^ Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) 4 | 5 | return [a, b]; 6 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-disallow-mutating-ref-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-disallow-mutating-ref-in-render.expect.md index b5547a1328629..ee3619c3dd2be 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-disallow-mutating-ref-in-render.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-disallow-mutating-ref-in-render.expect.md @@ -16,13 +16,19 @@ function Component() { ## Error ``` +Found 1 error: +Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) + +error.invalid-disallow-mutating-ref-in-render.ts:4:2 2 | function Component() { 3 | const ref = useRef(null); > 4 | ref.current = false; - | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4) + | ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) 5 | 6 | return ; 11 | }); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-8566f9a360e2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-8566f9a360e2.expect.md index fabbf9b089a22..4d2c55cdaa1f0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-8566f9a360e2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-8566f9a360e2.expect.md @@ -20,13 +20,19 @@ const MemoizedButton = memo(function (props) { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +todo.error.invalid-rules-of-hooks-8566f9a360e2.ts:8:4 6 | const MemoizedButton = memo(function (props) { 7 | if (props.fancy) { > 8 | useCustomHook(); - | ^^^^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8) + | ^^^^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 9 | } 10 | return ; 11 | }); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-a0058f0b446d.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-a0058f0b446d.expect.md index b6e240e26c37d..47d099c101633 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-a0058f0b446d.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.invalid-rules-of-hooks-a0058f0b446d.expect.md @@ -19,13 +19,19 @@ function ComponentWithConditionalHook() { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +todo.error.invalid-rules-of-hooks-a0058f0b446d.ts:8:4 6 | function ComponentWithConditionalHook() { 7 | if (cond) { > 8 | Namespace.useConditionalHook(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 9 | } 10 | } 11 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-27c18dc8dad2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-27c18dc8dad2.expect.md index 83e94b7616692..b3f75f3ab8d74 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-27c18dc8dad2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-27c18dc8dad2.expect.md @@ -20,13 +20,19 @@ const FancyButton = React.forwardRef((props, ref) => { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +todo.error.rules-of-hooks-27c18dc8dad2.ts:8:4 6 | const FancyButton = React.forwardRef((props, ref) => { 7 | if (props.fancy) { > 8 | useCustomHook(); - | ^^^^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8) + | ^^^^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 9 | } 10 | return ; 11 | }); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-d0935abedc42.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-d0935abedc42.expect.md index a96e8e0878bc3..d5dd79b964976 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-d0935abedc42.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-d0935abedc42.expect.md @@ -19,13 +19,19 @@ React.unknownFunction((foo, bar) => { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +todo.error.rules-of-hooks-d0935abedc42.ts:8:4 6 | React.unknownFunction((foo, bar) => { 7 | if (foo) { > 8 | useNotAHook(bar); - | ^^^^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (8:8) + | ^^^^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 9 | } 10 | }); 11 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-e29c874aa913.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-e29c874aa913.expect.md index 6ce7fc2c8bcdf..d5e2cbcb83e27 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-e29c874aa913.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/todo.error.rules-of-hooks-e29c874aa913.expect.md @@ -20,13 +20,19 @@ function useHook() { ## Error ``` +Found 1 error: +Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +todo.error.rules-of-hooks-e29c874aa913.ts:9:4 7 | try { 8 | f(); > 9 | useState(); - | ^^^^^^^^ InvalidReact: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) (9:9) + | ^^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 10 | } catch {} 11 | } 12 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.error.object-pattern-computed-key.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.error.object-pattern-computed-key.expect.md index 1856784ce0eb7..83807391218cf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.error.object-pattern-computed-key.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.error.object-pattern-computed-key.expect.md @@ -21,13 +21,19 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` +Found 1 error: +Todo: (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern + +todo.error.object-pattern-computed-key.ts:5:9 3 | const SCALE = 2; 4 | function Component(props) { > 5 | const {[props.name]: value} = props; - | ^^^^^^^^^^^^^^^^^^^ Todo: (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern (5:5) + | ^^^^^^^^^^^^^^^^^^^ (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern 6 | return value; 7 | } 8 | + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md index aa3d989296a77..7e9247c5ae9a1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md @@ -29,10 +29,16 @@ function Component({prop1}) { ## Error ``` +Found 1 error: +Error: [Fire] Untransformed reference to compiler-required feature. + + Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:4) + +error.todo-syntax.ts:18:4 16 | }; 17 | useEffect(() => { > 18 | fire(foo()); - | ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (18:18) + | ^^^^ Untransformed `fire` call 19 | }); 20 | } 21 | diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md index 0141ffb8adb9a..7ec5c5320f4be 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md @@ -13,10 +13,16 @@ console.log(fire == null); ## Error ``` +Found 1 error: +Error: [Fire] Untransformed reference to compiler-required feature. + + null + +error.untransformed-fire-reference.ts:4:12 2 | import {fire} from 'react'; 3 | > 4 | console.log(fire == null); - | ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler (4:4) + | ^^^^ Untransformed `fire` call 5 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md index 275012351c25b..55c9cfcb2c127 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md @@ -30,10 +30,16 @@ function Component({props, bar}) { ## Error ``` +Found 1 error: +Error: [Fire] Untransformed reference to compiler-required feature. + + null + +error.use-no-memo.ts:15:4 13 | }; 14 | useEffect(() => { > 15 | fire(foo(props)); - | ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler (15:15) + | ^^^^ Untransformed `fire` call 16 | fire(foo()); 17 | fire(bar()); 18 | }); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md index e73451a896ee4..ad15e74d97079 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md @@ -27,13 +27,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Cannot compile `fire` + +All uses of foo must be either used with a fire() call in this effect or not used with a fire() call at all. foo was used with fire() on line 10:10 in this effect. + +error.invalid-mix-fire-and-no-fire.ts:11:6 9 | function nested() { 10 | fire(foo(props)); > 11 | foo(props); - | ^^^ InvalidReact: Cannot compile `fire`. All uses of foo must be either used with a fire() call in this effect or not used with a fire() call at all. foo was used with fire() on line 10:10 in this effect (11:11) + | ^^^ Cannot compile `fire` 12 | } 13 | 14 | nested(); + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md index 8329717cb3939..8cb5ce3d788b4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md @@ -22,13 +22,21 @@ function Component({bar, baz}) { ## Error ``` +Found 1 error: +Error: Cannot compile `fire` + +fire() can only take in a single call expression as an argument but received multiple arguments. + +error.invalid-multiple-args.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(foo(bar), baz); - | ^^^^^^^^^^^^^^^^^^^ InvalidReact: Cannot compile `fire`. fire() can only take in a single call expression as an argument but received multiple arguments (9:9) + | ^^^^^^^^^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-nested-use-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-nested-use-effect.expect.md index 1e1ff49b372cb..c36f0b4db987d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-nested-use-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-nested-use-effect.expect.md @@ -28,13 +28,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) + +Cannot call useEffect within a function expression. + +error.invalid-nested-use-effect.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | useEffect(() => { - | ^^^^^^^^^ InvalidReact: Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning). Cannot call useEffect within a function expression (9:9) + | ^^^^^^^^^ Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning) 10 | function nested() { 11 | fire(foo(props)); 12 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md index 855c7b7d706cb..a66ddd3350caf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md @@ -22,13 +22,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Cannot compile `fire` + +`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed. + +error.invalid-not-call.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(props); - | ^^^^^^^^^^^ InvalidReact: Cannot compile `fire`. `fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed (9:9) + | ^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-outside-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-outside-effect.expect.md index 687a21f98cdb4..3f752a4a44d65 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-outside-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-outside-effect.expect.md @@ -24,15 +24,35 @@ function Component({props, bar}) { ## Error ``` +Found 2 errors: +Invariant: Cannot compile `fire` + +Cannot use `fire` outside of a useEffect function. + +error.invalid-outside-effect.ts:8:2 6 | console.log(props); 7 | }; > 8 | fire(foo(props)); - | ^^^^ Invariant: Cannot compile `fire`. Cannot use `fire` outside of a useEffect function (8:8) - -Invariant: Cannot compile `fire`. Cannot use `fire` outside of a useEffect function (11:11) + | ^^^^ Cannot compile `fire` 9 | 10 | useCallback(() => { 11 | fire(foo(props)); + + +Invariant: Cannot compile `fire` + +Cannot use `fire` outside of a useEffect function. + +error.invalid-outside-effect.ts:11:4 + 9 | + 10 | useCallback(() => { +> 11 | fire(foo(props)); + | ^^^^ Cannot compile `fire` + 12 | }, [foo, props]); + 13 | + 14 | return null; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-no-array-literal.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-no-array-literal.expect.md index dcd9312bb2e53..846816b7d478e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-no-array-literal.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-no-array-literal.expect.md @@ -25,13 +25,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Invariant: Cannot compile `fire` + +You must use an array literal for an effect dependency array when that effect uses `fire()`. + +error.invalid-rewrite-deps-no-array-literal.ts:13:5 11 | useEffect(() => { 12 | fire(foo(props)); > 13 | }, deps); - | ^^^^ Invariant: Cannot compile `fire`. You must use an array literal for an effect dependency array when that effect uses `fire()` (13:13) + | ^^^^ Cannot compile `fire` 14 | 15 | return null; 16 | } + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-spread.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-spread.expect.md index 91c5523564cdd..436515da99651 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-spread.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-rewrite-deps-spread.expect.md @@ -28,13 +28,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Invariant: Cannot compile `fire` + +You must use an array literal for an effect dependency array when that effect uses `fire()`. + +error.invalid-rewrite-deps-spread.ts:15:7 13 | fire(foo(props)); 14 | }, > 15 | ...deps - | ^^^^ Invariant: Cannot compile `fire`. You must use an array literal for an effect dependency array when that effect uses `fire()` (15:15) + | ^^^^ Cannot compile `fire` 16 | ); 17 | 18 | return null; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md index c0b797fc14471..0c232de9745ff 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md @@ -22,13 +22,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Cannot compile `fire` + +fire() can only take in a single call expression as an argument but received a spread argument. + +error.invalid-spread.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(...foo); - | ^^^^^^^^^^^^ InvalidReact: Cannot compile `fire`. fire() can only take in a single call expression as an argument but received a spread argument (9:9) + | ^^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md index 3f237cfc6f364..9515d32eb7914 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md @@ -22,13 +22,21 @@ function Component(props) { ## Error ``` +Found 1 error: +Error: Cannot compile `fire` + +`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed. + +error.todo-method.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(props.foo()); - | ^^^^^^^^^^^^^^^^^ InvalidReact: Cannot compile `fire`. `fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed (9:9) + | ^^^^^^^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; + + ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md index dd48adcda7158..ac55bd0469935 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInPassiveEffects +// @validateNoSetStateInEffects import {useEffect, useState} from 'react'; function Component() { @@ -26,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInPassiveEffects +import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects import { useEffect, useState } from "react"; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js index 8b1e159071ef3..525f3e97d1911 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInPassiveEffects +// @validateNoSetStateInEffects import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md index 7fdd01fd0a76e..a7deed9afb09e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInPassiveEffects +// @validateNoSetStateInEffects import {useEffect, useState} from 'react'; function Component() { @@ -23,7 +23,7 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInPassiveEffects +import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects import { useEffect, useState } from "react"; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js index ba9720cba9b70..723e4841f6d3a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInPassiveEffects +// @validateNoSetStateInEffects import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts index 903afe4c20b9f..0ee50a0e761f0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts @@ -15,11 +15,11 @@ describe('parseConfigPragmaForTests()', () => { // Validate defaults first to make sure that the parser is getting the value from the pragma, // and not just missing it and getting the default value expect(defaultConfig.enableUseTypeAnnotations).toBe(false); - expect(defaultConfig.validateNoSetStateInPassiveEffects).toBe(false); + expect(defaultConfig.validateNoSetStateInEffects).toBe(false); expect(defaultConfig.validateNoSetStateInRender).toBe(true); const config = parseConfigPragmaForTests( - '@enableUseTypeAnnotations @validateNoSetStateInPassiveEffects:true @validateNoSetStateInRender:false', + '@enableUseTypeAnnotations @validateNoSetStateInEffects:true @validateNoSetStateInRender:false', {compilationMode: defaultOptions.compilationMode}, ); expect(config).toEqual({ @@ -28,7 +28,7 @@ describe('parseConfigPragmaForTests()', () => { environment: { ...defaultOptions.environment, enableUseTypeAnnotations: true, - validateNoSetStateInPassiveEffects: true, + validateNoSetStateInEffects: true, validateNoSetStateInRender: false, enableResetCacheOnSourceFileChanges: false, }, diff --git a/compiler/packages/babel-plugin-react-compiler/src/index.ts b/compiler/packages/babel-plugin-react-compiler/src/index.ts index bbd814b2b60e3..34244892756b4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/index.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/index.ts @@ -9,9 +9,12 @@ export {runBabelPluginReactCompiler} from './Babel/RunReactCompilerBabelPlugin'; export { CompilerError, CompilerErrorDetail, + CompilerDiagnostic, CompilerSuggestionOperation, ErrorSeverity, type CompilerErrorDetailOptions, + type CompilerDiagnosticOptions, + type CompilerDiagnosticDetail, } from './CompilerError'; export { compileFn as compile, diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts index 39c5dc2ea8e8c..6e931e467bec0 100644 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts @@ -104,8 +104,7 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration', + message: /Handle var kinds in VariableDeclaration/, }, ], }, @@ -119,8 +118,7 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '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', + message: /React Compiler has skipped optimizing this component/, suggestions: [ { output: normalizeIndent` @@ -158,12 +156,10 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration', + message: /Handle var kinds in VariableDeclaration/, }, { - message: - 'Mutating component props or hook arguments is not allowed. Consider using a local variable instead', + message: /Mutating component props or hook arguments is not allowed/, }, ], }, @@ -182,8 +178,7 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '[ReactCompilerBailout] (BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration (@:3:2)', + message: /Handle var kinds in VariableDeclaration/, }, ], }, @@ -200,7 +195,7 @@ const tests: CompilerTestCases = { errors: [ { message: - 'Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)', + /Unexpected reassignment of a variable which was defined outside of the component/, }, ], }, @@ -274,8 +269,7 @@ const tests: CompilerTestCases = { ], errors: [ { - message: - '[InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.', + message: /Cannot infer dependencies of this effect/, }, ], }, diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRuleTypescript-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRuleTypescript-test.ts index f67ff673cbbbb..071bfb2e7b5f4 100644 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRuleTypescript-test.ts +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRuleTypescript-test.ts @@ -61,8 +61,7 @@ const tests: CompilerTestCases = { `, errors: [ { - message: - "Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead", + message: /Mutating a value returned from 'useState\(\)'/, line: 7, }, ], diff --git a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts index e9eee26bdabc6..51bc4e07533e4 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts @@ -10,6 +10,9 @@ import {transformFromAstSync} from '@babel/core'; import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods'; import type {SourceLocation as BabelSourceLocation} from '@babel/types'; import BabelPluginReactCompiler, { + CompilerDiagnostic, + CompilerDiagnosticOptions, + CompilerErrorDetail, CompilerErrorDetailOptions, CompilerSuggestionOperation, ErrorSeverity, @@ -18,15 +21,11 @@ import BabelPluginReactCompiler, { OPT_OUT_DIRECTIVES, type PluginOptions, } from 'babel-plugin-react-compiler/src'; -import {Logger} from 'babel-plugin-react-compiler/src/Entrypoint'; +import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint'; import type {Rule} from 'eslint'; import {Statement} from 'estree'; import * as HermesParser from 'hermes-parser'; -type CompilerErrorDetailWithLoc = Omit & { - loc: BabelSourceLocation; -}; - function assertExhaustive(_: never, errorMsg: string): never { throw new Error(errorMsg); } @@ -38,19 +37,15 @@ const DEFAULT_REPORTABLE_LEVELS = new Set([ let reportableLevels = DEFAULT_REPORTABLE_LEVELS; function isReportableDiagnostic( - detail: CompilerErrorDetailOptions, -): detail is CompilerErrorDetailWithLoc { - return ( - reportableLevels.has(detail.severity) && - detail.loc != null && - typeof detail.loc !== 'symbol' - ); + detail: CompilerErrorDetail | CompilerDiagnostic, +): boolean { + return reportableLevels.has(detail.severity); } function makeSuggestions( - detail: CompilerErrorDetailOptions, + detail: CompilerErrorDetailOptions | CompilerDiagnosticOptions, ): Array { - let suggest: Array = []; + const suggest: Array = []; if (Array.isArray(detail.suggestions)) { for (const suggestion of detail.suggestions) { switch (suggestion.op) { @@ -107,6 +102,12 @@ const COMPILER_OPTIONS: Partial = { flowSuppressions: false, environment: validateEnvironmentConfig({ validateRefAccessDuringRender: false, + validateNoSetStateInRender: true, + validateNoSetStateInEffects: true, + validateNoJSXInTryStatements: true, + validateNoImpureFunctionsInRender: true, + validateStaticComponents: true, + validateNoFreezingKnownMutableFunctions: true, }), }; @@ -128,10 +129,10 @@ const rule: Rule.RuleModule = { const filename = context.filename ?? context.getFilename(); const userOpts = context.options[0] ?? {}; if ( - userOpts['reportableLevels'] != null && - userOpts['reportableLevels'] instanceof Set + userOpts.reportableLevels != null && + userOpts.reportableLevels instanceof Set ) { - reportableLevels = userOpts['reportableLevels']; + reportableLevels = userOpts.reportableLevels; } else { reportableLevels = DEFAULT_REPORTABLE_LEVELS; } @@ -144,11 +145,11 @@ const rule: Rule.RuleModule = { */ let __unstable_donotuse_reportAllBailouts: boolean = false; if ( - userOpts['__unstable_donotuse_reportAllBailouts'] != null && - typeof userOpts['__unstable_donotuse_reportAllBailouts'] === 'boolean' + userOpts.__unstable_donotuse_reportAllBailouts != null && + typeof userOpts.__unstable_donotuse_reportAllBailouts === 'boolean' ) { __unstable_donotuse_reportAllBailouts = - userOpts['__unstable_donotuse_reportAllBailouts']; + userOpts.__unstable_donotuse_reportAllBailouts; } let shouldReportUnusedOptOutDirective = true; @@ -162,16 +163,17 @@ const rule: Rule.RuleModule = { }); const userLogger: Logger | null = options.logger; options.logger = { - logEvent: (filename, event): void => { - userLogger?.logEvent(filename, event); + logEvent: (eventFilename, event): void => { + userLogger?.logEvent(eventFilename, event); if (event.kind === 'CompileError') { shouldReportUnusedOptOutDirective = false; const detail = event.detail; - const suggest = makeSuggestions(detail); + const suggest = makeSuggestions(detail.options); if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) { + const loc = detail.primaryLocation(); const locStr = - detail.loc != null && typeof detail.loc !== 'symbol' - ? ` (@:${detail.loc.start.line}:${detail.loc.start.column})` + loc != null && typeof loc !== 'symbol' + ? ` (@:${loc.start.line}:${loc.start.column})` : ''; /** * Report bailouts with a smaller span (just the first line). @@ -187,10 +189,10 @@ const rule: Rule.RuleModule = { endLoc = { line: event.fnLoc.start.line, // Babel loc line numbers are 1-indexed - column: sourceCode.text.split( - /\r?\n|\r|\n/g, - event.fnLoc.start.line, - )[event.fnLoc.start.line - 1].length, + column: + sourceCode.text.split(/\r?\n|\r|\n/g)[ + event.fnLoc.start.line - 1 + ]?.length ?? 0, }; } const firstLineLoc = { @@ -198,29 +200,30 @@ const rule: Rule.RuleModule = { end: endLoc, }; context.report({ - message: `[ReactCompilerBailout] ${detail.reason}${locStr}`, + message: `${detail.printErrorMessage(sourceCode.text)} ${locStr}`, loc: firstLineLoc, suggest, }); } - if (!isReportableDiagnostic(detail)) { + const loc = detail.primaryLocation(); + if ( + !isReportableDiagnostic(detail) || + loc == null || + typeof loc === 'symbol' + ) { return; } if ( - hasFlowSuppression(detail.loc, 'react-rule-hook') || - hasFlowSuppression(detail.loc, 'react-rule-unsafe-ref') + hasFlowSuppression(loc, 'react-rule-hook') || + hasFlowSuppression(loc, 'react-rule-unsafe-ref') ) { // If Flow already caught this error, we don't need to report it again. return; } - const loc = - detail.loc == null || typeof detail.loc == 'symbol' - ? event.fnLoc - : detail.loc; if (loc != null) { context.report({ - message: detail.reason, + message: detail.printErrorMessage(sourceCode.text), loc, suggest, }); @@ -233,8 +236,8 @@ const rule: Rule.RuleModule = { options.environment = validateEnvironmentConfig( options.environment ?? {}, ); - } catch (err) { - options.logger?.logEvent('', err); + } catch (err: unknown) { + options.logger?.logEvent('', err as LoggerEvent); } function hasFlowSuppression( diff --git a/compiler/packages/snap/src/runner-worker.ts b/compiler/packages/snap/src/runner-worker.ts index fd4763b20322e..554348534e305 100644 --- a/compiler/packages/snap/src/runner-worker.ts +++ b/compiler/packages/snap/src/runner-worker.ts @@ -145,29 +145,6 @@ async function compile( console.error(e.stack); } error = e.message.replace(/\u001b[^m]*m/g, ''); - const loc = e.details?.[0]?.loc; - if (loc != null) { - try { - error = codeFrameColumns( - input, - { - start: { - line: loc.start.line, - column: loc.start.column + 1, - }, - end: { - line: loc.end.line, - column: loc.end.column + 1, - }, - }, - { - message: e.message, - }, - ); - } catch { - // In case the location data isn't valid, skip printing a code frame. - } - } } // Promote console errors so they can be recorded in fixture output diff --git a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts index 30762e5819535..1da7d8f57fbc7 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts +++ b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts @@ -106,8 +106,7 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration', + message: /Handle var kinds in VariableDeclaration/, }, ], }, @@ -121,8 +120,7 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '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', + message: /React Compiler has skipped optimizing this component/, suggestions: [ { output: normalizeIndent` @@ -160,12 +158,10 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration', + message: /Handle var kinds in VariableDeclaration/, }, { - message: - 'Mutating component props or hook arguments is not allowed. Consider using a local variable instead', + message: /Mutating component props or hook arguments is not allowed/, }, ], }, @@ -184,8 +180,7 @@ const tests: CompilerTestCases = { }`, errors: [ { - message: - '[ReactCompilerBailout] (BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration (@:3:2)', + message: /Handle var kinds in VariableDeclaration/, }, ], }, @@ -202,7 +197,7 @@ const tests: CompilerTestCases = { errors: [ { message: - 'Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)', + /Unexpected reassignment of a variable which was defined outside of the component/, }, ], }, @@ -248,6 +243,37 @@ const tests: CompilerTestCases = { }, ], }, + { + name: 'Pipeline errors are reported', + code: normalizeIndent` + import useMyEffect from 'useMyEffect'; + function Component({a}) { + 'use no memo'; + useMyEffect(() => console.log(a.b)); + return
Hello world
; + } + `, + options: [ + { + environment: { + inferEffectDependencies: [ + { + function: { + source: 'useMyEffect', + importSpecifierName: 'default', + }, + numRequiredArgs: 1, + }, + ], + }, + }, + ], + errors: [ + { + message: /Cannot infer dependencies of this effect/, + }, + ], + }, ], }; @@ -259,4 +285,4 @@ const eslintTester = new ESLintTesterV8({ enableExperimentalComponentSyntax: true, }, }); -eslintTester.run('react-compiler - eslint: v8', ReactCompilerRule, tests); +eslintTester.run('react-compiler', ReactCompilerRule, tests); diff --git a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts index 2efe6c7a38419..28133aee7bc4f 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts +++ b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts @@ -63,8 +63,7 @@ const tests: CompilerTestCases = { `, errors: [ { - message: - "Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead", + message: /Mutating a value returned from 'useState\(\)'/, line: 7, }, ], @@ -75,4 +74,4 @@ const tests: CompilerTestCases = { const eslintTester = new ESLintTesterV8({ parser: require.resolve('@typescript-eslint/parser-v5'), }); -eslintTester.run('react-compiler - eslint: v8', ReactCompilerRule, tests); +eslintTester.run('react-compiler', ReactCompilerRule, tests); diff --git a/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts b/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts index 67d5745a1c7ea..254962d99ccec 100644 --- a/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts +++ b/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts @@ -11,7 +11,10 @@ import {transformFromAstSync} from '@babel/core'; import PluginProposalPrivateMethods from '@babel/plugin-transform-private-methods'; import type {SourceLocation as BabelSourceLocation} from '@babel/types'; import BabelPluginReactCompiler, { + type CompilerErrorDetail, type CompilerErrorDetailOptions, + type CompilerDiagnostic, + type CompilerDiagnosticOptions, CompilerSuggestionOperation, ErrorSeverity, parsePluginOptions, @@ -25,10 +28,6 @@ import type {Rule} from 'eslint'; import {Statement} from 'estree'; import * as HermesParser from 'hermes-parser'; -type CompilerErrorDetailWithLoc = Omit & { - loc: BabelSourceLocation; -}; - function assertExhaustive(_: never, errorMsg: string): never { throw new Error(errorMsg); } @@ -40,17 +39,13 @@ const DEFAULT_REPORTABLE_LEVELS = new Set([ let reportableLevels = DEFAULT_REPORTABLE_LEVELS; function isReportableDiagnostic( - detail: CompilerErrorDetailOptions, -): detail is CompilerErrorDetailWithLoc { - return ( - reportableLevels.has(detail.severity) && - detail.loc != null && - typeof detail.loc !== 'symbol' - ); + detail: CompilerErrorDetail | CompilerDiagnostic, +): boolean { + return reportableLevels.has(detail.severity); } function makeSuggestions( - detail: CompilerErrorDetailOptions, + detail: CompilerErrorDetailOptions | CompilerDiagnosticOptions, ): Array { const suggest: Array = []; if (Array.isArray(detail.suggestions)) { @@ -109,6 +104,12 @@ const COMPILER_OPTIONS: Partial = { flowSuppressions: false, environment: validateEnvironmentConfig({ validateRefAccessDuringRender: false, + validateNoSetStateInRender: true, + validateNoSetStateInEffects: true, + validateNoJSXInTryStatements: true, + validateNoImpureFunctionsInRender: true, + validateStaticComponents: true, + validateNoFreezingKnownMutableFunctions: true, }), }; @@ -169,11 +170,12 @@ const rule: Rule.RuleModule = { if (event.kind === 'CompileError') { shouldReportUnusedOptOutDirective = false; const detail = event.detail; - const suggest = makeSuggestions(detail); + const suggest = makeSuggestions(detail.options); if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) { + const loc = detail.primaryLocation(); const locStr = - detail.loc != null && typeof detail.loc !== 'symbol' - ? ` (@:${detail.loc.start.line}:${detail.loc.start.column})` + loc != null && typeof loc !== 'symbol' + ? ` (@:${loc.start.line}:${loc.start.column})` : ''; /** * Report bailouts with a smaller span (just the first line). @@ -200,29 +202,30 @@ const rule: Rule.RuleModule = { end: endLoc, }; context.report({ - message: `[ReactCompilerBailout] ${detail.reason}${locStr}`, + message: `${detail.printErrorMessage(sourceCode.text)} ${locStr}`, loc: firstLineLoc, suggest, }); } - if (!isReportableDiagnostic(detail)) { + const loc = detail.primaryLocation(); + if ( + !isReportableDiagnostic(detail) || + loc == null || + typeof loc === 'symbol' + ) { return; } if ( - hasFlowSuppression(detail.loc, 'react-rule-hook') || - hasFlowSuppression(detail.loc, 'react-rule-unsafe-ref') + hasFlowSuppression(loc, 'react-rule-hook') || + hasFlowSuppression(loc, 'react-rule-unsafe-ref') ) { // If Flow already caught this error, we don't need to report it again. return; } - const loc = - detail.loc == null || typeof detail.loc === 'symbol' - ? event.fnLoc - : detail.loc; if (loc != null) { context.report({ - message: detail.reason, + message: detail.printErrorMessage(sourceCode.text), loc, suggest, });