Compare commits

..

1 Commits

Author SHA1 Message Date
Joe Savona
aa00d75433 [compiler] Script to enable a feature by default and update tests 2025-11-24 12:21:13 -08:00
161 changed files with 398 additions and 954 deletions

View File

@@ -221,7 +221,7 @@ export const EnvironmentConfigSchema = z.object({
/**
* Validate that dependencies supplied to manual memoization calls are exhaustive.
*/
validateExhaustiveMemoizationDependencies: z.boolean().default(true),
validateExhaustiveMemoizationDependencies: z.boolean().default(false),
/**
* When this is true, rather than pruning existing manual memoization but ensuring or validating

View File

@@ -803,11 +803,9 @@ export type ManualMemoDependency = {
| {
kind: 'NamedLocal';
value: Place;
constant: boolean;
}
| {kind: 'Global'; identifierName: string};
path: DependencyPath;
loc: SourceLocation;
};
export type StartMemoize = {

View File

@@ -65,7 +65,6 @@ export function collectMaybeMemoDependencies(
identifierName: value.binding.name,
},
path: [],
loc: value.loc,
};
}
case 'PropertyLoad': {
@@ -75,7 +74,6 @@ export function collectMaybeMemoDependencies(
root: object.root,
// TODO: determine if the access is optional
path: [...object.path, {property: value.property, optional}],
loc: value.loc,
};
}
break;
@@ -94,10 +92,8 @@ export function collectMaybeMemoDependencies(
root: {
kind: 'NamedLocal',
value: {...value.place},
constant: false,
},
path: [],
loc: value.place.loc,
};
}
break;

View File

@@ -609,19 +609,6 @@ function evaluateInstruction(
constantPropagationImpl(value.loweredFunc.func, constants);
return null;
}
case 'StartMemoize': {
if (value.deps != null) {
for (const dep of value.deps) {
if (dep.root.kind === 'NamedLocal') {
const placeValue = read(constants, dep.root.value);
if (placeValue != null && placeValue.kind === 'Primitive') {
dep.root.constant = true;
}
}
}
}
return null;
}
default: {
// TODO: handle more cases
return null;

View File

@@ -22,7 +22,6 @@ import {
Identifier,
IdentifierId,
InstructionKind,
isPrimitiveType,
isStableType,
isSubPath,
isSubPathIgnoringOptionals,
@@ -54,18 +53,20 @@ const DEBUG = false;
* - If the manual dependencies had extraneous deps, then auto memoization
* will remove them and cause the value to update *less* frequently.
*
* The implementation compares the manual dependencies against the values
* actually used within the memoization function
* - For each value V referenced in the memo function, either:
* - If the value is non-reactive *and* a known stable type, then the
* value may optionally be specified as an exact dependency.
* - Otherwise, report an error unless there is a manual dependency that will
* invalidate whenever V invalidates. If `x.y.z` is referenced, there must
* be a manual dependency for `x.y.z`, `x.y`, or `x`. Note that we assume
* no interior mutability, ie we assume that any changes to inner paths must
* always cause the other path to change as well.
* - Any dependencies that do not correspond to a value referenced in the memo
* function are considered extraneous and throw an error
* We consider a value V as missing if ALL of the following conditions are met:
* - V is reactive
* - There is no manual dependency path P such that whenever V would change,
* P would also change. If V is `x.y.z`, this means there must be some
* path P that is either `x.y.z`, `x.y`, or `x`. Note that we assume no
* interior mutability, such that a shorter path "covers" changes to longer
* more precise paths.
*
* We consider a value V extraneous if either of the folowing are true:
* - V is a reactive local that is unreferenced
* - V is a global that is unreferenced
*
* In other words, we allow extraneous non-reactive values since we know they cannot
* impact how often the memoization would run.
*
* ## TODO: Invalid, Complex Deps
*
@@ -225,6 +226,9 @@ export function validateExhaustiveDependencies(
reason: 'Unexpected function dependency',
loc: value.loc,
});
const isRequiredDependency = reactive.has(
inferredDependency.identifier.id,
);
let hasMatchingManualDependency = false;
for (const manualDependency of manualDependencies) {
if (
@@ -239,140 +243,81 @@ export function validateExhaustiveDependencies(
) {
hasMatchingManualDependency = true;
matched.add(manualDependency);
if (!isRequiredDependency) {
extra.push(manualDependency);
}
}
}
if (
hasMatchingManualDependency ||
isOptionalDependency(inferredDependency, reactive)
) {
continue;
if (isRequiredDependency && !hasMatchingManualDependency) {
missing.push(inferredDependency);
}
missing.push(inferredDependency);
}
for (const dep of startMemo.deps ?? []) {
if (matched.has(dep)) {
continue;
}
if (dep.root.kind === 'NamedLocal' && dep.root.constant) {
CompilerError.simpleInvariant(
!dep.root.value.reactive &&
isPrimitiveType(dep.root.value.identifier),
{
reason: 'Expected constant-folded dependency to be non-reactive',
loc: dep.root.value.loc,
},
);
/*
* Constant primitives can get constant-folded, which means we won't
* see a LoadLocal for the value within the memo function.
*/
continue;
}
extra.push(dep);
}
/**
* Per docblock, we only consider dependencies as extraneous if
* they are unused globals or reactive locals. Notably, this allows
* non-reactive locals.
*/
retainWhere(extra, dep => {
return dep.root.kind === 'Global' || dep.root.value.reactive;
});
if (missing.length !== 0 || extra.length !== 0) {
let suggestion: CompilerSuggestion | null = null;
let suggestions: Array<CompilerSuggestion> | null = null;
if (startMemo.depsLoc != null && typeof startMemo.depsLoc !== 'symbol') {
suggestion = {
description: 'Update dependencies',
range: [startMemo.depsLoc.start.index, startMemo.depsLoc.end.index],
op: CompilerSuggestionOperation.Replace,
text: `[${inferred
.filter(
dep =>
dep.kind === 'Local' && !isOptionalDependency(dep, reactive),
)
.map(printInferredDependency)
.join(', ')}]`,
};
suggestions = [
{
description: 'Update dependencies',
range: [startMemo.depsLoc.start.index, startMemo.depsLoc.end.index],
op: CompilerSuggestionOperation.Replace,
text: `[${inferred.map(printInferredDependency).join(', ')}]`,
},
];
}
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.MemoDependencies,
reason: 'Found missing/extra memoization dependencies',
description: [
missing.length !== 0
? 'Missing dependencies can cause a value to update less often than it should, ' +
'resulting in stale UI'
: null,
extra.length !== 0
? 'Extra dependencies can cause a value to update more often than it should, ' +
'resulting in performance problems such as excessive renders or effects firing too often'
: null,
]
.filter(Boolean)
.join('. '),
suggestions: suggestion != null ? [suggestion] : null,
});
for (const dep of missing) {
let reactiveStableValueHint = '';
if (isStableType(dep.identifier)) {
reactiveStableValueHint =
'. Refs, setState functions, and other "stable" values generally do not need to be added ' +
'as dependencies, but this variable may change over time to point to different values';
}
diagnostic.withDetails({
kind: 'error',
message: `Missing dependency \`${printInferredDependency(dep)}\`${reactiveStableValueHint}`,
loc: dep.loc,
if (missing.length !== 0) {
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.MemoDependencies,
reason: 'Found missing memoization dependencies',
description:
'Missing dependencies can cause a value not to update when those inputs change, ' +
'resulting in stale UI',
suggestions,
});
}
for (const dep of extra) {
if (dep.root.kind === 'Global') {
for (const dep of missing) {
let reactiveStableValueHint = '';
if (isStableType(dep.identifier)) {
reactiveStableValueHint =
'. Refs, setState functions, and other "stable" values generally do not need to be added as dependencies, but this variable may change over time to point to different values';
}
diagnostic.withDetails({
kind: 'error',
message:
`Unnecessary dependency \`${printManualMemoDependency(dep)}\`. ` +
'Values declared outside of a component/hook should not be listed as ' +
'dependencies as the component will not re-render if they change',
loc: dep.loc ?? startMemo.depsLoc ?? value.loc,
message: `Missing dependency \`${printInferredDependency(dep)}\`${reactiveStableValueHint}`,
loc: dep.loc,
});
error.pushDiagnostic(diagnostic);
} else {
const root = dep.root.value;
const matchingInferred = inferred.find(
(
inferredDep,
): inferredDep is Extract<InferredDependency, {kind: 'Local'}> => {
return (
inferredDep.kind === 'Local' &&
inferredDep.identifier.id === root.identifier.id &&
isSubPathIgnoringOptionals(inferredDep.path, dep.path)
);
},
);
if (
matchingInferred != null &&
!isOptionalDependency(matchingInferred, reactive)
) {
diagnostic.withDetails({
kind: 'error',
message:
`Overly precise dependency \`${printManualMemoDependency(dep)}\`, ` +
`use \`${printInferredDependency(matchingInferred)}\` instead`,
loc: dep.loc ?? startMemo.depsLoc ?? value.loc,
});
} else {
/**
* Else this dependency doesn't correspond to anything referenced in the memo function,
* or is an optional dependency so we don't want to suggest adding it
*/
diagnostic.withDetails({
kind: 'error',
message: `Unnecessary dependency \`${printManualMemoDependency(dep)}\``,
loc: dep.loc ?? startMemo.depsLoc ?? value.loc,
});
}
}
}
if (suggestion != null) {
diagnostic.withDetails({
kind: 'hint',
message: `Inferred dependencies: \`${suggestion.text}\``,
error.pushDiagnostic(diagnostic);
} else if (extra.length !== 0) {
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.MemoDependencies,
reason: 'Found unnecessary memoization dependencies',
description:
'Unnecessary dependencies can cause a value to update more often than necessary, ' +
'causing performance regressions and effects to fire more often than expected',
});
diagnostic.withDetails({
kind: 'error',
message: `Unnecessary dependencies ${extra.map(dep => `\`${printManualMemoDependency(dep)}\``).join(', ')}`,
loc: startMemo.depsLoc ?? value.loc,
});
error.pushDiagnostic(diagnostic);
}
error.pushDiagnostic(diagnostic);
}
dependencies.clear();
@@ -877,14 +822,3 @@ export function findOptionalPlaces(
}
return optionals;
}
function isOptionalDependency(
inferredDependency: Extract<InferredDependency, {kind: 'Local'}>,
reactive: Set<IdentifierId>,
): boolean {
return (
!reactive.has(inferredDependency.identifier.id) &&
(isStableType(inferredDependency.identifier) ||
isPrimitiveType(inferredDependency.identifier))
);
}

View File

@@ -242,7 +242,6 @@ function validateInferredDep(
normalizedDep = {
root: maybeNormalizedRoot.root,
path: [...maybeNormalizedRoot.path, ...dep.path],
loc: maybeNormalizedRoot.loc,
};
} else {
CompilerError.invariant(dep.identifier.name?.kind === 'named', {
@@ -268,10 +267,8 @@ function validateInferredDep(
effect: Effect.Read,
reactive: false,
},
constant: false,
},
path: [...dep.path],
loc: GeneratedSource,
};
}
for (const decl of declsWithinMemoBlock) {
@@ -382,10 +379,8 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
root: {
kind: 'NamedLocal',
value: storeTarget,
constant: false,
},
path: [],
loc: storeTarget.loc,
});
}
}
@@ -413,10 +408,8 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
root: {
kind: 'NamedLocal',
value: {...lvalue},
constant: false,
},
path: [],
loc: lvalue.loc,
});
}
}

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
const someGlobal = {value: 0};
@@ -33,7 +33,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
const someGlobal = { value: 0 };

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
const someGlobal = {value: 0};

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';
@@ -36,7 +36,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
import { useMemo } from "react";
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
import {makeObject_Primitives, ValidateMemoization} from 'shared-runtime';

View File

@@ -2,7 +2,6 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies:false
import {useMemo} from 'react';
function Component(props) {
@@ -31,7 +30,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime";
import { useMemo } from "react";
function Component(props) {

View File

@@ -1,4 +1,3 @@
// @validateExhaustiveMemoizationDependencies:false
import {useMemo} from 'react';
function Component(props) {

View File

@@ -11,7 +11,7 @@ function Component(props) {
Component = useMemo(() => {
return Component;
}, [Component]);
});
return <Component {...props} />;
}
@@ -36,7 +36,6 @@ function Component(props) {
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
Component = Stringify;
Component;
Component = Component;
$[0] = Component;
} else {

View File

@@ -7,7 +7,7 @@ function Component(props) {
Component = useMemo(() => {
return Component;
}, [Component]);
});
return <Component {...props} />;
}

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @eslintSuppressionRules:["my-app","react-rule"] @validateExhaustiveMemoizationDependencies:false
// @eslintSuppressionRules:["my-app","react-rule"]
/* eslint-disable my-app/react-rule */
function lowercasecomponent() {
@@ -26,7 +26,7 @@ Error: React Compiler has skipped optimizing this component because one or more
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable my-app/react-rule`.
error.bailout-on-suppression-of-custom-rule.ts:3:0
1 | // @eslintSuppressionRules:["my-app","react-rule"] @validateExhaustiveMemoizationDependencies:false
1 | // @eslintSuppressionRules:["my-app","react-rule"]
2 |
> 3 | /* eslint-disable my-app/react-rule */
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression

View File

@@ -1,4 +1,4 @@
// @eslintSuppressionRules:["my-app","react-rule"] @validateExhaustiveMemoizationDependencies:false
// @eslintSuppressionRules:["my-app","react-rule"]
/* eslint-disable my-app/react-rule */
function lowercasecomponent() {

View File

@@ -0,0 +1,109 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {Stringify} from 'shared-runtime';
function Component({x, y, z}) {
const a = useMemo(() => {
return x?.y.z?.a;
// error: too precise
}, [x?.y.z?.a.b]);
const b = useMemo(() => {
return x.y.z?.a;
// ok, not our job to type check nullability
}, [x.y.z.a]);
const c = useMemo(() => {
return x?.y.z.a?.b;
// error: too precise
}, [x?.y.z.a?.b.z]);
const d = useMemo(() => {
return x?.y?.[(console.log(y), z?.b)];
// ok
}, [x?.y, y, z?.b]);
const e = useMemo(() => {
const e = [];
e.push(x);
return e;
// ok
}, [x]);
const f = useMemo(() => {
return [];
// error: unnecessary
}, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
const ref1 = useRef(null);
const ref2 = useRef(null);
const ref = z ? ref1 : ref2;
const cb = useMemo(() => {
return () => {
return ref.current;
};
// error: ref is a stable type but reactive
}, []);
return <Stringify results={[a, b, c, d, e, f, cb]} />;
}
```
## Error
```
Found 4 errors:
Error: Found missing memoization dependencies
Missing dependencies can cause a value not to update when those inputs change, resulting in stale UI.
error.invalid-exhaustive-deps.ts:7:11
5 | function Component({x, y, z}) {
6 | const a = useMemo(() => {
> 7 | return x?.y.z?.a;
| ^^^^^^^^^ Missing dependency `x?.y.z?.a`
8 | // error: too precise
9 | }, [x?.y.z?.a.b]);
10 | const b = useMemo(() => {
Error: Found missing memoization dependencies
Missing dependencies can cause a value not to update when those inputs change, resulting in stale UI.
error.invalid-exhaustive-deps.ts:15:11
13 | }, [x.y.z.a]);
14 | const c = useMemo(() => {
> 15 | return x?.y.z.a?.b;
| ^^^^^^^^^^^ Missing dependency `x?.y.z.a?.b`
16 | // error: too precise
17 | }, [x?.y.z.a?.b.z]);
18 | const d = useMemo(() => {
Error: Found unnecessary memoization dependencies
Unnecessary dependencies can cause a value to update more often than necessary, causing performance regressions and effects to fire more often than expected.
error.invalid-exhaustive-deps.ts:31:5
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary dependencies `x`, `y.z`, `z?.y?.a`, `UNUSED_GLOBAL`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
Error: Found missing memoization dependencies
Missing dependencies can cause a value not to update when those inputs change, resulting in stale UI.
error.invalid-exhaustive-deps.ts:37:13
35 | const cb = useMemo(() => {
36 | return () => {
> 37 | return ref.current;
| ^^^ Missing dependency `ref`. Refs, setState functions, and other "stable" values generally do not need to be added as dependencies, but this variable may change over time to point to different values
38 | };
39 | // error: ref is a stable type but reactive
40 | }, []);
```

View File

@@ -2,7 +2,6 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies:false
/* eslint-disable react-hooks/rules-of-hooks */
function lowercasecomponent() {
'use forget';
@@ -24,26 +23,25 @@ Error: React Compiler has skipped optimizing this component because one or more
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`.
error.invalid-sketchy-code-use-forget.ts:2:0
1 | // @validateExhaustiveMemoizationDependencies:false
> 2 | /* eslint-disable react-hooks/rules-of-hooks */
error.invalid-sketchy-code-use-forget.ts:1:0
> 1 | /* eslint-disable react-hooks/rules-of-hooks */
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression
3 | function lowercasecomponent() {
4 | 'use forget';
5 | const x = [];
2 | function lowercasecomponent() {
3 | 'use forget';
4 | const x = [];
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line react-hooks/rules-of-hooks`.
error.invalid-sketchy-code-use-forget.ts:6:2
4 | 'use forget';
5 | const x = [];
> 6 | // eslint-disable-next-line react-hooks/rules-of-hooks
error.invalid-sketchy-code-use-forget.ts:5:2
3 | 'use forget';
4 | const x = [];
> 5 | // eslint-disable-next-line react-hooks/rules-of-hooks
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression
7 | return <div>{x}</div>;
8 | }
9 | /* eslint-enable react-hooks/rules-of-hooks */
6 | return <div>{x}</div>;
7 | }
8 | /* eslint-enable react-hooks/rules-of-hooks */
```

View File

@@ -1,4 +1,3 @@
// @validateExhaustiveMemoizationDependencies:false
/* eslint-disable react-hooks/rules-of-hooks */
function lowercasecomponent() {
'use forget';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// Note: Everything below this is sketchy @validateExhaustiveMemoizationDependencies:false
// Note: Everything below this is sketchy
/* eslint-disable react-hooks/rules-of-hooks */
function lowercasecomponent() {
'use forget';
@@ -43,7 +43,7 @@ Error: React Compiler has skipped optimizing this component because one or more
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`.
error.invalid-unclosed-eslint-suppression.ts:2:0
1 | // Note: Everything below this is sketchy @validateExhaustiveMemoizationDependencies:false
1 | // Note: Everything below this is sketchy
> 2 | /* eslint-disable react-hooks/rules-of-hooks */
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression
3 | function lowercasecomponent() {

View File

@@ -1,4 +1,4 @@
// Note: Everything below this is sketchy @validateExhaustiveMemoizationDependencies:false
// Note: Everything below this is sketchy
/* eslint-disable react-hooks/rules-of-hooks */
function lowercasecomponent() {
'use forget';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback, useRef} from 'react';
function useCustomRef() {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback, useRef} from 'react';
function useCustomRef() {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback, useRef} from 'react';
function useCustomRef() {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback, useRef} from 'react';
function useCustomRef() {

View File

@@ -0,0 +1,39 @@
## Input
```javascript
function Component() {
const item = [];
const foo = useCallback(
() => {
item.push(1);
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
return <Button foo={foo} />;
}
```
## Error
```
Found 1 error:
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line react-hooks/exhaustive-deps`.
error.sketchy-code-exhaustive-deps.ts:6:7
4 | () => {
5 | item.push(1);
> 6 | }, // eslint-disable-next-line react-hooks/exhaustive-deps
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression
7 | []
8 | );
9 |
```

View File

@@ -1,11 +1,8 @@
import {arrayPush} from 'shared-runtime';
// @validateExhaustiveMemoizationDependencies
function Component() {
const item = [];
const foo = useCallback(
() => {
arrayPush(item, 1);
item.push(1);
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[]
);

View File

@@ -2,7 +2,6 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies:false
/* eslint-disable react-hooks/rules-of-hooks */
function lowercasecomponent() {
const x = [];
@@ -28,13 +27,12 @@ Error: React Compiler has skipped optimizing this component because one or more
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`.
error.sketchy-code-rules-of-hooks.ts:2:0
1 | // @validateExhaustiveMemoizationDependencies:false
> 2 | /* eslint-disable react-hooks/rules-of-hooks */
error.sketchy-code-rules-of-hooks.ts:1:0
> 1 | /* eslint-disable react-hooks/rules-of-hooks */
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression
3 | function lowercasecomponent() {
4 | const x = [];
5 | return <div>{x}</div>;
2 | function lowercasecomponent() {
3 | const x = [];
4 | return <div>{x}</div>;
```

View File

@@ -1,4 +1,3 @@
// @validateExhaustiveMemoizationDependencies:false
/* eslint-disable react-hooks/rules-of-hooks */
function lowercasecomponent() {
const x = [];

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useFragment} from 'react-relay';
import LogEvent from 'LogEvent';
import {useCallback, useMemo} from 'react';

View File

@@ -1,4 +1,4 @@
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useFragment} from 'react-relay';
import LogEvent from 'LogEvent';
import {useCallback, useMemo} from 'react';

View File

@@ -1,40 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
function Component() {
const ref = useRef(null);
const onChange = useCallback(() => {
return ref.current.value;
}, [ref.current.value]);
return <input ref={ref} onChange={onChange} />;
}
```
## Error
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
error.invalid-dep-on-ref-current-value.ts:7:6
5 | const onChange = useCallback(() => {
6 | return ref.current.value;
> 7 | }, [ref.current.value]);
| ^^^^^^^^^^^^^^^^^ Unnecessary dependency `ref.current.value`
8 |
9 | return <input ref={ref} onChange={onChange} />;
10 | }
Inferred dependencies: `[]`
```

View File

@@ -1,10 +0,0 @@
// @validateExhaustiveMemoizationDependencies
function Component() {
const ref = useRef(null);
const onChange = useCallback(() => {
return ref.current.value;
}, [ref.current.value]);
return <input ref={ref} onChange={onChange} />;
}

View File

@@ -1,44 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
import {useState} from 'react';
import {Stringify} from 'shared-runtime';
function Component() {
const [state, setState] = useState(0);
const x = useMemo(() => {
return [state];
// error: `setState` is a stable type, but not actually referenced
}, [state, setState]);
return 'oops';
}
```
## Error
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
error.invalid-exhaustive-deps-disallow-unused-stable-types.ts:11:13
9 | return [state];
10 | // error: `setState` is a stable type, but not actually referenced
> 11 | }, [state, setState]);
| ^^^^^^^^ Unnecessary dependency `setState`
12 |
13 | return 'oops';
14 | }
Inferred dependencies: `[state]`
```

View File

@@ -1,14 +0,0 @@
// @validateExhaustiveMemoizationDependencies
import {useState} from 'react';
import {Stringify} from 'shared-runtime';
function Component() {
const [state, setState] = useState(0);
const x = useMemo(() => {
return [state];
// error: `setState` is a stable type, but not actually referenced
}, [state, setState]);
return 'oops';
}

View File

@@ -1,204 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {Stringify} from 'shared-runtime';
function Component({x, y, z}) {
const a = useMemo(() => {
return x?.y.z?.a;
// error: too precise
}, [x?.y.z?.a.b]);
const b = useMemo(() => {
return x.y.z?.a;
// ok, not our job to type check nullability
}, [x.y.z.a]);
const c = useMemo(() => {
return x?.y.z.a?.b;
// error: too precise
}, [x?.y.z.a?.b.z]);
const d = useMemo(() => {
return x?.y?.[(console.log(y), z?.b)];
// ok
}, [x?.y, y, z?.b]);
const e = useMemo(() => {
const e = [];
e.push(x);
return e;
// ok
}, [x]);
const f = useMemo(() => {
return [];
// error: unnecessary
}, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
const ref1 = useRef(null);
const ref2 = useRef(null);
const ref = z ? ref1 : ref2;
const cb = useMemo(() => {
return () => {
return ref.current;
};
// error: ref is a stable type but reactive
}, []);
return <Stringify results={[a, b, c, d, e, f, cb]} />;
}
```
## Error
```
Found 5 errors:
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI. Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
error.invalid-exhaustive-deps.ts:7:11
5 | function Component({x, y, z}) {
6 | const a = useMemo(() => {
> 7 | return x?.y.z?.a;
| ^^^^^^^^^ Missing dependency `x?.y.z?.a`
8 | // error: too precise
9 | }, [x?.y.z?.a.b]);
10 | const b = useMemo(() => {
error.invalid-exhaustive-deps.ts:9:6
7 | return x?.y.z?.a;
8 | // error: too precise
> 9 | }, [x?.y.z?.a.b]);
| ^^^^^^^^^^^ Overly precise dependency `x?.y.z?.a.b`, use `x?.y.z?.a` instead
10 | const b = useMemo(() => {
11 | return x.y.z?.a;
12 | // ok, not our job to type check nullability
Inferred dependencies: `[x?.y.z?.a]`
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI. Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
error.invalid-exhaustive-deps.ts:15:11
13 | }, [x.y.z.a]);
14 | const c = useMemo(() => {
> 15 | return x?.y.z.a?.b;
| ^^^^^^^^^^^ Missing dependency `x?.y.z.a?.b`
16 | // error: too precise
17 | }, [x?.y.z.a?.b.z]);
18 | const d = useMemo(() => {
error.invalid-exhaustive-deps.ts:17:6
15 | return x?.y.z.a?.b;
16 | // error: too precise
> 17 | }, [x?.y.z.a?.b.z]);
| ^^^^^^^^^^^^^ Overly precise dependency `x?.y.z.a?.b.z`, use `x?.y.z.a?.b` instead
18 | const d = useMemo(() => {
19 | return x?.y?.[(console.log(y), z?.b)];
20 | // ok
Inferred dependencies: `[x?.y.z.a?.b]`
Error: Found missing/extra memoization dependencies
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
error.invalid-exhaustive-deps.ts:31:6
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^ Unnecessary dependency `x`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
error.invalid-exhaustive-deps.ts:31:9
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^ Unnecessary dependency `y.z`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
error.invalid-exhaustive-deps.ts:31:14
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^^^^^ Unnecessary dependency `z?.y?.a`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
error.invalid-exhaustive-deps.ts:31:23
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^^^^^^^^^^^ Unnecessary dependency `UNUSED_GLOBAL`. Values declared outside of a component/hook should not be listed as dependencies as the component will not re-render if they change
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
Inferred dependencies: `[]`
Error: Found missing/extra memoization dependencies
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
error.invalid-exhaustive-deps.ts:31:6
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^ Unnecessary dependency `x`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
error.invalid-exhaustive-deps.ts:31:9
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^ Unnecessary dependency `y.z`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
error.invalid-exhaustive-deps.ts:31:14
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^^^^^ Unnecessary dependency `z?.y?.a`
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
error.invalid-exhaustive-deps.ts:31:23
29 | return [];
30 | // error: unnecessary
> 31 | }, [x, y.z, z?.y?.a, UNUSED_GLOBAL]);
| ^^^^^^^^^^^^^ Unnecessary dependency `UNUSED_GLOBAL`. Values declared outside of a component/hook should not be listed as dependencies as the component will not re-render if they change
32 | const ref1 = useRef(null);
33 | const ref2 = useRef(null);
34 | const ref = z ? ref1 : ref2;
Inferred dependencies: `[]`
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
error.invalid-exhaustive-deps.ts:37:13
35 | const cb = useMemo(() => {
36 | return () => {
> 37 | return ref.current;
| ^^^ Missing dependency `ref`. Refs, setState functions, and other "stable" values generally do not need to be added as dependencies, but this variable may change over time to point to different values
38 | };
39 | // error: ref is a stable type but reactive
40 | }, []);
Inferred dependencies: `[ref]`
```

View File

@@ -1,43 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {makeObject_Primitives} from 'shared-runtime';
function useHook() {
const object = makeObject_Primitives();
const fn = useCallback(() => {
const g = () => {
return [object];
};
return g;
});
return fn;
}
```
## Error
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
error.invalid-missing-nonreactive-dep-inner-function.ts:10:14
8 | const fn = useCallback(() => {
9 | const g = () => {
> 10 | return [object];
| ^^^^^^ Missing dependency `object`
11 | };
12 | return g;
13 | });
```

View File

@@ -1,15 +0,0 @@
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {makeObject_Primitives} from 'shared-runtime';
function useHook() {
const object = makeObject_Primitives();
const fn = useCallback(() => {
const g = () => {
return [object];
};
return g;
});
return fn;
}

View File

@@ -1,43 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {makeObject_Primitives, useIdentity} from 'shared-runtime';
function useHook() {
// object is non-reactive but not memoized bc the mutation surrounds a hook
const object = makeObject_Primitives();
useIdentity();
object.x = 0;
const array = useMemo(() => [object], []);
return array;
}
```
## Error
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
error.invalid-missing-nonreactive-dep-unmemoized.ts:11:31
9 | useIdentity();
10 | object.x = 0;
> 11 | const array = useMemo(() => [object], []);
| ^^^^^^ Missing dependency `object`
12 | return array;
13 | }
14 |
Inferred dependencies: `[object]`
```

View File

@@ -1,13 +0,0 @@
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {makeObject_Primitives, useIdentity} from 'shared-runtime';
function useHook() {
// object is non-reactive but not memoized bc the mutation surrounds a hook
const object = makeObject_Primitives();
useIdentity();
object.x = 0;
const array = useMemo(() => [object], []);
return array;
}

View File

@@ -1,40 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {makeObject_Primitives} from 'shared-runtime';
function useHook() {
const object = makeObject_Primitives();
const array = useMemo(() => [object], []);
return array;
}
```
## Error
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
error.invalid-missing-nonreactive-dep.ts:8:31
6 | function useHook() {
7 | const object = makeObject_Primitives();
> 8 | const array = useMemo(() => [object], []);
| ^^^^^^ Missing dependency `object`
9 | return array;
10 | }
11 |
Inferred dependencies: `[object]`
```

View File

@@ -1,10 +0,0 @@
// @validateExhaustiveMemoizationDependencies
import {useMemo} from 'react';
import {makeObject_Primitives} from 'shared-runtime';
function useHook() {
const object = makeObject_Primitives();
const array = useMemo(() => [object], []);
return array;
}

View File

@@ -1,44 +0,0 @@
## Input
```javascript
import {arrayPush} from 'shared-runtime';
// @validateExhaustiveMemoizationDependencies
function Component() {
const item = [];
const foo = useCallback(
() => {
arrayPush(item, 1);
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
return <Button foo={foo} />;
}
```
## Error
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
error.sketchy-code-exhaustive-deps.ts:8:16
6 | const foo = useCallback(
7 | () => {
> 8 | arrayPush(item, 1);
| ^^^^ Missing dependency `item`
9 | }, // eslint-disable-next-line react-hooks/exhaustive-deps
10 | []
11 | );
Inferred dependencies: `[item]`
```

View File

@@ -1,41 +0,0 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies
function Component() {
const x = 0;
const y = useMemo(() => {
return [x];
// x gets constant-folded but shouldn't count as extraneous,
// it was referenced in the memo block
}, [x]);
return y;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies
function Component() {
const $ = _c(1);
const x = 0;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [0];
$[0] = t0;
} else {
t0 = $[0];
}
const y = t0;
return y;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -1,11 +0,0 @@
// @validateExhaustiveMemoizationDependencies
function Component() {
const x = 0;
const y = useMemo(() => {
return [x];
// x gets constant-folded but shouldn't count as extraneous,
// it was referenced in the memo block
}, [x]);
return y;
}

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo, useState} from 'react';
import {ValidateMemoization} from 'shared-runtime';
@@ -29,7 +29,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c2 } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c2 } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo, useState } from "react";
import { ValidateMemoization } from "shared-runtime";

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo, useState} from 'react';
import {ValidateMemoization} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @compilationMode:"infer" @enableResetCacheOnSourceFileChanges @validateExhaustiveMemoizationDependencies:false
// @compilationMode:"infer" @enableResetCacheOnSourceFileChanges
import {useEffect, useMemo, useState} from 'react';
import {ValidateMemoization} from 'shared-runtime';
@@ -46,7 +46,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @compilationMode:"infer" @enableResetCacheOnSourceFileChanges @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @compilationMode:"infer" @enableResetCacheOnSourceFileChanges
import { useEffect, useMemo, useState } from "react";
import { ValidateMemoization } from "shared-runtime";
@@ -63,12 +63,12 @@ function unsafeUpdateConst() {
function Component() {
const $ = _c(3);
if (
$[0] !== "36c02976ff5bc474b7510128ea8220ffe31d92cd5d245148ed0a43146d18ded4"
$[0] !== "a585d27423c1181e7b6305ff909458183d284658c3c3d2e3764e1128be302fd7"
) {
for (let $i = 0; $i < 3; $i += 1) {
$[$i] = Symbol.for("react.memo_cache_sentinel");
}
$[0] = "36c02976ff5bc474b7510128ea8220ffe31d92cd5d245148ed0a43146d18ded4";
$[0] = "a585d27423c1181e7b6305ff909458183d284658c3c3d2e3764e1128be302fd7";
}
useState(_temp);

View File

@@ -1,4 +1,4 @@
// @compilationMode:"infer" @enableResetCacheOnSourceFileChanges @validateExhaustiveMemoizationDependencies:false
// @compilationMode:"infer" @enableResetCacheOnSourceFileChanges
import {useEffect, useMemo, useState} from 'react';
import {ValidateMemoization} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @dynamicGating:{"source":"shared-runtime"} @validatePreserveExistingMemoizationGuarantees @panicThreshold:"none" @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
// @dynamicGating:{"source":"shared-runtime"} @validatePreserveExistingMemoizationGuarantees @panicThreshold:"none" @loggerTestOnly
import {useMemo} from 'react';
import {identity} from 'shared-runtime';
@@ -30,7 +30,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
// @dynamicGating:{"source":"shared-runtime"} @validatePreserveExistingMemoizationGuarantees @panicThreshold:"none" @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
// @dynamicGating:{"source":"shared-runtime"} @validatePreserveExistingMemoizationGuarantees @panicThreshold:"none" @loggerTestOnly
import { useMemo } from "react";
import { identity } from "shared-runtime";
@@ -58,7 +58,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":255},"end":{"line":16,"column":1,"index":482},"filename":"dynamic-gating-bailout-nopanic.ts"},"detail":{"options":{"category":"PreserveManualMemo","reason":"Existing memoization could not be preserved","description":"React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `value`, but the source dependencies were []. Inferred dependency not present in source","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":9,"column":31,"index":337},"end":{"line":9,"column":52,"index":358},"filename":"dynamic-gating-bailout-nopanic.ts"},"message":"Could not preserve existing manual memoization"}]}}}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":206},"end":{"line":16,"column":1,"index":433},"filename":"dynamic-gating-bailout-nopanic.ts"},"detail":{"options":{"category":"PreserveManualMemo","reason":"Existing memoization could not be preserved","description":"React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `value`, but the source dependencies were []. Inferred dependency not present in source","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":9,"column":31,"index":288},"end":{"line":9,"column":52,"index":309},"filename":"dynamic-gating-bailout-nopanic.ts"},"message":"Could not preserve existing manual memoization"}]}}}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @dynamicGating:{"source":"shared-runtime"} @validatePreserveExistingMemoizationGuarantees @panicThreshold:"none" @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
// @dynamicGating:{"source":"shared-runtime"} @validatePreserveExistingMemoizationGuarantees @panicThreshold:"none" @loggerTestOnly
import {useMemo} from 'react';
import {identity} from 'shared-runtime';

View File

@@ -2,7 +2,6 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies:false
import {useState, useMemo} from 'react';
import {useIdentity} from 'shared-runtime';
@@ -37,7 +36,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime";
import { useState, useMemo } from "react";
import { useIdentity } from "shared-runtime";

View File

@@ -1,4 +1,3 @@
// @validateExhaustiveMemoizationDependencies:false
import {useState, useMemo} from 'react';
import {useIdentity} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
// @loggerTestOnly
function component(a) {
let x = useMemo(() => {
mutate(a);
@@ -15,7 +15,7 @@ function component(a) {
## Code
```javascript
// @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
// @loggerTestOnly
function component(a) {
mutate(a);
}
@@ -25,8 +25,8 @@ function component(a) {
## Logs
```
{"kind":"CompileError","detail":{"options":{"category":"VoidUseMemo","reason":"useMemo() callbacks must return a value","description":"This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":3,"column":18,"index":110},"end":{"line":5,"column":3,"index":136},"filename":"invalid-useMemo-return-empty.ts"},"message":"useMemo() callbacks must return a value"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":68},"end":{"line":7,"column":1,"index":156},"filename":"invalid-useMemo-return-empty.ts"},"fnName":"component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":1,"prunedMemoValues":0}
{"kind":"CompileError","detail":{"options":{"category":"VoidUseMemo","reason":"useMemo() callbacks must return a value","description":"This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":3,"column":18,"index":61},"end":{"line":5,"column":3,"index":87},"filename":"invalid-useMemo-return-empty.ts"},"message":"useMemo() callbacks must return a value"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":19},"end":{"line":7,"column":1,"index":107},"filename":"invalid-useMemo-return-empty.ts"},"fnName":"component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":1,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
// @loggerTestOnly
function component(a) {
let x = useMemo(() => {
mutate(a);

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {identity, ValidateMemoization} from 'shared-runtime';
@@ -33,7 +33,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import { identity, ValidateMemoization } from "shared-runtime";

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {identity, ValidateMemoization} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {identity, ValidateMemoization} from 'shared-runtime';
@@ -30,7 +30,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import { identity, ValidateMemoization } from "shared-runtime";

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {identity, ValidateMemoization} from 'shared-runtime';

View File

@@ -2,7 +2,6 @@
## Input
```javascript
// @validateExhaustiveMemoizationDependencies:false
export function useFormatRelativeTime(opts = {}) {
const {timeZone, minimal} = opts;
const format = useCallback(function formatWithUnit() {}, [minimal]);
@@ -22,7 +21,7 @@ export function useFormatRelativeTime(opts = {}) {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime";
export function useFormatRelativeTime(t0) {
const $ = _c(1);
const opts = t0 === undefined ? {} : t0;

View File

@@ -1,4 +1,3 @@
// @validateExhaustiveMemoizationDependencies:false
export function useFormatRelativeTime(opts = {}) {
const {timeZone, minimal} = opts;
const format = useCallback(function formatWithUnit() {}, [minimal]);

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
mutate,
@@ -50,7 +50,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import {
mutate,

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
mutate,

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,
@@ -43,7 +43,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import {
typedCapture,

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,
@@ -41,7 +41,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import {
typedCapture,

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,
@@ -37,7 +37,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import {
typedCapture,

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,
@@ -41,7 +41,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import {
typedCapture,

View File

@@ -1,4 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {
typedCapture,

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
@@ -36,7 +36,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import { useCallback } from "react";
import { Stringify } from "shared-runtime";

View File

@@ -1,4 +1,4 @@
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
@@ -31,7 +31,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import { useCallback } from "react";
import { Stringify } from "shared-runtime";

View File

@@ -1,4 +1,4 @@
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
function useFoo(arr1, arr2) {
@@ -27,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
function useFoo(arr1, arr2) {

View File

@@ -1,4 +1,4 @@
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false @validateExhaustiveMemoizationDependencies:false
// @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
function useFoo(arr1, arr2) {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback, useRef} from 'react';
function useFoo({cond}) {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback, useRef} from 'react';
function useFoo({cond}) {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
// This is technically a false positive, but source is already breaking
// `exhaustive-deps` lint rule (and can be considered invalid).

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
// This is technically a false positive, but source is already breaking
// `exhaustive-deps` lint rule (and can be considered invalid).

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback} from 'react';
function Component({propA}) {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useCallback} from 'react';
function Component({propA}) {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
// This is technically a false positive, but source is already breaking
// `exhaustive-deps` lint rule (and can be considered invalid).

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
// This is technically a false positive, but source is already breaking
// `exhaustive-deps` lint rule (and can be considered invalid).

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
function Component({propA}) {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
function Component({propA}) {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
function Component({propA}) {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
function Component({propA}) {

View File

@@ -32,20 +32,22 @@ function useFoo(input1) {
```
Found 1 error:
Error: Found missing/extra memoization dependencies
Compilation Skipped: Existing memoization could not be preserved
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `input1`, but the source dependencies were [y]. Inferred different dependency than source.
error.useMemo-unrelated-mutation-in-depslist.ts:18:14
16 | const memoized = useMemo(() => {
17 | return [y];
error.useMemo-unrelated-mutation-in-depslist.ts:16:27
14 | const x = {};
15 | const y = [input1];
> 16 | const memoized = useMemo(() => {
| ^^^^^^^
> 17 | return [y];
| ^^^^^^^^^^^^^^^
> 18 | }, [(mutate(x), y)]);
| ^ Missing dependency `x`
| ^^^^ Could not preserve existing manual memoization
19 |
20 | return [x, memoized];
21 | }
Inferred dependencies: `[x, y]`
```

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
function useHook(maybeRef, shouldRead) {
@@ -16,7 +16,7 @@ function useHook(maybeRef, shouldRead) {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
import { useMemo } from "react";
function useHook(maybeRef, shouldRead) {

View File

@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
function useHook(maybeRef, shouldRead) {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
// @validatePreserveExistingMemoizationGuarantees
import {useMemo} from 'react';
@@ -27,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
import { useMemo } from "react";

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