Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5008fd1d18 | ||
|
|
d2b2a1b195 | ||
|
|
1671142ea8 | ||
|
|
c5362a380f | ||
|
|
89a803fcec | ||
|
|
8d7b5e4903 | ||
|
|
3434ff4f4b | ||
|
|
bd5b1b7639 | ||
|
|
0a1f1fcd50 | ||
|
|
b870042915 | ||
|
|
33a1095d72 | ||
|
|
213594860f | ||
|
|
9c2e2b8475 | ||
|
|
4123f6b771 | ||
|
|
cb1e73be04 | ||
|
|
cacc20e37c | ||
|
|
bb7c9c1b8a | ||
|
|
44f8451ede | ||
|
|
ad4ecb6e6e | ||
|
|
26e87b5f15 | ||
|
|
75dc0026d6 | ||
|
|
df10309e2b | ||
|
|
e42f3d30ca | ||
|
|
67e743fba5 | ||
|
|
9eede45646 | ||
|
|
090777d78a | ||
|
|
4049cfeeab | ||
|
|
e67e3bed92 | ||
|
|
06cfa99f37 | ||
|
|
05addfc663 | ||
|
|
d260b0d8b8 | ||
|
|
425ba0ad6d | ||
|
|
6de32a5a07 | ||
|
|
698bb4deb7 | ||
|
|
11d7bcf88c | ||
|
|
a85ec041d6 | ||
|
|
7d29ecbeb2 | ||
|
|
253abc78a1 | ||
|
|
d73b6f1110 | ||
|
|
d5586e2059 | ||
|
|
ec5dd0ab3a | ||
|
|
8120753665 | ||
|
|
3770ff3853 | ||
|
|
873f711299 | ||
|
|
5f06c3d22a | ||
|
|
243a56b9a2 | ||
|
|
83c7379b96 | ||
|
|
c2ac8b4f0e | ||
|
|
03fda05d2c | ||
|
|
0bc71e67ab | ||
|
|
3e20dc8b9c | ||
|
|
ae5c2f82b3 |
@@ -28,3 +28,6 @@ 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/
|
||||
|
||||
11
.eslintrc.js
11
.eslintrc.js
@@ -547,13 +547,10 @@ module.exports = {
|
||||
},
|
||||
|
||||
globals: {
|
||||
$Call: 'readonly',
|
||||
$ElementType: 'readonly',
|
||||
$Flow$ModuleRef: 'readonly',
|
||||
$FlowFixMe: 'readonly',
|
||||
$Keys: 'readonly',
|
||||
$NonMaybeType: 'readonly',
|
||||
$PropertyType: 'readonly',
|
||||
$ReadOnly: 'readonly',
|
||||
$ReadOnlyArray: 'readonly',
|
||||
$ArrayBufferView: 'readonly',
|
||||
@@ -568,6 +565,7 @@ module.exports = {
|
||||
BigInt: 'readonly',
|
||||
BigInt64Array: 'readonly',
|
||||
BigUint64Array: 'readonly',
|
||||
CacheType: 'readonly',
|
||||
Class: 'readonly',
|
||||
ClientRect: 'readonly',
|
||||
CopyInspectedElementPath: 'readonly',
|
||||
@@ -582,13 +580,15 @@ module.exports = {
|
||||
IteratorResult: 'readonly',
|
||||
JSONValue: 'readonly',
|
||||
JSResourceReference: 'readonly',
|
||||
mixin$Animatable: 'readonly',
|
||||
MouseEventHandler: 'readonly',
|
||||
NavigateEvent: 'readonly',
|
||||
PerformanceMeasureOptions: 'readonly',
|
||||
PropagationPhases: 'readonly',
|
||||
PropertyDescriptor: 'readonly',
|
||||
React$AbstractComponent: 'readonly',
|
||||
PropertyDescriptorMap: 'readonly',
|
||||
Proxy$traps: 'readonly',
|
||||
React$Component: 'readonly',
|
||||
React$ComponentType: 'readonly',
|
||||
React$Config: 'readonly',
|
||||
React$Context: 'readonly',
|
||||
React$Element: 'readonly',
|
||||
@@ -620,7 +620,6 @@ module.exports = {
|
||||
PropertyIndexedKeyframes: 'readonly',
|
||||
KeyframeAnimationOptions: 'readonly',
|
||||
GetAnimationsOptions: 'readonly',
|
||||
Animatable: 'readonly',
|
||||
ScrollTimeline: 'readonly',
|
||||
EventListenerOptionsOrUseCapture: 'readonly',
|
||||
FocusOptions: 'readonly',
|
||||
|
||||
2
.github/workflows/compiler_playground.yml
vendored
2
.github/workflows/compiler_playground.yml
vendored
@@ -57,8 +57,6 @@ 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()'
|
||||
|
||||
17
.github/workflows/runtime_build_and_test.yml
vendored
17
.github/workflows/runtime_build_and_test.yml
vendored
@@ -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,9 +811,18 @@ jobs:
|
||||
pattern: _build_*
|
||||
path: build
|
||||
merge-multiple: true
|
||||
- run: |
|
||||
npx playwright install
|
||||
sudo npx playwright install-deps
|
||||
- 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: ./scripts/ci/run_devtools_e2e_tests.js
|
||||
env:
|
||||
RELEASE_CHANNEL: experimental
|
||||
|
||||
@@ -18,6 +18,7 @@ jobs:
|
||||
permissions:
|
||||
# Used to create a review and close PRs
|
||||
pull-requests: write
|
||||
contents: write
|
||||
steps:
|
||||
- name: Close PR
|
||||
uses: actions/github-script@v7
|
||||
|
||||
103
compiler/apps/playground/components/Editor/ConfigEditor.tsx
Normal file
103
compiler/apps/playground/components/Editor/ConfigEditor.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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>
|
||||
);
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
type Store,
|
||||
} from '../../lib/stores';
|
||||
import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
import ConfigEditor from './ConfigEditor';
|
||||
import Input from './Input';
|
||||
import {
|
||||
CompilerOutput,
|
||||
@@ -46,6 +47,7 @@ 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,
|
||||
@@ -291,7 +293,13 @@ 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();
|
||||
@@ -328,6 +336,7 @@ 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>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"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"
|
||||
"npm:publish": "node scripts/release/publish",
|
||||
"eslint-docs": "yarn workspace babel-plugin-react-compiler build && node scripts/build-eslint-docs.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "^4.0.2",
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
|
||||
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 {
|
||||
/**
|
||||
@@ -540,6 +541,9 @@ 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',
|
||||
|
||||
@@ -628,7 +632,18 @@ 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 {
|
||||
@@ -636,7 +651,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
name: 'automatic-effect-dependencies',
|
||||
description:
|
||||
'Verifies that automatic effect dependencies are compiled if opted-in',
|
||||
recommended: true,
|
||||
recommended: false,
|
||||
};
|
||||
}
|
||||
case ErrorCategory.CapitalizedCalls: {
|
||||
@@ -652,7 +667,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
return {
|
||||
category,
|
||||
name: 'config',
|
||||
description: 'Validates the configuration',
|
||||
description: 'Validates the compiler configuration options',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -678,7 +693,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'set-state-in-effect',
|
||||
description:
|
||||
'Validates against calling setState synchronously in an effect',
|
||||
'Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -687,7 +702,17 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'error-boundaries',
|
||||
description:
|
||||
'Validates usage of error boundaries instead of try/catch for errors in JSX',
|
||||
'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',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -711,7 +736,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
return {
|
||||
category,
|
||||
name: 'gating',
|
||||
description: 'Validates configuration of gating mode',
|
||||
description:
|
||||
'Validates configuration of [gating mode](https://react.dev/reference/react-compiler/gating)',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -720,7 +746,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'globals',
|
||||
description:
|
||||
'Validates against assignment/mutation of globals during render',
|
||||
'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)',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -742,7 +769,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'immutability',
|
||||
description:
|
||||
'Validates that immutable values (props, state, etc) are not mutated',
|
||||
'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)',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -759,7 +786,9 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'preserve-manual-memoization',
|
||||
description:
|
||||
'Validates that existing manual memoized is preserved by the compiler',
|
||||
'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)',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -768,7 +797,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'purity',
|
||||
description:
|
||||
'Validates that the component/hook is pure, and does not call known-impure functions',
|
||||
'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',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -777,7 +806,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'refs',
|
||||
description:
|
||||
'Validates correct usage of refs, not reading/writing during render',
|
||||
'Validates correct usage of refs, not reading/writing during render. See the "pitfalls" section in [`useRef()` usage](https://react.dev/reference/react/useRef#usage)',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -785,7 +814,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
return {
|
||||
category,
|
||||
name: 'set-state-in-render',
|
||||
description: 'Validates against setting state during render',
|
||||
description:
|
||||
'Validates against setting state during render, which can trigger additional renders and potential infinite render loops',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -794,7 +824,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
category,
|
||||
name: 'static-components',
|
||||
description:
|
||||
'Validates that components are static, not recreated every render',
|
||||
'Validates that components are static, not recreated every render. Components that are recreated dynamically can reset state and trigger excessive re-rendering',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -826,7 +856,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
return {
|
||||
category,
|
||||
name: 'unsupported-syntax',
|
||||
description: 'Validates against syntax that we do not plan to support',
|
||||
description:
|
||||
'Validates against syntax that we do not plan to support in React Compiler',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
@@ -834,7 +865,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
|
||||
return {
|
||||
category,
|
||||
name: 'use-memo',
|
||||
description: 'Validates usage of the useMemo() hook',
|
||||
description:
|
||||
'Validates usage of the useMemo() hook against common mistakes. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.',
|
||||
recommended: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -494,7 +494,20 @@ 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;
|
||||
}
|
||||
@@ -839,6 +852,73 @@ 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,
|
||||
@@ -877,11 +957,6 @@ 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: {
|
||||
|
||||
@@ -650,6 +650,13 @@ 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>;
|
||||
|
||||
@@ -323,6 +323,22 @@ 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;
|
||||
|
||||
@@ -880,7 +880,8 @@ 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) {
|
||||
return `:T${type.kind}<${type.shapeId}>`;
|
||||
const returnType = printType(type.return);
|
||||
return `:T${type.kind}<${type.shapeId}>()${returnType !== '' ? `: ${returnType}` : ''}`;
|
||||
} else {
|
||||
return `:T${type.kind}`;
|
||||
}
|
||||
@@ -983,7 +984,7 @@ export function printAliasingEffect(effect: AliasingEffect): string {
|
||||
case 'MutateConditionally':
|
||||
case 'MutateTransitive':
|
||||
case 'MutateTransitiveConditionally': {
|
||||
return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}`;
|
||||
return `${effect.kind} ${printPlaceForAliasEffect(effect.value)}${effect.kind === 'Mutate' && effect.reason?.kind === 'AssignCurrentProperty' ? ' (assign `.current`)' : ''}`;
|
||||
}
|
||||
case 'MutateFrozen': {
|
||||
return `MutateFrozen ${printPlaceForAliasEffect(effect.place)} reason=${JSON.stringify(effect.error.reason)}`;
|
||||
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
IdentifierId,
|
||||
Instruction,
|
||||
InstructionKind,
|
||||
InstructionValue,
|
||||
isArrayType,
|
||||
isJsxType,
|
||||
isMapType,
|
||||
isPrimitiveType,
|
||||
isRefOrRefValue,
|
||||
@@ -56,7 +56,6 @@ import {
|
||||
printAliasingSignature,
|
||||
printIdentifier,
|
||||
printInstruction,
|
||||
printInstructionValue,
|
||||
printPlace,
|
||||
printSourceLocation,
|
||||
} from '../HIR/PrintHIR';
|
||||
@@ -107,11 +106,11 @@ export function inferMutationAliasingEffects(
|
||||
const statesByBlock: Map<BlockId, InferenceState> = new Map();
|
||||
|
||||
for (const ref of fn.context) {
|
||||
// TODO: using InstructionValue as a bit of a hack, but it's pragmatic
|
||||
const value: InstructionValue = {
|
||||
kind: 'ObjectExpression',
|
||||
properties: [],
|
||||
loc: ref.loc,
|
||||
const value: AliasingEffect = {
|
||||
kind: 'Create',
|
||||
into: ref,
|
||||
value: ValueKind.Context,
|
||||
reason: ValueReason.Other,
|
||||
};
|
||||
initialState.initialize(value, {
|
||||
kind: ValueKind.Context,
|
||||
@@ -144,10 +143,11 @@ export function inferMutationAliasingEffects(
|
||||
}
|
||||
if (ref != null) {
|
||||
const place = ref.kind === 'Identifier' ? ref : ref.place;
|
||||
const value: InstructionValue = {
|
||||
kind: 'ObjectExpression',
|
||||
properties: [],
|
||||
loc: place.loc,
|
||||
const value: AliasingEffect = {
|
||||
kind: 'Create',
|
||||
into: place,
|
||||
value: ValueKind.Mutable,
|
||||
reason: ValueReason.Other,
|
||||
};
|
||||
initialState.initialize(value, {
|
||||
kind: ValueKind.Mutable,
|
||||
@@ -264,8 +264,6 @@ 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>
|
||||
@@ -317,10 +315,11 @@ function inferParam(
|
||||
paramKind: AbstractValue,
|
||||
): void {
|
||||
const place = param.kind === 'Identifier' ? param : param.place;
|
||||
const value: InstructionValue = {
|
||||
kind: 'Primitive',
|
||||
loc: place.loc,
|
||||
value: undefined,
|
||||
const value: AliasingEffect = {
|
||||
kind: 'Create',
|
||||
into: place,
|
||||
value: paramKind.kind,
|
||||
reason: ValueReason.Other,
|
||||
};
|
||||
initialState.initialize(value, paramKind);
|
||||
initialState.define(place, value);
|
||||
@@ -541,20 +540,11 @@ function applyEffect(
|
||||
});
|
||||
initialized.add(effect.into.identifier.id);
|
||||
|
||||
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, {
|
||||
state.initialize(effect, {
|
||||
kind: effect.value,
|
||||
reason: new Set([effect.reason]),
|
||||
});
|
||||
state.define(effect.into, value);
|
||||
state.define(effect.into, effect);
|
||||
effects.push(effect);
|
||||
break;
|
||||
}
|
||||
@@ -581,20 +571,11 @@ function applyEffect(
|
||||
initialized.add(effect.into.identifier.id);
|
||||
|
||||
const fromValue = state.kind(effect.from);
|
||||
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, {
|
||||
state.initialize(effect, {
|
||||
kind: fromValue.kind,
|
||||
reason: new Set(fromValue.reason),
|
||||
});
|
||||
state.define(effect.into, value);
|
||||
state.define(effect.into, effect);
|
||||
switch (fromValue.kind) {
|
||||
case ValueKind.Primitive:
|
||||
case ValueKind.Global: {
|
||||
@@ -682,11 +663,11 @@ function applyEffect(
|
||||
operand.effect = Effect.Read;
|
||||
}
|
||||
}
|
||||
state.initialize(effect.function, {
|
||||
state.initialize(effect, {
|
||||
kind: isMutable ? ValueKind.Mutable : ValueKind.Frozen,
|
||||
reason: new Set([]),
|
||||
});
|
||||
state.define(effect.into, effect.function);
|
||||
state.define(effect.into, effect);
|
||||
for (const capture of effect.captures) {
|
||||
applyEffect(
|
||||
context,
|
||||
@@ -792,38 +773,20 @@ function applyEffect(
|
||||
initialized,
|
||||
effects,
|
||||
);
|
||||
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, {
|
||||
state.initialize(effect, {
|
||||
kind: fromKind,
|
||||
reason: new Set(fromValue.reason),
|
||||
});
|
||||
state.define(effect.into, value);
|
||||
state.define(effect.into, effect);
|
||||
break;
|
||||
}
|
||||
case ValueKind.Global:
|
||||
case ValueKind.Primitive: {
|
||||
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, {
|
||||
state.initialize(effect, {
|
||||
kind: fromKind,
|
||||
reason: new Set(fromValue.reason),
|
||||
});
|
||||
state.define(effect.into, value);
|
||||
state.define(effect.into, effect);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -838,14 +801,15 @@ function applyEffect(
|
||||
const functionValues = state.values(effect.function);
|
||||
if (
|
||||
functionValues.length === 1 &&
|
||||
functionValues[0].kind === 'FunctionExpression' &&
|
||||
functionValues[0].loweredFunc.func.aliasingEffects != null
|
||||
functionValues[0].kind === 'CreateFunction' &&
|
||||
functionValues[0].function.kind === 'FunctionExpression' &&
|
||||
functionValues[0].function.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];
|
||||
const functionExpr = functionValues[0].function;
|
||||
let signature = context.functionSignatureCache.get(functionExpr);
|
||||
if (signature == null) {
|
||||
signature = buildSignatureFromFunctionExpression(
|
||||
@@ -1135,19 +1099,19 @@ class InferenceState {
|
||||
#isFunctionExpression: boolean;
|
||||
|
||||
// The kind of each value, based on its allocation site
|
||||
#values: Map<InstructionValue, AbstractValue>;
|
||||
#values: Map<AliasingEffect, 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<InstructionValue>>;
|
||||
#variables: Map<IdentifierId, Set<AliasingEffect>>;
|
||||
|
||||
constructor(
|
||||
env: Environment,
|
||||
isFunctionExpression: boolean,
|
||||
values: Map<InstructionValue, AbstractValue>,
|
||||
variables: Map<IdentifierId, Set<InstructionValue>>,
|
||||
values: Map<AliasingEffect, AbstractValue>,
|
||||
variables: Map<IdentifierId, Set<AliasingEffect>>,
|
||||
) {
|
||||
this.env = env;
|
||||
this.#isFunctionExpression = isFunctionExpression;
|
||||
@@ -1167,18 +1131,11 @@ class InferenceState {
|
||||
}
|
||||
|
||||
// (Re)initializes a @param value with its default @param kind.
|
||||
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,
|
||||
});
|
||||
initialize(value: AliasingEffect, kind: AbstractValue): void {
|
||||
this.#values.set(value, kind);
|
||||
}
|
||||
|
||||
values(place: Place): Array<InstructionValue> {
|
||||
values(place: Place): Array<AliasingEffect> {
|
||||
const values = this.#variables.get(place.identifier.id);
|
||||
CompilerError.invariant(values != null, {
|
||||
reason: `[InferMutationAliasingEffects] Expected value kind to be initialized`,
|
||||
@@ -1241,13 +1198,13 @@ class InferenceState {
|
||||
}
|
||||
|
||||
// Defines (initializing or updating) a variable with a specific kind of value.
|
||||
define(place: Place, value: InstructionValue): void {
|
||||
define(place: Place, value: AliasingEffect): void {
|
||||
CompilerError.invariant(this.#values.has(value), {
|
||||
reason: `[InferMutationAliasingEffects] Expected value to be initialized at '${printSourceLocation(
|
||||
value.loc,
|
||||
place.loc,
|
||||
)}'`,
|
||||
description: printInstructionValue(value),
|
||||
loc: value.loc,
|
||||
description: printAliasingEffect(value),
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#variables.set(place.identifier.id, new Set([value]));
|
||||
@@ -1287,17 +1244,17 @@ class InferenceState {
|
||||
}
|
||||
}
|
||||
|
||||
freezeValue(value: InstructionValue, reason: ValueReason): void {
|
||||
freezeValue(value: AliasingEffect, reason: ValueReason): void {
|
||||
this.#values.set(value, {
|
||||
kind: ValueKind.Frozen,
|
||||
reason: new Set([reason]),
|
||||
});
|
||||
if (
|
||||
value.kind === 'FunctionExpression' &&
|
||||
value.kind === 'CreateFunction' &&
|
||||
(this.env.config.enablePreserveExistingMemoizationGuarantees ||
|
||||
this.env.config.enableTransitivelyFreezeFunctionExpressions)
|
||||
) {
|
||||
for (const place of value.loweredFunc.func.context) {
|
||||
for (const place of value.function.loweredFunc.func.context) {
|
||||
this.freeze(place, reason);
|
||||
}
|
||||
}
|
||||
@@ -1372,8 +1329,8 @@ class InferenceState {
|
||||
* termination.
|
||||
*/
|
||||
merge(other: InferenceState): InferenceState | null {
|
||||
let nextValues: Map<InstructionValue, AbstractValue> | null = null;
|
||||
let nextVariables: Map<IdentifierId, Set<InstructionValue>> | null = null;
|
||||
let nextValues: Map<AliasingEffect, AbstractValue> | null = null;
|
||||
let nextVariables: Map<IdentifierId, Set<AliasingEffect>> | null = null;
|
||||
|
||||
for (const [id, thisValue] of this.#values) {
|
||||
const otherValue = other.#values.get(id);
|
||||
@@ -1397,7 +1354,7 @@ class InferenceState {
|
||||
for (const [id, thisValues] of this.#variables) {
|
||||
const otherValues = other.#variables.get(id);
|
||||
if (otherValues !== undefined) {
|
||||
let mergedValues: Set<InstructionValue> | null = null;
|
||||
let mergedValues: Set<AliasingEffect> | null = null;
|
||||
for (const otherValue of otherValues) {
|
||||
if (!thisValues.has(otherValue)) {
|
||||
mergedValues = mergedValues ?? new Set(thisValues);
|
||||
@@ -1450,8 +1407,8 @@ class InferenceState {
|
||||
*/
|
||||
debug(): any {
|
||||
const result: any = {values: {}, variables: {}};
|
||||
const objects: Map<InstructionValue, number> = new Map();
|
||||
function identify(value: InstructionValue): number {
|
||||
const objects: Map<AliasingEffect, number> = new Map();
|
||||
function identify(value: AliasingEffect): number {
|
||||
let id = objects.get(value);
|
||||
if (id == null) {
|
||||
id = objects.size;
|
||||
@@ -1463,7 +1420,7 @@ class InferenceState {
|
||||
const id = identify(value);
|
||||
result.values[id] = {
|
||||
abstract: this.debugAbstractValue(kind),
|
||||
value: printInstructionValue(value),
|
||||
value: printAliasingEffect(value),
|
||||
};
|
||||
}
|
||||
for (const [variable, values] of this.#variables) {
|
||||
@@ -1480,7 +1437,7 @@ class InferenceState {
|
||||
}
|
||||
|
||||
inferPhi(phi: Phi): void {
|
||||
const values: Set<InstructionValue> = new Set();
|
||||
const values: Set<AliasingEffect> = 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
|
||||
@@ -1871,6 +1828,23 @@ 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;
|
||||
}
|
||||
@@ -2329,8 +2303,9 @@ function areArgumentsImmutableAndNonMutating(
|
||||
const values = state.values(place);
|
||||
for (const value of values) {
|
||||
if (
|
||||
value.kind === 'FunctionExpression' &&
|
||||
value.loweredFunc.func.params.some(param => {
|
||||
value.kind === 'CreateFunction' &&
|
||||
value.function.kind === 'FunctionExpression' &&
|
||||
value.function.loweredFunc.func.params.some(param => {
|
||||
const place = param.kind === 'Identifier' ? param : param.place;
|
||||
const range = place.identifier.mutableRange;
|
||||
return range.end > range.start + 1;
|
||||
@@ -2523,10 +2498,47 @@ function computeEffectsForSignature(
|
||||
break;
|
||||
}
|
||||
case 'CreateFunction': {
|
||||
CompilerError.throwTodo({
|
||||
reason: `Support CreateFrom effects in signatures`,
|
||||
loc: receiver.loc,
|
||||
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,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
} from '../HIR/visitors';
|
||||
import {assertExhaustive, getOrInsertWith} from '../Utils/utils';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {AliasingEffect} from './AliasingEffects';
|
||||
import {AliasingEffect, MutationReason} from './AliasingEffects';
|
||||
|
||||
/**
|
||||
* This pass builds an abstract model of the heap and interprets the effects of the
|
||||
@@ -101,6 +101,7 @@ export function inferMutationAliasingRanges(
|
||||
transitive: boolean;
|
||||
kind: MutationKind;
|
||||
place: Place;
|
||||
reason: MutationReason | null;
|
||||
}> = [];
|
||||
const renders: Array<{index: number; place: Place}> = [];
|
||||
|
||||
@@ -140,7 +141,7 @@ export function inferMutationAliasingRanges(
|
||||
} else if (effect.kind === 'CreateFunction') {
|
||||
state.create(effect.into, {
|
||||
kind: 'Function',
|
||||
function: effect.function.loweredFunc.func,
|
||||
effect,
|
||||
});
|
||||
} else if (effect.kind === 'CreateFrom') {
|
||||
state.createFrom(index++, effect.from, effect.into);
|
||||
@@ -155,7 +156,7 @@ export function inferMutationAliasingRanges(
|
||||
* invariant here.
|
||||
*/
|
||||
if (!state.nodes.has(effect.into.identifier)) {
|
||||
state.create(effect.into, {kind: 'Object'});
|
||||
state.create(effect.into, {kind: 'Assign'});
|
||||
}
|
||||
state.assign(index++, effect.from, effect.into);
|
||||
} else if (effect.kind === 'Alias') {
|
||||
@@ -176,6 +177,7 @@ export function inferMutationAliasingRanges(
|
||||
effect.kind === 'MutateTransitive'
|
||||
? MutationKind.Definite
|
||||
: MutationKind.Conditional,
|
||||
reason: null,
|
||||
place: effect.value,
|
||||
});
|
||||
} else if (
|
||||
@@ -190,6 +192,7 @@ export function inferMutationAliasingRanges(
|
||||
effect.kind === 'Mutate'
|
||||
? MutationKind.Definite
|
||||
: MutationKind.Conditional,
|
||||
reason: effect.kind === 'Mutate' ? (effect.reason ?? null) : null,
|
||||
place: effect.value,
|
||||
});
|
||||
} else if (
|
||||
@@ -241,6 +244,7 @@ export function inferMutationAliasingRanges(
|
||||
mutation.transitive,
|
||||
mutation.kind,
|
||||
mutation.place.loc,
|
||||
mutation.reason,
|
||||
errors,
|
||||
);
|
||||
}
|
||||
@@ -267,6 +271,7 @@ export function inferMutationAliasingRanges(
|
||||
functionEffects.push({
|
||||
kind: 'Mutate',
|
||||
value: {...place, loc: node.local.loc},
|
||||
reason: node.mutationReason,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -469,6 +474,99 @@ 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
|
||||
@@ -476,28 +574,12 @@ 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(
|
||||
@@ -507,6 +589,7 @@ export function inferMutationAliasingRanges(
|
||||
true,
|
||||
MutationKind.Conditional,
|
||||
into.loc,
|
||||
null,
|
||||
ignoredErrors,
|
||||
);
|
||||
for (const from of tracked) {
|
||||
@@ -580,10 +663,16 @@ 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'; function: HIRFunction};
|
||||
| {
|
||||
kind: 'Function';
|
||||
effect: Extract<AliasingEffect, {kind: 'CreateFunction'}>;
|
||||
};
|
||||
};
|
||||
class AliasingState {
|
||||
nodes: Map<Identifier, Node> = new Map();
|
||||
@@ -599,12 +688,13 @@ class AliasingState {
|
||||
transitive: null,
|
||||
local: null,
|
||||
lastMutated: 0,
|
||||
mutationReason: null,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
createFrom(index: number, from: Place, into: Place): void {
|
||||
this.create(into, {kind: 'Object'});
|
||||
this.create(into, {kind: 'CreateFrom'});
|
||||
const fromNode = this.nodes.get(from.identifier);
|
||||
const toNode = this.nodes.get(into.identifier);
|
||||
if (fromNode == null || toNode == null) {
|
||||
@@ -666,7 +756,10 @@ class AliasingState {
|
||||
continue;
|
||||
}
|
||||
if (node.value.kind === 'Function') {
|
||||
appendFunctionErrors(errors, node.value.function);
|
||||
appendFunctionErrors(
|
||||
errors,
|
||||
node.value.effect.function.loweredFunc.func,
|
||||
);
|
||||
}
|
||||
for (const [alias, when] of node.createdFrom) {
|
||||
if (when >= index) {
|
||||
@@ -697,6 +790,7 @@ class AliasingState {
|
||||
transitive: boolean,
|
||||
startKind: MutationKind,
|
||||
loc: SourceLocation,
|
||||
reason: MutationReason | null,
|
||||
errors: CompilerError,
|
||||
): void {
|
||||
const seen = new Map<Identifier, MutationKind>();
|
||||
@@ -717,6 +811,7 @@ class AliasingState {
|
||||
if (node == null) {
|
||||
continue;
|
||||
}
|
||||
node.mutationReason ??= reason;
|
||||
node.lastMutated = Math.max(node.lastMutated, index);
|
||||
if (end != null) {
|
||||
node.id.mutableRange.end = makeInstructionId(
|
||||
@@ -728,7 +823,10 @@ class AliasingState {
|
||||
node.transitive == null &&
|
||||
node.local == null
|
||||
) {
|
||||
appendFunctionErrors(errors, node.value.function);
|
||||
appendFunctionErrors(
|
||||
errors,
|
||||
node.value.effect.function.loweredFunc.func,
|
||||
);
|
||||
}
|
||||
if (transitive) {
|
||||
if (node.transitive == null || node.transitive.kind < kind) {
|
||||
|
||||
@@ -777,6 +777,15 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +182,11 @@ export function parseConfigPragmaForTests(
|
||||
environment?: PartialEnvironmentConfig;
|
||||
},
|
||||
): PluginOptions {
|
||||
const overridePragma = parseConfigPragmaAsString(pragma);
|
||||
if (overridePragma !== '') {
|
||||
return parseConfigStringAsJS(overridePragma, defaults);
|
||||
}
|
||||
|
||||
const environment = parseConfigPragmaEnvironmentForTest(
|
||||
pragma,
|
||||
defaults.environment ?? {},
|
||||
@@ -217,3 +222,104 @@ 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);
|
||||
}
|
||||
|
||||
@@ -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, CompilerDiagnostic>();
|
||||
const errorsByPlace = new Map<t.SourceLocation, CompilerErrorDetail>();
|
||||
|
||||
function recordError(
|
||||
loc: SourceLocation,
|
||||
diagnostic: CompilerDiagnostic,
|
||||
errorDetail: CompilerErrorDetail,
|
||||
): void {
|
||||
if (typeof loc === 'symbol') {
|
||||
errors.pushDiagnostic(diagnostic);
|
||||
errors.pushErrorDetail(errorDetail);
|
||||
} else {
|
||||
errorsByPlace.set(loc, diagnostic);
|
||||
errorsByPlace.set(loc, errorDetail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 description =
|
||||
const reason =
|
||||
'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,19 +121,16 @@ 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 !== description) {
|
||||
if (previousError === undefined || previousError.reason !== reason) {
|
||||
recordError(
|
||||
place.loc,
|
||||
CompilerDiagnostic.create({
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Hooks,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot call hooks conditionally',
|
||||
description,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
description: null,
|
||||
reason,
|
||||
loc: place.loc,
|
||||
message: 'Cannot call hook conditionally',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -144,17 +141,14 @@ export function validateHooksUsage(
|
||||
if (previousError === undefined) {
|
||||
recordError(
|
||||
place.loc,
|
||||
CompilerDiagnostic.create({
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Hooks,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot reference hooks as regular values',
|
||||
description:
|
||||
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',
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: place.loc,
|
||||
message: 'Hooks may not be referenced as values',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -165,17 +159,14 @@ export function validateHooksUsage(
|
||||
if (previousError === undefined) {
|
||||
recordError(
|
||||
place.loc,
|
||||
CompilerDiagnostic.create({
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Hooks,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Hooks must be the same on every render',
|
||||
description:
|
||||
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',
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: place.loc,
|
||||
message: 'This value may change to a different function',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -436,8 +427,8 @@ export function validateHooksUsage(
|
||||
}
|
||||
}
|
||||
|
||||
for (const [, diagnostic] of errorsByPlace) {
|
||||
errors.pushDiagnostic(diagnostic);
|
||||
for (const [, error] of errorsByPlace) {
|
||||
errors.pushErrorDetail(error);
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
@@ -459,18 +450,15 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
|
||||
: instr.value.property;
|
||||
const hookKind = getHookKind(fn.env, callee.identifier);
|
||||
if (hookKind != null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Hooks,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot call hooks within function expressions',
|
||||
description:
|
||||
reason:
|
||||
'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)',
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: `Cannot call ${hookKind === 'Custom' ? 'hook' : hookKind} within a function expression`,
|
||||
description: `Cannot call ${hookKind === 'Custom' ? 'hook' : hookKind} within a function expression`,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,9 +24,18 @@ function useThing(fn) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Expected a non-reserved identifier name
|
||||
Error: `this` is not supported syntax
|
||||
|
||||
`this` is a reserved word in JavaScript and cannot be used as an 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 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
## 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".
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// 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]);
|
||||
}
|
||||
@@ -18,15 +18,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
5 | }
|
||||
6 | return x;
|
||||
7 | }
|
||||
|
||||
@@ -18,15 +18,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^ Cannot call hook 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)
|
||||
5 | }
|
||||
6 | return x;
|
||||
7 | }
|
||||
|
||||
@@ -25,28 +25,24 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
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: 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 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
|
||||
8 | return bar();
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
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: 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 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
|
||||
9 | }
|
||||
10 |
|
||||
11 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
function Component() {
|
||||
const f = () => () => {
|
||||
global.property = true;
|
||||
};
|
||||
f()();
|
||||
return <div>Ooops</div>;
|
||||
}
|
||||
@@ -16,14 +16,12 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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 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
|
||||
3 | const state = x(null);
|
||||
4 | return state[0];
|
||||
5 | }
|
||||
|
||||
@@ -20,15 +20,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^ Cannot call hook 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)
|
||||
7 | }
|
||||
8 | return data;
|
||||
9 | }
|
||||
|
||||
@@ -20,15 +20,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^ Cannot call hook 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)
|
||||
7 | }
|
||||
8 | return s;
|
||||
9 | }
|
||||
|
||||
@@ -20,15 +20,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
7 | }
|
||||
8 | return data;
|
||||
9 | }
|
||||
|
||||
@@ -38,6 +38,8 @@ 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".
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
## 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} />;
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
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} />;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## 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} />;
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
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} />;
|
||||
}
|
||||
@@ -14,14 +14,12 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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 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
|
||||
3 | }
|
||||
4 |
|
||||
```
|
||||
|
||||
@@ -14,14 +14,12 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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 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
|
||||
3 | }
|
||||
4 |
|
||||
```
|
||||
|
||||
@@ -15,51 +15,43 @@ function Component(props) {
|
||||
```
|
||||
Found 4 errors:
|
||||
|
||||
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: 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 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
|
||||
3 | return x();
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
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: 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 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
|
||||
3 | return x();
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
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: 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 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
|
||||
3 | return x();
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
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: 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 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
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
## 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 | };
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// @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() {}}],
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
## 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 = () => {
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// @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}],
|
||||
};
|
||||
@@ -15,27 +15,23 @@ function Component() {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
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: 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 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
|
||||
3 | return x();
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
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: 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 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
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
@@ -55,7 +55,7 @@ import { makeArray, Stringify, useIdentity } from "shared-runtime";
|
||||
*/
|
||||
function Foo(t0) {
|
||||
"use memo";
|
||||
const $ = _c(3);
|
||||
const $ = _c(5);
|
||||
const { b } = t0;
|
||||
|
||||
const fnFactory = () => () => {
|
||||
@@ -66,18 +66,26 @@ function Foo(t0) {
|
||||
useIdentity();
|
||||
|
||||
const fn = fnFactory();
|
||||
const arr = makeArray(b);
|
||||
fn(arr);
|
||||
let t1;
|
||||
if ($[0] !== arr || $[1] !== myVar) {
|
||||
t1 = <Stringify cb={myVar} value={arr} shouldInvokeFns={true} />;
|
||||
$[0] = arr;
|
||||
$[1] = myVar;
|
||||
$[2] = t1;
|
||||
if ($[0] !== b) {
|
||||
t1 = makeArray(b);
|
||||
$[0] = b;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
const arr = t1;
|
||||
fn(arr);
|
||||
let t2;
|
||||
if ($[2] !== arr || $[3] !== myVar) {
|
||||
t2 = <Stringify cb={myVar} value={arr} shouldInvokeFns={true} />;
|
||||
$[2] = arr;
|
||||
$[3] = myVar;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
function _temp2() {
|
||||
return console.log("b");
|
||||
|
||||
@@ -22,15 +22,15 @@ const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.bail.rules-of-hooks-3d692676194b.ts:8:4
|
||||
6 | const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
|
||||
7 | useEffect(() => {
|
||||
> 8 | useHookInsideCallback();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ 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)
|
||||
9 | });
|
||||
10 | return <button {...props} ref={ref} />;
|
||||
11 | });
|
||||
|
||||
@@ -22,15 +22,15 @@ const ComponentWithHookInsideCallback = React.memo(props => {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.bail.rules-of-hooks-8503ca76d6f8.ts:8:4
|
||||
6 | const ComponentWithHookInsideCallback = React.memo(props => {
|
||||
7 | useEffect(() => {
|
||||
> 8 | useHookInsideCallback();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ 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)
|
||||
9 | });
|
||||
10 | return <button {...props} />;
|
||||
11 | });
|
||||
|
||||
@@ -20,41 +20,35 @@ function Component(props) {
|
||||
```
|
||||
Found 3 errors:
|
||||
|
||||
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: 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 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
|
||||
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: 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: 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 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
|
||||
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: 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: 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 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
|
||||
9 | }
|
||||
10 |
|
||||
```
|
||||
|
||||
@@ -19,15 +19,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^ Cannot call hook 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)
|
||||
7 | }
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
@@ -16,15 +16,13 @@ function Component({cond, useFoo}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^ Cannot call hook 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)
|
||||
4 | }
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -19,15 +19,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^ Cannot call hook 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)
|
||||
7 | }
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
@@ -20,15 +20,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -16,15 +16,13 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ This value may change to a different function
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ 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
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
@@ -17,15 +17,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^ Cannot call hook 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)
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
@@ -15,14 +15,12 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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 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
|
||||
3 | return x;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
@@ -14,14 +14,12 @@ function Component({useFoo}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^ This value may change to a different function
|
||||
| ^^^^^^ 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
|
||||
3 | }
|
||||
4 |
|
||||
```
|
||||
|
||||
@@ -18,28 +18,24 @@ function Component(props) {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
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: 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);
|
||||
| ^^^^^^^ Cannot call hook 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)
|
||||
5 | }
|
||||
6 | return i;
|
||||
7 | }
|
||||
|
||||
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: 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++) {
|
||||
| ^^^^^^^ Cannot call hook 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)
|
||||
4 | i += useHook(x);
|
||||
5 | }
|
||||
6 | return i;
|
||||
|
||||
@@ -16,15 +16,13 @@ function useFoo({data}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ This value may change to a different function
|
||||
| ^^^^^^^^ 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
|
||||
4 | return foo;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -16,15 +16,13 @@ function useFoo({data}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^ This value may change to a different function
|
||||
| ^^^^^^^^^^^^^^^ 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
|
||||
4 | return foo;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -19,15 +19,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^ Cannot call hook 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)
|
||||
6 | }
|
||||
7 | return x;
|
||||
8 | }
|
||||
|
||||
@@ -18,15 +18,13 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^ Cannot call hook 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)
|
||||
5 | }
|
||||
6 | return x;
|
||||
7 | }
|
||||
|
||||
@@ -30,15 +30,15 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid-hook-in-nested-function-expression-object-expression.ts:10:21
|
||||
8 | const y = {
|
||||
9 | inner() {
|
||||
> 10 | return useFoo();
|
||||
| ^^^^^^ 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)
|
||||
11 | },
|
||||
12 | };
|
||||
13 | return y;
|
||||
|
||||
@@ -26,15 +26,15 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid-hook-in-nested-object-method.ts:8:17
|
||||
6 | const y = {
|
||||
7 | inner() {
|
||||
> 8 | return useFoo();
|
||||
| ^^^^^^ 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)
|
||||
9 | },
|
||||
10 | };
|
||||
11 | return y;
|
||||
|
||||
@@ -15,14 +15,12 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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?.() ?? {};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
3 | return result;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
@@ -15,14 +15,12 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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() ?? {};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
3 | return result;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
@@ -15,14 +15,12 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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?.() ?? {};
|
||||
| ^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
3 | return result;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
@@ -16,41 +16,35 @@ function Component(props) {
|
||||
```
|
||||
Found 3 errors:
|
||||
|
||||
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: 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 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
|
||||
4 | return y();
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
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: 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 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
|
||||
4 | return y();
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
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: 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 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
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
@@ -27,54 +27,46 @@ function useHookInLoops() {
|
||||
```
|
||||
Found 4 errors:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
8 | if (b) return;
|
||||
9 | useHook2();
|
||||
10 | }
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
10 | }
|
||||
11 | while (c) {
|
||||
12 | useHook3();
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
13 | if (d) return;
|
||||
14 | useHook4();
|
||||
15 | }
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
15 | }
|
||||
16 | }
|
||||
17 |
|
||||
|
||||
@@ -20,15 +20,13 @@ function ComponentWithConditionalHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -21,15 +21,13 @@ function useLabeledBlock() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 | }
|
||||
11 |
|
||||
|
||||
@@ -20,15 +20,13 @@ function ComponentWithHookInsideLoop() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -24,15 +24,13 @@ function useHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
13 | }
|
||||
14 |
|
||||
```
|
||||
|
||||
@@ -19,28 +19,24 @@ function useHook() {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
7 | b && useHook2();
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
@@ -18,15 +18,13 @@ function ComponentWithTernaryHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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;
|
||||
| ^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
7 | }
|
||||
8 |
|
||||
```
|
||||
|
||||
@@ -19,15 +19,13 @@ function useHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
@@ -20,15 +20,13 @@ function normalFunctionWithConditionalHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -22,28 +22,24 @@ function useHookInLoops() {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
8 | if (b) continue;
|
||||
9 | useHook2();
|
||||
10 | }
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
10 | }
|
||||
11 | }
|
||||
12 |
|
||||
|
||||
@@ -20,15 +20,13 @@ function useHookWithConditionalHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -24,15 +24,13 @@ function useHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
13 | }
|
||||
14 |
|
||||
```
|
||||
|
||||
@@ -20,41 +20,35 @@ function useHook({bar}) {
|
||||
```
|
||||
Found 3 errors:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
7 | let foo2 = bar || useState();
|
||||
8 | let foo3 = bar ?? useState();
|
||||
9 | }
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
8 | let foo3 = bar ?? useState();
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 |
|
||||
```
|
||||
|
||||
@@ -20,15 +20,15 @@ function createHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-0a1dbff27ba0.ts:6:6
|
||||
4 | return function useHookWithConditionalHook() {
|
||||
5 | if (cond) {
|
||||
> 6 | useConditionalHook();
|
||||
| ^^^^^^^^^^^^^^^^^^ 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)
|
||||
7 | }
|
||||
8 | };
|
||||
9 | }
|
||||
|
||||
@@ -20,28 +20,28 @@ function createComponent() {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-0de1224ce64b.ts:6:6
|
||||
4 | return function ComponentWithHookInsideCallback() {
|
||||
5 | useEffect(() => {
|
||||
> 6 | useHookInsideCallback();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ 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)
|
||||
7 | });
|
||||
8 | };
|
||||
9 | }
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
|
||||
Cannot call useEffect within a function expression.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-0de1224ce64b.ts:5:4
|
||||
3 | function createComponent() {
|
||||
4 | return function ComponentWithHookInsideCallback() {
|
||||
> 5 | useEffect(() => {
|
||||
| ^^^^^^^^^ 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)
|
||||
6 | useHookInsideCallback();
|
||||
7 | });
|
||||
8 | };
|
||||
|
||||
@@ -20,15 +20,15 @@ function createComponent() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-449a37146a83.ts:6:6
|
||||
4 | return function ComponentWithHookInsideCallback() {
|
||||
5 | function handleClick() {
|
||||
> 6 | useState();
|
||||
| ^^^^^^^^ 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)
|
||||
7 | }
|
||||
8 | };
|
||||
9 | }
|
||||
|
||||
@@ -18,15 +18,15 @@ function ComponentWithHookInsideCallback() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-76a74b4666e9.ts:5:4
|
||||
3 | function ComponentWithHookInsideCallback() {
|
||||
4 | function handleClick() {
|
||||
> 5 | useState();
|
||||
| ^^^^^^^^ 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)
|
||||
6 | }
|
||||
7 | }
|
||||
8 |
|
||||
|
||||
@@ -20,15 +20,15 @@ function createComponent() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-d842d36db450.ts:6:6
|
||||
4 | return function ComponentWithConditionalHook() {
|
||||
5 | if (cond) {
|
||||
> 6 | useConditionalHook();
|
||||
| ^^^^^^^^^^^^^^^^^^ 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)
|
||||
7 | }
|
||||
8 | };
|
||||
9 | }
|
||||
|
||||
@@ -18,15 +18,15 @@ function ComponentWithHookInsideCallback() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
error.invalid.invalid-rules-of-hooks-d952b82c2597.ts:5:4
|
||||
3 | function ComponentWithHookInsideCallback() {
|
||||
4 | useEffect(() => {
|
||||
> 5 | useHookInsideCallback();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ 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)
|
||||
6 | });
|
||||
7 | }
|
||||
8 |
|
||||
|
||||
@@ -22,15 +22,13 @@ const FancyButton = forwardRef(function (props, ref) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 | return <button ref={ref}>{props.children}</button>;
|
||||
11 | });
|
||||
|
||||
@@ -22,15 +22,13 @@ const MemoizedButton = memo(function (props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 | return <button>{props.children}</button>;
|
||||
11 | });
|
||||
|
||||
@@ -21,15 +21,13 @@ function ComponentWithConditionalHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 | }
|
||||
11 |
|
||||
|
||||
@@ -22,15 +22,13 @@ const FancyButton = React.forwardRef((props, ref) => {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 | return <button ref={ref}>{props.children}</button>;
|
||||
11 | });
|
||||
|
||||
@@ -21,15 +21,13 @@ React.unknownFunction((foo, bar) => {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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);
|
||||
| ^^^^^^^^^^^ Cannot call hook 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)
|
||||
9 | }
|
||||
10 | });
|
||||
11 |
|
||||
|
||||
@@ -22,15 +22,13 @@ function useHook() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
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: 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();
|
||||
| ^^^^^^^^ Cannot call hook 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)
|
||||
10 | } catch {}
|
||||
11 | }
|
||||
12 |
|
||||
|
||||
@@ -30,15 +30,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot call hooks within function expressions
|
||||
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)
|
||||
|
||||
Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
|
||||
Cannot call useEffect within a function expression.
|
||||
|
||||
error.invalid-nested-use-effect.ts:9:4
|
||||
7 | };
|
||||
8 | useEffect(() => {
|
||||
> 9 | useEffect(() => {
|
||||
| ^^^^^^^^^ 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)
|
||||
10 | function nested() {
|
||||
11 | fire(foo(props));
|
||||
12 | }
|
||||
|
||||
@@ -48,7 +48,10 @@ export {
|
||||
printReactiveFunction,
|
||||
printReactiveFunctionWithOutlined,
|
||||
} from './ReactiveScopes';
|
||||
export {parseConfigPragmaForTests} from './Utils/TestUtils';
|
||||
export {
|
||||
parseConfigPragmaForTests,
|
||||
parseConfigPragmaAsString,
|
||||
} from './Utils/TestUtils';
|
||||
declare global {
|
||||
let __DEV__: boolean | null | undefined;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,9 @@ testRule('plugin-recommended', TestRecommendedRules, {
|
||||
</>;
|
||||
}
|
||||
`,
|
||||
errors: [makeTestCaseError('Cannot call hooks conditionally')],
|
||||
errors: [
|
||||
makeTestCaseError('Hooks must always be called in a consistent order'),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Multiple diagnostics within the same file are surfaced',
|
||||
@@ -82,8 +84,8 @@ testRule('plugin-recommended', TestRecommendedRules, {
|
||||
return props.cond && useConditionalHook();
|
||||
}`,
|
||||
errors: [
|
||||
makeTestCaseError('Cannot call hooks conditionally'),
|
||||
makeTestCaseError('Cannot call hooks conditionally'),
|
||||
makeTestCaseError('Hooks must always be called in a consistent order'),
|
||||
makeTestCaseError('Hooks must always be called in a consistent order'),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -96,7 +98,9 @@ testRule('plugin-recommended', TestRecommendedRules, {
|
||||
|
||||
}
|
||||
`,
|
||||
errors: [makeTestCaseError('Cannot call hooks conditionally')],
|
||||
errors: [
|
||||
makeTestCaseError('Hooks must always be called in a consistent order'),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Multiple non-fatal useMemo diagnostics are surfaced',
|
||||
|
||||
@@ -15,7 +15,7 @@ const configs = {
|
||||
recommended: {
|
||||
plugins: {
|
||||
'react-compiler': {
|
||||
rules: recommendedRules,
|
||||
rules: allRules,
|
||||
},
|
||||
},
|
||||
rules: Object.fromEntries(
|
||||
|
||||
@@ -112,28 +112,6 @@ 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,
|
||||
|
||||
32
compiler/scripts/build-eslint-docs.js
Normal file
32
compiler/scripts/build-eslint-docs.js
Normal file
@@ -0,0 +1,32 @@
|
||||
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);
|
||||
@@ -3,7 +3,7 @@ import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
const {Fragment, useRef} = React;
|
||||
|
||||
export default function FocusCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
|
||||
@@ -2,7 +2,7 @@ import TestCase from '../../TestCase';
|
||||
import Fixture from '../../Fixture';
|
||||
|
||||
const React = window.React;
|
||||
const {Fragment, useEffect, useRef, useState} = React;
|
||||
const {Fragment, useRef, useState} = React;
|
||||
|
||||
export default function GetClientRectsCase() {
|
||||
const fragmentRef = useRef(null);
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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
Reference in New Issue
Block a user