mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
fix(react-compiler): use Babel v1 validation defaults (#25676)
Match the public validation defaults from babel-plugin-react-compiler 1.0 while retaining explicit opt-in for newer validations. Validated with fixture snapshots and `just ready`. AI-assisted investigation and implementation; reviewed and understood by the contributor.
This commit is contained in:
22 files changed
+103
-31
No files matched your search
+10
@@ -0,0 +1,10 @@
|
||||
// @compilationMode:"infer" @panicThreshold:"none" @validateExhaustiveMemoizationDependencies
|
||||
import {useMemo} from 'react';
|
||||
|
||||
export function Component({status, tick}) {
|
||||
const value = useMemo(() => {
|
||||
void tick;
|
||||
return status.expiresAt;
|
||||
}, [tick, status.expiresAt]);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// @compilationMode:"infer" @panicThreshold:"none"
|
||||
import {useMemo} from 'react';
|
||||
|
||||
export function Component({status, tick}) {
|
||||
const value = useMemo(() => {
|
||||
void tick;
|
||||
return status.expiresAt;
|
||||
}, [tick, status.expiresAt]);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// @compilationMode:"infer" @panicThreshold:"none"
|
||||
import {useMemo} from 'react';
|
||||
|
||||
export function Component() {
|
||||
const value = useMemo(() => {
|
||||
console.log('computing');
|
||||
}, []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -125,6 +125,8 @@ impl Default for EnvironmentConfig {
|
||||
module_type_provider: None,
|
||||
enable_preserve_existing_memoization_guarantees: true,
|
||||
validate_preserve_existing_memoization_guarantees: true,
|
||||
// Keep public defaults compatible with babel-plugin-react-compiler 1.0;
|
||||
// current React enables this validation by default.
|
||||
validate_exhaustive_memoization_dependencies: false,
|
||||
validate_exhaustive_effect_dependencies: ExhaustiveEffectDepsMode::Off,
|
||||
enable_optional_dependencies: true,
|
||||
@@ -154,7 +156,8 @@ impl Default for EnvironmentConfig {
|
||||
enable_custom_type_definition_for_reanimated: false,
|
||||
enable_treat_ref_like_identifiers_as_refs: true,
|
||||
enable_treat_set_identifiers_as_state_setters: false,
|
||||
validate_no_void_use_memo: true,
|
||||
// Babel plugin 1.0 leaves this off; current React enables it by default.
|
||||
validate_no_void_use_memo: false,
|
||||
enable_allow_set_state_from_refs_in_effects: true,
|
||||
enable_verbose_no_set_state_in_effect: false,
|
||||
enable_forest: false,
|
||||
|
||||
-1
@@ -4,7 +4,6 @@ input_file: crates/oxc_react_compiler/fixtures/error.invalid-ReactUseMemo-async-
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 51, end: 81 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] UseMemo: useMemo() callbacks may not be async or generator functions", labels: [LabeledSpan { label: Some("Async and generator functions are not supported"), span: Span { start: 51, end: 81 }, primary: false }], help: Some("useMemo() callbacks are called once and must synchronously return a value"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] PreserveManualMemo: Existing memoization could not be preserved", labels: [LabeledSpan { label: Some("Could not preserve existing manual memoization"), span: Span { start: 51, end: 81 }, primary: false }], help: Some("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 `a`, but the source dependencies were []. Inferred dependency not present in source"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
|
||||
-1
@@ -4,7 +4,6 @@ input_file: crates/oxc_react_compiler/fixtures/error.invalid-conditional-setStat
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 137, end: 214 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] RenderSetState: Calling setState from useMemo may trigger an infinite loop", labels: [LabeledSpan { label: Some("Found setState() within useMemo()"), span: Span { start: 167, end: 178 }, primary: false }], help: Some("Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] RenderSetState: Calling setState from useMemo may trigger an infinite loop", labels: [LabeledSpan { label: Some("Found setState() within useMemo()"), span: Span { start: 192, end: 200 }, primary: false }], help: Some("Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/error.invalid-extra-memo-dependency-validation-enabled.tsx
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] MemoDependencies: Found extra memoization dependencies", labels: [LabeledSpan { label: Some("Unnecessary dependency `tick`"), span: Span { start: 252, end: 256 }, primary: false }], help: Some("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"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
// @compilationMode:"infer" @panicThreshold:"none" @validateExhaustiveMemoizationDependencies
|
||||
import { useMemo } from "react";
|
||||
export function Component({ status, tick }) {
|
||||
const value = useMemo(() => {
|
||||
void tick;
|
||||
return status.expiresAt;
|
||||
}, [tick, status.expiresAt]);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
-1
@@ -4,7 +4,6 @@ input_file: crates/oxc_react_compiler/fixtures/error.invalid-setState-in-useMemo
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 257, end: 278 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] RenderSetState: Calling setState from useMemo may trigger an infinite loop", labels: [LabeledSpan { label: Some("Found setState() within useMemo()"), span: Span { start: 269, end: 271 }, primary: false }], help: Some("Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
No changes.
|
||||
@@ -4,7 +4,6 @@ input_file: crates/oxc_react_compiler/fixtures/error.invalid-setState-in-useMemo
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 140, end: 192 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] RenderSetState: Calling setState from useMemo may trigger an infinite loop", labels: [LabeledSpan { label: Some("Found setState() within useMemo()"), span: Span { start: 152, end: 162 }, primary: false }], help: Some("Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] RenderSetState: Calling setState from useMemo may trigger an infinite loop", labels: [LabeledSpan { label: Some("Found setState() within useMemo()"), span: Span { start: 173, end: 181 }, primary: false }], help: Some("Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ input_file: crates/oxc_react_compiler/fixtures/error.invalid-useMemo-async-callb
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 45, end: 75 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] UseMemo: useMemo() callbacks may not be async or generator functions", labels: [LabeledSpan { label: Some("Async and generator functions are not supported"), span: Span { start: 45, end: 75 }, primary: false }], help: Some("useMemo() callbacks are called once and must synchronously return a value"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] PreserveManualMemo: Existing memoization could not be preserved", labels: [LabeledSpan { label: Some("Could not preserve existing manual memoization"), span: Span { start: 45, end: 75 }, primary: false }], help: Some("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 `a`, but the source dependencies were []. Inferred dependency not present in source"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ input_file: crates/oxc_react_compiler/fixtures/error.useMemo-callback-generator.
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 206, end: 237 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] Todo: (BuildHIR::lowerExpression) Handle YieldExpression expressions", labels: [LabeledSpan { label: None, span: Span { start: 225, end: 232 }, primary: false }], help: None, note: None, severity: Warning, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] UseMemo: useMemo() callbacks may not be async or generator functions", labels: [LabeledSpan { label: Some("Async and generator functions are not supported"), span: Span { start: 206, end: 237 }, primary: false }], help: Some("useMemo() callbacks are called once and must synchronously return a value"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/exhaustive-deps/exhaustive-deps.js
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() result is unused", labels: [LabeledSpan { label: Some("useMemo() result is unused"), span: Span { start: 272, end: 279 }, primary: false }], help: Some("This useMemo() value is unused. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
// @validateExhaustiveMemoizationDependencies
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/invalid-useMemo-return-empty.js
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 110, end: 136 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
// @loggerTestOnly @validateExhaustiveMemoizationDependencies:false
|
||||
function component(a) {
|
||||
mutate(a);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/memo-extra-dependency-babel-v1-default.tsx
|
||||
---
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
// @compilationMode:"infer" @panicThreshold:"none"
|
||||
import { useMemo } from "react";
|
||||
export function Component(t0) {
|
||||
const $ = _c(2);
|
||||
const { status, tick } = t0;
|
||||
const value = status.expiresAt;
|
||||
let t1;
|
||||
if ($[0] !== value) {
|
||||
t1 = <div>{value}</div>;
|
||||
$[0] = value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
-4
@@ -2,10 +2,6 @@
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/preserve-memo-validation/maybe-invalid-useMemo-no-memoblock-sideeffect.ts
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() callbacks must return a value", labels: [LabeledSpan { label: Some("useMemo() callbacks must return a value"), span: Span { start: 363, end: 440 }, primary: false }], help: Some("This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
// This is currently considered valid because we don't ensure that every
|
||||
|
||||
-4
@@ -2,10 +2,6 @@
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/preserve-memo-validation/prune-nonescaping-useMemo-mult-returns-primitive.ts
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() result is unused", labels: [LabeledSpan { label: Some("useMemo() result is unused"), span: Span { start: 150, end: 157 }, primary: false }], help: Some("This useMemo() value is unused. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
-4
@@ -2,10 +2,6 @@
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/preserve-memo-validation/prune-nonescaping-useMemo-mult-returns.ts
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() result is unused", labels: [LabeledSpan { label: Some("useMemo() result is unused"), span: Span { start: 150, end: 157 }, primary: false }], help: Some("This useMemo() value is unused. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
-4
@@ -2,10 +2,6 @@
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/preserve-memo-validation/prune-nonescaping-useMemo.ts
|
||||
---
|
||||
Diagnostics:
|
||||
|
||||
OxcDiagnostic { inner: OxcDiagnosticInner { message: "[ReactCompiler] VoidUseMemo: useMemo() result is unused", labels: [LabeledSpan { label: Some("useMemo() result is unused"), span: Span { start: 283, end: 290 }, primary: false }], help: Some("This useMemo() value is unused. useMemo() is for computing and caching values, not for arbitrary side effects"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }
|
||||
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
source: crates/oxc_react_compiler/tests/snapshot.rs
|
||||
input_file: crates/oxc_react_compiler/fixtures/useMemo-void-babel-v1-default.tsx
|
||||
---
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
// @compilationMode:"infer" @panicThreshold:"none"
|
||||
import { useMemo } from "react";
|
||||
export function Component() {
|
||||
const $ = _c(1);
|
||||
console.log("computing");
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>{undefined}</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
Vendored
+5
@@ -153,6 +153,11 @@ export interface ReactCompilerEnvironmentOptions {
|
||||
enableCustomTypeDefinitionForReanimated?: boolean
|
||||
enableTreatRefLikeIdentifiersAsRefs?: boolean
|
||||
enableTreatSetIdentifiersAsStateSetters?: boolean
|
||||
/**
|
||||
* Validate that `useMemo` callbacks return a value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
validateNoVoidUseMemo?: boolean
|
||||
enableAllowSetStateFromRefsInEffects?: boolean
|
||||
enableVerboseNoSetStateInEffect?: boolean
|
||||
|
||||
@@ -259,6 +259,9 @@ pub struct ReactCompilerEnvironmentOptions {
|
||||
pub enable_custom_type_definition_for_reanimated: Option<bool>,
|
||||
pub enable_treat_ref_like_identifiers_as_refs: Option<bool>,
|
||||
pub enable_treat_set_identifiers_as_state_setters: Option<bool>,
|
||||
/// Validate that `useMemo` callbacks return a value.
|
||||
///
|
||||
/// @default false
|
||||
pub validate_no_void_use_memo: Option<bool>,
|
||||
pub enable_allow_set_state_from_refs_in_effects: Option<bool>,
|
||||
pub enable_verbose_no_set_state_in_effect: Option<bool>,
|
||||
|
||||
@@ -153,6 +153,11 @@ export interface ReactCompilerEnvironmentOptions {
|
||||
enableCustomTypeDefinitionForReanimated?: boolean
|
||||
enableTreatRefLikeIdentifiersAsRefs?: boolean
|
||||
enableTreatSetIdentifiersAsStateSetters?: boolean
|
||||
/**
|
||||
* Validate that `useMemo` callbacks return a value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
validateNoVoidUseMemo?: boolean
|
||||
enableAllowSetStateFromRefsInEffects?: boolean
|
||||
enableVerboseNoSetStateInEffect?: boolean
|
||||
|
||||
Reference in new issue
Block a user