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 58167194249..18946703a03 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 75e01abaefa..cac897a9269 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 0c23ceb3452..c13940ed10a 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 c5ca3434b1b..648ff01ba71 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 de8d16fb12a..79bbee37a56 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 d363e11831d..16d7c3713c6 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 cd7d6a7a35b..5b5702f19c6 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 a552803171a..5b5b78fc520 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 c3a6c18d3aa..81959ea3615 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 a36c347faa0..e9b0fdb8870 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 500edab8f20..f8a6330977f 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 f44ae83b2ce..eaa480f7c5f 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 5553f235a08..d15aba19d12 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 d380137836c..634d98394c3 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 3f0b5530ee2..2bcf5a49f8e 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 1d5b4abdf7d..988a8dbab8d 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 d74ebd119c3..c6653177a7a 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 e1cebb00df5..84370796a67 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 cb2ce1a20df..fea112547e4 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 94b3ae1035e..dad64bcbd8d 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 d8b0f8facfe..e2894b6efd2 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 39dc43e4a56..ecc0303692e 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 cff34e34493..9c9cd94dbd1 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 7ea8ae98093..86af8042219 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 c2ad547414a..427a573dc78 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 0318fa9525f..de50b215437 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 2a6dce11f24..6823db842d2 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 dbf084466d8..02e06c7a82f 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 b08d151be64..c0bd287e12e 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 a54cc98708f..d1e14765353 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 76ac6d77a27..47af9952483 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 048fee7ee1d..dcde3a9f83f 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 ca3ee2ae138..ea6683fd0ad 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 1ba0d59e172..c3ab81ba38b 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 5e0a9886272..7174acc43d2 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 c5af59d6424..7a969400a33 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 0949fb3072f..f3716d810c5 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 d92d918fe9f..abf18e43e3f 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 db616600e80..1c5c92d2c30 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 02748366456..d3dd7317ef9 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 e2ce2cceae3..7d7a0dafcee 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 0440117adbf..137d29cbc29 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 a4327cf961b..6abdb5b2efc 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 2318d38feb8..7391ae00490 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 14bf8305460..0f2a99872b3 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 6c81f3d2be5..8ac4baa899f 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 d0fb92e751c..8b70421efd2 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 f1666cc4013..5af5db112fb 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 7116e4d1971..363d4137f45 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 c6176d1afc5..ccea30731bd 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 b3471873eb0..7454d406952 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 b3303fa189a..dcb4a7af2fd 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 b5547a13286..ee3619c3dd2 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 fabbf9b089a..4d2c55cdaa1 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 b6e240e26c3..47d099c1016 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 83e94b76166..b3f75f3ab8d 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 a96e8e0878b..d5dd79b9649 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 6ce7fc2c8bc..d5e2cbcb83e 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 1856784ce0e..83807391218 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 aa3d989296a..7e9247c5ae9 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 0141ffb8adb..7ec5c5320f4 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 275012351c2..55c9cfcb2c1 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 e73451a896e..ad15e74d970 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 8329717cb39..8cb5ce3d788 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 1e1ff49b372..c36f0b4db98 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 855c7b7d706..a66ddd3350c 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 687a21f98cd..3f752a4a44d 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 dcd9312bb2e..846816b7d47 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 91c5523564c..436515da996 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 c0b797fc144..0c232de9745 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 3f237cfc6f3..9515d32eb79 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 dd48adcda71..ac55bd04699 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 8b1e159071e..525f3e97d19 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 7fdd01fd0a7..a7deed9afb0 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 ba9720cba9b..723e4841f6d 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 903afe4c20b..0ee50a0e761 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 bbd814b2b60..34244892756 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 39c5dc2ea8e..6e931e467be 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 f67ff673cbb..071bfb2e7b5 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 e9eee26bdab..51bc4e07533 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 fd4763b2032..554348534e3 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 30762e58195..1da7d8f57fb 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 2efe6c7a384..28133aee7bc 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 67d5745a1c7..254962d99cc 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, });