Compare commits

..

2 Commits

Author SHA1 Message Date
Joe Savona
9bd9e33e42 [compiler] Improve hook validation diagnostics 2025-08-20 17:49:04 -07:00
Mofei Zhang
7752f95dc8 [compiler] Aggregate error reporting, separate eslint rules
NOTE: this is a merged version of @mofeiZ's original PR along with my edits per offline discussion. The description is updated to reflect the latest approach.

The key problem we're trying to solve with this PR is to allow developers more control over the compiler's various validations. The idea is to have a number of rules targeting a specific category of issues, such as enforcing immutability of props/state/etc or disallowing access to refs during render. We don't want to have to run the compiler again for every single rule, though, so @mofeiZ added an LRU cache that caches the full compilation output of N most recent files. The first rule to run on a given file will cause it to get cached, and then subsequent rules can pull from the cache, with each rule filtering down to its specific category of errors.

For the categories, I went through and assigned a category roughly 1:1 to existing validations, and then used my judgement on some places that felt distinct enough to warrant a separate error. Every error in the compiler now has to supply both a severity (for legacy reasons) and a category (for ESLint). Each category corresponds 1:1 to a ESLint rule definition, so that the set of rules is automatically populated based on the defined categories.

Categories include a flag for whether they should be in the recommended set or not.

Note that as with the original version of this PR, only eslint-plugin-react-compiler is changed. We still have to update the main lint rule.
2025-08-20 17:37:58 -07:00
309 changed files with 2079 additions and 20191 deletions

View File

@@ -28,6 +28,3 @@ packages/react-devtools-shared/src/hooks/__tests__/__source__/__untransformed__/
packages/react-devtools-shell/dist
packages/react-devtools-timeline/dist
packages/react-devtools-timeline/static
# Imported third-party Flow types
flow-typed/

View File

@@ -547,10 +547,13 @@ module.exports = {
},
globals: {
$Call: 'readonly',
$ElementType: 'readonly',
$Flow$ModuleRef: 'readonly',
$FlowFixMe: 'readonly',
$Keys: 'readonly',
$NonMaybeType: 'readonly',
$PropertyType: 'readonly',
$ReadOnly: 'readonly',
$ReadOnlyArray: 'readonly',
$ArrayBufferView: 'readonly',
@@ -565,7 +568,6 @@ module.exports = {
BigInt: 'readonly',
BigInt64Array: 'readonly',
BigUint64Array: 'readonly',
CacheType: 'readonly',
Class: 'readonly',
ClientRect: 'readonly',
CopyInspectedElementPath: 'readonly',
@@ -580,15 +582,13 @@ module.exports = {
IteratorResult: 'readonly',
JSONValue: 'readonly',
JSResourceReference: 'readonly',
mixin$Animatable: 'readonly',
MouseEventHandler: 'readonly',
NavigateEvent: 'readonly',
PerformanceMeasureOptions: 'readonly',
PropagationPhases: 'readonly',
PropertyDescriptor: 'readonly',
PropertyDescriptorMap: 'readonly',
Proxy$traps: 'readonly',
React$AbstractComponent: 'readonly',
React$Component: 'readonly',
React$ComponentType: 'readonly',
React$Config: 'readonly',
React$Context: 'readonly',
React$Element: 'readonly',
@@ -620,6 +620,7 @@ module.exports = {
PropertyIndexedKeyframes: 'readonly',
KeyframeAnimationOptions: 'readonly',
GetAnimationsOptions: 'readonly',
Animatable: 'readonly',
ScrollTimeline: 'readonly',
EventListenerOptionsOrUseCapture: 'readonly',
FocusOptions: 'readonly',

View File

@@ -57,6 +57,8 @@ jobs:
key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }}
- run: npx playwright install --with-deps chromium
if: steps.cache_playwright_browsers.outputs.cache-hit != 'true'
- run: npx playwright install-deps
if: steps.cache_playwright_browsers.outputs.cache-hit == 'true'
- run: CI=true yarn test
- run: ls -R test-results
if: '!cancelled()'

View File

@@ -316,7 +316,7 @@ jobs:
if: steps.node_modules.outputs.cache-hit != 'true'
- run: ./scripts/react-compiler/build-compiler.sh && ./scripts/react-compiler/link-compiler.sh
- run: yarn workspace eslint-plugin-react-hooks test
# ----- BUILD -----
build_and_lint:
name: yarn build and lint
@@ -811,18 +811,9 @@ jobs:
pattern: _build_*
path: build
merge-multiple: true
- name: Check Playwright version
id: playwright_version
run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT"
- name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }}
id: cache_playwright_browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }}
- name: Playwright install deps
if: steps.cache_playwright_browsers.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium
- run: |
npx playwright install
sudo npx playwright install-deps
- run: ./scripts/ci/run_devtools_e2e_tests.js
env:
RELEASE_CHANNEL: experimental

View File

@@ -18,7 +18,6 @@ jobs:
permissions:
# Used to create a review and close PRs
pull-requests: write
contents: write
steps:
- name: Close PR
uses: actions/github-script@v7

View File

@@ -1,103 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
import {parseConfigPragmaAsString} from 'babel-plugin-react-compiler';
import type {editor} from 'monaco-editor';
import * as monaco from 'monaco-editor';
import parserBabel from 'prettier/plugins/babel';
import * as prettierPluginEstree from 'prettier/plugins/estree';
import * as prettier from 'prettier/standalone';
import {useState, useEffect} from 'react';
import {Resizable} from 're-resizable';
import {useStore} from '../StoreContext';
import {monacoOptions} from './monacoOptions';
loader.config({monaco});
export default function ConfigEditor(): JSX.Element {
const [, setMonaco] = useState<Monaco | null>(null);
const store = useStore();
// Parse string-based override config from pragma comment and format it
const [configJavaScript, setConfigJavaScript] = useState('');
useEffect(() => {
const pragma = store.source.substring(0, store.source.indexOf('\n'));
const configString = `(${parseConfigPragmaAsString(pragma)})`;
prettier
.format(configString, {
semi: true,
parser: 'babel-ts',
plugins: [parserBabel, prettierPluginEstree],
})
.then(formatted => {
setConfigJavaScript(formatted);
})
.catch(error => {
console.error('Error formatting config:', error);
setConfigJavaScript('({})'); // Return empty object if not valid for now
//TODO: Add validation and error handling for config
});
console.log('Config:', configString);
}, [store.source]);
const handleChange: (value: string | undefined) => void = value => {
if (!value) return;
// TODO: Implement sync logic to update pragma comments in the source
console.log('Config changed:', value);
};
const handleMount: (
_: editor.IStandaloneCodeEditor,
monaco: Monaco,
) => void = (_, monaco) => {
setMonaco(monaco);
const uri = monaco.Uri.parse(`file:///config.js`);
const model = monaco.editor.getModel(uri);
if (model) {
model.updateOptions({tabSize: 2});
}
};
return (
<div className="relative flex flex-col flex-none border-r border-gray-200">
<h2 className="p-4 duration-150 ease-in border-b cursor-default border-grey-200 font-light text-secondary">
Config Overrides
</h2>
<Resizable
minWidth={300}
maxWidth={600}
defaultSize={{width: 350, height: 'auto'}}
enable={{right: true}}
className="!h-[calc(100vh_-_3.5rem_-_4rem)]">
<MonacoEditor
path={'config.js'}
language={'javascript'}
value={configJavaScript}
onMount={handleMount}
onChange={handleChange}
options={{
...monacoOptions,
readOnly: true,
lineNumbers: 'off',
folding: false,
renderLineHighlight: 'none',
scrollBeyondLastLine: false,
hideCursorInOverviewRuler: true,
overviewRulerBorder: false,
overviewRulerLanes: 0,
fontSize: 12,
}}
/>
</Resizable>
</div>
);
}

View File

