mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge 1e556701e0 into sapling-pr-archive-josephsavona
This commit is contained in:
@@ -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 <Activity> when it is compatible with Monaco: https://github.com/suren-atoyan/monaco-react/issues/753
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: isExpanded ? 'block' : 'none',
|
||||
}}>
|
||||
<Activity mode={isExpanded ? 'visible' : 'hidden'}>
|
||||
<ExpandedEditor
|
||||
onToggle={() => {
|
||||
startTransition(() => {
|
||||
@@ -49,11 +46,8 @@ export default function ConfigEditor({
|
||||
}}
|
||||
formattedAppliedConfig={formattedAppliedConfig}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: !isExpanded ? 'block' : 'none',
|
||||
}}>
|
||||
</Activity>
|
||||
<Activity mode={isExpanded ? 'hidden' : 'visible'}>
|
||||
<CollapsedEditor
|
||||
onToggle={() => {
|
||||
startTransition(() => {
|
||||
@@ -62,7 +56,7 @@ export default function ConfigEditor({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Activity>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -122,7 +116,8 @@ function ExpandedEditor({
|
||||
|
||||
return (
|
||||
<ViewTransition
|
||||
update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}>
|
||||
enter={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}
|
||||
exit={{[CONFIG_PANEL_TRANSITION]: 'slide-out', default: 'none'}}>
|
||||
<Resizable
|
||||
minWidth={300}
|
||||
maxWidth={600}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@babel/traverse": "^7.18.9",
|
||||
"@babel/types": "7.26.3",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@monaco-editor/react": "^4.8.0-rc.2",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@use-gesture/react": "^10.2.22",
|
||||
"hermes-eslint": "^0.25.0",
|
||||
@@ -40,13 +40,13 @@
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-format": "^29.3.1",
|
||||
"re-resizable": "^6.9.16",
|
||||
"react": "19.1.1",
|
||||
"react-dom": "19.1.1"
|
||||
"react": "19.2",
|
||||
"react-dom": "19.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.9",
|
||||
"@types/react": "19.1.13",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"@types/react": "19.2",
|
||||
"@types/react-dom": "19.2",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"clsx": "^1.2.1",
|
||||
"concurrently": "^7.4.0",
|
||||
@@ -58,7 +58,7 @@
|
||||
"wait-on": "^7.2.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react-dom": "19.1.9"
|
||||
"@types/react": "19.2",
|
||||
"@types/react-dom": "19.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,15 @@
|
||||
::view-transition-group(.slide-in) {
|
||||
z-index: 1;
|
||||
}
|
||||
::view-transition-old(.slide-out) {
|
||||
animation-name: slideOutLeft;
|
||||
}
|
||||
::view-transition-new(.slide-out) {
|
||||
animation-name: slideInLeft;
|
||||
}
|
||||
::view-transition-group(.slide-out) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes slideOutLeft {
|
||||
from {
|
||||
|
||||
@@ -701,19 +701,19 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@monaco-editor/loader@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558"
|
||||
integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==
|
||||
"@monaco-editor/loader@^1.6.1":
|
||||
version "1.6.1"
|
||||
resolved "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.6.1.tgz#c99177d87765abf10de31a0086084e714acfbc0f"
|
||||
integrity sha512-w3tEnj9HYEC73wtjdpR089AqkUPskFRcdkxsiSFt3SoUc3OHpmu+leP94CXBm4mHfefmhsdfI0ZQu6qJ0wgtPg==
|
||||
dependencies:
|
||||
state-local "^1.0.6"
|
||||
|
||||
"@monaco-editor/react@^4.4.6":
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.6.0.tgz#bcc68671e358a21c3814566b865a54b191e24119"
|
||||
integrity sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==
|
||||
"@monaco-editor/react@^4.8.0-rc.2":
|
||||
version "4.8.0-rc.2"
|
||||
resolved "https://registry.npmjs.org/@monaco-editor/react/-/react-4.8.0-rc.2.tgz#e9acf652e23e9f640671a69875f496dde7f098aa"
|
||||
integrity sha512-RzFHKBCnRA4RnozaG/EPhKsbkhX5wcApSa5MElR/AD2ojxhMY+QP+G8aJpxALCnIwKs6L0dec5MJ0nAjMUEqnA==
|
||||
dependencies:
|
||||
"@monaco-editor/loader" "^1.4.0"
|
||||
"@monaco-editor/loader" "^1.6.1"
|
||||
|
||||
"@next/env@15.6.0-canary.7":
|
||||
version "15.6.0-canary.7"
|
||||
@@ -859,6 +859,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.1.9.tgz#5ab695fce1e804184767932365ae6569c11b4b4b"
|
||||
integrity sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==
|
||||
|
||||
"@types/react-dom@19.2":
|
||||
version "19.2.2"
|
||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz#a4cc874797b7ddc9cb180ef0d5dc23f596fc2332"
|
||||
integrity sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==
|
||||
|
||||
"@types/react@19.1.12":
|
||||
version "19.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.12.tgz#7bfaa76aabbb0b4fe0493c21a3a7a93d33e8937b"
|
||||
@@ -866,10 +871,10 @@
|
||||
dependencies:
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/react@19.1.13":
|
||||
version "19.1.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.13.tgz#fc650ffa680d739a25a530f5d7ebe00cdd771883"
|
||||
integrity sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==
|
||||
"@types/react@19.2":
|
||||
version "19.2.2"
|
||||
resolved "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz#ba123a75d4c2a51158697160a4ea2ff70aa6bf36"
|
||||
integrity sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==
|
||||
dependencies:
|
||||
csstype "^3.0.2"
|
||||
|
||||
@@ -3589,12 +3594,12 @@ re-resizable@^6.9.16:
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.10.0.tgz#d684a096ab438f1a93f59ad3a580a206b0ce31ee"
|
||||
integrity sha512-hysSK0xmA5nz24HBVztlk4yCqCLCvS32E6ZpWxVKop9x3tqCa4yAj1++facrmkOf62JsJHjmjABdKxXofYioCw==
|
||||
|
||||
react-dom@19.1.1:
|
||||
version "19.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.1.tgz#2daa9ff7f3ae384aeb30e76d5ee38c046dc89893"
|
||||
integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==
|
||||
react-dom@19.2:
|
||||
version "19.2.0"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8"
|
||||
integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==
|
||||
dependencies:
|
||||
scheduler "^0.26.0"
|
||||
scheduler "^0.27.0"
|
||||
|
||||
react-is@^16.13.1:
|
||||
version "16.13.1"
|
||||
@@ -3606,10 +3611,10 @@ react-is@^18.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
|
||||
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
||||
|
||||
react@19.1.1:
|
||||
version "19.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af"
|
||||
integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==
|
||||
react@19.2:
|
||||
version "19.2.0"
|
||||
resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5"
|
||||
integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==
|
||||
|
||||
read-cache@^1.0.0:
|
||||
version "1.0.0"
|
||||
@@ -3785,10 +3790,10 @@ safe-regex-test@^1.1.0:
|
||||
es-errors "^1.3.0"
|
||||
is-regex "^1.2.1"
|
||||
|
||||
scheduler@^0.26.0:
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337"
|
||||
integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==
|
||||
scheduler@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
|
||||
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
|
||||
|
||||
semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
|
||||
@@ -12,6 +12,24 @@ import {Err, Ok, Result} from './Utils/Result';
|
||||
import {assertExhaustive} from './Utils/utils';
|
||||
import invariant from 'invariant';
|
||||
|
||||
// Number of context lines to display above the source of an error
|
||||
const CODEFRAME_LINES_ABOVE = 2;
|
||||
// Number of context lines to display below the source of an error
|
||||
const CODEFRAME_LINES_BELOW = 3;
|
||||
// Max number of lines for the _source_ of an error, before we abbreviate
|
||||
// the display of the source portion
|
||||
const CODEFRAME_MAX_LINES = 3;
|
||||
// When the error source exceeds the above threshold, how many lines of
|
||||
// the source should be displayed? We show:
|
||||
// - CODEFRAME_LINES_ABOVE context lines
|
||||
// - CODEFRAME_ABBREVIATED_SOURCE_LINES of the error
|
||||
// - '...' ellipsis
|
||||
// - CODEFRAME_ABBREVIATED_SOURCE_LINES of the error
|
||||
// - CODEFRAME_LINES_BELOW context lines
|
||||
//
|
||||
// This value must be at least 2 or else we'll cut off important parts of the error message
|
||||
const CODEFRAME_ABBREVIATED_SOURCE_LINES = 2;
|
||||
|
||||
export enum ErrorSeverity {
|
||||
/**
|
||||
* An actionable error that the developer can fix. For example, product code errors should be
|
||||
@@ -496,7 +514,7 @@ function printCodeFrame(
|
||||
loc: t.SourceLocation,
|
||||
message: string,
|
||||
): string {
|
||||
return codeFrameColumns(
|
||||
const printed = codeFrameColumns(
|
||||
source,
|
||||
{
|
||||
start: {
|
||||
@@ -510,8 +528,25 @@ function printCodeFrame(
|
||||
},
|
||||
{
|
||||
message,
|
||||
linesAbove: CODEFRAME_LINES_ABOVE,
|
||||
linesBelow: CODEFRAME_LINES_BELOW,
|
||||
},
|
||||
);
|
||||
const lines = printed.split(/\r?\n/);
|
||||
if (loc.end.line - loc.start.line < CODEFRAME_MAX_LINES) {
|
||||
return printed;
|
||||
}
|
||||
const pipeIndex = lines[0].indexOf('|');
|
||||
return [
|
||||
...lines.slice(
|
||||
0,
|
||||
CODEFRAME_LINES_ABOVE + CODEFRAME_ABBREVIATED_SOURCE_LINES,
|
||||
),
|
||||
'> ' + ' '.repeat(pipeIndex - 2) + '…',
|
||||
...lines.slice(
|
||||
-(CODEFRAME_LINES_BELOW + CODEFRAME_ABBREVIATED_SOURCE_LINES),
|
||||
),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function printErrorSummary(category: ErrorCategory, message: string): string {
|
||||
|
||||
@@ -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',
|
||||
|
||||
+1
-12
@@ -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 (
|
||||
|
||||
+1
-12
@@ -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 (
|
||||
|
||||
+1
-6
@@ -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 <Foo data={data} />;
|
||||
|
||||
+1
-29
@@ -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<TInput, TOutput>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 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
|
||||
|
||||
+1
-4
@@ -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);
|
||||
|
||||
+1
-12
@@ -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 | <div key={key} onClick={() => data.set(key)}>
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 12 | {key}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 13 | </div>
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 14 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> …
|
||||
> 15 | }
|
||||
| ^^^^ Support non-trivial for..in inits
|
||||
16 | return <div>{items}</div>;
|
||||
|
||||
+1
-12
@@ -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 | <div key={item.id} onClick={() => data.set(item)}>
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 12 | {item.id}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 13 | </div>
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 14 | );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> …
|
||||
> 15 | }
|
||||
| ^^^^ Support non-trivial for..of inits
|
||||
16 | return <div>{items}</div>;
|
||||
|
||||
+1
-16
@@ -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 = <div value={value} />;
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 12 | }
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 13 | } finally {
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 14 | console.log(el);
|
||||
| ^^^^^^^^^^^^^^
|
||||
> …
|
||||
> 15 | }
|
||||
| ^^^^ (BuildHIR::lowerStatement) Handle TryStatement without a catch clause
|
||||
16 | return el;
|
||||
|
||||
+1
-6
@@ -28,12 +28,7 @@ error.todo-invalid-jsx-in-try-with-finally.ts:4:2
|
||||
3 | let el;
|
||||
> 4 | try {
|
||||
| ^^^^^
|
||||
> 5 | el = <div />;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 6 | } finally {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 7 | console.log(el);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> …
|
||||
> 8 | }
|
||||
| ^^^^ (BuildHIR::lowerStatement) Handle TryStatement without a catch clause
|
||||
9 | return el;
|
||||
|
||||
+1
-8
@@ -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 |
|
||||
|
||||
-41
@@ -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;
|
||||
```
|
||||
|
||||
|
||||
-41
@@ -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;
|
||||
```
|
||||
|
||||
|
||||
-40
@@ -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 | }
|
||||
```
|
||||
|
||||
|
||||
-42
@@ -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;
|
||||
```
|
||||
|
||||
|
||||
+1
-20
@@ -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(() => {
|
||||
|
||||
+2
-22
@@ -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]
|
||||
|
||||
+1
-18
@@ -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 <div>{prop1}</div>;
|
||||
|
||||
+1
-9
@@ -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
|
||||
|
||||
+56
@@ -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"}]
|
||||
+2
-1
@@ -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}],
|
||||
};
|
||||
+55
@@ -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]}
|
||||
+1
@@ -12,4 +12,5 @@ function Component(props) {
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
sequentialRenders: [{value: 42}, {value: 42}],
|
||||
};
|
||||
+67
@@ -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]}
|
||||
+54
@@ -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]}
|
||||
+1
-10
@@ -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 | }
|
||||
|
||||
+2
-28
@@ -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 | }
|
||||
|
||||
+1
-6
@@ -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 | }
|
||||
|
||||
+1
-8
@@ -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 | }
|
||||
|
||||
+1
-13
@@ -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
|
||||
|
||||
+1
-13
@@ -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
|
||||
|
||||
+2
-26
@@ -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
|
||||
|
||||
+1
-5
@@ -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
|
||||
|
||||
+1
-12
@@ -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 (
|
||||
|
||||
+1
-12
@@ -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 (
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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"
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -71,7 +71,10 @@ const configs = {
|
||||
plugins,
|
||||
rules: recommendedLatestRuleConfigs,
|
||||
},
|
||||
flat: {} as Record<string, ReactHooksFlatConfig>,
|
||||
flat: {} as {
|
||||
recommended: ReactHooksFlatConfig;
|
||||
'recommended-latest': ReactHooksFlatConfig;
|
||||
},
|
||||
};
|
||||
|
||||
const plugin = {
|
||||
|
||||
@@ -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.
|
||||
|
||||
+6
-2
@@ -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<number, SomeChunk<any>> = 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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<HTMLLinkElement | HTMLStyleElement> {
|
||||
const tags: Array<HTMLLinkElement | HTMLStyleElement> = [];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+21
-3
@@ -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}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ module.exports = {
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: true,
|
||||
sourceMap: __DEV__,
|
||||
modules: true,
|
||||
localIdentName: '[local]___[hash:base64:5]',
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3243,4 +3243,61 @@ describe('Store', () => {
|
||||
<Suspense name="inner-suspense" rects={[{x:1,y:2,width:15,height:1}]}>
|
||||
`);
|
||||
});
|
||||
|
||||
// @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 (
|
||||
<React.Suspense fallback={<p>Loading inner</p>}>
|
||||
<p>{promise}</p>
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
function Outer({children}) {
|
||||
return (
|
||||
<React.Suspense fallback={<p>Loading outer</p>}>
|
||||
<p>{promise}</p>
|
||||
{children}
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
await actAsync(() => {
|
||||
render(
|
||||
<Outer>
|
||||
<Inner />
|
||||
</Outer>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(store).toMatchInlineSnapshot(`
|
||||
[root]
|
||||
▾ <Outer>
|
||||
<Suspense>
|
||||
[suspense-root] rects={[{x:1,y:2,width:13,height:1}]}
|
||||
<Suspense name="Outer" rects={null}>
|
||||
`);
|
||||
|
||||
console.log('...........................');
|
||||
|
||||
await actAsync(() => {
|
||||
resolve('loaded');
|
||||
});
|
||||
|
||||
expect(store).toMatchInlineSnapshot(`
|
||||
[root]
|
||||
▾ <Outer>
|
||||
▾ <Suspense>
|
||||
▾ <Inner>
|
||||
<Suspense>
|
||||
[suspense-root] rects={[{x:1,y:2,width:6,height:1}, {x:1,y:2,width:6,height:1}]}
|
||||
<Suspense name="Outer" rects={[{x:1,y:2,width:6,height:1}, {x:1,y:2,width:6,height:1}]}>
|
||||
<Suspense name="Inner" rects={[{x:1,y:2,width:6,height:1}]}>
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
+36
-10
@@ -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);
|
||||
|
||||
+59
-11
@@ -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<Rect> {
|
||||
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<string>) => {
|
||||
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<Element['id'], number> = new Map();
|
||||
|
||||
// Stores all the SuspenseNode rects in an R-tree to make it fast to find overlaps.
|
||||
_rtree: RBush<Rect> = 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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -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 `<Suspense${name}${printedRects}>`;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -86,8 +86,10 @@ export function CallSiteView({
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ElementBadges environmentName={environmentName} />
|
||||
<ElementBadges
|
||||
className={styles.ElementBadges}
|
||||
environmentName={environmentName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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'}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+105
-5
@@ -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<Rect>,
|
||||
}): 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 (
|
||||
<ScaledRect
|
||||
@@ -196,7 +212,7 @@ function SuspenseRects({
|
||||
onPointerOver={handlePointerOver}
|
||||
onPointerLeave={handlePointerLeave}
|
||||
// Reach-UI tooltip will go out of bounds of parent scroll container.
|
||||
title={suspense.name}
|
||||
title={suspense.name || 'Unknown'}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -205,11 +221,22 @@ function SuspenseRects({
|
||||
className={styles.SuspenseRectsBoundaryChildren}
|
||||
rect={boundingBox}>
|
||||
{suspense.children.map(childID => {
|
||||
return <SuspenseRects key={childID} suspenseID={childID} />;
|
||||
return (
|
||||
<SuspenseRects
|
||||
key={childID}
|
||||
suspenseID={childID}
|
||||
parentRects={nextRects}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ScaledRect>
|
||||
)}
|
||||
{selected ? (
|
||||
{titleBox && suspense.name && visible ? (
|
||||
<ScaledRect className={styles.SuspenseRectsTitle} rect={titleBox}>
|
||||
<span>{suspense.name}</span>
|
||||
</ScaledRect>
|
||||
) : null}
|
||||
{selected && visible ? (
|
||||
<ScaledRect
|
||||
className={styles.SuspenseRectOutline}
|
||||
rect={boundingBox}
|
||||
@@ -320,6 +347,77 @@ function getDocumentBoundingRect(
|
||||
};
|
||||
}
|
||||
|
||||
function findTitleBox(
|
||||
rtree: RBush<Rect>,
|
||||
rects: Array<Rect>,
|
||||
parentRects: null | Array<Rect>,
|
||||
): 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 <SuspenseRects key={childID} suspenseID={childID} />;
|
||||
return (
|
||||
<SuspenseRects key={childID} suspenseID={childID} parentRects={null} />
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+2
@@ -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 {
|
||||
<SymbolKeys />
|
||||
<UseMemoCache />
|
||||
<UseEffectEvent />
|
||||
<UseSyncExternalStore />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
+133
@@ -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<T>(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 (
|
||||
<>
|
||||
<h2>useSyncExternalStore()</h2>
|
||||
<SingleHookCase />
|
||||
<HookTreeCase />
|
||||
<MultipleStoresCase />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SingleHookCase(): React.Node {
|
||||
const count = useSyncExternalStore(
|
||||
counterStore.subscribe,
|
||||
counterStore.getSnapshot,
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>Single hook case</h3>
|
||||
<p>Count: {count}</p>
|
||||
<button onClick={() => counterStore.setValue(count + 1)}>
|
||||
Increment
|
||||
</button>
|
||||
<button onClick={() => counterStore.setValue(count - 1)}>
|
||||
Decrement
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div>
|
||||
<h3>Hook tree case</h3>
|
||||
<p>External count: {count}</p>
|
||||
<p>Local state: {localState}</p>
|
||||
<button onClick={() => counterStore.setValue(count + 1)}>
|
||||
Increment External
|
||||
</button>
|
||||
<button onClick={() => setLocalState(localState + 1)}>
|
||||
Increment Local
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function useTheme() {
|
||||
const theme = useSyncExternalStore(
|
||||
themeStore.subscribe,
|
||||
themeStore.getSnapshot,
|
||||
);
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
function MultipleStoresCase() {
|
||||
const count = useSyncExternalStore(
|
||||
counterStore.subscribe,
|
||||
counterStore.getSnapshot,
|
||||
);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{background: theme === 'dark' ? '#333' : '#fff'}}>
|
||||
<h3>Multiple stores case</h3>
|
||||
<p>Count: {count}</p>
|
||||
<p>Theme: {theme}</p>
|
||||
<button
|
||||
onClick={() =>
|
||||
themeStore.setValue(theme === 'light' ? 'dark' : 'light')
|
||||
}>
|
||||
Toggle Theme
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,9 @@ export function experimental_renderToHTML(
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
const streamState = createFlightStreamState(flightResponse, null);
|
||||
const flightDestination = {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<T>(
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
__DEV__ && options && options.startTime != null
|
||||
? options.startTime
|
||||
: undefined,
|
||||
debugChannel,
|
||||
);
|
||||
|
||||
|
||||
@@ -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<T>(
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<T>(
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
__DEV__ && options && options.startTime != null
|
||||
? options.startTime
|
||||
: undefined,
|
||||
debugChannel,
|
||||
);
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<T>(
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
__DEV__ && options && options.startTime != null
|
||||
? options.startTime
|
||||
: undefined,
|
||||
debugChannel,
|
||||
);
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<T>(
|
||||
__DEV__ && options && options.environmentName
|
||||
? options.environmentName
|
||||
: undefined,
|
||||
__DEV__ && options && options.startTime != null
|
||||
? options.startTime
|
||||
: undefined,
|
||||
debugChannel,
|
||||
);
|
||||
|
||||
|
||||
+21
-10
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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<T, P> = {
|
||||
|
||||
function lazyInitializer<T>(payload: Payload<T>): 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<T>(payload: Payload<T>): T {
|
||||
const resolved: ResolvedPayload<T> = (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<T>(payload: Payload<T>): 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<T>(payload: Payload<T>): 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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<T> {
|
||||
/**
|
||||
* 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<T>;
|
||||
|
||||
/**
|
||||
* 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<T>): RBush<T>;
|
||||
|
||||
/**
|
||||
* 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<T>;
|
||||
|
||||
/**
|
||||
* Removes all items.
|
||||
*/
|
||||
clear(): RBush<T>;
|
||||
|
||||
/**
|
||||
* 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<T> extends RBush<T> {
|
||||
* 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<T> extends RBush<T> {
|
||||
* 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<T> extends RBush<T> {
|
||||
* 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<T>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)/)',
|
||||
'<rootDir>/build/',
|
||||
'/__compiled__/',
|
||||
'/__untransformed__/',
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user