diff --git a/compiler/apps/playground/components/Editor/ConfigEditor.tsx b/compiler/apps/playground/components/Editor/ConfigEditor.tsx index 18f904d225..cf80b48dbf 100644 --- a/compiler/apps/playground/components/Editor/ConfigEditor.tsx +++ b/compiler/apps/playground/components/Editor/ConfigEditor.tsx @@ -14,6 +14,7 @@ import React, { unstable_ViewTransition as ViewTransition, unstable_addTransitionType as addTransitionType, startTransition, + Activity, } from 'react'; import {Resizable} from 're-resizable'; import {useStore, useStoreDispatch} from '../StoreContext'; @@ -34,12 +35,8 @@ export default function ConfigEditor({ const [isExpanded, setIsExpanded] = useState(false); return ( - // TODO: Use when it is compatible with Monaco: https://github.com/suren-atoyan/monaco-react/issues/753 <> -
+ { startTransition(() => { @@ -49,11 +46,8 @@ export default function ConfigEditor({ }} formattedAppliedConfig={formattedAppliedConfig} /> -
-
+ + { startTransition(() => { @@ -62,7 +56,7 @@ export default function ConfigEditor({ }); }} /> -
+
); } @@ -122,7 +116,8 @@ function ExpandedEditor({ return ( + enter={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}} + exit={{[CONFIG_PANEL_TRANSITION]: 'slide-out', default: 'none'}}> ' + ' '.repeat(pipeIndex - 2) + '…', + ...lines.slice( + -(CODEFRAME_LINES_BELOW + CODEFRAME_ABBREVIATED_SOURCE_LINES), + ), + ].join('\n'); } function printErrorSummary(category: ErrorCategory, message: string): string { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 0ae338f5c7..f6872da111 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -1568,20 +1568,6 @@ function lowerObjectPropertyKey( name: key.node.value, }; } else if (property.node.computed && key.isExpression()) { - if (!key.isIdentifier() && !key.isMemberExpression()) { - /* - * NOTE: allowing complex key expressions can trigger a bug where a mutation is made conditional - * see fixture - * error.object-expression-computed-key-modified-during-after-construction.js - */ - builder.errors.push({ - reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`, - category: ErrorCategory.Todo, - loc: key.node.loc ?? null, - suggestions: null, - }); - return null; - } const place = lowerExpressionToTemporary(builder, key); return { kind: 'computed', diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md index 7913666aa3..bd3d513a8f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional-optional.expect.md @@ -35,18 +35,7 @@ error.hoist-optional-member-expression-with-conditional-optional.ts:4:23 3 | function Component(props) { > 4 | const data = useMemo(() => { | ^^^^^^^ -> 5 | const x = []; - | ^^^^^^^^^^^^^^^^^ -> 6 | x.push(props?.items); - | ^^^^^^^^^^^^^^^^^ -> 7 | if (props.cond) { - | ^^^^^^^^^^^^^^^^^ -> 8 | x.push(props?.items); - | ^^^^^^^^^^^^^^^^^ -> 9 | } - | ^^^^^^^^^^^^^^^^^ -> 10 | return x; - | ^^^^^^^^^^^^^^^^^ +> … > 11 | }, [props?.items, props.cond]); | ^^^^ Could not preserve existing manual memoization 12 | return ( diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md index b60a911875..5a512f9c7e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoist-optional-member-expression-with-conditional.expect.md @@ -35,18 +35,7 @@ error.hoist-optional-member-expression-with-conditional.ts:4:23 3 | function Component(props) { > 4 | const data = useMemo(() => { | ^^^^^^^ -> 5 | const x = []; - | ^^^^^^^^^^^^^^^^^ -> 6 | x.push(props?.items); - | ^^^^^^^^^^^^^^^^^ -> 7 | if (props.cond) { - | ^^^^^^^^^^^^^^^^^ -> 8 | x.push(props.items); - | ^^^^^^^^^^^^^^^^^ -> 9 | } - | ^^^^^^^^^^^^^^^^^ -> 10 | return x; - | ^^^^^^^^^^^^^^^^^ +> … > 11 | }, [props?.items, props.cond]); | ^^^^ Could not preserve existing manual memoization 12 | return ( diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-optional-member-expression-as-memo-dep-non-optional-in-body.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-optional-member-expression-as-memo-dep-non-optional-in-body.expect.md index fa5cc6d53f..697a786f49 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-optional-member-expression-as-memo-dep-non-optional-in-body.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-optional-member-expression-as-memo-dep-non-optional-in-body.expect.md @@ -29,12 +29,7 @@ error.invalid-optional-member-expression-as-memo-dep-non-optional-in-body.ts:3:2 2 | function Component(props) { > 3 | const data = useMemo(() => { | ^^^^^^^ -> 4 | // actual code is non-optional - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 5 | return props.items.edges.nodes ?? []; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 6 | // deps are optional - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> … > 7 | }, [props.items?.edges?.nodes]); | ^^^^ Could not preserve existing manual memoization 8 | return ; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md index 8592ae65e4..ee69f200ac 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md @@ -57,35 +57,7 @@ This argument is a function which may reassign or mutate `cache` after render, w 20 | ): TInput => TOutput { > 21 | return useMemo(() => { | ^^^^^^^^^^^^^^^ -> 22 | // The original issue is that `cache` was not memoized together with the returned - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 23 | // function. This was because neither appears to ever be mutated — the function - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 24 | // is known to mutate `cache` but the function isn't called. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 25 | // - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 26 | // The fix is to detect cases like this — functions that are mutable but not called - - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 27 | // and ensure that their mutable captures are aliased together into the same scope. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 28 | const cache = new WeakMap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 29 | return input => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 30 | let output = cache.get(input); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 31 | if (output == null) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 32 | output = map(input); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 33 | cache.set(input, output); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 34 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 35 | return output; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 36 | }; +> … | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 37 | }, [map]); | ^^^^^^^^^^^^ This function may (indirectly) reassign or modify `cache` after render diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-preserve-memoization-inner-destructured-value-mistaken-as-dependency-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-preserve-memoization-inner-destructured-value-mistaken-as-dependency-later-mutation.expect.md index 8d603c629b..32b062d891 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-preserve-memoization-inner-destructured-value-mistaken-as-dependency-later-mutation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-preserve-memoization-inner-destructured-value-mistaken-as-dependency-later-mutation.expect.md @@ -45,10 +45,7 @@ error.repro-preserve-memoization-inner-destructured-value-mistaken-as-dependency 18 | function useInputValue(input) { > 19 | const object = React.useMemo(() => { | ^^^^^^^^^^^^^^^^^^^^^ -> 20 | const {value} = transform(input); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 21 | return {value}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> … > 22 | }, [input]); | ^^^^^^^^^^^^^^ Could not preserve existing memoization 23 | mutate(object); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md index e5bcf704d1..775306e348 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md @@ -40,18 +40,7 @@ error.todo-for-in-loop-with-context-variable-iterator.ts:8:2 7 | // within a closure, the `onClick` handler of each item > 8 | for (let key in props.data) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 9 | key = key ?? null; // no-op reassignment to force a context variable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 10 | items.push( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 11 |
data.set(key)}> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 12 | {key} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 13 |
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 14 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> … > 15 | } | ^^^^ Support non-trivial for..in inits 16 | return
{items}
; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md index 800822b674..f487025bcb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md @@ -40,18 +40,7 @@ error.todo-for-of-loop-with-context-variable-iterator.ts:8:2 7 | // within a closure, the `onClick` handler of each item > 8 | for (let item of props.data) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 9 | item = item ?? {}; // reassignment to force a context variable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 10 | items.push( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 11 |
data.set(item)}> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 12 | {item.id} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 13 |
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 14 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> … > 15 | } | ^^^^ Support non-trivial for..of inits 16 | return
{items}
; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md index d82575b8c3..e6bb7274e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md @@ -35,22 +35,7 @@ error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.ts:6:2 5 | let el; > 6 | try { | ^^^^^ -> 7 | let value; - | ^^^^^^^^^^^^^^ -> 8 | try { - | ^^^^^^^^^^^^^^ -> 9 | value = identity(props.foo); - | ^^^^^^^^^^^^^^ -> 10 | } catch { - | ^^^^^^^^^^^^^^ -> 11 | el =
; - | ^^^^^^^^^^^^^^ -> 12 | } - | ^^^^^^^^^^^^^^ -> 13 | } finally { - | ^^^^^^^^^^^^^^ -> 14 | console.log(el); - | ^^^^^^^^^^^^^^ +> … > 15 | } | ^^^^ (BuildHIR::lowerStatement) Handle TryStatement without a catch clause 16 | return el; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md index e8a2920564..7b4307bdf2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md @@ -28,12 +28,7 @@ error.todo-invalid-jsx-in-try-with-finally.ts:4:2 3 | let el; > 4 | try { | ^^^^^ -> 5 | el =
; - | ^^^^^^^^^^^^^^^^^ -> 6 | } finally { - | ^^^^^^^^^^^^^^^^^ -> 7 | console.log(el); - | ^^^^^^^^^^^^^^^^^ +> … > 8 | } | ^^^^ (BuildHIR::lowerStatement) Handle TryStatement without a catch clause 9 | return el; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md index 32db5b2e7c..f11fa57689 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md @@ -101,14 +101,7 @@ error.todo-kitchensink.ts:5:2 4 | > 5 | class Bar { | ^^^^^^^^^^^ -> 6 | #secretSauce = 42; - | ^^^^^^^^^^^^^^^^^^^^^^ -> 7 | constructor() { - | ^^^^^^^^^^^^^^^^^^^^^^ -> 8 | console.log(this.#secretSauce); - | ^^^^^^^^^^^^^^^^^^^^^^ -> 9 | } - | ^^^^^^^^^^^^^^^^^^^^^^ +> … > 10 | } | ^^^^ Inline `class` declarations are not supported 11 | diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md deleted file mode 100644 index bebdd9dcc9..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md +++ /dev/null @@ -1,41 +0,0 @@ - -## Input - -```javascript -import {identity, mutate, mutateAndReturn} from 'shared-runtime'; - -function Component(props) { - const key = {}; - const context = { - [(mutate(key), key)]: identity([props.value]), - }; - mutate(key); - return context; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{value: 42}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: (BuildHIR::lowerExpression) Expected Identifier, got SequenceExpression key in ObjectExpression - -error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.ts:6:6 - 4 | const key = {}; - 5 | const context = { -> 6 | [(mutate(key), key)]: identity([props.value]), - | ^^^^^^^^^^^^^^^^ (BuildHIR::lowerExpression) Expected Identifier, got SequenceExpression key in ObjectExpression - 7 | }; - 8 | mutate(key); - 9 | return context; -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.expect.md deleted file mode 100644 index 215e5200a9..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.expect.md +++ /dev/null @@ -1,41 +0,0 @@ - -## Input - -```javascript -import {identity, mutate, mutateAndReturn} from 'shared-runtime'; - -function Component(props) { - const key = {}; - const context = { - [mutateAndReturn(key)]: identity([props.value]), - }; - mutate(key); - return context; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{value: 42}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression - -error.todo-object-expression-computed-key-modified-during-after-construction.ts:6:5 - 4 | const key = {}; - 5 | const context = { -> 6 | [mutateAndReturn(key)]: identity([props.value]), - | ^^^^^^^^^^^^^^^^^^^^ (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression - 7 | }; - 8 | mutate(key); - 9 | return context; -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.expect.md deleted file mode 100644 index abe4153e22..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.expect.md +++ /dev/null @@ -1,40 +0,0 @@ - -## Input - -```javascript -import {identity, mutate, mutateAndReturn} from 'shared-runtime'; - -function Component(props) { - const key = {}; - const context = { - [mutateAndReturn(key)]: identity([props.value]), - }; - return context; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{value: 42}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression - -error.todo-object-expression-computed-key-mutate-key-while-constructing-object.ts:6:5 - 4 | const key = {}; - 5 | const context = { -> 6 | [mutateAndReturn(key)]: identity([props.value]), - | ^^^^^^^^^^^^^^^^^^^^ (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression - 7 | }; - 8 | return context; - 9 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.expect.md deleted file mode 100644 index 560f40b991..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.expect.md +++ /dev/null @@ -1,42 +0,0 @@ - -## Input - -```javascript -import {identity, mutate, mutateAndReturn} from 'shared-runtime'; - -function Component(props) { - const obj = {mutateAndReturn}; - const key = {}; - const context = { - [obj.mutateAndReturn(key)]: identity([props.value]), - }; - mutate(key); - return context; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{value: 42}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression - -error.todo-object-expression-member-expr-call.ts:7:5 - 5 | const key = {}; - 6 | const context = { -> 7 | [obj.mutateAndReturn(key)]: identity([props.value]), - | ^^^^^^^^^^^^^^^^^^^^^^^^ (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression - 8 | }; - 9 | mutate(key); - 10 | return context; -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md index e9772e6799..9b61883725 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md @@ -61,26 +61,7 @@ error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.ts:7:25 6 | function Component({x}) { > 7 | const object = useMemo(() => { | ^^^^^^^ -> 8 | return identity({ - | ^^^^^^^^^^^^^^^^^^^^^ -> 9 | callback: () => { - | ^^^^^^^^^^^^^^^^^^^^^ -> 10 | // This is a bug in our dependency inference: we stop capturing dependencies - | ^^^^^^^^^^^^^^^^^^^^^ -> 11 | // after x.a.b?.c. But what this dependency is telling us is that if `x.a.b` - | ^^^^^^^^^^^^^^^^^^^^^ -> 12 | // was non-nullish, then we can access `.c.d?.e`. Thus we should take the - | ^^^^^^^^^^^^^^^^^^^^^ -> 13 | // full property chain, exactly as-is with optionals/non-optionals, as a - | ^^^^^^^^^^^^^^^^^^^^^ -> 14 | // dependency - | ^^^^^^^^^^^^^^^^^^^^^ -> 15 | return identity(x.a.b?.c.d?.e); - | ^^^^^^^^^^^^^^^^^^^^^ -> 16 | }, - | ^^^^^^^^^^^^^^^^^^^^^ -> 17 | }); - | ^^^^^^^^^^^^^^^^^^^^^ +> … > 18 | }, [x.a.b?.c.d?.e]); | ^^^^ Could not preserve existing manual memoization 19 | const result = useMemo(() => { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-missed-memoization-from-inferred-mutation-in-logger.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-missed-memoization-from-inferred-mutation-in-logger.expect.md index be31341d15..e356b92e08 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-missed-memoization-from-inferred-mutation-in-logger.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-missed-memoization-from-inferred-mutation-in-logger.expect.md @@ -62,14 +62,7 @@ React Compiler has skipped optimizing this component because the existing manual 10 | > 11 | const logData = useMemo(() => { | ^^^^^^^^^^^^^^^ -> 12 | const item = items[index]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 13 | return { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 14 | key: item.key, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 15 | }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> … > 16 | }, [index, items]); | ^^^^^^^^^^^^^^^^^^^^^ Could not preserve existing memoization 17 | @@ -96,20 +89,7 @@ React Compiler has skipped optimizing this component because the existing manual 18 | const setCurrentIndex = useCallback( > 19 | (index: number) => { | ^^^^^^^^^^^^^^^^^^^^ -> 20 | const object = { - | ^^^^^^^^^^^^^^^^^^^^^^ -> 21 | tracking: logData.key, - | ^^^^^^^^^^^^^^^^^^^^^^ -> 22 | }; - | ^^^^^^^^^^^^^^^^^^^^^^ -> 23 | // We infer that this may mutate `object`, which in turn aliases - | ^^^^^^^^^^^^^^^^^^^^^^ -> 24 | // data from `logData`, such that `logData` may be mutated. - | ^^^^^^^^^^^^^^^^^^^^^^ -> 25 | LogEvent.log(() => object); - | ^^^^^^^^^^^^^^^^^^^^^^ -> 26 | setIndex(index); - | ^^^^^^^^^^^^^^^^^^^^^^ +> … > 27 | }, | ^^^^^^ Could not preserve existing memoization 28 | [index, logData, items] diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md index 38d10ee0d1..d080efff9b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md @@ -43,24 +43,7 @@ error.todo-syntax.ts:11:2 10 | 'use memo'; > 11 | useSpecialEffect( | ^^^^^^^^^^^^^^^^^ -> 12 | () => { - | ^^^^^^^^^^^ -> 13 | try { - | ^^^^^^^^^^^ -> 14 | console.log(prop1); - | ^^^^^^^^^^^ -> 15 | } finally { - | ^^^^^^^^^^^ -> 16 | console.log('exiting'); - | ^^^^^^^^^^^ -> 17 | } - | ^^^^^^^^^^^ -> 18 | }, - | ^^^^^^^^^^^ -> 19 | [prop1], - | ^^^^^^^^^^^ -> 20 | AUTODEPS - | ^^^^^^^^^^^ +> … > 21 | ); | ^^^^ Cannot infer dependencies 22 | return
{prop1}
; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md index 0a5cde5cd6..f18cc75143 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/error.wrong-index.expect.md @@ -33,15 +33,7 @@ error.wrong-index.ts:6:2 5 | function Component({foo}) { > 6 | useEffectWrapper( | ^^^^^^^^^^^^^^^^^ -> 7 | () => { - | ^^^^^^^^^^^ -> 8 | console.log(foo); - | ^^^^^^^^^^^ -> 9 | }, - | ^^^^^^^^^^^ -> 10 | [foo], - | ^^^^^^^^^^^ -> 11 | AUTODEPS +> … | ^^^^^^^^^^^ > 12 | ); | ^^^^ Cannot infer dependencies diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md new file mode 100644 index 0000000000..d46db9c8de --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md @@ -0,0 +1,56 @@ + +## Input + +```javascript +import {identity, mutate, mutateAndReturn} from 'shared-runtime'; + +function Component(props) { + const key = {}; + const context = { + [(mutate(key), key)]: identity([props.value]), + }; + mutate(key); + return [context, key]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 42}], + sequentialRenders: [{value: 42}, {value: 42}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { identity, mutate, mutateAndReturn } from "shared-runtime"; + +function Component(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.value) { + const key = {}; + const context = { [(mutate(key), key)]: identity([props.value]) }; + mutate(key); + t0 = [context, key]; + $[0] = props.value; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], + sequentialRenders: [{ value: 42 }, { value: 42 }], +}; + +``` + +### Eval output +(kind: ok) [{"[object Object]":[42]},{"wat0":"joe","wat1":"joe"}] +[{"[object Object]":[42]},{"wat0":"joe","wat1":"joe"}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction-sequence-expr.js similarity index 79% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction-sequence-expr.js index 59437a9ffd..183c03cf91 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction-sequence-expr.js @@ -6,10 +6,11 @@ function Component(props) { [(mutate(key), key)]: identity([props.value]), }; mutate(key); - return context; + return [context, key]; } export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{value: 42}], + sequentialRenders: [{value: 42}, {value: 42}], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction.expect.md new file mode 100644 index 0000000000..1d671aa36c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction.expect.md @@ -0,0 +1,55 @@ + +## Input + +```javascript +import {identity, mutate, mutateAndReturn} from 'shared-runtime'; + +function Component(props) { + const key = {}; + const context = { + [mutateAndReturn(key)]: identity([props.value]), + }; + mutate(key); + return context; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 42}], + sequentialRenders: [{value: 42}, {value: 42}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { identity, mutate, mutateAndReturn } from "shared-runtime"; + +function Component(props) { + const $ = _c(2); + let context; + if ($[0] !== props.value) { + const key = {}; + context = { [mutateAndReturn(key)]: identity([props.value]) }; + mutate(key); + $[0] = props.value; + $[1] = context; + } else { + context = $[1]; + } + return context; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], + sequentialRenders: [{ value: 42 }, { value: 42 }], +}; + +``` + +### Eval output +(kind: ok) {"[object Object]":[42]} +{"[object Object]":[42]} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction.js similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction.js index 86d9cd7fd9..0176850e98 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-modified-during-after-construction.js @@ -12,4 +12,5 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{value: 42}], + sequentialRenders: [{value: 42}, {value: 42}], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-mutate-key-while-constructing-object.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-mutate-key-while-constructing-object.expect.md new file mode 100644 index 0000000000..4c4b45967e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-mutate-key-while-constructing-object.expect.md @@ -0,0 +1,67 @@ + +## Input + +```javascript +import {identity, mutate, mutateAndReturn} from 'shared-runtime'; + +function Component(props) { + const key = {}; + const context = { + [mutateAndReturn(key)]: identity([props.value]), + }; + return context; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 42}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { identity, mutate, mutateAndReturn } from "shared-runtime"; + +function Component(props) { + const $ = _c(5); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const key = {}; + + t0 = mutateAndReturn(key); + $[0] = t0; + } else { + t0 = $[0]; + } + let t1; + if ($[1] !== props.value) { + t1 = identity([props.value]); + $[1] = props.value; + $[2] = t1; + } else { + t1 = $[2]; + } + let t2; + if ($[3] !== t1) { + t2 = { [t0]: t1 }; + $[3] = t1; + $[4] = t2; + } else { + t2 = $[4]; + } + const context = t2; + return context; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], +}; + +``` + +### Eval output +(kind: ok) {"[object Object]":[42]} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-mutate-key-while-constructing-object.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-key-mutate-key-while-constructing-object.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-member-expr-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-member-expr-call.expect.md new file mode 100644 index 0000000000..ac6cb97b08 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-member-expr-call.expect.md @@ -0,0 +1,54 @@ + +## Input + +```javascript +import {identity, mutate, mutateAndReturn} from 'shared-runtime'; + +function Component(props) { + const obj = {mutateAndReturn}; + const key = {}; + const context = { + [obj.mutateAndReturn(key)]: identity([props.value]), + }; + mutate(key); + return context; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 42}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { identity, mutate, mutateAndReturn } from "shared-runtime"; + +function Component(props) { + const $ = _c(2); + let context; + if ($[0] !== props.value) { + const obj = { mutateAndReturn }; + const key = {}; + context = { [obj.mutateAndReturn(key)]: identity([props.value]) }; + mutate(key); + $[0] = props.value; + $[1] = context; + } else { + context = $[1]; + } + return context; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], +}; + +``` + +### Eval output +(kind: ok) {"[object Object]":[42]} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-member-expr-call.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-member-expr-call.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md index ed2e61d8ee..26eff22ed7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-conditional-access-own-scope.expect.md @@ -37,16 +37,7 @@ error.hoist-useCallback-conditional-access-own-scope.ts:5:21 4 | function Component({propA, propB}) { > 5 | return useCallback(() => { | ^^^^^^^ -> 6 | if (propA) { - | ^^^^^^^^^^^^^^^^ -> 7 | return { - | ^^^^^^^^^^^^^^^^ -> 8 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^ -> 9 | }; - | ^^^^^^^^^^^^^^^^ -> 10 | } - | ^^^^^^^^^^^^^^^^ +> … > 11 | }, [propA, propB.x.y]); | ^^^^ Could not preserve existing manual memoization 12 | } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md index a16ef317b0..440478ee07 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.hoist-useCallback-infer-conditional-value-block.expect.md @@ -40,20 +40,7 @@ error.hoist-useCallback-infer-conditional-value-block.ts:6:21 5 | function useHook(propA, propB) { > 6 | return useCallback(() => { | ^^^^^^^ -> 7 | const x = {}; - | ^^^^^^^^^^^^^^^^^ -> 8 | if (identity(null) ?? propA.a) { - | ^^^^^^^^^^^^^^^^^ -> 9 | mutate(x); - | ^^^^^^^^^^^^^^^^^ -> 10 | return { - | ^^^^^^^^^^^^^^^^^ -> 11 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^^ -> 12 | }; - | ^^^^^^^^^^^^^^^^^ -> 13 | } - | ^^^^^^^^^^^^^^^^^ +> … > 14 | }, [propA.a, propB.x.y]); | ^^^^ Could not preserve existing manual memoization 15 | } @@ -69,20 +56,7 @@ error.hoist-useCallback-infer-conditional-value-block.ts:6:21 5 | function useHook(propA, propB) { > 6 | return useCallback(() => { | ^^^^^^^ -> 7 | const x = {}; - | ^^^^^^^^^^^^^^^^^ -> 8 | if (identity(null) ?? propA.a) { - | ^^^^^^^^^^^^^^^^^ -> 9 | mutate(x); - | ^^^^^^^^^^^^^^^^^ -> 10 | return { - | ^^^^^^^^^^^^^^^^^ -> 11 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^^ -> 12 | }; - | ^^^^^^^^^^^^^^^^^ -> 13 | } - | ^^^^^^^^^^^^^^^^^ +> … > 14 | }, [propA.a, propB.x.y]); | ^^^^ Could not preserve existing manual memoization 15 | } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.preserve-use-memo-ref-missing-reactive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.preserve-use-memo-ref-missing-reactive.expect.md index 3eb8e6cb26..4f1597cdf7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.preserve-use-memo-ref-missing-reactive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.preserve-use-memo-ref-missing-reactive.expect.md @@ -39,12 +39,7 @@ error.preserve-use-memo-ref-missing-reactive.ts:9:21 8 | > 9 | return useCallback(() => { | ^^^^^^^ -> 10 | if (ref != null) { - | ^^^^^^^^^^^^^^^^^^^^^^ -> 11 | ref.current(); - | ^^^^^^^^^^^^^^^^^^^^^^ -> 12 | } - | ^^^^^^^^^^^^^^^^^^^^^^ +> … > 13 | }, []); | ^^^^ Could not preserve existing manual memoization 14 | } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-conditional-access-noAlloc.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-conditional-access-noAlloc.expect.md index 075458831b..07d37fb156 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-conditional-access-noAlloc.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-conditional-access-noAlloc.expect.md @@ -36,14 +36,7 @@ error.useCallback-conditional-access-noAlloc.ts:5:21 4 | function Component({propA, propB}) { > 5 | return useCallback(() => { | ^^^^^^^ -> 6 | return { - | ^^^^^^^^^^^^ -> 7 | value: propB?.x.y, - | ^^^^^^^^^^^^ -> 8 | other: propA, - | ^^^^^^^^^^^^ -> 9 | }; - | ^^^^^^^^^^^^ +> … > 10 | }, [propA, propB.x.y]); | ^^^^ Could not preserve existing manual memoization 11 | } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md index 077b9aa9f6..0d81b0cf7a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useCallback-infer-less-specific-conditional-access.expect.md @@ -35,19 +35,7 @@ error.useCallback-infer-less-specific-conditional-access.ts:6:21 5 | function Component({propA, propB}) { > 6 | return useCallback(() => { | ^^^^^^^ -> 7 | const x = {}; - | ^^^^^^^^^^^^^^^^^ -> 8 | if (propA?.a) { - | ^^^^^^^^^^^^^^^^^ -> 9 | mutate(x); - | ^^^^^^^^^^^^^^^^^ -> 10 | return { - | ^^^^^^^^^^^^^^^^^ -> 11 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^^ -> 12 | }; - | ^^^^^^^^^^^^^^^^^ -> 13 | } +> … | ^^^^^^^^^^^^^^^^^ > 14 | }, [propA?.a, propB.x.y]); | ^^^^ Could not preserve existing manual memoization diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-access.expect.md index d93c52a10c..5cb50ad525 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-access.expect.md @@ -35,19 +35,7 @@ error.useMemo-infer-less-specific-conditional-access.ts:6:17 5 | function Component({propA, propB}) { > 6 | return useMemo(() => { | ^^^^^^^ -> 7 | const x = {}; - | ^^^^^^^^^^^^^^^^^ -> 8 | if (propA?.a) { - | ^^^^^^^^^^^^^^^^^ -> 9 | mutate(x); - | ^^^^^^^^^^^^^^^^^ -> 10 | return { - | ^^^^^^^^^^^^^^^^^ -> 11 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^^ -> 12 | }; - | ^^^^^^^^^^^^^^^^^ -> 13 | } +> … | ^^^^^^^^^^^^^^^^^ > 14 | }, [propA?.a, propB.x.y]); | ^^^^ Could not preserve existing manual memoization diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-value-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-value-block.expect.md index 9d35f52504..943f64720a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-value-block.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-infer-less-specific-conditional-value-block.expect.md @@ -35,19 +35,7 @@ error.useMemo-infer-less-specific-conditional-value-block.ts:6:17 5 | function Component({propA, propB}) { > 6 | return useMemo(() => { | ^^^^^^^ -> 7 | const x = {}; - | ^^^^^^^^^^^^^^^^^ -> 8 | if (identity(null) ?? propA.a) { - | ^^^^^^^^^^^^^^^^^ -> 9 | mutate(x); - | ^^^^^^^^^^^^^^^^^ -> 10 | return { - | ^^^^^^^^^^^^^^^^^ -> 11 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^^ -> 12 | }; - | ^^^^^^^^^^^^^^^^^ -> 13 | } +> … | ^^^^^^^^^^^^^^^^^ > 14 | }, [propA.a, propB.x.y]); | ^^^^ Could not preserve existing manual memoization @@ -63,19 +51,7 @@ error.useMemo-infer-less-specific-conditional-value-block.ts:6:17 5 | function Component({propA, propB}) { > 6 | return useMemo(() => { | ^^^^^^^ -> 7 | const x = {}; - | ^^^^^^^^^^^^^^^^^ -> 8 | if (identity(null) ?? propA.a) { - | ^^^^^^^^^^^^^^^^^ -> 9 | mutate(x); - | ^^^^^^^^^^^^^^^^^ -> 10 | return { - | ^^^^^^^^^^^^^^^^^ -> 11 | value: propB.x.y, - | ^^^^^^^^^^^^^^^^^ -> 12 | }; - | ^^^^^^^^^^^^^^^^^ -> 13 | } +> … | ^^^^^^^^^^^^^^^^^ > 14 | }, [propA.a, propB.x.y]); | ^^^^ Could not preserve existing manual memoization diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-property-call-chained-object.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-property-call-chained-object.expect.md index 00cc3fb839..fe8bbd64b0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-property-call-chained-object.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-property-call-chained-object.expect.md @@ -30,11 +30,7 @@ error.useMemo-property-call-chained-object.ts:5:17 4 | function Component({propA}) { > 5 | return useMemo(() => { | ^^^^^^^ -> 6 | return { - | ^^^^^^^^^^^^ -> 7 | value: propA.x().y, - | ^^^^^^^^^^^^ -> 8 | }; +> … | ^^^^^^^^^^^^ > 9 | }, [propA.x]); | ^^^^ Could not preserve existing manual memoization diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional-optional.expect.md index 14ea4e7593..adf726b370 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional-optional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional-optional.expect.md @@ -35,18 +35,7 @@ error.todo-optional-member-expression-with-conditional-optional.ts:4:23 3 | function Component(props) { > 4 | const data = useMemo(() => { | ^^^^^^^ -> 5 | const x = []; - | ^^^^^^^^^^^^^^^^^ -> 6 | x.push(props?.items); - | ^^^^^^^^^^^^^^^^^ -> 7 | if (props.cond) { - | ^^^^^^^^^^^^^^^^^ -> 8 | x.push(props?.items); - | ^^^^^^^^^^^^^^^^^ -> 9 | } - | ^^^^^^^^^^^^^^^^^ -> 10 | return x; - | ^^^^^^^^^^^^^^^^^ +> … > 11 | }, [props?.items, props.cond]); | ^^^^ Could not preserve existing manual memoization 12 | return ( diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional.expect.md index f3fdb07697..dc2e11fd78 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-member-expression-with-conditional.expect.md @@ -35,18 +35,7 @@ error.todo-optional-member-expression-with-conditional.ts:4:23 3 | function Component(props) { > 4 | const data = useMemo(() => { | ^^^^^^^ -> 5 | const x = []; - | ^^^^^^^^^^^^^^^^^ -> 6 | x.push(props?.items); - | ^^^^^^^^^^^^^^^^^ -> 7 | if (props.cond) { - | ^^^^^^^^^^^^^^^^^ -> 8 | x.push(props.items); - | ^^^^^^^^^^^^^^^^^ -> 9 | } - | ^^^^^^^^^^^^^^^^^ -> 10 | return x; - | ^^^^^^^^^^^^^^^^^ +> … > 11 | }, [props?.items, props.cond]); | ^^^^ Could not preserve existing manual memoization 12 | return ( diff --git a/compiler/packages/babel-plugin-react-compiler/src/index.ts b/compiler/packages/babel-plugin-react-compiler/src/index.ts index d2abd744d6..ca5b653430 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/index.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/index.ts @@ -29,10 +29,13 @@ export { ProgramContext, tryFindDirectiveEnablingMemoization as findDirectiveEnablingMemoization, findDirectiveDisablingMemoization, + defaultOptions, type CompilerPipelineValue, type Logger, type LoggerEvent, type PluginOptions, + type AutoDepsDecorationsEvent, + type CompileSuccessEvent, } from './Entrypoint'; export { Effect, diff --git a/compiler/packages/react-forgive/package.json b/compiler/packages/react-forgive/package.json index 0bf48e232e..fc7d71095c 100644 --- a/compiler/packages/react-forgive/package.json +++ b/compiler/packages/react-forgive/package.json @@ -36,13 +36,14 @@ }, "scripts": { "build": "yarn run compile", + "build:compiler": "yarn workspace babel-plugin-react-compiler build --dts", "compile": "rimraf dist && concurrently -n server,client \"scripts/build.mjs -t server\" \"scripts/build.mjs -t client\"", "dev": "yarn run package && yarn run install-ext", "install-ext": "code --install-extension react-forgive-0.0.0.vsix", "lint": "echo 'no tests'", "package": "rm -f react-forgive-0.0.0.vsix && vsce package --yarn", "postinstall": "cd client && yarn install && cd ../server && yarn install && cd ..", - "pretest": "yarn run compile && yarn run lint", + "pretest": "yarn run build:compiler && yarn run compile && yarn run lint", "test": "vscode-test", "vscode:prepublish": "yarn run compile", "watch": "scripts/build.mjs --watch" diff --git a/compiler/packages/react-forgive/server/package.json b/compiler/packages/react-forgive/server/package.json index fb6f4feebd..c912586072 100644 --- a/compiler/packages/react-forgive/server/package.json +++ b/compiler/packages/react-forgive/server/package.json @@ -18,7 +18,6 @@ "@babel/parser": "^7.26.0", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/types": "^7.26.0", - "cosmiconfig": "^9.0.0", "prettier": "^3.3.3", "vscode-languageserver": "^9.0.1", "vscode-languageserver-textdocument": "^1.0.12" diff --git a/compiler/packages/react-forgive/server/src/compiler/compat.ts b/compiler/packages/react-forgive/server/src/compiler/compat.ts index 10271cbdcd..f467f70ba1 100644 --- a/compiler/packages/react-forgive/server/src/compiler/compat.ts +++ b/compiler/packages/react-forgive/server/src/compiler/compat.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {SourceLocation} from 'babel-plugin-react-compiler/src'; +import {type SourceLocation} from 'babel-plugin-react-compiler'; import {type Range} from 'vscode-languageserver'; export function babelLocationToRange(loc: SourceLocation): Range | null { diff --git a/compiler/packages/react-forgive/server/src/compiler/index.ts b/compiler/packages/react-forgive/server/src/compiler/index.ts index fe192c6213..b474253f71 100644 --- a/compiler/packages/react-forgive/server/src/compiler/index.ts +++ b/compiler/packages/react-forgive/server/src/compiler/index.ts @@ -9,7 +9,7 @@ import type * as BabelCore from '@babel/core'; import {parseAsync, transformFromAstAsync} from '@babel/core'; import BabelPluginReactCompiler, { type PluginOptions, -} from 'babel-plugin-react-compiler/src'; +} from 'babel-plugin-react-compiler'; import * as babelParser from 'prettier/plugins/babel.js'; import estreeParser from 'prettier/plugins/estree'; import * as typescriptParser from 'prettier/plugins/typescript'; diff --git a/compiler/packages/react-forgive/server/src/compiler/options.ts b/compiler/packages/react-forgive/server/src/compiler/options.ts deleted file mode 100644 index 226be799d3..0000000000 --- a/compiler/packages/react-forgive/server/src/compiler/options.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { - parsePluginOptions, - type PluginOptions, -} from 'babel-plugin-react-compiler/src'; -import {cosmiconfigSync} from 'cosmiconfig'; - -export function resolveReactConfig(projectPath: string): PluginOptions | null { - const explorerSync = cosmiconfigSync('react', { - searchStrategy: 'project', - cache: true, - }); - const result = explorerSync.search(projectPath); - if (result != null) { - return parsePluginOptions(result.config); - } else { - return null; - } -} diff --git a/compiler/packages/react-forgive/server/src/index.ts b/compiler/packages/react-forgive/server/src/index.ts index fd4ffd9988..7552337cbc 100644 --- a/compiler/packages/react-forgive/server/src/index.ts +++ b/compiler/packages/react-forgive/server/src/index.ts @@ -20,13 +20,12 @@ import { TextDocumentSyncKind, } from 'vscode-languageserver/node'; import {compile, lastResult} from './compiler'; -import {type PluginOptions} from 'babel-plugin-react-compiler/src'; -import {resolveReactConfig} from './compiler/options'; import { type CompileSuccessEvent, type LoggerEvent, + type PluginOptions, defaultOptions, -} from 'babel-plugin-react-compiler/src/Entrypoint/Options'; +} from 'babel-plugin-react-compiler'; import {babelLocationToRange, getRangeFirstCharacter} from './compiler/compat'; import { type AutoDepsDecorationsLSPEvent, @@ -64,8 +63,7 @@ type CodeActionLSPEvent = { }; connection.onInitialize((_params: InitializeParams) => { - // TODO(@poteto) get config fr - compilerOptions = resolveReactConfig('.') ?? defaultOptions; + compilerOptions = defaultOptions; compilerOptions = { ...compilerOptions, environment: { @@ -76,21 +74,21 @@ connection.onInitialize((_params: InitializeParams) => { importSpecifierName: 'useEffect', source: 'react', }, - numRequiredArgs: 1, + autodepsIndex: 1, }, { function: { importSpecifierName: 'useSpecialEffect', source: 'shared-runtime', }, - numRequiredArgs: 2, + autodepsIndex: 2, }, { function: { importSpecifierName: 'default', source: 'useEffectWrapper', }, - numRequiredArgs: 1, + autodepsIndex: 1, }, ], }, diff --git a/compiler/packages/react-forgive/server/src/requests/autodepsdecorations.ts b/compiler/packages/react-forgive/server/src/requests/autodepsdecorations.ts index 77a568662e..c9c9e8ca01 100644 --- a/compiler/packages/react-forgive/server/src/requests/autodepsdecorations.ts +++ b/compiler/packages/react-forgive/server/src/requests/autodepsdecorations.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {type AutoDepsDecorationsEvent} from 'babel-plugin-react-compiler/src/Entrypoint'; +import {type AutoDepsDecorationsEvent} from 'babel-plugin-react-compiler'; import {type Position} from 'vscode-languageserver-textdocument'; import {RequestType} from 'vscode-languageserver/node'; import {type Range, sourceLocationToRange} from '../utils/range'; diff --git a/compiler/packages/react-forgive/server/yarn.lock b/compiler/packages/react-forgive/server/yarn.lock index b72063294f..ee346ce7d3 100644 --- a/compiler/packages/react-forgive/server/yarn.lock +++ b/compiler/packages/react-forgive/server/yarn.lock @@ -10,7 +10,7 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -188,11 +188,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - browserslist@^4.24.0: version "4.24.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.3.tgz#5fc2725ca8fb3c1432e13dac278c7cc103e026d2" @@ -203,11 +198,6 @@ browserslist@^4.24.0: node-releases "^2.0.19" update-browserslist-db "^1.1.1" -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - caniuse-lite@^1.0.30001688: version "1.0.30001690" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz#f2d15e3aaf8e18f76b2b8c1481abde063b8104c8" @@ -218,16 +208,6 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cosmiconfig@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" - integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== - dependencies: - env-paths "^2.2.1" - import-fresh "^3.3.0" - js-yaml "^4.1.0" - parse-json "^5.2.0" - debug@^4.1.0, debug@^4.3.1: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" @@ -240,18 +220,6 @@ electron-to-chromium@^1.5.73: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz#cb886b504a6467e4c00bea3317edb38393c53413" integrity sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw== -env-paths@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -267,51 +235,21 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -import-fresh@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -329,23 +267,6 @@ node-releases@^2.0.19: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - picocolors@^1.0.0, picocolors@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -356,11 +277,6 @@ prettier@^3.3.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" diff --git a/packages/eslint-plugin-react-hooks/npm/index.d.ts b/packages/eslint-plugin-react-hooks/npm/index.d.ts index c883d6f8ad..7516dc1e84 100644 --- a/packages/eslint-plugin-react-hooks/npm/index.d.ts +++ b/packages/eslint-plugin-react-hooks/npm/index.d.ts @@ -5,4 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -export {default} from './cjs/eslint-plugin-react-hooks'; +import reactHooks from './cjs/eslint-plugin-react-hooks'; + +export = reactHooks; diff --git a/packages/eslint-plugin-react-hooks/npm/index.js b/packages/eslint-plugin-react-hooks/npm/index.js index b819fc7023..3e8352af1e 100644 --- a/packages/eslint-plugin-react-hooks/npm/index.js +++ b/packages/eslint-plugin-react-hooks/npm/index.js @@ -14,3 +14,13 @@ if (process.env.NODE_ENV === 'production') { } else { module.exports = require('./cjs/eslint-plugin-react-hooks.development.js'); } + +// Hint to Node’s cjs-module-lexer to make named imports work +// https://github.com/facebook/react/issues/34801#issuecomment-3433478810 +// eslint-disable-next-line ft-flow/no-unused-expressions +0 && + (module.exports = { + meta: true, + rules: true, + configs: true, + }); diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts index 6d72c1daa5..924299d898 100644 --- a/packages/eslint-plugin-react-hooks/src/index.ts +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -71,7 +71,10 @@ const configs = { plugins, rules: recommendedLatestRuleConfigs, }, - flat: {} as Record, + flat: {} as { + recommended: ReactHooksFlatConfig; + 'recommended-latest': ReactHooksFlatConfig; + }, }; const plugin = { diff --git a/packages/internal-test-utils/debugInfo.js b/packages/internal-test-utils/debugInfo.js index 7b9c730ba6..75183ce4b6 100644 --- a/packages/internal-test-utils/debugInfo.js +++ b/packages/internal-test-utils/debugInfo.js @@ -64,7 +64,7 @@ function normalizeIOInfo(config: DebugInfoConfig, ioInfo) { if (promise) { promise.then(); // init if (promise.status === 'fulfilled') { - if (ioInfo.name === 'RSC stream') { + if (ioInfo.name === 'rsc stream') { copy.byteSize = 0; copy.value = { value: 'stream', @@ -117,7 +117,7 @@ export function getDebugInfo(config: DebugInfoConfig, obj) { for (let i = 0; i < debugInfo.length; i++) { if ( debugInfo[i].awaited && - debugInfo[i].awaited.name === 'RSC stream' && + debugInfo[i].awaited.name === 'rsc stream' && config.ignoreRscStreamInfo ) { // Ignore RSC stream I/O info. diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 1c2151362d..c7506a13e5 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -2561,6 +2561,7 @@ function ResponseInstance( findSourceMapURL: void | FindSourceMapURLCallback, // DEV-only replayConsole: boolean, // DEV-only environmentName: void | string, // DEV-only + debugStartTime: void | number, // DEV-only debugChannel: void | DebugChannel, // DEV-only ) { const chunks: Map> = new Map(); @@ -2621,7 +2622,8 @@ function ResponseInstance( // Note: createFromFetch allows this to be marked at the start of the fetch // where as if you use createFromReadableStream from the body of the fetch // then the start time is when the headers resolved. - this._debugStartTime = performance.now(); + this._debugStartTime = + debugStartTime == null ? performance.now() : debugStartTime; this._debugIOStarted = false; // We consider everything before the first setTimeout task to be cached data // and is not considered I/O required to load the stream. @@ -2669,6 +2671,7 @@ export function createResponse( findSourceMapURL: void | FindSourceMapURLCallback, // DEV-only replayConsole: boolean, // DEV-only environmentName: void | string, // DEV-only + debugStartTime: void | number, // DEV-only debugChannel: void | DebugChannel, // DEV-only ): WeakResponse { return getWeakResponse( @@ -2684,6 +2687,7 @@ export function createResponse( findSourceMapURL, replayConsole, environmentName, + debugStartTime, debugChannel, ), ); @@ -2717,7 +2721,7 @@ export function createStreamState( (debugValuePromise: any).status = 'fulfilled'; (debugValuePromise: any).value = streamDebugValue; streamState._debugInfo = { - name: 'RSC stream', + name: 'rsc stream', start: response._debugStartTime, end: response._debugStartTime, // will be updated once we finish a chunk byteSize: 0, // will be updated as we resolve a data chunk diff --git a/packages/react-devtools-core/package.json b/packages/react-devtools-core/package.json index c8facd1a24..54b89e72e8 100644 --- a/packages/react-devtools-core/package.json +++ b/packages/react-devtools-core/package.json @@ -1,6 +1,6 @@ { "name": "react-devtools-core", - "version": "7.0.0", + "version": "7.0.1", "description": "Use react-devtools outside of the browser", "license": "MIT", "main": "./dist/backend.js", diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index 0b5285e91a..9bb0ab1177 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -2,8 +2,8 @@ "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Chrome Developer Tools.", - "version": "7.0.0", - "version_name": "7.0.0", + "version": "7.0.1", + "version_name": "7.0.1", "minimum_chrome_version": "114", "icons": { "16": "icons/16-production.png", diff --git a/packages/react-devtools-extensions/edge/manifest.json b/packages/react-devtools-extensions/edge/manifest.json index 342af4abb3..55b7248f25 100644 --- a/packages/react-devtools-extensions/edge/manifest.json +++ b/packages/react-devtools-extensions/edge/manifest.json @@ -2,8 +2,8 @@ "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Microsoft Edge Developer Tools.", - "version": "7.0.0", - "version_name": "7.0.0", + "version": "7.0.1", + "version_name": "7.0.1", "minimum_chrome_version": "114", "icons": { "16": "icons/16-production.png", diff --git a/packages/react-devtools-extensions/firefox/manifest.json b/packages/react-devtools-extensions/firefox/manifest.json index bc12ac0fcd..f401708c21 100644 --- a/packages/react-devtools-extensions/firefox/manifest.json +++ b/packages/react-devtools-extensions/firefox/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Firefox Developer Tools.", - "version": "7.0.0", + "version": "7.0.1", "browser_specific_settings": { "gecko": { "id": "@react-devtools", diff --git a/packages/react-devtools-extensions/src/main/cloneStyleTags.js b/packages/react-devtools-extensions/src/main/cloneStyleTags.js index dd84e01fc9..caad2a361c 100644 --- a/packages/react-devtools-extensions/src/main/cloneStyleTags.js +++ b/packages/react-devtools-extensions/src/main/cloneStyleTags.js @@ -1,5 +1,14 @@ -function cloneStyleTags() { - const linkTags = []; +/** + * 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. + * + * @flow + */ + +export function cloneStyleTags(): Array { + const tags: Array = []; // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (const linkTag of document.getElementsByTagName('link')) { @@ -11,11 +20,23 @@ function cloneStyleTags() { newLinkTag.setAttribute(attribute.nodeName, attribute.nodeValue); } - linkTags.push(newLinkTag); + tags.push(newLinkTag); } } - return linkTags; -} + // eslint-disable-next-line no-for-of-loops/no-for-of-loops + for (const styleTag of document.getElementsByTagName('style')) { + const newStyleTag = document.createElement('style'); -export default cloneStyleTags; + // eslint-disable-next-line no-for-of-loops/no-for-of-loops + for (const attribute of styleTag.attributes) { + newStyleTag.setAttribute(attribute.nodeName, attribute.nodeValue); + } + + newStyleTag.textContent = styleTag.textContent; + + tags.push(newStyleTag); + } + + return tags; +} diff --git a/packages/react-devtools-extensions/src/main/index.js b/packages/react-devtools-extensions/src/main/index.js index cec9fd6df4..bc948d1e6d 100644 --- a/packages/react-devtools-extensions/src/main/index.js +++ b/packages/react-devtools-extensions/src/main/index.js @@ -33,7 +33,7 @@ import { import {viewAttributeSource} from './sourceSelection'; import {startReactPolling} from './reactPolling'; -import cloneStyleTags from './cloneStyleTags'; +import {cloneStyleTags} from './cloneStyleTags'; import fetchFileWithCaching from './fetchFileWithCaching'; import injectBackendManager from './injectBackendManager'; import registerEventsLogger from './registerEventsLogger'; diff --git a/packages/react-devtools-extensions/utils.js b/packages/react-devtools-extensions/utils.js index 3b06bb71f7..9f02e98d19 100644 --- a/packages/react-devtools-extensions/utils.js +++ b/packages/react-devtools-extensions/utils.js @@ -6,7 +6,7 @@ */ const {execSync} = require('child_process'); -const {readFileSync} = require('fs'); +const {existsSync, readFileSync} = require('fs'); const {resolve} = require('path'); const GITHUB_URL = 'https://github.com/facebook/react'; @@ -18,8 +18,26 @@ function getGitCommit() { .trim(); } catch (error) { // Mozilla runs this command from a git archive. - // In that context, there is no Git revision. - return null; + // In that context, there is no Git context. + // Using the commit hash specified to download-experimental-build.js script as a fallback. + + // Try to read from build/COMMIT_SHA file + const commitShaPath = resolve(__dirname, '..', '..', 'build', 'COMMIT_SHA'); + if (!existsSync(commitShaPath)) { + throw new Error( + 'Could not find build/COMMIT_SHA file. Did you run scripts/release/download-experimental-build.js script?', + ); + } + + try { + const commitHash = readFileSync(commitShaPath, 'utf8').trim(); + // Return short hash (first 7 characters) to match abbreviated commit hash format + return commitHash.slice(0, 7); + } catch (readError) { + throw new Error( + `Failed to read build/COMMIT_SHA file: ${readError.message}`, + ); + } } } diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index 4592363c64..191eabc47c 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -285,7 +285,7 @@ module.exports = { { loader: 'css-loader', options: { - sourceMap: true, + sourceMap: __DEV__, modules: true, localIdentName: '[local]___[hash:base64:5]', }, diff --git a/packages/react-devtools-inline/package.json b/packages/react-devtools-inline/package.json index 0363530cd1..cc5fc51a6e 100644 --- a/packages/react-devtools-inline/package.json +++ b/packages/react-devtools-inline/package.json @@ -1,6 +1,6 @@ { "name": "react-devtools-inline", - "version": "7.0.0", + "version": "7.0.1", "description": "Embed react-devtools within a website", "license": "MIT", "main": "./dist/backend.js", diff --git a/packages/react-devtools-shared/package.json b/packages/react-devtools-shared/package.json index 543ac37e97..721cc9954b 100644 --- a/packages/react-devtools-shared/package.json +++ b/packages/react-devtools-shared/package.json @@ -23,6 +23,7 @@ "json5": "^2.2.3", "local-storage-fallback": "^4.1.1", "react-virtualized-auto-sizer": "^1.0.23", - "react-window": "^1.8.10" + "react-window": "^1.8.10", + "rbush": "4.0.1" } } diff --git a/packages/react-devtools-shared/src/__tests__/store-test.js b/packages/react-devtools-shared/src/__tests__/store-test.js index c02d8130c3..07e8204049 100644 --- a/packages/react-devtools-shared/src/__tests__/store-test.js +++ b/packages/react-devtools-shared/src/__tests__/store-test.js @@ -3243,4 +3243,61 @@ describe('Store', () => { `); }); + + // @reactVersion >= 19.0 + it('guesses a Suspense name based on the owner', async () => { + let resolve; + const promise = new Promise(_resolve => { + resolve = _resolve; + }); + function Inner() { + return ( + Loading inner

}> +

{promise}

+
+ ); + } + + function Outer({children}) { + return ( + Loading outer

}> +

{promise}

+ {children} +
+ ); + } + + await actAsync(() => { + render( + + + , + ); + }); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ + + [suspense-root] rects={[{x:1,y:2,width:13,height:1}]} + + `); + + console.log('...........................'); + + await actAsync(() => { + resolve('loaded'); + }); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ + ▾ + ▾ + + [suspense-root] rects={[{x:1,y:2,width:6,height:1}, {x:1,y:2,width:6,height:1}]} + + + `); + }); }); diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index de178909b2..9de9037690 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -1913,6 +1913,20 @@ export function attach( return false; } + function isUseSyncExternalStoreHook(hookObject: any): boolean { + const queue = hookObject.queue; + if (!queue) { + return false; + } + + const boundHasOwnProperty = hasOwnProperty.bind(queue); + return ( + boundHasOwnProperty('value') && + boundHasOwnProperty('getSnapshot') && + typeof queue.getSnapshot === 'function' + ); + } + function isHookThatCanScheduleUpdate(hookObject: any) { const queue = hookObject.queue; if (!queue) { @@ -1929,12 +1943,7 @@ export function attach( return true; } - // Detect useSyncExternalStore() - return ( - boundHasOwnProperty('value') && - boundHasOwnProperty('getSnapshot') && - typeof queue.getSnapshot === 'function' - ); + return isUseSyncExternalStoreHook(hookObject); } function didStatefulHookChange(prev: any, next: any): boolean { @@ -1955,10 +1964,18 @@ export function attach( const indices = []; let index = 0; + while (next !== null) { if (didStatefulHookChange(prev, next)) { indices.push(index); } + + // useSyncExternalStore creates 2 internal hooks, but we only count it as 1 user-facing hook + if (isUseSyncExternalStoreHook(next)) { + next = next.next; + prev = prev.next; + } + next = next.next; prev = prev.next; index++; @@ -2664,7 +2681,7 @@ export function attach( const fiber = fiberInstance.data; const props = fiber.memoizedProps; - // TODO: Compute a fallback name based on Owner, key etc. + // The frontend will guess a name based on heuristics (e.g. owner) if no explicit name is given. const name = fiber.tag !== SuspenseComponent || props === null ? null @@ -2862,7 +2879,10 @@ export function attach( let parentInstance = reconcilingParent; while ( parentInstance.kind === FILTERED_FIBER_INSTANCE && - parentInstance.parent !== null + parentInstance.parent !== null && + // We can't move past the parent Suspense node. + // The Suspense node holding async info must be a parent of the devtools instance (or the instance itself) + parentInstance !== parentSuspenseNode.instance ) { parentInstance = parentInstance.parent; } @@ -6168,7 +6188,10 @@ export function attach( } } const newIO = asyncInfo.awaited; - if (newIO.name === 'RSC stream' && newIO.value != null) { + if ( + (newIO.name === 'RSC stream' || newIO.name === 'rsc stream') && + newIO.value != null + ) { const streamPromise = newIO.value; // Special case RSC stream entries to pick the last entry keyed by the stream. const existingEntry = streamEntries.get(streamPromise); @@ -6227,7 +6250,10 @@ export function attach( continue; } foundIOEntries.add(ioInfo); - if (ioInfo.name === 'RSC stream' && ioInfo.value != null) { + if ( + (ioInfo.name === 'RSC stream' || ioInfo.name === 'rsc stream') && + ioInfo.value != null + ) { const streamPromise = ioInfo.value; // Special case RSC stream entries to pick the last entry keyed by the stream. const existingEntry = streamEntries.get(streamPromise); diff --git a/packages/react-devtools-shared/src/devtools/store.js b/packages/react-devtools-shared/src/devtools/store.js index b75e30d9c4..9377fa01df 100644 --- a/packages/react-devtools-shared/src/devtools/store.js +++ b/packages/react-devtools-shared/src/devtools/store.js @@ -62,6 +62,31 @@ import type { import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError'; import type {DevToolsHookSettings} from '../backend/types'; +import RBush from 'rbush'; + +// Custom version which works with our Rect data structure. +class RectRBush extends RBush { + toBBox(rect: Rect): { + minX: number, + minY: number, + maxX: number, + maxY: number, + } { + return { + minX: rect.x, + minY: rect.y, + maxX: rect.x + rect.width, + maxY: rect.y + rect.height, + }; + } + compareMinX(a: Rect, b: Rect): number { + return a.x - b.x; + } + compareMinY(a: Rect, b: Rect): number { + return a.y - b.y; + } +} + const debug = (methodName: string, ...args: Array) => { if (__DEBUG__) { console.log( @@ -194,6 +219,9 @@ export default class Store extends EventEmitter<{ // Renderer ID is needed to support inspection fiber props, state, and hooks. _rootIDToRendererID: Map = new Map(); + // Stores all the SuspenseNode rects in an R-tree to make it fast to find overlaps. + _rtree: RBush = new RectRBush(); + // These options may be initially set by a configuration option when constructing the Store. _supportsInspectMatchingDOMElement: boolean = false; _supportsClickToInspect: boolean = false; @@ -1622,7 +1650,12 @@ export default class Store extends EventEmitter<{ const y = operations[i + 1] / 1000; const width = operations[i + 2] / 1000; const height = operations[i + 3] / 1000; - rects.push({x, y, width, height}); + const rect = {x, y, width, height}; + if (parentID !== 0) { + // Track all rects except the root. + this._rtree.insert(rect); + } + rects.push(rect); i += 4; } } @@ -1646,10 +1679,6 @@ export default class Store extends EventEmitter<{ parentSuspense.children.push(id); } - if (name === null) { - name = 'Unknown'; - } - this._idToSuspense.set(id, { id, parentID, @@ -1684,13 +1713,20 @@ export default class Store extends EventEmitter<{ i += 1; - const {children, parentID} = suspense; + const {children, parentID, rects} = suspense; if (children.length > 0) { this._throwAndEmitError( Error(`Suspense node "${id}" was removed before its children.`), ); } + if (rects !== null && parentID !== 0) { + // Delete all the existing rects from the R-tree + for (let j = 0; j < rects.length; j++) { + this._rtree.remove(rects[j]); + } + } + this._idToSuspense.delete(id); removedSuspenseIDs.set(id, parentID); @@ -1789,6 +1825,14 @@ export default class Store extends EventEmitter<{ break; } + const prevRects = suspense.rects; + if (prevRects !== null && suspense.parentID !== 0) { + // Delete all the existing rects from the R-tree + for (let j = 0; j < prevRects.length; j++) { + this._rtree.remove(prevRects[j]); + } + } + let nextRects: SuspenseNode['rects']; if (numRects === -1) { nextRects = null; @@ -1800,7 +1844,12 @@ export default class Store extends EventEmitter<{ const width = operations[i + 2] / 1000; const height = operations[i + 3] / 1000; - nextRects.push({x, y, width, height}); + const rect = {x, y, width, height}; + if (suspense.parentID !== 0) { + // Track all rects except the root. + this._rtree.insert(rect); + } + nextRects.push(rect); i += 4; } @@ -2170,13 +2219,12 @@ export default class Store extends EventEmitter<{ throw error; } - _guessSuspenseName(element: Element): string { + _guessSuspenseName(element: Element): string | null { const owner = this._idToElement.get(element.ownerID); - let name = 'Unknown'; if (owner !== undefined && owner.displayName !== null) { - name = owner.displayName; + return owner.displayName; } - return name; + return null; } } diff --git a/packages/react-devtools-shared/src/devtools/utils.js b/packages/react-devtools-shared/src/devtools/utils.js index d5078679f4..640f0d19b5 100644 --- a/packages/react-devtools-shared/src/devtools/utils.js +++ b/packages/react-devtools-shared/src/devtools/utils.js @@ -63,11 +63,7 @@ function printRects(rects: SuspenseNode['rects']): string { } function printSuspense(suspense: SuspenseNode): string { - let name = ''; - if (suspense.name !== null) { - name = ` name="${suspense.name}"`; - } - + const name = ` name="${suspense.name || 'Unknown'}"`; const printedRects = printRects(suspense.rects); return ``; diff --git a/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.css b/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.css index 574ceb0236..98200ee9df 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.css +++ b/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.css @@ -2,9 +2,10 @@ padding: 0.25rem; } -.CallSite { - display: block; +.CallSite, .BuiltInCallSite { + display: flex; padding-left: 1rem; + white-space-collapse: preserve; } .IgnoredCallSite, .BuiltInCallSite { @@ -20,13 +21,15 @@ white-space: pre; overflow: hidden; text-overflow: ellipsis; - flex: 1; cursor: pointer; border-radius: 0.125rem; - padding: 0px 2px; } .Link:hover { background-color: var(--color-background-hover); } +.ElementBadges { + margin-left: 0.25rem; +} + diff --git a/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.js b/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.js index d3d797f781..a3cf91f14b 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/StackTraceView.js @@ -86,8 +86,10 @@ export function CallSiteView({ )} - - +
); } diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js index d7112ec7d3..b27e0f5987 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js @@ -72,7 +72,7 @@ export default function SuspenseBreadcrumbs(): React$Node { className={styles.SuspenseBreadcrumbsButton} onClick={handleClick.bind(null, id)} type="button"> - {node === null ? 'Unknown' : node.name} + {node === null ? 'Unknown' : node.name || 'Unknown'} ); diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css index db0a84d8d8..0af7baf51a 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css @@ -39,6 +39,25 @@ pointer-events: none; } +.SuspenseRectsTitle { + pointer-events: none; + color: color-mix(in srgb, var(--color-suspense) 50%, var(--color-text)); + overflow: hidden; + text-overflow: ellipsis; + font-family: var(--font-family-sans); + font-size: var(--font-size-sans-small); + line-height: var(--line-height-data); + padding: 0 .25rem; + container-type: size; + container-name: title; +} + +@container title (width < 30px) or (height < 18px) { + .SuspenseRectsTitle > span { + display: none; + } +} + .SuspenseRectsScaledRect[data-visible='false'] > .SuspenseRectsBoundaryChildren { overflow: initial; } @@ -75,7 +94,7 @@ transition: background-color 0.2s ease-out; } -.SuspenseRectsBoundary[data-selected='true'] { +.SuspenseRectsBoundary[data-selected='true'][data-visible='true'] { box-shadow: var(--elevation-4); } diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js index e6beca7c22..d5f44eeb32 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js @@ -31,6 +31,7 @@ import { SuspenseTreeDispatcherContext, } from './SuspenseTreeContext'; import {getClassNameForEnvironment} from './SuspenseEnvironmentColors.js'; +import type RBush from 'rbush'; function ScaledRect({ className, @@ -78,8 +79,10 @@ function ScaledRect({ function SuspenseRects({ suspenseID, + parentRects, }: { suspenseID: SuspenseNode['id'], + parentRects: null | Array, }): React$Node { const store = useContext(StoreContext); const treeDispatch = useContext(TreeDispatcherContext); @@ -167,7 +170,20 @@ function SuspenseRects({ } } - const boundingBox = getBoundingBox(suspense.rects); + const rects = suspense.rects; + const boundingBox = getBoundingBox(rects); + + // Next we'll try to find a rect within one of our rects that isn't intersecting with + // other rects. + // TODO: This should probably be memoized based on if any changes to the rtree has been made. + const titleBox: null | Rect = + rects === null ? null : findTitleBox(store._rtree, rects, parentRects); + const nextRects = + rects === null || rects.length === 0 + ? parentRects + : parentRects === null || parentRects.length === 0 + ? rects + : parentRects.concat(rects); return ( ); })} @@ -205,11 +221,22 @@ function SuspenseRects({ className={styles.SuspenseRectsBoundaryChildren} rect={boundingBox}> {suspense.children.map(childID => { - return ; + return ( + + ); })} )} - {selected ? ( + {titleBox && suspense.name && visible ? ( + + {suspense.name} + + ) : null} + {selected && visible ? ( , + rects: Array, + parentRects: null | Array, +): null | Rect { + for (let i = 0; i < rects.length; i++) { + const rect = rects[i]; + if (rect.width < 20 || rect.height < 10) { + // Skip small rects. They're likely not able to be contain anything useful anyway. + continue; + } + // Find all overlapping rects elsewhere in the tree to limit our rect. + const overlappingRects = rtree.search({ + minX: rect.x, + minY: rect.y, + maxX: rect.x + rect.width, + maxY: rect.y + rect.height, + }); + if ( + overlappingRects.length === 0 || + (overlappingRects.length === 1 && overlappingRects[0] === rect) + ) { + // There are no overlapping rects that isn't our own rect, so we can just use + // the full space of the rect. + return rect; + } + // We have some overlapping rects but they might not overlap everything. Let's + // shrink it up toward the top left corner until it has no more overlap. + const minX = rect.x; + const minY = rect.y; + let maxX = rect.x + rect.width; + let maxY = rect.y + rect.height; + for (let j = 0; j < overlappingRects.length; j++) { + const overlappingRect = overlappingRects[j]; + if (overlappingRect === rect) { + continue; + } + const x = overlappingRect.x; + const y = overlappingRect.y; + if (y < maxY && x < maxX) { + if ( + parentRects !== null && + parentRects.indexOf(overlappingRect) !== -1 + ) { + // This rect overlaps but it's part of a parent boundary. We let + // title content render if it's on top and not a sibling. + continue; + } + // This rect cuts into the remaining space. Let's figure out if we're + // better off cutting on the x or y axis to maximize remaining space. + const remainderX = x - minX; + const remainderY = y - minY; + if (remainderX > remainderY) { + maxX = x; + } else { + maxY = y; + } + } + } + if (maxX > minX && maxY > minY) { + return { + x: minX, + y: minY, + width: maxX - minX, + height: maxY - minY, + }; + } + } + return null; +} + function SuspenseRectsRoot({rootID}: {rootID: SuspenseNode['id']}): React$Node { const store = useContext(StoreContext); const root = store.getSuspenseByID(rootID); @@ -329,7 +427,9 @@ function SuspenseRectsRoot({rootID}: {rootID: SuspenseNode['id']}): React$Node { } return root.children.map(childID => { - return ; + return ( + + ); }); } diff --git a/packages/react-devtools-shared/src/devtools/views/useInferredName.js b/packages/react-devtools-shared/src/devtools/views/useInferredName.js index f0822e126d..2c93ded469 100644 --- a/packages/react-devtools-shared/src/devtools/views/useInferredName.js +++ b/packages/react-devtools-shared/src/devtools/views/useInferredName.js @@ -16,7 +16,7 @@ export default function useInferredName( const fetchFileWithCaching = useContext(FetchFileWithCachingContext); const name = asyncInfo.awaited.name; let inferNameFromStack = null; - if (!name || name === 'Promise') { + if (!name || name === 'Promise' || name === 'lazy') { // If all we have is a generic name, we can try to infer a better name from // the stack. We only do this if the stack has more than one frame since // otherwise it's likely to just be the name of the component which isn't better. diff --git a/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js b/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js index 95f104925b..34f46774b1 100644 --- a/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js +++ b/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js @@ -20,6 +20,7 @@ import SimpleValues from './SimpleValues'; import SymbolKeys from './SymbolKeys'; import UseMemoCache from './UseMemoCache'; import UseEffectEvent from './UseEffectEvent'; +import UseSyncExternalStore from './UseSyncExternalStore'; // TODO Add Immutable JS example @@ -38,6 +39,7 @@ export default function InspectableElements(): React.Node { + ); } diff --git a/packages/react-devtools-shell/src/app/InspectableElements/UseSyncExternalStore.js b/packages/react-devtools-shell/src/app/InspectableElements/UseSyncExternalStore.js new file mode 100644 index 0000000000..decb10b2db --- /dev/null +++ b/packages/react-devtools-shell/src/app/InspectableElements/UseSyncExternalStore.js @@ -0,0 +1,133 @@ +/** + * 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. + * + * @flow + */ + +import * as React from 'react'; + +const {useState, useEffect, useSyncExternalStore} = React; + +// Create a simple external store for demonstratio +function createStore(initialValue: T): { + subscribe: (cb: () => void) => () => any, + getSnapshot: () => T, + setValue: (newValue: T) => void, +} { + let value = initialValue; + const subscribers = new Set<() => void>(); + + return { + subscribe(callback) { + subscribers.add(callback); + return () => subscribers.delete(callback); + }, + getSnapshot() { + return value; + }, + setValue(newValue) { + value = newValue; + subscribers.forEach(callback => callback()); + }, + }; +} + +const counterStore = createStore(0); +const themeStore = createStore('light'); + +export default function UseSyncExternalStore(): React.Node { + return ( + <> +

useSyncExternalStore()

+ + + + + ); +} + +function SingleHookCase(): React.Node { + const count = useSyncExternalStore( + counterStore.subscribe, + counterStore.getSnapshot, + ); + + return ( +
+

Single hook case

+

Count: {count}

+ + +
+ ); +} + +function useCounter() { + const count = useSyncExternalStore( + counterStore.subscribe, + counterStore.getSnapshot, + ); + const [localState, setLocalState] = useState(0); + + useEffect(() => { + // Some effect + }, [count]); + + return {count, localState, setLocalState}; +} + +function HookTreeCase(): React.Node { + const {count, localState, setLocalState} = useCounter(); + + return ( +
+

Hook tree case

+

External count: {count}

+

Local state: {localState}

+ + +
+ ); +} + +function useTheme() { + const theme = useSyncExternalStore( + themeStore.subscribe, + themeStore.getSnapshot, + ); + + return theme; +} + +function MultipleStoresCase() { + const count = useSyncExternalStore( + counterStore.subscribe, + counterStore.getSnapshot, + ); + const theme = useTheme(); + + return ( +
+

Multiple stores case

+

Count: {count}

+

Theme: {theme}

+ +
+ ); +} diff --git a/packages/react-devtools-timeline/package.json b/packages/react-devtools-timeline/package.json index 2f54399ce2..5e4df83552 100644 --- a/packages/react-devtools-timeline/package.json +++ b/packages/react-devtools-timeline/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "react-devtools-timeline", - "version": "7.0.0", + "version": "7.0.1", "license": "MIT", "dependencies": { "@elg/speedscope": "1.9.0-a6f84db", diff --git a/packages/react-devtools/CHANGELOG.md b/packages/react-devtools/CHANGELOG.md index 0fc637b994..6c3ab1baf0 100644 --- a/packages/react-devtools/CHANGELOG.md +++ b/packages/react-devtools/CHANGELOG.md @@ -4,6 +4,15 @@ --- +### 7.0.1 +October 20, 2025 + +* Various UI improvements to experimental Suspense tab ([sebmarkbage](https://github.com/sebmarkbage) & [eps1lon](https://github.com/eps1lon)) +* devtools: fix ellipsis truncation for key values ([sophiebits](https://github.com/sophiebits) in [#34796](https://github.com/facebook/react/pull/34796)) +* fix(devtools): remove duplicated "Display density" field in General settings ([Anatole-Godard](https://github.com/Anatole-Godard) in [#34792](https://github.com/facebook/react/pull/34792)) + +--- + ### 7.0.0 Oct 2, 2025 diff --git a/packages/react-devtools/package.json b/packages/react-devtools/package.json index ea8bab1c95..3d1a1bda2f 100644 --- a/packages/react-devtools/package.json +++ b/packages/react-devtools/package.json @@ -1,6 +1,6 @@ { "name": "react-devtools", - "version": "7.0.0", + "version": "7.0.1", "description": "Use react-devtools outside of the browser", "license": "MIT", "repository": { @@ -26,7 +26,7 @@ "electron": "^23.1.2", "internal-ip": "^6.2.0", "minimist": "^1.2.3", - "react-devtools-core": "7.0.0", + "react-devtools-core": "7.0.1", "update-notifier": "^2.1.0" } } diff --git a/packages/react-markup/src/ReactMarkupServer.js b/packages/react-markup/src/ReactMarkupServer.js index 0b35404cb8..6f1e35e615 100644 --- a/packages/react-markup/src/ReactMarkupServer.js +++ b/packages/react-markup/src/ReactMarkupServer.js @@ -91,6 +91,9 @@ export function experimental_renderToHTML( undefined, undefined, false, + undefined, + undefined, + undefined, ); const streamState = createFlightStreamState(flightResponse, null); const flightDestination = { diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js index cfc8dcf5f1..371f08abc9 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js @@ -52,6 +52,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, }; function createDebugCallbackFromWritableStream( @@ -103,6 +104,9 @@ function createResponseFromOptions(options: void | Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js index 2bf3272947..78dce93615 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js @@ -57,6 +57,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Node.js client we only support a single-direction debug channel. debugChannel?: Readable, }; @@ -112,6 +113,9 @@ function createFromNodeStream( __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js index 2f71ce2fa9..0f0141e640 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js @@ -129,6 +129,9 @@ function createResponseFromOptions(options: void | Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } @@ -205,6 +208,7 @@ export type Options = { temporaryReferences?: TemporaryReferenceSet, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, }; export function createFromReadableStream( diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js index f9c63ccbc3..5c8d1023b2 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js @@ -79,6 +79,7 @@ export type Options = { temporaryReferences?: TemporaryReferenceSet, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Edge client we only support a single-direction debug channel. debugChannel?: {readable?: ReadableStream, ...}, }; @@ -107,6 +108,9 @@ function createResponseFromOptions(options?: Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js index b874bcd7d4..fbc633a175 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js @@ -52,6 +52,7 @@ export type Options = { encodeFormAction?: EncodeFormActionCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Node.js client we only support a single-direction debug channel. debugChannel?: Readable, }; @@ -103,6 +104,9 @@ export function createFromNodeStream( __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js index b4b84f1c41..b3d31bd1bb 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js @@ -51,6 +51,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, }; function createDebugCallbackFromWritableStream( @@ -102,6 +103,9 @@ function createResponseFromOptions(options: void | Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js index b994841be1..5517e0f73c 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js @@ -79,6 +79,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Edge client we only support a single-direction debug channel. debugChannel?: {readable?: ReadableStream, ...}, }; @@ -109,6 +110,9 @@ function createResponseFromOptions(options: Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js index f174f10c0b..6d117929df 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js @@ -60,6 +60,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Node.js client we only support a single-direction debug channel. debugChannel?: Readable, }; @@ -114,6 +115,9 @@ function createFromNodeStream( __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js index b4b84f1c41..b3d31bd1bb 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js @@ -51,6 +51,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, }; function createDebugCallbackFromWritableStream( @@ -102,6 +103,9 @@ function createResponseFromOptions(options: void | Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js index 63dae49545..bc4caac767 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js @@ -79,6 +79,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Edge client we only support a single-direction debug channel. debugChannel?: {readable?: ReadableStream, ...}, }; @@ -109,6 +110,9 @@ function createResponseFromOptions(options: Options) { __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); } diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js index f174f10c0b..6d117929df 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js @@ -60,6 +60,7 @@ export type Options = { findSourceMapURL?: FindSourceMapURLCallback, replayConsoleLogs?: boolean, environmentName?: string, + startTime?: number, // For the Node.js client we only support a single-direction debug channel. debugChannel?: Readable, }; @@ -114,6 +115,9 @@ function createFromNodeStream( __DEV__ && options && options.environmentName ? options.environmentName : undefined, + __DEV__ && options && options.startTime != null + ? options.startTime + : undefined, debugChannel, ); diff --git a/packages/react-server/src/ReactFlightServerConfigDebugNode.js b/packages/react-server/src/ReactFlightServerConfigDebugNode.js index a78297ad83..e79c19cc73 100644 --- a/packages/react-server/src/ReactFlightServerConfigDebugNode.js +++ b/packages/react-server/src/ReactFlightServerConfigDebugNode.js @@ -142,10 +142,28 @@ export function initAsyncDebugInfo(): void { }: UnresolvedPromiseNode); } } else if ( - type !== 'Microtask' && - type !== 'TickObject' && - type !== 'Immediate' + // bound-anonymous-fn is the default name for snapshots and .bind() without a name. + // This isn't I/O by itself but likely just a continuation. If the bound function + // has a name, we might treat it as I/O but we can't tell the difference. + type === 'bound-anonymous-fn' || + // queueMicroTask, process.nextTick and setImmediate aren't considered new I/O + // for our purposes but just continuation of existing I/O. + type === 'Microtask' || + type === 'TickObject' || + type === 'Immediate' ) { + // Treat the trigger as the node to carry along the sequence. + // For "bound-anonymous-fn" this will be the callsite of the .bind() which may not + // be the best if the callsite of the .run() call is within I/O which should be + // tracked. It might be better to track the execution context of "before()" as the + // execution context for anything spawned from within the run(). Basically as if + // it wasn't an AsyncResource at all. + if (trigger === undefined) { + return; + } + node = trigger; + } else { + // New I/O if (trigger === undefined) { // We have begun a new I/O sequence. const owner = resolveOwner(); @@ -181,13 +199,6 @@ export function initAsyncDebugInfo(): void { // Otherwise, this is just a continuation of the same I/O sequence. node = trigger; } - } else { - // Ignore nextTick and microtasks as they're not considered I/O operations. - // we just treat the trigger as the node to carry along the sequence. - if (trigger === undefined) { - return; - } - node = trigger; } pendingOperations.set(asyncId, node); }, diff --git a/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js b/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js index 00c6f78503..db1d7af3a6 100644 --- a/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js +++ b/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js @@ -459,7 +459,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -867,7 +867,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -985,7 +985,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -1460,7 +1460,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "byteSize": 0, "end": 0, "env": "Server", - "name": "RSC stream", + "name": "rsc stream", "start": 0, "value": { "value": "stream", @@ -1475,7 +1475,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -1789,7 +1789,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -2083,7 +2083,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -2499,7 +2499,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -2665,7 +2665,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -2845,7 +2845,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -3130,7 +3130,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { @@ -3275,7 +3275,7 @@ describe('ReactFlightAsyncDebugInfo', () => { "awaited": { "byteSize": 0, "end": 0, - "name": "RSC stream", + "name": "rsc stream", "owner": null, "start": 0, "value": { diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index 55b1690b7c..47f37afbae 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -20,6 +20,8 @@ import {enableAsyncDebugInfo} from 'shared/ReactFeatureFlags'; import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; +import noop from 'shared/noop'; + const Uninitialized = -1; const Pending = 0; const Resolved = 1; @@ -67,12 +69,20 @@ export type LazyComponent = { function lazyInitializer(payload: Payload): T { if (payload._status === Uninitialized) { + let resolveDebugValue: (void | T) => void = (null: any); + let rejectDebugValue: mixed => void = (null: any); if (__DEV__ && enableAsyncDebugInfo) { const ioInfo = payload._ioInfo; if (ioInfo != null) { // Mark when we first kicked off the lazy request. // $FlowFixMe[cannot-write] ioInfo.start = ioInfo.end = performance.now(); + // Stash a Promise for introspection of the value later. + // $FlowFixMe[cannot-write] + ioInfo.value = new Promise((resolve, reject) => { + resolveDebugValue = resolve; + rejectDebugValue = reject; + }); } } const ctor = payload._result; @@ -92,12 +102,20 @@ function lazyInitializer(payload: Payload): T { const resolved: ResolvedPayload = (payload: any); resolved._status = Resolved; resolved._result = moduleObject; - if (__DEV__) { + if (__DEV__ && enableAsyncDebugInfo) { const ioInfo = payload._ioInfo; if (ioInfo != null) { // Mark the end time of when we resolved. // $FlowFixMe[cannot-write] ioInfo.end = performance.now(); + // Surface the default export as the resolved "value" for debug purposes. + const debugValue = + moduleObject == null ? undefined : moduleObject.default; + resolveDebugValue(debugValue); + // $FlowFixMe + ioInfo.value.status = 'fulfilled'; + // $FlowFixMe + ioInfo.value.value = debugValue; } // Make the thenable introspectable if (thenable.status === undefined) { @@ -124,6 +142,14 @@ function lazyInitializer(payload: Payload): T { // Mark the end time of when we rejected. // $FlowFixMe[cannot-write] ioInfo.end = performance.now(); + // Hide unhandled rejections. + // $FlowFixMe + ioInfo.value.then(noop, noop); + rejectDebugValue(error); + // $FlowFixMe + ioInfo.value.status = 'rejected'; + // $FlowFixMe + ioInfo.value.reason = error; } // Make the thenable introspectable if (thenable.status === undefined) { @@ -139,9 +165,6 @@ function lazyInitializer(payload: Payload): T { if (__DEV__ && enableAsyncDebugInfo) { const ioInfo = payload._ioInfo; if (ioInfo != null) { - // Stash the thenable for introspection of the value later. - // $FlowFixMe[cannot-write] - ioInfo.value = thenable; const displayName = thenable.displayName; if (typeof displayName === 'string') { // $FlowFixMe[cannot-write] diff --git a/packages/shared/ReactIODescription.js b/packages/shared/ReactIODescription.js index e1a0fce2c4..6d7bf648fc 100644 --- a/packages/shared/ReactIODescription.js +++ b/packages/shared/ReactIODescription.js @@ -13,6 +13,8 @@ export function getIODescription(value: mixed): string { } try { switch (typeof value) { + case 'function': + return value.name || ''; case 'object': // Test the object for a bunch of common property names that are useful identifiers. // While we only have the return value here, it should ideally be a name that diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 5556fad1a0..f201c2e0e8 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -456,3 +456,191 @@ declare class NavigationDestination { getState(): mixed; } + +// Ported from definitely-typed +declare module 'rbush' { + declare interface BBox { + minX: number; + minY: number; + maxX: number; + maxY: number; + } + + declare export default class RBush { + /** + * Constructs an `RBush`, a high-performance 2D spatial index for points and + * rectangles. Based on an optimized __R-tree__ data structure with + * __bulk-insertion__ support. + * + * @param maxEntries An optional argument to RBush defines the maximum + * number of entries in a tree node. `9` (used by default) + * is a reasonable choice for most applications. Higher + * value means faster insertion and slower search, and + * vice versa. + */ + constructor(maxEntries?: number): void; + + /** + * Inserts an item. To insert many items at once, use `load()`. + * + * @param item The item to insert. + */ + insert(item: T): RBush; + + /** + * Bulk-inserts the given items into the tree. + * + * Bulk insertion is usually ~2-3 times faster than inserting items one by + * one. After bulk loading (bulk insertion into an empty tree), subsequent + * query performance is also ~20-30% better. + * + * Note that when you do bulk insertion into an existing tree, it bulk-loads + * the given data into a separate tree and inserts the smaller tree into the + * larger tree. This means that bulk insertion works very well for clustered + * data (where items in one update are close to each other), but makes query + * performance worse if the data is scattered. + * + * @param items The items to load. + */ + load(items: $ReadOnlyArray): RBush; + + /** + * Removes a previously inserted item, comparing by reference. + * + * To remove all items, use `clear()`. + * + * @param item The item to remove. + * @param equals A custom function that allows comparing by value instead. + * Useful when you have only a copy of the object you need + * removed (e.g. loaded from server). + */ + remove(item: T, equals?: (a: T, b: T) => boolean): RBush; + + /** + * Removes all items. + */ + clear(): RBush; + + /** + * Returns an array of data items (points or rectangles) that the given + * bounding box intersects. + * + * Note that the search method accepts a bounding box in `{minX, minY, maxX, + * maxY}` format regardless of the data format. + * + * @param box The bounding box in which to search. + */ + search(box: BBox): T[]; + + /** + * Returns all items contained in the tree. + */ + all(): T[]; + + /** + * Returns `true` if there are any items intersecting the given bounding + * box, otherwise `false`. + * + * @param box The bounding box in which to search. + */ + collides(box: BBox): boolean; + + /** + * Returns the bounding box for the provided item. + * + * By default, `RBush` assumes the format of data points to be an object + * with `minX`, `minY`, `maxX`, and `maxY`. However, you can specify a + * custom item format by overriding `toBBox()`, `compareMinX()`, and + * `compareMinY()`. + * + * @example + * class MyRBush extends RBush { + * toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; } + * compareMinX(a, b) { return a.x - b.x; } + * compareMinY(a, b) { return a.y - b.y; } + * } + * const tree = new MyRBush<[number, number]>(); + * tree.insert([20, 50]); // accepts [x, y] points + * + * @param item The item whose bounding box should be returned. + */ + toBBox(item: T): BBox; + + /** + * Compares the minimum x coordinate of two items. Returns -1 if `a`'s + * x-coordinate is smaller, 1 if `b`'s x coordinate is smaller, or 0 if + * they're equal. + * + * By default, `RBush` assumes the format of data points to be an object + * with `minX`, `minY`, `maxX`, and `maxY`. However, you can specify a + * custom item format by overriding `toBBox()`, `compareMinX()`, and + * `compareMinY()`. + * + * @example + * class MyRBush extends RBush { + * toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; } + * compareMinX(a, b) { return a.x - b.x; } + * compareMinY(a, b) { return a.y - b.y; } + * } + * const tree = new MyRBush<[number, number]>(); + * tree.insert([20, 50]); // accepts [x, y] points + * + * @param a The first item to compare. + * @param b The second item to compare. + */ + compareMinX(a: T, b: T): number; + + /** + * Compares the minimum y coordinate of two items. Returns -1 if `a`'s + * x-coordinate is smaller, 1 if `b`'s x coordinate is smaller, or 0 if + * they're equal. + * + * By default, `RBush` assumes the format of data points to be an object + * with `minX`, `minY`, `maxX`, and `maxY`. However, you can specify a + * custom item format by overriding `toBBox()`, `compareMinX()`, and + * `compareMinY()`. + * + * @example + * class MyRBush extends RBush { + * toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; } + * compareMinX(a, b) { return a.x - b.x; } + * compareMinY(a, b) { return a.y - b.y; } + * } + * const tree = new MyRBush<[number, number]>(); + * tree.insert([20, 50]); // accepts [x, y] points + * + * @param a The first item to compare. + * @param b The second item to compare. + */ + compareMinY(a: T, b: T): number; + + /** + * Exports the tree's contents as a JSON object. + * + * Importing and exporting as JSON allows you to use RBush on both the + * server (using Node.js) and the browser combined, e.g. first indexing the + * data on the server and and then importing the resulting tree data on the + * client for searching. + * + * Note that the `maxEntries` option from the constructor must be the same + * in both trees for export/import to work properly. + */ + toJSON(): any; + + /** + * Imports previously exported data into the tree (i.e., data that was + * emitted by `toJSON()`). + * + * Importing and exporting as JSON allows you to use RBush on both the + * server (using Node.js) and the browser combined, e.g. first indexing the + * data on the server and and then importing the resulting tree data on the + * client for searching. + * + * Note that the `maxEntries` option from the constructor must be the same + * in both trees for export/import to work properly. + * + * @param data The previously exported JSON data. + */ + fromJSON(data: any): RBush; + } +} diff --git a/scripts/jest/config.build-devtools.js b/scripts/jest/config.build-devtools.js index 9baf33a39b..3f03b50c3a 100644 --- a/scripts/jest/config.build-devtools.js +++ b/scripts/jest/config.build-devtools.js @@ -63,7 +63,7 @@ module.exports = Object.assign({}, baseConfig, { testPathIgnorePatterns: ['/node_modules/', '-test.internal.js$'], // Exclude the build output from transforms transformIgnorePatterns: [ - '/node_modules/', + '/node_modules/(?!(rbush|quickselect)/)', '/build/', '/__compiled__/', '/__untransformed__/', diff --git a/yarn.lock b/yarn.lock index ad8a3f0085..2543e4d176 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8271,7 +8271,7 @@ eslint-utils@^2.0.0, eslint-utils@^2.1.0: dependencies: eslint-visitor-keys "^1.1.0" -"eslint-v7@npm:eslint@^7.7.0": +"eslint-v7@npm:eslint@^7.7.0", eslint@^7.7.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== @@ -8470,52 +8470,6 @@ eslint@8.57.0: strip-ansi "^6.0.1" text-table "^0.2.0" -eslint@^7.7.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - espree@10.0.1, espree@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f" @@ -14317,7 +14271,7 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -"prettier-2@npm:prettier@^2": +"prettier-2@npm:prettier@^2", prettier@^2.5.1: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -14332,11 +14286,6 @@ prettier@^1.19.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@^2.5.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - pretty-format@^29.4.1: version "29.4.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.1.tgz#0da99b532559097b8254298da7c75a0785b1751c" @@ -14602,6 +14551,11 @@ quick-tmp@0.0.0: first-match "0.0.1" osenv "0.0.3" +quickselect@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-3.0.0.tgz#a37fc953867d56f095a20ac71c6d27063d2de603" + integrity sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g== + random-seed@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/random-seed/-/random-seed-0.3.0.tgz#d945f2e1f38f49e8d58913431b8bf6bb937556cd" @@ -14639,6 +14593,13 @@ raw-loader@^3.1.0: loader-utils "^1.1.0" schema-utils "^2.0.1" +rbush@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/rbush/-/rbush-4.0.1.tgz#1f55afa64a978f71bf9e9a99bc14ff84f3cb0d6d" + integrity sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ== + dependencies: + quickselect "^3.0.0" + rc@1.2.8, rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -16211,7 +16172,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16246,15 +16207,6 @@ string-width@^4.0.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -16315,7 +16267,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16343,13 +16295,6 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -17958,7 +17903,7 @@ workerize-loader@^2.0.2: dependencies: loader-utils "^2.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -17976,15 +17921,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"