@@ -37,7 +37,6 @@ import {
type Store,
} from '../../lib/stores';
import {useStore, useStoreDispatch} from '../StoreContext';
import ConfigEditor from './ConfigEditor';
import Input from './Input';
import {
CompilerOutput,
@@ -47,7 +46,6 @@ import {
} from './Output';
import {transformFromAstSync} from '@babel/core';
import {LoggerEvent} from 'babel-plugin-react-compiler/dist/Entrypoint';
import {useSearchParams} from 'next/navigation';
function parseInput(
input: string,
@@ -293,13 +291,7 @@ export default function Editor(): JSX.Element {
[deferredStore.source],
);
// TODO: Remove this once the config editor is more stable
const searchParams = useSearchParams();
const search = searchParams.get('showConfig');
const shouldShowConfig = search === 'true';
useMountEffect(() => {
// Initialize store
let mountStore: Store;
try {
mountStore = initStoreFromUrlOrLocalStorage();
@@ -336,7 +328,6 @@ export default function Editor(): JSX.Element {
return (
<>
<div className="relative flex basis top-14">
{shouldShowConfig && <ConfigEditor />}
<div className={clsx('relative sm:basis-1/4')}>
<Input language={language} errors={errors} />
</div>

View File

@@ -19,8 +19,7 @@
"test": "yarn workspaces run test",
"snap": "yarn workspace babel-plugin-react-compiler run snap",
"snap:build": "yarn workspace snap run build",
"npm:publish": "node scripts/release/publish",
"eslint-docs": "yarn workspace babel-plugin-react-compiler build && node scripts/build-eslint-docs.js"
"npm:publish": "node scripts/release/publish"
},
"dependencies": {
"fs-extra": "^4.0.2",

View File

@@ -7,10 +7,9 @@
import * as t from '@babel/types';
import {codeFrameColumns} from '@babel/code-frame';
import {type SourceLocation} from './HIR';
import type {SourceLocation} from './HIR';
import {Err, Ok, Result} from './Utils/Result';
import {assertExhaustive} from './Utils/utils';
import invariant from 'invariant';
export enum ErrorSeverity {
/**
@@ -541,9 +540,6 @@ export enum ErrorCategory {
// Checking for valid usage of manual memoization
UseMemo = 'UseMemo',
// Checking for higher order functions acting as factories for components/hooks
Factories = 'Factories',
// Checks that manual memoization is preserved
PreserveManualMemo = 'PreserveManualMemo',
@@ -632,18 +628,7 @@ export type LintRule = {
recommended: boolean;
};
const RULE_NAME_PATTERN = /^[a-z]+(-[a-z]+)*$/;
export function getRuleForCategory(category: ErrorCategory): LintRule {
const rule = getRuleForCategoryImpl(category);
invariant(
RULE_NAME_PATTERN.test(rule.name),
`Invalid rule name, got '${rule.name}' but rules must match ${RULE_NAME_PATTERN.toString()}`,
);
return rule;
}
function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
switch (category) {
case ErrorCategory.AutomaticEffectDependencies: {
return {
@@ -651,7 +636,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
name: 'automatic-effect-dependencies',
description:
'Verifies that automatic effect dependencies are compiled if opted-in',
recommended: false,
recommended: true,
};
}
case ErrorCategory.CapitalizedCalls: {
@@ -667,7 +652,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
return {
category,
name: 'config',
description: 'Validates the compiler configuration options',
description: 'Validates the configuration',
recommended: true,
};
}
@@ -693,7 +678,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'set-state-in-effect',
description:
'Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance',
'Validates against calling setState synchronously in an effect',
recommended: true,
};
}
@@ -702,17 +687,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'error-boundaries',
description:
'Validates usage of error boundaries instead of try/catch for errors in child components',
recommended: true,
};
}
case ErrorCategory.Factories: {
return {
category,
name: 'component-hook-factories',
description:
'Validates against higher order functions defining nested components or hooks. ' +
'Components and hooks should be defined at the module level',
'Validates usage of error boundaries instead of try/catch for errors in JSX',
recommended: true,
};
}
@@ -736,8 +711,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
return {
category,
name: 'gating',
description:
'Validates configuration of [gating mode](https://react.dev/reference/react-compiler/gating)',
description: 'Validates configuration of gating mode',
recommended: true,
};
}
@@ -746,8 +720,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'globals',
description:
'Validates against assignment/mutation of globals during render, part of ensuring that ' +
'[side effects must render outside of render](https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)',
'Validates against assignment/mutation of globals during render',
recommended: true,
};
}
@@ -769,7 +742,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'immutability',
description:
'Validates against mutating props, state, and other values that [are immutable](https://react.dev/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable)',
'Validates that immutable values (props, state, etc) are not mutated',
recommended: true,
};
}
@@ -786,9 +759,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'preserve-manual-memoization',
description:
'Validates that existing manual memoized is preserved by the compiler. ' +
'React Compiler will only compile components and hooks if its inference ' +
'[matches or exceeds the existing manual memoization](https://react.dev/learn/react-compiler/introduction#what-should-i-do-about-usememo-usecallback-and-reactmemo)',
'Validates that existing manual memoized is preserved by the compiler',
recommended: true,
};
}
@@ -797,7 +768,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'purity',
description:
'Validates that [components/hooks are pure](https://react.dev/reference/rules/components-and-hooks-must-be-pure) by checking that they do not call known-impure functions',
'Validates that the component/hook is pure, and does not call known-impure functions',
recommended: true,
};
}
@@ -806,7 +777,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'refs',
description:
'Validates correct usage of refs, not reading/writing during render. See the "pitfalls" section in [`useRef()` usage](https://react.dev/reference/react/useRef#usage)',
'Validates correct usage of refs, not reading/writing during render',
recommended: true,
};
}
@@ -814,8 +785,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
return {
category,
name: 'set-state-in-render',
description:
'Validates against setting state during render, which can trigger additional renders and potential infinite render loops',
description: 'Validates against setting state during render',
recommended: true,
};
}
@@ -824,7 +794,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
category,
name: 'static-components',
description:
'Validates that components are static, not recreated every render. Components that are recreated dynamically can reset state and trigger excessive re-rendering',
'Validates that components are static, not recreated every render',
recommended: true,
};
}
@@ -856,8 +826,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
return {
category,
name: 'unsupported-syntax',
description:
'Validates against syntax that we do not plan to support in React Compiler',
description: 'Validates against syntax that we do not plan to support',
recommended: true,
};
}
@@ -865,8 +834,7 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
return {
category,
name: 'use-memo',
description:
'Validates usage of the useMemo() hook against common mistakes. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.',
description: 'Validates usage of the useMemo() hook',
recommended: true,
};
}

View File

@@ -494,20 +494,7 @@ function findFunctionsToCompile(
): Array<CompileSource> {
const queue: Array<CompileSource> = [];
const traverseFunction = (fn: BabelFn, pass: CompilerPass): void => {
// In 'all' mode, compile only top level functions
if (
pass.opts.compilationMode === 'all' &&
fn.scope.getProgramParent() !== fn.scope.parent
) {
return;
}
const fnType = getReactFunctionType(fn, pass);
if (pass.opts.environment.validateNoDynamicallyCreatedComponentsOrHooks) {
validateNoDynamicallyCreatedComponentsOrHooks(fn, pass, programContext);
}
if (fnType === null || programContext.alreadyCompiled.has(fn.node)) {
return;
}
@@ -852,73 +839,6 @@ function shouldSkipCompilation(
return false;
}
/**
* Validates that Components/Hooks are always defined at module level. This prevents scope reference
* errors that occur when the compiler attempts to optimize the nested component/hook while its
* parent function remains uncompiled.
*/
function validateNoDynamicallyCreatedComponentsOrHooks(
fn: BabelFn,
pass: CompilerPass,
programContext: ProgramContext,
): void {
const parentNameExpr = getFunctionName(fn);
const parentName =
parentNameExpr !== null && parentNameExpr.isIdentifier()
? parentNameExpr.node.name
: '<anonymous>';
const validateNestedFunction = (
nestedFn: NodePath<
t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression
>,
): void => {
if (
nestedFn.node === fn.node ||
programContext.alreadyCompiled.has(nestedFn.node)
) {
return;
}
if (nestedFn.scope.getProgramParent() !== nestedFn.scope.parent) {
const nestedFnType = getReactFunctionType(nestedFn as BabelFn, pass);
const nestedFnNameExpr = getFunctionName(nestedFn as BabelFn);
const nestedName =
nestedFnNameExpr !== null && nestedFnNameExpr.isIdentifier()
? nestedFnNameExpr.node.name
: '<anonymous>';
if (nestedFnType === 'Component' || nestedFnType === 'Hook') {
CompilerError.throwDiagnostic({
category: ErrorCategory.Factories,
severity: ErrorSeverity.InvalidReact,
reason: `Components and hooks cannot be created dynamically`,
description: `The function \`${nestedName}\` appears to be a React ${nestedFnType.toLowerCase()}, but it's defined inside \`${parentName}\`. Components and Hooks should always be declared at module scope`,
details: [
{
kind: 'error',
message: 'this function dynamically created a component/hook',
loc: parentNameExpr?.node.loc ?? fn.node.loc ?? null,
},
{
kind: 'error',
message: 'the component is created here',
loc: nestedFnNameExpr?.node.loc ?? nestedFn.node.loc ?? null,
},
],
});
}
}
nestedFn.skip();
};
fn.traverse({
FunctionDeclaration: validateNestedFunction,
FunctionExpression: validateNestedFunction,
ArrowFunctionExpression: validateNestedFunction,
});
}
function getReactFunctionType(
fn: BabelFn,
pass: CompilerPass,
@@ -957,6 +877,11 @@ function getReactFunctionType(
return componentSyntaxType;
}
case 'all': {
// Compile only top level functions
if (fn.scope.getProgramParent() !== fn.scope.parent) {
return null;
}
return getComponentOrHookLike(fn, hookPattern) ?? 'Other';
}
default: {

View File

@@ -650,13 +650,6 @@ export const EnvironmentConfigSchema = z.object({
* useMemo(() => { ... }, [...]);
*/
validateNoVoidUseMemo: z.boolean().default(false),
/**
* Validates that Components/Hooks are always defined at module level. This prevents scope
* reference errors that occur when the compiler attempts to optimize the nested component/hook
* while its parent function remains uncompiled.
*/
validateNoDynamicallyCreatedComponentsOrHooks: z.boolean().default(false),
});
export type EnvironmentConfig = z.infer<typeof EnvironmentConfigSchema>;

View File

@@ -323,22 +323,6 @@ export default class HIRBuilder {
],
});
}
if (node.name === 'this') {
CompilerError.throwDiagnostic({
severity: ErrorSeverity.UnsupportedJS,
category: ErrorCategory.UnsupportedSyntax,
reason: '`this` is not supported syntax',
description:
'React Compiler does not support compiling functions that use `this`',
details: [
{
kind: 'error',
message: '`this` was used here',
loc: node.loc ?? GeneratedSource,
},
],
});
}
const originalName = node.name;
let name = originalName;
let index = 0;

View File

@@ -880,8 +880,7 @@ export function printType(type: Type): string {
if (type.kind === 'Object' && type.shapeId != null) {
return `:T${type.kind}<${type.shapeId}>`;
} else if (type.kind === 'Function' && type.shapeId != null) {
const returnType = printType(type.return);
return `:T${type.kind}<${type.shapeId}>()${returnType !== '' ? `: ${returnType}` : ''}`;
return `:T${type.kind}<${type.shapeId}>`;
} else {
return `:T${type.kind}`;
}
@@ -984,7 +983,7 @@ export function printAliasingEffect(effect: AliasingEffect): string {
case 'MutateConditionally':
case 'MutateTransitive':
case 'MutateTransitiveConditionally': {
return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}${effect.kind === 'Mutate' && effect.reason?.kind === 'AssignCurrentProperty' ? ' (assign `.current`)' : ''}`;
return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}`;
}
case 'MutateFrozen': {
return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;

View File

@@ -25,8 +25,8 @@ import {
IdentifierId,
Instruction,
InstructionKind,
InstructionValue,
isArrayType,
isJsxType,
isMapType,
isPrimitiveType,
isRefOrRefValue,
@@ -56,6 +56,7 @@ import {
printAliasingSignature,
printIdentifier,
printInstruction,
printInstructionValue,
printPlace,
printSourceLocation,
} from '../HIR/PrintHIR';
@@ -106,11 +107,11 @@ export function inferMutationAliasingEffects(
const statesByBlock: Map<BlockId, InferenceState> = new Map();
for (const ref of fn.context) {
const value: AliasingEffect = {
kind: 'Create',
into: ref,
value: ValueKind.Context,
reason: ValueReason.Other,
// TODO: using InstructionValue as a bit of a hack, but it's pragmatic
const value: InstructionValue = {
kind: 'ObjectExpression',
properties: [],
loc: ref.loc,
};
initialState.initialize(value, {
kind: ValueKind.Context,
@@ -143,11 +144,10 @@ export function inferMutationAliasingEffects(
}
if (ref != null) {
const place = ref.kind === 'Identifier' ? ref : ref.place;
const value: AliasingEffect = {
kind: 'Create',
into: place,
value: ValueKind.Mutable,
reason: ValueReason.Other,
const value: InstructionValue = {
kind: 'ObjectExpression',
properties: [],
loc: place.loc,
};
initialState.initialize(value, {
kind: ValueKind.Mutable,
@@ -264,6 +264,8 @@ function findHoistedContextDeclarations(
class Context {
internedEffects: Map<string, AliasingEffect> = new Map();
instructionSignatureCache: Map<Instruction, InstructionSignature> = new Map();
effectInstructionValueCache: Map<AliasingEffect, InstructionValue> =
new Map();
applySignatureCache: Map<
AliasingSignature,
Map<AliasingEffect, Array<AliasingEffect> | null>
@@ -315,11 +317,10 @@ function inferParam(
paramKind: AbstractValue,
): void {
const place = param.kind === 'Identifier' ? param : param.place;
const value: AliasingEffect = {
kind: 'Create',
into: place,
value: paramKind.kind,
reason: ValueReason.Other,
const value: InstructionValue = {
kind: 'Primitive',
loc: place.loc,
value: undefined,
};
initialState.initialize(value, paramKind);
initialState.define(place, value);
@@ -540,11 +541,20 @@ function applyEffect(
});
initialized.add(effect.into.identifier.id);
state.initialize(effect, {
let value = context.effectInstructionValueCache.get(effect);
if (value == null) {
value = {
kind: 'ObjectExpression',
properties: [],
loc: effect.into.loc,
};
context.effectInstructionValueCache.set(effect, value);
}
state.initialize(value, {
kind: effect.value,
reason: new Set([effect.reason]),
});
state.define(effect.into, effect);
state.define(effect.into, value);
effects.push(effect);
break;
}
@@ -571,11 +581,20 @@ function applyEffect(
initialized.add(effect.into.identifier.id);
const fromValue = state.kind(effect.from);
state.initialize(effect, {
let value = context.effectInstructionValueCache.get(effect);
if (value == null) {
value = {
kind: 'ObjectExpression',
properties: [],
loc: effect.into.loc,
};
context.effectInstructionValueCache.set(effect, value);
}
state.initialize(value, {
kind: fromValue.kind,
reason: new Set(fromValue.reason),
});
state.define(effect.into, effect);
state.define(effect.into, value);
switch (fromValue.kind) {
case ValueKind.Primitive:
case ValueKind.Global: {
@@ -663,11 +682,11 @@ function applyEffect(
operand.effect = Effect.Read;
}
}
state.initialize(effect, {
state.initialize(effect.function, {
kind: isMutable ? ValueKind.Mutable : ValueKind.Frozen,
reason: new Set([]),
});
state.define(effect.into, effect);
state.define(effect.into, effect.function);
for (const capture of effect.captures) {
applyEffect(
context,
@@ -773,20 +792,38 @@ function applyEffect(
initialized,
effects,
);
state.initialize(effect, {
let value = context.effectInstructionValueCache.get(effect);
if (value == null) {
value = {
kind: 'Primitive',
value: undefined,
loc: effect.from.loc,
};
context.effectInstructionValueCache.set(effect, value);
}
state.initialize(value, {
kind: fromKind,
reason: new Set(fromValue.reason),
});
state.define(effect.into, effect);
state.define(effect.into, value);
break;
}
case ValueKind.Global:
case ValueKind.Primitive: {
state.initialize(effect, {
let value = context.effectInstructionValueCache.get(effect);
if (value == null) {
value = {
kind: 'Primitive',
value: undefined,
loc: effect.from.loc,
};
context.effectInstructionValueCache.set(effect, value);
}
state.initialize(value, {
kind: fromKind,
reason: new Set(fromValue.reason),
});
state.define(effect.into, effect);
state.define(effect.into, value);
break;
}
default: {
@@ -801,15 +838,14 @@ function applyEffect(
const functionValues = state.values(effect.function);
if (
functionValues.length === 1 &&
functionValues[0].kind === 'CreateFunction' &&
functionValues[0].function.kind === 'FunctionExpression' &&
functionValues[0].function.loweredFunc.func.aliasingEffects != null
functionValues[0].kind === 'FunctionExpression' &&
functionValues[0].loweredFunc.func.aliasingEffects != null
) {
/*
* We're calling a locally declared function, we already know it's effects!
* We just have to substitute in the args for the params
*/
const functionExpr = functionValues[0].function;
const functionExpr = functionValues[0];
let signature = context.functionSignatureCache.get(functionExpr);
if (signature == null) {
signature = buildSignatureFromFunctionExpression(
@@ -1099,19 +1135,19 @@ class InferenceState {
#isFunctionExpression: boolean;
// The kind of each value, based on its allocation site
#values: Map<AliasingEffect, AbstractValue>;
#values: Map<InstructionValue, AbstractValue>;
/*
* The set of values pointed to by each identifier. This is a set
* to accomodate phi points (where a variable may have different
* values from different control flow paths).
*/
#variables: Map<IdentifierId, Set<AliasingEffect>>;
#variables: Map<IdentifierId, Set<InstructionValue>>;
constructor(
env: Environment,
isFunctionExpression: boolean,
values: Map<AliasingEffect, AbstractValue>,
variables: Map<IdentifierId, Set<AliasingEffect>>,
values: Map<InstructionValue, AbstractValue>,
variables: Map<IdentifierId, Set<InstructionValue>>,
) {
this.env = env;
this.#isFunctionExpression = isFunctionExpression;
@@ -1131,11 +1167,18 @@ class InferenceState {
}
// (Re)initializes a @param value with its default @param kind.
initialize(value: AliasingEffect, kind: AbstractValue): void {
initialize(value: InstructionValue, kind: AbstractValue): void {
CompilerError.invariant(value.kind !== 'LoadLocal', {
reason:
'[InferMutationAliasingEffects] Expected all top-level identifiers to be defined as variables, not values',
description: null,
loc: value.loc,
suggestions: null,
});
this.#values.set(value, kind);
}
values(place: Place): Array<AliasingEffect> {
values(place: Place): Array<InstructionValue> {
const values = this.#variables.get(place.identifier.id);
CompilerError.invariant(values != null, {
reason: `[InferMutationAliasingEffects] Expected value kind to be initialized`,
@@ -1198,13 +1241,13 @@ class InferenceState {
}
// Defines (initializing or updating) a variable with a specific kind of value.
define(place: Place, value: AliasingEffect): void {
define(place: Place, value: InstructionValue): void {
CompilerError.invariant(this.#values.has(value), {
reason: `[InferMutationAliasingEffects] Expected value to be initialized at '${printSourceLocation(
place.loc,
value.loc,
)}'`,
description: printAliasingEffect(value),
loc: place.loc,
description: printInstructionValue(value),
loc: value.loc,
suggestions: null,
});
this.#variables.set(place.identifier.id, new Set([value]));
@@ -1244,17 +1287,17 @@ class InferenceState {
}
}
freezeValue(value: AliasingEffect, reason: ValueReason): void {
freezeValue(value: InstructionValue, reason: ValueReason): void {
this.#values.set(value, {
kind: ValueKind.Frozen,
reason: new Set([reason]),
});
if (
value.kind === 'CreateFunction' &&
value.kind === 'FunctionExpression' &&
(this.env.config.enablePreserveExistingMemoizationGuarantees ||
this.env.config.enableTransitivelyFreezeFunctionExpressions)
) {
for (const place of value.function.loweredFunc.func.context) {
for (const place of value.loweredFunc.func.context) {
this.freeze(place, reason);
}
}
@@ -1329,8 +1372,8 @@ class InferenceState {
* termination.
*/
merge(other: InferenceState): InferenceState | null {
let nextValues: Map<AliasingEffect, AbstractValue> | null = null;
let nextVariables: Map<IdentifierId, Set<AliasingEffect>> | null = null;
let nextValues: Map<InstructionValue, AbstractValue> | null = null;
let nextVariables: Map<IdentifierId, Set<InstructionValue>> | null = null;
for (const [id, thisValue] of this.#values) {
const otherValue = other.#values.get(id);
@@ -1354,7 +1397,7 @@ class InferenceState {
for (const [id, thisValues] of this.#variables) {
const otherValues = other.#variables.get(id);
if (otherValues !== undefined) {
let mergedValues: Set<AliasingEffect> | null = null;
let mergedValues: Set<InstructionValue> | null = null;
for (const otherValue of otherValues) {
if (!thisValues.has(otherValue)) {
mergedValues = mergedValues ?? new Set(thisValues);
@@ -1407,8 +1450,8 @@ class InferenceState {
*/
debug(): any {
const result: any = {values: {}, variables: {}};
const objects: Map<AliasingEffect, number> = new Map();
function identify(value: AliasingEffect): number {
const objects: Map<InstructionValue, number> = new Map();
function identify(value: InstructionValue): number {
let id = objects.get(value);
if (id == null) {
id = objects.size;
@@ -1420,7 +1463,7 @@ class InferenceState {
const id = identify(value);
result.values[id] = {
abstract: this.debugAbstractValue(kind),
value: printAliasingEffect(value),
value: printInstructionValue(value),
};
}
for (const [variable, values] of this.#variables) {
@@ -1437,7 +1480,7 @@ class InferenceState {
}
inferPhi(phi: Phi): void {
const values: Set<AliasingEffect> = new Set();
const values: Set<InstructionValue> = new Set();
for (const [_, operand] of phi.operands) {
const operandValues = this.#variables.get(operand.identifier.id);
// This is a backedge that will be handled later by State.merge
@@ -1828,23 +1871,6 @@ function computeSignatureForInstruction(
});
}
}
for (const prop of value.props) {
if (
prop.kind === 'JsxAttribute' &&
prop.place.identifier.type.kind === 'Function' &&
(isJsxType(prop.place.identifier.type.return) ||
(prop.place.identifier.type.return.kind === 'Phi' &&
prop.place.identifier.type.return.operands.some(operand =>
isJsxType(operand),
)))
) {
// Any props which return jsx are assumed to be called during render
effects.push({
kind: 'Render',
place: prop.place,
});
}
}
}
break;
}
@@ -2303,9 +2329,8 @@ function areArgumentsImmutableAndNonMutating(
const values = state.values(place);
for (const value of values) {
if (
value.kind === 'CreateFunction' &&
value.function.kind === 'FunctionExpression' &&
value.function.loweredFunc.func.params.some(param => {
value.kind === 'FunctionExpression' &&
value.loweredFunc.func.params.some(param => {
const place = param.kind === 'Identifier' ? param : param.place;
const range = place.identifier.mutableRange;
return range.end > range.start + 1;
@@ -2498,47 +2523,10 @@ function computeEffectsForSignature(
break;
}
case 'CreateFunction': {
const applyInto = substitutions.get(effect.into.identifier.id);
if (applyInto == null || applyInto.length !== 1) {
return null;
}
const captures: Array<Place> = [];
for (let i = 0; i < effect.captures.length; i++) {
const substitution = substitutions.get(
effect.captures[i].identifier.id,
);
if (substitution == null || substitution.length !== 1) {
return null;
}
captures.push(substitution[0]);
}
const context: Array<Place> = [];
const originalContext = effect.function.loweredFunc.func.context;
for (let i = 0; i < originalContext.length; i++) {
const substitution = substitutions.get(
originalContext[i].identifier.id,
);
if (substitution == null || substitution.length !== 1) {
return null;
}
context.push(substitution[0]);
}
effects.push({
kind: 'CreateFunction',
into: applyInto[0],
function: {
...effect.function,
loweredFunc: {
...effect.function.loweredFunc,
func: {
...effect.function.loweredFunc.func,
context,
},
},
},
captures,
CompilerError.throwTodo({
reason: `Support CreateFrom effects in signatures`,
loc: receiver.loc,
});
break;
}
default: {
assertExhaustive(

View File

@@ -27,7 +27,7 @@ import {
} from '../HIR/visitors';
import {assertExhaustive, getOrInsertWith} from '../Utils/utils';
import {Err, Ok, Result} from '../Utils/Result';
import {AliasingEffect, MutationReason} from './AliasingEffects';
import {AliasingEffect} from './AliasingEffects';
/**
* This pass builds an abstract model of the heap and interprets the effects of the
@@ -101,7 +101,6 @@ export function inferMutationAliasingRanges(
transitive: boolean;
kind: MutationKind;
place: Place;
reason: MutationReason | null;
}> = [];
const renders: Array<{index: number; place: Place}> = [];
@@ -141,7 +140,7 @@ export function inferMutationAliasingRanges(
} else if (effect.kind === 'CreateFunction') {
state.create(effect.into, {
kind: 'Function',
effect,
function: effect.function.loweredFunc.func,
});
} else if (effect.kind === 'CreateFrom') {
state.createFrom(index++, effect.from, effect.into);
@@ -156,7 +155,7 @@ export function inferMutationAliasingRanges(
* invariant here.
*/
if (!state.nodes.has(effect.into.identifier)) {
state.create(effect.into, {kind: 'Assign'});
state.create(effect.into, {kind: 'Object'});
}
state.assign(index++, effect.from, effect.into);
} else if (effect.kind === 'Alias') {
@@ -177,7 +176,6 @@ export function inferMutationAliasingRanges(
effect.kind === 'MutateTransitive'
? MutationKind.Definite
: MutationKind.Conditional,
reason: null,
place: effect.value,
});
} else if (
@@ -192,7 +190,6 @@ export function inferMutationAliasingRanges(
effect.kind === 'Mutate'
? MutationKind.Definite
: MutationKind.Conditional,
reason: effect.kind === 'Mutate' ? (effect.reason ?? null) : null,
place: effect.value,
});
} else if (
@@ -244,7 +241,6 @@ export function inferMutationAliasingRanges(
mutation.transitive,
mutation.kind,
mutation.place.loc,
mutation.reason,
errors,
);
}
@@ -271,7 +267,6 @@ export function inferMutationAliasingRanges(
functionEffects.push({
kind: 'Mutate',
value: {...place, loc: node.local.loc},
reason: node.mutationReason,
});
}
}
@@ -474,99 +469,6 @@ export function inferMutationAliasingRanges(
}
}
const tracked: Array<Place> = [];
for (const param of [...fn.params, ...fn.context, fn.returns]) {
const place = param.kind === 'Identifier' ? param : param.place;
tracked.push(place);
}
const returned: Set<Node> = new Set();
const queue: Array<Node> = [state.nodes.get(fn.returns.identifier)!];
const seen: Set<Node> = new Set();
while (queue.length !== 0) {
const node = queue.pop()!;
if (seen.has(node)) {
continue;
}
seen.add(node);
for (const id of node.aliases.keys()) {
queue.push(state.nodes.get(id)!);
}
for (const id of node.createdFrom.keys()) {
queue.push(state.nodes.get(id)!);
}
if (node.id.id === fn.returns.identifier.id) {
continue;
}
switch (node.value.kind) {
case 'Assign':
case 'CreateFrom': {
break;
}
case 'Phi':
case 'Object':
case 'Function': {
returned.add(node);
break;
}
default: {
assertExhaustive(
node.value,
`Unexpected node value kind '${(node.value as any).kind}'`,
);
}
}
}
const returnedValues = [...returned];
if (
returnedValues.length === 1 &&
returnedValues[0].value.kind === 'Object' &&
tracked.some(place => place.identifier.id === returnedValues[0].id.id)
) {
const from = tracked.find(
place => place.identifier.id === returnedValues[0].id.id,
)!;
functionEffects.push({
kind: 'Assign',
from,
into: fn.returns,
});
} else if (
returnedValues.length === 1 &&
returnedValues[0].value.kind === 'Function'
) {
const outerContext = new Set(fn.context.map(p => p.identifier.id));
const effect = returnedValues[0].value.effect;
functionEffects.push({
kind: 'CreateFunction',
function: {
...effect.function,
loweredFunc: {
func: {
...effect.function.loweredFunc.func,
context: effect.function.loweredFunc.func.context.filter(p =>
outerContext.has(p.identifier.id),
),
},
},
},
captures: effect.captures.filter(p => outerContext.has(p.identifier.id)),
into: fn.returns,
});
} else {
const returns = fn.returns.identifier;
functionEffects.push({
kind: 'Create',
into: fn.returns,
value: isPrimitiveType(returns)
? ValueKind.Primitive
: isJsxType(returns.type)
? ValueKind.Frozen
: ValueKind.Mutable,
reason: ValueReason.KnownReturnSignature,
});
}
/**
* Part 3
* Finish populating the externally visible effects. Above we bubble-up the side effects
@@ -574,12 +476,28 @@ export function inferMutationAliasingRanges(
* Here we populate an effect to create the return value as well as populating alias/capture
* effects for how data flows between the params, context vars, and return.
*/
const returns = fn.returns.identifier;
functionEffects.push({
kind: 'Create',
into: fn.returns,
value: isPrimitiveType(returns)
? ValueKind.Primitive
: isJsxType(returns.type)
? ValueKind.Frozen
: ValueKind.Mutable,
reason: ValueReason.KnownReturnSignature,
});
/**
* Determine precise data-flow effects by simulating transitive mutations of the params/
* captures and seeing what other params/context variables are affected. Anything that
* would be transitively mutated needs a capture relationship.
*/
const tracked: Array<Place> = [];
const ignoredErrors = new CompilerError();
for (const param of [...fn.params, ...fn.context, fn.returns]) {
const place = param.kind === 'Identifier' ? param : param.place;
tracked.push(place);
}
for (const into of tracked) {
const mutationIndex = index++;
state.mutate(
@@ -589,7 +507,6 @@ export function inferMutationAliasingRanges(
true,
MutationKind.Conditional,
into.loc,
null,
ignoredErrors,
);
for (const from of tracked) {
@@ -663,16 +580,10 @@ type Node = {
transitive: {kind: MutationKind; loc: SourceLocation} | null;
local: {kind: MutationKind; loc: SourceLocation} | null;
lastMutated: number;
mutationReason: MutationReason | null;
value:
| {kind: 'Assign'}
| {kind: 'CreateFrom'}
| {kind: 'Object'}
| {kind: 'Phi'}
| {
kind: 'Function';
effect: Extract<AliasingEffect, {kind: 'CreateFunction'}>;
};
| {kind: 'Function'; function: HIRFunction};
};
class AliasingState {
nodes: Map<Identifier, Node> = new Map();
@@ -688,13 +599,12 @@ class AliasingState {
transitive: null,
local: null,
lastMutated: 0,
mutationReason: null,
value,
});
}
createFrom(index: number, from: Place, into: Place): void {
this.create(into, {kind: 'CreateFrom'});
this.create(into, {kind: 'Object'});
const fromNode = this.nodes.get(from.identifier);
const toNode = this.nodes.get(into.identifier);
if (fromNode == null || toNode == null) {
@@ -756,10 +666,7 @@ class AliasingState {
continue;
}
if (node.value.kind === 'Function') {
appendFunctionErrors(
errors,
node.value.effect.function.loweredFunc.func,
);
appendFunctionErrors(errors, node.value.function);
}
for (const [alias, when] of node.createdFrom) {
if (when >= index) {
@@ -790,7 +697,6 @@ class AliasingState {
transitive: boolean,
startKind: MutationKind,
loc: SourceLocation,
reason: MutationReason | null,
errors: CompilerError,
): void {
const seen = new Map<Identifier, MutationKind>();
@@ -811,7 +717,6 @@ class AliasingState {
if (node == null) {
continue;
}
node.mutationReason ??= reason;
node.lastMutated = Math.max(node.lastMutated, index);
if (end != null) {
node.id.mutableRange.end = makeInstructionId(
@@ -823,10 +728,7 @@ class AliasingState {
node.transitive == null &&
node.local == null
) {
appendFunctionErrors(
errors,
node.value.effect.function.loweredFunc.func,
);
appendFunctionErrors(errors, node.value.function);
}
if (transitive) {
if (node.transitive == null || node.transitive.kind < kind) {

View File

@@ -777,15 +777,6 @@ class Unifier {
return {kind: 'Phi', operands: type.operands.map(o => this.get(o))};
}
if (type.kind === 'Function') {
return {
kind: 'Function',
isConstructor: type.isConstructor,
shapeId: type.shapeId,
return: this.get(type.return),
};
}
return type;
}
}

View File

@@ -182,11 +182,6 @@ export function parseConfigPragmaForTests(
environment?: PartialEnvironmentConfig;
},
): PluginOptions {
const overridePragma = parseConfigPragmaAsString(pragma);
if (overridePragma !== '') {
return parseConfigStringAsJS(overridePragma, defaults);
}
const environment = parseConfigPragmaEnvironmentForTest(
pragma,
defaults.environment ?? {},
@@ -222,104 +217,3 @@ export function parseConfigPragmaForTests(
}
return parsePluginOptions(options);
}
export function parseConfigPragmaAsString(pragma: string): string {
// Check if it's in JS override format
for (const {key, value: val} of splitPragma(pragma)) {
if (key === 'OVERRIDE' && val != null) {
return val;
}
}
return '';
}
function parseConfigStringAsJS(
configString: string,
defaults: {
compilationMode: CompilationMode;
environment?: PartialEnvironmentConfig;
},
): PluginOptions {
let parsedConfig: any;
try {
// Parse the JavaScript object literal
parsedConfig = new Function(`return ${configString}`)();
} catch (error) {
CompilerError.invariant(false, {
reason: 'Failed to parse config pragma as JavaScript object',
description: `Could not parse: ${configString}. Error: ${error}`,
loc: null,
suggestions: null,
});
}
console.log('OVERRIDE:', parsedConfig);
const options: Record<keyof PluginOptions, unknown> = {
...defaultOptions,
panicThreshold: 'all_errors',
compilationMode: defaults.compilationMode,
environment: defaults.environment ?? defaultOptions.environment,
};
// Apply parsed config, merging environment if it exists
if (parsedConfig.environment) {
const mergedEnvironment = {
...(options.environment as Record<string, unknown>),
...parsedConfig.environment,
};
// Apply complex defaults for environment flags that are set to true
const environmentConfig: Partial<Record<keyof EnvironmentConfig, unknown>> =
{};
for (const [key, value] of Object.entries(mergedEnvironment)) {
if (hasOwnProperty(EnvironmentConfigSchema.shape, key)) {
if (value === true && key in testComplexConfigDefaults) {
environmentConfig[key] = testComplexConfigDefaults[key];
} else {
environmentConfig[key] = value;
}
}
}
// Validate environment config
const validatedEnvironment =
EnvironmentConfigSchema.safeParse(environmentConfig);
if (!validatedEnvironment.success) {
CompilerError.invariant(false, {
reason: 'Invalid environment configuration in config pragma',
description: `${fromZodError(validatedEnvironment.error)}`,
loc: null,
suggestions: null,
});
}
if (validatedEnvironment.data.enableResetCacheOnSourceFileChanges == null) {
validatedEnvironment.data.enableResetCacheOnSourceFileChanges = false;
}
options.environment = validatedEnvironment.data;
}
// Apply other config options
for (const [key, value] of Object.entries(parsedConfig)) {
if (key === 'environment') {
continue;
}
if (hasOwnProperty(defaultOptions, key)) {
if (value === true && key in testComplexPluginOptionDefaults) {
options[key] = testComplexPluginOptionDefaults[key];
} else if (key === 'target' && value === 'donotuse_meta_internal') {
options[key] = {
kind: value,
runtimeModule: 'react',
};
} else {
options[key] = value;
}
}
}
return parsePluginOptions(options);
}

View File

@@ -7,8 +7,8 @@
import * as t from '@babel/types';
import {
CompilerDiagnostic,
CompilerError,
CompilerErrorDetail,
ErrorCategory,
ErrorSeverity,
} from '../CompilerError';
@@ -95,16 +95,16 @@ export function validateHooksUsage(
const unconditionalBlocks = computeUnconditionalBlocks(fn);
const errors = new CompilerError();
const errorsByPlace = new Map<t.SourceLocation, CompilerErrorDetail>();
const errorsByPlace = new Map<t.SourceLocation, CompilerDiagnostic>();
function recordError(
loc: SourceLocation,
errorDetail: CompilerErrorDetail,
diagnostic: CompilerDiagnostic,
): void {
if (typeof loc === 'symbol') {
errors.pushErrorDetail(errorDetail);
errors.pushDiagnostic(diagnostic);
} else {
errorsByPlace.set(loc, errorDetail);
errorsByPlace.set(loc, diagnostic);
}
}
@@ -112,7 +112,7 @@ export function validateHooksUsage(
// Once a particular hook has a conditional call error, don't report any further issues for this hook
setKind(place, Kind.Error);
const reason =
const description =
'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)';
const previousError =
typeof place.loc !== 'symbol' ? errorsByPlace.get(place.loc) : undefined;
@@ -121,16 +121,19 @@ export function validateHooksUsage(
* In some circumstances such as optional calls, we may first encounter a "hook may not be referenced as normal values" error.
* If that same place is also used as a conditional call, upgrade the error to a conditonal hook error
*/
if (previousError === undefined || previousError.reason !== reason) {
if (previousError === undefined || previousError.reason !== description) {
recordError(
place.loc,
new CompilerErrorDetail({
CompilerDiagnostic.create({
category: ErrorCategory.Hooks,
description: null,
reason,
loc: place.loc,
severity: ErrorSeverity.InvalidReact,
reason: 'Cannot call hooks conditionally',
description,
suggestions: null,
}).withDetail({
kind: 'error',
loc: place.loc,
message: 'Cannot call hook conditionally',
}),
);
}
@@ -141,14 +144,17 @@ export function validateHooksUsage(
if (previousError === undefined) {
recordError(
place.loc,
new CompilerErrorDetail({
CompilerDiagnostic.create({
category: ErrorCategory.Hooks,
description: null,
reason:
'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',
loc: place.loc,
severity: ErrorSeverity.InvalidReact,
reason: 'Cannot reference hooks as regular values',
description:
'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',
suggestions: null,
}).withDetail({
kind: 'error',
loc: place.loc,
message: 'Hooks may not be referenced as values',
}),
);
}
@@ -159,14 +165,17 @@ export function validateHooksUsage(
if (previousError === undefined) {
recordError(
place.loc,
new CompilerErrorDetail({
CompilerDiagnostic.create({
category: ErrorCategory.Hooks,
description: null,
reason:
'Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks',
loc: place.loc,
severity: ErrorSeverity.InvalidReact,
reason: 'Hooks must be the same on every render',
description:
'Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks',
suggestions: null,
}).withDetail({
kind: 'error',
loc: place.loc,
message: 'This value may change to a different function',
}),
);
}
@@ -427,8 +436,8 @@ export function validateHooksUsage(
}
}
for (const [, error] of errorsByPlace) {
errors.pushErrorDetail(error);
for (const [, diagnostic] of errorsByPlace) {
errors.pushDiagnostic(diagnostic);
}
return errors.asResult();
}
@@ -450,15 +459,18 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
: instr.value.property;
const hookKind = getHookKind(fn.env, callee.identifier);
if (hookKind != null) {
errors.pushErrorDetail(
new CompilerErrorDetail({
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Hooks,
severity: ErrorSeverity.InvalidReact,
reason:
reason: 'Cannot call hooks within function expressions',
description:
'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)',
loc: callee.loc,
description: `Cannot call ${hookKind === 'Custom' ? 'hook' : hookKind} within a function expression`,
suggestions: null,
}).withDetail({
kind: 'error',
loc: callee.loc,
message: `Cannot call ${hookKind === 'Custom' ? 'hook' : hookKind} within a function expression`,
}),
);
}

View File

@@ -24,18 +24,9 @@ function useThing(fn) {
```
Found 1 error:
Error: `this` is not supported syntax
Error: Expected a non-reserved identifier name
React Compiler does not support compiling functions that use `this`
error.reserved-words.ts:8:28
6 |
7 | if (ref.current === null) {
> 8 | ref.current = function (this: unknown, ...args) {
| ^^^^^^^^^^^^^ `this` was used here
9 | return fnRef.current.call(this, ...args);
10 | };
11 | }
`this` is a reserved word in JavaScript and cannot be used as an identifier name.
```

View File

@@ -1,37 +0,0 @@
## Input
```javascript
// Fixture to test that we show a hint to name as `ref` or `-Ref` when attempting
// to assign .current inside an effect
function Component({foo}) {
useEffect(() => {
foo.current = true;
}, [foo]);
}
```
## Error
```
Found 1 error:
Error: This value cannot be modified
Modifying component props or hook arguments is not allowed. Consider using a local variable instead.
error.assign-ref-in-effect-hint.ts:5:4
3 | function Component({foo}) {
4 | useEffect(() => {
> 5 | foo.current = true;
| ^^^ `foo` cannot be modified
6 | }, [foo]);
7 | }
8 |
Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
```

View File

@@ -1,7 +0,0 @@
// Fixture to test that we show a hint to name as `ref` or `-Ref` when attempting
// to assign .current inside an effect
function Component({foo}) {
useEffect(() => {
foo.current = true;
}, [foo]);
}

View File

@@ -18,13 +18,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
5 | }
6 | return x;
7 | }

View File

@@ -18,13 +18,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^^^ 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)
| ^^^^^^^^^^ Cannot call hook conditionally
5 | }
6 | return x;
7 | }

View File

@@ -25,24 +25,28 @@ export const FIXTURE_ENTRYPOINT = {
```
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: Cannot reference hooks as regular values
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;
| ^^^^^^^^^^^^^ 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
| ^^^^^^^^^^^^^ Hooks may not be referenced as 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: Cannot reference hooks as regular values
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
| ^^^ Hooks may not be referenced as values
9 | }
10 |
11 | export const FIXTURE_ENTRYPOINT = {

View File

@@ -1,7 +0,0 @@
function Component() {
const f = () => () => {
global.property = true;
};
f()();
return <div>Ooops</div>;
}

View File

@@ -16,12 +16,14 @@ function Component(props) {
```
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: Cannot reference hooks as regular values
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;
| ^^^^^^^^ 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
| ^^^^^^^^ Hooks may not be referenced as values
3 | const state = x(null);
4 | return state[0];
5 | }

View File

@@ -20,13 +20,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^ Cannot call hook conditionally
7 | }
8 | return data;
9 | }

View File

@@ -20,13 +20,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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();
| ^^^^^ 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)
| ^^^^^ Cannot call hook conditionally
7 | }
8 | return s;
9 | }

View File

@@ -20,13 +20,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
7 | }
8 | return data;
9 | }

View File

@@ -38,8 +38,6 @@ error.invalid-mutate-context-in-callback.ts:12:4
13 | };
14 | return <div onClick={onClick} />;
15 | }
Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
```

View File

@@ -1,44 +0,0 @@
## Input
```javascript
function Component() {
const renderItem = item => {
// Multiple returns so that the return type is a Phi (union)
if (item == null) {
return null;
}
// Normally we assume that it's safe to mutate globals in a function passed
// as a prop, because the prop could be used as an event handler or effect.
// But if the function returns JSX we can assume it's a render helper, ie
// called during render, and thus it's unsafe to mutate globals or call
// other impure code.
global.property = true;
return <Item item={item} value={rand} />;
};
return <ItemList renderItem={renderItem} />;
}
```
## Error
```
Found 1 error:
Error: This value cannot be modified
Modifying a variable defined outside a component or hook is not allowed. Consider using an effect.
error.invalid-mutate-global-in-render-helper-phi-return-prop.ts:12:4
10 | // called during render, and thus it's unsafe to mutate globals or call
11 | // other impure code.
> 12 | global.property = true;
| ^^^^^^ value cannot be modified
13 | return <Item item={item} value={rand} />;
14 | };
15 | return <ItemList renderItem={renderItem} />;
```

View File

@@ -1,16 +0,0 @@
function Component() {
const renderItem = item => {
// Multiple returns so that the return type is a Phi (union)
if (item == null) {
return null;
}
// Normally we assume that it's safe to mutate globals in a function passed
// as a prop, because the prop could be used as an event handler or effect.
// But if the function returns JSX we can assume it's a render helper, ie
// called during render, and thus it's unsafe to mutate globals or call
// other impure code.
global.property = true;
return <Item item={item} value={rand} />;
};
return <ItemList renderItem={renderItem} />;
}

View File

@@ -1,40 +0,0 @@
## Input
```javascript
function Component() {
const renderItem = item => {
// Normally we assume that it's safe to mutate globals in a function passed
// as a prop, because the prop could be used as an event handler or effect.
// But if the function returns JSX we can assume it's a render helper, ie
// called during render, and thus it's unsafe to mutate globals or call
// other impure code.
global.property = true;
return <Item item={item} value={rand} />;
};
return <ItemList renderItem={renderItem} />;
}
```
## Error
```
Found 1 error:
Error: This value cannot be modified
Modifying a variable defined outside a component or hook is not allowed. Consider using an effect.
error.invalid-mutate-global-in-render-helper-prop.ts:8:4
6 | // called during render, and thus it's unsafe to mutate globals or call
7 | // other impure code.
> 8 | global.property = true;
| ^^^^^^ value cannot be modified
9 | return <Item item={item} value={rand} />;
10 | };
11 | return <ItemList renderItem={renderItem} />;
```

View File

@@ -1,12 +0,0 @@
function Component() {
const renderItem = item => {
// Normally we assume that it's safe to mutate globals in a function passed
// as a prop, because the prop could be used as an event handler or effect.
// But if the function returns JSX we can assume it's a render helper, ie
// called during render, and thus it's unsafe to mutate globals or call
// other impure code.
global.property = true;
return <Item item={item} value={rand} />;
};
return <ItemList renderItem={renderItem} />;
}

View File

@@ -14,12 +14,14 @@ function Component(props) {
```
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: Cannot reference hooks as regular values
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-pass-hook-as-call-arg.ts:2:13
1 | function Component(props) {
> 2 | return foo(useFoo);
| ^^^^^^ 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
| ^^^^^^ Hooks may not be referenced as values
3 | }
4 |
```

View File

@@ -14,12 +14,14 @@ function Component(props) {
```
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: Cannot reference hooks as regular values
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-pass-hook-as-prop.ts:2:21
1 | function Component(props) {
> 2 | return <Child foo={useFoo} />;
| ^^^^^^ 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
| ^^^^^^ Hooks may not be referenced as values
3 | }
4 |
```

View File

@@ -15,43 +15,51 @@ function Component(props) {
```
Found 4 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: Cannot reference hooks as regular values
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-ternary-with-hook-values.ts:2:25
1 | function Component(props) {
> 2 | const x = props.cond ? useA : useB;
| ^^^^ 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
| ^^^^ Hooks may not be referenced as values
3 | return x();
4 | }
5 |
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: Cannot reference hooks as regular values
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-ternary-with-hook-values.ts:2:32
1 | function Component(props) {
> 2 | const x = props.cond ? useA : useB;
| ^^^^ 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
| ^^^^ Hooks may not be referenced as values
3 | return x();
4 | }
5 |
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: Cannot reference hooks as regular values
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-ternary-with-hook-values.ts:2:12
1 | function Component(props) {
> 2 | const x = props.cond ? useA : useB;
| ^^^^^^^^^^^^^^^^^^^^^^^^ 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
| ^^^^^^^^^^^^^^^^^^^^^^^^ Hooks may not be referenced as values
3 | return x();
4 | }
5 |
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: Cannot reference hooks as regular values
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-ternary-with-hook-values.ts:3:9
1 | function Component(props) {
2 | const x = props.cond ? useA : useB;
> 3 | return x();
| ^ 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
| ^ Hooks may not be referenced as values
4 | }
5 |
```

View File

@@ -1,54 +0,0 @@
## Input
```javascript
// @validateNoDynamicallyCreatedComponentsOrHooks
export function getInput(a) {
const Wrapper = () => {
const handleChange = () => {
a.onChange();
};
return <input onChange={handleChange} />;
};
return Wrapper;
}
export const FIXTURE_ENTRYPOINT = {
fn: getInput,
isComponent: false,
params: [{onChange() {}}],
};
```
## Error
```
Found 1 error:
Error: Components and hooks cannot be created dynamically
The function `Wrapper` appears to be a React component, but it's defined inside `getInput`. Components and Hooks should always be declared at module scope
error.nested-component-in-normal-function.ts:2:16
1 | // @validateNoDynamicallyCreatedComponentsOrHooks
> 2 | export function getInput(a) {
| ^^^^^^^^ this function dynamically created a component/hook
3 | const Wrapper = () => {
4 | const handleChange = () => {
5 | a.onChange();
error.nested-component-in-normal-function.ts:3:8
1 | // @validateNoDynamicallyCreatedComponentsOrHooks
2 | export function getInput(a) {
> 3 | const Wrapper = () => {
| ^^^^^^^ the component is created here
4 | const handleChange = () => {
5 | a.onChange();
6 | };
```

View File

@@ -1,18 +0,0 @@
// @validateNoDynamicallyCreatedComponentsOrHooks
export function getInput(a) {
const Wrapper = () => {
const handleChange = () => {
a.onChange();
};
return <input onChange={handleChange} />;
};
return Wrapper;
}
export const FIXTURE_ENTRYPOINT = {
fn: getInput,
isComponent: false,
params: [{onChange() {}}],
};

View File

@@ -1,59 +0,0 @@
## Input
```javascript
// @validateNoDynamicallyCreatedComponentsOrHooks
import {useState} from 'react';
function createCustomHook(config) {
function useConfiguredState() {
const [state, setState] = useState(0);
const increment = () => {
setState(state + config.step);
};
return [state, increment];
}
return useConfiguredState;
}
export const FIXTURE_ENTRYPOINT = {
fn: createCustomHook,
isComponent: false,
params: [{step: 1}],
};
```
## Error
```
Found 1 error:
Error: Components and hooks cannot be created dynamically
The function `useConfiguredState` appears to be a React hook, but it's defined inside `createCustomHook`. Components and Hooks should always be declared at module scope
error.nested-hook-in-normal-function.ts:4:9
2 | import {useState} from 'react';
3 |
> 4 | function createCustomHook(config) {
| ^^^^^^^^^^^^^^^^ this function dynamically created a component/hook
5 | function useConfiguredState() {
6 | const [state, setState] = useState(0);
7 |
error.nested-hook-in-normal-function.ts:5:11
3 |
4 | function createCustomHook(config) {
> 5 | function useConfiguredState() {
| ^^^^^^^^^^^^^^^^^^ the component is created here
6 | const [state, setState] = useState(0);
7 |
8 | const increment = () => {
```

View File

@@ -1,22 +0,0 @@
// @validateNoDynamicallyCreatedComponentsOrHooks
import {useState} from 'react';
function createCustomHook(config) {
function useConfiguredState() {
const [state, setState] = useState(0);
const increment = () => {
setState(state + config.step);
};
return [state, increment];
}
return useConfiguredState;
}
export const FIXTURE_ENTRYPOINT = {
fn: createCustomHook,
isComponent: false,
params: [{step: 1}],
};

View File

@@ -15,23 +15,27 @@ function Component() {
```
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: Cannot reference hooks as regular values
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.propertyload-hook.ts:2:12
1 | function Component() {
> 2 | const x = Foo.useFoo;
| ^^^^^^^^^^ 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
| ^^^^^^^^^^ Hooks may not be referenced as values
3 | return x();
4 | }
5 |
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: Cannot reference hooks as regular values
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.propertyload-hook.ts:3:9
1 | function Component() {
2 | const x = Foo.useFoo;
> 3 | return x();
| ^ 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
| ^ Hooks may not be referenced as values
4 | }
5 |
```

View File

@@ -55,7 +55,7 @@ import { makeArray, Stringify, useIdentity } from "shared-runtime";
*/
function Foo(t0) {
"use memo";
const $ = _c(5);
const $ = _c(3);
const { b } = t0;
const fnFactory = () => () => {
@@ -66,26 +66,18 @@ function Foo(t0) {
useIdentity();
const fn = fnFactory();
let t1;
if ($[0] !== b) {
t1 = makeArray(b);
$[0] = b;
$[1] = t1;
} else {
t1 = $[1];
}
const arr = t1;
const arr = makeArray(b);
fn(arr);
let t2;
if ($[2] !== arr || $[3] !== myVar) {
t2 = <Stringify cb={myVar} value={arr} shouldInvokeFns={true} />;
$[2] = arr;
$[3] = myVar;
$[4] = t2;
let t1;
if ($[0] !== arr || $[1] !== myVar) {
t1 = <Stringify cb={myVar} value={arr} shouldInvokeFns={true} />;
$[0] = arr;
$[1] = myVar;
$[2] = t1;
} else {
t2 = $[4];
t1 = $[2];
}
return t2;
return t1;
}
function _temp2() {
return console.log("b");

View File

@@ -22,15 +22,15 @@ const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.bail.rules-of-hooks-3d692676194b.ts:8:4
6 | const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
7 | useEffect(() => {
> 8 | useHookInsideCallback();
| ^^^^^^^^^^^^^^^^^^^^^ 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 hook within a function expression
9 | });
10 | return <button {...props} ref={ref} />;
11 | });

View File

@@ -22,15 +22,15 @@ const ComponentWithHookInsideCallback = React.memo(props => {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.bail.rules-of-hooks-8503ca76d6f8.ts:8:4
6 | const ComponentWithHookInsideCallback = React.memo(props => {
7 | useEffect(() => {
> 8 | useHookInsideCallback();
| ^^^^^^^^^^^^^^^^^^^^^ 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 hook within a function expression
9 | });
10 | return <button {...props} />;
11 | });

View File

@@ -20,35 +20,41 @@ function Component(props) {
```
Found 3 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: Cannot reference hooks as regular values
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-call-phi-possibly-hook.ts:3:31
1 | function Component(props) {
2 | // This is a violation of using a hook as a normal value rule:
> 3 | const getUser = props.cond ? useGetUser : emptyFunction;
| ^^^^^^^^^^ 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
| ^^^^^^^^^^ Hooks may not be referenced as values
4 |
5 | // Ideally we would report a "conditional hook call" error here.
6 | // It's an unconditional call, but the value may or may not be a hook.
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: Cannot reference hooks as regular values
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-call-phi-possibly-hook.ts:3:18
1 | function Component(props) {
2 | // This is a violation of using a hook as a normal value rule:
> 3 | const getUser = props.cond ? useGetUser : emptyFunction;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Hooks may not be referenced as values
4 |
5 | // Ideally we would report a "conditional hook call" error here.
6 | // It's an unconditional call, but the value may or may not be a hook.
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: Cannot reference hooks as regular values
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-call-phi-possibly-hook.ts:8:9
6 | // It's an unconditional call, but the value may or may not be a hook.
7 | // TODO: report a conditional hook call error here
> 8 | return getUser();
| ^^^^^^^ 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
| ^^^^^^^ Hooks may not be referenced as values
9 | }
10 |
```

View File

@@ -19,13 +19,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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-conditionally-call-local-named-like-hook.ts:6:4
4 | const useFoo = makeObject_Primitives();
5 | if (props.cond) {
> 6 | useFoo();
| ^^^^^^ 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)
| ^^^^^^ Cannot call hook conditionally
7 | }
8 | }
9 |

View File

@@ -16,13 +16,15 @@ function Component({cond, useFoo}) {
```
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: Cannot call hooks conditionally
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-conditionally-call-prop-named-like-hook.ts:3:4
1 | function Component({cond, useFoo}) {
2 | if (cond) {
> 3 | useFoo();
| ^^^^^^ 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)
| ^^^^^^ Cannot call hook conditionally
4 | }
5 | }
6 |

View File

@@ -19,13 +19,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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-conditionally-methodcall-hooklike-property-of-local.ts:6:4
4 | const local = makeObject_Primitives();
5 | if (props.cond) {
> 6 | local.useFoo();
| ^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^ Cannot call hook conditionally
7 | }
8 | }
9 |

View File

@@ -20,13 +20,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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-condtionally-call-hooklike-property-of-local.ts:7:4
5 | if (props.cond) {
6 | const foo = local.useFoo;
> 7 | foo();
| ^^^ 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)
| ^^^ Cannot call hook conditionally
8 | }
9 | }
10 |

View File

@@ -16,13 +16,15 @@ function Component() {
```
Found 1 error:
Error: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
Error: Hooks must be the same on every render
Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
error.invalid-dynamic-hook-via-hooklike-local.ts:4:2
2 | const someFunction = useContext(FooContext);
3 | const useOhItsNamedLikeAHookNow = someFunction;
> 4 | useOhItsNamedLikeAHookNow();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
| ^^^^^^^^^^^^^^^^^^^^^^^^^ This value may change to a different function
5 | }
6 |
```

View File

@@ -17,13 +17,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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-hook-after-early-return.ts:5:9
3 | return null;
4 | }
> 5 | return useHook();
| ^^^^^^^ 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)
| ^^^^^^^ Cannot call hook conditionally
6 | }
7 |
```

View File

@@ -15,12 +15,14 @@ function Component(props) {
```
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: Cannot reference hooks as regular values
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-hook-as-conditional-test.ts:2:26
1 | function Component(props) {
> 2 | const x = props.cond ? (useFoo ? 1 : 2) : 3;
| ^^^^^^ 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
| ^^^^^^ Hooks may not be referenced as values
3 | return x;
4 | }
5 |

View File

@@ -14,12 +14,14 @@ function Component({useFoo}) {
```
Found 1 error:
Error: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
Error: Hooks must be the same on every render
Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
error.invalid-hook-as-prop.ts:2:2
1 | function Component({useFoo}) {
> 2 | useFoo();
| ^^^^^^ Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
| ^^^^^^ This value may change to a different function
3 | }
4 |
```

View File

@@ -18,24 +18,28 @@ function Component(props) {
```
Found 2 errors:
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: Cannot call hooks conditionally
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-hook-for.ts:4:9
2 | let i = 0;
3 | for (let x = 0; useHook(x) < 10; useHook(i), x++) {
> 4 | i += useHook(x);
| ^^^^^^^ 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)
| ^^^^^^^ Cannot call hook conditionally
5 | }
6 | return i;
7 | }
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: Cannot call hooks conditionally
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-hook-for.ts:3:35
1 | function Component(props) {
2 | let i = 0;
> 3 | for (let x = 0; useHook(x) < 10; useHook(i), x++) {
| ^^^^^^^ 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)
| ^^^^^^^ Cannot call hook conditionally
4 | i += useHook(x);
5 | }
6 | return i;

View File

@@ -16,13 +16,15 @@ function useFoo({data}) {
```
Found 1 error:
Error: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
Error: Hooks must be the same on every render
Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
error.invalid-hook-from-hook-return.ts:3:14
1 | function useFoo({data}) {
2 | const useMedia = useVideoPlayer();
> 3 | const foo = useMedia();
| ^^^^^^^^ Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
| ^^^^^^^^ This value may change to a different function
4 | return foo;
5 | }
6 |

View File

@@ -16,13 +16,15 @@ function useFoo({data}) {
```
Found 1 error:
Error: Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
Error: Hooks must be the same on every render
Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
error.invalid-hook-from-property-of-other-hook.ts:3:14
1 | function useFoo({data}) {
2 | const player = useVideoPlayer();
> 3 | const foo = player.useMedia();
| ^^^^^^^^^^^^^^^ Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks
| ^^^^^^^^^^^^^^^ This value may change to a different function
4 | return foo;
5 | }
6 |

View File

@@ -19,13 +19,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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-hook-if-alternate.ts:5:8
3 | if (props.cond) {
4 | } else {
> 5 | x = useHook();
| ^^^^^^^ 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)
| ^^^^^^^ Cannot call hook conditionally
6 | }
7 | return x;
8 | }

View File

@@ -18,13 +18,15 @@ function Component(props) {
```
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: Cannot call hooks conditionally
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-hook-if-consequent.ts:4:8
2 | let x = null;
3 | if (props.cond) {
> 4 | x = useHook();
| ^^^^^^^ 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)
| ^^^^^^^ Cannot call hook conditionally
5 | }
6 | return x;
7 | }

View File

@@ -30,15 +30,15 @@ function Component() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.invalid-hook-in-nested-function-expression-object-expression.ts:10:21
8 | const y = {
9 | inner() {
> 10 | return useFoo();
| ^^^^^^ 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 hook within a function expression
11 | },
12 | };
13 | return y;

View File

@@ -26,15 +26,15 @@ function Component() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.invalid-hook-in-nested-object-method.ts:8:17
6 | const y = {
7 | inner() {
> 8 | return useFoo();
| ^^^^^^ 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 hook within a function expression
9 | },
10 | };
11 | return y;

View File

@@ -15,12 +15,14 @@ function Component() {
```
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: Cannot call hooks conditionally
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-hook-optional-methodcall.ts:2:19
1 | function Component() {
> 2 | const {result} = Module.useConditionalHook?.() ?? {};
| ^^^^^^^^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
3 | return result;
4 | }
5 |

View File

@@ -15,12 +15,14 @@ function Component() {
```
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: Cannot call hooks conditionally
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-hook-optional-property.ts:2:19
1 | function Component() {
> 2 | const {result} = Module?.useConditionalHook() ?? {};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
3 | return result;
4 | }
5 |

View File

@@ -15,12 +15,14 @@ function Component() {
```
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: Cannot call hooks conditionally
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-hook-optionalcall.ts:2:19
1 | function Component() {
> 2 | const {result} = useConditionalHook?.() ?? {};
| ^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
3 | return result;
4 | }
5 |

View File

@@ -16,35 +16,41 @@ function Component(props) {
```
Found 3 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: Cannot reference hooks as regular values
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-hook-reassigned-in-conditional.ts:3:20
1 | function Component(props) {
2 | let y;
> 3 | props.cond ? (y = useFoo) : null;
| ^^^^^^ 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
| ^^^^^^ Hooks may not be referenced as values
4 | return y();
5 | }
6 |
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: Cannot reference hooks as regular values
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-hook-reassigned-in-conditional.ts:3:16
1 | function Component(props) {
2 | let y;
> 3 | props.cond ? (y = useFoo) : null;
| ^ 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
| ^ Hooks may not be referenced as values
4 | return y();
5 | }
6 |
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: Cannot reference hooks as regular values
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-hook-reassigned-in-conditional.ts:4:9
2 | let y;
3 | props.cond ? (y = useFoo) : null;
> 4 | return y();
| ^ 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
| ^ Hooks may not be referenced as values
5 | }
6 |
```

View File

@@ -27,46 +27,54 @@ function useHookInLoops() {
```
Found 4 errors:
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: Cannot call hooks conditionally
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-rules-of-hooks-1b9527f967f3.ts:7:4
5 | function useHookInLoops() {
6 | while (a) {
> 7 | useHook1();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
8 | if (b) return;
9 | useHook2();
10 | }
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: Cannot call hooks conditionally
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-rules-of-hooks-1b9527f967f3.ts:9:4
7 | useHook1();
8 | if (b) return;
> 9 | useHook2();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
10 | }
11 | while (c) {
12 | useHook3();
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: Cannot call hooks conditionally
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-rules-of-hooks-1b9527f967f3.ts:12:4
10 | }
11 | while (c) {
> 12 | useHook3();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
13 | if (d) return;
14 | useHook4();
15 | }
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: Cannot call hooks conditionally
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-rules-of-hooks-1b9527f967f3.ts:14:4
12 | useHook3();
13 | if (d) return;
> 14 | useHook4();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
15 | }
16 | }
17 |

View File

@@ -20,13 +20,15 @@ function ComponentWithConditionalHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-2aabd222fc6a.ts:7:4
5 | function ComponentWithConditionalHook() {
6 | if (cond) {
> 7 | useConditionalHook();
| ^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
8 | }
9 | }
10 |

View File

@@ -21,13 +21,15 @@ function useLabeledBlock() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-49d341e5d68f.ts:8:4
6 | label: {
7 | if (a) break label;
> 8 | useHook();
| ^^^^^^^ 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)
| ^^^^^^^ Cannot call hook conditionally
9 | }
10 | }
11 |

View File

@@ -20,13 +20,15 @@ function ComponentWithHookInsideLoop() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-79128a755612.ts:7:4
5 | function ComponentWithHookInsideLoop() {
6 | while (cond) {
> 7 | useHookInsideLoop();
| ^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
8 | }
9 | }
10 |

View File

@@ -24,13 +24,15 @@ function useHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-9718e30b856c.ts:12:2
10 | console.log('false');
11 | }
> 12 | useState();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
13 | }
14 |
```

View File

@@ -19,24 +19,28 @@ function useHook() {
```
Found 2 errors:
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: Cannot call hooks conditionally
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-rules-of-hooks-9bf17c174134.ts:6:7
4 | // This *must* be invalid.
5 | function useHook() {
> 6 | a && useHook1();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
7 | b && useHook2();
8 | }
9 |
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: Cannot call hooks conditionally
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-rules-of-hooks-9bf17c174134.ts:7:7
5 | function useHook() {
6 | a && useHook1();
> 7 | b && useHook2();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
8 | }
9 |
```

View File

@@ -18,13 +18,15 @@ function ComponentWithTernaryHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-b4dcda3d60ed.ts:6:9
4 | // This *must* be invalid.
5 | function ComponentWithTernaryHook() {
> 6 | cond ? useTernaryHook() : null;
| ^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^ Cannot call hook conditionally
7 | }
8 |
```

View File

@@ -19,13 +19,15 @@ function useHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-c906cace44e9.ts:7:2
5 | function useHook() {
6 | if (a) return;
> 7 | useState();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
8 | }
9 |
```

View File

@@ -20,13 +20,15 @@ function normalFunctionWithConditionalHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-d740d54e9c21.ts:7:4
5 | function normalFunctionWithConditionalHook() {
6 | if (cond) {
> 7 | useHookInsideNormalFunction();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
8 | }
9 | }
10 |

View File

@@ -22,24 +22,28 @@ function useHookInLoops() {
```
Found 2 errors:
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: Cannot call hooks conditionally
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-rules-of-hooks-d85c144bdf40.ts:7:4
5 | function useHookInLoops() {
6 | while (a) {
> 7 | useHook1();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
8 | if (b) continue;
9 | useHook2();
10 | }
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: Cannot call hooks conditionally
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-rules-of-hooks-d85c144bdf40.ts:9:4
7 | useHook1();
8 | if (b) continue;
> 9 | useHook2();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
10 | }
11 | }
12 |

View File

@@ -20,13 +20,15 @@ function useHookWithConditionalHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-ea7c2fb545a9.ts:7:4
5 | function useHookWithConditionalHook() {
6 | if (cond) {
> 7 | useConditionalHook();
| ^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
8 | }
9 | }
10 |

View File

@@ -24,13 +24,15 @@ function useHook() {
```
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: Cannot call hooks conditionally
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-rules-of-hooks-f3d6c5e9c83d.ts:12:2
10 | }
11 | if (a) return;
> 12 | useState();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
13 | }
14 |
```

View File

@@ -20,35 +20,41 @@ function useHook({bar}) {
```
Found 3 errors:
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: Cannot call hooks conditionally
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-rules-of-hooks-f69800950ff0.ts:6:20
4 | // This *must* be invalid.
5 | function useHook({bar}) {
> 6 | let foo1 = bar && useState();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
7 | let foo2 = bar || useState();
8 | let foo3 = bar ?? useState();
9 | }
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: Cannot call hooks conditionally
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-rules-of-hooks-f69800950ff0.ts:7:20
5 | function useHook({bar}) {
6 | let foo1 = bar && useState();
> 7 | let foo2 = bar || useState();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
8 | let foo3 = bar ?? useState();
9 | }
10 |
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: Cannot call hooks conditionally
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-rules-of-hooks-f69800950ff0.ts:8:20
6 | let foo1 = bar && useState();
7 | let foo2 = bar || useState();
> 8 | let foo3 = bar ?? useState();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
9 | }
10 |
```

View File

@@ -20,15 +20,15 @@ function createHook() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.invalid.invalid-rules-of-hooks-0a1dbff27ba0.ts:6:6
4 | return function useHookWithConditionalHook() {
5 | if (cond) {
> 6 | useConditionalHook();
| ^^^^^^^^^^^^^^^^^^ 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 hook within a function expression
7 | }
8 | };
9 | }

View File

@@ -20,28 +20,28 @@ function createComponent() {
```
Found 2 errors:
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.invalid.invalid-rules-of-hooks-0de1224ce64b.ts:6:6
4 | return function ComponentWithHookInsideCallback() {
5 | useEffect(() => {
> 6 | useHookInsideCallback();
| ^^^^^^^^^^^^^^^^^^^^^ 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 hook within a function expression
7 | });
8 | };
9 | }
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)
Error: Cannot call hooks within function expressions
Cannot call useEffect within a function expression.
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)
error.invalid.invalid-rules-of-hooks-0de1224ce64b.ts:5:4
3 | function createComponent() {
4 | return function ComponentWithHookInsideCallback() {
> 5 | useEffect(() => {
| ^^^^^^^^^ 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
6 | useHookInsideCallback();
7 | });
8 | };

View File

@@ -20,15 +20,15 @@ function createComponent() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call useState within a function expression.
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)
error.invalid.invalid-rules-of-hooks-449a37146a83.ts:6:6
4 | return function ComponentWithHookInsideCallback() {
5 | function handleClick() {
> 6 | useState();
| ^^^^^^^^ 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 useState within a function expression
7 | }
8 | };
9 | }

View File

@@ -18,15 +18,15 @@ function ComponentWithHookInsideCallback() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call useState within a function expression.
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)
error.invalid.invalid-rules-of-hooks-76a74b4666e9.ts:5:4
3 | function ComponentWithHookInsideCallback() {
4 | function handleClick() {
> 5 | useState();
| ^^^^^^^^ 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 useState within a function expression
6 | }
7 | }
8 |

View File

@@ -20,15 +20,15 @@ function createComponent() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.invalid.invalid-rules-of-hooks-d842d36db450.ts:6:6
4 | return function ComponentWithConditionalHook() {
5 | if (cond) {
> 6 | useConditionalHook();
| ^^^^^^^^^^^^^^^^^^ 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 hook within a function expression
7 | }
8 | };
9 | }

View File

@@ -18,15 +18,15 @@ function ComponentWithHookInsideCallback() {
```
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)
Error: Cannot call hooks within function expressions
Cannot call hook within a function expression.
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)
error.invalid.invalid-rules-of-hooks-d952b82c2597.ts:5:4
3 | function ComponentWithHookInsideCallback() {
4 | useEffect(() => {
> 5 | useHookInsideCallback();
| ^^^^^^^^^^^^^^^^^^^^^ 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 hook within a function expression
6 | });
7 | }
8 |

View File

@@ -22,13 +22,15 @@ const FancyButton = forwardRef(function (props, ref) {
```
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: Cannot call hooks conditionally
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-368024110a58.ts:8:4
6 | const FancyButton = forwardRef(function (props, ref) {
7 | if (props.fancy) {
> 8 | useCustomHook();
| ^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^ Cannot call hook conditionally
9 | }
10 | return <button ref={ref}>{props.children}</button>;
11 | });

View File

@@ -22,13 +22,15 @@ const MemoizedButton = memo(function (props) {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^ Cannot call hook conditionally
9 | }
10 | return <button>{props.children}</button>;
11 | });

View File

@@ -21,13 +21,15 @@ function ComponentWithConditionalHook() {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook conditionally
9 | }
10 | }
11 |

View File

@@ -22,13 +22,15 @@ const FancyButton = React.forwardRef((props, ref) => {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^ Cannot call hook conditionally
9 | }
10 | return <button ref={ref}>{props.children}</button>;
11 | });

View File

@@ -21,13 +21,15 @@ React.unknownFunction((foo, bar) => {
```
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: Cannot call hooks conditionally
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);
| ^^^^^^^^^^^ 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)
| ^^^^^^^^^^^ Cannot call hook conditionally
9 | }
10 | });
11 |

View File

@@ -22,13 +22,15 @@ function useHook() {
```
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: Cannot call hooks conditionally
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();
| ^^^^^^^^ 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)
| ^^^^^^^^ Cannot call hook conditionally
10 | } catch {}
11 | }
12 |

View File

@@ -30,15 +30,15 @@ function Component(props) {
```
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)
Error: Cannot call hooks within function expressions
Cannot call useEffect within a function expression.
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)
error.invalid-nested-use-effect.ts:9:4
7 | };
8 | useEffect(() => {
> 9 | useEffect(() => {
| ^^^^^^^^^ 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
10 | function nested() {
11 | fire(foo(props));
12 | }

View File

@@ -48,10 +48,7 @@ export {
printReactiveFunction,
printReactiveFunctionWithOutlined,
} from './ReactiveScopes';
export {
parseConfigPragmaForTests,
parseConfigPragmaAsString,
} from './Utils/TestUtils';
export {parseConfigPragmaForTests} from './Utils/TestUtils';
declare global {
let __DEV__: boolean | null | undefined;
}

View File

@@ -68,9 +68,7 @@ testRule('plugin-recommended', TestRecommendedRules, {
</>;
}
`,
errors: [
makeTestCaseError('Hooks must always be called in a consistent order'),
],
errors: [makeTestCaseError('Cannot call hooks conditionally')],
},
{
name: 'Multiple diagnostics within the same file are surfaced',
@@ -84,8 +82,8 @@ testRule('plugin-recommended', TestRecommendedRules, {
return props.cond && useConditionalHook();
}`,
errors: [
makeTestCaseError('Hooks must always be called in a consistent order'),
makeTestCaseError('Hooks must always be called in a consistent order'),
makeTestCaseError('Cannot call hooks conditionally'),
makeTestCaseError('Cannot call hooks conditionally'),
],
},
{
@@ -98,9 +96,7 @@ testRule('plugin-recommended', TestRecommendedRules, {
}
`,
errors: [
makeTestCaseError('Hooks must always be called in a consistent order'),
],
errors: [makeTestCaseError('Cannot call hooks conditionally')],
},
{
name: 'Multiple non-fatal useMemo diagnostics are surfaced',

View File

@@ -15,7 +15,7 @@ const configs = {
recommended: {
plugins: {
'react-compiler': {
rules: allRules,
rules: recommendedRules,
},
},
rules: Object.fromEntries(

View File

@@ -112,6 +112,28 @@ function runReactCompilerImpl({
userOpts,
}: RunParams): RunCacheEntry {
// Compat with older versions of eslint
for (const [key, entry] of Object.entries(userOpts)) {
if (key === 'environment' && COMPILER_OPTIONS.environment != null) {
for (const envKey of Object.keys(entry as Record<string, any>)) {
if (
Object.prototype.hasOwnProperty.call(
COMPILER_OPTIONS.environment,
envKey,
) &&
!isDeepStrictEqual(
(entry as Record<string, any>)[envKey],
(COMPILER_OPTIONS.environment as Record<string, any>)[envKey],
)
) {
console.warn('Conflicting environment option detected: ' + envKey);
}
}
} else if (Object.prototype.hasOwnProperty.call(COMPILER_OPTIONS, key)) {
if (!isDeepStrictEqual(entry, (COMPILER_OPTIONS as any)[key])) {
console.warn('Conflicting option detected: ' + key);
}
}
}
const options: PluginOptions = parsePluginOptions({
...COMPILER_OPTIONS,
...userOpts,

View File

@@ -1,32 +0,0 @@
const ReactCompiler = require('../packages/babel-plugin-react-compiler/dist');
const combinedRules = [
{
name: 'rules-of-hooks',
recommended: true,
description:
'Validates that components and hooks follow the [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks)',
},
{
name: 'exhaustive-deps',
recommended: true,
description:
'Validates that hooks which accept dependency arrays (`useMemo()`, `useCallback()`, `useEffect()`, etc) ' +
'list all referenced variables in their dependency array. Referencing a value without including it in the ' +
'dependency array can lead to stale UI or callbacks.',
},
...ReactCompiler.LintRules,
];
const printed = combinedRules
.filter(rule => rule.recommended)
.map(rule => {
return `
## \`react-hooks/${rule.name}\`
${rule.description}
`.trim();
})
.join('\n\n');
console.log(printed);

View File

@@ -3,7 +3,7 @@ import Fixture from '../../Fixture';
const React = window.React;
const {Fragment, useRef} = React;
const {Fragment, useEffect, useRef, useState} = React;
export default function FocusCase() {
const fragmentRef = useRef(null);

View File

@@ -2,7 +2,7 @@ import TestCase from '../../TestCase';
import Fixture from '../../Fixture';
const React = window.React;
const {Fragment, useRef, useState} = React;
const {Fragment, useEffect, useRef, useState} = React;
export default function GetClientRectsCase() {
const fragmentRef = useRef(null);

View File

@@ -1,184 +0,0 @@
import TestCase from '../../TestCase';
import Fixture from '../../Fixture';
import ScrollIntoViewCaseComplex from './ScrollIntoViewCaseComplex';
import ScrollIntoViewCaseSimple from './ScrollIntoViewCaseSimple';
import ScrollIntoViewTargetElement from './ScrollIntoViewTargetElement';
const React = window.React;
const {Fragment, useRef, useState, useEffect} = React;
const ReactDOM = window.ReactDOM;
function Controls({
alignToTop,
setAlignToTop,
scrollVertical,
exampleType,
setExampleType,
}) {
return (
<div>
<label>
Example Type:
<select
value={exampleType}
onChange={e => setExampleType(e.target.value)}>
<option value="simple">Simple</option>
<option value="multiple">Multiple Scroll Containers</option>
<option value="horizontal">Horizontal</option>
<option value="empty">Empty Fragment</option>
</select>
</label>
<div>
<label>
Align to Top:
<input
type="checkbox"
checked={alignToTop}
onChange={e => setAlignToTop(e.target.checked)}
/>
</label>
</div>
<div>
<button onClick={scrollVertical}>scrollIntoView()</button>
</div>
</div>
);
}
export default function ScrollIntoViewCase() {
const [exampleType, setExampleType] = useState('simple');
const [alignToTop, setAlignToTop] = useState(true);
const [caseInViewport, setCaseInViewport] = useState(false);
const fragmentRef = useRef(null);
const testCaseRef = useRef(null);
const noChildRef = useRef(null);
const scrollContainerRef = useRef(null);
const scrollVertical = () => {
fragmentRef.current.experimental_scrollIntoView(alignToTop);
};
const scrollVerticalNoChildren = () => {
noChildRef.current.experimental_scrollIntoView(alignToTop);
};
useEffect(() => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setCaseInViewport(true);
} else {
setCaseInViewport(false);
}
});
});
testCaseRef.current.observeUsing(observer);
const lastRef = testCaseRef.current;
return () => {
lastRef.unobserveUsing(observer);
observer.disconnect();
};
});
return (
<Fragment ref={testCaseRef}>
<TestCase title="ScrollIntoView">
<TestCase.Steps>
<li>Toggle alignToTop and click the buttons to scroll</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
<p>When the Fragment has children:</p>
<p>
In order to handle the case where children are split between
multiple scroll containers, we call scrollIntoView on each child in
reverse order.
</p>
<p>When the Fragment does not have children:</p>
<p>
The Fragment still represents a virtual space. We can scroll to the
nearest edge by selecting the host sibling before if
alignToTop=false, or after if alignToTop=true|undefined. We'll fall
back to the other sibling or parent in the case that the preferred
sibling target doesn't exist.
</p>
</TestCase.ExpectedResult>
<Fixture>
<Fixture.Controls>
<Controls
alignToTop={alignToTop}
setAlignToTop={setAlignToTop}
scrollVertical={scrollVertical}
exampleType={exampleType}
setExampleType={setExampleType}
/>
</Fixture.Controls>
{exampleType === 'simple' && (
<Fragment ref={fragmentRef}>
<ScrollIntoViewCaseSimple />
</Fragment>
)}
{exampleType === 'horizontal' && (
<div
style={{
display: 'flex',
overflowX: 'auto',
flexDirection: 'row',
border: '1px solid #ccc',
padding: '1rem 10rem',
marginBottom: '1rem',
width: '100%',
whiteSpace: 'nowrap',
justifyContent: 'space-between',
}}>
<Fragment ref={fragmentRef}>
<ScrollIntoViewCaseSimple />
</Fragment>
</div>
)}
{exampleType === 'multiple' && (
<Fragment>
<div
style={{
height: '50vh',
overflowY: 'auto',
border: '1px solid black',
marginBottom: '1rem',
}}
ref={scrollContainerRef}
/>
<Fragment ref={fragmentRef}>
<ScrollIntoViewCaseComplex
caseInViewport={caseInViewport}
scrollContainerRef={scrollContainerRef}
/>
</Fragment>
</Fragment>
)}
{exampleType === 'empty' && (
<Fragment>
<ScrollIntoViewTargetElement
color="lightyellow"
id="ABOVE EMPTY FRAGMENT"
/>
<Fragment ref={fragmentRef}></Fragment>
<ScrollIntoViewTargetElement
color="lightblue"
id="BELOW EMPTY FRAGMENT"
/>
</Fragment>
)}
<Fixture.Controls>
<Controls
alignToTop={alignToTop}
setAlignToTop={setAlignToTop}
scrollVertical={scrollVertical}
exampleType={exampleType}
setExampleType={setExampleType}
/>
</Fixture.Controls>
</Fixture>
</TestCase>
</Fragment>
);
}

View File

@@ -1,50 +0,0 @@
import ScrollIntoViewTargetElement from './ScrollIntoViewTargetElement';
const React = window.React;
const {Fragment, useRef, useState, useEffect} = React;
const ReactDOM = window.ReactDOM;
export default function ScrollIntoViewCaseComplex({
caseInViewport,
scrollContainerRef,
}) {
const [didMount, setDidMount] = useState(false);
// Hack to portal child into the scroll container
// after the first render. This is to simulate a case where
// an item is portaled into another scroll container.
useEffect(() => {
if (!didMount) {
setDidMount(true);
}
}, []);
return (
<Fragment>
{caseInViewport && (
<div
style={{position: 'fixed', top: 0, backgroundColor: 'red'}}
id="header">
Fixed header
</div>
)}
{didMount &&
ReactDOM.createPortal(
<ScrollIntoViewTargetElement color="red" id="FROM_PORTAL" />,
scrollContainerRef.current
)}
<ScrollIntoViewTargetElement color="lightgreen" id="A" />
<ScrollIntoViewTargetElement color="lightcoral" id="B" />
<ScrollIntoViewTargetElement color="lightblue" id="C" />
{caseInViewport && (
<div
style={{
position: 'fixed',
bottom: 0,
backgroundColor: 'purple',
}}
id="footer">
Fixed footer
</div>
)}
</Fragment>
);
}

Some files were not shown because too many files have changed in this diff Show More