From ae3190c5e929ec3b899734e2a6aface7cd4e42d0 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 7 Feb 2025 10:54:44 +0000 Subject: [PATCH 001/300] React DevTools 6.1.0 -> 6.1.1 (#32326) Full list of changes: * DevTools: refactor NativeStyleEditor, don't use custom cache implementation ([hoxyq](https://github.com/hoxyq) in [#32298](https://github.com/facebook/react/pull/32298)) * fix[react-devtools-fusebox]: add extension globals to build ([hoxyq](https://github.com/hoxyq) in [#32297](https://github.com/facebook/react/pull/32297)) * DevTools: fix host component filter option title ([hoxyq](https://github.com/hoxyq) in [#32296](https://github.com/facebook/react/pull/32296)) * chore[DevTools]: make clipboardWrite optional for chromium ([hoxyq](https://github.com/hoxyq) in [#32262](https://github.com/facebook/react/pull/32262)) * DevTools: support useEffectEvent and forward-fix experimental prefix support ([hoxyq](https://github.com/hoxyq) in [#32106](https://github.com/facebook/react/pull/32106)) --- packages/react-devtools-core/package.json | 2 +- packages/react-devtools-extensions/chrome/manifest.json | 4 ++-- packages/react-devtools-extensions/edge/manifest.json | 4 ++-- packages/react-devtools-extensions/firefox/manifest.json | 2 +- packages/react-devtools-inline/package.json | 2 +- packages/react-devtools-timeline/package.json | 2 +- packages/react-devtools/CHANGELOG.md | 9 +++++++++ packages/react-devtools/package.json | 4 ++-- 8 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/react-devtools-core/package.json b/packages/react-devtools-core/package.json index 88951de463c96..6794f631772be 100644 --- a/packages/react-devtools-core/package.json +++ b/packages/react-devtools-core/package.json @@ -1,6 +1,6 @@ { "name": "react-devtools-core", - "version": "6.1.0", + "version": "6.1.1", "description": "Use react-devtools outside of the browser", "license": "MIT", "main": "./dist/backend.js", diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index 4b2d810f60411..5cf6397bc75ef 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -2,8 +2,8 @@ "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Chrome Developer Tools.", - "version": "6.1.0", - "version_name": "6.1.0", + "version": "6.1.1", + "version_name": "6.1.1", "minimum_chrome_version": "102", "icons": { "16": "icons/16-production.png", diff --git a/packages/react-devtools-extensions/edge/manifest.json b/packages/react-devtools-extensions/edge/manifest.json index 37a76be2f24bc..512dd888f7d94 100644 --- a/packages/react-devtools-extensions/edge/manifest.json +++ b/packages/react-devtools-extensions/edge/manifest.json @@ -2,8 +2,8 @@ "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Microsoft Edge Developer Tools.", - "version": "6.1.0", - "version_name": "6.1.0", + "version": "6.1.1", + "version_name": "6.1.1", "minimum_chrome_version": "102", "icons": { "16": "icons/16-production.png", diff --git a/packages/react-devtools-extensions/firefox/manifest.json b/packages/react-devtools-extensions/firefox/manifest.json index d053921ebeecc..22dee6a10a42d 100644 --- a/packages/react-devtools-extensions/firefox/manifest.json +++ b/packages/react-devtools-extensions/firefox/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Firefox Developer Tools.", - "version": "6.1.0", + "version": "6.1.1", "browser_specific_settings": { "gecko": { "id": "@react-devtools", diff --git a/packages/react-devtools-inline/package.json b/packages/react-devtools-inline/package.json index 86cdc5758b3e9..4a484dedbeb5e 100644 --- a/packages/react-devtools-inline/package.json +++ b/packages/react-devtools-inline/package.json @@ -1,6 +1,6 @@ { "name": "react-devtools-inline", - "version": "6.1.0", + "version": "6.1.1", "description": "Embed react-devtools within a website", "license": "MIT", "main": "./dist/backend.js", diff --git a/packages/react-devtools-timeline/package.json b/packages/react-devtools-timeline/package.json index 5519ab339da78..9dbf65b931587 100644 --- a/packages/react-devtools-timeline/package.json +++ b/packages/react-devtools-timeline/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "react-devtools-timeline", - "version": "6.1.0", + "version": "6.1.1", "license": "MIT", "dependencies": { "@elg/speedscope": "1.9.0-a6f84db", diff --git a/packages/react-devtools/CHANGELOG.md b/packages/react-devtools/CHANGELOG.md index 4e6109a3186fd..b1a4fd9a801e0 100644 --- a/packages/react-devtools/CHANGELOG.md +++ b/packages/react-devtools/CHANGELOG.md @@ -4,6 +4,15 @@ --- +### 6.1.1 +February 7, 2025 + +* DevTools: refactor NativeStyleEditor, don't use custom cache implementation ([hoxyq](https://github.com/hoxyq) in [#32298](https://github.com/facebook/react/pull/32298)) +* DevTools: fix host component filter option title ([hoxyq](https://github.com/hoxyq) in [#32296](https://github.com/facebook/react/pull/32296)) +* chore[DevTools]: make clipboardWrite optional for chromium ([hoxyq](https://github.com/hoxyq) in [#32262](https://github.com/facebook/react/pull/32262)) + +--- + ### 6.1.0 January 16, 2025 diff --git a/packages/react-devtools/package.json b/packages/react-devtools/package.json index bc6af6588e9ec..a384b168166aa 100644 --- a/packages/react-devtools/package.json +++ b/packages/react-devtools/package.json @@ -1,6 +1,6 @@ { "name": "react-devtools", - "version": "6.1.0", + "version": "6.1.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": "6.1.0", + "react-devtools-core": "6.1.1", "update-notifier": "^2.1.0" } } From 44c3d3d665761bba86eb5c143a6eafc0ebf73263 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 7 Feb 2025 11:07:46 +0000 Subject: [PATCH 002/300] fix[react-devtools-standalone]: define missing globals (#32327) Same as what we did for `react-devtools-fusebox` in https://github.com/facebook/react/pull/32297. --- packages/react-devtools-core/webpack.standalone.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-devtools-core/webpack.standalone.js b/packages/react-devtools-core/webpack.standalone.js index 778c1fa85c627..8caadec10b070 100644 --- a/packages/react-devtools-core/webpack.standalone.js +++ b/packages/react-devtools-core/webpack.standalone.js @@ -88,6 +88,9 @@ module.exports = { __PROFILE__: false, __TEST__: NODE_ENV === 'test', __IS_NATIVE__: true, + __IS_FIREFOX__: false, + __IS_CHROME__: false, + __IS_EDGE__: false, 'process.env.DEVTOOLS_PACKAGE': `"react-devtools-core"`, 'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`, 'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null, From 8759c5c8d6aef34df576827215ff7ebaeafc79ea Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Fri, 7 Feb 2025 11:12:29 -0500 Subject: [PATCH 003/300] Ship enableFabricCompleteRootInCommitPhase (#32318) --- .../src/ReactFiberConfigFabric.js | 14 +++----------- packages/shared/ReactFeatureFlags.js | 5 ----- .../forks/ReactFeatureFlags.native-fb-dynamic.js | 1 - .../shared/forks/ReactFeatureFlags.native-fb.js | 1 - .../shared/forks/ReactFeatureFlags.native-oss.js | 1 - .../forks/ReactFeatureFlags.test-renderer.js | 1 - .../ReactFeatureFlags.test-renderer.native-fb.js | 1 - .../forks/ReactFeatureFlags.test-renderer.www.js | 1 - packages/shared/forks/ReactFeatureFlags.www.js | 1 - 9 files changed, 3 insertions(+), 23 deletions(-) diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index 59bd296138e0b..d602ab05efa81 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -57,10 +57,7 @@ import { getInspectorDataForInstance, } from './ReactNativeFiberInspector'; -import { - enableFabricCompleteRootInCommitPhase, - passChildrenWhenCloningPersistedNodes, -} from 'shared/ReactFeatureFlags'; +import {passChildrenWhenCloningPersistedNodes} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type {ReactContext} from 'shared/ReactTypes'; @@ -505,19 +502,14 @@ export function finalizeContainerChildren( container: Container, newChildren: ChildSet, ): void { - if (!enableFabricCompleteRootInCommitPhase) { - completeRoot(container.containerTag, newChildren); - } + // Noop - children will be finalized in replaceContainerChildren } export function replaceContainerChildren( container: Container, newChildren: ChildSet, ): void { - // Noop - children will be replaced in finalizeContainerChildren - if (enableFabricCompleteRootInCommitPhase) { - completeRoot(container.containerTag, newChildren); - } + completeRoot(container.containerTag, newChildren); } export {getClosestInstanceFromNode as getInstanceFromNode}; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index a94f3402de4f8..290d5ba159d68 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -92,11 +92,6 @@ export const enableHalt = __EXPERIMENTAL__; export const enableViewTransition = __EXPERIMENTAL__; -/** - * Switches the Fabric API from doing layout in commit work instead of complete work. - */ -export const enableFabricCompleteRootInCommitPhase = false; - /** * Switches Fiber creation to a simple object instead of a constructor. */ diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index cff57d26ecd95..6aaefdfa336fe 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -23,7 +23,6 @@ export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__; export const enablePersistedModeClonedFlag = __VARIANT__; export const enableShallowPropDiffing = __VARIANT__; export const passChildrenWhenCloningPersistedNodes = __VARIANT__; -export const enableFabricCompleteRootInCommitPhase = __VARIANT__; export const enableSiblingPrerendering = __VARIANT__; export const enableUseResourceEffectHook = __VARIANT__; export const enableOwnerStacks = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 31b3333966410..878d344a42a41 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any); // the exports object every time a flag is read. export const { alwaysThrottleRetries, - enableFabricCompleteRootInCommitPhase, enableHiddenSubtreeInsertionEffectCleanup, enableObjectFiber, enablePersistedModeClonedFlag, diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 84307ce62746c..a3202c0b80049 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -30,7 +30,6 @@ export const enableAsyncIterableChildren = false; export const enableCPUSuspense = false; export const enableCreateEventHandleAPI = false; export const enableDO_NOT_USE_disableStrictPassiveEffect = false; -export const enableFabricCompleteRootInCommitPhase = false; export const enableMoveBefore = true; export const enableFizzExternalRuntime = true; export const enableHalt = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 90725f666e1a1..6bc085f540b23 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -36,7 +36,6 @@ export const enableUseEffectEventHook = false; export const favorSafetyOverHydrationPerf = true; export const enableLegacyFBSupport = false; export const enableMoveBefore = false; -export const enableFabricCompleteRootInCommitPhase = false; export const enableHiddenSubtreeInsertionEffectCleanup = false; export const enableHydrationLaneScheduling = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index b4df6f248f9fd..d62a59122f82c 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -61,7 +61,6 @@ export const renameElementSymbol = false; export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; -export const enableFabricCompleteRootInCommitPhase = false; export const enableSiblingPrerendering = true; export const enableUseResourceEffectHook = true; export const enableHydrationLaneScheduling = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index a09e479f1732b..301c01a018090 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -39,7 +39,6 @@ export const favorSafetyOverHydrationPerf = true; export const enableLegacyFBSupport = false; export const enableMoveBefore = false; export const enableRenderableContext = false; -export const enableFabricCompleteRootInCommitPhase = false; export const enableHiddenSubtreeInsertionEffectCleanup = true; export const enableRetryLaneExpiration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index c84b89847fa0c..dcb77f9d84b5e 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -48,7 +48,6 @@ export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__; export const enableUpdaterTracking = __PROFILE__; -export const enableFabricCompleteRootInCommitPhase = false; export const enableSuspenseAvoidThisFallback = true; From 76e44c29110f12caff8302da624076b68547b8ef Mon Sep 17 00:00:00 2001 From: inottn Date: Sat, 8 Feb 2025 04:48:03 +0800 Subject: [PATCH 004/300] [compiler] Improve error messages for unhandled terminal and instruction kinds (#32324) ## Summary Improve the error message, as the value is currently an object instead of a string, which results in it being converted to '[object Object]'. ## How did you test this change? Already tested locally. --- .../src/ReactiveScopes/PrintReactiveFunction.ts | 5 ++++- .../src/TypeInference/InferTypes.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts index b5aa44ead095d..259fc06486888 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts @@ -394,7 +394,10 @@ function writeTerminal(writer: Writer, terminal: ReactiveTerminal): void { break; } default: - assertExhaustive(terminal, `Unhandled terminal ${terminal}`); + assertExhaustive( + terminal, + `Unhandled terminal kind \`${(terminal as any).kind}\``, + ); } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 0270723c3e863..44bf539e847bb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -420,7 +420,10 @@ function* generateInstructionTypes( break; } default: - assertExhaustive(value, `Unhandled instruction value kind: ${value}`); + assertExhaustive( + value, + `Unhandled instruction value kind: ${(value as any).kind}`, + ); } } From 989b0cccc215e5c4692552b0cc01d23938dcf99b Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 7 Feb 2025 16:04:46 -0500 Subject: [PATCH 005/300] [compiler] Add simple walltime measurement (#32331) Adds a new Timing logger event to the compiler which currently only records the walltime of running the compiler from the time the babel plugin's Program visitor enters to the time it exits. To enable, run the compiler with `ENABLE_REACT_COMPILER_TIMINGS=1 ...` or `export ENABLE_REACT_COMPILER_TIMINGS=1` to set it by default. --- .../src/Babel/BabelPlugin.ts | 93 +++++++++++++------ .../src/Entrypoint/Options.ts | 4 + 2 files changed, 67 insertions(+), 30 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts index c648c66043980..aa49bda22b27e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts @@ -6,12 +6,15 @@ */ import type * as BabelCore from '@babel/core'; -import {compileProgram, parsePluginOptions} from '../Entrypoint'; +import {compileProgram, Logger, parsePluginOptions} from '../Entrypoint'; import { injectReanimatedFlag, pipelineUsesReanimatedPlugin, } from '../Entrypoint/Reanimated'; +const ENABLE_REACT_COMPILER_TIMINGS = + process.env['ENABLE_REACT_COMPILER_TIMINGS'] === '1'; + /* * The React Forget Babel Plugin * @param {*} _babel @@ -28,35 +31,65 @@ export default function BabelPluginReactCompiler( * prior to B, if A does not have a Program visitor and B does, B will run first. We always * want Forget to run true to source as possible. */ - Program(prog, pass): void { - let opts = parsePluginOptions(pass.opts); - const isDev = - (typeof __DEV__ !== 'undefined' && __DEV__ === true) || - process.env['NODE_ENV'] === 'development'; - if ( - opts.enableReanimatedCheck === true && - pipelineUsesReanimatedPlugin(pass.file.opts.plugins) - ) { - opts = injectReanimatedFlag(opts); - } - if ( - opts.environment.enableResetCacheOnSourceFileChanges !== false && - isDev - ) { - opts = { - ...opts, - environment: { - ...opts.environment, - enableResetCacheOnSourceFileChanges: true, - }, - }; - } - compileProgram(prog, { - opts, - filename: pass.filename ?? null, - comments: pass.file.ast.comments ?? [], - code: pass.file.code, - }); + Program: { + enter(prog, pass): void { + const filename = pass.filename ?? 'unknown'; + if (ENABLE_REACT_COMPILER_TIMINGS === true) { + performance.mark(`${filename}:start`, { + detail: 'BabelPlugin:Program:start', + }); + } + let opts = parsePluginOptions(pass.opts); + const isDev = + (typeof __DEV__ !== 'undefined' && __DEV__ === true) || + process.env['NODE_ENV'] === 'development'; + if ( + opts.enableReanimatedCheck === true && + pipelineUsesReanimatedPlugin(pass.file.opts.plugins) + ) { + opts = injectReanimatedFlag(opts); + } + if ( + opts.environment.enableResetCacheOnSourceFileChanges !== false && + isDev + ) { + opts = { + ...opts, + environment: { + ...opts.environment, + enableResetCacheOnSourceFileChanges: true, + }, + }; + } + compileProgram(prog, { + opts, + filename: pass.filename ?? null, + comments: pass.file.ast.comments ?? [], + code: pass.file.code, + }); + if (ENABLE_REACT_COMPILER_TIMINGS === true) { + performance.mark(`${filename}:end`, { + detail: 'BabelPlugin:Program:end', + }); + } + }, + exit(_, pass): void { + if (ENABLE_REACT_COMPILER_TIMINGS === true) { + const filename = pass.filename ?? 'unknown'; + const measurement = performance.measure(filename, { + start: `${filename}:start`, + end: `${filename}:end`, + detail: 'BabelPlugin:Program', + }); + if ('logger' in pass.opts && pass.opts.logger != null) { + const logger: Logger = pass.opts.logger as Logger; + logger.logEvent(filename, { + kind: 'Timing', + measurement, + }); + } + } + }, }, }, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index fb951d25c5398..35c2c4134eb44 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -206,6 +206,10 @@ export type LoggerEvent = kind: 'PipelineError'; fnLoc: t.SourceLocation | null; data: string; + } + | { + kind: 'Timing'; + measurement: PerformanceMeasure; }; export type Logger = { From bc78de3a521f65f756579fee0996ec5c81f3af73 Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 7 Feb 2025 16:34:26 -0500 Subject: [PATCH 006/300] [ci] Use 'opened' event for discord notifications (#32332) We don't need to wait for it to be labeled now that we have the shared maintainer check workflow. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32332). * #32333 * __->__ #32332 --- .github/workflows/compiler_discord_notify.yml | 4 ++-- .github/workflows/runtime_discord_notify.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index 1ab9c1b1aff5b..da85190dd2127 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -2,7 +2,7 @@ name: (Compiler) Discord Notify on: pull_request_target: - types: [labeled] + types: [opened] paths: - compiler/** - .github/workflows/compiler_**.yml @@ -14,7 +14,7 @@ jobs: actor: ${{ github.event.pull_request.user.login }} notify: - if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' && github.event.label.name == 'React Core Team' }} + if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }} needs: check_maintainer runs-on: ubuntu-latest steps: diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index 59a1078b499e8..5a138ce71b13b 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -2,7 +2,7 @@ name: (Runtime) Discord Notify on: pull_request_target: - types: [labeled] + types: [opened] paths-ignore: - compiler/** - .github/workflows/compiler_**.yml @@ -14,7 +14,7 @@ jobs: actor: ${{ github.event.pull_request.user.login }} notify: - if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' && github.event.label.name == 'React Core Team' }} + if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }} needs: check_maintainer runs-on: ubuntu-latest steps: From 569c3b28ee968df58ff3c55be9a8efa2ee72fc82 Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 7 Feb 2025 16:39:37 -0500 Subject: [PATCH 007/300] [ci] Combine sizebot jobs (#32333) There's no real reason to have 2 jobs for sizebot. It's more of a historical artifact from before the GH migration. Merging them should require one less worker needing to be provisioned and some of the extra overhead --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32333). * __->__ #32333 * #32332 --- .github/workflows/runtime_build_and_test.yml | 47 +++----------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 9a592780b17c4..12e341a86fd96 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -585,9 +585,10 @@ jobs: RELEASE_CHANNEL: experimental # ----- SIZEBOT ----- - download_base_build_for_sizebot: - if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' }} - name: Download base build for sizebot + sizebot: + if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }} + name: Run sizebot + needs: [build_and_lint] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -603,16 +604,14 @@ jobs: id: node_modules with: path: "**/node_modules" - key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - - run: yarn install --frozen-lockfile - run: yarn install --frozen-lockfile working-directory: scripts/release - name: Download artifacts for base revision run: | - git fetch origin main - GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse origin/main) + GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }}) mv ./build ./base-build # TODO: The `download-experimental-build` script copies the npm # packages into the `node_modules` directory. This is a historical @@ -620,33 +619,8 @@ jobs: # don't exist. - name: Delete extraneous files run: rm -rf ./base-build/node_modules - - name: Display structure of base-build + - name: Display structure of base-build from origin/main run: ls -R base-build - - name: Archive base-build - uses: actions/upload-artifact@v4 - with: - name: base-build - path: base-build - - sizebot: - name: Run sizebot - needs: [build_and_lint, download_base_build_for_sizebot] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - cache-dependency-path: yarn.lock - - name: Restore cached node_modules - uses: actions/cache@v4 - id: node_modules - with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -662,13 +636,6 @@ jobs: node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js - name: Display structure of build for PR run: ls -R build - - name: Restore archived base-build from origin/main - uses: actions/download-artifact@v4 - with: - name: base-build - path: base-build - - name: Display structure of base-build from origin/main - run: ls -R base-build - run: echo ${{ github.sha }} >> build/COMMIT_SHA - run: node ./scripts/tasks/danger - name: Archive sizebot results From 7588b6b291b17fb2130244115b07e9945a864626 Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 7 Feb 2025 17:49:09 -0500 Subject: [PATCH 008/300] [ci] Disallow PRs against builds branch (#32335) Our internal build infra relies on a 1:1 mapping between `main` and the 2 build branches. Directly committing changes to those branches breaks that infra. Adds a simple workflow to leave a comment and decline the PR. --- .../shared_close_direct_sync_branch_prs.yml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/shared_close_direct_sync_branch_prs.yml diff --git a/.github/workflows/shared_close_direct_sync_branch_prs.yml b/.github/workflows/shared_close_direct_sync_branch_prs.yml new file mode 100644 index 0000000000000..1f5dd5b1a465b --- /dev/null +++ b/.github/workflows/shared_close_direct_sync_branch_prs.yml @@ -0,0 +1,37 @@ +name: (Shared) Close Direct Sync Branch PRs + +on: + pull_request: + branches: + - builds/facebook-* + +env: + TZ: /usr/share/zoneinfo/America/Los_Angeles + # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 + +jobs: + close_pr: + runs-on: ubuntu-latest + steps: + - name: Close PR + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const pullNumber = ${{ github.event.number }}; + + await github.rest.pulls.createReview({ + owner, + repo, + pull_number: pullNumber, + body: 'Do not land changes to `${{ github.event.pull_request.base.ref }}`. Please re-open your PR targeting `main` instead.', + event: 'REQUEST_CHANGES' + }); + await github.rest.pulls.update({ + owner, + repo, + pull_number: pullNumber, + state: 'closed' + }); From 062fb31155e42b6997a35b97180055814471620c Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 7 Feb 2025 18:01:53 -0500 Subject: [PATCH 009/300] [ci] Fix typo (#32337) Oops. --- .github/workflows/shared_close_direct_sync_branch_prs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/shared_close_direct_sync_branch_prs.yml b/.github/workflows/shared_close_direct_sync_branch_prs.yml index 1f5dd5b1a465b..7575c0e913c7e 100644 --- a/.github/workflows/shared_close_direct_sync_branch_prs.yml +++ b/.github/workflows/shared_close_direct_sync_branch_prs.yml @@ -3,7 +3,7 @@ name: (Shared) Close Direct Sync Branch PRs on: pull_request: branches: - - builds/facebook-* + - 'builds/facebook-**' env: TZ: /usr/share/zoneinfo/America/Los_Angeles From 594ea533d39f71fa16503c0e5d5e146e9278647e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Sun, 9 Feb 2025 15:01:28 -0500 Subject: [PATCH 010/300] Remove useActionState Hook export from experimental react-server condition (#32342) This Hook is not available in RSC environments. This is already the case in stable but not in experimental for some reason. Probably an oversight. --- packages/react/src/ReactServer.experimental.development.js | 2 -- packages/react/src/ReactServer.experimental.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/react/src/ReactServer.experimental.development.js b/packages/react/src/ReactServer.experimental.development.js index fc22dda4e7896..0176e0e94f7cc 100644 --- a/packages/react/src/ReactServer.experimental.development.js +++ b/packages/react/src/ReactServer.experimental.development.js @@ -30,7 +30,6 @@ import { useCallback, useDebugValue, useMemo, - useActionState, getCacheForType, } from './ReactHooks'; import {forwardRef} from './ReactForwardRef'; @@ -78,7 +77,6 @@ export { useCallback, useDebugValue, useMemo, - useActionState, version, // Experimental REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList, diff --git a/packages/react/src/ReactServer.experimental.js b/packages/react/src/ReactServer.experimental.js index 758c3d8b89ae0..c687bff7213b4 100644 --- a/packages/react/src/ReactServer.experimental.js +++ b/packages/react/src/ReactServer.experimental.js @@ -30,7 +30,6 @@ import { useCallback, useDebugValue, useMemo, - useActionState, getCacheForType, } from './ReactHooks'; import {forwardRef} from './ReactForwardRef'; @@ -77,7 +76,6 @@ export { useCallback, useDebugValue, useMemo, - useActionState, version, // Experimental REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList, From 93b58361d9c9632acdda76eb8a1a582d1ff9701a Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Sun, 9 Feb 2025 23:55:50 +0100 Subject: [PATCH 011/300] Trigger Discord notification when draft PR is set to "ready for review" (#32344) Follow-up for #32332. The Discord webhook seems to ignore draft PRs, which is a good thing. But when a draft PR is then later set to "ready for review" we do want to send another notification that should not be filtered out. --- .github/workflows/runtime_discord_notify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index 5a138ce71b13b..e41b1c56405a7 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -2,7 +2,7 @@ name: (Runtime) Discord Notify on: pull_request_target: - types: [opened] + types: [opened, ready_for_review] paths-ignore: - compiler/** - .github/workflows/compiler_**.yml From 3dd2c627707fea4f45fd8e5cc583036a72e3f77b Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 10 Feb 2025 14:08:44 -0500 Subject: [PATCH 012/300] [react-native] fix divergence in synced code (#32348) Alternative to #32334 --- packages/react-native-renderer/src/ReactNativeTypes.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 55ae2cc1b0a0e..07030a6b7f6c7 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -10,6 +10,7 @@ */ import type {ElementRef, ElementType, MixedElement} from 'react'; +import {type PublicRootInstance} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; export type MeasureOnSuccessCallback = ( x: number, @@ -231,7 +232,6 @@ export opaque type Node = mixed; export opaque type InternalInstanceHandle = mixed; type PublicInstance = mixed; type PublicTextInstance = mixed; -export opaque type PublicRootInstance = mixed; export type ReactFabricType = { findHostInstance_DEPRECATED( @@ -261,6 +261,7 @@ export type ReactFabricType = { getPublicInstanceFromInternalInstanceHandle( internalInstanceHandle: InternalInstanceHandle, ): PublicInstance | PublicTextInstance | null, + getPublicInstanceFromRootTag(rootTag: number): PublicRootInstance | null, ... }; From cd90a4d8c0d5dbaa8ab61e839b112b1518d5058f Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 10 Feb 2025 15:46:47 -0500 Subject: [PATCH 013/300] [react-native] Suppress Flow nonstrict-import check in ReactNativeTypes (#32349) Summary: Unblock internal sync. Test Plan: Reviewers: Subscribers: Tasks: Tags: --- packages/react-native-renderer/src/ReactNativeTypes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 07030a6b7f6c7..8bce919d385e9 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -10,6 +10,7 @@ */ import type {ElementRef, ElementType, MixedElement} from 'react'; +// $FlowFixMe[nonstrict-import] TODO(@rubennorte) import {type PublicRootInstance} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; export type MeasureOnSuccessCallback = ( From 0a7dc1b1c714e74a1594c712d2317969e6421685 Mon Sep 17 00:00:00 2001 From: Brendan Abbott Date: Wed, 12 Feb 2025 04:14:43 +1000 Subject: [PATCH 014/300] [devtools] Introduce REACT_DEVTOOLS_PORT for the standalone react-devtools (#30767) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR attempts to make running the React DevTools a little friendlier in projects that are not completely React. At the moment, running the DevTools with `npx react-devtools` will default to the port to use the `PORT` env variable otherwise it'll fall back to `8097`. `PORT` is a common env variable, so we can get into this strange situation where the a Rails server (eg Puma) is using `PORT`, and then the React DevTools attempts to boot using the same `PORT`. This PR introduces a dedicated env variable, `REACT_DEVTOOLS_PORT` to assist in this scenario. ## How did you test this change? I'm using fish shell, so I did the following, please let me know if there's a better way: ```sh cd packages/react-devtools set -x PORT 1000 set -x REACT_DEVTOOLS_PORT 2000 node bin.js ``` We can see in the UI that it's listening on `2000`. Without this PR, it'd listen on `1000`: ![Screenshot 2024-08-21 at 10 45 42 AM](https://github.com/user-attachments/assets/a5c7590c-1b54-4ac8-9a8b-8eb66ff67cfb) --- packages/react-devtools/README.md | 2 +- packages/react-devtools/preload.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-devtools/README.md b/packages/react-devtools/README.md index 149ec97f8af57..8c436c1fb2b4d 100644 --- a/packages/react-devtools/README.md +++ b/packages/react-devtools/README.md @@ -87,7 +87,7 @@ This will ensure the developer tools are connected. **Don’t forget to remove i ## Advanced -By default DevTools listen to port `8097` on `localhost`. If you need to customize host, port, or other settings, see the `react-devtools-core` package instead. +By default DevTools listen to port `8097` on `localhost`. The port can be modified by setting the `REACT_DEVTOOLS_PORT` environment variable. If you need to further customize host, port, or other settings, see the `react-devtools-core` package instead. ## FAQ diff --git a/packages/react-devtools/preload.js b/packages/react-devtools/preload.js index 634cffc635a40..33a9e3d6dd46e 100644 --- a/packages/react-devtools/preload.js +++ b/packages/react-devtools/preload.js @@ -35,7 +35,7 @@ contextBridge.exposeInMainWorld('api', { } const host = process.env.HOST || 'localhost'; const protocol = useHttps ? 'https' : 'http'; - const port = +process.env.PORT || 8097; + const port = +process.env.REACT_DEVTOOLS_PORT || +process.env.PORT || 8097; return {options, useHttps, host, protocol, port}; }, }); From 899e3d1297ec15a5aa8d73e2f1bd478918090a12 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 11 Feb 2025 13:52:25 -0500 Subject: [PATCH 015/300] [crud] Narrow resource type (#32203) Small refactor to the `resource` type to narrow it to an arbitrary object or void/null instead of the top type. This makes the overload on useEffect simpler since the return type of create is no longer widened to the top type when we merge their definitions. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32203). * #32206 * #32205 * #32204 * __->__ #32203 --- .../react-debug-tools/src/ReactDebugHooks.js | 6 +- .../src/ReactFiberCallUserSpace.js | 2 +- .../src/ReactFiberCommitEffects.js | 8 +- .../react-reconciler/src/ReactFiberHooks.js | 78 +++++++++---------- .../src/ReactInternalTypes.js | 6 +- packages/react/src/ReactHooks.js | 6 +- 6 files changed, 54 insertions(+), 52 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index f30489b7f655f..9c310723b2605 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -739,11 +739,11 @@ function useHostTransitionStatus(): TransitionStatus { } function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { nextHook(); hookLog.push({ diff --git a/packages/react-reconciler/src/ReactFiberCallUserSpace.js b/packages/react-reconciler/src/ReactFiberCallUserSpace.js index a1b86c7560a2a..dd7beecd93582 100644 --- a/packages/react-reconciler/src/ReactFiberCallUserSpace.js +++ b/packages/react-reconciler/src/ReactFiberCallUserSpace.js @@ -183,7 +183,7 @@ export const callComponentWillUnmountInDEV: ( const callCreate = { 'react-stack-bottom-frame': function ( effect: Effect, - ): (() => void) | mixed | void { + ): (() => void) | {...} | void | null { if (!enableUseResourceEffectHook) { if (effect.resourceKind != null) { if (__DEV__) { diff --git a/packages/react-reconciler/src/ReactFiberCommitEffects.js b/packages/react-reconciler/src/ReactFiberCommitEffects.js index 6873bdf9e7a63..a8e08721eed5f 100644 --- a/packages/react-reconciler/src/ReactFiberCommitEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitEffects.js @@ -274,6 +274,7 @@ export function commitHookEffectListMount( addendum = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).'; + // $FlowFixMe (@poteto) this check is safe on arbitrary non-null/void objects } else if (typeof destroy.then === 'function') { addendum = '\n\nIt looks like you wrote ' + @@ -1036,10 +1037,10 @@ function safelyCallDestroy( function safelyCallDestroyWithResource( current: Fiber, nearestMountedAncestor: Fiber | null, - destroy: mixed => void, - resource: mixed, + destroy: ({...}) => void, + resource: {...}, ) { - const destroy_ = resource == null ? destroy : destroy.bind(null, resource); + const destroy_ = destroy.bind(null, resource); if (__DEV__) { runWithFiberInDEV( current, @@ -1050,6 +1051,7 @@ function safelyCallDestroyWithResource( ); } else { try { + // $FlowFixMe(incompatible-call) Already bound to resource destroy_(); } catch (error) { captureCommitPhaseError(current, nearestMountedAncestor, error); diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index f3a581a97476a..9d97710003317 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -205,8 +205,8 @@ export type Hook = { // the additional memory and we can follow up with performance // optimizations later. type EffectInstance = { - resource: mixed, - destroy: void | (() => void) | ((resource: mixed) => void), + resource: {...} | void | null, + destroy: void | (() => void) | ((resource: {...} | void | null) => void), }; export const ResourceEffectIdentityKind: 0 = 0; @@ -229,7 +229,7 @@ export type ResourceEffectIdentity = { resourceKind: typeof ResourceEffectIdentityKind, tag: HookFlags, inst: EffectInstance, - create: () => mixed, + create: () => {...} | void | null, deps: Array | void | null, next: Effect, }; @@ -237,7 +237,7 @@ export type ResourceEffectUpdate = { resourceKind: typeof ResourceEffectUpdateKind, tag: HookFlags, inst: EffectInstance, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, deps: Array | void | null, next: Effect, identity: ResourceEffectIdentity, @@ -2540,9 +2540,9 @@ function pushResourceEffect( identityTag: HookFlags, updateTag: HookFlags, inst: EffectInstance, - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, ): Effect { const effectIdentity: ResourceEffectIdentity = { @@ -2694,11 +2694,11 @@ function updateEffect( } function mountResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { if ( __DEV__ && @@ -2730,11 +2730,11 @@ function mountResourceEffect( function mountResourceEffectImpl( fiberFlags: Flags, hookFlags: HookFlags, - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { const hook = mountWorkInProgressHook(); currentlyRenderingFiber.flags |= fiberFlags; @@ -2752,11 +2752,11 @@ function mountResourceEffectImpl( } function updateResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { updateResourceEffectImpl( PassiveEffect, @@ -2772,11 +2772,11 @@ function updateResourceEffect( function updateResourceEffectImpl( fiberFlags: Flags, hookFlags: HookFlags, - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { const hook = updateWorkInProgressHook(); const effect: Effect = hook.memoizedState; @@ -4245,11 +4245,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (HooksDispatcherOnMountInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useResourceEffect'; mountHookTypesDev(); @@ -4433,11 +4433,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useResourceEffect'; updateHookTypesDev(); @@ -4620,11 +4620,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (HooksDispatcherOnUpdateInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { currentHookNameInDev = 'useResourceEffect'; updateHookTypesDev(); @@ -4807,11 +4807,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (HooksDispatcherOnRerenderInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { currentHookNameInDev = 'useResourceEffect'; updateHookTypesDev(); @@ -5019,11 +5019,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useResourceEffect'; warnInvalidHookAccess(); @@ -5232,11 +5232,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { currentHookNameInDev = 'useResourceEffect'; warnInvalidHookAccess(); @@ -5445,11 +5445,11 @@ if (__DEV__) { if (enableUseResourceEffectHook) { (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useResourceEffect = function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) { currentHookNameInDev = 'useResourceEffect'; warnInvalidHookAccess(); diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 0c5504cab468e..e31b7cefd1ed7 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -398,11 +398,11 @@ export type Dispatcher = { useEffectEvent?: ) => mixed>(callback: F) => F, // TODO: Non-nullable once `enableUseResourceEffectHook` is on everywhere. useResourceEffect?: ( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ) => void, useInsertionEffect( create: () => (() => void) | void, diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 32f49268880f0..ff45b5415c416 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -202,11 +202,11 @@ export function useEffectEvent) => mixed>( } export function useResourceEffect( - create: () => mixed, + create: () => {...} | void | null, createDeps: Array | void | null, - update: ((resource: mixed) => void) | void, + update: ((resource: {...} | void | null) => void) | void, updateDeps: Array | void | null, - destroy: ((resource: mixed) => void) | void, + destroy: ((resource: {...} | void | null) => void) | void, ): void { if (!enableUseResourceEffectHook) { throw new Error('Not implemented.'); From 0461c0d8a49730d1c8ebca2071d9bb7adfc8ac92 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 11 Feb 2025 14:05:50 -0500 Subject: [PATCH 016/300] [crud] Rename useResourceEffect flag (#32204) Rename the flag in preparation for the overload. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32204). * #32206 * #32205 * __->__ #32204 --- .../ReactDOMServerIntegrationHooks-test.js | 2 +- .../src/ReactFiberCallUserSpace.js | 6 ++-- .../src/ReactFiberCommitEffects.js | 16 +++++----- .../react-reconciler/src/ReactFiberHooks.js | 24 +++++++------- .../src/ReactInternalTypes.js | 2 +- .../ReactHooksWithNoopRenderer-test.js | 32 +++++++++---------- packages/react-server/src/ReactFizzHooks.js | 4 +-- packages/react/src/ReactClient.js | 4 +-- packages/react/src/ReactHooks.js | 4 +-- packages/shared/ReactFeatureFlags.js | 2 +- .../ReactFeatureFlags.native-fb-dynamic.js | 2 +- .../forks/ReactFeatureFlags.native-fb.js | 2 +- .../forks/ReactFeatureFlags.native-oss.js | 2 +- .../forks/ReactFeatureFlags.test-renderer.js | 2 +- ...actFeatureFlags.test-renderer.native-fb.js | 2 +- .../ReactFeatureFlags.test-renderer.www.js | 2 +- .../forks/ReactFeatureFlags.www-dynamic.js | 2 +- .../shared/forks/ReactFeatureFlags.www.js | 2 +- 18 files changed, 56 insertions(+), 56 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js index b79e59ad004dd..bce830ddf0647 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js @@ -657,7 +657,7 @@ describe('ReactDOMServerHooks', () => { describe('useResourceEffect', () => { gate(flags => { - if (flags.enableUseResourceEffectHook) { + if (flags.enableUseEffectCRUDOverload) { const yields = []; itRenders( 'should ignore resource effects on the server', diff --git a/packages/react-reconciler/src/ReactFiberCallUserSpace.js b/packages/react-reconciler/src/ReactFiberCallUserSpace.js index dd7beecd93582..25ede645b5e7f 100644 --- a/packages/react-reconciler/src/ReactFiberCallUserSpace.js +++ b/packages/react-reconciler/src/ReactFiberCallUserSpace.js @@ -18,7 +18,7 @@ import { ResourceEffectIdentityKind, ResourceEffectUpdateKind, } from './ReactFiberHooks'; -import {enableUseResourceEffectHook} from 'shared/ReactFeatureFlags'; +import {enableUseEffectCRUDOverload} from 'shared/ReactFeatureFlags'; // These indirections exists so we can exclude its stack frame in DEV (and anything below it). // TODO: Consider marking the whole bundle instead of these boundaries. @@ -184,11 +184,11 @@ const callCreate = { 'react-stack-bottom-frame': function ( effect: Effect, ): (() => void) | {...} | void | null { - if (!enableUseResourceEffectHook) { + if (!enableUseEffectCRUDOverload) { if (effect.resourceKind != null) { if (__DEV__) { console.error( - 'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, ' + + 'Expected only SimpleEffects when enableUseEffectCRUDOverload is disabled, ' + 'got %s', effect.resourceKind, ); diff --git a/packages/react-reconciler/src/ReactFiberCommitEffects.js b/packages/react-reconciler/src/ReactFiberCommitEffects.js index a8e08721eed5f..29e5de2817201 100644 --- a/packages/react-reconciler/src/ReactFiberCommitEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitEffects.js @@ -22,7 +22,7 @@ import { enableProfilerCommitHooks, enableProfilerNestedUpdatePhase, enableSchedulingProfiler, - enableUseResourceEffectHook, + enableUseEffectCRUDOverload, enableViewTransition, } from 'shared/ReactFeatureFlags'; import { @@ -160,7 +160,7 @@ export function commitHookEffectListMount( // Mount let destroy; - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { if (effect.resourceKind === ResourceEffectIdentityKind) { if (__DEV__) { effect.inst.resource = runWithFiberInDEV( @@ -200,7 +200,7 @@ export function commitHookEffectListMount( if ((flags & HookInsertion) !== NoHookEffect) { setIsRunningInsertionEffect(true); } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { if (effect.resourceKind == null) { destroy = runWithFiberInDEV( finishedWork, @@ -219,7 +219,7 @@ export function commitHookEffectListMount( setIsRunningInsertionEffect(false); } } else { - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { if (effect.resourceKind == null) { const create = effect.create; const inst = effect.inst; @@ -230,7 +230,7 @@ export function commitHookEffectListMount( if (effect.resourceKind != null) { if (__DEV__) { console.error( - 'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, ' + + 'Expected only SimpleEffects when enableUseEffectCRUDOverload is disabled, ' + 'got %s', effect.resourceKind, ); @@ -262,7 +262,7 @@ export function commitHookEffectListMount( } else if ((effect.tag & HookInsertion) !== NoFlags) { hookName = 'useInsertionEffect'; } else if ( - enableUseResourceEffectHook && + enableUseEffectCRUDOverload && effect.resourceKind != null ) { hookName = 'useResourceEffect'; @@ -338,7 +338,7 @@ export function commitHookEffectListUnmount( const inst = effect.inst; const destroy = inst.destroy; if (destroy !== undefined) { - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { if (effect.resourceKind == null) { inst.destroy = undefined; } @@ -358,7 +358,7 @@ export function commitHookEffectListUnmount( setIsRunningInsertionEffect(true); } } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { if ( effect.resourceKind === ResourceEffectIdentityKind && effect.inst.resource != null diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 9d97710003317..87f601dae597d 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -38,7 +38,7 @@ import { enableSchedulingProfiler, enableTransitionTracing, enableUseEffectEventHook, - enableUseResourceEffectHook, + enableUseEffectCRUDOverload, enableLegacyCache, disableLegacyMode, enableNoCloningMemoCache, @@ -3938,7 +3938,7 @@ export const ContextOnlyDispatcher: Dispatcher = { if (enableUseEffectEventHook) { (ContextOnlyDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError; } -if (enableUseResourceEffectHook) { +if (enableUseEffectCRUDOverload) { (ContextOnlyDispatcher: Dispatcher).useResourceEffect = throwInvalidHookError; } @@ -3971,7 +3971,7 @@ const HooksDispatcherOnMount: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnMount: Dispatcher).useEffectEvent = mountEvent; } -if (enableUseResourceEffectHook) { +if (enableUseEffectCRUDOverload) { (HooksDispatcherOnMount: Dispatcher).useResourceEffect = mountResourceEffect; } @@ -4004,7 +4004,7 @@ const HooksDispatcherOnUpdate: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnUpdate: Dispatcher).useEffectEvent = updateEvent; } -if (enableUseResourceEffectHook) { +if (enableUseEffectCRUDOverload) { (HooksDispatcherOnUpdate: Dispatcher).useResourceEffect = updateResourceEffect; } @@ -4038,7 +4038,7 @@ const HooksDispatcherOnRerender: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnRerender: Dispatcher).useEffectEvent = updateEvent; } -if (enableUseResourceEffectHook) { +if (enableUseEffectCRUDOverload) { (HooksDispatcherOnRerender: Dispatcher).useResourceEffect = updateResourceEffect; } @@ -4242,7 +4242,7 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (HooksDispatcherOnMountInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, @@ -4430,7 +4430,7 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, @@ -4617,7 +4617,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (HooksDispatcherOnUpdateInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, @@ -4804,7 +4804,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (HooksDispatcherOnRerenderInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, @@ -5016,7 +5016,7 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, @@ -5229,7 +5229,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, @@ -5442,7 +5442,7 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseResourceEffectHook) { + if (enableUseEffectCRUDOverload) { (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useResourceEffect = function useResourceEffect( create: () => {...} | void | null, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index e31b7cefd1ed7..9ac0680fb6279 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -396,7 +396,7 @@ export type Dispatcher = { ): void, // TODO: Non-nullable once `enableUseEffectEventHook` is on everywhere. useEffectEvent?: ) => mixed>(callback: F) => F, - // TODO: Non-nullable once `enableUseResourceEffectHook` is on everywhere. + // TODO: Non-nullable once `enableUseEffectCRUDOverload` is on everywhere. useResourceEffect?: ( create: () => {...} | void | null, createDeps: Array | void | null, diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index e4febdb3e28f1..27bff1e0806c4 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -3311,7 +3311,7 @@ describe('ReactHooksWithNoopRenderer', () => { }); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload describe('useResourceEffect', () => { class Resource { isDeleted: false; @@ -3333,12 +3333,12 @@ describe('ReactHooksWithNoopRenderer', () => { } } - // @gate !enableUseResourceEffectHook + // @gate !enableUseEffectCRUDOverload it('is null when flag is disabled', async () => { expect(useResourceEffect).toBeUndefined(); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('validates create return value', async () => { function App({id}) { useResourceEffect(() => { @@ -3359,7 +3359,7 @@ describe('ReactHooksWithNoopRenderer', () => { ); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('validates non-empty update deps', async () => { function App({id}) { useResourceEffect( @@ -3386,7 +3386,7 @@ describe('ReactHooksWithNoopRenderer', () => { ]); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('simple mount and update', async () => { function App({id, username}) { const opts = useMemo(() => { @@ -3443,7 +3443,7 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['destroy(2, Jack)']); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('simple mount with no update', async () => { function App({id, username}) { const opts = useMemo(() => { @@ -3480,7 +3480,7 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['destroy(1, Jack)']); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('calls update on every render if no deps are specified', async () => { function App({id, username}) { const opts = useMemo(() => { @@ -3523,7 +3523,7 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['update(2, Lauren)']); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('does not unmount previous useResourceEffect between updates', async () => { function App({id}) { useResourceEffect( @@ -3562,7 +3562,7 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['update(0)']); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('unmounts only on deletion', async () => { function App({id}) { useResourceEffect( @@ -3596,7 +3596,7 @@ describe('ReactHooksWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('unmounts on deletion', async () => { function Wrapper(props) { return ; @@ -3650,7 +3650,7 @@ describe('ReactHooksWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('handles errors in create on mount', async () => { function App({id}) { useResourceEffect( @@ -3700,7 +3700,7 @@ describe('ReactHooksWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('handles errors in create on update', async () => { function App({id}) { useResourceEffect( @@ -3744,7 +3744,7 @@ describe('ReactHooksWithNoopRenderer', () => { }).rejects.toThrow('Oops error!'); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('handles errors in destroy on update', async () => { function App({id, username}) { const opts = useMemo(() => { @@ -3800,7 +3800,7 @@ describe('ReactHooksWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); - // @gate enableUseResourceEffectHook && enableActivity + // @gate enableUseEffectCRUDOverload && enableActivity it('composes with activity', async () => { function App({id, username}) { const opts = useMemo(() => { @@ -3873,7 +3873,7 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['destroy(0, Lauren)']); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('composes with suspense', async () => { function TextBox({text}) { return ; @@ -3991,7 +3991,7 @@ describe('ReactHooksWithNoopRenderer', () => { ); }); - // @gate enableUseResourceEffectHook + // @gate enableUseEffectCRUDOverload it('composes with other kinds of effects', async () => { let rerender; function App({id, username}) { diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 0db0b00b3bdee..63e8576ca788c 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -40,7 +40,7 @@ import {createFastHash} from './ReactServerStreamConfig'; import { enableUseEffectEventHook, - enableUseResourceEffectHook, + enableUseEffectCRUDOverload, } from 'shared/ReactFeatureFlags'; import is from 'shared/objectIs'; import { @@ -866,7 +866,7 @@ export const HooksDispatcher: Dispatcher = supportsClientAPIs if (enableUseEffectEventHook) { HooksDispatcher.useEffectEvent = useEffectEvent; } -if (enableUseResourceEffectHook) { +if (enableUseEffectCRUDOverload) { HooksDispatcher.useResourceEffect = supportsClientAPIs ? noop : clientHookNotSupported; diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index f633c7617d275..2bacbc85676fd 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -65,7 +65,7 @@ import {addTransitionType} from './ReactTransitionType'; import {act} from './ReactAct'; import {captureOwnerStack} from './ReactOwnerStack'; import * as ReactCompilerRuntime from './ReactCompilerRuntime'; -import {enableUseResourceEffectHook} from 'shared/ReactFeatureFlags'; +import {enableUseEffectCRUDOverload} from 'shared/ReactFeatureFlags'; const Children = { map, @@ -134,4 +134,4 @@ export { }; export const experimental_useResourceEffect: typeof useResourceEffect | void = - enableUseResourceEffectHook ? useResourceEffect : undefined; + enableUseEffectCRUDOverload ? useResourceEffect : undefined; diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index ff45b5415c416..677e0d705b8aa 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -18,7 +18,7 @@ import {REACT_CONSUMER_TYPE} from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {enableUseResourceEffectHook} from 'shared/ReactFeatureFlags'; +import {enableUseEffectCRUDOverload} from 'shared/ReactFeatureFlags'; type BasicStateAction = (S => S) | S; type Dispatch = A => void; @@ -208,7 +208,7 @@ export function useResourceEffect( updateDeps: Array | void | null, destroy: ((resource: {...} | void | null) => void) | void, ): void { - if (!enableUseResourceEffectHook) { + if (!enableUseEffectCRUDOverload) { throw new Error('Not implemented.'); } const dispatcher = resolveDispatcher(); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 290d5ba159d68..1cb1cb4cf7dc0 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -150,7 +150,7 @@ export const enableInfiniteRenderLoopDetection = false; /** * Experimental new hook for better managing resources in effects. */ -export const enableUseResourceEffectHook = false; +export const enableUseEffectCRUDOverload = false; export const enableFastAddPropertiesInDiffing = true; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 6aaefdfa336fe..001b3d64c06dc 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -24,7 +24,7 @@ export const enablePersistedModeClonedFlag = __VARIANT__; export const enableShallowPropDiffing = __VARIANT__; export const passChildrenWhenCloningPersistedNodes = __VARIANT__; export const enableSiblingPrerendering = __VARIANT__; -export const enableUseResourceEffectHook = __VARIANT__; +export const enableUseEffectCRUDOverload = __VARIANT__; export const enableOwnerStacks = __VARIANT__; export const enableRemoveConsolePatches = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 878d344a42a41..6e82185b5d98c 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -24,7 +24,7 @@ export const { enableObjectFiber, enablePersistedModeClonedFlag, enableShallowPropDiffing, - enableUseResourceEffectHook, + enableUseEffectCRUDOverload, passChildrenWhenCloningPersistedNodes, enableSiblingPrerendering, enableOwnerStacks, diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index a3202c0b80049..3f373e998cd8e 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -63,7 +63,7 @@ export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; export const enableSiblingPrerendering = true; -export const enableUseResourceEffectHook = false; +export const enableUseEffectCRUDOverload = false; export const enableHydrationLaneScheduling = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 6bc085f540b23..da9b8a2612c9b 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -64,7 +64,7 @@ export const renameElementSymbol = true; export const enableShallowPropDiffing = false; export const enableSiblingPrerendering = true; -export const enableUseResourceEffectHook = false; +export const enableUseEffectCRUDOverload = false; export const enableYieldingBeforePassive = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index d62a59122f82c..8c20cc28deb15 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -62,7 +62,7 @@ export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; export const enableSiblingPrerendering = true; -export const enableUseResourceEffectHook = true; +export const enableUseEffectCRUDOverload = true; export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 301c01a018090..bc30992eb6eb5 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -74,7 +74,7 @@ export const enableOwnerStacks = false; export const enableShallowPropDiffing = false; export const enableSiblingPrerendering = true; -export const enableUseResourceEffectHook = false; +export const enableUseEffectCRUDOverload = false; export const enableHydrationLaneScheduling = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index e6fd46d0b1fb4..3cab1318c490e 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -36,7 +36,7 @@ export const enableSchedulingProfiler = __VARIANT__; export const enableInfiniteRenderLoopDetection = __VARIANT__; export const enableSiblingPrerendering = __VARIANT__; -export const enableUseResourceEffectHook = __VARIANT__; +export const enableUseEffectCRUDOverload = __VARIANT__; export const enableRemoveConsolePatches = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableViewTransition = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index dcb77f9d84b5e..57be83577c956 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -29,7 +29,7 @@ export const { enableSiblingPrerendering, enableTransitionTracing, enableTrustedTypesIntegration, - enableUseResourceEffectHook, + enableUseEffectCRUDOverload, favorSafetyOverHydrationPerf, renameElementSymbol, retryLaneExpirationMs, From 2c5fd26c07c0fb94ff21a6c10c5a757ef3c5d6a4 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 11 Feb 2025 14:18:50 -0500 Subject: [PATCH 017/300] [crud] Merge useResourceEffect into useEffect (#32205) Merges the useResourceEffect API into useEffect while keeping the underlying implementation the same. useResourceEffect will be removed in the next diff. To fork between behavior we rely on a `typeof` check for the updater or destroy function in addition to the CRUD feature flag. This does now have to be checked every time (instead of inlined statically like before due to them being different hooks) which will incur some non-zero amount (possibly negligble) of overhead for every effect. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32205). * #32206 * __->__ #32205 --- .../react-debug-tools/src/ReactDebugHooks.js | 7 +- .../ReactDOMServerIntegrationHooks-test.js | 6 +- .../src/ReactFiberCallUserSpace.js | 2 +- .../src/ReactFiberCommitEffects.js | 41 +-- .../react-reconciler/src/ReactFiberHooks.js | 268 ++++++++++++++---- .../src/ReactInternalTypes.js | 7 +- .../ReactHooksWithNoopRenderer-test.js | 70 +++-- packages/react/src/ReactHooks.js | 26 +- scripts/error-codes/codes.json | 3 +- 9 files changed, 296 insertions(+), 134 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 9c310723b2605..f45ccfecdcaec 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -373,8 +373,11 @@ function useInsertionEffect( } function useEffect( - create: () => (() => void) | void, - inputs: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { nextHook(); hookLog.push({ diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js index bce830ddf0647..840d6c5b15d5e 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js @@ -27,7 +27,6 @@ let useRef; let useImperativeHandle; let useInsertionEffect; let useLayoutEffect; -let useResourceEffect; let useDebugValue; let forwardRef; let yieldedValues; @@ -52,7 +51,6 @@ function initModules() { useImperativeHandle = React.useImperativeHandle; useInsertionEffect = React.useInsertionEffect; useLayoutEffect = React.useLayoutEffect; - useResourceEffect = React.experimental_useResourceEffect; forwardRef = React.forwardRef; yieldedValues = []; @@ -655,7 +653,7 @@ describe('ReactDOMServerHooks', () => { }); }); - describe('useResourceEffect', () => { + describe('useEffect with CRUD overload', () => { gate(flags => { if (flags.enableUseEffectCRUDOverload) { const yields = []; @@ -663,7 +661,7 @@ describe('ReactDOMServerHooks', () => { 'should ignore resource effects on the server', async render => { function Counter(props) { - useResourceEffect( + useEffect( () => { yieldValue('created on client'); return {resource_counter: props.count}; diff --git a/packages/react-reconciler/src/ReactFiberCallUserSpace.js b/packages/react-reconciler/src/ReactFiberCallUserSpace.js index 25ede645b5e7f..a9b5590f387b7 100644 --- a/packages/react-reconciler/src/ReactFiberCallUserSpace.js +++ b/packages/react-reconciler/src/ReactFiberCallUserSpace.js @@ -254,7 +254,7 @@ const callDestroy = { export const callDestroyInDEV: ( current: Fiber, nearestMountedAncestor: Fiber | null, - destroy: () => void, + destroy: (() => void) | (({...}) => void), ) => void = __DEV__ ? // We use this technique to trick minifiers to preserve the function name. (callDestroy['react-stack-bottom-frame'].bind(callDestroy): any) diff --git a/packages/react-reconciler/src/ReactFiberCommitEffects.js b/packages/react-reconciler/src/ReactFiberCommitEffects.js index 29e5de2817201..05cf17a34273e 100644 --- a/packages/react-reconciler/src/ReactFiberCommitEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitEffects.js @@ -170,8 +170,9 @@ export function commitHookEffectListMount( ); if (effect.inst.resource == null) { console.error( - 'useResourceEffect must provide a callback which returns a resource. ' + - 'If a managed resource is not needed here, use useEffect. Received %s', + 'useEffect must provide a callback which returns a resource. ' + + 'If a managed resource is not needed here, do not provide an updater or ' + + 'destroy callback. Received %s', effect.inst.resource, ); } @@ -261,11 +262,6 @@ export function commitHookEffectListMount( hookName = 'useLayoutEffect'; } else if ((effect.tag & HookInsertion) !== NoFlags) { hookName = 'useInsertionEffect'; - } else if ( - enableUseEffectCRUDOverload && - effect.resourceKind != null - ) { - hookName = 'useResourceEffect'; } else { hookName = 'useEffect'; } @@ -363,7 +359,7 @@ export function commitHookEffectListUnmount( effect.resourceKind === ResourceEffectIdentityKind && effect.inst.resource != null ) { - safelyCallDestroyWithResource( + safelyCallDestroy( finishedWork, nearestMountedAncestor, destroy, @@ -1015,32 +1011,11 @@ export function safelyDetachRef( function safelyCallDestroy( current: Fiber, nearestMountedAncestor: Fiber | null, - destroy: () => void, -) { - if (__DEV__) { - runWithFiberInDEV( - current, - callDestroyInDEV, - current, - nearestMountedAncestor, - destroy, - ); - } else { - try { - destroy(); - } catch (error) { - captureCommitPhaseError(current, nearestMountedAncestor, error); - } - } -} - -function safelyCallDestroyWithResource( - current: Fiber, - nearestMountedAncestor: Fiber | null, - destroy: ({...}) => void, - resource: {...}, + destroy: (() => void) | (({...}) => void), + resource?: {...} | void | null, ) { - const destroy_ = destroy.bind(null, resource); + // $FlowFixMe[extra-arg] @poteto this is safe either way because the extra arg is ignored if it's not a CRUD effect + const destroy_ = resource == null ? destroy : destroy.bind(null, resource); if (__DEV__) { runWithFiberInDEV( current, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 87f601dae597d..976657d4c68f7 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -2523,12 +2523,15 @@ function pushSimpleEffect( tag: HookFlags, inst: EffectInstance, create: () => (() => void) | void, - deps: Array | void | null, + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): Effect { const effect: Effect = { tag, create, - deps, + deps: createDeps, inst, // Circular next: (null: any), @@ -2608,10 +2611,13 @@ function mountEffectImpl( fiberFlags: Flags, hookFlags: HookFlags, create: () => (() => void) | void, - deps: Array | void | null, + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { const hook = mountWorkInProgressHook(); - const nextDeps = deps === undefined ? null : deps; + const nextDeps = createDeps === undefined ? null : createDeps; currentlyRenderingFiber.flags |= fiberFlags; hook.memoizedState = pushSimpleEffect( HookHasEffect | hookFlags, @@ -2662,35 +2668,89 @@ function updateEffectImpl( } function mountEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { if ( __DEV__ && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode && (currentlyRenderingFiber.mode & NoStrictPassiveEffectsMode) === NoMode ) { - mountEffectImpl( - MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, - HookPassive, - create, - deps, - ); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + mountResourceEffectImpl( + MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, + HookPassive, + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + mountEffectImpl( + MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, + HookPassive, + // $FlowFixMe[incompatible-call] @poteto it's not possible to narrow `create` without calling it. + create, + createDeps, + ); + } } else { - mountEffectImpl( - PassiveEffect | PassiveStaticEffect, - HookPassive, - create, - deps, - ); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + mountResourceEffectImpl( + PassiveEffect | PassiveStaticEffect, + HookPassive, + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + mountEffectImpl( + PassiveEffect | PassiveStaticEffect, + HookPassive, + // $FlowFixMe[incompatible-call] @poteto it's not possible to narrow `create` without calling it. + create, + createDeps, + ); + } } } function updateEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { - updateEffectImpl(PassiveEffect, HookPassive, create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + updateResourceEffectImpl( + PassiveEffect, + HookPassive, + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + // $FlowFixMe[incompatible-call] @poteto it's not possible to narrow `create` without calling it. + updateEffectImpl(PassiveEffect, HookPassive, create, createDeps); + } } function mountResourceEffect( @@ -2705,15 +2765,6 @@ function mountResourceEffect( (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode && (currentlyRenderingFiber.mode & NoStrictPassiveEffectsMode) === NoMode ) { - mountResourceEffectImpl( - MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, - HookPassive, - create, - createDeps, - update, - updateDeps, - destroy, - ); } else { mountResourceEffectImpl( PassiveEffect | PassiveStaticEffect, @@ -4087,13 +4138,30 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; mountHookTypesDev(); - checkDepsAreArrayDev(deps); - return mountEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + checkDepsAreNonEmptyArrayDev(updateDeps); + return mountResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + checkDepsAreArrayDev(createDeps); + return mountEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, @@ -4280,12 +4348,28 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; updateHookTypesDev(); - return mountEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + return mountResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + return mountEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, @@ -4467,12 +4551,28 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; updateHookTypesDev(); - return updateEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + return updateResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + return updateEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, @@ -4654,12 +4754,28 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; updateHookTypesDev(); - return updateEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + return updateResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + return updateEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, @@ -4847,13 +4963,29 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; warnInvalidHookAccess(); mountHookTypesDev(); - return mountEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + return mountResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + return mountEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, @@ -5060,13 +5192,29 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; warnInvalidHookAccess(); updateHookTypesDev(); - return updateEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + return updateResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + return updateEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, @@ -5273,13 +5421,29 @@ if (__DEV__) { return readContext(context); }, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { currentHookNameInDev = 'useEffect'; warnInvalidHookAccess(); updateHookTypesDev(); - return updateEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + return updateResourceEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else { + return updateEffect(create, createDeps); + } }, useImperativeHandle( ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 9ac0680fb6279..756d88c4a5d15 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -391,8 +391,11 @@ export type Dispatcher = { useContext(context: ReactContext): T, useRef(initialValue: T): {current: T}, useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void, // TODO: Non-nullable once `enableUseEffectEventHook` is on everywhere. useEffectEvent?: ) => mixed>(callback: F) => F, diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 27bff1e0806c4..47fd534e683a9 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -41,7 +41,6 @@ let waitFor; let waitForThrow; let waitForPaint; let assertLog; -let useResourceEffect; let assertConsoleErrorDev; describe('ReactHooksWithNoopRenderer', () => { @@ -70,7 +69,6 @@ describe('ReactHooksWithNoopRenderer', () => { useDeferredValue = React.useDeferredValue; Suspense = React.Suspense; Activity = React.unstable_Activity; - useResourceEffect = React.experimental_useResourceEffect; ContinuousEventPriority = require('react-reconciler/constants').ContinuousEventPriority; if (gate(flags => flags.enableSuspenseList)) { @@ -3312,7 +3310,7 @@ describe('ReactHooksWithNoopRenderer', () => { }); // @gate enableUseEffectCRUDOverload - describe('useResourceEffect', () => { + describe('useEffect CRUD overload', () => { class Resource { isDeleted: false; id: string; @@ -3335,34 +3333,34 @@ describe('ReactHooksWithNoopRenderer', () => { // @gate !enableUseEffectCRUDOverload it('is null when flag is disabled', async () => { - expect(useResourceEffect).toBeUndefined(); - }); - - // @gate enableUseEffectCRUDOverload - it('validates create return value', async () => { function App({id}) { - useResourceEffect(() => { - Scheduler.log(`create(${id})`); - }, [id]); + useEffect( + () => { + Scheduler.log(`create(${id})`); + return {}; + }, + [id], + () => { + Scheduler.log('update'); + }, + [], + ); return null; } - await act(() => { - ReactNoop.render(); - }); - assertConsoleErrorDev( - [ - 'useResourceEffect must provide a callback which returns a resource. ' + - 'If a managed resource is not needed here, use useEffect. Received undefined', - ], - {withoutStack: true}, + await expect(async () => { + await act(() => { + ReactNoop.render(); + }); + }).rejects.toThrow( + 'useEffect CRUD overload is not enabled in this build of React.', ); }); // @gate enableUseEffectCRUDOverload it('validates non-empty update deps', async () => { function App({id}) { - useResourceEffect( + useEffect( () => { Scheduler.log(`create(${id})`); return {}; @@ -3380,7 +3378,7 @@ describe('ReactHooksWithNoopRenderer', () => { ReactNoop.render(); }); assertConsoleErrorDev([ - 'useResourceEffect received a dependency array with no dependencies. ' + + 'useEffect received a dependency array with no dependencies. ' + 'When specified, the dependency array must have at least one dependency.\n' + ' in App (at **)', ]); @@ -3392,7 +3390,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); @@ -3449,7 +3447,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); @@ -3486,7 +3484,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); @@ -3524,9 +3522,9 @@ describe('ReactHooksWithNoopRenderer', () => { }); // @gate enableUseEffectCRUDOverload - it('does not unmount previous useResourceEffect between updates', async () => { + it('does not unmount previous useEffect between updates', async () => { function App({id}) { - useResourceEffect( + useEffect( () => { const resource = new Resource(id); Scheduler.log(`create(${resource.id})`); @@ -3565,7 +3563,7 @@ describe('ReactHooksWithNoopRenderer', () => { // @gate enableUseEffectCRUDOverload it('unmounts only on deletion', async () => { function App({id}) { - useResourceEffect( + useEffect( () => { const resource = new Resource(id); Scheduler.log(`create(${resource.id})`); @@ -3605,7 +3603,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); @@ -3653,7 +3651,7 @@ describe('ReactHooksWithNoopRenderer', () => { // @gate enableUseEffectCRUDOverload it('handles errors in create on mount', async () => { function App({id}) { - useResourceEffect( + useEffect( () => { Scheduler.log(`Mount A [${id}]`); return {}; @@ -3665,7 +3663,7 @@ describe('ReactHooksWithNoopRenderer', () => { Scheduler.log(`Unmount A [${id}]`); }, ); - useResourceEffect( + useEffect( () => { Scheduler.log('Oops!'); throw new Error('Oops!'); @@ -3703,7 +3701,7 @@ describe('ReactHooksWithNoopRenderer', () => { // @gate enableUseEffectCRUDOverload it('handles errors in create on update', async () => { function App({id}) { - useResourceEffect( + useEffect( () => { Scheduler.log(`Mount A [${id}]`); return {}; @@ -3750,7 +3748,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`Mount A [${id}, ${resource.opts.username}]`); @@ -3806,7 +3804,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); @@ -3885,7 +3883,7 @@ describe('ReactHooksWithNoopRenderer', () => { const opts = useMemo(() => { return {username}; }, [username]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); @@ -4003,7 +4001,7 @@ describe('ReactHooksWithNoopRenderer', () => { useEffect(() => { Scheduler.log(`useEffect(${count})`); }, [count]); - useResourceEffect( + useEffect( () => { const resource = new Resource(id, opts); Scheduler.log(`create(${resource.id}, ${resource.opts.username})`); diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 677e0d705b8aa..2fe6d2b85bcf1 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -87,11 +87,31 @@ export function useRef(initialValue: T): {current: T} { } export function useEffect( - create: () => (() => void) | void, - deps: Array | void | null, + create: (() => (() => void) | void) | (() => {...} | void | null), + createDeps: Array | void | null, + update?: ((resource: {...} | void | null) => void) | void, + updateDeps?: Array | void | null, + destroy?: ((resource: {...} | void | null) => void) | void, ): void { const dispatcher = resolveDispatcher(); - return dispatcher.useEffect(create, deps); + if ( + enableUseEffectCRUDOverload && + (typeof update === 'function' || typeof destroy === 'function') + ) { + // $FlowFixMe[not-a-function] This is unstable, thus optional + return dispatcher.useEffect( + create, + createDeps, + update, + updateDeps, + destroy, + ); + } else if (typeof update === 'function') { + throw new Error( + 'useEffect CRUD overload is not enabled in this build of React.', + ); + } + return dispatcher.useEffect(create, createDeps); } export function useInsertionEffect( diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 8fa3d190ecb70..6ab654f1d35e5 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -530,5 +530,6 @@ "542": "Suspense Exception: This is not a real error! It's an implementation detail of `useActionState` to interrupt the current render. You must either rethrow it immediately, or move the `useActionState` call outside of the `try/catch` block. Capturing without rethrowing will lead to unexpected behavior.\n\nTo handle async errors, wrap your component in an error boundary.", "543": "Expected a ResourceEffectUpdate to be pushed together with ResourceEffectIdentity. This is a bug in React.", "544": "Found a pair with an auto name. This is a bug in React.", - "545": "The %s tag may only be rendered once." + "545": "The %s tag may only be rendered once.", + "546": "useEffect CRUD overload is not enabled in this build of React." } From a69b80d07e5d1bf363ed15d6209a55b35e0765c2 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 11 Feb 2025 14:19:34 -0500 Subject: [PATCH 018/300] [crud] Remove useResourceEffect (#32206) Removes useResourceEffect. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32206). * __->__ #32206 * #32205 --- .../react-debug-tools/src/ReactDebugHooks.js | 22 --- .../react-reconciler/src/ReactFiberHooks.js | 158 ------------------ .../src/ReactInternalTypes.js | 9 - packages/react-server/src/ReactFizzHooks.js | 10 +- packages/react/index.development.js | 1 - .../react/index.experimental.development.js | 1 - packages/react/index.fb.js | 1 - packages/react/src/ReactClient.js | 5 - packages/react/src/ReactHooks.js | 21 --- 9 files changed, 1 insertion(+), 227 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index f45ccfecdcaec..114080d03e921 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -128,9 +128,6 @@ function getPrimitiveStackCache(): Map> { Dispatcher.useId(); - if (typeof Dispatcher.useResourceEffect === 'function') { - Dispatcher.useResourceEffect(() => ({}), []); - } if (typeof Dispatcher.useEffectEvent === 'function') { Dispatcher.useEffectEvent((args: empty) => {}); } @@ -741,24 +738,6 @@ function useHostTransitionStatus(): TransitionStatus { return status; } -function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, -) { - nextHook(); - hookLog.push({ - displayName: null, - primitive: 'ResourceEffect', - stackError: new Error(), - value: create, - debugInfo: null, - dispatcherHookName: 'ResourceEffect', - }); -} - function useEffectEvent) => mixed>(callback: F): F { nextHook(); hookLog.push({ @@ -798,7 +777,6 @@ const Dispatcher: DispatcherType = { useActionState, useHostTransitionStatus, useEffectEvent, - useResourceEffect, }; // create a proxy to throw a custom error diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 976657d4c68f7..2ab41770bd0ac 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -3989,9 +3989,6 @@ export const ContextOnlyDispatcher: Dispatcher = { if (enableUseEffectEventHook) { (ContextOnlyDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError; } -if (enableUseEffectCRUDOverload) { - (ContextOnlyDispatcher: Dispatcher).useResourceEffect = throwInvalidHookError; -} const HooksDispatcherOnMount: Dispatcher = { readContext, @@ -4022,9 +4019,6 @@ const HooksDispatcherOnMount: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnMount: Dispatcher).useEffectEvent = mountEvent; } -if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnMount: Dispatcher).useResourceEffect = mountResourceEffect; -} const HooksDispatcherOnUpdate: Dispatcher = { readContext, @@ -4055,10 +4049,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnUpdate: Dispatcher).useEffectEvent = updateEvent; } -if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnUpdate: Dispatcher).useResourceEffect = - updateResourceEffect; -} const HooksDispatcherOnRerender: Dispatcher = { readContext, @@ -4089,10 +4079,6 @@ const HooksDispatcherOnRerender: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnRerender: Dispatcher).useEffectEvent = updateEvent; } -if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnRerender: Dispatcher).useResourceEffect = - updateResourceEffect; -} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -4310,27 +4296,6 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnMountInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ): void { - currentHookNameInDev = 'useResourceEffect'; - mountHookTypesDev(); - checkDepsAreNonEmptyArrayDev(updateDeps); - return mountResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext): T { @@ -4514,26 +4479,6 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ): void { - currentHookNameInDev = 'useResourceEffect'; - updateHookTypesDev(); - return mountResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext): T { @@ -4717,26 +4662,6 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnUpdateInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ) { - currentHookNameInDev = 'useResourceEffect'; - updateHookTypesDev(); - return updateResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext): T { @@ -4920,26 +4845,6 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (HooksDispatcherOnRerenderInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ) { - currentHookNameInDev = 'useResourceEffect'; - updateHookTypesDev(); - return updateResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext): T { @@ -5148,27 +5053,6 @@ if (__DEV__) { return mountEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ): void { - currentHookNameInDev = 'useResourceEffect'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } InvalidNestedHooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext): T { @@ -5377,27 +5261,6 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ) { - currentHookNameInDev = 'useResourceEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } InvalidNestedHooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext): T { @@ -5606,25 +5469,4 @@ if (__DEV__) { return updateEvent(callback); }; } - if (enableUseEffectCRUDOverload) { - (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useResourceEffect = - function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ) { - currentHookNameInDev = 'useResourceEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); - }; - } } diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 756d88c4a5d15..98e7d4deefea3 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -47,7 +47,6 @@ export type HookType = | 'useRef' | 'useEffect' | 'useEffectEvent' - | 'useResourceEffect' | 'useInsertionEffect' | 'useLayoutEffect' | 'useCallback' @@ -399,14 +398,6 @@ export type Dispatcher = { ): void, // TODO: Non-nullable once `enableUseEffectEventHook` is on everywhere. useEffectEvent?: ) => mixed>(callback: F) => F, - // TODO: Non-nullable once `enableUseEffectCRUDOverload` is on everywhere. - useResourceEffect?: ( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, - ) => void, useInsertionEffect( create: () => (() => void) | void, deps: Array | void | null, diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 63e8576ca788c..a0ec1c7414982 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -38,10 +38,7 @@ import { } from './ReactFizzConfig'; import {createFastHash} from './ReactServerStreamConfig'; -import { - enableUseEffectEventHook, - enableUseEffectCRUDOverload, -} from 'shared/ReactFeatureFlags'; +import {enableUseEffectEventHook} from 'shared/ReactFeatureFlags'; import is from 'shared/objectIs'; import { REACT_CONTEXT_TYPE, @@ -866,11 +863,6 @@ export const HooksDispatcher: Dispatcher = supportsClientAPIs if (enableUseEffectEventHook) { HooksDispatcher.useEffectEvent = useEffectEvent; } -if (enableUseEffectCRUDOverload) { - HooksDispatcher.useResourceEffect = supportsClientAPIs - ? noop - : clientHookNotSupported; -} export let currentResumableState: null | ResumableState = (null: any); export function setCurrentResumableState( diff --git a/packages/react/index.development.js b/packages/react/index.development.js index 809e940f070ba..fa79633001a06 100644 --- a/packages/react/index.development.js +++ b/packages/react/index.development.js @@ -59,7 +59,6 @@ export { useDeferredValue, useEffect, experimental_useEffectEvent, - experimental_useResourceEffect, useImperativeHandle, useInsertionEffect, useLayoutEffect, diff --git a/packages/react/index.experimental.development.js b/packages/react/index.experimental.development.js index 49c98bb208acb..6074b683b781d 100644 --- a/packages/react/index.experimental.development.js +++ b/packages/react/index.experimental.development.js @@ -42,7 +42,6 @@ export { useDeferredValue, useEffect, experimental_useEffectEvent, - experimental_useResourceEffect, useImperativeHandle, useInsertionEffect, useLayoutEffect, diff --git a/packages/react/index.fb.js b/packages/react/index.fb.js index 8e11bade4df59..29133df24f3a7 100644 --- a/packages/react/index.fb.js +++ b/packages/react/index.fb.js @@ -21,7 +21,6 @@ export { createElement, createRef, experimental_useEffectEvent, - experimental_useResourceEffect, forwardRef, Fragment, isValidElement, diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index 2bacbc85676fd..715ea8ab47c35 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -41,7 +41,6 @@ import { useContext, useEffect, useEffectEvent, - useResourceEffect, useImperativeHandle, useDebugValue, useInsertionEffect, @@ -65,7 +64,6 @@ import {addTransitionType} from './ReactTransitionType'; import {act} from './ReactAct'; import {captureOwnerStack} from './ReactOwnerStack'; import * as ReactCompilerRuntime from './ReactCompilerRuntime'; -import {enableUseEffectCRUDOverload} from 'shared/ReactFeatureFlags'; const Children = { map, @@ -132,6 +130,3 @@ export { act, // DEV-only captureOwnerStack, // DEV-only }; - -export const experimental_useResourceEffect: typeof useResourceEffect | void = - enableUseEffectCRUDOverload ? useResourceEffect : undefined; diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 2fe6d2b85bcf1..06d61d223874d 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -221,27 +221,6 @@ export function useEffectEvent) => mixed>( return dispatcher.useEffectEvent(callback); } -export function useResourceEffect( - create: () => {...} | void | null, - createDeps: Array | void | null, - update: ((resource: {...} | void | null) => void) | void, - updateDeps: Array | void | null, - destroy: ((resource: {...} | void | null) => void) | void, -): void { - if (!enableUseEffectCRUDOverload) { - throw new Error('Not implemented.'); - } - const dispatcher = resolveDispatcher(); - // $FlowFixMe[not-a-function] This is unstable, thus optional - return dispatcher.useResourceEffect( - create, - createDeps, - update, - updateDeps, - destroy, - ); -} - export function useOptimistic( passthrough: S, reducer: ?(S, A) => S, From 192555bb0ed88db30f91c58651c421f178f90384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Tue, 11 Feb 2025 17:01:04 -0500 Subject: [PATCH 019/300] Added dev-only warning for null/undefined create in use*Effect (#32355) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes #32354. Re-creation of #15197: adds a dev-only warning if `create == null` to the three `use*Effect` functions: * `useEffect` * `useInsertionEffect` * `useLayoutEffect` Updates the warning to match the same text given in the `react/exhaustive-deps` lint rule. ## How did you test this change? I applied the changes manually within `node_modules/` on a local clone of https://github.com/JoshuaKGoldberg/repros/tree/react-use-effect-no-arguments. Please pardon me for opening a PR addressing a not-accepted issue. I was excited to get back to #15194 -> #15197 now that I have time. 🙂 --------- Co-authored-by: lauren --- packages/react/src/ReactHooks.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 06d61d223874d..ba6b0ffbcb71e 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -93,6 +93,14 @@ export function useEffect( updateDeps?: Array | void | null, destroy?: ((resource: {...} | void | null) => void) | void, ): void { + if (__DEV__) { + if (create == null) { + console.warn( + 'React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?', + ); + } + } + const dispatcher = resolveDispatcher(); if ( enableUseEffectCRUDOverload && @@ -118,6 +126,14 @@ export function useInsertionEffect( create: () => (() => void) | void, deps: Array | void | null, ): void { + if (__DEV__) { + if (create == null) { + console.warn( + 'React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?', + ); + } + } + const dispatcher = resolveDispatcher(); return dispatcher.useInsertionEffect(create, deps); } @@ -126,6 +142,14 @@ export function useLayoutEffect( create: () => (() => void) | void, deps: Array | void | null, ): void { + if (__DEV__) { + if (create == null) { + console.warn( + 'React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?', + ); + } + } + const dispatcher = resolveDispatcher(); return dispatcher.useLayoutEffect(create, deps); } From f83903bfcc5a61811bd1b69b14f0ebbac4754462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Wed, 12 Feb 2025 13:52:57 +0000 Subject: [PATCH 020/300] [RN] Set up test to create public instances lazily in Fabric (#32363) ## Summary In React Native, public instances and internal host nodes are not represented by the same object (ReactNativeElement & shadow nodes vs. just DOM elements), and the only one that's required for rendering is the shadow node. Public instances are generally only necessary when accessed via refs or events, and that usually happens for a small amount of components in the tree. This implements an optimization to create the public instance on demand, instead of eagerly creating it when creating the host node. We expect this to improve performance by reducing the logic we do per node and the number of object allocations. ## How did you test this change? Manually synced the changes to React Native and run Fantom tests and benchmarks, with the flag enabled and disabled. All tests pass in both cases, and benchmarks show a slight but consistent performance improvement. --- .../src/ReactFiberConfigFabric.js | 69 ++++++++++++++----- packages/shared/ReactFeatureFlags.js | 2 + .../ReactFeatureFlags.native-fb-dynamic.js | 1 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + ...actFeatureFlags.test-renderer.native-fb.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 2 + 10 files changed, 61 insertions(+), 19 deletions(-) diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index d602ab05efa81..d28b3400c66c3 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -57,7 +57,10 @@ import { getInspectorDataForInstance, } from './ReactNativeFiberInspector'; -import {passChildrenWhenCloningPersistedNodes} from 'shared/ReactFeatureFlags'; +import { + passChildrenWhenCloningPersistedNodes, + enableLazyPublicInstanceInFabric, +} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type {ReactContext} from 'shared/ReactTypes'; @@ -93,8 +96,11 @@ export type Instance = { currentProps: Props, // Reference to the React handle (the fiber) internalInstanceHandle: InternalInstanceHandle, - // Exposed through refs. - publicInstance: PublicInstance, + // Exposed through refs. Potentially lazily created. + publicInstance: PublicInstance | null, + // This is only necessary to lazily create `publicInstance`. + // Will be set to `null` after that is created. + publicRootInstance?: PublicRootInstance | null, }, }; export type TextInstance = { @@ -186,23 +192,37 @@ export function createInstance( internalInstanceHandle, // internalInstanceHandle ); - const component = createPublicInstance( - tag, - viewConfig, - internalInstanceHandle, - rootContainerInstance.publicInstance, - ); - - return { - node: node, - canonical: { - nativeTag: tag, + if (enableLazyPublicInstanceInFabric) { + return { + node: node, + canonical: { + nativeTag: tag, + viewConfig, + currentProps: props, + internalInstanceHandle, + publicInstance: null, + publicRootInstance: rootContainerInstance.publicInstance, + }, + }; + } else { + const component = createPublicInstance( + tag, viewConfig, - currentProps: props, internalInstanceHandle, - publicInstance: component, - }, - }; + rootContainerInstance.publicInstance, + ); + + return { + node: node, + canonical: { + nativeTag: tag, + viewConfig, + currentProps: props, + internalInstanceHandle, + publicInstance: component, + }, + }; + } } export function createTextInstance( @@ -277,7 +297,18 @@ export function getChildHostContext( } export function getPublicInstance(instance: Instance): null | PublicInstance { - if (instance.canonical != null && instance.canonical.publicInstance != null) { + if (instance.canonical != null) { + if (instance.canonical.publicInstance == null) { + instance.canonical.publicInstance = createPublicInstance( + instance.canonical.nativeTag, + instance.canonical.viewConfig, + instance.canonical.internalInstanceHandle, + instance.canonical.publicRootInstance ?? null, + ); + // This was only necessary to create the public instance. + instance.canonical.publicRootInstance = null; + } + return instance.canonical.publicInstance; } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 1cb1cb4cf7dc0..fbdfe5a468516 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -154,6 +154,8 @@ export const enableUseEffectCRUDOverload = false; export const enableFastAddPropertiesInDiffing = true; +export const enableLazyPublicInstanceInFabric = false; + // ----------------------------------------------------------------------------- // Ready for next major. // diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 001b3d64c06dc..ffcec9334d663 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -28,3 +28,4 @@ export const enableUseEffectCRUDOverload = __VARIANT__; export const enableOwnerStacks = __VARIANT__; export const enableRemoveConsolePatches = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; +export const enableLazyPublicInstanceInFabric = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 6e82185b5d98c..2eff113ae42f1 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -30,6 +30,7 @@ export const { enableOwnerStacks, enableRemoveConsolePatches, enableFastAddPropertiesInDiffing, + enableLazyPublicInstanceInFabric, } = dynamicFlags; // The rest of the flags are static for better dead code elimination. diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 3f373e998cd8e..bcd8b375eca67 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -72,6 +72,7 @@ export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableFastAddPropertiesInDiffing = false; +export const enableLazyPublicInstanceInFabric = false; // Profiling Only export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index da9b8a2612c9b..6fcd35c40774b 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -71,6 +71,7 @@ export const enableYieldingBeforePassive = true; export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableFastAddPropertiesInDiffing = true; +export const enableLazyPublicInstanceInFabric = false; // TODO: This must be in sync with the main ReactFeatureFlags file because // the Test Renderer's value must be the same as the one used by the diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 8c20cc28deb15..743ba39a86792 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -69,6 +69,7 @@ export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableRemoveConsolePatches = false; export const enableFastAddPropertiesInDiffing = false; +export const enableLazyPublicInstanceInFabric = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index bc30992eb6eb5..26747d3fb69a9 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -84,6 +84,7 @@ export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableRemoveConsolePatches = false; export const enableFastAddPropertiesInDiffing = false; +export const enableLazyPublicInstanceInFabric = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 3cab1318c490e..d0b4404a3e946 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -39,6 +39,7 @@ export const enableSiblingPrerendering = __VARIANT__; export const enableUseEffectCRUDOverload = __VARIANT__; export const enableRemoveConsolePatches = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; +export const enableLazyPublicInstanceInFabric = false; export const enableViewTransition = __VARIANT__; // TODO: These flags are hard-coded to the default values used in open source. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 57be83577c956..8da74c54bd69e 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -110,5 +110,7 @@ export const disableLegacyMode = true; export const enableShallowPropDiffing = false; +export const enableLazyPublicInstanceInFabric = false; + // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); From d814852baf03c61b1e32b59a45c3f72e00577ac8 Mon Sep 17 00:00:00 2001 From: lauren Date: Wed, 12 Feb 2025 11:59:56 -0500 Subject: [PATCH 021/300] [compiler] Upgrade esbuild (#32368) Just a simple upgrade --- compiler/package.json | 2 +- compiler/yarn.lock | 314 +++++++++++++++++++++--------------------- 2 files changed, 158 insertions(+), 158 deletions(-) diff --git a/compiler/package.json b/compiler/package.json index b16756f0aa2c0..f1696b9b2a5c5 100644 --- a/compiler/package.json +++ b/compiler/package.json @@ -28,7 +28,7 @@ "devDependencies": { "@tsconfig/strictest": "^2.0.5", "concurrently": "^7.4.0", - "esbuild": "^0.24.2", + "esbuild": "^0.25.0", "folder-hash": "^4.0.4", "npm-dts": "^1.3.13", "object-assign": "^4.1.1", diff --git a/compiler/yarn.lock b/compiler/yarn.lock index 354cc199b0285..b5bd5f8eaf7b4 100644 --- a/compiler/yarn.lock +++ b/compiler/yarn.lock @@ -1714,130 +1714,130 @@ enabled "2.0.x" kuler "^2.0.0" -"@esbuild/aix-ppc64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" - integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== - -"@esbuild/android-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" - integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== - -"@esbuild/android-arm@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" - integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== - -"@esbuild/android-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" - integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== - -"@esbuild/darwin-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" - integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== - -"@esbuild/darwin-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" - integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== - -"@esbuild/freebsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" - integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== - -"@esbuild/freebsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" - integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== - -"@esbuild/linux-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" - integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== - -"@esbuild/linux-arm@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" - integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== - -"@esbuild/linux-ia32@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" - integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== - -"@esbuild/linux-loong64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" - integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== - -"@esbuild/linux-mips64el@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" - integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== - -"@esbuild/linux-ppc64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" - integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== - -"@esbuild/linux-riscv64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" - integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== - -"@esbuild/linux-s390x@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" - integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== - -"@esbuild/linux-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" - integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== - -"@esbuild/netbsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" - integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== - -"@esbuild/netbsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" - integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== - -"@esbuild/openbsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" - integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== - -"@esbuild/openbsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" - integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== - -"@esbuild/sunos-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" - integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== - -"@esbuild/win32-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" - integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== - -"@esbuild/win32-ia32@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" - integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== - -"@esbuild/win32-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" - integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== +"@esbuild/aix-ppc64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" + integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ== + +"@esbuild/android-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f" + integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g== + +"@esbuild/android-arm@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b" + integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g== + +"@esbuild/android-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163" + integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg== + +"@esbuild/darwin-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c" + integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw== + +"@esbuild/darwin-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a" + integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg== + +"@esbuild/freebsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce" + integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w== + +"@esbuild/freebsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7" + integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A== + +"@esbuild/linux-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73" + integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg== + +"@esbuild/linux-arm@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3" + integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg== + +"@esbuild/linux-ia32@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19" + integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg== + +"@esbuild/linux-loong64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7" + integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw== + +"@esbuild/linux-mips64el@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1" + integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ== + +"@esbuild/linux-ppc64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951" + integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw== + +"@esbuild/linux-riscv64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987" + integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA== + +"@esbuild/linux-s390x@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4" + integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA== + +"@esbuild/linux-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a" + integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw== + +"@esbuild/netbsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b" + integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw== + +"@esbuild/netbsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b" + integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA== + +"@esbuild/openbsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7" + integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw== + +"@esbuild/openbsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde" + integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg== + +"@esbuild/sunos-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92" + integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg== + +"@esbuild/win32-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c" + integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw== + +"@esbuild/win32-ia32@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079" + integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA== + +"@esbuild/win32-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" + integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -2799,10 +2799,10 @@ dependencies: "@babel/types" "^7.3.0" -"@types/eslint@^8.56.6": - version "8.56.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.6.tgz#d5dc16cac025d313ee101108ba5714ea10eb3ed0" - integrity sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A== +"@types/eslint@^8.56.12": + version "8.56.12" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" + integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -4105,36 +4105,36 @@ es5-ext@0.8.x: resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.8.2.tgz#aba8d9e1943a895ac96837a62a39b3f55ecd94ab" integrity sha512-H19ompyhnKiBdjHR1DPHvf5RHgHPmJaY9JNzFGbMbPgdsUkvnUCN1Ke8J4Y0IMyTwFM2M9l4h2GoHwzwpSmXbA== -esbuild@^0.24.2: - version "0.24.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" - integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== +esbuild@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92" + integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw== optionalDependencies: - "@esbuild/aix-ppc64" "0.24.2" - "@esbuild/android-arm" "0.24.2" - "@esbuild/android-arm64" "0.24.2" - "@esbuild/android-x64" "0.24.2" - "@esbuild/darwin-arm64" "0.24.2" - "@esbuild/darwin-x64" "0.24.2" - "@esbuild/freebsd-arm64" "0.24.2" - "@esbuild/freebsd-x64" "0.24.2" - "@esbuild/linux-arm" "0.24.2" - "@esbuild/linux-arm64" "0.24.2" - "@esbuild/linux-ia32" "0.24.2" - "@esbuild/linux-loong64" "0.24.2" - "@esbuild/linux-mips64el" "0.24.2" - "@esbuild/linux-ppc64" "0.24.2" - "@esbuild/linux-riscv64" "0.24.2" - "@esbuild/linux-s390x" "0.24.2" - "@esbuild/linux-x64" "0.24.2" - "@esbuild/netbsd-arm64" "0.24.2" - "@esbuild/netbsd-x64" "0.24.2" - "@esbuild/openbsd-arm64" "0.24.2" - "@esbuild/openbsd-x64" "0.24.2" - "@esbuild/sunos-x64" "0.24.2" - "@esbuild/win32-arm64" "0.24.2" - "@esbuild/win32-ia32" "0.24.2" - "@esbuild/win32-x64" "0.24.2" + "@esbuild/aix-ppc64" "0.25.0" + "@esbuild/android-arm" "0.25.0" + "@esbuild/android-arm64" "0.25.0" + "@esbuild/android-x64" "0.25.0" + "@esbuild/darwin-arm64" "0.25.0" + "@esbuild/darwin-x64" "0.25.0" + "@esbuild/freebsd-arm64" "0.25.0" + "@esbuild/freebsd-x64" "0.25.0" + "@esbuild/linux-arm" "0.25.0" + "@esbuild/linux-arm64" "0.25.0" + "@esbuild/linux-ia32" "0.25.0" + "@esbuild/linux-loong64" "0.25.0" + "@esbuild/linux-mips64el" "0.25.0" + "@esbuild/linux-ppc64" "0.25.0" + "@esbuild/linux-riscv64" "0.25.0" + "@esbuild/linux-s390x" "0.25.0" + "@esbuild/linux-x64" "0.25.0" + "@esbuild/netbsd-arm64" "0.25.0" + "@esbuild/netbsd-x64" "0.25.0" + "@esbuild/openbsd-arm64" "0.25.0" + "@esbuild/openbsd-x64" "0.25.0" + "@esbuild/sunos-x64" "0.25.0" + "@esbuild/win32-arm64" "0.25.0" + "@esbuild/win32-ia32" "0.25.0" + "@esbuild/win32-x64" "0.25.0" escalade@^3.1.1: version "3.1.1" From 5a78dd7cfec4217c1feb89ee6312c13f40b01aed Mon Sep 17 00:00:00 2001 From: lauren Date: Wed, 12 Feb 2025 15:13:47 -0500 Subject: [PATCH 022/300] [ci] Also notify compiler ready for review PRs (#32371) Similar to #32344 --- .github/workflows/compiler_discord_notify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index da85190dd2127..f81f42b9cb7df 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -2,7 +2,7 @@ name: (Compiler) Discord Notify on: pull_request_target: - types: [opened] + types: [opened, ready_for_review] paths: - compiler/** - .github/workflows/compiler_**.yml From e0131f1edae0fc411bf8abb2fed211ca07af60fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Thu, 13 Feb 2025 13:02:55 +0100 Subject: [PATCH 023/300] fix(devtools): Handle nullish values passed to `formatConsoleArguments` (#32372) ## Summary When using React Devtools, calling `console.log('%s', null)` in userland can cause it to throw an error: ``` TypeError: Cannot read properties of null (reading 'toString') ``` ## How did you test this change? Added a unit test. See https://github.com/47ng/nuqs/issues/808. --- .../react-devtools-shared/src/__tests__/utils-test.js | 9 +++++++++ .../src/backend/utils/formatConsoleArguments.js | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/__tests__/utils-test.js b/packages/react-devtools-shared/src/__tests__/utils-test.js index 94726312c3bfa..876aaa99f1ea9 100644 --- a/packages/react-devtools-shared/src/__tests__/utils-test.js +++ b/packages/react-devtools-shared/src/__tests__/utils-test.js @@ -470,5 +470,14 @@ function f() { } {}, ]); }); + + it('formats nullish values', () => { + expect(formatConsoleArguments('This is the %s template', null)).toEqual([ + 'This is the null template', + ]); + expect( + formatConsoleArguments('This is the %s template', undefined), + ).toEqual(['This is the undefined template']); + }); }); }); diff --git a/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js b/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js index a2d303e543ac0..eaf4170970ed4 100644 --- a/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js +++ b/packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js @@ -58,7 +58,7 @@ export default function formatConsoleArguments( } case 's': { const [arg] = args.splice(argumentsPointer, 1); - template += arg.toString(); + template += String(arg); break; } From cbbe8666a8d6e6f1b81dffb11bd5d767e4acd6ac Mon Sep 17 00:00:00 2001 From: LoganDark <4723091+LoganDark@users.noreply.github.com> Date: Thu, 13 Feb 2025 04:04:53 -0800 Subject: [PATCH 024/300] fix value formatting of proxies of class instances (#30880) For Hookstate Proxies of class instances, `data.constructor.name` returns `Proxy({})`, so use `Object.getPrototypeOf(data).constructor.name` instead, which works correctly from my testing. ## Summary React DevTools immediately bricks itself if you inspect any component that has a prop that is a Hookstate that wraps a class instance ... because these are proxies where `data.constructor.name` returns some un-cloneable object, but `Object.getPrototypeOf(data)` doesn't return `Object` (it returns the prototype of the class inside). ## How did you test this change? This part of the code has no associated tests at all. Technically, `packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js` exists, but I tried `yarn test` and these tests aren't even executed anymore. I can't figure it out, so whatever. If you run this code: ```js class Class {} const instance = new Class(); const instanceProxy = new Proxy(instance, { get(target, key, receiver) { if (key === 'constructor') { return { name: new Proxy({}, {}) }; } return Reflect.get(target, key, receiver); }, }); ``` then `instanceProxy.constructor.name` returns some proxy that cannot be cloned, but `Object.getPrototypeOf(instanceProxy).constructor.name` returns the correct value. This PR fixes the devtools to use `Object.getPrototypeOf(instanceProxy).constructor.name`. I modified my local copy of devtools to use this method and it fixed the bricking that I experienced. Related #29954 --- packages/react-devtools-shared/src/utils.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index f6f7f3f4209aa..12c9fb739ce95 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -915,7 +915,25 @@ export function formatDataForPreview( case 'date': return data.toString(); case 'class_instance': - return data.constructor.name; + try { + let resolvedConstructorName = data.constructor.name; + if (typeof resolvedConstructorName === 'string') { + return resolvedConstructorName; + } + + resolvedConstructorName = Object.getPrototypeOf(data).constructor.name; + if (typeof resolvedConstructorName === 'string') { + return resolvedConstructorName; + } + + try { + return truncateForDisplay(String(data)); + } catch (error) { + return 'unserializable'; + } + } catch (error) { + return 'unserializable'; + } case 'object': if (showFormattedValue) { const keys = Array.from(getAllEnumerableKeys(data)).sort(alphaSortKeys); From c6a7e18636e610efd3aa7a437bbcaf321bf73abd Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Thu, 13 Feb 2025 18:09:49 +0100 Subject: [PATCH 025/300] Ensure `captureOwnerStack` returns `null` when no stack is available (#32353) Co-authored-by: Younes Henni --- packages/react-reconciler/src/ReactCurrentFiber.js | 2 +- .../src/__tests__/ReactOwnerStacks-test.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/react-reconciler/src/ReactCurrentFiber.js b/packages/react-reconciler/src/ReactCurrentFiber.js index 18406d991909a..f65ae5f19232c 100644 --- a/packages/react-reconciler/src/ReactCurrentFiber.js +++ b/packages/react-reconciler/src/ReactCurrentFiber.js @@ -72,7 +72,7 @@ export function runWithFiberInDEV( } return callback(arg0, arg1, arg2, arg3, arg4); } finally { - current = previousFiber; + setCurrentFiber(previousFiber); } } // These errors should never make it into a build so we don't need to encode them in codes.json diff --git a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js index ca0e9fd12cc11..7ee176a106e15 100644 --- a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js @@ -62,4 +62,17 @@ describe('ReactOwnerStacks', () => { '\n in Bar (at **)' + '\n in Foo (at **)', ); }); + + it('returns null outside of render', async () => { + // Awkward to gate since some builds will have `captureOwnerStack` return null in prod + if (__DEV__ && gate('enableOwnerStacks')) { + expect(React.captureOwnerStack()).toBe(null); + + await act(() => { + ReactNoop.render(
); + }); + + expect(React.captureOwnerStack()).toBe(null); + } + }); }); From ed8b68dd178af17a2dd36c8678f81f8b454559a9 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Thu, 13 Feb 2025 18:26:36 +0100 Subject: [PATCH 026/300] Stop exporting dev-only methods in OSS production builds (#32200) --- .../src/__tests__/ReactTestUtilsAct-test.js | 8 --- .../src/__tests__/ReactIsomorphicAct-test.js | 12 +++++ .../src/__tests__/ReactOwnerStacks-test.js | 10 ++++ packages/react/index.js | 2 - packages/react/index.stable.development.js | 51 +++++++++++++++++++ packages/react/index.stable.js | 1 - scripts/jest/TestFlags.js | 1 + scripts/jest/setupHostConfigs.js | 19 ++++--- scripts/rollup/build.js | 21 ++++---- 9 files changed, 96 insertions(+), 29 deletions(-) create mode 100644 packages/react/index.stable.development.js diff --git a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js index 76cfab25e0016..64ccc387d75a3 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js @@ -779,13 +779,5 @@ function runActTests(render, unmount, rerender) { }); } }); - describe('throw in prod mode', () => { - // @gate !__DEV__ - it('warns if you try to use act() in prod mode', () => { - expect(() => act(() => {})).toThrow( - 'act(...) is not supported in production builds of React', - ); - }); - }); }); } diff --git a/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js b/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js index eeec3b2b7eb3b..874969b131ea5 100644 --- a/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js @@ -50,6 +50,18 @@ describe('isomorphic act()', () => { return text; } + it('behavior in production', () => { + if (!__DEV__) { + if (gate('fb')) { + expect(() => act(() => {})).toThrow( + 'act(...) is not supported in production builds of React', + ); + } else { + expect(React).not.toHaveProperty('act'); + } + } + }); + // @gate __DEV__ it('bypasses queueMicrotask', async () => { const root = ReactNoop.createRoot(); diff --git a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js index 7ee176a106e15..42294269e7b14 100644 --- a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js @@ -31,6 +31,16 @@ describe('ReactOwnerStacks', () => { ); } + it('behavior in production', () => { + if (!__DEV__) { + if (gate('fb')) { + expect(React).toHaveProperty('captureOwnerStack', undefined); + } else { + expect(React).not.toHaveProperty('captureOwnerStack'); + } + } + }); + // @gate __DEV__ && enableOwnerStacks it('can get the component owner stacks during rendering in dev', async () => { let stack; diff --git a/packages/react/index.js b/packages/react/index.js index 711a044455257..af4cd4b0c1b17 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -23,8 +23,6 @@ export type ElementRef = React$ElementRef; export type Config = React$Config; export type ChildrenArray<+T> = $ReadOnlyArray> | T; -// Export all exports so that they're available in tests. -// We can't use export * from in Flow for some reason. export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __COMPILER_RUNTIME, diff --git a/packages/react/index.stable.development.js b/packages/react/index.stable.development.js new file mode 100644 index 0000000000000..f9ac98bafbf58 --- /dev/null +++ b/packages/react/index.stable.development.js @@ -0,0 +1,51 @@ +/** + * 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 { + __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, + Children, + Component, + Fragment, + Profiler, + PureComponent, + StrictMode, + Suspense, + cloneElement, + createContext, + createElement, + createRef, + use, + forwardRef, + isValidElement, + lazy, + memo, + cache, + unstable_useCacheRefresh, + startTransition, + useId, + useCallback, + useContext, + useDebugValue, + useDeferredValue, + useEffect, + useImperativeHandle, + useInsertionEffect, + useLayoutEffect, + useMemo, + useReducer, + useOptimistic, + useRef, + useState, + useSyncExternalStore, + useTransition, + useActionState, + version, + act, // DEV-only +} from './src/ReactClient'; diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index 26e22c343223e..6f25c7a37d983 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -10,7 +10,6 @@ export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __COMPILER_RUNTIME, - act, Children, Component, Fragment, diff --git a/scripts/jest/TestFlags.js b/scripts/jest/TestFlags.js index b970955491aae..f736ca2254f64 100644 --- a/scripts/jest/TestFlags.js +++ b/scripts/jest/TestFlags.js @@ -78,6 +78,7 @@ function getTestFlags() { classic: releaseChannel === 'classic', source: !process.env.IS_BUILD, www, + fb: www || xplat, // These aren't flags, just a useful aliases for tests. enableActivity: releaseChannel === 'experimental' || www || xplat, diff --git a/scripts/jest/setupHostConfigs.js b/scripts/jest/setupHostConfigs.js index b52fac3201ebf..aacfaf116fcd2 100644 --- a/scripts/jest/setupHostConfigs.js +++ b/scripts/jest/setupHostConfigs.js @@ -43,9 +43,9 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { } resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint); - const developmentEntry = resolvedEntry.replace('.js', '.development.js'); - if (fs.existsSync(developmentEntry)) { - return developmentEntry; + const devEntry = resolvedEntry.replace('.js', '.development.js'); + if (__DEV__ && fs.existsSync(devEntry)) { + return devEntry; } if (fs.existsSync(resolvedEntry)) { return resolvedEntry; @@ -60,13 +60,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' ); const devFBEntry = resolvedFBEntry.replace('.js', '.development.js'); - if (fs.existsSync(devFBEntry)) { + if (__DEV__ && fs.existsSync(devFBEntry)) { return devFBEntry; } if (fs.existsSync(resolvedFBEntry)) { return resolvedFBEntry; } const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js'); + const devGenericFBEntry = resolvedGenericFBEntry.replace( + '.js', + '.development.js' + ); + if (__DEV__ && fs.existsSync(devGenericFBEntry)) { + return devGenericFBEntry; + } if (fs.existsSync(resolvedGenericFBEntry)) { return resolvedGenericFBEntry; } @@ -77,14 +84,14 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js' ); const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js'); - if (fs.existsSync(devForkedEntry)) { + if (__DEV__ && fs.existsSync(devForkedEntry)) { return devForkedEntry; } if (fs.existsSync(resolvedForkedEntry)) { return resolvedForkedEntry; } const plainDevEntry = resolvedEntry.replace('.js', '.development.js'); - if (fs.existsSync(plainDevEntry)) { + if (__DEV__ && fs.existsSync(plainDevEntry)) { return plainDevEntry; } // Just use the plain .js one. diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 2a67c13e1e620..547362dae7123 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -571,7 +571,7 @@ function shouldSkipBundle(bundle, bundleType) { return false; } -function resolveEntryFork(resolvedEntry, isFBBundle) { +function resolveEntryFork(resolvedEntry, isFBBundle, isDev) { // Pick which entry point fork to use: // .modern.fb.js // .classic.fb.js @@ -586,23 +586,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { '.js', __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' ); - const developmentFBEntry = resolvedFBEntry.replace( - '.js', - '.development.js' - ); - if (fs.existsSync(developmentFBEntry)) { - return developmentFBEntry; + const devFBEntry = resolvedFBEntry.replace('.js', '.development.js'); + if (isDev && fs.existsSync(devFBEntry)) { + return devFBEntry; } if (fs.existsSync(resolvedFBEntry)) { return resolvedFBEntry; } const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js'); - const developmentGenericFBEntry = resolvedGenericFBEntry.replace( + const devGenericFBEntry = resolvedGenericFBEntry.replace( '.js', '.development.js' ); - if (fs.existsSync(developmentGenericFBEntry)) { - return developmentGenericFBEntry; + if (isDev && fs.existsSync(devGenericFBEntry)) { + return devGenericFBEntry; } if (fs.existsSync(resolvedGenericFBEntry)) { return resolvedGenericFBEntry; @@ -614,7 +611,7 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js' ); const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js'); - if (fs.existsSync(devForkedEntry)) { + if (isDev && fs.existsSync(devForkedEntry)) { return devForkedEntry; } if (fs.existsSync(resolvedForkedEntry)) { @@ -633,7 +630,7 @@ async function createBundle(bundle, bundleType) { const {isFBWWWBundle, isFBRNBundle} = getBundleTypeFlags(bundleType); - let resolvedEntry = resolveEntryFork( + const resolvedEntry = resolveEntryFork( require.resolve(bundle.entry), isFBWWWBundle || isFBRNBundle, !isProductionBundleType(bundleType) From 32b0cad8f74da3d6e8b07f4ffbad26dfe8d8a71a Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Thu, 13 Feb 2025 20:38:57 +0100 Subject: [PATCH 027/300] Enable owner stacks in Canary builds (#32053) Pending internal decision to ship in Canary. Still off for FB builds. Docs: https://github.com/reactjs/react.dev/pull/7427 --- packages/react/index.stable.development.js | 1 + packages/react/src/ReactServer.js | 2 ++ packages/shared/ReactFeatureFlags.js | 2 +- packages/shared/forks/ReactFeatureFlags.test-renderer.js | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react/index.stable.development.js b/packages/react/index.stable.development.js index f9ac98bafbf58..2397010cf5604 100644 --- a/packages/react/index.stable.development.js +++ b/packages/react/index.stable.development.js @@ -48,4 +48,5 @@ export { useActionState, version, act, // DEV-only + captureOwnerStack, // DEV-only } from './src/ReactClient'; diff --git a/packages/react/src/ReactServer.js b/packages/react/src/ReactServer.js index d6702023e4741..a00e192bd3c1d 100644 --- a/packages/react/src/ReactServer.js +++ b/packages/react/src/ReactServer.js @@ -28,6 +28,7 @@ import {lazy} from './ReactLazy'; import {memo} from './ReactMemo'; import {cache} from './ReactCacheServer'; import version from 'shared/ReactVersion'; +import {captureOwnerStack} from './ReactOwnerStack'; const Children = { map, @@ -57,4 +58,5 @@ export { useDebugValue, useMemo, version, + captureOwnerStack, // DEV-only }; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index fbdfe5a468516..844b2a2f6a914 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -127,7 +127,7 @@ export const passChildrenWhenCloningPersistedNodes = false; */ export const enablePersistedModeClonedFlag = false; -export const enableOwnerStacks = __EXPERIMENTAL__; +export const enableOwnerStacks = true; export const enableShallowPropDiffing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 6fcd35c40774b..ef3c300602bdb 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -88,7 +88,7 @@ export const enableReactTestRendererWarning = true; export const disableDefaultPropsExceptForClasses = true; export const enableObjectFiber = false; -export const enableOwnerStacks = false; +export const enableOwnerStacks = true; export const enableRemoveConsolePatches = true; // Flow magic to verify the exports of this file match the original version. From a53da6abe1593483098df2baf927fe07d80153a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Thu, 13 Feb 2025 16:06:01 -0500 Subject: [PATCH 028/300] Add useSwipeTransition Hook Behind Experimental Flag (#32373) This Hook will be used to drive a View Transition based on a gesture. ```js const [value, startGesture] = useSwipeTransition(prev, current, next); ``` The `enableSwipeTransition` flag will depend on `enableViewTransition` flag but we may decide to ship them independently. This PR doesn't do anything interesting yet. There will be a lot more PRs to build out the actual functionality. This is just wiring up the plumbing for the new Hook. This first PR is mainly concerned with how the whole starts (and stops). The core API is the `startGesture` function (although there will be other conveniences added in the future). You can call this to start a gesture with a source provider. You can call this multiple times in one event to batch multiple Hooks listening to the same provider. However, each render can only handle one source provider at a time and so it does one render per scheduled gesture provider. This uses a separate `GestureLane` to drive gesture renders by marking the Hook as having an update on that lane. Then schedule a render. These renders should be blocking and in the same microtask as the `startGesture` to ensure it can block the paint. So it's similar to sync. It may not be possible to finish it synchronously e.g. if something suspends. If so, it just tries again later when it can like any other render. This can also happen because it also may not be possible to drive more than one gesture at a time like if we're limited to one View Transition per document. So right now you can only run one gesture at a time in practice. These renders never commit. This means that we can't clear the `GestureLane` the normal way. Instead, we have to clear only the root's `pendingLanes` if we don't have any new renders scheduled. Then wait until something else updates the Fiber after all gestures on it have stopped before it really clears. --- .../view-transition/src/components/Page.css | 11 + .../view-transition/src/components/Page.js | 39 ++- .../react-debug-tools/src/ReactDebugHooks.js | 37 ++- .../src/ReactFiberConcurrentUpdates.js | 38 ++- .../src/ReactFiberGestureScheduler.js | 90 +++++++ .../react-reconciler/src/ReactFiberHooks.js | 242 ++++++++++++++++++ .../react-reconciler/src/ReactFiberLane.js | 52 ++-- .../react-reconciler/src/ReactFiberRoot.js | 5 + .../src/ReactFiberRootScheduler.js | 8 +- .../src/ReactFiberWorkLoop.js | 44 ++++ .../src/ReactInternalTypes.js | 14 +- packages/react-server/src/ReactFizzHooks.js | 42 ++- packages/react-server/src/ReactFlightHooks.js | 45 ++-- .../react/index.experimental.development.js | 1 + packages/react/index.experimental.js | 1 + packages/react/src/ReactClient.js | 8 +- packages/react/src/ReactHooks.js | 19 +- packages/shared/ReactFeatureFlags.js | 2 + packages/shared/ReactTypes.js | 6 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + ...actFeatureFlags.test-renderer.native-fb.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 2 + scripts/error-codes/codes.json | 4 +- 26 files changed, 647 insertions(+), 68 deletions(-) create mode 100644 packages/react-reconciler/src/ReactFiberGestureScheduler.js diff --git a/fixtures/view-transition/src/components/Page.css b/fixtures/view-transition/src/components/Page.css index 5afcc33a8f4bc..a5a9373194ef8 100644 --- a/fixtures/view-transition/src/components/Page.css +++ b/fixtures/view-transition/src/components/Page.css @@ -6,3 +6,14 @@ font-variation-settings: "wdth" 100; } + +.swipe-recognizer { + width: 200px; + overflow-x: scroll; + border: 1px solid #333333; + border-radius: 10px; +} + +.swipe-overscroll { + width: 200%; +} diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index 40f60327728fd..1c7a9bfeb32db 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -1,6 +1,9 @@ import React, { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity, + unstable_useSwipeTransition as useSwipeTransition, + useRef, + useLayoutEffect, } from 'react'; import './Page.css'; @@ -35,7 +38,8 @@ function Component() { } export default function Page({url, navigate}) { - const show = url === '/?b'; + const [renderedUrl, startGesture] = useSwipeTransition('/?a', url, '/?b'); + const show = renderedUrl === '/?b'; function onTransition(viewTransition, types) { const keyframes = [ {rotate: '0deg', transformOrigin: '30px 8px'}, @@ -44,6 +48,32 @@ export default function Page({url, navigate}) { viewTransition.old.animate(keyframes, 250); viewTransition.new.animate(keyframes, 250); } + + const swipeRecognizer = useRef(null); + const activeGesture = useRef(null); + function onScroll() { + if (activeGesture.current !== null) { + return; + } + // eslint-disable-next-line no-undef + const scrollTimeline = new ScrollTimeline({ + source: swipeRecognizer.current, + axis: 'x', + }); + activeGesture.current = startGesture(scrollTimeline); + } + function onScrollEnd() { + if (activeGesture.current !== null) { + const cancelGesture = activeGesture.current; + activeGesture.current = null; + cancelGesture(); + } + } + + useLayoutEffect(() => { + swipeRecognizer.current.scrollLeft = show ? 0 : 10000; + }, [show]); + const exclamation = ( ! @@ -90,6 +120,13 @@ export default function Page({url, navigate}) {

+
+
Swipe me
+

{show ? null : ( diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 114080d03e921..798cdec4380f8 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -14,6 +14,7 @@ import type { Usable, Thenable, ReactDebugInfo, + StartGesture, } from 'shared/ReactTypes'; import type { ContextDependency, @@ -131,6 +132,9 @@ function getPrimitiveStackCache(): Map> { if (typeof Dispatcher.useEffectEvent === 'function') { Dispatcher.useEffectEvent((args: empty) => {}); } + if (typeof Dispatcher.useSwipeTransition === 'function') { + Dispatcher.useSwipeTransition(null, null, null); + } } finally { readHookLog = hookLog; hookLog = []; @@ -752,31 +756,50 @@ function useEffectEvent) => mixed>(callback: F): F { return callback; } +function useSwipeTransition( + previous: T, + current: T, + next: T, +): [T, StartGesture] { + nextHook(); + hookLog.push({ + displayName: null, + primitive: 'SwipeTransition', + stackError: new Error(), + value: current, + debugInfo: null, + dispatcherHookName: 'SwipeTransition', + }); + return [current, () => () => {}]; +} + const Dispatcher: DispatcherType = { - use, readContext, - useCacheRefresh, + + use, useCallback, useContext, useEffect, useImperativeHandle, - useDebugValue, useLayoutEffect, useInsertionEffect, useMemo, - useMemoCache, - useOptimistic, useReducer, useRef, useState, + useDebugValue, + useDeferredValue, useTransition, useSyncExternalStore, - useDeferredValue, useId, + useHostTransitionStatus, useFormState, useActionState, - useHostTransitionStatus, + useOptimistic, + useMemoCache, + useCacheRefresh, useEffectEvent, + useSwipeTransition, }; // create a proxy to throw a custom error diff --git a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js index 5413db10f6f75..859e553ccbcb6 100644 --- a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js +++ b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js @@ -24,7 +24,14 @@ import { throwIfInfiniteUpdateLoopDetected, getWorkInProgressRoot, } from './ReactFiberWorkLoop'; -import {NoLane, NoLanes, mergeLanes, markHiddenUpdate} from './ReactFiberLane'; +import { + NoLane, + NoLanes, + mergeLanes, + markHiddenUpdate, + markRootUpdated, + GestureLane, +} from './ReactFiberLane'; import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; import {HostRoot, OffscreenComponent} from './ReactWorkTags'; import {OffscreenVisible} from './ReactFiberActivityComponent'; @@ -169,6 +176,25 @@ export function enqueueConcurrentRenderForLane( return getRootForUpdatedFiber(fiber); } +export function enqueueGestureRender(fiber: Fiber): FiberRoot | null { + // We can't use the concurrent queuing for these so this is basically just a + // short cut for marking the lane on the parent path. It is possible for a + // gesture render to suspend and then in the gap get another gesture starting. + // However, marking the lane doesn't make much different in this case because + // it would have to call startGesture with the same exact provider as was + // already rendering. Because otherwise it has no effect on the Hook itself. + // TODO: We could potentially solve this case by popping a ScheduledGesture + // off the root's queue while we're rendering it so that it can't dedupe + // and so new startGesture with the same provider would create a new + // ScheduledGesture which goes into a separate render pass anyway. + // This is such an edge case it probably doesn't matter much. + const root = markUpdateLaneFromFiberToRoot(fiber, null, GestureLane); + if (root !== null) { + markRootUpdated(root, GestureLane); + } + return root; +} + // Calling this function outside this module should only be done for backwards // compatibility and should always be accompanied by a warning. export function unsafe_markUpdateLaneFromFiberToRoot( @@ -189,7 +215,7 @@ function markUpdateLaneFromFiberToRoot( sourceFiber: Fiber, update: ConcurrentUpdate | null, lane: Lane, -): void { +): null | FiberRoot { // Update the source fiber's lanes sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane); let alternate = sourceFiber.alternate; @@ -238,10 +264,14 @@ function markUpdateLaneFromFiberToRoot( parent = parent.return; } - if (isHidden && update !== null && node.tag === HostRoot) { + if (node.tag === HostRoot) { const root: FiberRoot = node.stateNode; - markHiddenUpdate(root, update, lane); + if (isHidden && update !== null) { + markHiddenUpdate(root, update, lane); + } + return root; } + return null; } function getRootForUpdatedFiber(sourceFiber: Fiber): FiberRoot | null { diff --git a/packages/react-reconciler/src/ReactFiberGestureScheduler.js b/packages/react-reconciler/src/ReactFiberGestureScheduler.js new file mode 100644 index 0000000000000..7664316b67558 --- /dev/null +++ b/packages/react-reconciler/src/ReactFiberGestureScheduler.js @@ -0,0 +1,90 @@ +/** + * 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 type {FiberRoot} from './ReactInternalTypes'; +import type {GestureProvider} from 'shared/ReactTypes'; + +import {GestureLane} from './ReactFiberLane'; +import {ensureRootIsScheduled} from './ReactFiberRootScheduler'; + +// This type keeps track of any scheduled or active gestures. +export type ScheduledGesture = { + provider: GestureProvider, + count: number, // The number of times this same provider has been started. + prev: null | ScheduledGesture, // The previous scheduled gesture in the queue for this root. + next: null | ScheduledGesture, // The next scheduled gesture in the queue for this root. +}; + +export function scheduleGesture( + root: FiberRoot, + provider: GestureProvider, +): ScheduledGesture { + let prev = root.gestures; + while (prev !== null) { + if (prev.provider === provider) { + // Existing instance found. + prev.count++; + return prev; + } + const next = prev.next; + if (next === null) { + break; + } + prev = next; + } + // Add new instance to the end of the queue. + const gesture: ScheduledGesture = { + provider: provider, + count: 1, + prev: prev, + next: null, + }; + if (prev === null) { + root.gestures = gesture; + } else { + prev.next = gesture; + } + ensureRootIsScheduled(root); + return gesture; +} + +export function cancelScheduledGesture( + root: FiberRoot, + gesture: ScheduledGesture, +): void { + gesture.count--; + if (gesture.count === 0) { + // Delete the scheduled gesture from the queue. + deleteScheduledGesture(root, gesture); + } +} + +export function deleteScheduledGesture( + root: FiberRoot, + gesture: ScheduledGesture, +): void { + if (gesture.prev === null) { + if (root.gestures === gesture) { + root.gestures = gesture.next; + if (root.gestures === null) { + // Gestures don't clear their lanes while the gesture is still active but it + // might not be scheduled to do any more renders and so we shouldn't schedule + // any more gesture lane work until a new gesture is scheduled. + root.pendingLanes &= ~GestureLane; + } + } + } else { + gesture.prev.next = gesture.next; + if (gesture.next !== null) { + gesture.next.prev = gesture.prev; + } + gesture.prev = null; + gesture.next = null; + } +} diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 2ab41770bd0ac..06e00441c3b8e 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -14,6 +14,8 @@ import type { Thenable, RejectedThenable, Awaited, + StartGesture, + GestureProvider, } from 'shared/ReactTypes'; import type { Fiber, @@ -26,6 +28,7 @@ import type {Lanes, Lane} from './ReactFiberLane'; import type {HookFlags} from './ReactHookEffectTags'; import type {Flags} from './ReactFiberFlags'; import type {TransitionStatus} from './ReactFiberConfig'; +import type {ScheduledGesture} from './ReactFiberGestureScheduler'; import { HostTransitionContext, @@ -42,6 +45,7 @@ import { enableLegacyCache, disableLegacyMode, enableNoCloningMemoCache, + enableSwipeTransition, } from 'shared/ReactFeatureFlags'; import { REACT_CONTEXT_TYPE, @@ -70,6 +74,8 @@ import { isTransitionLane, markRootEntangled, includesSomeLane, + isGestureRender, + GestureLane, } from './ReactFiberLane'; import { ContinuousEventPriority, @@ -130,6 +136,7 @@ import { enqueueConcurrentHookUpdate, enqueueConcurrentHookUpdateAndEagerlyBailout, enqueueConcurrentRenderForLane, + enqueueGestureRender, } from './ReactFiberConcurrentUpdates'; import {getTreeId} from './ReactFiberTreeContext'; import {now} from './Scheduler'; @@ -153,6 +160,11 @@ import {requestCurrentTransition} from './ReactFiberTransition'; import {callComponentInDEV} from './ReactFiberCallUserSpace'; +import { + scheduleGesture, + cancelScheduledGesture, +} from './ReactFiberGestureScheduler'; + export type Update = { lane: Lane, revertLane: Lane, @@ -3960,6 +3972,133 @@ function markUpdateInDevTools
(fiber: Fiber, lane: Lane, action: A): void { } } +type SwipeTransitionGestureUpdate = { + gesture: ScheduledGesture, + prev: SwipeTransitionGestureUpdate | null, + next: SwipeTransitionGestureUpdate | null, +}; + +type SwipeTransitionUpdateQueue = { + pending: null | SwipeTransitionGestureUpdate, + dispatch: StartGesture, +}; + +function startGesture( + fiber: Fiber, + queue: SwipeTransitionUpdateQueue, + gestureProvider: GestureProvider, +): () => void { + const root = enqueueGestureRender(fiber); + if (root === null) { + // Already unmounted. + // TODO: Should we warn here about starting on an unmounted Fiber? + return function cancelGesture() { + // Noop. + }; + } + const scheduledGesture = scheduleGesture(root, gestureProvider); + // Add this particular instance to the queue. + // We add multiple of the same provider even if they get batched so + // that if we cancel one but not the other we can keep track of this. + // Order doesn't matter but we insert in the beginning to avoid two fields. + const update: SwipeTransitionGestureUpdate = { + gesture: scheduledGesture, + prev: null, + next: queue.pending, + }; + if (queue.pending !== null) { + queue.pending.prev = update; + } + queue.pending = update; + return function cancelGesture(): void { + if (update.prev === null) { + if (queue.pending === update) { + queue.pending = update.next; + } else { + // This was already cancelled. Avoid double decrementing if someone calls this twice by accident. + // TODO: Should we warn here about double cancelling? + return; + } + } else { + update.prev.next = update.next; + if (update.next !== null) { + update.next.prev = update.prev; + } + update.prev = null; + update.next = null; + } + const cancelledGestured = update.gesture; + // Decrement ref count of the root schedule. + cancelScheduledGesture(root, cancelledGestured); + }; +} + +function mountSwipeTransition( + previous: T, + current: T, + next: T, +): [T, StartGesture] { + const queue: SwipeTransitionUpdateQueue = { + pending: null, + dispatch: (null: any), + }; + const startGestureOnHook: StartGesture = (queue.dispatch = (startGesture.bind( + null, + currentlyRenderingFiber, + queue, + ): any)); + const hook = mountWorkInProgressHook(); + hook.queue = queue; + return [current, startGestureOnHook]; +} + +function updateSwipeTransition( + previous: T, + current: T, + next: T, +): [T, StartGesture] { + const hook = updateWorkInProgressHook(); + const queue: SwipeTransitionUpdateQueue = hook.queue; + const startGestureOnHook: StartGesture = queue.dispatch; + const rootRenderLanes = getWorkInProgressRootRenderLanes(); + let value = current; + if (isGestureRender(rootRenderLanes)) { + // We're inside a gesture render. We'll traverse the queue to see if + // this specific Hook is part of this gesture and, if so, which + // direction to render. + const root: FiberRoot | null = getWorkInProgressRoot(); + if (root === null) { + throw new Error( + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.', + ); + } + // We assume that the currently rendering gesture is the one first in the queue. + const rootRenderGesture = root.gestures; + let update = queue.pending; + while (update !== null) { + if (rootRenderGesture === update.gesture) { + // We had a match, meaning we're currently rendering a direction of this + // hook for this gesture. + // TODO: Determine which direction this gesture is currently rendering. + value = previous; + break; + } + update = update.next; + } + } + if (queue.pending !== null) { + // As long as there are any active gestures we need to leave the lane on + // in case we need to render it later. Since a gesture render doesn't commit + // the only time it really fully gets cleared is if something else rerenders + // this component after all the active gestures has cleared. + currentlyRenderingFiber.lanes = mergeLanes( + currentlyRenderingFiber.lanes, + GestureLane, + ); + } + return [value, startGestureOnHook]; +} + export const ContextOnlyDispatcher: Dispatcher = { readContext, @@ -3989,6 +4128,10 @@ export const ContextOnlyDispatcher: Dispatcher = { if (enableUseEffectEventHook) { (ContextOnlyDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError; } +if (enableSwipeTransition) { + (ContextOnlyDispatcher: Dispatcher).useSwipeTransition = + throwInvalidHookError; +} const HooksDispatcherOnMount: Dispatcher = { readContext, @@ -4019,6 +4162,10 @@ const HooksDispatcherOnMount: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnMount: Dispatcher).useEffectEvent = mountEvent; } +if (enableSwipeTransition) { + (HooksDispatcherOnMount: Dispatcher).useSwipeTransition = + mountSwipeTransition; +} const HooksDispatcherOnUpdate: Dispatcher = { readContext, @@ -4049,6 +4196,10 @@ const HooksDispatcherOnUpdate: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnUpdate: Dispatcher).useEffectEvent = updateEvent; } +if (enableSwipeTransition) { + (HooksDispatcherOnUpdate: Dispatcher).useSwipeTransition = + updateSwipeTransition; +} const HooksDispatcherOnRerender: Dispatcher = { readContext, @@ -4079,6 +4230,10 @@ const HooksDispatcherOnRerender: Dispatcher = { if (enableUseEffectEventHook) { (HooksDispatcherOnRerender: Dispatcher).useEffectEvent = updateEvent; } +if (enableSwipeTransition) { + (HooksDispatcherOnRerender: Dispatcher).useSwipeTransition = + updateSwipeTransition; +} let HooksDispatcherOnMountInDEV: Dispatcher | null = null; let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null; @@ -4296,6 +4451,18 @@ if (__DEV__) { return mountEvent(callback); }; } + if (enableSwipeTransition) { + (HooksDispatcherOnMountInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + mountHookTypesDev(); + return mountSwipeTransition(previous, current, next); + }; + } HooksDispatcherOnMountWithHookTypesInDEV = { readContext(context: ReactContext): T { @@ -4479,6 +4646,18 @@ if (__DEV__) { return mountEvent(callback); }; } + if (enableSwipeTransition) { + (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + updateHookTypesDev(); + return updateSwipeTransition(previous, current, next); + }; + } HooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext): T { @@ -4662,6 +4841,18 @@ if (__DEV__) { return updateEvent(callback); }; } + if (enableSwipeTransition) { + (HooksDispatcherOnUpdateInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + updateHookTypesDev(); + return updateSwipeTransition(previous, current, next); + }; + } HooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext): T { @@ -4845,6 +5036,18 @@ if (__DEV__) { return updateEvent(callback); }; } + if (enableSwipeTransition) { + (HooksDispatcherOnRerenderInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + updateHookTypesDev(); + return updateSwipeTransition(previous, current, next); + }; + } InvalidNestedHooksDispatcherOnMountInDEV = { readContext(context: ReactContext): T { @@ -5053,6 +5256,19 @@ if (__DEV__) { return mountEvent(callback); }; } + if (enableSwipeTransition) { + (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountSwipeTransition(previous, current, next); + }; + } InvalidNestedHooksDispatcherOnUpdateInDEV = { readContext(context: ReactContext): T { @@ -5261,6 +5477,19 @@ if (__DEV__) { return updateEvent(callback); }; } + if (enableSwipeTransition) { + (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateSwipeTransition(previous, current, next); + }; + } InvalidNestedHooksDispatcherOnRerenderInDEV = { readContext(context: ReactContext): T { @@ -5469,4 +5698,17 @@ if (__DEV__) { return updateEvent(callback); }; } + if (enableSwipeTransition) { + (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useSwipeTransition = + function useSwipeTransition( + previous: T, + current: T, + next: T, + ): [T, StartGesture] { + currentHookNameInDev = 'useSwipeTransition'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateSwipeTransition(previous, current, next); + }; + } } diff --git a/packages/react-reconciler/src/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.js index 922fe2f977d45..22bf99624dbb4 100644 --- a/packages/react-reconciler/src/ReactFiberLane.js +++ b/packages/react-reconciler/src/ReactFiberLane.js @@ -54,23 +54,24 @@ export const DefaultLane: Lane = /* */ 0b0000000000000000000 export const SyncUpdateLanes: Lane = SyncLane | InputContinuousLane | DefaultLane; -const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000001000000; -const TransitionLanes: Lanes = /* */ 0b0000000001111111111111110000000; -const TransitionLane1: Lane = /* */ 0b0000000000000000000000010000000; -const TransitionLane2: Lane = /* */ 0b0000000000000000000000100000000; -const TransitionLane3: Lane = /* */ 0b0000000000000000000001000000000; -const TransitionLane4: Lane = /* */ 0b0000000000000000000010000000000; -const TransitionLane5: Lane = /* */ 0b0000000000000000000100000000000; -const TransitionLane6: Lane = /* */ 0b0000000000000000001000000000000; -const TransitionLane7: Lane = /* */ 0b0000000000000000010000000000000; -const TransitionLane8: Lane = /* */ 0b0000000000000000100000000000000; -const TransitionLane9: Lane = /* */ 0b0000000000000001000000000000000; -const TransitionLane10: Lane = /* */ 0b0000000000000010000000000000000; -const TransitionLane11: Lane = /* */ 0b0000000000000100000000000000000; -const TransitionLane12: Lane = /* */ 0b0000000000001000000000000000000; -const TransitionLane13: Lane = /* */ 0b0000000000010000000000000000000; -const TransitionLane14: Lane = /* */ 0b0000000000100000000000000000000; -const TransitionLane15: Lane = /* */ 0b0000000001000000000000000000000; +export const GestureLane: Lane = /* */ 0b0000000000000000000000001000000; + +const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000010000000; +const TransitionLanes: Lanes = /* */ 0b0000000001111111111111100000000; +const TransitionLane1: Lane = /* */ 0b0000000000000000000000100000000; +const TransitionLane2: Lane = /* */ 0b0000000000000000000001000000000; +const TransitionLane3: Lane = /* */ 0b0000000000000000000010000000000; +const TransitionLane4: Lane = /* */ 0b0000000000000000000100000000000; +const TransitionLane5: Lane = /* */ 0b0000000000000000001000000000000; +const TransitionLane6: Lane = /* */ 0b0000000000000000010000000000000; +const TransitionLane7: Lane = /* */ 0b0000000000000000100000000000000; +const TransitionLane8: Lane = /* */ 0b0000000000000001000000000000000; +const TransitionLane9: Lane = /* */ 0b0000000000000010000000000000000; +const TransitionLane10: Lane = /* */ 0b0000000000000100000000000000000; +const TransitionLane11: Lane = /* */ 0b0000000000001000000000000000000; +const TransitionLane12: Lane = /* */ 0b0000000000010000000000000000000; +const TransitionLane13: Lane = /* */ 0b0000000000100000000000000000000; +const TransitionLane14: Lane = /* */ 0b0000000001000000000000000000000; const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000; const RetryLane1: Lane = /* */ 0b0000000010000000000000000000000; @@ -175,6 +176,8 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { return DefaultHydrationLane; case DefaultLane: return DefaultLane; + case GestureLane: + return GestureLane; case TransitionHydrationLane: return TransitionHydrationLane; case TransitionLane1: @@ -191,7 +194,6 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { case TransitionLane12: case TransitionLane13: case TransitionLane14: - case TransitionLane15: return lanes & TransitionLanes; case RetryLane1: case RetryLane2: @@ -459,6 +461,7 @@ function computeExpirationTime(lane: Lane, currentTime: number) { case SyncLane: case InputContinuousHydrationLane: case InputContinuousLane: + case GestureLane: // User interactions should expire slightly more quickly. // // NOTE: This is set to the corresponding constant as in Scheduler.js. @@ -486,7 +489,6 @@ function computeExpirationTime(lane: Lane, currentTime: number) { case TransitionLane12: case TransitionLane13: case TransitionLane14: - case TransitionLane15: return currentTime + transitionLaneExpirationMs; case RetryLane1: case RetryLane2: @@ -640,7 +642,8 @@ export function includesBlockingLane(lanes: Lanes): boolean { InputContinuousHydrationLane | InputContinuousLane | DefaultHydrationLane | - DefaultLane; + DefaultLane | + GestureLane; return (lanes & SyncDefaultLanes) !== NoLanes; } @@ -663,6 +666,11 @@ export function isTransitionLane(lane: Lane): boolean { return (lane & TransitionLanes) !== NoLanes; } +export function isGestureRender(lanes: Lanes): boolean { + // This should render only the one lane. + return lanes === GestureLane; +} + export function claimNextTransitionLane(): Lane { // Cycle through the lanes, assigning each new transition to the next lane. // In most cases, this means every transition gets its own lane, until we @@ -1053,7 +1061,6 @@ export function getBumpedLaneForHydrationByLane(lane: Lane): Lane { case TransitionLane12: case TransitionLane13: case TransitionLane14: - case TransitionLane15: case RetryLane1: case RetryLane2: case RetryLane3: @@ -1197,7 +1204,8 @@ export function getGroupNameOfHighestPriorityLane(lanes: Lanes): string { InputContinuousHydrationLane | InputContinuousLane | DefaultHydrationLane | - DefaultLane) + DefaultLane | + GestureLane) ) { return 'Blocking'; } diff --git a/packages/react-reconciler/src/ReactFiberRoot.js b/packages/react-reconciler/src/ReactFiberRoot.js index 4971bb4c2be34..03ddde7a5ab5b 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.js +++ b/packages/react-reconciler/src/ReactFiberRoot.js @@ -33,6 +33,7 @@ import { enableUpdaterTracking, enableTransitionTracing, disableLegacyMode, + enableSwipeTransition, } from 'shared/ReactFeatureFlags'; import {initializeUpdateQueue} from './ReactFiberClassUpdateQueue'; import {LegacyRoot, ConcurrentRoot} from './ReactRootTags'; @@ -97,6 +98,10 @@ function FiberRootNode( this.formState = formState; + if (enableSwipeTransition) { + this.gestures = null; + } + this.incompleteTransitions = new Map(); if (enableTransitionTracing) { this.transitionCallbacks = null; diff --git a/packages/react-reconciler/src/ReactFiberRootScheduler.js b/packages/react-reconciler/src/ReactFiberRootScheduler.js index 41daa6b806065..293992e40618d 100644 --- a/packages/react-reconciler/src/ReactFiberRootScheduler.js +++ b/packages/react-reconciler/src/ReactFiberRootScheduler.js @@ -20,6 +20,7 @@ import { enableComponentPerformanceTrack, enableSiblingPrerendering, enableYieldingBeforePassive, + enableSwipeTransition, } from 'shared/ReactFeatureFlags'; import { NoLane, @@ -32,6 +33,7 @@ import { claimNextTransitionLane, getNextLanesToFlushSync, checkIfRootIsPrerendering, + isGestureRender, } from './ReactFiberLane'; import { CommitContext, @@ -211,7 +213,8 @@ function flushSyncWorkAcrossRoots_impl( rootHasPendingCommit, ); if ( - includesSyncLane(nextLanes) && + (includesSyncLane(nextLanes) || + (enableSwipeTransition && isGestureRender(nextLanes))) && !checkIfRootIsPrerendering(root, nextLanes) ) { // This root has pending sync work. Flush it now. @@ -296,7 +299,8 @@ function processRootScheduleInMicrotask() { syncTransitionLanes !== NoLanes || // Common case: we're not treating any extra lanes as synchronous, so we // can just check if the next lanes are sync. - includesSyncLane(nextLanes) + includesSyncLane(nextLanes) || + (enableSwipeTransition && isGestureRender(nextLanes)) ) { mightHavePendingSyncWork = true; } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 5f17ca31b3693..b3d1e5371c06c 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -47,6 +47,7 @@ import { enableYieldingBeforePassive, enableThrottledScheduling, enableViewTransition, + enableSwipeTransition, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import is from 'shared/objectIs'; @@ -184,6 +185,8 @@ import { claimNextTransitionLane, checkIfRootIsPrerendering, includesOnlyViewTransitionEligibleLanes, + isGestureRender, + GestureLane, } from './ReactFiberLane'; import { DiscreteEventPriority, @@ -338,6 +341,7 @@ import { import {getMaskedContext, getUnmaskedContext} from './ReactFiberContext'; import {peekEntangledActionLane} from './ReactFiberAsyncAction'; import {logUncaughtError} from './ReactFiberErrorLogger'; +import {deleteScheduledGesture} from './ReactFiberGestureScheduler'; const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; @@ -3287,6 +3291,13 @@ function commitRoot( const concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes(); remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes); + if (enableSwipeTransition && root.gestures === null) { + // Gestures don't clear their lanes while the gesture is still active but it + // might not be scheduled to do any more renders and so we shouldn't schedule + // any more gesture lane work until a new gesture is scheduled. + remainingLanes &= ~GestureLane; + } + markRootFinished( root, lanes, @@ -3310,6 +3321,21 @@ function commitRoot( // times out. } + if (enableSwipeTransition && isGestureRender(lanes)) { + // This is a special kind of render that doesn't commit regular effects. + commitGestureOnRoot( + root, + finishedWork, + recoverableErrors, + enableProfilerTimer + ? suspendedCommitReason === IMMEDIATE_COMMIT + ? completedRenderEndTime + : commitStartTime + : 0, + ); + return; + } + // workInProgressX might be overwritten, so we want // to store it in pendingPassiveX until they get processed // We need to pass this through as an argument to commitRoot @@ -3802,6 +3828,24 @@ function flushSpawnedWork(): void { } } +function commitGestureOnRoot( + root: FiberRoot, + finishedWork: null | Fiber, + recoverableErrors: null | Array>, + renderEndTime: number, // Profiling-only +): void { + // We assume that the gesture we just rendered was the first one in the queue. + const finishedGesture = root.gestures; + if (finishedGesture === null) { + throw new Error( + 'Finished rendering the gesture lane but there were no pending gestures. ' + + 'React should not have started a render in this case. This is a bug in React.', + ); + } + deleteScheduledGesture(root, finishedGesture); + // TODO: Run the gesture +} + function makeErrorInfo(componentStack: ?string) { const errorInfo = { componentStack, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 98e7d4deefea3..3479eba1b9dd3 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -17,6 +17,7 @@ import type { Awaited, ReactComponentInfo, ReactDebugInfo, + StartGesture, } from 'shared/ReactTypes'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; @@ -38,6 +39,7 @@ import type { import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates'; import type {ComponentStackNode} from 'react-server/src/ReactFizzComponentStack'; import type {ThenableState} from './ReactFiberThenable'; +import type {ScheduledGesture} from './ReactFiberGestureScheduler'; // Unwind Circular: moved from ReactFiberHooks.old export type HookType = @@ -60,7 +62,8 @@ export type HookType = | 'useCacheRefresh' | 'useOptimistic' | 'useFormState' - | 'useActionState'; + | 'useActionState' + | 'useSwipeTransition'; export type ContextDependency = { context: ReactContext, @@ -279,6 +282,9 @@ type BaseFiberRootProperties = { ) => void, formState: ReactFormState | null, + + // enableSwipeTransition only + gestures: null | ScheduledGesture, }; // The following attributes are only used by DevTools and are only present in DEV builds. @@ -442,6 +448,12 @@ export type Dispatcher = { initialState: Awaited, permalink?: string, ) => [Awaited, (P) => void, boolean], + // TODO: Non-nullable once `enableSwipeTransition` is on everywhere. + useSwipeTransition?: ( + previous: T, + current: T, + next: T, + ) => [T, StartGesture], }; export type AsyncDispatcher = { diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index a0ec1c7414982..8ae00568ae670 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -16,6 +16,7 @@ import type { Usable, ReactCustomFormAction, Awaited, + StartGesture, } from 'shared/ReactTypes'; import type {ResumableState} from './ReactFizzConfig'; @@ -38,7 +39,10 @@ import { } from './ReactFizzConfig'; import {createFastHash} from './ReactServerStreamConfig'; -import {enableUseEffectEventHook} from 'shared/ReactFeatureFlags'; +import { + enableUseEffectEventHook, + enableSwipeTransition, +} from 'shared/ReactFeatureFlags'; import is from 'shared/objectIs'; import { REACT_CONTEXT_TYPE, @@ -795,6 +799,19 @@ function useMemoCache(size: number): Array { return data; } +function unsupportedStartGesture() { + throw new Error('startGesture cannot be called during server rendering.'); +} + +function useSwipeTransition( + previous: T, + current: T, + next: T, +): [T, StartGesture] { + resolveCurrentlyRenderingComponent(); + return [current, unsupportedStartGesture]; +} + function noop(): void {} function clientHookNotSupported() { @@ -837,25 +854,25 @@ export const HooksDispatcher: Dispatcher = supportsClientAPIs : { readContext, use, + useCallback, useContext, + useEffect: clientHookNotSupported, + useImperativeHandle: clientHookNotSupported, + useInsertionEffect: clientHookNotSupported, + useLayoutEffect: clientHookNotSupported, useMemo, useReducer: clientHookNotSupported, useRef: clientHookNotSupported, useState: clientHookNotSupported, - useInsertionEffect: clientHookNotSupported, - useLayoutEffect: clientHookNotSupported, - useCallback, - useImperativeHandle: clientHookNotSupported, - useEffect: clientHookNotSupported, useDebugValue: noop, useDeferredValue: clientHookNotSupported, useTransition: clientHookNotSupported, - useId, useSyncExternalStore: clientHookNotSupported, - useOptimistic, - useActionState, - useFormState: useActionState, + useId, useHostTransitionStatus, + useFormState: useActionState, + useActionState, + useOptimistic, useMemoCache, useCacheRefresh, }; @@ -863,6 +880,11 @@ export const HooksDispatcher: Dispatcher = supportsClientAPIs if (enableUseEffectEventHook) { HooksDispatcher.useEffectEvent = useEffectEvent; } +if (enableSwipeTransition) { + HooksDispatcher.useSwipeTransition = supportsClientAPIs + ? useSwipeTransition + : clientHookNotSupported; +} export let currentResumableState: null | ResumableState = (null: any); export function setCurrentResumableState( diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index d0351e38c86aa..f1d31f3e486eb 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -17,6 +17,10 @@ import { } from 'shared/ReactSymbols'; import {createThenableState, trackUsedThenable} from './ReactFlightThenable'; import {isClientReference} from './ReactFlightServerConfig'; +import { + enableUseEffectEventHook, + enableSwipeTransition, +} from 'shared/ReactFeatureFlags'; let currentRequest = null; let thenableIndexCounter = 0; @@ -58,33 +62,32 @@ export function getThenableStateAfterSuspending(): ThenableState { } export const HooksDispatcher: Dispatcher = { - useMemo(nextCreate: () => T): T { - return nextCreate(); - }, + readContext: (unsupportedContext: any), + + use, useCallback(callback: T): T { return callback; }, - useDebugValue(): void {}, - useDeferredValue: (unsupportedHook: any), - useTransition: (unsupportedHook: any), - readContext: (unsupportedContext: any), useContext: (unsupportedContext: any), + useEffect: (unsupportedHook: any), + useImperativeHandle: (unsupportedHook: any), + useLayoutEffect: (unsupportedHook: any), + useInsertionEffect: (unsupportedHook: any), + useMemo(nextCreate: () => T): T { + return nextCreate(); + }, useReducer: (unsupportedHook: any), useRef: (unsupportedHook: any), useState: (unsupportedHook: any), - useInsertionEffect: (unsupportedHook: any), - useLayoutEffect: (unsupportedHook: any), - useImperativeHandle: (unsupportedHook: any), - useEffect: (unsupportedHook: any), + useDebugValue(): void {}, + useDeferredValue: (unsupportedHook: any), + useTransition: (unsupportedHook: any), + useSyncExternalStore: (unsupportedHook: any), useId, useHostTransitionStatus: (unsupportedHook: any), - useOptimistic: (unsupportedHook: any), useFormState: (unsupportedHook: any), useActionState: (unsupportedHook: any), - useSyncExternalStore: (unsupportedHook: any), - useCacheRefresh(): (?() => T, ?T) => void { - return unsupportedRefresh; - }, + useOptimistic: (unsupportedHook: any), useMemoCache(size: number): Array { const data = new Array(size); for (let i = 0; i < size; i++) { @@ -92,8 +95,16 @@ export const HooksDispatcher: Dispatcher = { } return data; }, - use, + useCacheRefresh(): (?() => T, ?T) => void { + return unsupportedRefresh; + }, }; +if (enableUseEffectEventHook) { + HooksDispatcher.useEffectEvent = (unsupportedHook: any); +} +if (enableSwipeTransition) { + HooksDispatcher.useSwipeTransition = (unsupportedHook: any); +} function unsupportedHook(): void { throw new Error('This Hook is not supported in Server Components.'); diff --git a/packages/react/index.experimental.development.js b/packages/react/index.experimental.development.js index 6074b683b781d..e8ebc236133b0 100644 --- a/packages/react/index.experimental.development.js +++ b/packages/react/index.experimental.development.js @@ -33,6 +33,7 @@ export { unstable_getCacheForType, unstable_SuspenseList, unstable_ViewTransition, + unstable_useSwipeTransition, unstable_addTransitionType, unstable_useCacheRefresh, useId, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index 77cf6bd0e0317..a3c6f4a10f090 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -33,6 +33,7 @@ export { unstable_getCacheForType, unstable_SuspenseList, unstable_ViewTransition, + unstable_useSwipeTransition, unstable_addTransitionType, unstable_useCacheRefresh, useId, diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index 715ea8ab47c35..33175a9a77f35 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -57,6 +57,7 @@ import { use, useOptimistic, useActionState, + useSwipeTransition, } from './ReactHooks'; import ReactSharedInternals from './ReactSharedInternalsClient'; import {startTransition} from './ReactStartTransition'; @@ -126,7 +127,10 @@ export { // enableViewTransition REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition, addTransitionType as unstable_addTransitionType, + // enableSwipeTransition + useSwipeTransition as unstable_useSwipeTransition, + // DEV-only useId, - act, // DEV-only - captureOwnerStack, // DEV-only + act, + captureOwnerStack, }; diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index ba6b0ffbcb71e..605337480f731 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -13,12 +13,16 @@ import type { StartTransitionOptions, Usable, Awaited, + StartGesture, } from 'shared/ReactTypes'; import {REACT_CONSUMER_TYPE} from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {enableUseEffectCRUDOverload} from 'shared/ReactFeatureFlags'; +import { + enableUseEffectCRUDOverload, + enableSwipeTransition, +} from 'shared/ReactFeatureFlags'; type BasicStateAction = (S => S) | S; type Dispatch = A => void; @@ -261,3 +265,16 @@ export function useActionState( const dispatcher = resolveDispatcher(); return dispatcher.useActionState(action, initialState, permalink); } + +export function useSwipeTransition( + previous: T, + current: T, + next: T, +): [T, StartGesture] { + if (!enableSwipeTransition) { + throw new Error('Not implemented.'); + } + const dispatcher = resolveDispatcher(); + // $FlowFixMe[not-a-function] This is unstable, thus optional + return dispatcher.useSwipeTransition(previous, current, next); +} diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 844b2a2f6a914..ea09d0a3c76e3 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -92,6 +92,8 @@ export const enableHalt = __EXPERIMENTAL__; export const enableViewTransition = __EXPERIMENTAL__; +export const enableSwipeTransition = __EXPERIMENTAL__; + /** * Switches Fiber creation to a simple object instead of a constructor. */ diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 735f96a36f509..0402f9aac1832 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -168,6 +168,12 @@ export type ReactFormState = [ number /* number of bound arguments */, ]; +// Intrinsic GestureProvider. This type varies by Environment whether a particular +// renderer supports it. +export type GestureProvider = AnimationTimeline; // TODO: More provider types. + +export type StartGesture = (gestureProvider: GestureProvider) => () => void; + export type Awaited = T extends null | void ? T // special case for `null | undefined` when not in `--strictNullChecks` mode : T extends Object // `await` only unwraps object types with a callable then. Non-object types are not unwrapped. diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 2eff113ae42f1..470e148959327 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -82,6 +82,7 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableSwipeTransition = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index bcd8b375eca67..361ebde4136ce 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -71,6 +71,7 @@ export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index ef3c300602bdb..5945416050286 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -70,6 +70,7 @@ export const enableYieldingBeforePassive = true; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = true; export const enableLazyPublicInstanceInFabric = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 743ba39a86792..18172fdfe5724 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -67,6 +67,7 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableSwipeTransition = false; export const enableRemoveConsolePatches = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 26747d3fb69a9..628a834133f2f 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -82,6 +82,7 @@ export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableSwipeTransition = false; export const enableRemoveConsolePatches = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 8da74c54bd69e..5932c6eddf6f1 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -112,5 +112,7 @@ export const enableShallowPropDiffing = false; export const enableLazyPublicInstanceInFabric = false; +export const enableSwipeTransition = false; + // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 6ab654f1d35e5..f001b660bd2aa 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -531,5 +531,7 @@ "543": "Expected a ResourceEffectUpdate to be pushed together with ResourceEffectIdentity. This is a bug in React.", "544": "Found a pair with an auto name. This is a bug in React.", "545": "The %s tag may only be rendered once.", - "546": "useEffect CRUD overload is not enabled in this build of React." + "546": "useEffect CRUD overload is not enabled in this build of React.", + "547": "startGesture cannot be called during server rendering.", + "548": "Finished rendering the gesture lane but there were no pending gestures. React should not have started a render in this case. This is a bug in React." } From e670e72fa076449e40172e20d17cc67c1c15419c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ma=C5=82ecki?= Date: Fri, 14 Feb 2025 15:10:37 +0100 Subject: [PATCH 029/300] Change TouchedViewDataAtPoint type in ReactNativeTypes to use supported by Flow tooling syntax (#32382) ## Summary The `flow-api-translator` from the `hermes` repo does not support flow type spreads. It is currently not able to digest the ReactNativeTypes file as it contains unsupported syntax. The simplest solution is to change the type of the `TouchedViewDataAtPoint` to equivalent, yet supported by the Flow tooling. In this case the intersection can be used as the `TouchedViewDataAtPoint` and `InspectorData` have no common property. ## How did you test this change? Run yarn flow native --- .../src/ReactNativeTypes.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 8bce919d385e9..261e1f0b63104 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -164,18 +164,19 @@ export type InspectorData = $ReadOnly<{ componentStack: string, }>; -export type TouchedViewDataAtPoint = $ReadOnly<{ - pointerY: number, - touchedViewTag?: number, - frame: $ReadOnly<{ - top: number, - left: number, - width: number, - height: number, - }>, - closestPublicInstance?: PublicInstance, - ...InspectorData, -}>; +export type TouchedViewDataAtPoint = $ReadOnly< + { + pointerY: number, + touchedViewTag?: number, + frame: $ReadOnly<{ + top: number, + left: number, + width: number, + height: number, + }>, + closestPublicInstance?: PublicInstance, + } & InspectorData, +>; export type RenderRootOptions = { onUncaughtError?: ( From 0d9834caeb3b334eaa1be45f136499f51303e7d3 Mon Sep 17 00:00:00 2001 From: michael faith Date: Sun, 16 Feb 2025 09:38:13 -0600 Subject: [PATCH 030/300] build: add support to the rollup build for building typescript packages (#32393) --- babel.config-ts.js | 15 + package.json | 5 +- .../__tests__/ReactTypeScriptClass-test.ts | 4 +- scripts/jest/config.base.js | 6 +- scripts/rollup/build.js | 45 +- scripts/rollup/bundles.js | 7 + scripts/rollup/packaging.js | 4 + scripts/rollup/wrappers.js | 36 ++ yarn.lock | 488 +++++++++++++----- 9 files changed, 459 insertions(+), 151 deletions(-) create mode 100644 babel.config-ts.js diff --git a/babel.config-ts.js b/babel.config-ts.js new file mode 100644 index 0000000000000..20b35ec013e0f --- /dev/null +++ b/babel.config-ts.js @@ -0,0 +1,15 @@ +/** + * This file is purely being used for local jest runs, and doesn't participate in the build process. + */ +'use strict'; + +module.exports = { + plugins: [ + '@babel/plugin-syntax-jsx', + '@babel/plugin-transform-flow-strip-types', + ], + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + '@babel/preset-typescript', + ], +}; diff --git a/package.json b/package.json index cffd0ba28b493..95fffa700a7e7 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,13 @@ "@babel/plugin-transform-template-literals": "^7.10.5", "@babel/preset-flow": "^7.10.4", "@babel/preset-react": "^7.23.3", + "@babel/preset-typescript": "^7.26.0", "@babel/traverse": "^7.11.0", "@rollup/plugin-babel": "^6.0.3", "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-replace": "^5.0.2", + "@rollup/plugin-typescript": "^12.1.2", "abortcontroller-polyfill": "^1.7.5", "art": "0.10.1", "babel-plugin-syntax-trailing-function-commas": "^6.5.0", @@ -90,6 +92,7 @@ "react-lifecycles-compat": "^3.0.4", "rimraf": "^3.0.0", "rollup": "^3.29.5", + "rollup-plugin-dts": "^6.1.1", "rollup-plugin-prettier": "^4.1.1", "rollup-plugin-strip-banner": "^3.0.0", "semver": "^7.1.1", @@ -98,7 +101,7 @@ "targz": "^1.0.1", "through2": "^3.0.1", "tmp": "^0.1.0", - "typescript": "^3.7.5", + "typescript": "^5.4.3", "undici": "^5.28.4", "web-streams-polyfill": "^3.1.1", "yargs": "^15.3.1" diff --git a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts index 5f51cc6f38c77..603edb86ea495 100644 --- a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts +++ b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts @@ -529,7 +529,7 @@ describe('ReactTypeScriptClass', function () { 'StateBasedOnContext uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + (ReactFeatureFlags.enableOwnerStacks - ? ' in ProvideChildContextTypes.Object..ProvideChildContextTypes (at **)' + ? ' in ProvideChildContextTypes.createElement (at **)' : ' in StateBasedOnContext (at **)\n') + ' in ProvideChildContextTypes (at **)', ]); @@ -725,7 +725,7 @@ describe('ReactTypeScriptClass', function () { 'ReadContext uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + (ReactFeatureFlags.enableOwnerStacks - ? ' in ProvideContext.Object..ProvideContext (at **)' + ? ' in ProvideContext.createElement (at **)' : ' in ReadContext (at **)\n') + ' in ProvideContext (at **)', ]); diff --git a/scripts/jest/config.base.js b/scripts/jest/config.base.js index 6fa4a3619429f..262c6ab18a144 100644 --- a/scripts/jest/config.base.js +++ b/scripts/jest/config.base.js @@ -7,7 +7,11 @@ module.exports = { '/scripts/bench/', ], transform: { - '.*': require.resolve('./preprocessor.js'), + '^.+\\.ts$': [ + 'babel-jest', + {configFile: require.resolve('../../babel.config-ts.js')}, + ], + '.(?!ts$)': require.resolve('./preprocessor.js'), }, prettierPath: require.resolve('prettier-2'), setupFiles: [require.resolve('./setupEnvironment.js')], diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 547362dae7123..53b308d16a2d8 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -4,8 +4,10 @@ const rollup = require('rollup'); const babel = require('@rollup/plugin-babel').babel; const closure = require('./plugins/closure-plugin'); const flowRemoveTypes = require('flow-remove-types'); +const {dts} = require('rollup-plugin-dts'); const prettier = require('rollup-plugin-prettier'); const replace = require('@rollup/plugin-replace'); +const typescript = require('@rollup/plugin-typescript'); const stripBanner = require('rollup-plugin-strip-banner'); const chalk = require('chalk'); const resolve = require('@rollup/plugin-node-resolve').nodeResolve; @@ -61,6 +63,8 @@ const { RN_FB_PROD, RN_FB_PROFILING, BROWSER_SCRIPT, + CJS_DTS, + ESM_DTS, } = Bundles.bundleTypes; const {getFilename} = Bundles; @@ -250,9 +254,11 @@ function getFormat(bundleType) { case RN_FB_DEV: case RN_FB_PROD: case RN_FB_PROFILING: + case CJS_DTS: return `cjs`; case ESM_DEV: case ESM_PROD: + case ESM_DTS: return `es`; case BROWSER_SCRIPT: return `iife`; @@ -281,6 +287,8 @@ function isProductionBundleType(bundleType) { case RN_FB_PROD: case RN_FB_PROFILING: case BROWSER_SCRIPT: + case CJS_DTS: + case ESM_DTS: return true; default: throw new Error(`Unknown type: ${bundleType}`); @@ -303,6 +311,8 @@ function isProfilingBundleType(bundleType) { case ESM_DEV: case ESM_PROD: case BROWSER_SCRIPT: + case CJS_DTS: + case ESM_DTS: return false; case FB_WWW_PROFILING: case NODE_PROFILING: @@ -368,27 +378,36 @@ function getPlugins( pureExternalModules, bundle ) { + // Short-circuit if we're only building a .d.ts bundle + if (bundleType === CJS_DTS || bundleType === ESM_DTS) { + return [dts({tsconfig: bundle.tsconfig})]; + } try { const forks = Modules.getForks(bundleType, entry, moduleType, bundle); const isProduction = isProductionBundleType(bundleType); const isProfiling = isProfilingBundleType(bundleType); const needsMinifiedByClosure = - bundleType !== ESM_PROD && bundleType !== ESM_DEV; + bundleType !== ESM_PROD && + bundleType !== ESM_DEV && + // TODO(@poteto) figure out ICE in closure compiler for eslint-plugin-react-hooks (ts) + bundle.tsconfig == null; return [ // Keep dynamic imports as externals dynamicImports(), - { - name: 'rollup-plugin-flow-remove-types', - transform(code) { - const transformed = flowRemoveTypes(code); - return { - code: transformed.toString(), - map: null, - }; - }, - }, + bundle.tsconfig != null + ? typescript({tsconfig: bundle.tsconfig}) + : { + name: 'rollup-plugin-flow-remove-types', + transform(code) { + const transformed = flowRemoveTypes(code); + return { + code: transformed.toString(), + map: null, + }; + }, + }, // Shim any modules that need forking in this environment. useForks(forks), // Ensure we don't try to bundle any fbjs modules. @@ -839,7 +858,9 @@ async function buildEverything() { [bundle, RN_FB_DEV], [bundle, RN_FB_PROD], [bundle, RN_FB_PROFILING], - [bundle, BROWSER_SCRIPT] + [bundle, BROWSER_SCRIPT], + [bundle, CJS_DTS], + [bundle, ESM_DTS] ); } diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 04bfb1a0e5479..c66cff82c5ac2 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -26,6 +26,8 @@ const bundleTypes = { RN_FB_PROD: 'RN_FB_PROD', RN_FB_PROFILING: 'RN_FB_PROFILING', BROWSER_SCRIPT: 'BROWSER_SCRIPT', + CJS_DTS: 'CJS_DTS', + ESM_DTS: 'ESM_DTS', }; const { @@ -47,6 +49,8 @@ const { RN_FB_PROD, RN_FB_PROFILING, BROWSER_SCRIPT, + CJS_DTS, + ESM_DTS, } = bundleTypes; const moduleTypes = { @@ -1270,6 +1274,9 @@ function getFilename(bundle, bundleType) { return `${globalName}-profiling.js`; case BROWSER_SCRIPT: return `${name}.js`; + case CJS_DTS: + case ESM_DTS: + return `${name}.d.ts`; } } diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js index bc83cb8789eb8..564c40f71b922 100644 --- a/scripts/rollup/packaging.js +++ b/scripts/rollup/packaging.js @@ -34,6 +34,8 @@ const { RN_FB_PROD, RN_FB_PROFILING, BROWSER_SCRIPT, + CJS_DTS, + ESM_DTS, } = Bundles.bundleTypes; function getPackageName(name) { @@ -49,6 +51,7 @@ function getBundleOutputPath(bundle, bundleType, filename, packageName) { return `build/node_modules/${packageName}/cjs/${filename}`; case ESM_DEV: case ESM_PROD: + case ESM_DTS: return `build/node_modules/${packageName}/esm/${filename}`; case BUN_DEV: case BUN_PROD: @@ -56,6 +59,7 @@ function getBundleOutputPath(bundle, bundleType, filename, packageName) { case NODE_DEV: case NODE_PROD: case NODE_PROFILING: + case CJS_DTS: return `build/node_modules/${packageName}/cjs/${filename}`; case FB_WWW_DEV: case FB_WWW_PROD: diff --git a/scripts/rollup/wrappers.js b/scripts/rollup/wrappers.js index e01ca30a6d054..1773253cc9653 100644 --- a/scripts/rollup/wrappers.js +++ b/scripts/rollup/wrappers.js @@ -21,6 +21,8 @@ const { RN_FB_PROD, RN_FB_PROFILING, BROWSER_SCRIPT, + CJS_DTS, + ESM_DTS, } = bundleTypes; const {RECONCILER} = moduleTypes; @@ -248,6 +250,16 @@ ${source} Object.defineProperty(module.exports, "__esModule", { value: true }); `; }, + + /***************** CJS_DTS *****************/ + [CJS_DTS](source, globalName, filename, moduleType) { + return source; + }, + + /***************** ESM_DTS *****************/ + [ESM_DTS](source, globalName, filename, moduleType) { + return source; + }, }; const licenseHeaderWrappers = { @@ -464,6 +476,30 @@ ${license} * @preventMunge */ +${source}`; + }, + + /***************** CJS_DTS *****************/ + [CJS_DTS](source, globalName, filename, moduleType) { + return `/** + * @license React + * ${filename} + * +${license} + */ + +${source}`; + }, + + /***************** ESM_DTS *****************/ + [ESM_DTS](source, globalName, filename, moduleType) { + return `/** + * @license React + * ${filename} + * +${license} + */ + ${source}`; }, }; diff --git a/yarn.lock b/yarn.lock index 4922028dafc3b..44d590625111c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,6 +68,15 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.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== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -225,6 +234,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" +"@babel/generator@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== + dependencies: + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/generator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03" @@ -263,6 +283,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" @@ -369,6 +396,19 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" @@ -506,6 +546,14 @@ dependencies: "@babel/types" "^7.20.7" +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-imports@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" @@ -534,6 +582,14 @@ dependencies: "@babel/types" "^7.22.15" +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" @@ -572,6 +628,15 @@ "@babel/helper-split-export-declaration" "^7.24.5" "@babel/helper-validator-identifier" "^7.24.5" +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/helper-optimise-call-expression@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" @@ -593,6 +658,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a" @@ -608,6 +680,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== +"@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + "@babel/helper-plugin-utils@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" @@ -663,6 +740,15 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + "@babel/helper-simple-access@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" @@ -706,6 +792,14 @@ dependencies: "@babel/types" "^7.20.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" @@ -744,6 +838,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.10.4": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" @@ -769,6 +868,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" @@ -779,6 +883,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + "@babel/helper-wrap-function@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" @@ -905,6 +1014,13 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== +"@babel/parser@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== + dependencies: + "@babel/types" "^7.26.9" + "@babel/plugin-external-helpers@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.10.4.tgz#40d38e8e48a1fa3766ab43496253266ca26783ce" @@ -1197,6 +1313,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-jsx@^7.7.2": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" @@ -1281,6 +1404,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-typescript@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-typescript@^7.7.2": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" @@ -1533,6 +1663,14 @@ "@babel/helper-simple-access" "^7.16.0" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== + dependencies: + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-modules-systemjs@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" @@ -1794,6 +1932,17 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript" "^7.16.0" +"@babel/plugin-transform-typescript@^7.25.9": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz#2e9caa870aa102f50d7125240d9dbf91334b0950" + integrity sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-syntax-typescript" "^7.25.9" + "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -1936,6 +2085,17 @@ "@babel/helper-validator-option" "^7.14.5" "@babel/plugin-transform-typescript" "^7.16.0" +"@babel/preset-typescript@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-typescript" "^7.25.9" + "@babel/register@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233" @@ -1995,6 +2155,15 @@ "@babel/parser" "^7.24.0" "@babel/types" "^7.24.0" +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@babel/template@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" @@ -2096,6 +2265,19 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.7", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.3.3": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" @@ -2148,6 +2330,14 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.25.9", "@babel/types@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/types@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -2785,6 +2975,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.9": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" @@ -3175,6 +3370,14 @@ "@rollup/pluginutils" "^5.0.1" magic-string "^0.27.0" +"@rollup/plugin-typescript@^12.1.2": + version "12.1.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-12.1.2.tgz#ebaeec2e7376faa889030ccd7cb485a649e63118" + integrity sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg== + dependencies: + "@rollup/pluginutils" "^5.1.0" + resolve "^1.22.1" + "@rollup/pluginutils@^5.0.1": version "5.0.2" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" @@ -3184,100 +3387,109 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz#c18bad635ba24220a6c8cc427ab2cab12e1531a3" - integrity sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA== - -"@rollup/rollup-android-arm64@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz#b5c00344b80f20889b72bfe65d3c209cef247362" - integrity sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q== - -"@rollup/rollup-darwin-arm64@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz#78e5358d4a2a08c090f75dd87fa2eada42eca1e5" - integrity sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA== - -"@rollup/rollup-darwin-x64@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz#c04c9e173244d44de50278f3f893fb68d987fcc6" - integrity sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q== - -"@rollup/rollup-freebsd-arm64@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz#3bdf18d4ef32dcfe9b20bba18d7a53a101ed79d9" - integrity sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA== - -"@rollup/rollup-freebsd-x64@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz#35867b15c276f4b4ca8eb226f7dd6df8c64640db" - integrity sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw== - -"@rollup/rollup-linux-arm-gnueabihf@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz#92c212d1b38c105bd1eb101254722d27d869b1ac" - integrity sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g== - -"@rollup/rollup-linux-arm-musleabihf@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz#ebb94d8cd438f23e38caa4a87ca80d4cf5b50fa1" - integrity sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q== - -"@rollup/rollup-linux-arm64-gnu@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz#ce6a5eacbd5fd4bdf7bf27bd818980230bdb9fab" - integrity sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw== - -"@rollup/rollup-linux-arm64-musl@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz#31b4e0a543607e6eb4f982ffb45830919a952a83" - integrity sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw== - -"@rollup/rollup-linux-loongarch64-gnu@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz#ad7b35f193f1d2e0dc37eba733069b4af5f6498d" - integrity sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw== - -"@rollup/rollup-linux-powerpc64le-gnu@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz#b713a55d7eac4d2c8a0109c3daca6ea85fc178b3" - integrity sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg== - -"@rollup/rollup-linux-riscv64-gnu@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz#bea4fd8ad190e9bc1d11efafa2efc9d121f50b96" - integrity sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g== - -"@rollup/rollup-linux-s390x-gnu@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz#cc98c32733ca472635759c78a79b5f8d887b2a6a" - integrity sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ== - -"@rollup/rollup-linux-x64-gnu@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz#5c009c264a7ce0e19b40890ca9945440bb420691" - integrity sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg== - -"@rollup/rollup-linux-x64-musl@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz#73d2f44070c23e031262b601927fdb4eec253bc1" - integrity sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA== - -"@rollup/rollup-win32-arm64-msvc@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz#fa106304818078f9d3fc9005642ad99f596eed2d" - integrity sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ== - -"@rollup/rollup-win32-ia32-msvc@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz#a1a394c705a0d2a974a124c4b471fc1cf851a56f" - integrity sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ== - -"@rollup/rollup-win32-x64-msvc@4.32.1": - version "4.32.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz#512db088df67afee8f07183cdf8c9eecd64f6ef8" - integrity sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q== +"@rollup/pluginutils@^5.1.0": + version "5.1.4" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a" + integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^4.0.2" + +"@rollup/rollup-android-arm-eabi@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz#e554185b1afa5509a7a4040d15ec0c3b4435ded1" + integrity sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw== + +"@rollup/rollup-android-arm64@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz#b1ee64bb413b2feba39803b0a1bebf2a9f3d70e1" + integrity sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg== + +"@rollup/rollup-darwin-arm64@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz#bfdce3e07a345dd1bd628f3b796050f39629d7f0" + integrity sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA== + +"@rollup/rollup-darwin-x64@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz#781a94a537c57bdf0a500e47a25ab5985e5e8dff" + integrity sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA== + +"@rollup/rollup-freebsd-arm64@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz#7a028357cbd12c5869c446ad18177c89f3405102" + integrity sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA== + +"@rollup/rollup-freebsd-x64@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz#f24836a6371cccc4408db74f0fd986dacf098950" + integrity sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz#95f27e96f0eb9b9ae9887739a8b6dffc90c1237f" + integrity sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ== + +"@rollup/rollup-linux-arm-musleabihf@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz#677b34fba9d070877736c3fe8b02aacb5e142d97" + integrity sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw== + +"@rollup/rollup-linux-arm64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz#32d3d19dedde54e91574a098f22ea43a09cf63dd" + integrity sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA== + +"@rollup/rollup-linux-arm64-musl@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz#a58dff44a18696df65ed8c0ad68a2945cf900484" + integrity sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz#a7488ab078233111e8aeb370d1ecf107ec7e1716" + integrity sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz#e9b9c0d6bd248a92b2d6ec01ebf99c62ae1f2e9a" + integrity sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w== + +"@rollup/rollup-linux-riscv64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz#0df84ce2bea48ee686fb55060d76ab47aff45c4c" + integrity sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw== + +"@rollup/rollup-linux-s390x-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz#73df374c57d036856e33dbd2715138922e91e452" + integrity sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw== + +"@rollup/rollup-linux-x64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz#f27af0b55f0cdd84e182e6cd44a6d03da0458149" + integrity sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA== + +"@rollup/rollup-linux-x64-musl@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz#c7981ad5cfb8c3cd5d643d33ca54e4d2802b9201" + integrity sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ== + +"@rollup/rollup-win32-arm64-msvc@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz#06cedc0ef3cbf1cbd8abcf587090712e40ae6941" + integrity sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw== + +"@rollup/rollup-win32-ia32-msvc@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz#90b39b977b14961a769be6ea61238e7fc668dd4d" + integrity sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg== + +"@rollup/rollup-win32-x64-msvc@4.34.7": + version "4.34.7" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz#6531d61e7141091eaab0461ee8e0380c10e4ca57" + integrity sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w== "@sinclair/typebox@^0.25.16": version "0.25.24" @@ -3468,21 +3680,11 @@ dependencies: "@types/estree" "*" -"@types/estree@*": - version "0.0.42" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11" - integrity sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ== - -"@types/estree@1.0.6", "@types/estree@^1.0.6": +"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/estree@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": version "4.17.35" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f" @@ -11370,6 +11572,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" @@ -11976,6 +12183,13 @@ magic-string@^0.27.0: dependencies: "@jridgewell/sourcemap-codec" "^1.4.13" +magic-string@^0.30.10: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-dir@^1.0.0, make-dir@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -14343,9 +14557,9 @@ readdirp@^2.2.1: readable-stream "^2.0.2" readdirp@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.1.tgz#bd115327129672dc47f87408f05df9bd9ca3ef55" - integrity sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw== + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== readdirp@~3.6.0: version "3.6.0" @@ -14744,6 +14958,15 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +rollup-plugin-dts@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-6.1.1.tgz#46b33f4d1d7f4e66f1171ced9b282ac11a15a254" + integrity sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA== + dependencies: + magic-string "^0.30.10" + optionalDependencies: + "@babel/code-frame" "^7.24.2" + rollup-plugin-prettier@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/rollup-plugin-prettier/-/rollup-plugin-prettier-4.1.1.tgz#eb74bd47c3cc3ba68bdf34b5323d0d7a47be8cec" @@ -14781,31 +15004,31 @@ rollup@^3.29.5: fsevents "~2.3.2" rollup@^4.24.0: - version "4.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.32.1.tgz#95309604d92c3d21cbf06c3ee46a098209ce13a4" - integrity sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA== + version "4.34.7" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.7.tgz#e00d8550688a616a3481c6446bb688d4c753ba8f" + integrity sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ== dependencies: "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.32.1" - "@rollup/rollup-android-arm64" "4.32.1" - "@rollup/rollup-darwin-arm64" "4.32.1" - "@rollup/rollup-darwin-x64" "4.32.1" - "@rollup/rollup-freebsd-arm64" "4.32.1" - "@rollup/rollup-freebsd-x64" "4.32.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.32.1" - "@rollup/rollup-linux-arm-musleabihf" "4.32.1" - "@rollup/rollup-linux-arm64-gnu" "4.32.1" - "@rollup/rollup-linux-arm64-musl" "4.32.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.32.1" - "@rollup/rollup-linux-powerpc64le-gnu" "4.32.1" - "@rollup/rollup-linux-riscv64-gnu" "4.32.1" - "@rollup/rollup-linux-s390x-gnu" "4.32.1" - "@rollup/rollup-linux-x64-gnu" "4.32.1" - "@rollup/rollup-linux-x64-musl" "4.32.1" - "@rollup/rollup-win32-arm64-msvc" "4.32.1" - "@rollup/rollup-win32-ia32-msvc" "4.32.1" - "@rollup/rollup-win32-x64-msvc" "4.32.1" + "@rollup/rollup-android-arm-eabi" "4.34.7" + "@rollup/rollup-android-arm64" "4.34.7" + "@rollup/rollup-darwin-arm64" "4.34.7" + "@rollup/rollup-darwin-x64" "4.34.7" + "@rollup/rollup-freebsd-arm64" "4.34.7" + "@rollup/rollup-freebsd-x64" "4.34.7" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.7" + "@rollup/rollup-linux-arm-musleabihf" "4.34.7" + "@rollup/rollup-linux-arm64-gnu" "4.34.7" + "@rollup/rollup-linux-arm64-musl" "4.34.7" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.7" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.7" + "@rollup/rollup-linux-riscv64-gnu" "4.34.7" + "@rollup/rollup-linux-s390x-gnu" "4.34.7" + "@rollup/rollup-linux-x64-gnu" "4.34.7" + "@rollup/rollup-linux-x64-musl" "4.34.7" + "@rollup/rollup-win32-arm64-msvc" "4.34.7" + "@rollup/rollup-win32-ia32-msvc" "4.34.7" + "@rollup/rollup-win32-x64-msvc" "4.34.7" fsevents "~2.3.2" rrweb-cssom@^0.6.0: @@ -16530,11 +16753,6 @@ typescript@3.9.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== -typescript@^3.7.5: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== - typescript@^5.4.3: version "5.7.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" From 037b25cfdcd18deea0e1c6c2e8d2548dbf32f7f3 Mon Sep 17 00:00:00 2001 From: michael faith Date: Sun, 16 Feb 2025 12:23:44 -0600 Subject: [PATCH 031/300] test(eslint): create eslint test fixtures (#32396) --- fixtures/{eslint => eslint-v6}/.eslintrc.json | 7 +- fixtures/eslint-v6/README.md | 12 + fixtures/eslint-v6/build.mjs | 5 + fixtures/eslint-v6/index.js | 143 +++ fixtures/eslint-v6/package.json | 12 + fixtures/eslint-v6/yarn.lock | 865 ++++++++++++++++++ fixtures/eslint-v7/.eslintrc.json | 14 + fixtures/eslint-v7/README.md | 12 + fixtures/eslint-v7/build.mjs | 5 + fixtures/eslint-v7/index.js | 143 +++ fixtures/eslint-v7/package.json | 12 + fixtures/eslint-v7/yarn.lock | 769 ++++++++++++++++ fixtures/eslint-v8/.eslintrc.json | 14 + fixtures/eslint-v8/README.md | 12 + fixtures/eslint-v8/build.mjs | 5 + fixtures/eslint-v8/index.js | 143 +++ fixtures/eslint-v8/package.json | 12 + fixtures/eslint-v8/yarn.lock | 676 ++++++++++++++ fixtures/eslint-v9/README.md | 12 + fixtures/eslint-v9/build.mjs | 5 + fixtures/eslint-v9/eslint.config.mjs | 21 + fixtures/eslint-v9/index.js | 143 +++ fixtures/eslint-v9/package.json | 12 + fixtures/eslint-v9/yarn.lock | 579 ++++++++++++ fixtures/eslint/README.md | 7 - fixtures/eslint/index.js | 26 - fixtures/eslint/package.json | 12 - fixtures/eslint/proxy/index.js | 35 - fixtures/eslint/proxy/package.json | 4 - fixtures/eslint/watch.sh | 3 - fixtures/eslint/yarn.lock | 853 ----------------- 31 files changed, 3629 insertions(+), 944 deletions(-) rename fixtures/{eslint => eslint-v6}/.eslintrc.json (52%) create mode 100644 fixtures/eslint-v6/README.md create mode 100644 fixtures/eslint-v6/build.mjs create mode 100644 fixtures/eslint-v6/index.js create mode 100644 fixtures/eslint-v6/package.json create mode 100644 fixtures/eslint-v6/yarn.lock create mode 100644 fixtures/eslint-v7/.eslintrc.json create mode 100644 fixtures/eslint-v7/README.md create mode 100644 fixtures/eslint-v7/build.mjs create mode 100644 fixtures/eslint-v7/index.js create mode 100644 fixtures/eslint-v7/package.json create mode 100644 fixtures/eslint-v7/yarn.lock create mode 100644 fixtures/eslint-v8/.eslintrc.json create mode 100644 fixtures/eslint-v8/README.md create mode 100644 fixtures/eslint-v8/build.mjs create mode 100644 fixtures/eslint-v8/index.js create mode 100644 fixtures/eslint-v8/package.json create mode 100644 fixtures/eslint-v8/yarn.lock create mode 100644 fixtures/eslint-v9/README.md create mode 100644 fixtures/eslint-v9/build.mjs create mode 100644 fixtures/eslint-v9/eslint.config.mjs create mode 100644 fixtures/eslint-v9/index.js create mode 100644 fixtures/eslint-v9/package.json create mode 100644 fixtures/eslint-v9/yarn.lock delete mode 100644 fixtures/eslint/README.md delete mode 100644 fixtures/eslint/index.js delete mode 100644 fixtures/eslint/package.json delete mode 100644 fixtures/eslint/proxy/index.js delete mode 100644 fixtures/eslint/proxy/package.json delete mode 100755 fixtures/eslint/watch.sh delete mode 100644 fixtures/eslint/yarn.lock diff --git a/fixtures/eslint/.eslintrc.json b/fixtures/eslint-v6/.eslintrc.json similarity index 52% rename from fixtures/eslint/.eslintrc.json rename to fixtures/eslint-v6/.eslintrc.json index e03178a42a72a..33b2cfaacabcd 100644 --- a/fixtures/eslint/.eslintrc.json +++ b/fixtures/eslint-v6/.eslintrc.json @@ -1,15 +1,14 @@ { "root": true, + "extends": ["plugin:react-hooks/recommended-legacy"], "parserOptions": { - "ecmaVersion": 8, + "ecmaVersion": 2020, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, - "plugins": ["react-hooks"], "rules": { - "react-hooks/rules-of-hooks": 2, - "react-hooks/exhaustive-deps": 2 + "react-hooks/exhaustive-deps": "error" } } diff --git a/fixtures/eslint-v6/README.md b/fixtures/eslint-v6/README.md new file mode 100644 index 0000000000000..7f0ebe8b038ba --- /dev/null +++ b/fixtures/eslint-v6/README.md @@ -0,0 +1,12 @@ +# ESLint v6 Fixture + +This fixture allows us to test e2e functionality for `eslint-plugin-react-hooks` with eslint version 6. + +Run the following to test. + +```sh +cd fixtures/eslint-v6 +yarn +yarn build +yarn lint +``` diff --git a/fixtures/eslint-v6/build.mjs b/fixtures/eslint-v6/build.mjs new file mode 100644 index 0000000000000..02a1bbd25e00e --- /dev/null +++ b/fixtures/eslint-v6/build.mjs @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import {exec} from 'node:child_process'; + +exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); diff --git a/fixtures/eslint-v6/index.js b/fixtures/eslint-v6/index.js new file mode 100644 index 0000000000000..6d4a7235dd257 --- /dev/null +++ b/fixtures/eslint-v6/index.js @@ -0,0 +1,143 @@ +/** + * Exhaustive Deps + */ +// Valid because dependencies are declared correctly +function Comment({comment, commentSource}) { + const currentUserID = comment.viewer.id; + const environment = RelayEnvironment.forUser(currentUserID); + const commentID = nullthrows(comment.id); + useEffect(() => { + const subscription = SubscriptionCounter.subscribeOnce( + `StoreSubscription_${commentID}`, + () => + StoreSubscription.subscribe( + environment, + { + comment_id: commentID, + }, + currentUserID, + commentSource + ) + ); + return () => subscription.dispose(); + }, [commentID, commentSource, currentUserID, environment]); +} + +// Valid because no dependencies +function UseEffectWithNoDependencies() { + const local = {}; + useEffect(() => { + console.log(local); + }); +} +function UseEffectWithEmptyDependencies() { + useEffect(() => { + const local = {}; + console.log(local); + }, []); +} + +// OK because `props` wasn't defined. +function ComponentWithNoPropsDefined() { + useEffect(() => { + console.log(props.foo); + }, []); +} + +// Valid because props are declared as a dependency +function ComponentWithPropsDeclaredAsDep({foo}) { + useEffect(() => { + console.log(foo.length); + console.log(foo.slice(0)); + }, [foo]); +} + +// Valid because individual props are declared as dependencies +function ComponentWithIndividualPropsDeclaredAsDeps(props) { + useEffect(() => { + console.log(props.foo); + console.log(props.bar); + }, [props.bar, props.foo]); +} + +// Invalid because neither props or props.foo are declared as dependencies +function ComponentWithoutDeclaringPropAsDep(props) { + useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.notReactiveHook(() => { + console.log(props.foo); + }, []); // This one isn't a violation +} + +/** + * Rules of Hooks + */ +// Valid because functions can call functions. +function normalFunctionWithConditionalFunction() { + if (cond) { + doSomething(); + } +} + +// Valid because hooks can call hooks. +function useHook() { + useState(); +} +const whatever = function useHook() { + useState(); +}; +const useHook1 = () => { + useState(); +}; +let useHook2 = () => useState(); +useHook2 = () => { + useState(); +}; + +// Invalid because hooks can't be called in conditionals. +function ComponentWithConditionalHook() { + if (cond) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useConditionalHook(); + } +} + +// Invalid because hooks can't be called in loops. +function useHookInLoops() { + while (a) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook1(); + if (b) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook2(); + } + while (c) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook3(); + if (d) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook4(); + } +} diff --git a/fixtures/eslint-v6/package.json b/fixtures/eslint-v6/package.json new file mode 100644 index 0000000000000..50c92054b436f --- /dev/null +++ b/fixtures/eslint-v6/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "name": "eslint-v6", + "dependencies": { + "eslint": "^6", + "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + }, + "scripts": { + "build": "node build.mjs && yarn", + "lint": "eslint index.js --report-unused-disable-directives" + } +} diff --git a/fixtures/eslint-v6/yarn.lock b/fixtures/eslint-v6/yarn.lock new file mode 100644 index 0000000000000..3d6dbca037b38 --- /dev/null +++ b/fixtures/eslint-v6/yarn.lock @@ -0,0 +1,865 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +acorn-jsx@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ajv@^6.10.0, ajv@^6.10.2: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.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== + +chalk@^2.1.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-spawn@^6.0.5: + version "6.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" + integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +debug@^4.0.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": + version "0.0.0" + uid "" + +eslint-scope@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^6: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +glob-parent@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +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@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash@^4.17.14, lodash@^4.17.19: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +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" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +picocolors@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +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== + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.1.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + 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" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + 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-json-comments@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +v8-compile-cache@^2.0.3: + version "2.4.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128" + integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" diff --git a/fixtures/eslint-v7/.eslintrc.json b/fixtures/eslint-v7/.eslintrc.json new file mode 100644 index 0000000000000..33b2cfaacabcd --- /dev/null +++ b/fixtures/eslint-v7/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "root": true, + "extends": ["plugin:react-hooks/recommended-legacy"], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "rules": { + "react-hooks/exhaustive-deps": "error" + } +} diff --git a/fixtures/eslint-v7/README.md b/fixtures/eslint-v7/README.md new file mode 100644 index 0000000000000..ea4628b0e035a --- /dev/null +++ b/fixtures/eslint-v7/README.md @@ -0,0 +1,12 @@ +# ESLint v7 Fixture + +This fixture allows us to test e2e functionality for `eslint-plugin-react-hooks` with eslint version 7. + +Run the following to test. + +```sh +cd fixtures/eslint-v7 +yarn +yarn build +yarn lint +``` diff --git a/fixtures/eslint-v7/build.mjs b/fixtures/eslint-v7/build.mjs new file mode 100644 index 0000000000000..02a1bbd25e00e --- /dev/null +++ b/fixtures/eslint-v7/build.mjs @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import {exec} from 'node:child_process'; + +exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); diff --git a/fixtures/eslint-v7/index.js b/fixtures/eslint-v7/index.js new file mode 100644 index 0000000000000..6d4a7235dd257 --- /dev/null +++ b/fixtures/eslint-v7/index.js @@ -0,0 +1,143 @@ +/** + * Exhaustive Deps + */ +// Valid because dependencies are declared correctly +function Comment({comment, commentSource}) { + const currentUserID = comment.viewer.id; + const environment = RelayEnvironment.forUser(currentUserID); + const commentID = nullthrows(comment.id); + useEffect(() => { + const subscription = SubscriptionCounter.subscribeOnce( + `StoreSubscription_${commentID}`, + () => + StoreSubscription.subscribe( + environment, + { + comment_id: commentID, + }, + currentUserID, + commentSource + ) + ); + return () => subscription.dispose(); + }, [commentID, commentSource, currentUserID, environment]); +} + +// Valid because no dependencies +function UseEffectWithNoDependencies() { + const local = {}; + useEffect(() => { + console.log(local); + }); +} +function UseEffectWithEmptyDependencies() { + useEffect(() => { + const local = {}; + console.log(local); + }, []); +} + +// OK because `props` wasn't defined. +function ComponentWithNoPropsDefined() { + useEffect(() => { + console.log(props.foo); + }, []); +} + +// Valid because props are declared as a dependency +function ComponentWithPropsDeclaredAsDep({foo}) { + useEffect(() => { + console.log(foo.length); + console.log(foo.slice(0)); + }, [foo]); +} + +// Valid because individual props are declared as dependencies +function ComponentWithIndividualPropsDeclaredAsDeps(props) { + useEffect(() => { + console.log(props.foo); + console.log(props.bar); + }, [props.bar, props.foo]); +} + +// Invalid because neither props or props.foo are declared as dependencies +function ComponentWithoutDeclaringPropAsDep(props) { + useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.notReactiveHook(() => { + console.log(props.foo); + }, []); // This one isn't a violation +} + +/** + * Rules of Hooks + */ +// Valid because functions can call functions. +function normalFunctionWithConditionalFunction() { + if (cond) { + doSomething(); + } +} + +// Valid because hooks can call hooks. +function useHook() { + useState(); +} +const whatever = function useHook() { + useState(); +}; +const useHook1 = () => { + useState(); +}; +let useHook2 = () => useState(); +useHook2 = () => { + useState(); +}; + +// Invalid because hooks can't be called in conditionals. +function ComponentWithConditionalHook() { + if (cond) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useConditionalHook(); + } +} + +// Invalid because hooks can't be called in loops. +function useHookInLoops() { + while (a) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook1(); + if (b) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook2(); + } + while (c) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook3(); + if (d) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook4(); + } +} diff --git a/fixtures/eslint-v7/package.json b/fixtures/eslint-v7/package.json new file mode 100644 index 0000000000000..90c369c603e6d --- /dev/null +++ b/fixtures/eslint-v7/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "name": "eslint-v7", + "dependencies": { + "eslint": "^7", + "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + }, + "scripts": { + "build": "node build.mjs && yarn", + "lint": "eslint index.js --report-unused-disable-directives" + } +} diff --git a/fixtures/eslint-v7/yarn.lock b/fixtures/eslint-v7/yarn.lock new file mode 100644 index 0000000000000..62f97bbc20d69 --- /dev/null +++ b/fixtures/eslint-v7/yarn.lock @@ -0,0 +1,769 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/highlight@^7.10.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.9.tgz#8141ce68fc73757946f983b343f1231f4691acc6" + integrity sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.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== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-spawn@^7.0.2: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.0.1, debug@^4.1.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +enquirer@^2.3.5: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": + version "0.0.0" + uid "" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint@^7: + 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@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.6.0, globals@^13.9.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +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@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +optionator@^0.9.1: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +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" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +picocolors@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +regexpp@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +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== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +semver@^7.2.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +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" + +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-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +table@^6.0.9: + version "6.9.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" + integrity sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +v8-compile-cache@^2.0.3: + version "2.4.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128" + integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/fixtures/eslint-v8/.eslintrc.json b/fixtures/eslint-v8/.eslintrc.json new file mode 100644 index 0000000000000..33b2cfaacabcd --- /dev/null +++ b/fixtures/eslint-v8/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "root": true, + "extends": ["plugin:react-hooks/recommended-legacy"], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "rules": { + "react-hooks/exhaustive-deps": "error" + } +} diff --git a/fixtures/eslint-v8/README.md b/fixtures/eslint-v8/README.md new file mode 100644 index 0000000000000..da220cef5cbdc --- /dev/null +++ b/fixtures/eslint-v8/README.md @@ -0,0 +1,12 @@ +# ESLint v8 Fixture + +This fixture allows us to test e2e functionality for `eslint-plugin-react-hooks` with eslint version 8. + +Run the following to test. + +```sh +cd fixtures/eslint-v8 +yarn +yarn build +yarn lint +``` diff --git a/fixtures/eslint-v8/build.mjs b/fixtures/eslint-v8/build.mjs new file mode 100644 index 0000000000000..02a1bbd25e00e --- /dev/null +++ b/fixtures/eslint-v8/build.mjs @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import {exec} from 'node:child_process'; + +exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); diff --git a/fixtures/eslint-v8/index.js b/fixtures/eslint-v8/index.js new file mode 100644 index 0000000000000..6d4a7235dd257 --- /dev/null +++ b/fixtures/eslint-v8/index.js @@ -0,0 +1,143 @@ +/** + * Exhaustive Deps + */ +// Valid because dependencies are declared correctly +function Comment({comment, commentSource}) { + const currentUserID = comment.viewer.id; + const environment = RelayEnvironment.forUser(currentUserID); + const commentID = nullthrows(comment.id); + useEffect(() => { + const subscription = SubscriptionCounter.subscribeOnce( + `StoreSubscription_${commentID}`, + () => + StoreSubscription.subscribe( + environment, + { + comment_id: commentID, + }, + currentUserID, + commentSource + ) + ); + return () => subscription.dispose(); + }, [commentID, commentSource, currentUserID, environment]); +} + +// Valid because no dependencies +function UseEffectWithNoDependencies() { + const local = {}; + useEffect(() => { + console.log(local); + }); +} +function UseEffectWithEmptyDependencies() { + useEffect(() => { + const local = {}; + console.log(local); + }, []); +} + +// OK because `props` wasn't defined. +function ComponentWithNoPropsDefined() { + useEffect(() => { + console.log(props.foo); + }, []); +} + +// Valid because props are declared as a dependency +function ComponentWithPropsDeclaredAsDep({foo}) { + useEffect(() => { + console.log(foo.length); + console.log(foo.slice(0)); + }, [foo]); +} + +// Valid because individual props are declared as dependencies +function ComponentWithIndividualPropsDeclaredAsDeps(props) { + useEffect(() => { + console.log(props.foo); + console.log(props.bar); + }, [props.bar, props.foo]); +} + +// Invalid because neither props or props.foo are declared as dependencies +function ComponentWithoutDeclaringPropAsDep(props) { + useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.notReactiveHook(() => { + console.log(props.foo); + }, []); // This one isn't a violation +} + +/** + * Rules of Hooks + */ +// Valid because functions can call functions. +function normalFunctionWithConditionalFunction() { + if (cond) { + doSomething(); + } +} + +// Valid because hooks can call hooks. +function useHook() { + useState(); +} +const whatever = function useHook() { + useState(); +}; +const useHook1 = () => { + useState(); +}; +let useHook2 = () => useState(); +useHook2 = () => { + useState(); +}; + +// Invalid because hooks can't be called in conditionals. +function ComponentWithConditionalHook() { + if (cond) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useConditionalHook(); + } +} + +// Invalid because hooks can't be called in loops. +function useHookInLoops() { + while (a) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook1(); + if (b) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook2(); + } + while (c) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook3(); + if (d) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook4(); + } +} diff --git a/fixtures/eslint-v8/package.json b/fixtures/eslint-v8/package.json new file mode 100644 index 0000000000000..620520ecdbfd4 --- /dev/null +++ b/fixtures/eslint-v8/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "name": "eslint-v8", + "dependencies": { + "eslint": "^8", + "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + }, + "scripts": { + "build": "node build.mjs && yarn", + "lint": "eslint index.js --report-unused-disable-directives" + } +} diff --git a/fixtures/eslint-v8/yarn.lock b/fixtures/eslint-v8/yarn.lock new file mode 100644 index 0000000000000..88d04029954a4 --- /dev/null +++ b/fixtures/eslint-v8/yarn.lock @@ -0,0 +1,676 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.6.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@ungap/structured-clone@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.9.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +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== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.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== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-spawn@^7.0.2: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": + version "0.0.0" + uid "" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.0.tgz#a82c6b7c2bb4e44766d865f07997785fecfdcb89" + integrity sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +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" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.5, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +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" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +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== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +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-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/fixtures/eslint-v9/README.md b/fixtures/eslint-v9/README.md new file mode 100644 index 0000000000000..3ea521cfa8909 --- /dev/null +++ b/fixtures/eslint-v9/README.md @@ -0,0 +1,12 @@ +# ESLint v9 Fixture + +This fixture allows us to test e2e functionality for `eslint-plugin-react-hooks` with eslint version 9. + +Run the following to test. + +```sh +cd fixtures/eslint-v9 +yarn +yarn build +yarn lint +``` diff --git a/fixtures/eslint-v9/build.mjs b/fixtures/eslint-v9/build.mjs new file mode 100644 index 0000000000000..02a1bbd25e00e --- /dev/null +++ b/fixtures/eslint-v9/build.mjs @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import {exec} from 'node:child_process'; + +exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); diff --git a/fixtures/eslint-v9/eslint.config.mjs b/fixtures/eslint-v9/eslint.config.mjs new file mode 100644 index 0000000000000..ffc8375265c2b --- /dev/null +++ b/fixtures/eslint-v9/eslint.config.mjs @@ -0,0 +1,21 @@ +import * as reactHooks from 'eslint-plugin-react-hooks'; + +export default [ + { + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + reactHooks.configs['recommended-latest'], + { + rules: { + 'react-hooks/exhaustive-deps': 'error', + }, + }, +]; diff --git a/fixtures/eslint-v9/index.js b/fixtures/eslint-v9/index.js new file mode 100644 index 0000000000000..6d4a7235dd257 --- /dev/null +++ b/fixtures/eslint-v9/index.js @@ -0,0 +1,143 @@ +/** + * Exhaustive Deps + */ +// Valid because dependencies are declared correctly +function Comment({comment, commentSource}) { + const currentUserID = comment.viewer.id; + const environment = RelayEnvironment.forUser(currentUserID); + const commentID = nullthrows(comment.id); + useEffect(() => { + const subscription = SubscriptionCounter.subscribeOnce( + `StoreSubscription_${commentID}`, + () => + StoreSubscription.subscribe( + environment, + { + comment_id: commentID, + }, + currentUserID, + commentSource + ) + ); + return () => subscription.dispose(); + }, [commentID, commentSource, currentUserID, environment]); +} + +// Valid because no dependencies +function UseEffectWithNoDependencies() { + const local = {}; + useEffect(() => { + console.log(local); + }); +} +function UseEffectWithEmptyDependencies() { + useEffect(() => { + const local = {}; + console.log(local); + }, []); +} + +// OK because `props` wasn't defined. +function ComponentWithNoPropsDefined() { + useEffect(() => { + console.log(props.foo); + }, []); +} + +// Valid because props are declared as a dependency +function ComponentWithPropsDeclaredAsDep({foo}) { + useEffect(() => { + console.log(foo.length); + console.log(foo.slice(0)); + }, [foo]); +} + +// Valid because individual props are declared as dependencies +function ComponentWithIndividualPropsDeclaredAsDeps(props) { + useEffect(() => { + console.log(props.foo); + console.log(props.bar); + }, [props.bar, props.foo]); +} + +// Invalid because neither props or props.foo are declared as dependencies +function ComponentWithoutDeclaringPropAsDep(props) { + useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useEffect(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useCallback(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useMemo(() => { + console.log(props.foo); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.notReactiveHook(() => { + console.log(props.foo); + }, []); // This one isn't a violation +} + +/** + * Rules of Hooks + */ +// Valid because functions can call functions. +function normalFunctionWithConditionalFunction() { + if (cond) { + doSomething(); + } +} + +// Valid because hooks can call hooks. +function useHook() { + useState(); +} +const whatever = function useHook() { + useState(); +}; +const useHook1 = () => { + useState(); +}; +let useHook2 = () => useState(); +useHook2 = () => { + useState(); +}; + +// Invalid because hooks can't be called in conditionals. +function ComponentWithConditionalHook() { + if (cond) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useConditionalHook(); + } +} + +// Invalid because hooks can't be called in loops. +function useHookInLoops() { + while (a) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook1(); + if (b) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook2(); + } + while (c) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook3(); + if (d) return; + // eslint-disable-next-line react-hooks/rules-of-hooks + useHook4(); + } +} diff --git a/fixtures/eslint-v9/package.json b/fixtures/eslint-v9/package.json new file mode 100644 index 0000000000000..e8ae91a0613e1 --- /dev/null +++ b/fixtures/eslint-v9/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "name": "eslint-v9", + "dependencies": { + "eslint": "^9", + "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + }, + "scripts": { + "build": "node build.mjs && yarn", + "lint": "eslint index.js --report-unused-disable-directives" + } +} diff --git a/fixtures/eslint-v9/yarn.lock b/fixtures/eslint-v9/yarn.lock new file mode 100644 index 0000000000000..5a1a246827f6f --- /dev/null +++ b/fixtures/eslint-v9/yarn.lock @@ -0,0 +1,579 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.0": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" + integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091" + integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/core@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.11.0.tgz#7a9226e850922e42cbd2ba71361eacbe74352a12" + integrity sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.20.0": + version "9.20.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.20.0.tgz#7421bcbe74889fcd65d1be59f00130c289856eb4" + integrity sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81" + integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A== + dependencies: + "@eslint/core" "^0.10.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +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== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.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== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": + version "0.0.0" + uid "" + +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9: + version "9.20.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6" + integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.11.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.20.0" + "@eslint/plugin-kit" "^0.2.5" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.1" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +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" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +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" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +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== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/fixtures/eslint/README.md b/fixtures/eslint/README.md deleted file mode 100644 index 28ab22551372d..0000000000000 --- a/fixtures/eslint/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# ESLint Playground Fixture - -This is an internal playground for quick iteration on our lint rules inside an IDE like VSCode. - -See instructions in `./index.js` in this directory. - -![Demo](https://duaw26jehqd4r.cloudfront.net/items/2Z390a31003O0l0o0e3O/Screen%20Recording%202019-01-16%20at%2010.29%20PM.gif?v=d6856125) \ No newline at end of file diff --git a/fixtures/eslint/index.js b/fixtures/eslint/index.js deleted file mode 100644 index d53415310560d..0000000000000 --- a/fixtures/eslint/index.js +++ /dev/null @@ -1,26 +0,0 @@ -// This is a testing playground for our lint rules. - -// 1. Run yarn && yarn start -// 2. "File > Add Folder to Workspace" this specific folder in VSCode with ESLint extension -// 3. Changes to the rule source should get picked up without restarting ESLint server - -function Comment({comment, commentSource}) { - const currentUserID = comment.viewer.id; - const environment = RelayEnvironment.forUser(currentUserID); - const commentID = nullthrows(comment.id); - useEffect(() => { - const subscription = SubscriptionCounter.subscribeOnce( - `StoreSubscription_${commentID}`, - () => - StoreSubscription.subscribe( - environment, - { - comment_id: commentID, - }, - currentUserID, - commentSource - ) - ); - return () => subscription.dispose(); - }, [commentID, commentSource, currentUserID, environment]); -} diff --git a/fixtures/eslint/package.json b/fixtures/eslint/package.json deleted file mode 100644 index 04f8d6b98be99..0000000000000 --- a/fixtures/eslint/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "private": true, - "name": "eslint-playground", - "dependencies": { - "eslint": "4.1.0", - "eslint-plugin-react-hooks": "link:./proxy" - }, - "scripts": { - "start": "./watch.sh", - "lint": "eslint index.js" - } -} diff --git a/fixtures/eslint/proxy/index.js b/fixtures/eslint/proxy/index.js deleted file mode 100644 index f54e672d21126..0000000000000 --- a/fixtures/eslint/proxy/index.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -// This file is a proxy for our rule definition that will -// load the latest built version on every check. This makes -// it convenient to test inside IDEs (which would otherwise -// load a version of our rule once and never restart the server). -// See instructions in ../index.js playground. - -let build; -reload(); - -function reload() { - for (let id in require.cache) { - if (/eslint-plugin-react-hooks/.test(id)) { - delete require.cache[id]; - } - } - // Point to the built version. - build = require('../../../build/oss-experimental/eslint-plugin-react-hooks'); -} - -let rules = {}; -for (let key in build.rules) { - if (build.rules.hasOwnProperty(key)) { - rules[key] = Object.assign({}, build.rules, { - create() { - // Reload changes to the built rule - reload(); - return build.rules[key].create.apply(this, arguments); - }, - }); - } -} - -module.exports = {rules}; diff --git a/fixtures/eslint/proxy/package.json b/fixtures/eslint/proxy/package.json deleted file mode 100644 index 1461c2d43f5f1..0000000000000 --- a/fixtures/eslint/proxy/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "private": true, - "version": "0.0.0" -} \ No newline at end of file diff --git a/fixtures/eslint/watch.sh b/fixtures/eslint/watch.sh deleted file mode 100755 index 0ca6359c03516..0000000000000 --- a/fixtures/eslint/watch.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -(cd ../.. && yarn build eslint --type=NODE_DEV) -(cd ../.. && watchman-make --make 'yarn build eslint --type=NODE_DEV' -p 'packages/eslint-plugin-*/**/*' -t ignored) diff --git a/fixtures/eslint/yarn.lock b/fixtures/eslint/yarn.lock deleted file mode 100644 index 7a557036da3aa..0000000000000 --- a/fixtures/eslint/yarn.lock +++ /dev/null @@ -1,853 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= - dependencies: - acorn "^3.0.4" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - -acorn@^5.5.0: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== - -ajv-keywords@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" - integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= - -ajv@^6.0.1: - version "6.7.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96" - integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-escapes@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" - integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.1.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -debug@^2.6.8: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -doctrine@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -"eslint-plugin-react-hooks@link:./proxy": - version "0.0.0" - uid "" - -eslint-scope@^3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" - integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.1.0.tgz#bbb55a28220ee08b69da9554d45a6b2ebfd7d913" - integrity sha1-u7VaKCIO4Itp2pVU1FprLr/X2RM= - dependencies: - babel-code-frame "^6.22.0" - chalk "^1.1.3" - concat-stream "^1.6.0" - debug "^2.6.8" - doctrine "^2.0.0" - eslint-scope "^3.7.1" - espree "^3.4.3" - esquery "^1.0.0" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - glob "^7.1.2" - globals "^9.17.0" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-my-json-valid "^2.16.0" - is-resolvable "^1.0.0" - js-yaml "^3.8.4" - json-stable-stringify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^4.0.0" - progress "^2.0.0" - require-uncached "^1.0.3" - strip-json-comments "~2.0.1" - table "^4.0.1" - text-table "~0.2.0" - -espree@^3.4.3: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" - integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= - -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== - dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -generate-function@^2.0.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= - dependencies: - is-property "^1.0.0" - -glob@^7.1.2, glob@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^9.17.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== - -graceful-fs@^4.1.2: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -iconv-lite@^0.4.17: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore@^3.3.3: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-my-ip-valid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" - integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== - -is-my-json-valid@^2.16.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz#8fd6e40363cd06b963fa877d444bfb5eddc62175" - integrity sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q== - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - is-my-ip-valid "^1.0.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-property@^1.0.0, is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-yaml@^3.8.4: - version "3.12.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" - integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lodash@^4.17.4, lodash@^4.3.0: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -minimatch@^3.0.2, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - -optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -pluralize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762" - integrity sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I= - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -readable-stream@^2.2.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" - integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== - dependencies: - is-fullwidth-code-point "^2.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -table@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" - integrity sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg== - dependencies: - ajv "^6.0.1" - ajv-keywords "^3.0.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" - -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= - dependencies: - mkdirp "^0.5.1" - -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= From 5adf40208f4a2f56bda5c059d18ce578c5091dab Mon Sep 17 00:00:00 2001 From: michael faith Date: Sun, 16 Feb 2025 13:10:54 -0600 Subject: [PATCH 032/300] feat(eslint-plugin-react-hooks): convert to typescript and package type declarations (#32240) ## Summary This change converts the eslint hooks plugin to typescript, which also allows us to include type declarations in the package, for those using [typescript eslint configs](https://eslint.org/blog/2025/01/eslint-v9.18.0-released/#stable-typescript-configuration-file-support). ### Constituent changes that should land before this one - [x] ~https://github.com/facebook/react/pull/32276~ - [x] https://github.com/facebook/react/pull/32279 - [x] https://github.com/facebook/react/pull/32283 - [x] https://github.com/facebook/react/pull/32393 - [x] https://github.com/facebook/react/pull/32396 Closes #30119 --------- Co-authored-by: Lauren Tan --- .../eslint-plugin-react-hooks/babel.config.js | 8 + packages/eslint-plugin-react-hooks/index.js | 9 +- .../eslint-plugin-react-hooks/jest.config.js | 8 + .../eslint-plugin-react-hooks/npm/index.d.ts | 1 + .../eslint-plugin-react-hooks/package.json | 11 +- .../{ExhaustiveDeps.js => ExhaustiveDeps.ts} | 631 +++++++++++------- .../src/{RulesOfHooks.js => RulesOfHooks.ts} | 145 ++-- .../eslint-plugin-react-hooks/src/index.js | 61 -- .../eslint-plugin-react-hooks/src/index.ts | 64 ++ .../src/types/estree.d.ts | 2 + .../eslint-plugin-react-hooks/tsconfig.json | 1 - .../eslint-plugin-react-hooks/tsup.config.ts | 9 - scripts/rollup/bundles.js | 14 +- yarn.lock | 467 +------------ 14 files changed, 574 insertions(+), 857 deletions(-) create mode 100644 packages/eslint-plugin-react-hooks/babel.config.js create mode 100644 packages/eslint-plugin-react-hooks/jest.config.js create mode 100644 packages/eslint-plugin-react-hooks/npm/index.d.ts rename packages/eslint-plugin-react-hooks/src/{ExhaustiveDeps.js => ExhaustiveDeps.ts} (78%) rename packages/eslint-plugin-react-hooks/src/{RulesOfHooks.js => RulesOfHooks.ts} (85%) delete mode 100644 packages/eslint-plugin-react-hooks/src/index.js create mode 100644 packages/eslint-plugin-react-hooks/src/index.ts delete mode 100644 packages/eslint-plugin-react-hooks/tsup.config.ts diff --git a/packages/eslint-plugin-react-hooks/babel.config.js b/packages/eslint-plugin-react-hooks/babel.config.js new file mode 100644 index 0000000000000..3b947a7163bcb --- /dev/null +++ b/packages/eslint-plugin-react-hooks/babel.config.js @@ -0,0 +1,8 @@ +/** + * This file is purely being used for local jest runs, and doesn't participate in the build process. + */ +'use strict'; + +module.exports = { + extends: '../../babel.config-ts.js', +}; diff --git a/packages/eslint-plugin-react-hooks/index.js b/packages/eslint-plugin-react-hooks/index.js index 754dc9f9c7f23..ce26a10c31518 100644 --- a/packages/eslint-plugin-react-hooks/index.js +++ b/packages/eslint-plugin-react-hooks/index.js @@ -1,8 +1 @@ -/** - * 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. - */ - -export * from './src/index'; +module.exports = require('./src/index.ts'); diff --git a/packages/eslint-plugin-react-hooks/jest.config.js b/packages/eslint-plugin-react-hooks/jest.config.js new file mode 100644 index 0000000000000..a7b91c3ef1cbe --- /dev/null +++ b/packages/eslint-plugin-react-hooks/jest.config.js @@ -0,0 +1,8 @@ +'use strict'; + +process.env.NODE_ENV = 'development'; + +module.exports = { + setupFiles: [require.resolve('../../scripts/jest/setupEnvironment.js')], + moduleFileExtensions: ['ts', 'js', 'json'], +}; diff --git a/packages/eslint-plugin-react-hooks/npm/index.d.ts b/packages/eslint-plugin-react-hooks/npm/index.d.ts new file mode 100644 index 0000000000000..62ca164ec2173 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/npm/index.d.ts @@ -0,0 +1 @@ +export * from './cjs/eslint-plugin-react-hooks'; diff --git a/packages/eslint-plugin-react-hooks/package.json b/packages/eslint-plugin-react-hooks/package.json index b45609362faac..d3717b9dee61c 100644 --- a/packages/eslint-plugin-react-hooks/package.json +++ b/packages/eslint-plugin-react-hooks/package.json @@ -10,8 +10,9 @@ "files": [ "LICENSE", "README.md", + "cjs", "index.js", - "cjs" + "index.d.ts" ], "keywords": [ "eslint", @@ -19,10 +20,16 @@ "eslintplugin", "react" ], + "scripts": { + "test": "jest", + "typecheck": "tsc --noEmit" + }, "license": "MIT", "bugs": { "url": "https://github.com/facebook/react/issues" }, + "main": "./index.js", + "types": "./index.d.ts", "engines": { "node": ">=10" }, @@ -32,6 +39,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.11.4", + "@babel/preset-typescript": "^7.26.0", "@tsconfig/strictest": "^2.0.5", "@typescript-eslint/parser-v2": "npm:@typescript-eslint/parser@^2.26.0", "@typescript-eslint/parser-v3": "npm:@typescript-eslint/parser@^3.10.0", @@ -45,7 +53,6 @@ "eslint-v7": "npm:eslint@^7.7.0", "eslint-v9": "npm:eslint@^9.0.0", "jest": "^29.5.0", - "tsup": "^8.3.5", "typescript": "^5.4.3" } } diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts similarity index 78% rename from packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js rename to packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts index b2818327ff9e4..9f3d4d4db9004 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts @@ -4,12 +4,41 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ - /* eslint-disable no-for-of-loops/no-for-of-loops */ +import type {Rule, Scope} from 'eslint'; +import type { + ArrayExpression, + ArrowFunctionExpression, + CallExpression, + Expression, + FunctionDeclaration, + FunctionExpression, + Identifier, + Node, + Pattern, + PrivateIdentifier, + Super, + VariableDeclarator, +} from 'estree'; + +type DeclaredDependency = { + key: string; + node: Node; +}; -'use strict'; +type Dependency = { + isStable: boolean; + references: Scope.Reference[]; +}; + +type DependencyTreeNode = { + isUsed: boolean; // True if used in code + isSatisfiedRecursively: boolean; // True if specified in deps + isSubtreeUsed: boolean; // True if something deeper is used by code + children: Map; // Nodes for properties +}; -export default { +const rule = { meta: { type: 'suggestion', docs: { @@ -36,7 +65,7 @@ export default { }, ], }, - create(context) { + create(context: Rule.RuleContext) { // Parse the `additionalHooks` regex. const additionalHooks = context.options && @@ -45,7 +74,7 @@ export default { ? new RegExp(context.options[0].additionalHooks) : undefined; - const enableDangerousAutofixThisMayCauseInfiniteLoops = + const enableDangerousAutofixThisMayCauseInfiniteLoops: boolean = (context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops) || @@ -56,11 +85,15 @@ export default { enableDangerousAutofixThisMayCauseInfiniteLoops, }; - function reportProblem(problem) { + function reportProblem(problem: Rule.ReportDescriptor) { if (enableDangerousAutofixThisMayCauseInfiniteLoops) { // Used to enable legacy behavior. Dangerous. // Keep this as an option until major IDEs upgrade (including VSCode FB ESLint extension). - if (Array.isArray(problem.suggest) && problem.suggest.length > 0) { + if ( + Array.isArray(problem.suggest) && + problem.suggest.length > 0 && + problem.suggest[0] + ) { problem.fix = problem.suggest[0].fix; } } @@ -68,15 +101,15 @@ export default { } /** - * SourceCode#getText that also works down to ESLint 3.0.0 + * SourceCode that also works down to ESLint 3.0.0 */ - const getSource = - typeof context.getSource === 'function' - ? node => { - return context.getSource(node); + const getSourceCode = + typeof context.getSourceCode === 'function' + ? () => { + return context.getSourceCode(); } - : node => { - return context.sourceCode.getText(node); + : () => { + return context.sourceCode; }; /** * SourceCode#getScope that also works down to ESLint 3.0.0 @@ -86,24 +119,34 @@ export default { ? () => { return context.getScope(); } - : node => { + : (node: Node) => { return context.sourceCode.getScope(node); }; - const scopeManager = context.getSourceCode().scopeManager; + const scopeManager = getSourceCode().scopeManager; // Should be shared between visitors. - const setStateCallSites = new WeakMap(); - const stateVariables = new WeakSet(); - const stableKnownValueCache = new WeakMap(); - const functionWithoutCapturedValueCache = new WeakMap(); - const useEffectEventVariables = new WeakSet(); - function memoizeWithWeakMap(fn, map) { - return function (arg) { + const setStateCallSites = new WeakMap< + Expression | Super, + Pattern | null | undefined + >(); + const stateVariables = new WeakSet(); + const stableKnownValueCache = new WeakMap(); + const functionWithoutCapturedValueCache = new WeakMap< + Scope.Variable, + boolean + >(); + const useEffectEventVariables = new WeakSet(); + + function memoizeWithWeakMap( + fn: (resolved: Scope.Variable) => boolean, + map: WeakMap, + ) { + return function (arg: Scope.Variable): boolean { if (map.has(arg)) { // to verify cache hits: // console.log(arg.name) - return map.get(arg); + return map.get(arg)!; } const result = fn(arg); map.set(arg, result); @@ -114,12 +157,12 @@ export default { * Visitor for both function expressions and arrow function expressions. */ function visitFunctionWithDependencies( - node, - declaredDependenciesNode, - reactiveHook, - reactiveHookName, - isEffect, - ) { + node: ArrowFunctionExpression | FunctionDeclaration | FunctionExpression, + declaredDependenciesNode: Node | undefined, + reactiveHook: Node, + reactiveHookName: string, + isEffect: boolean, + ): void { if (isEffect && node.async) { reportProblem({ node: node, @@ -140,6 +183,9 @@ export default { // Get the current scope. const scope = scopeManager.acquire(node); + if (!scope) { + return; + } // Find all our "pure scopes". On every re-render of a component these // pure scopes may have changes to the variables declared within. So all @@ -150,7 +196,7 @@ export default { // scope. We can't enforce this in a lint so we trust that all variables // declared outside of pure scope are indeed frozen. const pureScopes = new Set(); - let componentScope = null; + let componentScope: Scope.Scope | null = null; { let currentScope = scope.upper; while (currentScope) { @@ -186,7 +232,7 @@ export default { // const onStuff = useEffectEvent(() => {}) // ^^^ true for this reference // False for everything else. - function isStableKnownHookValue(resolved) { + function isStableKnownHookValue(resolved: Scope.Variable): boolean { if (!isArray(resolved.defs)) { return false; } @@ -195,10 +241,11 @@ export default { return false; } // Look for `let stuff = ...` - if (def.node.type !== 'VariableDeclarator') { + const defNode: VariableDeclarator = def.node; + if (defNode.type !== 'VariableDeclarator') { return false; } - let init = def.node.init; + let init = defNode.init; if (init == null) { return false; } @@ -207,8 +254,8 @@ export default { } // Detect primitive constants // const foo = 42 - let declaration = def.node.parent; - if (declaration == null) { + let declaration = defNode.parent; + if (declaration == null && componentScope) { // This might happen if variable is declared after the callback. // In that case ESLint won't set up .parent refs. // So we'll set them up manually. @@ -219,6 +266,8 @@ export default { } } if ( + declaration && + 'kind' in declaration && declaration.kind === 'const' && init.type === 'Literal' && (typeof init.value === 'string' || @@ -233,10 +282,11 @@ export default { if (init.type !== 'CallExpression') { return false; } - let callee = init.callee; + let callee: Expression | PrivateIdentifier | Super = init.callee; // Step into `= React.something` initializer. if ( callee.type === 'MemberExpression' && + 'name' in callee.object && callee.object.name === 'React' && callee.property != null && !callee.computed @@ -246,7 +296,8 @@ export default { if (callee.type !== 'Identifier') { return false; } - const id = def.node.id; + const definitionNode: VariableDeclarator = def.node; + const id = definitionNode.id; const {name} = callee; if (name === 'useRef' && id.type === 'Identifier') { // useRef() return value is stable. @@ -256,6 +307,7 @@ export default { id.type === 'Identifier' ) { for (const ref of resolved.references) { + // @ts-expect-error These types are not compatible (Reference and Identifier) if (ref !== id) { useEffectEventVariables.add(ref.identifier); } @@ -278,17 +330,14 @@ export default { if (name === 'useState') { const references = resolved.references; let writeCount = 0; - for (let i = 0; i < references.length; i++) { - if (references[i].isWrite()) { + for (const reference of references) { + if (reference.isWrite()) { writeCount++; } if (writeCount > 1) { return false; } - setStateCallSites.set( - references[i].identifier, - id.elements[0], - ); + setStateCallSites.set(reference.identifier, id.elements[0]); } } // Setter is stable. @@ -296,8 +345,8 @@ export default { } else if (id.elements[0] === resolved.identifiers[0]) { if (name === 'useState') { const references = resolved.references; - for (let i = 0; i < references.length; i++) { - stateVariables.add(references[i].identifier); + for (const reference of references) { + stateVariables.add(reference.identifier); } } // State variable itself is dynamic. @@ -323,7 +372,9 @@ export default { } // Some are just functions that don't reference anything dynamic. - function isFunctionWithoutCapturedValues(resolved) { + function isFunctionWithoutCapturedValues( + resolved: Scope.Variable, + ): boolean { if (!isArray(resolved.defs)) { return false; } @@ -336,12 +387,10 @@ export default { } // Search the direct component subscopes for // top-level function definitions matching this reference. - const fnNode = def.node; - const childScopes = componentScope.childScopes; + const fnNode: Node = def.node; + const childScopes = componentScope?.childScopes || []; let fnScope = null; - let i; - for (i = 0; i < childScopes.length; i++) { - const childScope = childScopes[i]; + for (const childScope of childScopes) { const childScopeBlock = childScope.block; if ( // function handleChange() {} @@ -362,8 +411,7 @@ export default { } // Does this function capture any values // that are in pure scopes (aka render)? - for (i = 0; i < fnScope.through.length; i++) { - const ref = fnScope.through[i]; + for (const ref of fnScope.through) { if (ref.resolved == null) { continue; } @@ -392,15 +440,21 @@ export default { ); // These are usually mistaken. Collect them. - const currentRefsInEffectCleanup = new Map(); + const currentRefsInEffectCleanup = new Map< + string, + { + reference: Scope.Reference; + dependencyNode: Identifier; + } + >(); // Is this reference inside a cleanup function for this effect node? // We can check by traversing scopes upwards from the reference, and checking // if the last "return () => " we encounter is located directly inside the effect. - function isInsideEffectCleanup(reference) { - let curScope = reference.from; + function isInsideEffectCleanup(reference: Scope.Reference): boolean { + let curScope: Scope.Scope | null = reference.from; let isInReturnedFunction = false; - while (curScope.block !== node) { + while (curScope && curScope.block !== node) { if (curScope.type === 'function') { isInReturnedFunction = curScope.block.parent != null && @@ -413,11 +467,11 @@ export default { // Get dependencies from all our resolved references in pure scopes. // Key is dependency string, value is whether it's stable. - const dependencies = new Map(); - const optionalChains = new Map(); + const dependencies = new Map(); + const optionalChains = new Map(); gatherDependenciesRecursively(scope); - function gatherDependenciesRecursively(currentScope) { + function gatherDependenciesRecursively(currentScope: Scope.Scope): void { for (const reference of currentScope.references) { // If this reference is not resolved or it is not declared in a pure // scope then we don't care about this reference. @@ -434,6 +488,9 @@ export default { node, reference.identifier, ); + if (referenceNode == null) { + continue; + } const dependencyNode = getDependency(referenceNode); const dependency = analyzePropertyChain( dependencyNode, @@ -446,8 +503,8 @@ export default { isEffect && // ... and this look like accessing .current... dependencyNode.type === 'Identifier' && - (dependencyNode.parent.type === 'MemberExpression' || - dependencyNode.parent.type === 'OptionalMemberExpression') && + (dependencyNode.parent?.type === 'MemberExpression' || + dependencyNode.parent?.type === 'OptionalMemberExpression') && !dependencyNode.parent.computed && dependencyNode.parent.property.type === 'Identifier' && dependencyNode.parent.property.name === 'current' && @@ -461,8 +518,8 @@ export default { } if ( - dependencyNode.parent.type === 'TSTypeQuery' || - dependencyNode.parent.type === 'TSTypeReference' + dependencyNode.parent?.type === 'TSTypeQuery' || + dependencyNode.parent?.type === 'TSTypeReference' ) { continue; } @@ -472,10 +529,11 @@ export default { continue; } // Ignore references to the function itself as it's not defined yet. - if (def.node != null && def.node.init === node.parent) { + if (def.node && def.node.init === node.parent) { continue; } // Ignore Flow type parameters + // @ts-expect-error We don't have flow types if (def.type === 'TypeParameter') { continue; } @@ -492,7 +550,7 @@ export default { references: [reference], }); } else { - dependencies.get(dependency).references.push(reference); + dependencies.get(dependency)?.references.push(reference); } } @@ -504,12 +562,12 @@ export default { // Warn about accessing .current in cleanup effects. currentRefsInEffectCleanup.forEach( ({reference, dependencyNode}, dependency) => { - const references = reference.resolved.references; + const references = reference.resolved?.references || []; // Is React managing this ref or us? // Let's see if we can find a .current assignment. let foundCurrentAssignment = false; - for (let i = 0; i < references.length; i++) { - const {identifier} = references[i]; + for (const reference of references) { + const {identifier} = reference; const {parent} = identifier; if ( parent != null && @@ -520,7 +578,7 @@ export default { parent.property.type === 'Identifier' && parent.property.name === 'current' && // ref.current = - parent.parent.type === 'AssignmentExpression' && + parent.parent?.type === 'AssignmentExpression' && parent.parent.left === parent ) { foundCurrentAssignment = true; @@ -532,6 +590,7 @@ export default { return; } reportProblem({ + // @ts-expect-error We can do better here (dependencyNode.parent has not been type narrowed) node: dependencyNode.parent.property, message: `The ref value '${dependency}.current' will likely have ` + @@ -545,8 +604,8 @@ export default { // Warn about assigning to variables in the outer scope. // Those are usually bugs. - const staleAssignments = new Set(); - function reportStaleAssignment(writeExpr, key) { + const staleAssignments = new Set(); + function reportStaleAssignment(writeExpr: Node, key: string): void { if (staleAssignments.has(key)) { return; } @@ -555,16 +614,16 @@ export default { node: writeExpr, message: `Assignments to the '${key}' variable from inside React Hook ` + - `${getSource(reactiveHook)} will be lost after each ` + + `${getSourceCode().getText(reactiveHook)} will be lost after each ` + `render. To preserve the value over time, store it in a useRef ` + `Hook and keep the mutable value in the '.current' property. ` + `Otherwise, you can move this variable directly inside ` + - `${getSource(reactiveHook)}.`, + `${getSourceCode().getText(reactiveHook)}.`, }); } // Remember which deps are stable and report bad usage first. - const stableDependencies = new Set(); + const stableDependencies = new Set(); dependencies.forEach(({isStable, references}, key) => { if (isStable) { stableDependencies.add(key); @@ -584,8 +643,8 @@ export default { if (!declaredDependenciesNode) { // Check if there are any top-level setState() calls. // Those tend to lead to infinite loops. - let setStateInsideEffectWithoutDeps = null; - dependencies.forEach(({isStable, references}, key) => { + let setStateInsideEffectWithoutDeps: string | null = null; + dependencies.forEach(({references}, key) => { if (setStateInsideEffectWithoutDeps) { return; } @@ -600,11 +659,11 @@ export default { return; } - let fnScope = reference.from; - while (fnScope.type !== 'function') { + let fnScope: Scope.Scope | null = reference.from; + while (fnScope && fnScope.type !== 'function') { fnScope = fnScope.upper; } - const isDirectlyInsideEffect = fnScope.block === node; + const isDirectlyInsideEffect = fnScope?.block === node; if (isDirectlyInsideEffect) { // TODO: we could potentially ignore early returns. setStateInsideEffectWithoutDeps = key; @@ -616,7 +675,7 @@ export default { dependencies, declaredDependencies: [], stableDependencies, - externalDependencies: new Set(), + externalDependencies: new Set(), isEffect: true, }); reportProblem({ @@ -645,8 +704,8 @@ export default { return; } - const declaredDependencies = []; - const externalDependencies = new Set(); + const declaredDependencies: DeclaredDependency[] = []; + const externalDependencies = new Set(); const isArrayExpression = declaredDependenciesNode.type === 'ArrayExpression'; const isTSAsArrayExpression = @@ -660,7 +719,7 @@ export default { reportProblem({ node: declaredDependenciesNode, message: - `React Hook ${getSource(reactiveHook)} was passed a ` + + `React Hook ${getSourceCode().getText(reactiveHook)} was passed a ` + 'dependency list that is not an array literal. This means we ' + "can't statically verify whether you've passed the correct " + 'dependencies.', @@ -670,108 +729,117 @@ export default { ? declaredDependenciesNode.expression : declaredDependenciesNode; - arrayExpression.elements.forEach(declaredDependencyNode => { - // Skip elided elements. - if (declaredDependencyNode === null) { - return; - } - // If we see a spread element then add a special warning. - if (declaredDependencyNode.type === 'SpreadElement') { - reportProblem({ - node: declaredDependencyNode, - message: - `React Hook ${getSource(reactiveHook)} has a spread ` + - "element in its dependency array. This means we can't " + - "statically verify whether you've passed the " + - 'correct dependencies.', - }); - return; - } - if (useEffectEventVariables.has(declaredDependencyNode)) { - reportProblem({ - node: declaredDependencyNode, - message: - 'Functions returned from `useEffectEvent` must not be included in the dependency array. ' + - `Remove \`${getSource( - declaredDependencyNode, - )}\` from the list.`, - suggest: [ - { - desc: `Remove the dependency \`${getSource( + (arrayExpression as ArrayExpression).elements.forEach( + declaredDependencyNode => { + // Skip elided elements. + if (declaredDependencyNode === null) { + return; + } + // If we see a spread element then add a special warning. + if (declaredDependencyNode.type === 'SpreadElement') { + reportProblem({ + node: declaredDependencyNode, + message: + `React Hook ${getSourceCode().getText(reactiveHook)} has a spread ` + + "element in its dependency array. This means we can't " + + "statically verify whether you've passed the " + + 'correct dependencies.', + }); + return; + } + if (useEffectEventVariables.has(declaredDependencyNode)) { + reportProblem({ + node: declaredDependencyNode, + message: + 'Functions returned from `useEffectEvent` must not be included in the dependency array. ' + + `Remove \`${getSourceCode().getText( declaredDependencyNode, - )}\``, - fix(fixer) { - return fixer.removeRange(declaredDependencyNode.range); + )}\` from the list.`, + suggest: [ + { + desc: `Remove the dependency \`${getSourceCode().getText( + declaredDependencyNode, + )}\``, + fix(fixer) { + return fixer.removeRange(declaredDependencyNode.range!); + }, }, - }, - ], - }); - } - // Try to normalize the declared dependency. If we can't then an error - // will be thrown. We will catch that error and report an error. - let declaredDependency; - try { - declaredDependency = analyzePropertyChain( - declaredDependencyNode, - null, - ); - } catch (error) { - if (/Unsupported node type/.test(error.message)) { - if (declaredDependencyNode.type === 'Literal') { - if (dependencies.has(declaredDependencyNode.value)) { - reportProblem({ - node: declaredDependencyNode, - message: - `The ${declaredDependencyNode.raw} literal is not a valid dependency ` + - `because it never changes. ` + - `Did you mean to include ${declaredDependencyNode.value} in the array instead?`, - }); + ], + }); + } + // Try to normalize the declared dependency. If we can't then an error + // will be thrown. We will catch that error and report an error. + let declaredDependency; + try { + declaredDependency = analyzePropertyChain( + declaredDependencyNode, + null, + ); + } catch (error: unknown) { + if ( + error instanceof Error && + /Unsupported node type/.test(error.message) + ) { + if (declaredDependencyNode.type === 'Literal') { + if ( + declaredDependencyNode.value && + dependencies.has(declaredDependencyNode.value as string) + ) { + reportProblem({ + node: declaredDependencyNode, + message: + `The ${declaredDependencyNode.raw} literal is not a valid dependency ` + + `because it never changes. ` + + `Did you mean to include ${declaredDependencyNode.value} in the array instead?`, + }); + } else { + reportProblem({ + node: declaredDependencyNode, + message: + `The ${declaredDependencyNode.raw} literal is not a valid dependency ` + + 'because it never changes. You can safely remove it.', + }); + } } else { reportProblem({ node: declaredDependencyNode, message: - `The ${declaredDependencyNode.raw} literal is not a valid dependency ` + - 'because it never changes. You can safely remove it.', + `React Hook ${getSourceCode().getText(reactiveHook)} has a ` + + `complex expression in the dependency array. ` + + 'Extract it to a separate variable so it can be statically checked.', }); } + + return; } else { - reportProblem({ - node: declaredDependencyNode, - message: - `React Hook ${getSource(reactiveHook)} has a ` + - `complex expression in the dependency array. ` + - 'Extract it to a separate variable so it can be statically checked.', - }); + throw error; } - - return; - } else { - throw error; } - } - let maybeID = declaredDependencyNode; - while ( - maybeID.type === 'MemberExpression' || - maybeID.type === 'OptionalMemberExpression' || - maybeID.type === 'ChainExpression' - ) { - maybeID = maybeID.object || maybeID.expression.object; - } - const isDeclaredInComponent = !componentScope.through.some( - ref => ref.identifier === maybeID, - ); + let maybeID = declaredDependencyNode; + while ( + maybeID.type === 'MemberExpression' || + maybeID.type === 'OptionalMemberExpression' || + maybeID.type === 'ChainExpression' + ) { + // @ts-expect-error This can be done better + maybeID = maybeID.object || maybeID.expression.object; + } + const isDeclaredInComponent = !componentScope.through.some( + ref => ref.identifier === maybeID, + ); - // Add the dependency to our declared dependency map. - declaredDependencies.push({ - key: declaredDependency, - node: declaredDependencyNode, - }); + // Add the dependency to our declared dependency map. + declaredDependencies.push({ + key: declaredDependency, + node: declaredDependencyNode, + }); - if (!isDeclaredInComponent) { - externalDependencies.add(declaredDependency); - } - }); + if (!isDeclaredInComponent) { + externalDependencies.add(declaredDependency); + } + }, + ); } const { @@ -824,10 +892,10 @@ export default { const message = `The '${construction.name.name}' ${depType} ${causation} the dependencies of ` + - `${reactiveHookName} Hook (at line ${declaredDependenciesNode.loc.start.line}) ` + + `${reactiveHookName} Hook (at line ${declaredDependenciesNode.loc?.start.line}) ` + `change on every render. ${advice}`; - let suggest; + let suggest: Rule.ReportDescriptor['suggest']; // Only handle the simple case of variable assignments. // Wrapping function declarations can mess up hoisting. if ( @@ -848,12 +916,12 @@ export default { : ['useCallback(', ')']; return [ // TODO: also add an import? - fixer.insertTextBefore(construction.node.init, before), + fixer.insertTextBefore(construction.node.init!, before), // TODO: ideally we'd gather deps here but it would require // restructuring the rule code. This will cause a new lint // error to appear immediately for useCallback. Note we're // not adding [] because would that changes semantics. - fixer.insertTextAfter(construction.node.init, after), + fixer.insertTextAfter(construction.node.init!, after), ]; }, }, @@ -889,7 +957,7 @@ export default { } // Alphabetize the suggestions, but only if deps were already alphabetized. - function areDeclaredDepsAlphabetized() { + function areDeclaredDepsAlphabetized(): boolean { if (declaredDependencies.length === 0) { return true; } @@ -905,7 +973,7 @@ export default { // This function is the last step before printing a dependency, so now is a good time to // check whether any members in our path are always used as optional-only. In that case, // we will use ?. instead of . to concatenate those parts of the path. - function formatDependency(path) { + function formatDependency(path: string): string { const members = path.split('.'); let finalPath = ''; for (let i = 0; i < members.length; i++) { @@ -919,7 +987,12 @@ export default { return finalPath; } - function getWarningMessage(deps, singlePrefix, label, fixVerb) { + function getWarningMessage( + deps: Set, + singlePrefix: string, + label: string, + fixVerb: string, + ): string | null { if (deps.size === 0) { return null; } @@ -942,7 +1015,7 @@ export default { let extraWarning = ''; if (unnecessaryDependencies.size > 0) { - let badRef = null; + let badRef: string | null = null; Array.from(unnecessaryDependencies.keys()).forEach(key => { if (badRef !== null) { return; @@ -956,7 +1029,7 @@ export default { ` Mutable values like '${badRef}' aren't valid dependencies ` + "because mutating them doesn't re-render the component."; } else if (externalDependencies.size > 0) { - const dep = Array.from(externalDependencies)[0]; + const dep = Array.from(externalDependencies)[0]!; // Don't show this warning for things that likely just got moved *inside* the callback // because in that case they're clearly not referring to globals. if (!scope.set.has(dep)) { @@ -980,8 +1053,7 @@ export default { return; } let isPropsOnlyUsedInMembers = true; - for (let i = 0; i < refs.length; i++) { - const ref = refs[i]; + for (const ref of refs) { const id = fastFindReferenceWithParent( componentScope.block, ref.identifier, @@ -1008,14 +1080,14 @@ export default { ` However, 'props' will change when *any* prop changes, so the ` + `preferred fix is to destructure the 'props' object outside of ` + `the ${reactiveHookName} call and refer to those specific props ` + - `inside ${getSource(reactiveHook)}.`; + `inside ${getSourceCode().getText(reactiveHook)}.`; } } if (!extraWarning && missingDependencies.size > 0) { // See if the user is trying to avoid specifying a callable prop. // This usually means they're unaware of useCallback. - let missingCallbackDep = null; + let missingCallbackDep: string | null = null; missingDependencies.forEach(missingDep => { if (missingCallbackDep) { return; @@ -1023,19 +1095,22 @@ export default { // Is this a variable from top scope? const topScopeRef = componentScope.set.get(missingDep); const usedDep = dependencies.get(missingDep); - if (usedDep.references[0].resolved !== topScopeRef) { + if ( + !usedDep?.references || + usedDep?.references[0]?.resolved !== topScopeRef + ) { return; } // Is this a destructured prop? - const def = topScopeRef.defs[0]; + const def = topScopeRef?.defs[0]; if (def == null || def.name == null || def.type !== 'Parameter') { return; } // Was it called in at least one case? Then it's a function. let isFunctionCall = false; - let id; - for (let i = 0; i < usedDep.references.length; i++) { - id = usedDep.references[i].identifier; + let id: Identifier | undefined; + for (const reference of usedDep.references) { + id = reference.identifier; if ( id != null && id.parent != null && @@ -1064,17 +1139,21 @@ export default { } if (!extraWarning && missingDependencies.size > 0) { - let setStateRecommendation = null; - missingDependencies.forEach(missingDep => { + let setStateRecommendation: { + missingDep: string; + setter: string; + form: 'reducer' | 'updater' | 'inlineReducer'; + } | null = null; + for (const missingDep of missingDependencies) { if (setStateRecommendation !== null) { - return; + break; } - const usedDep = dependencies.get(missingDep); + const usedDep = dependencies.get(missingDep)!; const references = usedDep.references; let id; let maybeCall; - for (let i = 0; i < references.length; i++) { - id = references[i].identifier; + for (const reference of references) { + id = reference.identifier; maybeCall = id.parent; // Try to see if we have setState(someExpr(missingDep)). while (maybeCall != null && maybeCall !== componentScope.block) { @@ -1083,22 +1162,27 @@ export default { maybeCall.callee, ); if (correspondingStateVariable != null) { - if (correspondingStateVariable.name === missingDep) { + if ( + 'name' in correspondingStateVariable && + correspondingStateVariable.name === missingDep + ) { // setCount(count + 1) setStateRecommendation = { missingDep, - setter: maybeCall.callee.name, + setter: + 'name' in maybeCall.callee ? maybeCall.callee.name : '', form: 'updater', }; } else if (stateVariables.has(id)) { // setCount(count + increment) setStateRecommendation = { missingDep, - setter: maybeCall.callee.name, + setter: + 'name' in maybeCall.callee ? maybeCall.callee.name : '', form: 'reducer', }; } else { - const resolved = references[i].resolved; + const resolved = reference.resolved; if (resolved != null) { // If it's a parameter *and* a missing dep, // it must be a prop or something inside a prop. @@ -1107,7 +1191,10 @@ export default { if (def != null && def.type === 'Parameter') { setStateRecommendation = { missingDep, - setter: maybeCall.callee.name, + setter: + 'name' in maybeCall.callee + ? maybeCall.callee.name + : '', form: 'inlineReducer', }; } @@ -1122,7 +1209,7 @@ export default { break; } } - }); + } if (setStateRecommendation !== null) { switch (setStateRecommendation.form) { case 'reducer': @@ -1158,7 +1245,7 @@ export default { reportProblem({ node: declaredDependenciesNode, message: - `React Hook ${getSource(reactiveHook)} has ` + + `React Hook ${getSourceCode().getText(reactiveHook)} has ` + // To avoid a long message, show the next actionable item. (getWarningMessage(missingDependencies, 'a', 'missing', 'include') || getWarningMessage( @@ -1191,7 +1278,7 @@ export default { }); } - function visitCallExpression(node) { + function visitCallExpression(node: CallExpression): void { const callbackIndex = getReactiveHookCallbackIndex(node.callee, options); if (callbackIndex === -1) { // Not a React Hook call that needs deps. @@ -1199,7 +1286,9 @@ export default { } let callback = node.arguments[callbackIndex]; const reactiveHook = node.callee; - const reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name; + const nodeWithoutNamespace = getNodeWithoutReactNamespace(reactiveHook); + const reactiveHookName = + 'name' in nodeWithoutNamespace ? nodeWithoutNamespace.name : ''; const maybeNode = node.arguments[callbackIndex + 1]; const declaredDependenciesNode = maybeNode && @@ -1268,6 +1357,7 @@ export default { // The function passed as a callback is not written inline. // But perhaps it's in the dependencies array? if ( + 'elements' in declaredDependenciesNode && declaredDependenciesNode.elements && declaredDependenciesNode.elements.some( el => el && el.type === 'Identifier' && el.name === callback.name, @@ -1368,7 +1458,7 @@ export default { CallExpression: visitCallExpression, }; }, -}; +} satisfies Rule.RuleModule; // The meat of the logic. function collectRecommendations({ @@ -1377,6 +1467,12 @@ function collectRecommendations({ stableDependencies, externalDependencies, isEffect, +}: { + dependencies: Map; + declaredDependencies: DeclaredDependency[]; + stableDependencies: Set; + externalDependencies: Set; + isEffect: boolean; }) { // Our primary data structure. // It is a logical representation of property chains: @@ -1388,7 +1484,7 @@ function collectRecommendations({ // and the nodes that were *declared* as deps. Then we will // traverse it to learn which deps are missing or unnecessary. const depTree = createDepTree(); - function createDepTree() { + function createDepTree(): DependencyTreeNode { return { isUsed: false, // True if used in code isSatisfiedRecursively: false, // True if specified in deps @@ -1419,7 +1515,10 @@ function collectRecommendations({ }); // Tree manipulation helpers. - function getOrCreateNodeByPath(rootNode, path) { + function getOrCreateNodeByPath( + rootNode: DependencyTreeNode, + path: string, + ): DependencyTreeNode { const keys = path.split('.'); let node = rootNode; for (const key of keys) { @@ -1432,7 +1531,11 @@ function collectRecommendations({ } return node; } - function markAllParentsByPath(rootNode, path, fn) { + function markAllParentsByPath( + rootNode: DependencyTreeNode, + path: string, + fn: (node: DependencyTreeNode) => void, + ): void { const keys = path.split('.'); let node = rootNode; for (const key of keys) { @@ -1446,15 +1549,20 @@ function collectRecommendations({ } // Now we can learn which dependencies are missing or necessary. - const missingDependencies = new Set(); - const satisfyingDependencies = new Set(); + const missingDependencies = new Set(); + const satisfyingDependencies = new Set(); scanTreeRecursively( depTree, missingDependencies, satisfyingDependencies, key => key, ); - function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) { + function scanTreeRecursively( + node: DependencyTreeNode, + missingPaths: Set, + satisfyingPaths: Set, + keyToPath: (key: string) => string, + ): void { node.children.forEach((child, key) => { const path = keyToPath(key); if (child.isSatisfiedRecursively) { @@ -1484,9 +1592,9 @@ function collectRecommendations({ } // Collect suggestions in the order they were originally specified. - const suggestedDependencies = []; - const unnecessaryDependencies = new Set(); - const duplicateDependencies = new Set(); + const suggestedDependencies: string[] = []; + const unnecessaryDependencies = new Set(); + const duplicateDependencies = new Set(); declaredDependencies.forEach(({key}) => { // Does this declared dep satisfy a real need? if (satisfyingDependencies.has(key)) { @@ -1532,7 +1640,7 @@ function collectRecommendations({ // If the node will result in constructing a referentially unique value, return // its human readable type name, else return null. -function getConstructionExpressionType(node) { +function getConstructionExpressionType(node: Node): string | null { switch (node.type) { case 'ObjectExpression': return 'object'; @@ -1590,6 +1698,11 @@ function scanForConstructions({ declaredDependenciesNode, componentScope, scope, +}: { + declaredDependencies: DeclaredDependency[]; + declaredDependenciesNode: Node; + componentScope: Scope.Scope; + scope: Scope.Scope; }) { const constructions = declaredDependencies .map(({key}) => { @@ -1616,7 +1729,7 @@ function scanForConstructions({ const constantExpressionType = getConstructionExpressionType( node.node.init, ); - if (constantExpressionType != null) { + if (constantExpressionType) { return [ref, constantExpressionType]; } } @@ -1634,12 +1747,11 @@ function scanForConstructions({ } return null; }) - .filter(Boolean); + .filter(Boolean) as [Scope.Variable, string][]; - function isUsedOutsideOfHook(ref) { + function isUsedOutsideOfHook(ref: Scope.Variable): boolean { let foundWriteExpr = false; - for (let i = 0; i < ref.references.length; i++) { - const reference = ref.references[i]; + for (const reference of ref.references) { if (reference.writeExpr) { if (foundWriteExpr) { // Two writes to the same function. @@ -1650,7 +1762,7 @@ function scanForConstructions({ continue; } } - let currentScope = reference.from; + let currentScope: Scope.Scope | null = reference.from; while (currentScope !== scope && currentScope != null) { currentScope = currentScope.upper; } @@ -1666,7 +1778,7 @@ function scanForConstructions({ } return constructions.map(([ref, depType]) => ({ - construction: ref.defs[0], + construction: ref.defs[0] as Scope.Definition, depType, isUsedOutsideOfHook: isUsedOutsideOfHook(ref), })); @@ -1679,11 +1791,13 @@ function scanForConstructions({ * props.foo.(bar) => (props).foo.bar * props.foo.bar.(baz) => (props).foo.bar.baz */ -function getDependency(node) { +function getDependency(node: Node): Node { if ( + node.parent && (node.parent.type === 'MemberExpression' || node.parent.type === 'OptionalMemberExpression') && node.parent.object === node && + 'name' in node.parent.property && node.parent.property.name !== 'current' && !node.parent.computed && !( @@ -1713,9 +1827,13 @@ function getDependency(node) { * It just means there is an optional member somewhere inside. * This particular node might still represent a required member, so check .optional field. */ -function markNode(node, optionalChains, result) { +function markNode( + node: Node, + optionalChains: Map | null, + result: string, +): void { if (optionalChains) { - if (node.optional) { + if ('optional' in node && node.optional) { // We only want to consider it optional if *all* usages were optional. if (!optionalChains.has(result)) { // Mark as (maybe) optional. If there's a required usage, this will be overridden. @@ -1735,7 +1853,10 @@ function markNode(node, optionalChains, result) { * foo.bar(.)baz -> 'foo.bar.baz' * Otherwise throw. */ -function analyzePropertyChain(node, optionalChains) { +function analyzePropertyChain( + node: Node, + optionalChains: Map | null, +): string { if (node.type === 'Identifier' || node.type === 'JSXIdentifier') { const result = node.name; if (optionalChains) { @@ -1755,7 +1876,10 @@ function analyzePropertyChain(node, optionalChains) { const result = `${object}.${property}`; markNode(node, optionalChains, result); return result; - } else if (node.type === 'ChainExpression' && !node.computed) { + } else if ( + node.type === 'ChainExpression' && + (!('computed' in node) || !node.computed) + ) { const expression = node.expression; if (expression.type === 'CallExpression') { @@ -1772,7 +1896,9 @@ function analyzePropertyChain(node, optionalChains) { } } -function getNodeWithoutReactNamespace(node, options) { +function getNodeWithoutReactNamespace( + node: Expression | Super, +): Expression | Identifier | Super { if ( node.type === 'MemberExpression' && node.object.type === 'Identifier' && @@ -1790,7 +1916,13 @@ function getNodeWithoutReactNamespace(node, options) { // 0 for useEffect/useMemo/useCallback(fn). // 1 for useImperativeHandle(ref, fn). // For additionally configured Hooks, assume that they're like useEffect (0). -function getReactiveHookCallbackIndex(calleeNode, options) { +function getReactiveHookCallbackIndex( + calleeNode: Expression | Super, + options?: { + additionalHooks: RegExp | undefined; + enableDangerousAutofixThisMayCauseInfiniteLoops?: boolean; + }, +): 0 | -1 | 1 { const node = getNodeWithoutReactNamespace(calleeNode); if (node.type !== 'Identifier') { return -1; @@ -1812,8 +1944,11 @@ function getReactiveHookCallbackIndex(calleeNode, options) { let name; try { name = analyzePropertyChain(node, null); - } catch (error) { - if (/Unsupported node type/.test(error.message)) { + } catch (error: unknown) { + if ( + error instanceof Error && + /Unsupported node type/.test(error.message) + ) { return 0; } else { throw error; @@ -1836,12 +1971,12 @@ function getReactiveHookCallbackIndex(calleeNode, options) { * - optimized by only searching nodes with a range surrounding our target node * - agnostic to AST node types, it looks for `{ type: string, ... }` */ -function fastFindReferenceWithParent(start, target) { +function fastFindReferenceWithParent(start: Node, target: Node): Node | null { const queue = [start]; - let item = null; + let item: Node; while (queue.length) { - item = queue.shift(); + item = queue.shift() as Node; if (isSameIdentifier(item, target)) { return item; @@ -1872,7 +2007,7 @@ function fastFindReferenceWithParent(start, target) { return null; } -function joinEnglish(arr) { +function joinEnglish(arr: string[]): string { let s = ''; for (let i = 0; i < arr.length; i++) { s += arr[i]; @@ -1887,39 +2022,49 @@ function joinEnglish(arr) { return s; } -function isNodeLike(val) { +function isNodeLike(val: unknown): boolean { return ( typeof val === 'object' && val !== null && !Array.isArray(val) && + 'type' in val && typeof val.type === 'string' ); } -function isSameIdentifier(a, b) { +function isSameIdentifier(a: Node, b: Node): boolean { return ( (a.type === 'Identifier' || a.type === 'JSXIdentifier') && a.type === b.type && a.name === b.name && + !!a.range && + !!b.range && a.range[0] === b.range[0] && a.range[1] === b.range[1] ); } -function isAncestorNodeOf(a, b) { - return a.range[0] <= b.range[0] && a.range[1] >= b.range[1]; +function isAncestorNodeOf(a: Node, b: Node): boolean { + return ( + !!a.range && + !!b.range && + a.range[0] <= b.range[0] && + a.range[1] >= b.range[1] + ); } -function isUseEffectEventIdentifier(node) { +function isUseEffectEventIdentifier(node: Node): boolean { if (__EXPERIMENTAL__) { return node.type === 'Identifier' && node.name === 'useEffectEvent'; } return false; } -function getUnknownDependenciesMessage(reactiveHookName) { +function getUnknownDependenciesMessage(reactiveHookName: string): string { return ( `React Hook ${reactiveHookName} received a function whose dependencies ` + `are unknown. Pass an inline function instead.` ); } + +export default rule; diff --git a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts similarity index 85% rename from packages/eslint-plugin-react-hooks/src/RulesOfHooks.js rename to packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts index fc340519e233e..24870d71614ea 100644 --- a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js +++ b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts @@ -4,18 +4,16 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ - -/* global BigInt */ /* eslint-disable no-for-of-loops/no-for-of-loops */ -'use strict'; +import type {Rule, Scope} from 'eslint'; +import type {CallExpression, DoWhileStatement, Node} from 'estree'; /** * Catch all identifiers that begin with "use" followed by an uppercase Latin * character to exclude identifiers like "user". */ - -function isHookName(s) { +function isHookName(s: string): boolean { return s === 'use' || /^use[A-Z0-9]/.test(s); } @@ -23,8 +21,7 @@ function isHookName(s) { * We consider hooks to be a hook name identifier or a member expression * containing a hook name. */ - -function isHook(node) { +function isHook(node: Node): boolean { if (node.type === 'Identifier') { return isHookName(node.name); } else if ( @@ -44,16 +41,17 @@ function isHook(node) { * Checks if the node is a React component name. React component names must * always start with an uppercase letter. */ - -function isComponentName(node) { +function isComponentName(node: Node): boolean { return node.type === 'Identifier' && /^[A-Z]/.test(node.name); } -function isReactFunction(node, functionName) { +function isReactFunction(node: Node, functionName: string): boolean { return ( - node.name === functionName || + ('name' in node && node.name === functionName) || (node.type === 'MemberExpression' && + 'name' in node.object && node.object.name === 'React' && + 'name' in node.property && node.property.name === functionName) ); } @@ -62,10 +60,10 @@ function isReactFunction(node, functionName) { * Checks if the node is a callback argument of forwardRef. This render function * should follow the rules of hooks. */ - -function isForwardRefCallback(node) { +function isForwardRefCallback(node: Node): boolean { return !!( node.parent && + 'callee' in node.parent && node.parent.callee && isReactFunction(node.parent.callee, 'forwardRef') ); @@ -75,16 +73,16 @@ function isForwardRefCallback(node) { * Checks if the node is a callback argument of React.memo. This anonymous * functional component should follow the rules of hooks. */ - -function isMemoCallback(node) { +function isMemoCallback(node: Node): boolean { return !!( node.parent && + 'callee' in node.parent && node.parent.callee && isReactFunction(node.parent.callee, 'memo') ); } -function isInsideComponentOrHook(node) { +function isInsideComponentOrHook(node: Node | undefined): boolean { while (node) { const functionName = getFunctionName(node); if (functionName) { @@ -100,7 +98,7 @@ function isInsideComponentOrHook(node) { return false; } -function isInsideDoWhileLoop(node) { +function isInsideDoWhileLoop(node: Node | undefined): node is DoWhileStatement { while (node) { if (node.type === 'DoWhileStatement') { return true; @@ -110,18 +108,18 @@ function isInsideDoWhileLoop(node) { return false; } -function isUseEffectEventIdentifier(node) { +function isUseEffectEventIdentifier(node: Node): boolean { if (__EXPERIMENTAL__) { return node.type === 'Identifier' && node.name === 'useEffectEvent'; } return false; } -function isUseIdentifier(node) { +function isUseIdentifier(node: Node): boolean { return isReactFunction(node, 'use'); } -export default { +const rule = { meta: { type: 'problem', docs: { @@ -130,25 +128,28 @@ export default { url: 'https://reactjs.org/docs/hooks-rules.html', }, }, - create(context) { - let lastEffect = null; - const codePathReactHooksMapStack = []; - const codePathSegmentStack = []; + create(context: Rule.RuleContext) { + let lastEffect: CallExpression | null = null; + const codePathReactHooksMapStack: Map[] = []; + const codePathSegmentStack: Rule.CodePathSegment[] = []; const useEffectEventFunctions = new WeakSet(); // For a given scope, iterate through the references and add all useEffectEvent definitions. We can // do this in non-Program nodes because we can rely on the assumption that useEffectEvent functions // can only be declared within a component or hook at its top level. - function recordAllUseEffectEventFunctions(scope) { + function recordAllUseEffectEventFunctions(scope: Scope.Scope): void { for (const reference of scope.references) { const parent = reference.identifier.parent; if ( - parent.type === 'VariableDeclarator' && + parent?.type === 'VariableDeclarator' && parent.init && parent.init.type === 'CallExpression' && parent.init.callee && isUseEffectEventIdentifier(parent.init.callee) ) { + if (reference.resolved === null) { + throw new Error('Unexpected null reference.resolved'); + } for (const ref of reference.resolved.references) { if (ref !== reference) { useEffectEventFunctions.add(ref.identifier); @@ -159,26 +160,26 @@ export default { } /** - * SourceCode#getText that also works down to ESLint 3.0.0 + * SourceCode that also works down to ESLint 3.0.0 */ - const getSource = - typeof context.getSource === 'function' - ? node => { - return context.getSource(node); + const getSourceCode = + typeof context.getSourceCode === 'function' + ? () => { + return context.getSourceCode(); } - : node => { - return context.sourceCode.getText(node); + : () => { + return context.sourceCode; }; /** * SourceCode#getScope that also works down to ESLint 3.0.0 */ const getScope = typeof context.getScope === 'function' - ? () => { + ? (): Scope.Scope => { return context.getScope(); } - : node => { - return context.sourceCode.getScope(node); + : (node: Node): Scope.Scope => { + return getSourceCode().getScope(node); }; return { @@ -187,7 +188,10 @@ export default { onCodePathSegmentEnd: () => codePathSegmentStack.pop(), // Maintain code path stack as we traverse. - onCodePathStart: () => codePathReactHooksMapStack.push(new Map()), + onCodePathStart: () => + codePathReactHooksMapStack.push( + new Map(), + ), // Process our code path. // @@ -195,8 +199,10 @@ export default { // segment and reachable from every final segment. onCodePathEnd(codePath, codePathNode) { const reactHooksMap = codePathReactHooksMapStack.pop(); - if (reactHooksMap.size === 0) { + if (reactHooksMap?.size === 0) { return; + } else if (typeof reactHooksMap === 'undefined') { + throw new Error('Unexpected undefined reactHooksMap'); } // All of the segments which are cyclic are recorded in this set. @@ -223,11 +229,13 @@ export default { * * Populates `cyclic` with cyclic segments. */ - - function countPathsFromStart(segment, pathHistory) { + function countPathsFromStart( + segment: Rule.CodePathSegment, + pathHistory?: Set, + ): bigint { const {cache} = countPathsFromStart; let paths = cache.get(segment.id); - const pathList = new Set(pathHistory); + const pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle! // We need to fill `cyclic` with all segments inside cycle @@ -295,7 +303,10 @@ export default { * Populates `cyclic` with cyclic segments. */ - function countPathsToEnd(segment, pathHistory) { + function countPathsToEnd( + segment: Rule.CodePathSegment, + pathHistory?: Set, + ): bigint { const {cache} = countPathsToEnd; let paths = cache.get(segment.id); const pathList = new Set(pathHistory); @@ -359,7 +370,9 @@ export default { * so we would return that. */ - function shortestPathLengthToStart(segment) { + function shortestPathLengthToStart( + segment: Rule.CodePathSegment, + ): number { const {cache} = shortestPathLengthToStart; let length = cache.get(segment.id); @@ -392,9 +405,9 @@ export default { return length; } - countPathsFromStart.cache = new Map(); - countPathsToEnd.cache = new Map(); - shortestPathLengthToStart.cache = new Map(); + countPathsFromStart.cache = new Map(); + countPathsToEnd.cache = new Map(); + shortestPathLengthToStart.cache = new Map(); // Count all code paths to the end of our component/hook. Also primes // the `countPathsToEnd` cache. @@ -502,7 +515,7 @@ export default { context.report({ node: hook, message: - `React Hook "${getSource(hook)}" may be executed ` + + `React Hook "${getSourceCode().getText(hook)}" may be executed ` + 'more than once. Possibly because it is called in a loop. ' + 'React Hooks must be called in the exact same order in ' + 'every component render.', @@ -516,12 +529,13 @@ export default { // called in. if (isDirectlyInsideComponentOrHook) { // Report an error if the hook is called inside an async function. + // @ts-expect-error the above check hasn't properly type-narrowed `codePathNode` (async doesn't exist on Node) const isAsyncFunction = codePathNode.async; if (isAsyncFunction) { context.report({ node: hook, message: - `React Hook "${getSource(hook)}" cannot be ` + + `React Hook "${getSourceCode().getText(hook)}" cannot be ` + 'called in an async function.', }); } @@ -537,7 +551,7 @@ export default { !isInsideDoWhileLoop(hook) // wrapping do/while loops are checked separately. ) { const message = - `React Hook "${getSource(hook)}" is called ` + + `React Hook "${getSourceCode().getText(hook)}" is called ` + 'conditionally. React Hooks must be called in the exact ' + 'same order in every component render.' + (possiblyHasEarlyReturn @@ -549,21 +563,22 @@ export default { } else if ( codePathNode.parent && (codePathNode.parent.type === 'MethodDefinition' || + // @ts-expect-error `ClassProperty` was removed from typescript-estree in https://github.com/typescript-eslint/typescript-eslint/pull/3806 codePathNode.parent.type === 'ClassProperty' || codePathNode.parent.type === 'PropertyDefinition') && codePathNode.parent.value === codePathNode ) { // Custom message for hooks inside a class const message = - `React Hook "${getSource(hook)}" cannot be called ` + + `React Hook "${getSourceCode().getText(hook)}" cannot be called ` + 'in a class component. React Hooks must be called in a ' + 'React function component or a custom React Hook function.'; context.report({node: hook, message}); } else if (codePathFunctionName) { // Custom message if we found an invalid function name. const message = - `React Hook "${getSource(hook)}" is called in ` + - `function "${getSource(codePathFunctionName)}" ` + + `React Hook "${getSourceCode().getText(hook)}" is called in ` + + `function "${getSourceCode().getText(codePathFunctionName)}" ` + 'that is neither a React function component nor a custom ' + 'React Hook function.' + ' React component names must start with an uppercase letter.' + @@ -572,7 +587,7 @@ export default { } else if (codePathNode.type === 'Program') { // These are dangerous if you have inline requires enabled. const message = - `React Hook "${getSource(hook)}" cannot be called ` + + `React Hook "${getSourceCode().getText(hook)}" cannot be called ` + 'at the top level. React Hooks must be called in a ' + 'React function component or a custom React Hook function.'; context.report({node: hook, message}); @@ -585,7 +600,7 @@ export default { // `use(...)` can be called in callbacks. if (isSomewhereInsideComponentOrHook && !isUseIdentifier(hook)) { const message = - `React Hook "${getSource(hook)}" cannot be called ` + + `React Hook "${getSourceCode().getText(hook)}" cannot be called ` + 'inside a callback. React Hooks must be called in a ' + 'React function component or a custom React Hook function.'; context.report({node: hook, message}); @@ -638,7 +653,7 @@ export default { context.report({ node, message: - `\`${getSource( + `\`${getSourceCode().getText( node, )}\` is a function created with React Hook "useEffectEvent", and can only be called from ` + 'the same component. They cannot be assigned to variables or passed down.', @@ -667,7 +682,7 @@ export default { }, }; }, -}; +} satisfies Rule.RuleModule; /** * Gets the static name of a function AST node. For function declarations it is @@ -677,7 +692,7 @@ export default { * same AST nodes with some exceptions to better fit our use case. */ -function getFunctionName(node) { +function getFunctionName(node: Node) { if ( node.type === 'FunctionDeclaration' || (node.type === 'FunctionExpression' && node.id) @@ -693,20 +708,20 @@ function getFunctionName(node) { node.type === 'ArrowFunctionExpression' ) { if ( - node.parent.type === 'VariableDeclarator' && + node.parent?.type === 'VariableDeclarator' && node.parent.init === node ) { // const useHook = () => {}; return node.parent.id; } else if ( - node.parent.type === 'AssignmentExpression' && + node.parent?.type === 'AssignmentExpression' && node.parent.right === node && node.parent.operator === '=' ) { // useHook = () => {}; return node.parent.left; } else if ( - node.parent.type === 'Property' && + node.parent?.type === 'Property' && node.parent.value === node && !node.parent.computed ) { @@ -721,8 +736,9 @@ function getFunctionName(node) { // class {useHook = () => {}} // class {useHook() {}} } else if ( - node.parent.type === 'AssignmentPattern' && + node.parent?.type === 'AssignmentPattern' && node.parent.right === node && + // @ts-expect-error Property computed does not exist on type `AssignmentPattern`. !node.parent.computed ) { // const {useHook = () => {}} = {}; @@ -742,7 +758,8 @@ function getFunctionName(node) { /** * Convenience function for peeking the last item in a stack. */ - -function last(array) { - return array[array.length - 1]; +function last(array: T[]): T { + return array[array.length - 1] as T; } + +export default rule; diff --git a/packages/eslint-plugin-react-hooks/src/index.js b/packages/eslint-plugin-react-hooks/src/index.js deleted file mode 100644 index 0ebfbc91a5a8d..0000000000000 --- a/packages/eslint-plugin-react-hooks/src/index.js +++ /dev/null @@ -1,61 +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. - */ - -'use strict'; - -import RulesOfHooks from './RulesOfHooks'; -import ExhaustiveDeps from './ExhaustiveDeps'; - -// All rules -export const rules = { - 'rules-of-hooks': RulesOfHooks, - 'exhaustive-deps': ExhaustiveDeps, -}; - -// Config rules -const configRules = { - 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'warn', -}; - -// Legacy config -const legacyRecommendedConfig = { - plugins: ['react-hooks'], - rules: configRules, -}; - -// Base plugin object -const reactHooksPlugin = { - meta: {name: 'eslint-plugin-react-hooks'}, - rules, -}; - -// Flat config -const flatRecommendedConfig = { - name: 'react-hooks/recommended', - plugins: {'react-hooks': reactHooksPlugin}, - rules: configRules, -}; - -export const configs = { - /** Legacy recommended config, to be used with rc-based configurations */ - 'recommended-legacy': legacyRecommendedConfig, - - /** Latest recommended config, to be used with flat configurations */ - 'recommended-latest': flatRecommendedConfig, - - /** - * 'recommended' is currently aliased to the legacy / rc recommended config) to maintain backwards compatibility. - * This is deprecated and in v6, it will switch to alias the flat recommended config. - */ - recommended: legacyRecommendedConfig, -}; - -export default { - ...reactHooksPlugin, - configs, -}; diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts new file mode 100644 index 0000000000000..42b55320fcd89 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -0,0 +1,64 @@ +/** + * 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 RulesOfHooks from './RulesOfHooks'; +import ExhaustiveDeps from './ExhaustiveDeps'; +import type {ESLint, Linter, Rule} from 'eslint'; + +// All rules +const rules = { + 'rules-of-hooks': RulesOfHooks, + 'exhaustive-deps': ExhaustiveDeps, +} satisfies Record; + +// Config rules +const configRules = { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', +} satisfies Linter.RulesRecord; + +// Legacy config +const legacyRecommendedConfig = { + plugins: ['react-hooks'], + rules: configRules, +} satisfies Linter.LegacyConfig; + +// Plugin object +const plugin = { + // TODO: Make this more dynamic to populate version from package.json. + // This can be done by injecting at build time, since importing the package.json isn't an option in Meta + meta: {name: 'eslint-plugin-react-hooks'}, + rules, + configs: { + /** Legacy recommended config, to be used with rc-based configurations */ + 'recommended-legacy': legacyRecommendedConfig, + + /** + * 'recommended' is currently aliased to the legacy / rc recommended config) to maintain backwards compatibility. + * This is deprecated and in v6, it will switch to alias the flat recommended config. + */ + recommended: legacyRecommendedConfig, + + /** Latest recommended config, to be used with flat configurations */ + 'recommended-latest': { + name: 'react-hooks/recommended', + plugins: { + get 'react-hooks'(): ESLint.Plugin { + return plugin; + }, + }, + rules: configRules, + }, + }, +} satisfies ESLint.Plugin; + +const configs = plugin.configs; +const meta = plugin.meta; +export {configs, meta, rules}; + +// TODO: If the plugin is ever updated to be pure ESM and drops support for rc-based configs, then it should be exporting the plugin as default +// instead of individual named exports. +// export default plugin; diff --git a/packages/eslint-plugin-react-hooks/src/types/estree.d.ts b/packages/eslint-plugin-react-hooks/src/types/estree.d.ts index 10337ef7d6bf0..f266369704885 100644 --- a/packages/eslint-plugin-react-hooks/src/types/estree.d.ts +++ b/packages/eslint-plugin-react-hooks/src/types/estree.d.ts @@ -1,3 +1,5 @@ +import {Expression, Identifier, Node} from 'estree-jsx'; + /** * This file augments the `estree` types to include types that are not built-in to `estree` or `estree-jsx`. * This is necessary because the `estree` types are used by ESLint, and ESLint does not natively support diff --git a/packages/eslint-plugin-react-hooks/tsconfig.json b/packages/eslint-plugin-react-hooks/tsconfig.json index db8a4893649ea..e67b840d3fd7a 100644 --- a/packages/eslint-plugin-react-hooks/tsconfig.json +++ b/packages/eslint-plugin-react-hooks/tsconfig.json @@ -6,7 +6,6 @@ "moduleResolution": "Bundler", "lib": ["ES2020"], "rootDir": ".", - "noEmit": true, "sourceMap": false, "types": ["estree-jsx", "node"] }, diff --git a/packages/eslint-plugin-react-hooks/tsup.config.ts b/packages/eslint-plugin-react-hooks/tsup.config.ts deleted file mode 100644 index e275d0495e26c..0000000000000 --- a/packages/eslint-plugin-react-hooks/tsup.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {defineConfig} from 'tsup'; - -export default defineConfig({ - clean: true, - dts: true, - entry: ['src/index.ts'], - format: ['cjs'], - outDir: 'build', -}); diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index c66cff82c5ac2..0a378c4f31bf7 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -1184,17 +1184,19 @@ const bundles = [ /******* ESLint Plugin for Hooks *******/ { - // TODO: it's awkward to create a bundle for this but if we don't, the package - // won't get copied. We also can't create just DEV bundle because it contains a - // NODE_ENV check inside. We should probably tweak our build process to allow - // "raw" packages that don't get bundled. - bundleTypes: [NODE_DEV, NODE_PROD], + // TODO: we're building this from typescript source now, but there's really + // no reason to have both dev and prod for this package. It's + // currently required in order for the package to be copied over correctly. + // So, it would be worth improving that flow. + name: 'eslint-plugin-react-hooks', + bundleTypes: [NODE_DEV, NODE_PROD, CJS_DTS], moduleType: ISOMORPHIC, - entry: 'eslint-plugin-react-hooks', + entry: 'eslint-plugin-react-hooks/src/index.ts', global: 'ESLintPluginReactHooks', minifyWithProdErrorCodes: false, wrapWithModuleBoundaries: false, externals: [], + tsconfig: './packages/eslint-plugin-react-hooks/tsconfig.json', }, /******* React Fresh *******/ diff --git a/yarn.lock b/yarn.lock index 44d590625111c..ba71ba7abe323 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2403,131 +2403,6 @@ opn "5.3.0" react "^16.13.1" -"@esbuild/aix-ppc64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" - integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== - -"@esbuild/android-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" - integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== - -"@esbuild/android-arm@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" - integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== - -"@esbuild/android-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" - integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== - -"@esbuild/darwin-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" - integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== - -"@esbuild/darwin-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" - integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== - -"@esbuild/freebsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" - integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== - -"@esbuild/freebsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" - integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== - -"@esbuild/linux-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" - integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== - -"@esbuild/linux-arm@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" - integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== - -"@esbuild/linux-ia32@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" - integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== - -"@esbuild/linux-loong64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" - integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== - -"@esbuild/linux-mips64el@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" - integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== - -"@esbuild/linux-ppc64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" - integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== - -"@esbuild/linux-riscv64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" - integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== - -"@esbuild/linux-s390x@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" - integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== - -"@esbuild/linux-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" - integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== - -"@esbuild/netbsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" - integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== - -"@esbuild/netbsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" - integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== - -"@esbuild/openbsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" - integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== - -"@esbuild/openbsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" - integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== - -"@esbuild/sunos-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" - integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== - -"@esbuild/win32-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" - integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== - -"@esbuild/win32-ia32@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" - integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== - -"@esbuild/win32-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" - integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== - "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -2924,15 +2799,6 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -3396,101 +3262,6 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-android-arm-eabi@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz#e554185b1afa5509a7a4040d15ec0c3b4435ded1" - integrity sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw== - -"@rollup/rollup-android-arm64@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz#b1ee64bb413b2feba39803b0a1bebf2a9f3d70e1" - integrity sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg== - -"@rollup/rollup-darwin-arm64@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz#bfdce3e07a345dd1bd628f3b796050f39629d7f0" - integrity sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA== - -"@rollup/rollup-darwin-x64@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz#781a94a537c57bdf0a500e47a25ab5985e5e8dff" - integrity sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA== - -"@rollup/rollup-freebsd-arm64@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz#7a028357cbd12c5869c446ad18177c89f3405102" - integrity sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA== - -"@rollup/rollup-freebsd-x64@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz#f24836a6371cccc4408db74f0fd986dacf098950" - integrity sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q== - -"@rollup/rollup-linux-arm-gnueabihf@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz#95f27e96f0eb9b9ae9887739a8b6dffc90c1237f" - integrity sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ== - -"@rollup/rollup-linux-arm-musleabihf@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz#677b34fba9d070877736c3fe8b02aacb5e142d97" - integrity sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw== - -"@rollup/rollup-linux-arm64-gnu@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz#32d3d19dedde54e91574a098f22ea43a09cf63dd" - integrity sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA== - -"@rollup/rollup-linux-arm64-musl@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz#a58dff44a18696df65ed8c0ad68a2945cf900484" - integrity sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw== - -"@rollup/rollup-linux-loongarch64-gnu@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz#a7488ab078233111e8aeb370d1ecf107ec7e1716" - integrity sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw== - -"@rollup/rollup-linux-powerpc64le-gnu@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz#e9b9c0d6bd248a92b2d6ec01ebf99c62ae1f2e9a" - integrity sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w== - -"@rollup/rollup-linux-riscv64-gnu@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz#0df84ce2bea48ee686fb55060d76ab47aff45c4c" - integrity sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw== - -"@rollup/rollup-linux-s390x-gnu@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz#73df374c57d036856e33dbd2715138922e91e452" - integrity sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw== - -"@rollup/rollup-linux-x64-gnu@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz#f27af0b55f0cdd84e182e6cd44a6d03da0458149" - integrity sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA== - -"@rollup/rollup-linux-x64-musl@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz#c7981ad5cfb8c3cd5d643d33ca54e4d2802b9201" - integrity sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ== - -"@rollup/rollup-win32-arm64-msvc@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz#06cedc0ef3cbf1cbd8abcf587090712e40ae6941" - integrity sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw== - -"@rollup/rollup-win32-ia32-msvc@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz#90b39b977b14961a769be6ea61238e7fc668dd4d" - integrity sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg== - -"@rollup/rollup-win32-x64-msvc@4.34.7": - version "4.34.7" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz#6531d61e7141091eaab0461ee8e0380c10e4ca57" - integrity sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w== - "@sinclair/typebox@^0.25.16": version "0.25.24" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" @@ -3680,7 +3451,7 @@ dependencies: "@types/estree" "*" -"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": +"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -5760,13 +5531,6 @@ bundle-name@^3.0.0: dependencies: run-applescript "^5.0.0" -bundle-require@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-5.1.0.tgz#8db66f41950da3d77af1ef3322f4c3e04009faee" - integrity sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA== - dependencies: - load-tsconfig "^0.2.3" - bunyan@1.8.15: version "1.8.15" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" @@ -5787,11 +5551,6 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cac@^6.7.14: - version "6.7.14" - resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" - integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -6093,13 +5852,6 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chokidar@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" - integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== - dependencies: - readdirp "^4.0.1" - chownr@^1.0.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -6368,7 +6120,7 @@ commander@^2.18.0, commander@^2.20.0, commander@^2.6.0, commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.0, commander@^4.0.1: +commander@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -6511,11 +6263,6 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== -consola@^3.2.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.0.tgz#4cfc9348fd85ed16a17940b3032765e31061ab88" - integrity sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA== - console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" @@ -7039,13 +6786,6 @@ debug@^4.3.4, debug@~4.3.1: dependencies: ms "2.1.2" -debug@^4.3.7: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== - dependencies: - ms "^2.1.3" - decamelize@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-6.0.0.tgz#8cad4d916fde5c41a264a43d0ecc56fe3d31749e" @@ -7744,37 +7484,6 @@ es6-error@4.1.1, es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.24.0: - version "0.24.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" - integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== - optionalDependencies: - "@esbuild/aix-ppc64" "0.24.2" - "@esbuild/android-arm" "0.24.2" - "@esbuild/android-arm64" "0.24.2" - "@esbuild/android-x64" "0.24.2" - "@esbuild/darwin-arm64" "0.24.2" - "@esbuild/darwin-x64" "0.24.2" - "@esbuild/freebsd-arm64" "0.24.2" - "@esbuild/freebsd-x64" "0.24.2" - "@esbuild/linux-arm" "0.24.2" - "@esbuild/linux-arm64" "0.24.2" - "@esbuild/linux-ia32" "0.24.2" - "@esbuild/linux-loong64" "0.24.2" - "@esbuild/linux-mips64el" "0.24.2" - "@esbuild/linux-ppc64" "0.24.2" - "@esbuild/linux-riscv64" "0.24.2" - "@esbuild/linux-s390x" "0.24.2" - "@esbuild/linux-x64" "0.24.2" - "@esbuild/netbsd-arm64" "0.24.2" - "@esbuild/netbsd-x64" "0.24.2" - "@esbuild/openbsd-arm64" "0.24.2" - "@esbuild/openbsd-x64" "0.24.2" - "@esbuild/sunos-x64" "0.24.2" - "@esbuild/win32-arm64" "0.24.2" - "@esbuild/win32-ia32" "0.24.2" - "@esbuild/win32-x64" "0.24.2" - escalade@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" @@ -8649,11 +8358,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fdir@^6.4.2: - version "6.4.3" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" - integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== - fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" @@ -9292,18 +8996,6 @@ glob@^10.2.5: minipass "^5.0.0 || ^6.0.2" path-scurry "^1.7.0" -glob@^10.3.10: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - glob@^6.0.1: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" @@ -11489,11 +11181,6 @@ jose@5.4.1: resolved "https://registry.yarnpkg.com/jose/-/jose-5.4.1.tgz#b471ee3963920ba5452fd1b1398c8ba72a7b2fcf" integrity sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ== -joycon@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" - integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== - jpeg-js@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" @@ -11880,11 +11567,6 @@ lighthouse-logger@^1.0.0: debug "^2.6.8" marky "^1.2.0" -lilconfig@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" - integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== - 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" @@ -11906,11 +11588,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -load-tsconfig@^0.2.3: - version "0.2.5" - resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" - integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== - loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -12047,11 +11724,6 @@ lodash.omitby@4.6.0: resolved "https://registry.yarnpkg.com/lodash.omitby/-/lodash.omitby-4.6.0.tgz#5c15ff4754ad555016b53c041311e8f079204791" integrity sha1-XBX/R1StVVAWtTwEExHo8HkgR5E= -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== - lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -12572,7 +12244,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1, ms@^2.1.2, ms@^2.1.3: +ms@2.1.3, ms@^2.1.1, ms@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -12604,7 +12276,7 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -mz@2.7.0, mz@^2.7.0: +mz@2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== @@ -13646,11 +13318,6 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -13821,13 +13488,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss-load-config@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" - integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== - dependencies: - lilconfig "^3.1.1" - postcss-modules-extract-imports@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a" @@ -14556,11 +14216,6 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" - integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -15003,34 +14658,6 @@ rollup@^3.29.5: optionalDependencies: fsevents "~2.3.2" -rollup@^4.24.0: - version "4.34.7" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.7.tgz#e00d8550688a616a3481c6446bb688d4c753ba8f" - integrity sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ== - dependencies: - "@types/estree" "1.0.6" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.34.7" - "@rollup/rollup-android-arm64" "4.34.7" - "@rollup/rollup-darwin-arm64" "4.34.7" - "@rollup/rollup-darwin-x64" "4.34.7" - "@rollup/rollup-freebsd-arm64" "4.34.7" - "@rollup/rollup-freebsd-x64" "4.34.7" - "@rollup/rollup-linux-arm-gnueabihf" "4.34.7" - "@rollup/rollup-linux-arm-musleabihf" "4.34.7" - "@rollup/rollup-linux-arm64-gnu" "4.34.7" - "@rollup/rollup-linux-arm64-musl" "4.34.7" - "@rollup/rollup-linux-loongarch64-gnu" "4.34.7" - "@rollup/rollup-linux-powerpc64le-gnu" "4.34.7" - "@rollup/rollup-linux-riscv64-gnu" "4.34.7" - "@rollup/rollup-linux-s390x-gnu" "4.34.7" - "@rollup/rollup-linux-x64-gnu" "4.34.7" - "@rollup/rollup-linux-x64-musl" "4.34.7" - "@rollup/rollup-win32-arm64-msvc" "4.34.7" - "@rollup/rollup-win32-ia32-msvc" "4.34.7" - "@rollup/rollup-win32-x64-msvc" "4.34.7" - fsevents "~2.3.2" - rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -15671,13 +15298,6 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@0.8.0-beta.0: - version "0.8.0-beta.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" - integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== - dependencies: - whatwg-url "^7.0.0" - source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -16136,19 +15756,6 @@ style-loader@^1.2.1: loader-utils "^2.0.0" schema-utils "^2.6.6" -sucrase@^3.35.0: - version "3.35.0" - resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" - integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== - dependencies: - "@jridgewell/gen-mapping" "^0.3.2" - commander "^4.0.0" - glob "^10.3.10" - lines-and-columns "^1.1.6" - mz "^2.7.0" - pirates "^4.0.1" - ts-interface-checker "^0.1.9" - sumchecker@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" @@ -16437,19 +16044,6 @@ tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== -tinyexec@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" - integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== - -tinyglobby@^0.2.9: - version "0.2.10" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" - integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== - dependencies: - fdir "^6.4.2" - picomatch "^4.0.2" - titleize@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" @@ -16556,13 +16150,6 @@ tough-cookie@^4.1.2: universalify "^0.2.0" url-parse "^1.5.3" -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== - dependencies: - punycode "^2.1.0" - tr46@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" @@ -16580,11 +16167,6 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -16602,11 +16184,6 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== -ts-interface-checker@^0.1.9: - version "0.1.13" - resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" - integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== - ts-node@8.9.1: version "8.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.9.1.tgz#2f857f46c47e91dcd28a14e052482eb14cfd65a5" @@ -16633,28 +16210,6 @@ tslib@^2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tsup@^8.3.5: - version "8.3.6" - resolved "https://registry.yarnpkg.com/tsup/-/tsup-8.3.6.tgz#a10eb2dc27f84b510a0f00341ab75cad03d13a88" - integrity sha512-XkVtlDV/58S9Ye0JxUUTcrQk4S+EqlOHKzg6Roa62rdjL1nGWNUstG0xgI4vanHdfIpjP448J8vlN0oK6XOJ5g== - dependencies: - bundle-require "^5.0.0" - cac "^6.7.14" - chokidar "^4.0.1" - consola "^3.2.3" - debug "^4.3.7" - esbuild "^0.24.0" - joycon "^3.1.1" - picocolors "^1.1.1" - postcss-load-config "^6.0.1" - resolve-from "^5.0.0" - rollup "^4.24.0" - source-map "0.8.0-beta.0" - sucrase "^3.35.0" - tinyexec "^0.3.1" - tinyglobby "^0.2.9" - tree-kill "^1.2.2" - tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -17287,11 +16842,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -17462,15 +17012,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - when@3.7.7: version "3.7.7" resolved "https://registry.yarnpkg.com/when/-/when-3.7.7.tgz#aba03fc3bb736d6c88b091d013d8a8e590d84718" From be91130f184f04c6e7bb9ff18222aba2ef2f96f6 Mon Sep 17 00:00:00 2001 From: michael faith Date: Sun, 16 Feb 2025 15:14:43 -0600 Subject: [PATCH 033/300] chore: remove `devEngines` declaration in root package (#32398) This change removes the `devEngines` declaration in the root package. It didn't match the package.json spec and in npm 10.9.0 (released in October), a breaking change was introduced that checks the `devEngines` property. This causes `npm pack` calls to fail, due to the malformed `devEngines`. Since there's already an `.nvmrc` defined in the repo, and no strong need to enforce a specific node version for local development, this removes the declaration altogether. --- package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/package.json b/package.json index 95fffa700a7e7..8e5dae3f944a0 100644 --- a/package.json +++ b/package.json @@ -106,9 +106,6 @@ "web-streams-polyfill": "^3.1.1", "yargs": "^15.3.1" }, - "devEngines": { - "node": "16.x || 18.x || 20.x || 22.x" - }, "jest": { "testRegex": "/scripts/jest/dont-run-jest-directly\\.js$" }, @@ -122,7 +119,7 @@ "lint": "node ./scripts/tasks/eslint.js", "lint-build": "node ./scripts/rollup/validate/index.js", "extract-errors": "node scripts/error-codes/extract-errors.js", - "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfigs.js", + "postinstall": "node ./scripts/flow/createFlowConfigs.js", "test": "node ./scripts/jest/jest-cli.js", "test-stable": "node ./scripts/jest/jest-cli.js --release-channel=stable", "test-www": "node ./scripts/jest/jest-cli.js --release-channel=www-modern", From eb1f77dedfc8f7491ecd39b160e4743fa39dfc99 Mon Sep 17 00:00:00 2001 From: michael faith Date: Sun, 16 Feb 2025 16:00:43 -0600 Subject: [PATCH 034/300] ci: add workflow for running the eslint plugin e2e tests (#32397) This change adds a workflow for PR builds, that runs the e2e tests for `eslint-plugin-react-hooks` created in #32396 ![screenshot of ci tests running](https://github.com/user-attachments/assets/307a878c-92b5-44cf-84f2-3b21979b262a) --- .../workflows/runtime_eslint_plugin_e2e.yml | 56 +++++++++++++++++++ fixtures/eslint-v6/build.mjs | 9 ++- fixtures/eslint-v6/package.json | 2 +- fixtures/eslint-v6/yarn.lock | 2 +- fixtures/eslint-v7/build.mjs | 9 ++- fixtures/eslint-v7/package.json | 2 +- fixtures/eslint-v7/yarn.lock | 2 +- fixtures/eslint-v8/build.mjs | 9 ++- fixtures/eslint-v8/package.json | 2 +- fixtures/eslint-v8/yarn.lock | 2 +- fixtures/eslint-v9/build.mjs | 9 ++- fixtures/eslint-v9/package.json | 2 +- fixtures/eslint-v9/yarn.lock | 2 +- 13 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/runtime_eslint_plugin_e2e.yml diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml new file mode 100644 index 0000000000000..a6f0bf28f005e --- /dev/null +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -0,0 +1,56 @@ +name: (Runtime) ESLint Plugin E2E + +on: + push: + branches: [main] + pull_request: + paths-ignore: + - compiler/** + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +env: + TZ: /usr/share/zoneinfo/America/Los_Angeles + +jobs: + # ----- TESTS ----- + test: + name: ESLint v${{ matrix.eslint_major }} + runs-on: ubuntu-latest + strategy: + matrix: + eslint_major: + - "6" + - "7" + - "8" + - "9" + continue-on-error: true + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: yarn + cache-dependency-path: yarn.lock + - name: Restore cached node_modules + uses: actions/cache@v4 + id: node_modules + with: + path: "**/node_modules" + key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + - name: Ensure clean build directory + run: rm -rf build + - run: yarn install --frozen-lockfile + - name: Build plugin + working-directory: fixtures/eslint-v${{ matrix.eslint_major }} + run: node build.mjs + - name: Install fixture dependencies + working-directory: ./fixtures/eslint-v${{ matrix.eslint_major }} + run: yarn --frozen-lockfile + - name: Run lint test + working-directory: ./fixtures/eslint-v${{ matrix.eslint_major }} + run: yarn lint diff --git a/fixtures/eslint-v6/build.mjs b/fixtures/eslint-v6/build.mjs index 02a1bbd25e00e..ebcea15190dae 100644 --- a/fixtures/eslint-v6/build.mjs +++ b/fixtures/eslint-v6/build.mjs @@ -1,5 +1,12 @@ #!/usr/bin/env node import {exec} from 'node:child_process'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; -exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +exec('yarn build -r stable eslint-plugin-react-hooks', { + cwd: resolve(__dirname, '..', '..'), +}); diff --git a/fixtures/eslint-v6/package.json b/fixtures/eslint-v6/package.json index 50c92054b436f..c410edd78fc87 100644 --- a/fixtures/eslint-v6/package.json +++ b/fixtures/eslint-v6/package.json @@ -3,7 +3,7 @@ "name": "eslint-v6", "dependencies": { "eslint": "^6", - "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks" }, "scripts": { "build": "node build.mjs && yarn", diff --git a/fixtures/eslint-v6/yarn.lock b/fixtures/eslint-v6/yarn.lock index 3d6dbca037b38..5af9623e877f3 100644 --- a/fixtures/eslint-v6/yarn.lock +++ b/fixtures/eslint-v6/yarn.lock @@ -205,7 +205,7 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": +"eslint-plugin-react-hooks@link:../../build/oss-stable/eslint-plugin-react-hooks": version "0.0.0" uid "" diff --git a/fixtures/eslint-v7/build.mjs b/fixtures/eslint-v7/build.mjs index 02a1bbd25e00e..ebcea15190dae 100644 --- a/fixtures/eslint-v7/build.mjs +++ b/fixtures/eslint-v7/build.mjs @@ -1,5 +1,12 @@ #!/usr/bin/env node import {exec} from 'node:child_process'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; -exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +exec('yarn build -r stable eslint-plugin-react-hooks', { + cwd: resolve(__dirname, '..', '..'), +}); diff --git a/fixtures/eslint-v7/package.json b/fixtures/eslint-v7/package.json index 90c369c603e6d..5e64917514599 100644 --- a/fixtures/eslint-v7/package.json +++ b/fixtures/eslint-v7/package.json @@ -3,7 +3,7 @@ "name": "eslint-v7", "dependencies": { "eslint": "^7", - "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks" }, "scripts": { "build": "node build.mjs && yarn", diff --git a/fixtures/eslint-v7/yarn.lock b/fixtures/eslint-v7/yarn.lock index 62f97bbc20d69..4a191de947f37 100644 --- a/fixtures/eslint-v7/yarn.lock +++ b/fixtures/eslint-v7/yarn.lock @@ -234,7 +234,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": +"eslint-plugin-react-hooks@link:../../build/oss-stable/eslint-plugin-react-hooks": version "0.0.0" uid "" diff --git a/fixtures/eslint-v8/build.mjs b/fixtures/eslint-v8/build.mjs index 02a1bbd25e00e..ebcea15190dae 100644 --- a/fixtures/eslint-v8/build.mjs +++ b/fixtures/eslint-v8/build.mjs @@ -1,5 +1,12 @@ #!/usr/bin/env node import {exec} from 'node:child_process'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; -exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +exec('yarn build -r stable eslint-plugin-react-hooks', { + cwd: resolve(__dirname, '..', '..'), +}); diff --git a/fixtures/eslint-v8/package.json b/fixtures/eslint-v8/package.json index 620520ecdbfd4..ed3de107c524f 100644 --- a/fixtures/eslint-v8/package.json +++ b/fixtures/eslint-v8/package.json @@ -3,7 +3,7 @@ "name": "eslint-v8", "dependencies": { "eslint": "^8", - "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks" }, "scripts": { "build": "node build.mjs && yarn", diff --git a/fixtures/eslint-v8/yarn.lock b/fixtures/eslint-v8/yarn.lock index 88d04029954a4..d5a947b6347d7 100644 --- a/fixtures/eslint-v8/yarn.lock +++ b/fixtures/eslint-v8/yarn.lock @@ -192,7 +192,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": +"eslint-plugin-react-hooks@link:../../build/oss-stable/eslint-plugin-react-hooks": version "0.0.0" uid "" diff --git a/fixtures/eslint-v9/build.mjs b/fixtures/eslint-v9/build.mjs index 02a1bbd25e00e..ebcea15190dae 100644 --- a/fixtures/eslint-v9/build.mjs +++ b/fixtures/eslint-v9/build.mjs @@ -1,5 +1,12 @@ #!/usr/bin/env node import {exec} from 'node:child_process'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; -exec('cd ../.. && yarn build -r stable eslint-plugin-react-hooks'); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +exec('yarn build -r stable eslint-plugin-react-hooks', { + cwd: resolve(__dirname, '..', '..'), +}); diff --git a/fixtures/eslint-v9/package.json b/fixtures/eslint-v9/package.json index e8ae91a0613e1..58d0a94c0c58c 100644 --- a/fixtures/eslint-v9/package.json +++ b/fixtures/eslint-v9/package.json @@ -3,7 +3,7 @@ "name": "eslint-v9", "dependencies": { "eslint": "^9", - "eslint-plugin-react-hooks": "link:../../build/node_modules/eslint-plugin-react-hooks" + "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks" }, "scripts": { "build": "node build.mjs && yarn", diff --git a/fixtures/eslint-v9/yarn.lock b/fixtures/eslint-v9/yarn.lock index 5a1a246827f6f..1583b73f14e21 100644 --- a/fixtures/eslint-v9/yarn.lock +++ b/fixtures/eslint-v9/yarn.lock @@ -209,7 +209,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -"eslint-plugin-react-hooks@link:../../build/node_modules/eslint-plugin-react-hooks": +"eslint-plugin-react-hooks@link:../../build/oss-stable/eslint-plugin-react-hooks": version "0.0.0" uid "" From 4632e36a4ef16a1af24987c56e42b664f6403e64 Mon Sep 17 00:00:00 2001 From: michael faith Date: Sun, 16 Feb 2025 19:28:12 -0600 Subject: [PATCH 035/300] refactor(eslint-plugin-react-hooks): change array type and improve conditionals (#32400) - [build(eslint-plugin-react-hooks): add ts-linting](https://github.com/facebook/react/commit/4c0fbe73d9abc2681445f62b9450737f3df12ee2) This change adds configuration to the eslint config governing `eslint-plugin-react-hooks` to use the typescript-eslint plugin and parser. It adds the typescript-recommended config, and configures the team's preferred `array-type` convention. - [refactor(eslint-plugin-react-hooks): improve conditionals](https://github.com/facebook/react/commit/540d0d95bc5172ef95ccc2ad70b4b202b6eeedd2) This change addresses several feedback items from https://github.com/facebook/react/pull/32240 - [ci (eslint-e2e): exclude nested node_modules from cache](https://github.com/facebook/react/pull/32400/commits/a3279f46a85cfb4ddea5a863a6f7c71344280d36) This change removes the nested fixture `node_modules` from being cached, so that the symbolic link can be made after the build happens. --- .eslintrc.js | 25 ++++++++-- .../workflows/runtime_eslint_plugin_e2e.yml | 2 +- package.json | 2 + .../src/ExhaustiveDeps.ts | 32 ++++++------ .../src/RulesOfHooks.ts | 12 +++-- yarn.lock | 50 ++++++++++++++++++- 6 files changed, 97 insertions(+), 26 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 7fe08f4cdf36e..360bd5ba76a9c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -446,10 +446,7 @@ module.exports = { }, }, { - files: [ - 'scripts/eslint-rules/*.js', - 'packages/eslint-plugin-react-hooks/src/*.js', - ], + files: ['scripts/eslint-rules/*.js'], plugins: ['eslint-plugin'], rules: { 'eslint-plugin/prefer-object-rule': ERROR, @@ -517,6 +514,26 @@ module.exports = { __IS_INTERNAL_VERSION__: 'readonly', }, }, + { + files: ['packages/eslint-plugin-react-hooks/src/**/*'], + extends: ['plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'eslint-plugin'], + rules: { + '@typescript-eslint/no-explicit-any': OFF, + '@typescript-eslint/no-non-null-assertion': OFF, + '@typescript-eslint/array-type': [ERROR, {default: 'generic'}], + + 'es/no-optional-chaining': OFF, + + 'eslint-plugin/prefer-object-rule': ERROR, + 'eslint-plugin/require-meta-fixable': [ + ERROR, + {catchNoFixerButFixableProperty: true}, + ], + 'eslint-plugin/require-meta-has-suggestions': ERROR, + }, + }, ], env: { diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index a6f0bf28f005e..edc188f38622e 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -40,7 +40,7 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" + path: "node_modules" key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build diff --git a/package.json b/package.json index 8e5dae3f944a0..0a2b6c3024c93 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-typescript": "^12.1.2", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "abortcontroller-polyfill": "^1.7.5", "art": "0.10.1", "babel-plugin-syntax-trailing-function-commas": "^6.5.0", diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts index 9f3d4d4db9004..1b0059757278f 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts @@ -28,7 +28,7 @@ type DeclaredDependency = { type Dependency = { isStable: boolean; - references: Scope.Reference[]; + references: Array; }; type DependencyTreeNode = { @@ -184,7 +184,9 @@ const rule = { // Get the current scope. const scope = scopeManager.acquire(node); if (!scope) { - return; + throw new Error( + 'Unable to acquire scope for the current node. This is a bug in eslint-plugin-react-hooks, please file an issue.', + ); } // Find all our "pure scopes". On every re-render of a component these @@ -255,7 +257,7 @@ const rule = { // Detect primitive constants // const foo = 42 let declaration = defNode.parent; - if (declaration == null && componentScope) { + if (declaration == null && componentScope != null) { // This might happen if variable is declared after the callback. // In that case ESLint won't set up .parent refs. // So we'll set them up manually. @@ -266,7 +268,7 @@ const rule = { } } if ( - declaration && + declaration != null && 'kind' in declaration && declaration.kind === 'const' && init.type === 'Literal' && @@ -454,7 +456,7 @@ const rule = { function isInsideEffectCleanup(reference: Scope.Reference): boolean { let curScope: Scope.Scope | null = reference.from; let isInReturnedFunction = false; - while (curScope && curScope.block !== node) { + while (curScope != null && curScope.block !== node) { if (curScope.type === 'function') { isInReturnedFunction = curScope.block.parent != null && @@ -529,7 +531,7 @@ const rule = { continue; } // Ignore references to the function itself as it's not defined yet. - if (def.node && def.node.init === node.parent) { + if (def.node != null && def.node.init === node.parent) { continue; } // Ignore Flow type parameters @@ -566,8 +568,8 @@ const rule = { // Is React managing this ref or us? // Let's see if we can find a .current assignment. let foundCurrentAssignment = false; - for (const reference of references) { - const {identifier} = reference; + for (const ref of references) { + const {identifier} = ref; const {parent} = identifier; if ( parent != null && @@ -660,7 +662,7 @@ const rule = { } let fnScope: Scope.Scope | null = reference.from; - while (fnScope && fnScope.type !== 'function') { + while (fnScope != null && fnScope.type !== 'function') { fnScope = fnScope.upper; } const isDirectlyInsideEffect = fnScope?.block === node; @@ -704,7 +706,7 @@ const rule = { return; } - const declaredDependencies: DeclaredDependency[] = []; + const declaredDependencies: Array = []; const externalDependencies = new Set(); const isArrayExpression = declaredDependenciesNode.type === 'ArrayExpression'; @@ -1469,7 +1471,7 @@ function collectRecommendations({ isEffect, }: { dependencies: Map; - declaredDependencies: DeclaredDependency[]; + declaredDependencies: Array; stableDependencies: Set; externalDependencies: Set; isEffect: boolean; @@ -1592,7 +1594,7 @@ function collectRecommendations({ } // Collect suggestions in the order they were originally specified. - const suggestedDependencies: string[] = []; + const suggestedDependencies: Array = []; const unnecessaryDependencies = new Set(); const duplicateDependencies = new Set(); declaredDependencies.forEach(({key}) => { @@ -1699,7 +1701,7 @@ function scanForConstructions({ componentScope, scope, }: { - declaredDependencies: DeclaredDependency[]; + declaredDependencies: Array; declaredDependenciesNode: Node; componentScope: Scope.Scope; scope: Scope.Scope; @@ -1747,7 +1749,7 @@ function scanForConstructions({ } return null; }) - .filter(Boolean) as [Scope.Variable, string][]; + .filter(Boolean) as Array<[Scope.Variable, string]>; function isUsedOutsideOfHook(ref: Scope.Variable): boolean { let foundWriteExpr = false; @@ -2007,7 +2009,7 @@ function fastFindReferenceWithParent(start: Node, target: Node): Node | null { return null; } -function joinEnglish(arr: string[]): string { +function joinEnglish(arr: Array): string { let s = ''; for (let i = 0; i < arr.length; i++) { s += arr[i]; diff --git a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts index 24870d71614ea..0ab0c5ff21e4b 100644 --- a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts +++ b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts @@ -130,8 +130,10 @@ const rule = { }, create(context: Rule.RuleContext) { let lastEffect: CallExpression | null = null; - const codePathReactHooksMapStack: Map[] = []; - const codePathSegmentStack: Rule.CodePathSegment[] = []; + const codePathReactHooksMapStack: Array< + Map> + > = []; + const codePathSegmentStack: Array = []; const useEffectEventFunctions = new WeakSet(); // For a given scope, iterate through the references and add all useEffectEvent definitions. We can @@ -190,7 +192,7 @@ const rule = { // Maintain code path stack as we traverse. onCodePathStart: () => codePathReactHooksMapStack.push( - new Map(), + new Map>(), ), // Process our code path. @@ -561,7 +563,7 @@ const rule = { context.report({node: hook, message}); } } else if ( - codePathNode.parent && + codePathNode.parent != null && (codePathNode.parent.type === 'MethodDefinition' || // @ts-expect-error `ClassProperty` was removed from typescript-estree in https://github.com/typescript-eslint/typescript-eslint/pull/3806 codePathNode.parent.type === 'ClassProperty' || @@ -758,7 +760,7 @@ function getFunctionName(node: Node) { /** * Convenience function for peeking the last item in a stack. */ -function last(array: T[]): T { +function last(array: Array): T { return array[array.length - 1] as T; } diff --git a/yarn.lock b/yarn.lock index ba71ba7abe323..7bc936afae4bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2410,6 +2410,11 @@ dependencies: eslint-visitor-keys "^3.3.0" +"@eslint-community/regexpp@^4.5.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + "@eslint-community/regexpp@^4.6.1": version "4.10.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" @@ -3718,6 +3723,23 @@ dependencies: "@types/node" "*" +"@typescript-eslint/eslint-plugin@^6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" + integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/type-utils" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + "@typescript-eslint/experimental-utils@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" @@ -3780,6 +3802,17 @@ "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" +"@typescript-eslint/parser@^6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.1.0.tgz#9e389745ee9cfe12252ed1e9958808abd6b3a683" @@ -3804,6 +3837,16 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" +"@typescript-eslint/type-utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" + integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== + dependencies: + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + "@typescript-eslint/types@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" @@ -3892,7 +3935,7 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@^6.0.0": +"@typescript-eslint/utils@6.21.0", "@typescript-eslint/utils@^6.0.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== @@ -9799,6 +9842,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +ignore@^5.2.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + image-size@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.1.1.tgz#ddd67d4dc340e52ac29ce5f546a09f4e29e840ac" From 8a7b487e3b171c91f2fe18e9142af53f4dd83454 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 18 Feb 2025 10:29:40 -0500 Subject: [PATCH 036/300] [flags] enable owner stacks everywhere (#32376) this is now canary and on everywhere --- .../shared/forks/ReactFeatureFlags.native-fb-dynamic.js | 2 -- packages/shared/forks/ReactFeatureFlags.native-fb.js | 6 ++++-- packages/shared/forks/ReactFeatureFlags.native-oss.js | 4 ++-- .../forks/ReactFeatureFlags.test-renderer.native-fb.js | 4 ++-- .../shared/forks/ReactFeatureFlags.test-renderer.www.js | 4 ++-- packages/shared/forks/ReactFeatureFlags.www-dynamic.js | 2 -- packages/shared/forks/ReactFeatureFlags.www.js | 6 ++++-- scripts/jest/setupTests.www.js | 1 + scripts/jest/setupTests.xplat.js | 2 ++ 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index ffcec9334d663..d3c5de81bd909 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -25,7 +25,5 @@ export const enableShallowPropDiffing = __VARIANT__; export const passChildrenWhenCloningPersistedNodes = __VARIANT__; export const enableSiblingPrerendering = __VARIANT__; export const enableUseEffectCRUDOverload = __VARIANT__; -export const enableOwnerStacks = __VARIANT__; -export const enableRemoveConsolePatches = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 470e148959327..a0544dc161173 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -27,12 +27,14 @@ export const { enableUseEffectCRUDOverload, passChildrenWhenCloningPersistedNodes, enableSiblingPrerendering, - enableOwnerStacks, - enableRemoveConsolePatches, enableFastAddPropertiesInDiffing, enableLazyPublicInstanceInFabric, } = dynamicFlags; +// These two can be removed +export const enableOwnerStacks = true; +export const enableRemoveConsolePatches = true; + // The rest of the flags are static for better dead code elimination. export const disableClientCache = true; export const disableCommentsAsDOMContainers = true; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 361ebde4136ce..fd846cf312212 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -40,7 +40,7 @@ export const enableLegacyFBSupport = false; export const enableLegacyHidden = false; export const enableNoCloningMemoCache = false; export const enableObjectFiber = false; -export const enableOwnerStacks = false; +export const enableOwnerStacks = true; export const enablePersistedModeClonedFlag = false; export const enablePostpone = false; export const enableReactTestRendererWarning = false; @@ -80,7 +80,7 @@ export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__; export const enableUpdaterTracking = __PROFILE__; -export const enableRemoveConsolePatches = false; +export const enableRemoveConsolePatches = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 18172fdfe5724..e4f1210c29f00 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -35,7 +35,7 @@ export const enableLegacyFBSupport = false; export const enableLegacyHidden = false; export const enableNoCloningMemoCache = false; export const enableObjectFiber = false; -export const enableOwnerStacks = false; +export const enableOwnerStacks = true; export const enablePersistedModeClonedFlag = false; export const enablePostpone = false; export const enableProfilerCommitHooks = __PROFILE__; @@ -67,8 +67,8 @@ export const enableHydrationLaneScheduling = true; export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableRemoveConsolePatches = true; export const enableSwipeTransition = false; -export const enableRemoveConsolePatches = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 628a834133f2f..112b30fe04a81 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -70,7 +70,7 @@ export const disableDefaultPropsExceptForClasses = true; export const renameElementSymbol = false; export const enableObjectFiber = false; -export const enableOwnerStacks = false; +export const enableOwnerStacks = true; export const enableShallowPropDiffing = false; export const enableSiblingPrerendering = true; @@ -82,8 +82,8 @@ export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; +export const enableRemoveConsolePatches = true; export const enableSwipeTransition = false; -export const enableRemoveConsolePatches = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index d0b4404a3e946..e0f431fe7f10c 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -26,7 +26,6 @@ export const enableRetryLaneExpiration = __VARIANT__; export const enableTransitionTracing = __VARIANT__; export const favorSafetyOverHydrationPerf = __VARIANT__; export const renameElementSymbol = __VARIANT__; -export const enableOwnerStacks = __VARIANT__; export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; @@ -37,7 +36,6 @@ export const enableInfiniteRenderLoopDetection = __VARIANT__; export const enableSiblingPrerendering = __VARIANT__; export const enableUseEffectCRUDOverload = __VARIANT__; -export const enableRemoveConsolePatches = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = false; export const enableViewTransition = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 5932c6eddf6f1..ec0337382e957 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -35,8 +35,6 @@ export const { retryLaneExpirationMs, syncLaneExpirationMs, transitionLaneExpirationMs, - enableOwnerStacks, - enableRemoveConsolePatches, enableFastAddPropertiesInDiffing, enableViewTransition, } = dynamicFeatureFlags; @@ -44,6 +42,10 @@ export const { // On WWW, __EXPERIMENTAL__ is used for a new modern build. // It's not used anywhere in production yet. +// Can remove these two +export const enableOwnerStacks = true; +export const enableRemoveConsolePatches = true; + export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__; diff --git a/scripts/jest/setupTests.www.js b/scripts/jest/setupTests.www.js index b0b653bb78ff9..9ac7ffc6db002 100644 --- a/scripts/jest/setupTests.www.js +++ b/scripts/jest/setupTests.www.js @@ -11,6 +11,7 @@ jest.mock('shared/ReactFeatureFlags', () => { // Flags that aren't currently used, but we still want to force variants to keep the // code live. actual.disableInputAttributeSyncing = __VARIANT__; + actual.enableOwnerStacks = __VARIANT__; // These are hardcoded to true for the next release, // but still run the tests against both variants until diff --git a/scripts/jest/setupTests.xplat.js b/scripts/jest/setupTests.xplat.js index 859a5065988d6..87fbf8bef8ac5 100644 --- a/scripts/jest/setupTests.xplat.js +++ b/scripts/jest/setupTests.xplat.js @@ -11,6 +11,8 @@ jest.mock('shared/ReactFeatureFlags', () => { 'shared/forks/ReactFeatureFlags.native-fb.js' ); + actual.enableOwnerStacks = __VARIANT__; + // Lots of tests use these, but we don't want to expose it to RN. // Ideally, tests for xplat wouldn't use react-dom, but many of our tests do. // Since the xplat tests run with the www entry points, some of these flags From d99f8bba2e07e3bb953f0821d4da5e341136fe5c Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:32:49 -0700 Subject: [PATCH 037/300] [compiler] Delete LoweredFunction.dependencies and hoisted instructions (#32096) LoweredFunction dependencies were exclusively used for dependency extraction (in `propagateScopeDeps`). Now that we have a `propagateScopeDepsHIR` that recursively traverses into nested functions, we can delete `dependencies` and their associated synthetic `LoadLocal`/`PropertyLoad` instructions. [Internal snapshot diff](https://www.internalfb.com/phabricator/paste/view/P1716950202) for this change shows ~.2% of files changed. I [read through ~60 of the changed files](https://www.internalfb.com/phabricator/paste/view/P1733074307) - most changes are due to better outlining (due to better DCE) - a few changes in memo inference are due to changed ordering ``` // source arr.map(() => contextVar.inner); // previous instructions $0 = LoadLocal arr $1 = $0.map // Below instructions are synthetic $2 = LoadLocal contextVar $3 = $2.inner $4 = Function deps=$3 context=contextVar { ... } ``` - a few changes are effectively bugfixes (see `aliased-nested-scope-fn-expr`) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32096). * #32099 * #32286 * #32104 * #32098 * #32097 * __->__ #32096 --- .../src/HIR/BuildHIR.ts | 154 +----------- .../src/HIR/CollectHoistablePropertyLoads.ts | 37 +-- .../src/HIR/Environment.ts | 2 - .../src/HIR/HIR.ts | 1 - .../HIR/MergeOverlappingReactiveScopesHIR.ts | 17 +- .../src/HIR/PrintHIR.ts | 5 +- .../src/HIR/PropagateScopeDependenciesHIR.ts | 5 +- .../src/HIR/visitors.ts | 7 +- .../src/Inference/AnalyseFunctions.ts | 146 +++--------- .../Inference/InferMutableContextVariables.ts | 23 +- .../src/Inference/InferReferenceEffects.ts | 48 ++-- .../src/Optimization/LowerContextAccess.ts | 1 - .../src/Optimization/OutlineFunctions.ts | 1 - .../InferReactiveScopeVariables.ts | 8 + .../src/SSA/EliminateRedundantPhi.ts | 19 ++ .../src/SSA/EnterSSA.ts | 3 - .../src/Transform/TransformFire.ts | 53 +---- .../aliased-nested-scope-fn-expr.expect.md | 120 ++++++++++ .../compiler/aliased-nested-scope-fn-expr.tsx | 46 ++++ ...iased-nested-scope-truncated-dep.expect.md | 221 ++++++++++++++++++ .../aliased-nested-scope-truncated-dep.tsx | 93 ++++++++ ...access-in-unused-callback-nested.expect.md | 40 ++-- .../capturing-func-mutate-2.expect.md | 1 - .../capturing-func-no-mutate.expect.md | 12 +- ...capturing-func-simple-alias-iife.expect.md | 1 - ...ction-alias-computed-load-2-iife.expect.md | 1 - ...ction-alias-computed-load-3-iife.expect.md | 2 - ...ction-alias-computed-load-4-iife.expect.md | 1 - ...unction-alias-computed-load-iife.expect.md | 1 - ...capturing-reference-changes-type.expect.md | 1 - .../codegen-inline-iife-reassign.expect.md | 3 +- ...-into-function-expression-global.expect.md | 7 +- ...to-function-expression-primitive.expect.md | 7 +- ...gation-into-function-expressions.expect.md | 9 +- ...text-variable-as-jsx-element-tag.expect.md | 3 +- ...ting-simple-function-declaration.expect.md | 10 +- ...call-freezes-captured-identifier.expect.md | 41 ++++ ....hook-call-freezes-captured-identifier.tsx | 20 ++ ...call-freezes-captured-memberexpr.expect.md | 41 ++++ ....hook-call-freezes-captured-memberexpr.jsx | 20 ++ ...on-with-shadowed-local-same-name.expect.md | 2 +- ...setstate-captured-indirectly-jsx.expect.md | 81 +++++++ ...isting-setstate-captured-indirectly-jsx.js | 17 ++ .../compiler/hoisting-setstate.expect.md | 77 ++++++ .../fixtures/compiler/hoisting-setstate.js | 28 +++ ...call-freezes-captured-memberexpr.expect.md | 87 +++++++ .../hook-call-freezes-captured-memberexpr.tsx | 21 ++ .../jsx-local-tag-in-lambda.expect.md | 7 +- .../jsx-memberexpr-tag-in-lambda.expect.md | 7 +- ...mutated-non-reactive-to-reactive.expect.md | 1 - .../lambda-mutated-ref-non-reactive.expect.md | 1 - ...ed-function-shadowed-identifiers.expect.md | 5 +- ...o-reordering-depslist-assignment.expect.md | 1 - ...e-phis-in-lambda-capture-context.expect.md | 29 +-- .../use-operator-conditional.expect.md | 1 - 55 files changed, 1123 insertions(+), 473 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.jsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index d5c3ea6a492a9..67cdcab0381f2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -7,7 +7,6 @@ import {NodePath, Scope} from '@babel/traverse'; import * as t from '@babel/types'; -import {Expression} from '@babel/types'; import invariant from 'invariant'; import { CompilerError, @@ -75,7 +74,7 @@ export function lower( parent: NodePath | null = null, ): Result { const builder = new HIRBuilder(env, parent ?? func, bindings, capturedRefs); - const context: Array = []; + const context: HIRFunction['context'] = []; for (const ref of capturedRefs ?? []) { context.push({ @@ -3378,7 +3377,7 @@ function lowerFunction( >, ): LoweredFunction | null { const componentScope: Scope = builder.parentFunction.scope; - const captured = gatherCapturedDeps(builder, expr, componentScope); + const capturedContext = gatherCapturedContext(expr, componentScope); /* * TODO(gsn): In the future, we could only pass in the context identifiers @@ -3392,7 +3391,7 @@ function lowerFunction( expr, builder.environment, builder.bindings, - [...builder.context, ...captured.identifiers], + [...builder.context, ...capturedContext], builder.parentFunction, ); let loweredFunc: HIRFunction; @@ -3405,7 +3404,6 @@ function lowerFunction( loweredFunc = lowering.unwrap(); return { func: loweredFunc, - dependencies: captured.refs, }; } @@ -4079,14 +4077,6 @@ function lowerAssignment( } } -function isValidDependency(path: NodePath): boolean { - const parent: NodePath = path.parentPath; - return ( - !path.node.computed && - !(parent.isCallExpression() && parent.get('callee') === path) - ); -} - function captureScopes({from, to}: {from: Scope; to: Scope}): Set { let scopes: Set = new Set(); while (from) { @@ -4101,8 +4091,7 @@ function captureScopes({from, to}: {from: Scope; to: Scope}): Set { return scopes; } -function gatherCapturedDeps( - builder: HIRBuilder, +function gatherCapturedContext( fn: NodePath< | t.FunctionExpression | t.ArrowFunctionExpression @@ -4110,10 +4099,8 @@ function gatherCapturedDeps( | t.ObjectMethod >, componentScope: Scope, -): {identifiers: Array; refs: Array} { - const capturedIds: Map = new Map(); - const capturedRefs: Set = new Set(); - const seenPaths: Set = new Set(); +): Array { + const capturedIds = new Set(); /* * Capture all the scopes from the parent of this function up to and including @@ -4124,33 +4111,11 @@ function gatherCapturedDeps( to: componentScope, }); - function addCapturedId(bindingIdentifier: t.Identifier): number { - if (!capturedIds.has(bindingIdentifier)) { - const index = capturedIds.size; - capturedIds.set(bindingIdentifier, index); - return index; - } else { - return capturedIds.get(bindingIdentifier)!; - } - } - function handleMaybeDependency( - path: - | NodePath - | NodePath - | NodePath, + path: NodePath | NodePath, ): void { // Base context variable to depend on let baseIdentifier: NodePath | NodePath; - /* - * Base expression to depend on, which (for now) may contain non side-effectful - * member expressions - */ - let dependency: - | NodePath - | NodePath - | NodePath - | NodePath; if (path.isJSXOpeningElement()) { const name = path.get('name'); if (!(name.isJSXMemberExpression() || name.isJSXIdentifier())) { @@ -4166,115 +4131,20 @@ function gatherCapturedDeps( 'Invalid logic in gatherCapturedDeps', ); baseIdentifier = current; - - /* - * Get the expression to depend on, which may involve PropertyLoads - * for member expressions - */ - let currentDep: - | NodePath - | NodePath - | NodePath = baseIdentifier; - - while (true) { - const nextDep: null | NodePath = currentDep.parentPath; - if (nextDep && nextDep.isJSXMemberExpression()) { - currentDep = nextDep; - } else { - break; - } - } - dependency = currentDep; - } else if (path.isMemberExpression()) { - // Calculate baseIdentifier - let currentId: NodePath = path; - while (currentId.isMemberExpression()) { - currentId = currentId.get('object'); - } - if (!currentId.isIdentifier()) { - return; - } - baseIdentifier = currentId; - - /* - * Get the expression to depend on, which may involve PropertyLoads - * for member expressions - */ - let currentDep: - | NodePath - | NodePath - | NodePath = baseIdentifier; - - while (true) { - const nextDep: null | NodePath = currentDep.parentPath; - if ( - nextDep && - nextDep.isMemberExpression() && - isValidDependency(nextDep) - ) { - currentDep = nextDep; - } else { - break; - } - } - - dependency = currentDep; } else { baseIdentifier = path; - dependency = path; } /* * Skip dependency path, as we already tried to recursively add it (+ all subexpressions) * as a dependency. */ - dependency.skip(); + path.skip(); // Add the base identifier binding as a dependency. const binding = baseIdentifier.scope.getBinding(baseIdentifier.node.name); - if (binding === undefined || !pureScopes.has(binding.scope)) { - return; - } - const idKey = String(addCapturedId(binding.identifier)); - - // Add the expression (potentially a memberexpr path) as a dependency. - let exprKey = idKey; - if (dependency.isMemberExpression()) { - let pathTokens = []; - let current: NodePath = dependency; - while (current.isMemberExpression()) { - const property = current.get('property') as NodePath; - pathTokens.push(property.node.name); - current = current.get('object'); - } - - exprKey += '.' + pathTokens.reverse().join('.'); - } else if (dependency.isJSXMemberExpression()) { - let pathTokens = []; - let current: NodePath = - dependency; - while (current.isJSXMemberExpression()) { - const property = current.get('property'); - pathTokens.push(property.node.name); - current = current.get('object'); - } - } - - if (!seenPaths.has(exprKey)) { - let loweredDep: Place; - if (dependency.isJSXIdentifier()) { - loweredDep = lowerValueToTemporary(builder, { - kind: 'LoadLocal', - place: lowerIdentifier(builder, dependency), - loc: path.node.loc ?? GeneratedSource, - }); - } else if (dependency.isJSXMemberExpression()) { - loweredDep = lowerJsxMemberExpression(builder, dependency); - } else { - loweredDep = lowerExpressionToTemporary(builder, dependency); - } - capturedRefs.add(loweredDep); - seenPaths.add(exprKey); + if (binding !== undefined && pureScopes.has(binding.scope)) { + capturedIds.add(binding.identifier); } } @@ -4305,13 +4175,13 @@ function gatherCapturedDeps( return; } else if (path.isJSXElement()) { handleMaybeDependency(path.get('openingElement')); - } else if (path.isMemberExpression() || path.isIdentifier()) { + } else if (path.isIdentifier()) { handleMaybeDependency(path); } }, }); - return {identifiers: [...capturedIds.keys()], refs: [...capturedRefs]}; + return [...capturedIds.keys()]; } function notNull(value: T | null): value is T { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts index d3c919a6d8afe..a422570fffead 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts @@ -131,15 +131,7 @@ function collectHoistablePropertyLoadsImpl( fn: HIRFunction, context: CollectHoistablePropertyLoadsContext, ): ReadonlyMap { - const functionExpressionLoads = collectFunctionExpressionFakeLoads(fn); - const actuallyEvaluatedTemporaries = new Map( - [...context.temporaries].filter(([id]) => !functionExpressionLoads.has(id)), - ); - - const nodes = collectNonNullsInBlocks(fn, { - ...context, - temporaries: actuallyEvaluatedTemporaries, - }); + const nodes = collectNonNullsInBlocks(fn, context); propagateNonNull(fn, nodes, context.registry); if (DEBUG_PRINT) { @@ -598,30 +590,3 @@ function reduceMaybeOptionalChains( } } while (changed); } - -function collectFunctionExpressionFakeLoads( - fn: HIRFunction, -): Set { - const sources = new Map(); - const functionExpressionReferences = new Set(); - - for (const [_, block] of fn.body.blocks) { - for (const {lvalue, value} of block.instructions) { - if ( - value.kind === 'FunctionExpression' || - value.kind === 'ObjectMethod' - ) { - for (const reference of value.loweredFunc.dependencies) { - let curr: IdentifierId | undefined = reference.identifier.id; - while (curr != null) { - functionExpressionReferences.add(curr); - curr = sources.get(curr); - } - } - } else if (value.kind === 'PropertyLoad') { - sources.set(lvalue.identifier.id, value.object.identifier.id); - } - } - } - return functionExpressionReferences; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 113af2025dd2b..8de9218a662e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -245,8 +245,6 @@ const EnvironmentConfigSchema = z.object({ */ enableUseTypeAnnotations: z.boolean().default(false), - enableFunctionDependencyRewrite: z.boolean().default(true), - /** * Enables inference of optional dependency chains. Without this flag * a property chain such as `props?.items?.foo` will infer as a dep on diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index a6a9cbcd48ec5..4df6da6c1704c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -722,7 +722,6 @@ export type ObjectProperty = { }; export type LoweredFunction = { - dependencies: Array; func: HIRFunction; }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeOverlappingReactiveScopesHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeOverlappingReactiveScopesHIR.ts index a3740539b295b..5b286e917d0d3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeOverlappingReactiveScopesHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/MergeOverlappingReactiveScopesHIR.ts @@ -148,6 +148,14 @@ function collectScopeInfo(fn: HIRFunction): ScopeInfo { const scope = place.identifier.scope; if (scope != null) { placeScopes.set(place, scope); + /** + * Record both mutating and non-mutating scopes to merge scopes with + * still-mutating values with inner scopes that alias those values + * (see `nonmutating-capture-in-unsplittable-memo-block`) + * + * Note that this isn't perfect, as it also leads to merging of mutating + * scopes with JSX single-instruction scopes (see `mutation-within-jsx`) + */ if (scope.range.start !== scope.range.end) { getOrInsertDefault(scopeStarts, scope.range.start, new Set()).add( scope, @@ -254,7 +262,7 @@ function visitPlace( * of the stack to the mutated outer scope. */ const placeScope = getPlaceScope(id, place); - if (placeScope != null && isMutable({id} as any, place)) { + if (placeScope != null && isMutable({id}, place)) { const placeScopeIdx = activeScopes.indexOf(placeScope); if (placeScopeIdx !== -1 && placeScopeIdx !== activeScopes.length - 1) { joined.union([placeScope, ...activeScopes.slice(placeScopeIdx + 1)]); @@ -275,6 +283,13 @@ function getOverlappingReactiveScopes( for (const instr of block.instructions) { visitInstructionId(instr.id, context, state); for (const place of eachInstructionOperand(instr)) { + if ( + (instr.value.kind === 'FunctionExpression' || + instr.value.kind === 'ObjectMethod') && + place.identifier.type.kind === 'Primitive' + ) { + continue; + } visitPlace(instr.id, place, state); } for (const place of eachInstructionLValue(instr)) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts index a6f6c606e118d..7a6586730f9c1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts @@ -538,9 +538,6 @@ export function printInstructionValue(instrValue: ReactiveValue): string { .split('\n') .map(line => ` ${line}`) .join('\n'); - const deps = instrValue.loweredFunc.dependencies - .map(dep => printPlace(dep)) - .join(','); const context = instrValue.loweredFunc.func.context .map(dep => printPlace(dep)) .join(','); @@ -557,7 +554,7 @@ export function printInstructionValue(instrValue: ReactiveValue): string { }) .join(', ') ?? ''; const type = printType(instrValue.loweredFunc.func.returnType).trim(); - value = `${kind} ${name} @deps[${deps}] @context[${context}] @effects[${effects}]${type !== '' ? ` return${type}` : ''}:\n${fn}`; + value = `${kind} ${name} @context[${context}] @effects[${effects}]${type !== '' ? ` return${type}` : ''}:\n${fn}`; break; } case 'TaggedTemplateExpression': { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts index 08856e9143139..4cb84870a8a33 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts @@ -738,9 +738,8 @@ function collectDependencies( } for (const instr of block.instructions) { if ( - fn.env.config.enableFunctionDependencyRewrite && - (instr.value.kind === 'FunctionExpression' || - instr.value.kind === 'ObjectMethod') + instr.value.kind === 'FunctionExpression' || + instr.value.kind === 'ObjectMethod' ) { context.declare(instr.lvalue.identifier, { id: instr.id, diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts index c9ee803bfaffd..49ff3c256e016 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts @@ -193,7 +193,7 @@ export function* eachInstructionValueOperand( } case 'ObjectMethod': case 'FunctionExpression': { - yield* instrValue.loweredFunc.dependencies; + yield* instrValue.loweredFunc.func.context; break; } case 'TaggedTemplateExpression': { @@ -517,8 +517,9 @@ export function mapInstructionValueOperands( } case 'ObjectMethod': case 'FunctionExpression': { - instrValue.loweredFunc.dependencies = - instrValue.loweredFunc.dependencies.map(d => fn(d)); + instrValue.loweredFunc.func.context = + instrValue.loweredFunc.func.context.map(d => fn(d)); + break; } case 'TaggedTemplateExpression': { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts index 75bd8f6811ffb..5381262874369 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts @@ -10,9 +10,8 @@ import { Effect, HIRFunction, Identifier, - IdentifierName, + IdentifierId, LoweredFunction, - Place, isRefOrRefValue, makeInstructionId, } from '../HIR'; @@ -23,78 +22,39 @@ import {inferMutableContextVariables} from './InferMutableContextVariables'; import {inferMutableRanges} from './InferMutableRanges'; import inferReferenceEffects from './InferReferenceEffects'; -type Dependency = { - identifier: Identifier; - path: Array; -}; - // Helper class to track indirections such as LoadLocal and PropertyLoad. export class IdentifierState { - properties: Map = new Map(); + properties: Map = new Map(); resolve(identifier: Identifier): Identifier { - const resolved = this.properties.get(identifier); + const resolved = this.properties.get(identifier.id); if (resolved !== undefined) { - return resolved.identifier; + return resolved; } return identifier; } - declareProperty(lvalue: Place, object: Place, property: string): void { - const objectDependency = this.properties.get(object.identifier); - let nextDependency: Dependency; - if (objectDependency === undefined) { - nextDependency = {identifier: object.identifier, path: [property]}; - } else { - nextDependency = { - identifier: objectDependency.identifier, - path: [...objectDependency.path, property], - }; - } - this.properties.set(lvalue.identifier, nextDependency); - } - - declareTemporary(lvalue: Place, value: Place): void { - const resolved: Dependency = this.properties.get(value.identifier) ?? { - identifier: value.identifier, - path: [], - }; - this.properties.set(lvalue.identifier, resolved); + alias(lvalue: Identifier, value: Identifier): void { + this.properties.set(lvalue.id, this.properties.get(value.id) ?? value); } } export default function analyseFunctions(func: HIRFunction): void { - const state = new IdentifierState(); - for (const [_, block] of func.body.blocks) { for (const instr of block.instructions) { switch (instr.value.kind) { case 'ObjectMethod': case 'FunctionExpression': { lower(instr.value.loweredFunc.func); - infer(instr.value.loweredFunc, state, func.context); - break; - } - case 'PropertyLoad': { - state.declareProperty( - instr.lvalue, - instr.value.object, - instr.value.property, - ); - break; - } - case 'ComputedLoad': { - /* - * The path is set to an empty string as the path doesn't really - * matter for a computed load. + infer(instr.value.loweredFunc); + + /** + * Reset mutable range for outer inferReferenceEffects */ - state.declareProperty(instr.lvalue, instr.value.object, ''); - break; - } - case 'LoadLocal': - case 'LoadContext': { - if (instr.lvalue.identifier.name === null) { - state.declareTemporary(instr.lvalue, instr.value.place); + for (const operand of instr.value.loweredFunc.func.context) { + operand.identifier.mutableRange.start = makeInstructionId(0); + operand.identifier.mutableRange.end = makeInstructionId(0); + operand.identifier.scope = null; } break; } @@ -110,7 +70,6 @@ function lower(func: HIRFunction): void { inferMutableRanges(func); rewriteInstructionKindsBasedOnReassignment(func); inferReactiveScopeVariables(func); - inferMutableContextVariables(func); func.env.logger?.debugLogIRs?.({ kind: 'hir', name: 'AnalyseFunction (inner)', @@ -118,32 +77,16 @@ function lower(func: HIRFunction): void { }); } -function infer( - loweredFunc: LoweredFunction, - state: IdentifierState, - context: Array, -): void { - const mutations = new Map(); +function infer(loweredFunc: LoweredFunction): void { + const knownMutated = inferMutableContextVariables(loweredFunc.func); for (const operand of loweredFunc.func.context) { - if ( - isMutatedOrReassigned(operand.identifier) && - operand.identifier.name !== null - ) { - mutations.set(operand.identifier.name.value, operand.effect); - } - } - - for (const dep of loweredFunc.dependencies) { - let name: IdentifierName | null = null; - - if (state.properties.has(dep.identifier)) { - const receiver = state.properties.get(dep.identifier)!; - name = receiver.identifier.name; - } else { - name = dep.identifier.name; - } - - if (isRefOrRefValue(dep.identifier)) { + const identifier = operand.identifier; + CompilerError.invariant(operand.effect === Effect.Unknown, { + reason: + '[AnalyseFunctions] Expected Function context effects to not have been set', + loc: operand.loc, + }); + if (isRefOrRefValue(identifier)) { /* * TODO: this is a hack to ensure we treat functions which reference refs * as having a capture and therefore being considered mutable. this ensures @@ -151,43 +94,16 @@ function infer( * could be called, and allows us to help ensure it isn't called during * render */ - dep.effect = Effect.Capture; - } else if (name !== null) { - const effect = mutations.get(name.value); - if (effect !== undefined) { - dep.effect = effect === Effect.Unknown ? Effect.Capture : effect; - } - } - } - - /* - * This could potentially add duplicate deps to mutatedDeps in the case of - * mutating a context ref in the child function and in this parent function. - * It might be useful to dedupe this. - * - * In practice this never really matters because the Component function has no - * context refs, so it will never have duplicate deps. - */ - for (const place of context) { - CompilerError.invariant(place.identifier.name !== null, { - reason: 'context refs should always have a name', - description: null, - loc: place.loc, - suggestions: null, - }); - - const effect = mutations.get(place.identifier.name.value); - if (effect !== undefined) { - place.effect = effect === Effect.Unknown ? Effect.Capture : effect; - loweredFunc.dependencies.push(place); + operand.effect = Effect.Capture; + } else if (knownMutated.has(operand)) { + operand.effect = Effect.Mutate; + } else if (isMutatedOrReassigned(identifier)) { + // Note that this also reflects if identifier is ConditionallyMutated + operand.effect = Effect.Capture; + } else { + operand.effect = Effect.Read; } } - - for (const operand of loweredFunc.func.context) { - operand.identifier.mutableRange.start = makeInstructionId(0); - operand.identifier.mutableRange.end = makeInstructionId(0); - operand.identifier.scope = null; - } } function isMutatedOrReassigned(id: Identifier): boolean { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts index 67babf43db22f..0025472721542 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts @@ -55,32 +55,21 @@ import {IdentifierState} from './AnalyseFunctions'; * fn(); * ``` */ -export function inferMutableContextVariables(fn: HIRFunction): void { +export function inferMutableContextVariables(fn: HIRFunction): Set { const state = new IdentifierState(); const knownMutatedIdentifiers = new Set(); for (const [, block] of fn.body.blocks) { for (const instr of block.instructions) { switch (instr.value.kind) { - case 'PropertyLoad': { - state.declareProperty( - instr.lvalue, - instr.value.object, - instr.value.property, - ); - break; - } + case 'PropertyLoad': case 'ComputedLoad': { - /* - * The path is set to an empty string as the path doesn't really - * matter for a computed load. - */ - state.declareProperty(instr.lvalue, instr.value.object, ''); + state.alias(instr.lvalue.identifier, instr.value.object.identifier); break; } case 'LoadLocal': case 'LoadContext': { if (instr.lvalue.identifier.name === null) { - state.declareTemporary(instr.lvalue, instr.value.place); + state.alias(instr.lvalue.identifier, instr.value.place.identifier); } break; } @@ -95,11 +84,13 @@ export function inferMutableContextVariables(fn: HIRFunction): void { visitOperand(state, knownMutatedIdentifiers, operand); } } + const results = new Set(); for (const operand of fn.context) { if (knownMutatedIdentifiers.has(operand.identifier)) { - operand.effect = Effect.Mutate; + results.add(operand); } } + return results; } function visitOperand( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 0afe479df08dd..6420e74a2fd20 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -390,29 +390,31 @@ class InferenceState { freezeValues(values: Set, reason: Set): void { for (const value of values) { + if (value.kind === 'DeclareContext') { + /** + * Avoid freezing hoisted context declarations + * function Component() { + * const cb = useBar(() => foo(2)); // produces a hoisted context declaration + * const foo = useFoo(); // reassigns to the context variable + * return ; + * } + */ + continue; + } this.#values.set(value, { kind: ValueKind.Frozen, reason, context: new Set(), }); - if (value.kind === 'FunctionExpression') { - if ( - this.#env.config.enablePreserveExistingMemoizationGuarantees || - this.#env.config.enableTransitivelyFreezeFunctionExpressions - ) { - if (value.kind === 'FunctionExpression') { - /* - * We want to freeze the captured values, not mark the operands - * themselves as frozen. There could be mutations that occur - * before the freeze we are processing, and it would be invalid - * to overwrite those mutations as a freeze. - */ - for (const operand of eachInstructionValueOperand(value)) { - const operandValues = this.#variables.get(operand.identifier.id); - if (operandValues !== undefined) { - this.freezeValues(operandValues, reason); - } - } + if ( + value.kind === 'FunctionExpression' && + (this.#env.config.enablePreserveExistingMemoizationGuarantees || + this.#env.config.enableTransitivelyFreezeFunctionExpressions) + ) { + for (const operand of value.loweredFunc.func.context) { + const operandValues = this.#variables.get(operand.identifier.id); + if (operandValues !== undefined) { + this.freezeValues(operandValues, reason); } } } @@ -1143,17 +1145,17 @@ function inferBlock( case 'ObjectMethod': case 'FunctionExpression': { let hasMutableOperand = false; - const mutableOperands: Array = []; for (const operand of eachInstructionOperand(instr)) { + CompilerError.invariant(operand.effect !== Effect.Unknown, { + reason: 'Expected fn effects to be populated', + loc: operand.loc, + }); state.referenceAndRecordEffects( freezeActions, operand, - operand.effect === Effect.Unknown ? Effect.Read : operand.effect, + operand.effect, ValueReason.Other, ); - if (isMutableEffect(operand.effect, operand.loc)) { - mutableOperands.push(operand); - } hasMutableOperand ||= isMutableEffect(operand.effect, operand.loc); } /* diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts index e27b8f952148a..5b700b23b4049 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts @@ -270,7 +270,6 @@ function emitSelectorFn(env: Environment, keys: Array): Instruction { name: null, loweredFunc: { func: fn, - dependencies: [], }, type: 'ArrowFunctionExpression', loc: GeneratedSource, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts index 7a1473be40c87..0e6d1fd59201f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts @@ -24,7 +24,6 @@ export function outlineFunctions( } if ( value.kind === 'FunctionExpression' && - value.loweredFunc.dependencies.length === 0 && value.loweredFunc.func.context.length === 0 && // TODO: handle outlining named functions value.loweredFunc.func.id === null && diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts index 1108422f070d7..1f104d8592fab 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -379,6 +379,14 @@ export function findDisjointMutableValues( */ operand.identifier.mutableRange.start > 0 ) { + if ( + instr.value.kind === 'FunctionExpression' || + instr.value.kind === 'ObjectMethod' + ) { + if (operand.identifier.type.kind === 'Primitive') { + continue; + } + } operands.push(operand.identifier); } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/SSA/EliminateRedundantPhi.ts b/compiler/packages/babel-plugin-react-compiler/src/SSA/EliminateRedundantPhi.ts index bae038f9bd9df..12c8c0e2e6221 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/SSA/EliminateRedundantPhi.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/SSA/EliminateRedundantPhi.ts @@ -13,6 +13,8 @@ import { eachTerminalOperand, } from '../HIR/visitors'; +const DEBUG = false; + /* * Pass to eliminate redundant phi nodes: * - all operands are the same identifier, ie `x2 = phi(x1, x1, x1)`. @@ -141,6 +143,23 @@ export function eliminateRedundantPhi( * have already propagated forwards since we visit in reverse postorder. */ } while (rewrites.size > size && hasBackEdge); + + if (DEBUG) { + for (const [, block] of ir.blocks) { + for (const phi of block.phis) { + CompilerError.invariant(!rewrites.has(phi.place.identifier), { + reason: '[EliminateRedundantPhis]: rewrite not complete', + loc: phi.place.loc, + }); + for (const [, operand] of phi.operands) { + CompilerError.invariant(!rewrites.has(operand.identifier), { + reason: '[EliminateRedundantPhis]: rewrite not complete', + loc: phi.place.loc, + }); + } + } + } + } } function rewritePlace( diff --git a/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts b/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts index caba0d3c36992..820f7388dc41b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/SSA/EnterSSA.ts @@ -301,9 +301,6 @@ function enterSSAImpl( entry.preds.add(blockId); builder.defineFunction(loweredFunc); builder.enter(() => { - loweredFunc.context = loweredFunc.context.map(p => - builder.getPlace(p), - ); loweredFunc.params = loweredFunc.params.map(param => { if (param.kind === 'Identifier') { return builder.definePlace(param); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts b/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts index a35c4ddb0182c..a480b5d7c78de 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts @@ -319,51 +319,6 @@ function visitFunctionExpressionAndPropagateFireDependencies( replaceFireFunctions(fnExpr.loweredFunc.func, context), ); - /* - * Make a mapping from each dependency to the corresponding LoadLocal for it so that - * we can replace the loaded place with the generated fire function binding - */ - const loadLocalsToDepLoads = new Map(); - for (const dep of fnExpr.loweredFunc.dependencies) { - const loadLocal = context.getLoadLocalInstr(dep.identifier.id); - if (loadLocal != null) { - loadLocalsToDepLoads.set(loadLocal.place.identifier.id, loadLocal); - } - } - - const replacedCallees = new Map(); - for (const [ - calleeIdentifierId, - loadedFireFunctionBindingPlace, - ] of calleesCapturedByFnExpression.entries()) { - /* - * Given the ids of captured fire callees, look at the deps for loads of those identifiers - * and replace them with the new fire function binding - */ - const loadLocal = loadLocalsToDepLoads.get(calleeIdentifierId); - if (loadLocal == null) { - context.pushError({ - loc: fnExpr.loc, - description: null, - severity: ErrorSeverity.Invariant, - reason: - '[InsertFire] No loadLocal found for fire call argument for lambda', - suggestions: null, - }); - continue; - } - - const oldPlaceId = loadLocal.place.identifier.id; - loadLocal.place = { - ...loadedFireFunctionBindingPlace.fireFunctionBinding, - }; - - replacedCallees.set( - oldPlaceId, - loadedFireFunctionBindingPlace.fireFunctionBinding, - ); - } - // For each replaced callee, update the context of the function expression to track it for ( let contextIdx = 0; @@ -371,9 +326,13 @@ function visitFunctionExpressionAndPropagateFireDependencies( contextIdx++ ) { const contextItem = fnExpr.loweredFunc.func.context[contextIdx]; - const replacedCallee = replacedCallees.get(contextItem.identifier.id); + const replacedCallee = calleesCapturedByFnExpression.get( + contextItem.identifier.id, + ); if (replacedCallee != null) { - fnExpr.loweredFunc.func.context[contextIdx] = replacedCallee; + fnExpr.loweredFunc.func.context[contextIdx] = { + ...replacedCallee.fireFunctionBinding, + }; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.expect.md new file mode 100644 index 0000000000000..b30ee7e0d6817 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.expect.md @@ -0,0 +1,120 @@ + +## Input + +```javascript +// @enableTransitivelyFreezeFunctionExpressions:false +import { + Stringify, + mutate, + identity, + setPropertyByKey, + shallowCopy, +} from 'shared-runtime'; +/** + * Function expression version of `aliased-nested-scope-truncated-dep`. + * + * In this fixture, the output would be invalid if propagateScopeDeps did not + * avoid adding MemberExpression dependencies which would other evaluate during + * the mutable ranges of their base objects. + * This is different from `aliased-nested-scope-truncated-dep` which *does* + * produce correct output regardless of MemberExpression dependency truncation. + * + * Note while other expressions evaluate inline, function expressions *always* + * represent deferred evaluation. This means that + * (1) it's always safe to reorder function expression creation until its + * earliest potential invocation + * (2) it's invalid to eagerly evaluate function expression dependencies during + * their respective mutable ranges. + */ + +function Component({prop}) { + let obj = shallowCopy(prop); + + const aliasedObj = identity(obj); + + // When `obj` is mutable (either directly or through aliases), taking a + // dependency on `obj.id` is invalid as it may change before getId() is invoked + const getId = () => obj.id; + + mutate(aliasedObj); + setPropertyByKey(aliasedObj, 'id', prop.id + 1); + + // Calling getId() should return prop.id + 1, not the prev + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{prop: {id: 1}}], + sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions:false +import { + Stringify, + mutate, + identity, + setPropertyByKey, + shallowCopy, +} from "shared-runtime"; +/** + * Function expression version of `aliased-nested-scope-truncated-dep`. + * + * In this fixture, the output would be invalid if propagateScopeDeps did not + * avoid adding MemberExpression dependencies which would other evaluate during + * the mutable ranges of their base objects. + * This is different from `aliased-nested-scope-truncated-dep` which *does* + * produce correct output regardless of MemberExpression dependency truncation. + * + * Note while other expressions evaluate inline, function expressions *always* + * represent deferred evaluation. This means that + * (1) it's always safe to reorder function expression creation until its + * earliest potential invocation + * (2) it's invalid to eagerly evaluate function expression dependencies during + * their respective mutable ranges. + */ + +function Component(t0) { + const $ = _c(2); + const { prop } = t0; + let t1; + if ($[0] !== prop) { + const obj = shallowCopy(prop); + + const aliasedObj = identity(obj); + + const getId = () => obj.id; + + mutate(aliasedObj); + setPropertyByKey(aliasedObj, "id", prop.id + 1); + + t1 = ; + $[0] = prop; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ prop: { id: 1 } }], + sequentialRenders: [ + { prop: { id: 1 } }, + { prop: { id: 1 } }, + { prop: { id: 2 } }, + ], +}; + +``` + +### Eval output +(kind: ok)
{"getId":{"kind":"Function","result":2},"shouldInvokeFns":true}
+
{"getId":{"kind":"Function","result":2},"shouldInvokeFns":true}
+
{"getId":{"kind":"Function","result":3},"shouldInvokeFns":true}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.tsx new file mode 100644 index 0000000000000..40022c6f651fd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-fn-expr.tsx @@ -0,0 +1,46 @@ +// @enableTransitivelyFreezeFunctionExpressions:false +import { + Stringify, + mutate, + identity, + setPropertyByKey, + shallowCopy, +} from 'shared-runtime'; +/** + * Function expression version of `aliased-nested-scope-truncated-dep`. + * + * In this fixture, the output would be invalid if propagateScopeDeps did not + * avoid adding MemberExpression dependencies which would other evaluate during + * the mutable ranges of their base objects. + * This is different from `aliased-nested-scope-truncated-dep` which *does* + * produce correct output regardless of MemberExpression dependency truncation. + * + * Note while other expressions evaluate inline, function expressions *always* + * represent deferred evaluation. This means that + * (1) it's always safe to reorder function expression creation until its + * earliest potential invocation + * (2) it's invalid to eagerly evaluate function expression dependencies during + * their respective mutable ranges. + */ + +function Component({prop}) { + let obj = shallowCopy(prop); + + const aliasedObj = identity(obj); + + // When `obj` is mutable (either directly or through aliases), taking a + // dependency on `obj.id` is invalid as it may change before getId() is invoked + const getId = () => obj.id; + + mutate(aliasedObj); + setPropertyByKey(aliasedObj, 'id', prop.id + 1); + + // Calling getId() should return prop.id + 1, not the prev + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{prop: {id: 1}}], + sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md new file mode 100644 index 0000000000000..933fafff5f1ba --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md @@ -0,0 +1,221 @@ + +## Input + +```javascript +import { + Stringify, + mutate, + identity, + shallowCopy, + setPropertyByKey, +} from 'shared-runtime'; + +/** + * This fixture is similar to `bug-aliased-capture-aliased-mutate` and + * `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on + * dependency extraction. + * + * NOTE: this fixture is currently valid, but will break with optimizations: + * - Scope and mutable-range based reordering may move the array creation + * *after* the `mutate(aliasedObj)` call. This is invalid if mutate + * reassigns inner properties. + * - RecycleInto or other deeper-equality optimizations may produce invalid + * output -- it may compare the array's contents / dependencies too early. + * - Runtime validation for immutable values will break if `mutate` does + * interior mutation of the value captured into the array. + * + * Before scope block creation, HIR looks like this: + * // + * // $1 is unscoped as obj's mutable range will be + * // extended in a later pass + * // + * $1 = LoadLocal obj@0[0:12] + * $2 = PropertyLoad $1.id + * // + * // $3 gets assigned a scope as Array is an allocating + * // instruction, but this does *not* get extended or + * // merged into the later mutation site. + * // (explained in `bug-aliased-capture-aliased-mutate`) + * // + * $3@1 = Array[$2] + * ... + * $10@0 = LoadLocal shallowCopy@0[0, 12] + * $11 = LoadGlobal mutate + * $12 = $11($10@0[0, 12]) + * + * When filling in scope dependencies, we find that it's incorrect to depend on + * PropertyLoads from obj as it hasn't completed its mutable range. Following + * the immutable / mutable-new typing system, we check the identity of obj to + * detect whether it was newly created (and thus mutable) in this render pass. + * + * HIR with scopes looks like this. + * bb0: + * $1 = LoadLocal obj@0[0:12] + * $2 = PropertyLoad $1.id + * scopeTerminal deps=[obj@0] block=bb1 fallt=bb2 + * bb1: + * $3@1 = Array[$2] + * goto bb2 + * bb2: + * ... + * + * This is surprising as deps now is entirely decoupled from temporaries used + * by the block itself. scope @1's instructions now reference a value (1) + * produced outside its scope range and (2) not represented in its dependencies + * + * The right thing to do is to ensure that all Loads from a value get assigned + * the value's reactive scope. This also requires track mutating and aliasing + * separately from scope range. In this example, that would correctly merge + * the scopes of $3 with obj. + * Runtime validation and optimizations such as ReactiveGraph-based reordering + * require this as well. + * + * A tempting fix is to instead extend $3's ReactiveScope range up to include + * $2 (the PropertyLoad). This fixes dependency deduping but not reordering + * and mutability. + */ +function Component({prop}) { + let obj = shallowCopy(prop); + const aliasedObj = identity(obj); + + // [obj.id] currently is assigned its own reactive scope + const id = [obj.id]; + + // Writing to the alias may reassign to previously captured references. + // The compiler currently produces valid output, but this breaks with + // reordering, recycleInto, and other potential optimizations. + mutate(aliasedObj); + setPropertyByKey(aliasedObj, 'id', prop.id + 1); + + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{prop: {id: 1}}], + sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { + Stringify, + mutate, + identity, + shallowCopy, + setPropertyByKey, +} from "shared-runtime"; + +/** + * This fixture is similar to `bug-aliased-capture-aliased-mutate` and + * `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on + * dependency extraction. + * + * NOTE: this fixture is currently valid, but will break with optimizations: + * - Scope and mutable-range based reordering may move the array creation + * *after* the `mutate(aliasedObj)` call. This is invalid if mutate + * reassigns inner properties. + * - RecycleInto or other deeper-equality optimizations may produce invalid + * output -- it may compare the array's contents / dependencies too early. + * - Runtime validation for immutable values will break if `mutate` does + * interior mutation of the value captured into the array. + * + * Before scope block creation, HIR looks like this: + * // + * // $1 is unscoped as obj's mutable range will be + * // extended in a later pass + * // + * $1 = LoadLocal obj@0[0:12] + * $2 = PropertyLoad $1.id + * // + * // $3 gets assigned a scope as Array is an allocating + * // instruction, but this does *not* get extended or + * // merged into the later mutation site. + * // (explained in `bug-aliased-capture-aliased-mutate`) + * // + * $3@1 = Array[$2] + * ... + * $10@0 = LoadLocal shallowCopy@0[0, 12] + * $11 = LoadGlobal mutate + * $12 = $11($10@0[0, 12]) + * + * When filling in scope dependencies, we find that it's incorrect to depend on + * PropertyLoads from obj as it hasn't completed its mutable range. Following + * the immutable / mutable-new typing system, we check the identity of obj to + * detect whether it was newly created (and thus mutable) in this render pass. + * + * HIR with scopes looks like this. + * bb0: + * $1 = LoadLocal obj@0[0:12] + * $2 = PropertyLoad $1.id + * scopeTerminal deps=[obj@0] block=bb1 fallt=bb2 + * bb1: + * $3@1 = Array[$2] + * goto bb2 + * bb2: + * ... + * + * This is surprising as deps now is entirely decoupled from temporaries used + * by the block itself. scope @1's instructions now reference a value (1) + * produced outside its scope range and (2) not represented in its dependencies + * + * The right thing to do is to ensure that all Loads from a value get assigned + * the value's reactive scope. This also requires track mutating and aliasing + * separately from scope range. In this example, that would correctly merge + * the scopes of $3 with obj. + * Runtime validation and optimizations such as ReactiveGraph-based reordering + * require this as well. + * + * A tempting fix is to instead extend $3's ReactiveScope range up to include + * $2 (the PropertyLoad). This fixes dependency deduping but not reordering + * and mutability. + */ +function Component(t0) { + const $ = _c(4); + const { prop } = t0; + let t1; + if ($[0] !== prop) { + const obj = shallowCopy(prop); + const aliasedObj = identity(obj); + let t2; + if ($[2] !== obj) { + t2 = [obj.id]; + $[2] = obj; + $[3] = t2; + } else { + t2 = $[3]; + } + const id = t2; + + mutate(aliasedObj); + setPropertyByKey(aliasedObj, "id", prop.id + 1); + + t1 = ; + $[0] = prop; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ prop: { id: 1 } }], + sequentialRenders: [ + { prop: { id: 1 } }, + { prop: { id: 1 } }, + { prop: { id: 2 } }, + ], +}; + +``` + +### Eval output +(kind: ok)
{"id":[1]}
+
{"id":[1]}
+
{"id":[2]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.tsx new file mode 100644 index 0000000000000..4d9d7e78fb309 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.tsx @@ -0,0 +1,93 @@ +import { + Stringify, + mutate, + identity, + shallowCopy, + setPropertyByKey, +} from 'shared-runtime'; + +/** + * This fixture is similar to `bug-aliased-capture-aliased-mutate` and + * `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on + * dependency extraction. + * + * NOTE: this fixture is currently valid, but will break with optimizations: + * - Scope and mutable-range based reordering may move the array creation + * *after* the `mutate(aliasedObj)` call. This is invalid if mutate + * reassigns inner properties. + * - RecycleInto or other deeper-equality optimizations may produce invalid + * output -- it may compare the array's contents / dependencies too early. + * - Runtime validation for immutable values will break if `mutate` does + * interior mutation of the value captured into the array. + * + * Before scope block creation, HIR looks like this: + * // + * // $1 is unscoped as obj's mutable range will be + * // extended in a later pass + * // + * $1 = LoadLocal obj@0[0:12] + * $2 = PropertyLoad $1.id + * // + * // $3 gets assigned a scope as Array is an allocating + * // instruction, but this does *not* get extended or + * // merged into the later mutation site. + * // (explained in `bug-aliased-capture-aliased-mutate`) + * // + * $3@1 = Array[$2] + * ... + * $10@0 = LoadLocal shallowCopy@0[0, 12] + * $11 = LoadGlobal mutate + * $12 = $11($10@0[0, 12]) + * + * When filling in scope dependencies, we find that it's incorrect to depend on + * PropertyLoads from obj as it hasn't completed its mutable range. Following + * the immutable / mutable-new typing system, we check the identity of obj to + * detect whether it was newly created (and thus mutable) in this render pass. + * + * HIR with scopes looks like this. + * bb0: + * $1 = LoadLocal obj@0[0:12] + * $2 = PropertyLoad $1.id + * scopeTerminal deps=[obj@0] block=bb1 fallt=bb2 + * bb1: + * $3@1 = Array[$2] + * goto bb2 + * bb2: + * ... + * + * This is surprising as deps now is entirely decoupled from temporaries used + * by the block itself. scope @1's instructions now reference a value (1) + * produced outside its scope range and (2) not represented in its dependencies + * + * The right thing to do is to ensure that all Loads from a value get assigned + * the value's reactive scope. This also requires track mutating and aliasing + * separately from scope range. In this example, that would correctly merge + * the scopes of $3 with obj. + * Runtime validation and optimizations such as ReactiveGraph-based reordering + * require this as well. + * + * A tempting fix is to instead extend $3's ReactiveScope range up to include + * $2 (the PropertyLoad). This fixes dependency deduping but not reordering + * and mutability. + */ +function Component({prop}) { + let obj = shallowCopy(prop); + const aliasedObj = identity(obj); + + // [obj.id] currently is assigned its own reactive scope + const id = [obj.id]; + + // Writing to the alias may reassign to previously captured references. + // The compiler currently produces valid output, but this breaks with + // reordering, recycleInto, and other potential optimizations. + mutate(aliasedObj); + setPropertyByKey(aliasedObj, 'id', prop.id + 1); + + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{prop: {id: 1}}], + sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-unused-callback-nested.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-unused-callback-nested.expect.md index 37a510b8c290a..3584faf699f86 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-unused-callback-nested.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-unused-callback-nested.expect.md @@ -44,48 +44,44 @@ import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRen import { useEffect, useRef, useState } from "react"; function Component() { - const $ = _c(6); + const $ = _c(5); const ref = useRef(null); const [state, setState] = useState(false); let t0; - let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => {}; - - t1 = []; + t0 = []; $[0] = t0; - $[1] = t1; } else { t0 = $[0]; - t1 = $[1]; } - useEffect(t0, t1); + useEffect(_temp, t0); + let t1; let t2; - let t3; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = () => { + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { setState(true); }; - t3 = []; + t2 = []; + $[1] = t1; $[2] = t2; - $[3] = t3; } else { + t1 = $[1]; t2 = $[2]; - t3 = $[3]; } - useEffect(t2, t3); + useEffect(t1, t2); - const t4 = String(state); - let t5; - if ($[4] !== t4) { - t5 = ; + const t3 = String(state); + let t4; + if ($[3] !== t3) { + t4 = ; + $[3] = t3; $[4] = t4; - $[5] = t5; } else { - t5 = $[5]; + t4 = $[4]; } - return t5; + return t4; } +function _temp() {} function Child(t0) { const { ref } = t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md index c071d5d20ed99..6836544c5d337 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-mutate-2.expect.md @@ -27,7 +27,6 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; function component(a, b) { const $ = _c(2); - const y = { b }; let z; if ($[0] !== a) { z = { a }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-no-mutate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-no-mutate.expect.md index aa32b3260ef50..14bf94e770a6b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-no-mutate.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-no-mutate.expect.md @@ -31,12 +31,20 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(t0) { - const $ = _c(3); + const $ = _c(5); const { a, b } = t0; let z; if ($[0] !== a || $[1] !== b) { z = { a }; - const y = { b }; + let t1; + if ($[3] !== b) { + t1 = { b }; + $[3] = b; + $[4] = t1; + } else { + t1 = $[4]; + } + const y = t1; const x = function () { z.a = 2; return Math.max(y.b, 0); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-simple-alias-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-simple-alias-iife.expect.md index 1b91bc1a11275..a071dddba6b4c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-simple-alias-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-func-simple-alias-iife.expect.md @@ -34,7 +34,6 @@ function component(a) { const x = { a }; y = {}; - y; y = x; mutate(y); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md index f4721a507f31f..2afc5fd25dbac 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md @@ -31,7 +31,6 @@ function bar(a) { const x = [a]; y = {}; - y; y = x[0][1]; $[0] = a; $[1] = y; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md index 5c0be290a6930..3e57b7dc7c255 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md @@ -37,8 +37,6 @@ function bar(a, b) { let t; t = {}; - y; - t; y = x[0][1]; t = x[1][0]; $[0] = a; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md index 34b927d91e5f7..22728aaf4323d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md @@ -31,7 +31,6 @@ function bar(a) { const x = [a]; y = {}; - y; y = x[0].a[1]; $[0] = a; $[1] = y; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md index 0978be54acb46..60f829cdc4d66 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md @@ -30,7 +30,6 @@ function bar(a) { const x = [a]; y = {}; - y; y = x[0]; $[0] = a; $[1] = y; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-reference-changes-type.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-reference-changes-type.expect.md index 1bdc1c09a3483..299aa5a31dff2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-reference-changes-type.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-reference-changes-type.expect.md @@ -25,7 +25,6 @@ function component(a) { const x = { a }; y = 1; - y; y = x; mutate(y); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-inline-iife-reassign.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-inline-iife-reassign.expect.md index d17c934b3b42c..cf85967682607 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-inline-iife-reassign.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-inline-iife-reassign.expect.md @@ -38,9 +38,8 @@ function useTest() { const t1 = (w = 42); const t2 = w; - - w; let t3; + w = 999; t3 = 2; t0 = makeArray(t1, t2, t3); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-global.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-global.expect.md index e42ea8ce933b8..04b6c4f17f41a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-global.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-global.expect.md @@ -19,10 +19,10 @@ function foo() { import { c as _c } from "react/compiler-runtime"; function foo() { const $ = _c(1); + + const getJSX = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const getJSX = () => ; - t0 = getJSX(); $[0] = t0; } else { @@ -31,6 +31,9 @@ function foo() { const result = t0; return result; } +function _temp() { + return ; +} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-primitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-primitive.expect.md index 6686c0b5301bf..60fe0808d922a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-primitive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-primitive.expect.md @@ -23,13 +23,14 @@ export const FIXTURE_ENTRYPOINT = { ```javascript function foo() { - const f = () => { - console.log(42); - }; + const f = _temp; f(); return 42; } +function _temp() { + console.log(42); +} export const FIXTURE_ENTRYPOINT = { fn: foo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-into-function-expressions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-into-function-expressions.expect.md index 8ea2190480003..8822eddcdb69f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-into-function-expressions.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-into-function-expressions.expect.md @@ -18,12 +18,10 @@ function Component(props) { import { c as _c } from "react/compiler-runtime"; function Component(props) { const $ = _c(1); + + const onEvent = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const onEvent = () => { - console.log(42); - }; - t0 = ; $[0] = t0; } else { @@ -31,6 +29,9 @@ function Component(props) { } return t0; } +function _temp() { + console.log(42); +} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/context-variable-as-jsx-element-tag.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/context-variable-as-jsx-element-tag.expect.md index 3dc0dba27c364..da3bb94ed5ed4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/context-variable-as-jsx-element-tag.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/context-variable-as-jsx-element-tag.expect.md @@ -34,9 +34,8 @@ function Component(props) { let Component; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { Component = Stringify; - - Component; let t0; + t0 = Component; Component = t0; $[0] = Component; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md index 2045ee7901e96..1ba0d59e17265 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hoisting-simple-function-declaration.expect.md @@ -24,13 +24,17 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` + 4 | } 5 | return baz(); // OK: FuncDecls are HoistableDeclarations that have both declaration and value hoisting - 6 | function baz() { +> 6 | function baz() { + | ^^^^^^^^^^^^^^^^ > 7 | return bar(); - | ^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (7:7) - 8 | } + | ^^^^^^^^^^^^^^^^^ +> 8 | } + | ^^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (6:8) 9 | } 10 | + 11 | export const FIXTURE_ENTRYPOINT = { ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md new file mode 100644 index 0000000000000..7babe57b000e2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @enableTransitivelyFreezeFunctionExpressions +import {setPropertyByKey, Stringify, useIdentity} from 'shared-runtime'; + +function Foo({count}) { + const x = {value: 0}; + /** + * After this custom hook call, it's no longer valid to mutate x. + */ + const cb = useIdentity(() => { + setPropertyByKey(x, 'value', count); + }); + + x.value += count; + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{count: 1}], +}; + +``` + + +## Error + +``` + 11 | }); + 12 | +> 13 | x.value += count; + | ^ InvalidReact: This mutates a variable that React considers immutable (13:13) + 14 | return ; + 15 | } + 16 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.tsx new file mode 100644 index 0000000000000..b71626a435b78 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-identifier.tsx @@ -0,0 +1,20 @@ +// @enableTransitivelyFreezeFunctionExpressions +import {setPropertyByKey, Stringify, useIdentity} from 'shared-runtime'; + +function Foo({count}) { + const x = {value: 0}; + /** + * After this custom hook call, it's no longer valid to mutate x. + */ + const cb = useIdentity(() => { + setPropertyByKey(x, 'value', count); + }); + + x.value += count; + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{count: 1}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md new file mode 100644 index 0000000000000..fcc47ddc2b14f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @enableTransitivelyFreezeFunctionExpressions +import {mutate, Stringify, useIdentity} from 'shared-runtime'; + +function Foo({count}) { + const x = {value: 0}; + /** + * After this custom hook call, it's no longer valid to mutate x. + */ + const cb = useIdentity(() => { + x.value++; + }); + + x.value += count; + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{count: 1}], +}; + +``` + + +## Error + +``` + 11 | }); + 12 | +> 13 | x.value += count; + | ^ InvalidReact: This mutates a variable that React considers immutable (13:13) + 14 | return ; + 15 | } + 16 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.jsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.jsx new file mode 100644 index 0000000000000..2a94559c1f026 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.hook-call-freezes-captured-memberexpr.jsx @@ -0,0 +1,20 @@ +// @enableTransitivelyFreezeFunctionExpressions +import {mutate, Stringify, useIdentity} from 'shared-runtime'; + +function Foo({count}) { + const x = {value: 0}; + /** + * After this custom hook call, it's no longer valid to mutate x. + */ + const cb = useIdentity(() => { + x.value++; + }); + + x.value += count; + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{count: 1}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-named-function-with-shadowed-local-same-name.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-named-function-with-shadowed-local-same-name.expect.md index db3a192eaf604..f66b970f00dd4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-named-function-with-shadowed-local-same-name.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-named-function-with-shadowed-local-same-name.expect.md @@ -22,7 +22,7 @@ function Component(props) { 7 | return hasErrors; 8 | } > 9 | return hasErrors(); - | ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$16 (9:9) + | ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$14 (9:9) 10 | } 11 | ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md new file mode 100644 index 0000000000000..55cab1e9f3bd1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md @@ -0,0 +1,81 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +function useFoo() { + const onClick = response => { + setState(DISABLED_FORM); + }; + + const [state, setState] = useState(); + const handleLogout = useCallback(() => { + setState(DISABLED_FORM); + }, [setState]); + const getComponent = () => { + return handleLogout()} />; + }; + + // this `getComponent` call should not be inferred as mutating setState + return [getComponent(), onClick]; // pass onClick to avoid dce +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees +function useFoo() { + const $ = _c(9); + const onClick = (response) => { + setState(DISABLED_FORM); + }; + + const [, t0] = useState(); + const setState = t0; + let t1; + if ($[0] !== setState) { + t1 = () => { + setState(DISABLED_FORM); + }; + $[0] = setState; + $[1] = t1; + } else { + t1 = $[1]; + } + setState; + const handleLogout = t1; + let t2; + if ($[2] !== handleLogout) { + t2 = () => handleLogout()} />; + $[2] = handleLogout; + $[3] = t2; + } else { + t2 = $[3]; + } + const getComponent = t2; + let t3; + if ($[4] !== getComponent) { + t3 = getComponent(); + $[4] = getComponent; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== onClick || $[7] !== t3) { + t4 = [t3, onClick]; + $[6] = onClick; + $[7] = t3; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.js new file mode 100644 index 0000000000000..4a44679390a80 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.js @@ -0,0 +1,17 @@ +// @validatePreserveExistingMemoizationGuarantees +function useFoo() { + const onClick = response => { + setState(DISABLED_FORM); + }; + + const [state, setState] = useState(); + const handleLogout = useCallback(() => { + setState(DISABLED_FORM); + }, [setState]); + const getComponent = () => { + return handleLogout()} />; + }; + + // this `getComponent` call should not be inferred as mutating setState + return [getComponent(), onClick]; // pass onClick to avoid dce +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md new file mode 100644 index 0000000000000..483d9b1a8e2da --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md @@ -0,0 +1,77 @@ + +## Input + +```javascript +import {useEffect, useState} from 'react'; +import {Stringify} from 'shared-runtime'; + +function Foo() { + /** + * Previously, this lowered to + * $1 = LoadContext capture setState + * $2 = FunctionExpression deps=$1 context=setState + * [[ at this point, we freeze the `LoadContext setState` instruction, but it will never be referenced again ]] + * + * Now, this function expression directly references `setState`, which freezes + * the source `DeclareContext HoistedConst setState`. Freezing source identifiers + * (instead of the one level removed `LoadContext`) is more semantically correct + * for everything *other* than hoisted context declarations. + * + * $2 = Function context=setState + */ + useEffect(() => setState(2), []); + + const [state, setState] = useState(0); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useEffect, useState } from "react"; +import { Stringify } from "shared-runtime"; + +function Foo() { + const $ = _c(3); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = []; + $[0] = t0; + } else { + t0 = $[0]; + } + useEffect(() => setState(2), t0); + + const [state, t1] = useState(0); + const setState = t1; + let t2; + if ($[1] !== state) { + t2 = ; + $[1] = state; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +### Eval output +(kind: ok)
{"state":2}
+
{"state":2}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.js new file mode 100644 index 0000000000000..7b26c8d086491 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.js @@ -0,0 +1,28 @@ +import {useEffect, useState} from 'react'; +import {Stringify} from 'shared-runtime'; + +function Foo() { + /** + * Previously, this lowered to + * $1 = LoadContext capture setState + * $2 = FunctionExpression deps=$1 context=setState + * [[ at this point, we freeze the `LoadContext setState` instruction, but it will never be referenced again ]] + * + * Now, this function expression directly references `setState`, which freezes + * the source `DeclareContext HoistedConst setState`. Freezing source identifiers + * (instead of the one level removed `LoadContext`) is more semantically correct + * for everything *other* than hoisted context declarations. + * + * $2 = Function context=setState + */ + useEffect(() => setState(2), []); + + const [state, setState] = useState(0); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{}], + sequentialRenders: [{}, {}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.expect.md new file mode 100644 index 0000000000000..957919516d09d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.expect.md @@ -0,0 +1,87 @@ + +## Input + +```javascript +import {useIdentity, Stringify, identity} from 'shared-runtime'; + +function Foo({val1}) { + // `x={inner: val1}` should be able to be memoized + const x = {inner: val1}; + + // Any references to `x` after this hook call should be read-only + const cb = useIdentity(() => x.inner); + + // With enableTransitivelyFreezeFunctionExpressions, it's invalid + // to write to `x` after it's been frozen. + // TODO: runtime validation for DX + const copy = identity(x); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{val1: 1}], + sequentialRenders: [{val1: 1}, {val1: 1}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, Stringify, identity } from "shared-runtime"; + +function Foo(t0) { + const $ = _c(9); + const { val1 } = t0; + let t1; + if ($[0] !== val1) { + t1 = { inner: val1 }; + $[0] = val1; + $[1] = t1; + } else { + t1 = $[1]; + } + const x = t1; + let t2; + if ($[2] !== x.inner) { + t2 = () => x.inner; + $[2] = x.inner; + $[3] = t2; + } else { + t2 = $[3]; + } + const cb = useIdentity(t2); + let t3; + if ($[4] !== x) { + t3 = identity(x); + $[4] = x; + $[5] = t3; + } else { + t3 = $[5]; + } + const copy = t3; + let t4; + if ($[6] !== cb || $[7] !== copy) { + t4 = ; + $[6] = cb; + $[7] = copy; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ val1: 1 }], + sequentialRenders: [{ val1: 1 }, { val1: 1 }], +}; + +``` + +### Eval output +(kind: ok)
{"copy":{"inner":1},"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}
+
{"copy":{"inner":1},"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.tsx new file mode 100644 index 0000000000000..68e8e034379e1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hook-call-freezes-captured-memberexpr.tsx @@ -0,0 +1,21 @@ +import {useIdentity, Stringify, identity} from 'shared-runtime'; + +function Foo({val1}) { + // `x={inner: val1}` should be able to be memoized + const x = {inner: val1}; + + // Any references to `x` after this hook call should be read-only + const cb = useIdentity(() => x.inner); + + // With enableTransitivelyFreezeFunctionExpressions, it's invalid + // to write to `x` after it's been frozen. + // TODO: runtime validation for DX + const copy = identity(x); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{val1: 1}], + sequentialRenders: [{val1: 1}, {val1: 1}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md index 74e01a72d52ba..a7d27bc38193f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-local-tag-in-lambda.expect.md @@ -25,10 +25,10 @@ import { c as _c } from "react/compiler-runtime"; import { Stringify } from "shared-runtime"; function useFoo() { const $ = _c(1); + + const callback = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const callback = () => ; - t0 = callback(); $[0] = t0; } else { @@ -36,6 +36,9 @@ function useFoo() { } return t0; } +function _temp() { + return ; +} export const FIXTURE_ENTRYPOINT = { fn: useFoo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-memberexpr-tag-in-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-memberexpr-tag-in-lambda.expect.md index 22fa3b2e2a2e3..e5ead2479dd40 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-memberexpr-tag-in-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-memberexpr-tag-in-lambda.expect.md @@ -25,10 +25,10 @@ import { c as _c } from "react/compiler-runtime"; import * as SharedRuntime from "shared-runtime"; function useFoo() { const $ = _c(1); + + const callback = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const callback = () => ; - t0 = callback(); $[0] = t0; } else { @@ -36,6 +36,9 @@ function useFoo() { } return t0; } +function _temp() { + return ; +} export const FIXTURE_ENTRYPOINT = { fn: useFoo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-non-reactive-to-reactive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-non-reactive-to-reactive.expect.md index d34db46d6aa28..ed0ddda55b32f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-non-reactive-to-reactive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-non-reactive-to-reactive.expect.md @@ -26,7 +26,6 @@ function f(a) { const $ = _c(4); let x; if ($[0] !== a) { - x; x = { a }; $[0] = a; $[1] = x; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-ref-non-reactive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-ref-non-reactive.expect.md index 2aa5d4d06dfb6..8dc4839085ee5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-ref-non-reactive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lambda-mutated-ref-non-reactive.expect.md @@ -27,7 +27,6 @@ function f(a) { const $ = _c(2); let x; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x; x = {}; $[0] = x; } else { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-function-shadowed-identifiers.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-function-shadowed-identifiers.expect.md index 13ba6d17986bb..3c624de9ebe57 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-function-shadowed-identifiers.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/nested-function-shadowed-identifiers.expect.md @@ -31,7 +31,7 @@ function Component(props) { let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = (e) => { - setX((currentX) => currentX + null); + setX(_temp); }; $[0] = t0; } else { @@ -48,6 +48,9 @@ function Component(props) { } return t1; } +function _temp(currentX) { + return currentX + null; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md index e8a3e2d627c59..3fffec6a7dc20 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md @@ -35,7 +35,6 @@ function useFoo(arr1, arr2) { if ($[0] !== arr1 || $[1] !== arr2) { const x = [arr1]; - y; (y = x.concat(arr2)), y; $[0] = arr1; $[1] = arr2; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rewrite-phis-in-lambda-capture-context.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rewrite-phis-in-lambda-capture-context.expect.md index 2e451d8948988..0c66dee6a85b1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rewrite-phis-in-lambda-capture-context.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rewrite-phis-in-lambda-capture-context.expect.md @@ -22,26 +22,21 @@ function Component() { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; function Component() { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - while (bar()) { - if (baz) { - bar(); - } - } - return () => 4; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const get4 = t0; + const get4 = _temp2; return get4; } +function _temp2() { + while (bar()) { + if (baz) { + bar(); + } + } + return _temp; +} +function _temp() { + return 4; +} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-operator-conditional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-operator-conditional.expect.md index e335273026791..6ad460347fa50 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-operator-conditional.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-operator-conditional.expect.md @@ -85,7 +85,6 @@ function Inner(props) { input = use(FooContext); } - input; input; let t0; let t1; From a92acdb188990b2b64130d74ccd6437dc9db1901 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:37:21 -0700 Subject: [PATCH 038/300] [compiler] Remove redundant InferMutableContextVariables (#32097) This removes special casing for `PropertyStore` mutability inference within FunctionExpressions. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32097). * #32287 * #32104 * #32098 * __->__ #32097 --- .../src/Inference/AnalyseFunctions.ts | 27 +---- .../Inference/InferMutableContextVariables.ts | 105 ------------------ ...mutate-global-in-effect-fixpoint.expect.md | 66 ++++++----- .../allow-mutate-global-in-effect-fixpoint.js | 7 +- .../reanimated-shared-value-writes.expect.md | 23 ++-- 5 files changed, 54 insertions(+), 174 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts index 5381262874369..a439b4cd01232 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts @@ -10,7 +10,6 @@ import { Effect, HIRFunction, Identifier, - IdentifierId, LoweredFunction, isRefOrRefValue, makeInstructionId, @@ -18,27 +17,9 @@ import { import {deadCodeElimination} from '../Optimization'; import {inferReactiveScopeVariables} from '../ReactiveScopes'; import {rewriteInstructionKindsBasedOnReassignment} from '../SSA'; -import {inferMutableContextVariables} from './InferMutableContextVariables'; import {inferMutableRanges} from './InferMutableRanges'; import inferReferenceEffects from './InferReferenceEffects'; -// Helper class to track indirections such as LoadLocal and PropertyLoad. -export class IdentifierState { - properties: Map = new Map(); - - resolve(identifier: Identifier): Identifier { - const resolved = this.properties.get(identifier.id); - if (resolved !== undefined) { - return resolved; - } - return identifier; - } - - alias(lvalue: Identifier, value: Identifier): void { - this.properties.set(lvalue.id, this.properties.get(value.id) ?? value); - } -} - export default function analyseFunctions(func: HIRFunction): void { for (const [_, block] of func.body.blocks) { for (const instr of block.instructions) { @@ -78,7 +59,6 @@ function lower(func: HIRFunction): void { } function infer(loweredFunc: LoweredFunction): void { - const knownMutated = inferMutableContextVariables(loweredFunc.func); for (const operand of loweredFunc.func.context) { const identifier = operand.identifier; CompilerError.invariant(operand.effect === Effect.Unknown, { @@ -95,10 +75,11 @@ function infer(loweredFunc: LoweredFunction): void { * render */ operand.effect = Effect.Capture; - } else if (knownMutated.has(operand)) { - operand.effect = Effect.Mutate; } else if (isMutatedOrReassigned(identifier)) { - // Note that this also reflects if identifier is ConditionallyMutated + /** + * Reflects direct reassignments, PropertyStores, and ConditionallyMutate + * (directly or through maybe-aliases) + */ operand.effect = Effect.Capture; } else { operand.effect = Effect.Read; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts deleted file mode 100644 index 0025472721542..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableContextVariables.ts +++ /dev/null @@ -1,105 +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 {Effect, HIRFunction, Identifier, Place} from '../HIR'; -import { - eachInstructionValueOperand, - eachTerminalOperand, -} from '../HIR/visitors'; -import {IdentifierState} from './AnalyseFunctions'; - -/* - * This pass infers which of the given function's context (free) variables - * are definitively mutated by the function. This analysis is *partial*, - * and only annotates provable mutations, and may miss mutations via indirections. - * The intent of this pass is to drive validations, rejecting known-bad code - * while avoiding false negatives, and the inference should *not* be used to - * drive changes in output. - * - * Note that a complete analysis is possible but would have too many false negatives. - * The approach would be to run LeaveSSA and InferReactiveScopeVariables in order to - * find all possible aliases of a context variable which may be mutated. However, this - * can lead to false negatives: - * - * ``` - * const [x, setX] = useState(null); // x is frozen - * const fn = () => { // context=[x] - * const z = {}; // z is mutable - * foo(z, x); // potentially mutate z and x - * z.a = true; // definitively mutate z - * } - * fn(); - * ``` - * - * When we analyze function expressions we assume that context variables are mutable, - * so we assume that `x` is mutable. We infer that `foo(z, x)` could be mutating the - * two variables to alias each other, such that `z.a = true` could be mutating `x`, - * and we would infer that `x` is definitively mutated. Then when we run - * InferReferenceEffects on the outer code we'd reject it, since there is a definitive - * mutation of a frozen value. - * - * Thus the actual implementation looks at only basic aliasing. The above example would - * pass, since z does not directly alias `x`. However, mutations through trivial aliases - * are detected: - * - * ``` - * const [x, setX] = useState(null); // x is frozen - * const fn = () => { // context=[x] - * const z = x; - * z.a = true; // ERROR: mutates x - * } - * fn(); - * ``` - */ -export function inferMutableContextVariables(fn: HIRFunction): Set { - const state = new IdentifierState(); - const knownMutatedIdentifiers = new Set(); - for (const [, block] of fn.body.blocks) { - for (const instr of block.instructions) { - switch (instr.value.kind) { - case 'PropertyLoad': - case 'ComputedLoad': { - state.alias(instr.lvalue.identifier, instr.value.object.identifier); - break; - } - case 'LoadLocal': - case 'LoadContext': { - if (instr.lvalue.identifier.name === null) { - state.alias(instr.lvalue.identifier, instr.value.place.identifier); - } - break; - } - default: { - for (const operand of eachInstructionValueOperand(instr.value)) { - visitOperand(state, knownMutatedIdentifiers, operand); - } - } - } - } - for (const operand of eachTerminalOperand(block.terminal)) { - visitOperand(state, knownMutatedIdentifiers, operand); - } - } - const results = new Set(); - for (const operand of fn.context) { - if (knownMutatedIdentifiers.has(operand.identifier)) { - results.add(operand); - } - } - return results; -} - -function visitOperand( - state: IdentifierState, - knownMutatedIdentifiers: Set, - operand: Place, -): void { - const resolved = state.resolve(operand.identifier); - if (operand.effect === Effect.Mutate || operand.effect === Effect.Store) { - knownMutatedIdentifiers.add(resolved); - } -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.expect.md index 942daec1dd08c..76e4432fe90d9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.expect.md @@ -19,9 +19,14 @@ function Component() { // capture into a separate variable that is not a context variable. const y = x; + /** + * Note that this fixture currently produces a stale effect closure if `y = x + * = someGlobal` changes between renders. Under current compiler assumptions, + * that would be a rule of react violation. + */ useEffect(() => { y.value = 'hello'; - }, []); + }); useEffect(() => { setState(someGlobal.value); @@ -46,57 +51,50 @@ import { useEffect, useState } from "react"; let someGlobal = { value: null }; function Component() { - const $ = _c(7); + const $ = _c(5); const [state, setState] = useState(someGlobal); + + let x = someGlobal; + while (x == null) { + x = someGlobal; + } + + const y = x; let t0; - let t1; - let t2; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - let x = someGlobal; - while (x == null) { - x = someGlobal; - } - - const y = x; - t0 = useEffect; - t1 = () => { + t0 = () => { y.value = "hello"; }; - t2 = []; $[0] = t0; + } else { + t0 = $[0]; + } + useEffect(t0); + let t1; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + setState(someGlobal.value); + }; + t2 = [someGlobal]; $[1] = t1; $[2] = t2; } else { - t0 = $[0]; t1 = $[1]; t2 = $[2]; } - t0(t1, t2); - let t3; + useEffect(t1, t2); + + const t3 = String(state); let t4; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = () => { - setState(someGlobal.value); - }; - t4 = [someGlobal]; + if ($[3] !== t3) { + t4 =
{t3}
; $[3] = t3; $[4] = t4; } else { - t3 = $[3]; t4 = $[4]; } - useEffect(t3, t4); - - const t5 = String(state); - let t6; - if ($[5] !== t5) { - t6 =
{t5}
; - $[5] = t5; - $[6] = t6; - } else { - t6 = $[6]; - } - return t6; + return t4; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.js index 84bd42aaefd18..6e44adf204101 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutate-global-in-effect-fixpoint.js @@ -15,9 +15,14 @@ function Component() { // capture into a separate variable that is not a context variable. const y = x; + /** + * Note that this fixture currently produces a stale effect closure if `y = x + * = someGlobal` changes between renders. Under current compiler assumptions, + * that would be a rule of react violation. + */ useEffect(() => { y.value = 'hello'; - }, []); + }); useEffect(() => { setState(someGlobal.value); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reanimated-shared-value-writes.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reanimated-shared-value-writes.expect.md index 8f808c94b3043..0a19a85428939 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reanimated-shared-value-writes.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reanimated-shared-value-writes.expect.md @@ -36,21 +36,22 @@ import { useSharedValue } from "react-native-reanimated"; * of render */ function SomeComponent() { - const $ = _c(3); + const $ = _c(2); const sharedVal = useSharedValue(0); - - const T0 = Button; - const t0 = () => (sharedVal.value = Math.random()); - let t1; - if ($[0] !== T0 || $[1] !== t0) { - t1 = ; - $[0] = T0; + let t0; + if ($[0] !== sharedVal) { + t0 = ( +
diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index ddf051bcdb94f..50424e9d7b4db 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -3595,7 +3595,7 @@ function mountId(): string { const treeId = getTreeId(); // Use a captial R prefix for server-generated ids. - id = ':' + identifierPrefix + 'R' + treeId; + id = '\u00AB' + identifierPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end // that represents the position of this useId hook among all the useId @@ -3605,11 +3605,16 @@ function mountId(): string { id += 'H' + localId.toString(32); } - id += ':'; + id += '\u00BB'; } else { // Use a lowercase r prefix for client-generated ids. const globalClientId = globalClientIdCounter++; - id = ':' + identifierPrefix + 'r' + globalClientId.toString(32) + ':'; + id = + '\u00AB' + + identifierPrefix + + 'r' + + globalClientId.toString(32) + + '\u00BB'; } hook.memoizedState = id; From 403d4fb852384b820a8fe405413891d8c74bbf5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 25 Feb 2025 12:45:44 -0500 Subject: [PATCH 060/300] Move ViewTransitions helpers to ReactFiberCommitViewTransitions (#32462) This doesn't change anything. It just moves some functions. This moves the view transitions helper functions into its own file. This is similar to how I already moved ReactFiberCommitEffects and ReactFiberCommitHostEffects out of ReactFiberCommitWork. This makes it a bit easier to navigate and get an overview of ReactFiberCommitWork but another motivation is also so that I can refer to these helpers from [ReactFiberApplyGesture](https://github.com/facebook/react/pull/32451/files#diff-42297cf327dee8e01d83c85314b8965953b9674e7c4615ce6c430464dcc8550b). --- .../src/ReactFiberCommitViewTransitions.js | 831 ++++++++++++++++++ .../src/ReactFiberCommitWork.js | 819 +---------------- .../src/ReactFiberWorkLoop.js | 2 +- 3 files changed, 861 insertions(+), 791 deletions(-) create mode 100644 packages/react-reconciler/src/ReactFiberCommitViewTransitions.js diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js new file mode 100644 index 0000000000000..22b9d11ac67d2 --- /dev/null +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -0,0 +1,831 @@ +/** + * 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 type {Instance, InstanceMeasurement, Props} from './ReactFiberConfig'; +import type {Fiber} from './ReactInternalTypes'; +import type { + ViewTransitionProps, + ViewTransitionState, +} from './ReactFiberViewTransitionComponent'; + +import { + HostComponent, + OffscreenComponent, + ViewTransitionComponent, +} from './ReactWorkTags'; +import { + NoFlags, + Update, + ViewTransitionStatic, + AffectedParentLayout, + ViewTransitionNamedStatic, +} from './ReactFiberFlags'; +import { + supportsMutation, + applyViewTransitionName, + restoreViewTransitionName, + measureInstance, + hasInstanceChanged, + hasInstanceAffectedParent, + wasInstanceInViewport, +} from './ReactFiberConfig'; +import {scheduleViewTransitionEvent} from './ReactFiberWorkLoop'; +import { + getViewTransitionName, + getViewTransitionClassName, +} from './ReactFiberViewTransitionComponent'; + +export let shouldStartViewTransition: boolean = false; + +export function resetShouldStartViewTransition(): void { + shouldStartViewTransition = false; +} + +// This tracks named ViewTransition components found in the accumulateSuspenseyCommit +// phase that might need to find deleted pairs in the beforeMutation phase. +let appearingViewTransitions: Map | null = null; + +export function resetAppearingViewTransitions(): void { + appearingViewTransitions = null; +} + +export function trackAppearingViewTransition( + name: string, + state: ViewTransitionState, +): void { + if (appearingViewTransitions === null) { + appearingViewTransitions = new Map(); + } + appearingViewTransitions.set(name, state); +} + +// We can't cancel view transition children until we know that their parent also +// don't need to transition. +export let viewTransitionCancelableChildren: null | Array< + Instance | string | Props, +> = null; // tupled array where each entry is [instance: Instance, oldName: string, props: Props] + +export function setViewTransitionCancelableChildren( + children: null | Array, +): void { + viewTransitionCancelableChildren = children; +} + +let viewTransitionHostInstanceIdx = 0; + +function applyViewTransitionToHostInstances( + child: null | Fiber, + name: string, + className: ?string, + collectMeasurements: null | Array, + stopAtNestedViewTransitions: boolean, +): boolean { + if (!supportsMutation) { + return false; + } + let inViewport = false; + while (child !== null) { + if (child.tag === HostComponent) { + shouldStartViewTransition = true; + const instance: Instance = child.stateNode; + if (collectMeasurements !== null) { + const measurement = measureInstance(instance); + collectMeasurements.push(measurement); + if (wasInstanceInViewport(measurement)) { + inViewport = true; + } + } else if (!inViewport) { + if (wasInstanceInViewport(measureInstance(instance))) { + inViewport = true; + } + } + applyViewTransitionName( + instance, + viewTransitionHostInstanceIdx === 0 + ? name + : // If we have multiple Host Instances below, we add a suffix to the name to give + // each one a unique name. + name + '_' + viewTransitionHostInstanceIdx, + className, + ); + viewTransitionHostInstanceIdx++; + } else if ( + child.tag === OffscreenComponent && + child.memoizedState !== null + ) { + // Skip any hidden subtrees. They were or are effectively not there. + } else if ( + child.tag === ViewTransitionComponent && + stopAtNestedViewTransitions + ) { + // Skip any nested view transitions for updates since in that case the + // inner most one is the one that handles the update. + } else { + if ( + applyViewTransitionToHostInstances( + child.child, + name, + className, + collectMeasurements, + stopAtNestedViewTransitions, + ) + ) { + inViewport = true; + } + } + child = child.sibling; + } + return inViewport; +} + +function restoreViewTransitionOnHostInstances( + child: null | Fiber, + stopAtNestedViewTransitions: boolean, +): void { + if (!supportsMutation) { + return; + } + while (child !== null) { + if (child.tag === HostComponent) { + const instance: Instance = child.stateNode; + restoreViewTransitionName(instance, child.memoizedProps); + } else if ( + child.tag === OffscreenComponent && + child.memoizedState !== null + ) { + // Skip any hidden subtrees. They were or are effectively not there. + } else if ( + child.tag === ViewTransitionComponent && + stopAtNestedViewTransitions + ) { + // Skip any nested view transitions for updates since in that case the + // inner most one is the one that handles the update. + } else { + restoreViewTransitionOnHostInstances( + child.child, + stopAtNestedViewTransitions, + ); + } + child = child.sibling; + } +} + +function commitAppearingPairViewTransitions(placement: Fiber): void { + if ((placement.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { + // This has no named view transitions in its subtree. + return; + } + let child = placement.child; + while (child !== null) { + if (child.tag === OffscreenComponent && child.memoizedState === null) { + // This tree was already hidden so we skip it. + } else { + commitAppearingPairViewTransitions(child); + if ( + child.tag === ViewTransitionComponent && + (child.flags & ViewTransitionNamedStatic) !== NoFlags + ) { + const instance: ViewTransitionState = child.stateNode; + if (instance.paired) { + const props: ViewTransitionProps = child.memoizedProps; + if (props.name == null || props.name === 'auto') { + throw new Error( + 'Found a pair with an auto name. This is a bug in React.', + ); + } + const name = props.name; + const className: ?string = getViewTransitionClassName( + props.className, + props.share, + ); + if (className !== 'none') { + // We found a new appearing view transition with the same name as this deletion. + // We'll transition between them. + viewTransitionHostInstanceIdx = 0; + const inViewport = applyViewTransitionToHostInstances( + child.child, + name, + className, + null, + false, + ); + if (!inViewport) { + // This boundary is exiting within the viewport but is going to leave the viewport. + // Instead, we treat this as an exit of the previous entry by reverting the new name. + // Ideally we could undo the old transition but it's now too late. It's also on its + // on snapshot. We have know was for it to paint onto the original group. + // TODO: This will lead to things unexpectedly having exit animations that normally + // wouldn't happen. Consider if we should just let this fly off the screen instead. + restoreViewTransitionOnHostInstances(child.child, false); + } + } + } + } + } + child = child.sibling; + } +} + +export function commitEnterViewTransitions(placement: Fiber): void { + if (placement.tag === ViewTransitionComponent) { + const state: ViewTransitionState = placement.stateNode; + const props: ViewTransitionProps = placement.memoizedProps; + const name = getViewTransitionName(props, state); + const className: ?string = getViewTransitionClassName( + props.className, + state.paired ? props.share : props.enter, + ); + if (className !== 'none') { + viewTransitionHostInstanceIdx = 0; + const inViewport = applyViewTransitionToHostInstances( + placement.child, + name, + className, + null, + false, + ); + if (!inViewport) { + // TODO: If this was part of a pair we will still run the onShare callback. + // Revert the transition names. This boundary is not in the viewport + // so we won't bother animating it. + restoreViewTransitionOnHostInstances(placement.child, false); + // TODO: Should we still visit the children in case a named one was in the viewport? + } else { + commitAppearingPairViewTransitions(placement); + + if (!state.paired) { + scheduleViewTransitionEvent(placement, props.onEnter); + } + } + } else { + commitAppearingPairViewTransitions(placement); + } + } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + let child = placement.child; + while (child !== null) { + commitEnterViewTransitions(child); + child = child.sibling; + } + } else { + commitAppearingPairViewTransitions(placement); + } +} + +function commitDeletedPairViewTransitions(deletion: Fiber): void { + if ( + appearingViewTransitions === null || + appearingViewTransitions.size === 0 + ) { + // We've found all. + return; + } + const pairs = appearingViewTransitions; + if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { + // This has no named view transitions in its subtree. + return; + } + let child = deletion.child; + while (child !== null) { + if (child.tag === OffscreenComponent && child.memoizedState === null) { + // This tree was already hidden so we skip it. + } else { + if ( + child.tag === ViewTransitionComponent && + (child.flags & ViewTransitionNamedStatic) !== NoFlags + ) { + const props: ViewTransitionProps = child.memoizedProps; + const name = props.name; + if (name != null && name !== 'auto') { + const pair = pairs.get(name); + if (pair !== undefined) { + const className: ?string = getViewTransitionClassName( + props.className, + props.share, + ); + if (className !== 'none') { + // We found a new appearing view transition with the same name as this deletion. + viewTransitionHostInstanceIdx = 0; + const inViewport = applyViewTransitionToHostInstances( + child.child, + name, + className, + null, + false, + ); + if (!inViewport) { + // This boundary is not in the viewport so we won't treat it as a matched pair. + // Revert the transition names. This avoids it flying onto the screen which can + // be disruptive and doesn't really preserve any continuity anyway. + restoreViewTransitionOnHostInstances(child.child, false); + } else { + // We'll transition between them. + const oldinstance: ViewTransitionState = child.stateNode; + const newInstance: ViewTransitionState = pair; + newInstance.paired = oldinstance; + // Note: If the other side ends up outside the viewport, we'll still run this. + // Therefore it's possible for onShare to be called with only an old snapshot. + scheduleViewTransitionEvent(child, props.onShare); + } + } + // Delete the entry so that we know when we've found all of them + // and can stop searching (size reaches zero). + pairs.delete(name); + if (pairs.size === 0) { + break; + } + } + } + } + commitDeletedPairViewTransitions(child); + } + child = child.sibling; + } +} + +export function commitExitViewTransitions(deletion: Fiber): void { + if (deletion.tag === ViewTransitionComponent) { + const props: ViewTransitionProps = deletion.memoizedProps; + const name = getViewTransitionName(props, deletion.stateNode); + const pair = + appearingViewTransitions !== null + ? appearingViewTransitions.get(name) + : undefined; + const className: ?string = getViewTransitionClassName( + props.className, + pair !== undefined ? props.share : props.exit, + ); + if (className !== 'none') { + viewTransitionHostInstanceIdx = 0; + const inViewport = applyViewTransitionToHostInstances( + deletion.child, + name, + className, + null, + false, + ); + if (!inViewport) { + // Revert the transition names. This boundary is not in the viewport + // so we won't bother animating it. + restoreViewTransitionOnHostInstances(deletion.child, false); + // TODO: Should we still visit the children in case a named one was in the viewport? + } else if (pair !== undefined) { + // We found a new appearing view transition with the same name as this deletion. + // We'll transition between them instead of running the normal exit. + const oldinstance: ViewTransitionState = deletion.stateNode; + const newInstance: ViewTransitionState = pair; + newInstance.paired = oldinstance; + // Delete the entry so that we know when we've found all of them + // and can stop searching (size reaches zero). + // $FlowFixMe[incompatible-use]: Refined by the pair. + appearingViewTransitions.delete(name); + // Note: If the other side ends up outside the viewport, we'll still run this. + // Therefore it's possible for onShare to be called with only an old snapshot. + scheduleViewTransitionEvent(deletion, props.onShare); + } else { + scheduleViewTransitionEvent(deletion, props.onExit); + } + } + if (appearingViewTransitions !== null) { + // Look for more pairs deeper in the tree. + commitDeletedPairViewTransitions(deletion); + } + } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + let child = deletion.child; + while (child !== null) { + commitExitViewTransitions(child); + child = child.sibling; + } + } else { + if (appearingViewTransitions !== null) { + commitDeletedPairViewTransitions(deletion); + } + } +} + +export function commitBeforeUpdateViewTransition( + current: Fiber, + finishedWork: Fiber, +): void { + // The way we deal with multiple HostInstances as children of a View Transition in an + // update can get tricky. The important bit is that if you swap out n HostInstances + // from n HostInstances then they match up in order. Similarly, if you don't swap + // any HostInstances each instance just transitions as is. + // + // We call this function twice. First we apply the view transition names on the + // "current" tree in the snapshot phase. Then in the mutation phase we apply view + // transition names to the "finishedWork" tree. + // + // This means that if there were insertions or deletions before an updated Instance + // that same Instance might get different names in the "old" and the "new" state. + // For example if you swap two HostInstances inside a ViewTransition they don't + // animate to swap position but rather cross-fade into the other instance. This might + // be unexpected but it is in line with the semantics that the ViewTransition is its + // own layer that cross-fades its content when it updates. If you want to reorder then + // each child needs its own ViewTransition. + const oldProps: ViewTransitionProps = current.memoizedProps; + const oldName = getViewTransitionName(oldProps, current.stateNode); + const newProps: ViewTransitionProps = finishedWork.memoizedProps; + // This className applies only if there are fewer child DOM nodes than + // before or if this update should've been cancelled but we ended up with + // a parent animating so we need to animate the child too. + // For example, if update="foo" layout="none" and it turns out this was + // a layout only change, then the "foo" class will be applied even though + // it was not actually an update. Which is a bug. + let className: ?string = getViewTransitionClassName( + newProps.className, + newProps.update, + ); + if (className === 'none') { + className = getViewTransitionClassName(newProps.className, newProps.layout); + if (className === 'none') { + // If both update and layout are both "none" then we don't have to + // apply a name. Since we won't animate this boundary. + return; + } + } + viewTransitionHostInstanceIdx = 0; + applyViewTransitionToHostInstances( + current.child, + oldName, + className, + (current.memoizedState = []), + true, + ); +} + +export function commitNestedViewTransitions(changedParent: Fiber): void { + let child = changedParent.child; + while (child !== null) { + if (child.tag === ViewTransitionComponent) { + // In this case the outer ViewTransition component wins but if there + // was an update through this component then the inner one wins. + const props: ViewTransitionProps = child.memoizedProps; + const name = getViewTransitionName(props, child.stateNode); + const className: ?string = getViewTransitionClassName( + props.className, + props.layout, + ); + if (className !== 'none') { + viewTransitionHostInstanceIdx = 0; + applyViewTransitionToHostInstances( + child.child, + name, + className, + (child.memoizedState = []), + false, + ); + } + } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + commitNestedViewTransitions(child); + } + child = child.sibling; + } +} + +function restorePairedViewTransitions(parent: Fiber): void { + if ((parent.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { + // This has no named view transitions in its subtree. + return; + } + let child = parent.child; + while (child !== null) { + if (child.tag === OffscreenComponent && child.memoizedState === null) { + // This tree was already hidden so we skip it. + } else { + if ( + child.tag === ViewTransitionComponent && + (child.flags & ViewTransitionNamedStatic) !== NoFlags + ) { + const instance: ViewTransitionState = child.stateNode; + if (instance.paired !== null) { + instance.paired = null; + restoreViewTransitionOnHostInstances(child.child, false); + } + } + restorePairedViewTransitions(child); + } + child = child.sibling; + } +} + +export function restoreEnterViewTransitions(placement: Fiber): void { + if (placement.tag === ViewTransitionComponent) { + const instance: ViewTransitionState = placement.stateNode; + instance.paired = null; + restoreViewTransitionOnHostInstances(placement.child, false); + restorePairedViewTransitions(placement); + } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + let child = placement.child; + while (child !== null) { + restoreEnterViewTransitions(child); + child = child.sibling; + } + } else { + restorePairedViewTransitions(placement); + } +} + +export function restoreExitViewTransitions(deletion: Fiber): void { + if (deletion.tag === ViewTransitionComponent) { + const instance: ViewTransitionState = deletion.stateNode; + instance.paired = null; + restoreViewTransitionOnHostInstances(deletion.child, false); + restorePairedViewTransitions(deletion); + } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + let child = deletion.child; + while (child !== null) { + restoreExitViewTransitions(child); + child = child.sibling; + } + } else { + restorePairedViewTransitions(deletion); + } +} + +export function restoreUpdateViewTransition( + current: Fiber, + finishedWork: Fiber, +): void { + finishedWork.memoizedState = null; + restoreViewTransitionOnHostInstances(current.child, true); + restoreViewTransitionOnHostInstances(finishedWork.child, true); +} + +export function restoreNestedViewTransitions(changedParent: Fiber): void { + let child = changedParent.child; + while (child !== null) { + if (child.tag === ViewTransitionComponent) { + child.memoizedState = null; + restoreViewTransitionOnHostInstances(child.child, false); + } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + restoreNestedViewTransitions(child); + } + child = child.sibling; + } +} + +function cancelViewTransitionHostInstances( + currentViewTransition: Fiber, + child: null | Fiber, + stopAtNestedViewTransitions: boolean, +): void { + if (!supportsMutation) { + return; + } + while (child !== null) { + if (child.tag === HostComponent) { + const instance: Instance = child.stateNode; + const oldName = getViewTransitionName( + currentViewTransition.memoizedProps, + currentViewTransition.stateNode, + ); + if (viewTransitionCancelableChildren === null) { + viewTransitionCancelableChildren = []; + } + viewTransitionCancelableChildren.push( + instance, + oldName, + child.memoizedProps, + ); + viewTransitionHostInstanceIdx++; + } else if ( + child.tag === OffscreenComponent && + child.memoizedState !== null + ) { + // Skip any hidden subtrees. They were or are effectively not there. + } else if ( + child.tag === ViewTransitionComponent && + stopAtNestedViewTransitions + ) { + // Skip any nested view transitions for updates since in that case the + // inner most one is the one that handles the update. + } else { + cancelViewTransitionHostInstances( + currentViewTransition, + child.child, + stopAtNestedViewTransitions, + ); + } + child = child.sibling; + } +} + +function measureViewTransitionHostInstances( + currentViewTransition: Fiber, + parentViewTransition: Fiber, + child: null | Fiber, + name: string, + className: ?string, + previousMeasurements: null | Array, + stopAtNestedViewTransitions: boolean, +): boolean { + if (!supportsMutation) { + return true; + } + let inViewport = false; + while (child !== null) { + if (child.tag === HostComponent) { + const instance: Instance = child.stateNode; + if ( + previousMeasurements !== null && + viewTransitionHostInstanceIdx < previousMeasurements.length + ) { + // The previous measurement of the Instance in this location within the ViewTransition. + // Note that this might not be the same exact Instance if the Instances within the + // ViewTransition changed. + const previousMeasurement = + previousMeasurements[viewTransitionHostInstanceIdx]; + const nextMeasurement = measureInstance(instance); + if ( + wasInstanceInViewport(previousMeasurement) || + wasInstanceInViewport(nextMeasurement) + ) { + // If either the old or new state was within the viewport we have to animate this. + // But if it turns out that none of them were we'll be able to skip it. + inViewport = true; + } + if ( + (parentViewTransition.flags & Update) === NoFlags && + hasInstanceChanged(previousMeasurement, nextMeasurement) + ) { + parentViewTransition.flags |= Update; + } + if (hasInstanceAffectedParent(previousMeasurement, nextMeasurement)) { + // If this instance size within its parent has changed it might have caused the + // parent to relayout which needs a cross fade. + parentViewTransition.flags |= AffectedParentLayout; + } + } else { + // If there was an insertion of extra nodes, we have to assume they affected the parent. + // It should have already been marked as an Update due to the mutation. + parentViewTransition.flags |= AffectedParentLayout; + } + if ((parentViewTransition.flags & Update) !== NoFlags) { + // We might update this node so we need to apply its new name for the new state. + applyViewTransitionName( + instance, + viewTransitionHostInstanceIdx === 0 + ? name + : // If we have multiple Host Instances below, we add a suffix to the name to give + // each one a unique name. + name + '_' + viewTransitionHostInstanceIdx, + className, + ); + } + if (!inViewport || (parentViewTransition.flags & Update) === NoFlags) { + // It turns out that we had no other deeper mutations, the child transitions didn't + // affect the parent layout and this instance hasn't changed size. So we can skip + // animating it. However, in the current model this only works if the parent also + // doesn't animate. So we have to queue these and wait until we complete the parent + // to cancel them. + const oldName = getViewTransitionName( + currentViewTransition.memoizedProps, + currentViewTransition.stateNode, + ); + if (viewTransitionCancelableChildren === null) { + viewTransitionCancelableChildren = []; + } + viewTransitionCancelableChildren.push( + instance, + oldName, + child.memoizedProps, + ); + } + viewTransitionHostInstanceIdx++; + } else if ( + child.tag === OffscreenComponent && + child.memoizedState !== null + ) { + // Skip any hidden subtrees. They were or are effectively not there. + } else if ( + child.tag === ViewTransitionComponent && + stopAtNestedViewTransitions + ) { + // Skip any nested view transitions for updates since in that case the + // inner most one is the one that handles the update. + // If this inner boundary resized we need to bubble that information up. + parentViewTransition.flags |= child.flags & AffectedParentLayout; + } else { + if ( + measureViewTransitionHostInstances( + currentViewTransition, + parentViewTransition, + child.child, + name, + className, + previousMeasurements, + stopAtNestedViewTransitions, + ) + ) { + inViewport = true; + } + } + child = child.sibling; + } + return inViewport; +} + +export function measureUpdateViewTransition( + current: Fiber, + finishedWork: Fiber, +): boolean { + const props: ViewTransitionProps = finishedWork.memoizedProps; + const updateClassName: ?string = getViewTransitionClassName( + props.className, + props.update, + ); + const layoutClassName: ?string = getViewTransitionClassName( + props.className, + props.layout, + ); + let className: ?string; + if (updateClassName === 'none') { + if (layoutClassName === 'none') { + // If both update and layout class name were none, then we didn't apply any + // names in the before update phase so we shouldn't now neither. + return false; + } + // We don't care if this is mutated or children layout changed, but we still + // measure each instance to see if it moved and therefore should apply layout. + finishedWork.flags &= ~Update; + className = layoutClassName; + } else if ((finishedWork.flags & Update) !== NoFlags) { + // It was updated and we have an appropriate class name to apply. + className = updateClassName; + } else { + if (layoutClassName === 'none') { + // If we did not update, then all changes are considered a layout. We'll + // attempt to cancel. + viewTransitionHostInstanceIdx = 0; + cancelViewTransitionHostInstances(current, finishedWork.child, true); + return false; + } + // We didn't update but we might still apply layout so we measure each + // instance to see if it moved or resized. + className = layoutClassName; + } + const name = getViewTransitionName(props, finishedWork.stateNode); + // If nothing changed due to a mutation, or children changing size + // and the measurements end up unchanged, we should restore it to not animate. + viewTransitionHostInstanceIdx = 0; + const previousMeasurements = current.memoizedState; + const inViewport = measureViewTransitionHostInstances( + current, + finishedWork, + finishedWork.child, + name, + className, + previousMeasurements, + true, + ); + const previousCount = + previousMeasurements === null ? 0 : previousMeasurements.length; + if (viewTransitionHostInstanceIdx !== previousCount) { + // If we found a different number of child DOM nodes we need to assume that + // the parent layout may have changed as a result. This is not necessarily + // true if those nodes were absolutely positioned. + finishedWork.flags |= AffectedParentLayout; + } + return inViewport; +} + +export function measureNestedViewTransitions(changedParent: Fiber): void { + let child = changedParent.child; + while (child !== null) { + if (child.tag === ViewTransitionComponent) { + const current = child.alternate; + if (current !== null) { + const props: ViewTransitionProps = child.memoizedProps; + const name = getViewTransitionName(props, child.stateNode); + const className: ?string = getViewTransitionClassName( + props.className, + props.layout, + ); + viewTransitionHostInstanceIdx = 0; + const inViewport = measureViewTransitionHostInstances( + current, + child, + child.child, + name, + className, + child.memoizedState, + false, + ); + if ((child.flags & Update) === NoFlags || !inViewport) { + // Nothing changed. + } else { + scheduleViewTransitionEvent(child, props.onLayout); + } + } + } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + measureNestedViewTransitions(child); + } + child = child.sibling; + } +} diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 850f3c67067ff..f5d0987393046 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -14,7 +14,6 @@ import type { Container, HoistableRoot, FormInstance, - InstanceMeasurement, Props, } from './ReactFiberConfig'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; @@ -112,9 +111,7 @@ import { PerformedWork, ForceClientRender, DidCapture, - ViewTransitionStatic, AffectedParentLayout, - ViewTransitionNamedStatic, } from './ReactFiberFlags'; import { commitStartTime, @@ -163,15 +160,9 @@ import { suspendResource, resetFormInstance, registerSuspenseInstanceRetry, - applyViewTransitionName, - restoreViewTransitionName, cancelViewTransitionName, cancelRootViewTransitionName, restoreRootViewTransitionName, - measureInstance, - hasInstanceChanged, - hasInstanceAffectedParent, - wasInstanceInViewport, isSingletonScope, } from './ReactFiberConfig'; import { @@ -203,10 +194,6 @@ import { OffscreenDetached, OffscreenPassiveEffectsConnected, } from './ReactFiberActivityComponent'; -import { - getViewTransitionName, - getViewTransitionClassName, -} from './ReactFiberViewTransitionComponent'; import { TransitionRoot, TransitionTracingMarker, @@ -249,6 +236,23 @@ import { commitHostSingletonAcquisition, commitHostSingletonRelease, } from './ReactFiberCommitHostEffects'; +import { + commitEnterViewTransitions, + commitExitViewTransitions, + commitBeforeUpdateViewTransition, + commitNestedViewTransitions, + restoreEnterViewTransitions, + restoreExitViewTransitions, + restoreUpdateViewTransition, + restoreNestedViewTransitions, + measureUpdateViewTransition, + measureNestedViewTransitions, + resetShouldStartViewTransition, + resetAppearingViewTransitions, + trackAppearingViewTransition, + viewTransitionCancelableChildren, + setViewTransitionCancelableChildren, +} from './ReactFiberCommitViewTransitions'; import { viewTransitionMutationContext, pushMutationContext, @@ -274,19 +278,9 @@ let inProgressRoot: FiberRoot | null = null; let focusedInstanceHandle: null | Fiber = null; export let shouldFireAfterActiveInstanceBlur: boolean = false; -export let shouldStartViewTransition: boolean = false; - -// This tracks named ViewTransition components found in the accumulateSuspenseyCommit -// phase that might need to find deleted pairs in the beforeMutation phase. -let appearingViewTransitions: Map | null = null; - // Used during the commit phase to track whether a parent ViewTransition component // might have been affected by any mutations / relayouts below. let viewTransitionContextChanged: boolean = false; -// We can't cancel view transition children until we know that their parent also -// don't need to transition. -let viewTransitionCancelableChildren: null | Array = - null; // tupled array where each entry is [instance: Instance, oldName: string, props: Props] export function commitBeforeMutationEffects( root: FiberRoot, @@ -295,7 +289,8 @@ export function commitBeforeMutationEffects( ): void { focusedInstanceHandle = prepareForCommit(root.containerInfo); shouldFireAfterActiveInstanceBlur = false; - shouldStartViewTransition = false; + + resetShouldStartViewTransition(); const isViewTransitionEligible = enableViewTransition && @@ -307,7 +302,7 @@ export function commitBeforeMutationEffects( // We no longer need to track the active instance fiber focusedInstanceHandle = null; // We've found any matched pairs and can now reset. - appearingViewTransitions = null; + resetAppearingViewTransitions(); } function commitBeforeMutationEffects_begin(isViewTransitionEligible: boolean) { @@ -542,759 +537,6 @@ function commitBeforeMutationEffectsDeletion( } } -let viewTransitionHostInstanceIdx = 0; - -function applyViewTransitionToHostInstances( - child: null | Fiber, - name: string, - className: ?string, - collectMeasurements: null | Array, - stopAtNestedViewTransitions: boolean, -): boolean { - if (!supportsMutation) { - return false; - } - let inViewport = false; - while (child !== null) { - if (child.tag === HostComponent) { - shouldStartViewTransition = true; - const instance: Instance = child.stateNode; - if (collectMeasurements !== null) { - const measurement = measureInstance(instance); - collectMeasurements.push(measurement); - if (wasInstanceInViewport(measurement)) { - inViewport = true; - } - } else if (!inViewport) { - if (wasInstanceInViewport(measureInstance(instance))) { - inViewport = true; - } - } - applyViewTransitionName( - instance, - viewTransitionHostInstanceIdx === 0 - ? name - : // If we have multiple Host Instances below, we add a suffix to the name to give - // each one a unique name. - name + '_' + viewTransitionHostInstanceIdx, - className, - ); - viewTransitionHostInstanceIdx++; - } else if ( - child.tag === OffscreenComponent && - child.memoizedState !== null - ) { - // Skip any hidden subtrees. They were or are effectively not there. - } else if ( - child.tag === ViewTransitionComponent && - stopAtNestedViewTransitions - ) { - // Skip any nested view transitions for updates since in that case the - // inner most one is the one that handles the update. - } else { - if ( - applyViewTransitionToHostInstances( - child.child, - name, - className, - collectMeasurements, - stopAtNestedViewTransitions, - ) - ) { - inViewport = true; - } - } - child = child.sibling; - } - return inViewport; -} - -function restoreViewTransitionOnHostInstances( - child: null | Fiber, - stopAtNestedViewTransitions: boolean, -): void { - if (!supportsMutation) { - return; - } - while (child !== null) { - if (child.tag === HostComponent) { - const instance: Instance = child.stateNode; - restoreViewTransitionName(instance, child.memoizedProps); - } else if ( - child.tag === OffscreenComponent && - child.memoizedState !== null - ) { - // Skip any hidden subtrees. They were or are effectively not there. - } else if ( - child.tag === ViewTransitionComponent && - stopAtNestedViewTransitions - ) { - // Skip any nested view transitions for updates since in that case the - // inner most one is the one that handles the update. - } else { - restoreViewTransitionOnHostInstances( - child.child, - stopAtNestedViewTransitions, - ); - } - child = child.sibling; - } -} - -function commitAppearingPairViewTransitions(placement: Fiber): void { - if ((placement.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { - // This has no named view transitions in its subtree. - return; - } - let child = placement.child; - while (child !== null) { - if (child.tag === OffscreenComponent && child.memoizedState === null) { - // This tree was already hidden so we skip it. - } else { - commitAppearingPairViewTransitions(child); - if ( - child.tag === ViewTransitionComponent && - (child.flags & ViewTransitionNamedStatic) !== NoFlags - ) { - const instance: ViewTransitionState = child.stateNode; - if (instance.paired) { - const props: ViewTransitionProps = child.memoizedProps; - if (props.name == null || props.name === 'auto') { - throw new Error( - 'Found a pair with an auto name. This is a bug in React.', - ); - } - const name = props.name; - const className: ?string = getViewTransitionClassName( - props.className, - props.share, - ); - if (className !== 'none') { - // We found a new appearing view transition with the same name as this deletion. - // We'll transition between them. - viewTransitionHostInstanceIdx = 0; - const inViewport = applyViewTransitionToHostInstances( - child.child, - name, - className, - null, - false, - ); - if (!inViewport) { - // This boundary is exiting within the viewport but is going to leave the viewport. - // Instead, we treat this as an exit of the previous entry by reverting the new name. - // Ideally we could undo the old transition but it's now too late. It's also on its - // on snapshot. We have know was for it to paint onto the original group. - // TODO: This will lead to things unexpectedly having exit animations that normally - // wouldn't happen. Consider if we should just let this fly off the screen instead. - restoreViewTransitionOnHostInstances(child.child, false); - } - } - } - } - } - child = child.sibling; - } -} - -function commitEnterViewTransitions(placement: Fiber): void { - if (placement.tag === ViewTransitionComponent) { - const state: ViewTransitionState = placement.stateNode; - const props: ViewTransitionProps = placement.memoizedProps; - const name = getViewTransitionName(props, state); - const className: ?string = getViewTransitionClassName( - props.className, - state.paired ? props.share : props.enter, - ); - if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; - const inViewport = applyViewTransitionToHostInstances( - placement.child, - name, - className, - null, - false, - ); - if (!inViewport) { - // TODO: If this was part of a pair we will still run the onShare callback. - // Revert the transition names. This boundary is not in the viewport - // so we won't bother animating it. - restoreViewTransitionOnHostInstances(placement.child, false); - // TODO: Should we still visit the children in case a named one was in the viewport? - } else { - commitAppearingPairViewTransitions(placement); - - if (!state.paired) { - scheduleViewTransitionEvent(placement, props.onEnter); - } - } - } else { - commitAppearingPairViewTransitions(placement); - } - } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - let child = placement.child; - while (child !== null) { - commitEnterViewTransitions(child); - child = child.sibling; - } - } else { - commitAppearingPairViewTransitions(placement); - } -} - -function commitDeletedPairViewTransitions(deletion: Fiber): void { - if ( - appearingViewTransitions === null || - appearingViewTransitions.size === 0 - ) { - // We've found all. - return; - } - const pairs = appearingViewTransitions; - if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { - // This has no named view transitions in its subtree. - return; - } - let child = deletion.child; - while (child !== null) { - if (child.tag === OffscreenComponent && child.memoizedState === null) { - // This tree was already hidden so we skip it. - } else { - if ( - child.tag === ViewTransitionComponent && - (child.flags & ViewTransitionNamedStatic) !== NoFlags - ) { - const props: ViewTransitionProps = child.memoizedProps; - const name = props.name; - if (name != null && name !== 'auto') { - const pair = pairs.get(name); - if (pair !== undefined) { - const className: ?string = getViewTransitionClassName( - props.className, - props.share, - ); - if (className !== 'none') { - // We found a new appearing view transition with the same name as this deletion. - viewTransitionHostInstanceIdx = 0; - const inViewport = applyViewTransitionToHostInstances( - child.child, - name, - className, - null, - false, - ); - if (!inViewport) { - // This boundary is not in the viewport so we won't treat it as a matched pair. - // Revert the transition names. This avoids it flying onto the screen which can - // be disruptive and doesn't really preserve any continuity anyway. - restoreViewTransitionOnHostInstances(child.child, false); - } else { - // We'll transition between them. - const oldinstance: ViewTransitionState = child.stateNode; - const newInstance: ViewTransitionState = pair; - newInstance.paired = oldinstance; - // Note: If the other side ends up outside the viewport, we'll still run this. - // Therefore it's possible for onShare to be called with only an old snapshot. - scheduleViewTransitionEvent(child, props.onShare); - } - } - // Delete the entry so that we know when we've found all of them - // and can stop searching (size reaches zero). - pairs.delete(name); - if (pairs.size === 0) { - break; - } - } - } - } - commitDeletedPairViewTransitions(child); - } - child = child.sibling; - } -} - -function commitExitViewTransitions(deletion: Fiber): void { - if (deletion.tag === ViewTransitionComponent) { - const props: ViewTransitionProps = deletion.memoizedProps; - const name = getViewTransitionName(props, deletion.stateNode); - const pair = - appearingViewTransitions !== null - ? appearingViewTransitions.get(name) - : undefined; - const className: ?string = getViewTransitionClassName( - props.className, - pair !== undefined ? props.share : props.exit, - ); - if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; - const inViewport = applyViewTransitionToHostInstances( - deletion.child, - name, - className, - null, - false, - ); - if (!inViewport) { - // Revert the transition names. This boundary is not in the viewport - // so we won't bother animating it. - restoreViewTransitionOnHostInstances(deletion.child, false); - // TODO: Should we still visit the children in case a named one was in the viewport? - } else if (pair !== undefined) { - // We found a new appearing view transition with the same name as this deletion. - // We'll transition between them instead of running the normal exit. - const oldinstance: ViewTransitionState = deletion.stateNode; - const newInstance: ViewTransitionState = pair; - newInstance.paired = oldinstance; - // Delete the entry so that we know when we've found all of them - // and can stop searching (size reaches zero). - // $FlowFixMe[incompatible-use]: Refined by the pair. - appearingViewTransitions.delete(name); - // Note: If the other side ends up outside the viewport, we'll still run this. - // Therefore it's possible for onShare to be called with only an old snapshot. - scheduleViewTransitionEvent(deletion, props.onShare); - } else { - scheduleViewTransitionEvent(deletion, props.onExit); - } - } - if (appearingViewTransitions !== null) { - // Look for more pairs deeper in the tree. - commitDeletedPairViewTransitions(deletion); - } - } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - let child = deletion.child; - while (child !== null) { - commitExitViewTransitions(child); - child = child.sibling; - } - } else { - if (appearingViewTransitions !== null) { - commitDeletedPairViewTransitions(deletion); - } - } -} - -function commitBeforeUpdateViewTransition( - current: Fiber, - finishedWork: Fiber, -): void { - // The way we deal with multiple HostInstances as children of a View Transition in an - // update can get tricky. The important bit is that if you swap out n HostInstances - // from n HostInstances then they match up in order. Similarly, if you don't swap - // any HostInstances each instance just transitions as is. - // - // We call this function twice. First we apply the view transition names on the - // "current" tree in the snapshot phase. Then in the mutation phase we apply view - // transition names to the "finishedWork" tree. - // - // This means that if there were insertions or deletions before an updated Instance - // that same Instance might get different names in the "old" and the "new" state. - // For example if you swap two HostInstances inside a ViewTransition they don't - // animate to swap position but rather cross-fade into the other instance. This might - // be unexpected but it is in line with the semantics that the ViewTransition is its - // own layer that cross-fades its content when it updates. If you want to reorder then - // each child needs its own ViewTransition. - const oldProps: ViewTransitionProps = current.memoizedProps; - const oldName = getViewTransitionName(oldProps, current.stateNode); - const newProps: ViewTransitionProps = finishedWork.memoizedProps; - // This className applies only if there are fewer child DOM nodes than - // before or if this update should've been cancelled but we ended up with - // a parent animating so we need to animate the child too. - // For example, if update="foo" layout="none" and it turns out this was - // a layout only change, then the "foo" class will be applied even though - // it was not actually an update. Which is a bug. - let className: ?string = getViewTransitionClassName( - newProps.className, - newProps.update, - ); - if (className === 'none') { - className = getViewTransitionClassName(newProps.className, newProps.layout); - if (className === 'none') { - // If both update and layout are both "none" then we don't have to - // apply a name. Since we won't animate this boundary. - return; - } - } - viewTransitionHostInstanceIdx = 0; - applyViewTransitionToHostInstances( - current.child, - oldName, - className, - (current.memoizedState = []), - true, - ); -} - -function commitNestedViewTransitions(changedParent: Fiber): void { - let child = changedParent.child; - while (child !== null) { - if (child.tag === ViewTransitionComponent) { - // In this case the outer ViewTransition component wins but if there - // was an update through this component then the inner one wins. - const props: ViewTransitionProps = child.memoizedProps; - const name = getViewTransitionName(props, child.stateNode); - const className: ?string = getViewTransitionClassName( - props.className, - props.layout, - ); - if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; - applyViewTransitionToHostInstances( - child.child, - name, - className, - (child.memoizedState = []), - false, - ); - } - } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - commitNestedViewTransitions(child); - } - child = child.sibling; - } -} - -function restorePairedViewTransitions(parent: Fiber): void { - if ((parent.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { - // This has no named view transitions in its subtree. - return; - } - let child = parent.child; - while (child !== null) { - if (child.tag === OffscreenComponent && child.memoizedState === null) { - // This tree was already hidden so we skip it. - } else { - if ( - child.tag === ViewTransitionComponent && - (child.flags & ViewTransitionNamedStatic) !== NoFlags - ) { - const instance: ViewTransitionState = child.stateNode; - if (instance.paired !== null) { - instance.paired = null; - restoreViewTransitionOnHostInstances(child.child, false); - } - } - restorePairedViewTransitions(child); - } - child = child.sibling; - } -} - -function restoreEnterViewTransitions(placement: Fiber): void { - if (placement.tag === ViewTransitionComponent) { - const instance: ViewTransitionState = placement.stateNode; - instance.paired = null; - restoreViewTransitionOnHostInstances(placement.child, false); - restorePairedViewTransitions(placement); - } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - let child = placement.child; - while (child !== null) { - restoreEnterViewTransitions(child); - child = child.sibling; - } - } else { - restorePairedViewTransitions(placement); - } -} - -function restoreExitViewTransitions(deletion: Fiber): void { - if (deletion.tag === ViewTransitionComponent) { - const instance: ViewTransitionState = deletion.stateNode; - instance.paired = null; - restoreViewTransitionOnHostInstances(deletion.child, false); - restorePairedViewTransitions(deletion); - } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - let child = deletion.child; - while (child !== null) { - restoreExitViewTransitions(child); - child = child.sibling; - } - } else { - restorePairedViewTransitions(deletion); - } -} - -function restoreUpdateViewTransition( - current: Fiber, - finishedWork: Fiber, -): void { - finishedWork.memoizedState = null; - restoreViewTransitionOnHostInstances(current.child, true); - restoreViewTransitionOnHostInstances(finishedWork.child, true); -} - -function restoreNestedViewTransitions(changedParent: Fiber): void { - let child = changedParent.child; - while (child !== null) { - if (child.tag === ViewTransitionComponent) { - child.memoizedState = null; - restoreViewTransitionOnHostInstances(child.child, false); - } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - restoreNestedViewTransitions(child); - } - child = child.sibling; - } -} - -function cancelViewTransitionHostInstances( - currentViewTransition: Fiber, - child: null | Fiber, - stopAtNestedViewTransitions: boolean, -): void { - if (!supportsMutation) { - return; - } - while (child !== null) { - if (child.tag === HostComponent) { - const instance: Instance = child.stateNode; - const oldName = getViewTransitionName( - currentViewTransition.memoizedProps, - currentViewTransition.stateNode, - ); - if (viewTransitionCancelableChildren === null) { - viewTransitionCancelableChildren = []; - } - viewTransitionCancelableChildren.push( - instance, - oldName, - child.memoizedProps, - ); - viewTransitionHostInstanceIdx++; - } else if ( - child.tag === OffscreenComponent && - child.memoizedState !== null - ) { - // Skip any hidden subtrees. They were or are effectively not there. - } else if ( - child.tag === ViewTransitionComponent && - stopAtNestedViewTransitions - ) { - // Skip any nested view transitions for updates since in that case the - // inner most one is the one that handles the update. - } else { - cancelViewTransitionHostInstances( - currentViewTransition, - child.child, - stopAtNestedViewTransitions, - ); - } - child = child.sibling; - } -} - -function measureViewTransitionHostInstances( - currentViewTransition: Fiber, - parentViewTransition: Fiber, - child: null | Fiber, - name: string, - className: ?string, - previousMeasurements: null | Array, - stopAtNestedViewTransitions: boolean, -): boolean { - if (!supportsMutation) { - return true; - } - let inViewport = false; - while (child !== null) { - if (child.tag === HostComponent) { - const instance: Instance = child.stateNode; - if ( - previousMeasurements !== null && - viewTransitionHostInstanceIdx < previousMeasurements.length - ) { - // The previous measurement of the Instance in this location within the ViewTransition. - // Note that this might not be the same exact Instance if the Instances within the - // ViewTransition changed. - const previousMeasurement = - previousMeasurements[viewTransitionHostInstanceIdx]; - const nextMeasurement = measureInstance(instance); - if ( - wasInstanceInViewport(previousMeasurement) || - wasInstanceInViewport(nextMeasurement) - ) { - // If either the old or new state was within the viewport we have to animate this. - // But if it turns out that none of them were we'll be able to skip it. - inViewport = true; - } - if ( - (parentViewTransition.flags & Update) === NoFlags && - hasInstanceChanged(previousMeasurement, nextMeasurement) - ) { - parentViewTransition.flags |= Update; - } - if (hasInstanceAffectedParent(previousMeasurement, nextMeasurement)) { - // If this instance size within its parent has changed it might have caused the - // parent to relayout which needs a cross fade. - parentViewTransition.flags |= AffectedParentLayout; - } - } else { - // If there was an insertion of extra nodes, we have to assume they affected the parent. - // It should have already been marked as an Update due to the mutation. - parentViewTransition.flags |= AffectedParentLayout; - } - if ((parentViewTransition.flags & Update) !== NoFlags) { - // We might update this node so we need to apply its new name for the new state. - applyViewTransitionName( - instance, - viewTransitionHostInstanceIdx === 0 - ? name - : // If we have multiple Host Instances below, we add a suffix to the name to give - // each one a unique name. - name + '_' + viewTransitionHostInstanceIdx, - className, - ); - } - if (!inViewport || (parentViewTransition.flags & Update) === NoFlags) { - // It turns out that we had no other deeper mutations, the child transitions didn't - // affect the parent layout and this instance hasn't changed size. So we can skip - // animating it. However, in the current model this only works if the parent also - // doesn't animate. So we have to queue these and wait until we complete the parent - // to cancel them. - const oldName = getViewTransitionName( - currentViewTransition.memoizedProps, - currentViewTransition.stateNode, - ); - if (viewTransitionCancelableChildren === null) { - viewTransitionCancelableChildren = []; - } - viewTransitionCancelableChildren.push( - instance, - oldName, - child.memoizedProps, - ); - } - viewTransitionHostInstanceIdx++; - } else if ( - child.tag === OffscreenComponent && - child.memoizedState !== null - ) { - // Skip any hidden subtrees. They were or are effectively not there. - } else if ( - child.tag === ViewTransitionComponent && - stopAtNestedViewTransitions - ) { - // Skip any nested view transitions for updates since in that case the - // inner most one is the one that handles the update. - // If this inner boundary resized we need to bubble that information up. - parentViewTransition.flags |= child.flags & AffectedParentLayout; - } else { - if ( - measureViewTransitionHostInstances( - currentViewTransition, - parentViewTransition, - child.child, - name, - className, - previousMeasurements, - stopAtNestedViewTransitions, - ) - ) { - inViewport = true; - } - } - child = child.sibling; - } - return inViewport; -} - -function measureUpdateViewTransition( - current: Fiber, - finishedWork: Fiber, -): boolean { - const props: ViewTransitionProps = finishedWork.memoizedProps; - const updateClassName: ?string = getViewTransitionClassName( - props.className, - props.update, - ); - const layoutClassName: ?string = getViewTransitionClassName( - props.className, - props.layout, - ); - let className: ?string; - if (updateClassName === 'none') { - if (layoutClassName === 'none') { - // If both update and layout class name were none, then we didn't apply any - // names in the before update phase so we shouldn't now neither. - return false; - } - // We don't care if this is mutated or children layout changed, but we still - // measure each instance to see if it moved and therefore should apply layout. - finishedWork.flags &= ~Update; - className = layoutClassName; - } else if ((finishedWork.flags & Update) !== NoFlags) { - // It was updated and we have an appropriate class name to apply. - className = updateClassName; - } else { - if (layoutClassName === 'none') { - // If we did not update, then all changes are considered a layout. We'll - // attempt to cancel. - viewTransitionHostInstanceIdx = 0; - cancelViewTransitionHostInstances(current, finishedWork.child, true); - return false; - } - // We didn't update but we might still apply layout so we measure each - // instance to see if it moved or resized. - className = layoutClassName; - } - const name = getViewTransitionName(props, finishedWork.stateNode); - // If nothing changed due to a mutation, or children changing size - // and the measurements end up unchanged, we should restore it to not animate. - viewTransitionHostInstanceIdx = 0; - const previousMeasurements = current.memoizedState; - const inViewport = measureViewTransitionHostInstances( - current, - finishedWork, - finishedWork.child, - name, - className, - previousMeasurements, - true, - ); - const previousCount = - previousMeasurements === null ? 0 : previousMeasurements.length; - if (viewTransitionHostInstanceIdx !== previousCount) { - // If we found a different number of child DOM nodes we need to assume that - // the parent layout may have changed as a result. This is not necessarily - // true if those nodes were absolutely positioned. - finishedWork.flags |= AffectedParentLayout; - } - return inViewport; -} - -function measureNestedViewTransitions(changedParent: Fiber): void { - let child = changedParent.child; - while (child !== null) { - if (child.tag === ViewTransitionComponent) { - const current = child.alternate; - if (current !== null) { - const props: ViewTransitionProps = child.memoizedProps; - const name = getViewTransitionName(props, child.stateNode); - const className: ?string = getViewTransitionClassName( - props.className, - props.layout, - ); - viewTransitionHostInstanceIdx = 0; - const inViewport = measureViewTransitionHostInstances( - current, - child, - child.child, - name, - className, - child.memoizedState, - false, - ); - if ((child.flags & Update) === NoFlags || !inViewport) { - // Nothing changed. - } else { - scheduleViewTransitionEvent(child, props.onLayout); - } - } - } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - measureNestedViewTransitions(child); - } - child = child.sibling; - } -} - function commitLayoutEffectOnFiber( finishedRoot: FiberRoot, current: Fiber | null, @@ -3204,14 +2446,14 @@ function commitAfterMutationEffectsOnFiber( switch (finishedWork.tag) { case HostRoot: { viewTransitionContextChanged = false; - viewTransitionCancelableChildren = null; + setViewTransitionCancelableChildren(null); recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); if (!viewTransitionContextChanged) { // If we didn't leak any resizing out to the root, we don't have to transition // the root itself. This means that we can now safely cancel any cancellations // that bubbled all the way up. const cancelableChildren = viewTransitionCancelableChildren; - viewTransitionCancelableChildren = null; + setViewTransitionCancelableChildren(null); if (cancelableChildren !== null) { for (let i = 0; i < cancelableChildren.length; i += 3) { cancelViewTransitionName( @@ -3264,7 +2506,7 @@ function commitAfterMutationEffectsOnFiber( const prevContextChanged = viewTransitionContextChanged; const prevCancelableChildren = viewTransitionCancelableChildren; viewTransitionContextChanged = false; - viewTransitionCancelableChildren = null; + setViewTransitionCancelableChildren(null); recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); if (viewTransitionContextChanged) { @@ -3287,7 +2529,7 @@ function commitAfterMutationEffectsOnFiber( prevCancelableChildren, viewTransitionCancelableChildren, ); - viewTransitionCancelableChildren = prevCancelableChildren; + setViewTransitionCancelableChildren(prevCancelableChildren); } // TODO: If this doesn't end up canceled, because a parent animates, // then we should probably issue an event since this instance is part of it. @@ -3301,7 +2543,7 @@ function commitAfterMutationEffectsOnFiber( ); // If this boundary did update, we cannot cancel its children so those are dropped. - viewTransitionCancelableChildren = prevCancelableChildren; + setViewTransitionCancelableChildren(prevCancelableChildren); } if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) { @@ -4810,7 +4052,7 @@ export function commitPassiveUnmountEffects(finishedWork: Fiber): void { // pairs. let suspenseyCommitFlag = ShouldSuspendCommit; export function accumulateSuspenseyCommit(finishedWork: Fiber): void { - appearingViewTransitions = null; + resetAppearingViewTransitions(); accumulateSuspenseyCommitOnFiber(finishedWork); } @@ -4897,14 +4139,11 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { if (name != null && name !== 'auto') { // This is a named ViewTransition being mounted or reappearing. Let's add it to // the map so we can match it with deletions later. - if (appearingViewTransitions === null) { - appearingViewTransitions = new Map(); - } + const state: ViewTransitionState = fiber.stateNode; // Reset the pair in case we didn't end up restoring the instance in previous commits. // This shouldn't really happen anymore but just in case. We could maybe add an invariant. - const instance: ViewTransitionState = fiber.stateNode; - instance.paired = null; - appearingViewTransitions.set(name, instance); + state.paired = null; + trackAppearingViewTransition(name, state); } } recursivelyAccumulateSuspenseyCommit(fiber); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index b3d1e5371c06c..d5ef003e3dc61 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -226,8 +226,8 @@ import { invokeLayoutEffectUnmountInDEV, invokePassiveEffectUnmountInDEV, accumulateSuspenseyCommit, - shouldStartViewTransition, } from './ReactFiberCommitWork'; +import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions'; import {enqueueUpdate} from './ReactFiberClassUpdateQueue'; import {resetContextDependencies} from './ReactFiberNewContext'; import { From 92e65ca68f6bfc6be515ccacaa918e33b63911df Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 25 Feb 2025 18:55:49 -0500 Subject: [PATCH 061/300] [forgive] Add basic codelens provider (#32476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a first codelens provider for successfully compiled functions. A later PR will add an actual command that will fire when the codelens is clicked ![Screenshot 2025-02-25 at 6 40 20 PM](https://github.com/user-attachments/assets/924586e0-f70a-45d1-b0e6-a89af9371c8d) --- .../src/Entrypoint/Options.ts | 77 ++++++++++-------- .../server/src/compiler/compat.ts | 22 +++++ .../server/src/compiler/index.ts | 12 ++- .../react-forgive/server/src/index.ts | 80 +++++++++++++------ 4 files changed, 129 insertions(+), 62 deletions(-) create mode 100644 compiler/packages/react-forgive/server/src/compiler/compat.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index 35c2c4134eb44..781abd05f35da 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -176,41 +176,48 @@ export type CompilationMode = z.infer; * babel or other unhandled exceptions). */ export type LoggerEvent = - | { - kind: 'CompileError'; - fnLoc: t.SourceLocation | null; - detail: CompilerErrorDetailOptions; - } - | { - kind: 'CompileDiagnostic'; - fnLoc: t.SourceLocation | null; - detail: Omit, 'suggestions'>; - } - | { - kind: 'CompileSkip'; - fnLoc: t.SourceLocation | null; - reason: string; - loc: t.SourceLocation | null; - } - | { - kind: 'CompileSuccess'; - fnLoc: t.SourceLocation | null; - fnName: string | null; - memoSlots: number; - memoBlocks: number; - memoValues: number; - prunedMemoBlocks: number; - prunedMemoValues: number; - } - | { - kind: 'PipelineError'; - fnLoc: t.SourceLocation | null; - data: string; - } - | { - kind: 'Timing'; - measurement: PerformanceMeasure; - }; + | CompileSuccessEvent + | CompileErrorEvent + | CompileDiagnosticEvent + | CompileSkipEvent + | PipelineErrorEvent + | TimingEvent; + +export type CompileErrorEvent = { + kind: 'CompileError'; + fnLoc: t.SourceLocation | null; + detail: CompilerErrorDetailOptions; +}; +export type CompileDiagnosticEvent = { + kind: 'CompileDiagnostic'; + fnLoc: t.SourceLocation | null; + detail: Omit, 'suggestions'>; +}; +export type CompileSuccessEvent = { + kind: 'CompileSuccess'; + fnLoc: t.SourceLocation | null; + fnName: string | null; + memoSlots: number; + memoBlocks: number; + memoValues: number; + prunedMemoBlocks: number; + prunedMemoValues: number; +}; +export type CompileSkipEvent = { + kind: 'CompileSkip'; + fnLoc: t.SourceLocation | null; + reason: string; + loc: t.SourceLocation | null; +}; +export type PipelineErrorEvent = { + kind: 'PipelineError'; + fnLoc: t.SourceLocation | null; + data: string; +}; +export type TimingEvent = { + kind: 'Timing'; + measurement: PerformanceMeasure; +}; export type Logger = { logEvent: (filename: string | null, event: LoggerEvent) => void; diff --git a/compiler/packages/react-forgive/server/src/compiler/compat.ts b/compiler/packages/react-forgive/server/src/compiler/compat.ts new file mode 100644 index 0000000000000..8b13f1df886ea --- /dev/null +++ b/compiler/packages/react-forgive/server/src/compiler/compat.ts @@ -0,0 +1,22 @@ +import {SourceLocation} from 'babel-plugin-react-compiler/src'; +import {type Range} from 'vscode-languageserver'; + +export function babelLocationToRange(loc: SourceLocation): Range | null { + if (typeof loc === 'symbol') { + return null; + } + return { + start: {line: loc.start.line - 1, character: loc.start.column}, + end: {line: loc.end.line - 1, character: loc.end.column}, + }; +} + +/** + * Refine range to only the first character. + */ +export function getRangeFirstCharacter(range: Range): Range { + return { + start: range.start, + end: range.start, + }; +} diff --git a/compiler/packages/react-forgive/server/src/compiler/index.ts b/compiler/packages/react-forgive/server/src/compiler/index.ts index be2cca94ca154..3723785cfbebd 100644 --- a/compiler/packages/react-forgive/server/src/compiler/index.ts +++ b/compiler/packages/react-forgive/server/src/compiler/index.ts @@ -11,10 +11,12 @@ import BabelPluginReactCompiler, { type PluginOptions, } from 'babel-plugin-react-compiler/src'; import * as babelParser from 'prettier/plugins/babel.js'; -import * as estreeParser from 'prettier/plugins/estree'; +import estreeParser from 'prettier/plugins/estree'; import * as typescriptParser from 'prettier/plugins/typescript'; import * as prettier from 'prettier/standalone'; +export let lastResult: BabelCore.BabelFileResult | null = null; + type CompileOptions = { text: string; file: string; @@ -24,7 +26,7 @@ export async function compile({ text, file, options, -}: CompileOptions): Promise { +}: CompileOptions): Promise { const ast = await parseAsync(text, { sourceFileName: file, parserOpts: { @@ -32,6 +34,9 @@ export async function compile({ }, sourceType: 'module', }); + if (ast == null) { + return null; + } const plugins = options != null ? [[BabelPluginReactCompiler, options]] @@ -54,5 +59,8 @@ export async function compile({ parser: 'babel-ts', plugins: [babelParser, estreeParser, typescriptParser], }); + if (result.code != null) { + lastResult = result; + } return result; } diff --git a/compiler/packages/react-forgive/server/src/index.ts b/compiler/packages/react-forgive/server/src/index.ts index 057df617c8d10..395969c5e06bf 100644 --- a/compiler/packages/react-forgive/server/src/index.ts +++ b/compiler/packages/react-forgive/server/src/index.ts @@ -7,6 +7,7 @@ import {TextDocument} from 'vscode-languageserver-textdocument'; import { + CodeLens, createConnection, type InitializeParams, type InitializeResult, @@ -14,10 +15,15 @@ import { TextDocuments, TextDocumentSyncKind, } from 'vscode-languageserver/node'; -import {compile} from './compiler'; +import {compile, lastResult} from './compiler'; import {type PluginOptions} from 'babel-plugin-react-compiler/src'; import {resolveReactConfig} from './compiler/options'; -import {type BabelFileResult} from '@babel/core'; +import { + CompileSuccessEvent, + defaultOptions, + LoggerEvent, +} from 'babel-plugin-react-compiler/src/Entrypoint/Options'; +import {babelLocationToRange, getRangeFirstCharacter} from './compiler/compat'; const SUPPORTED_LANGUAGE_IDS = new Set([ 'javascript', @@ -30,11 +36,21 @@ const connection = createConnection(ProposedFeatures.all); const documents = new TextDocuments(TextDocument); let compilerOptions: PluginOptions | null = null; -let lastResult: BabelFileResult | null = null; +let compiledFns: Set = new Set(); connection.onInitialize((_params: InitializeParams) => { // TODO(@poteto) get config fr - compilerOptions = resolveReactConfig('.'); + compilerOptions = resolveReactConfig('.') ?? defaultOptions; + compilerOptions = { + ...compilerOptions, + logger: { + logEvent(_filename: string | null, event: LoggerEvent) { + if (event.kind === 'CompileSuccess') { + compiledFns.add(event); + } + }, + }, + }; const result: InitializeResult = { capabilities: { textDocumentSync: TextDocumentSyncKind.Full, @@ -48,44 +64,58 @@ connection.onInitialized(() => { connection.console.log('initialized'); }); -documents.onDidOpen(async event => { - if (SUPPORTED_LANGUAGE_IDS.has(event.document.languageId)) { - const text = event.document.getText(); - const result = await compile({ - text, - file: event.document.uri, - options: compilerOptions, - }); - if (result.code != null) { - lastResult = result; - } - } -}); - documents.onDidChangeContent(async event => { + connection.console.info(`Changed: ${event.document.uri}`); + compiledFns.clear(); if (SUPPORTED_LANGUAGE_IDS.has(event.document.languageId)) { const text = event.document.getText(); - const result = await compile({ + await compile({ text, file: event.document.uri, options: compilerOptions, }); - if (result.code != null) { - lastResult = result; - } } }); connection.onDidChangeWatchedFiles(change => { + compiledFns.clear(); connection.console.log( change.changes.map(c => `File changed: ${c.uri}`).join('\n'), ); }); connection.onCodeLens(params => { - connection.console.log('lastResult: ' + JSON.stringify(lastResult, null, 2)); - connection.console.log('params: ' + JSON.stringify(params, null, 2)); - return []; + connection.console.info(`Handling codelens for: ${params.textDocument.uri}`); + if (compiledFns.size === 0) { + return; + } + const lenses: Array = []; + for (const compiled of compiledFns) { + if (compiled.fnLoc != null) { + const fnLoc = babelLocationToRange(compiled.fnLoc); + if (fnLoc === null) continue; + const lens = CodeLens.create( + getRangeFirstCharacter(fnLoc), + compiled.fnLoc, + ); + if (lastResult?.code != null) { + lens.command = { + title: 'Optimized by React Compiler', + command: 'todo', + }; + } + lenses.push(lens); + } + } + return lenses; +}); + +connection.onCodeLensResolve(lens => { + connection.console.info(`Resolving codelens for: ${JSON.stringify(lens)}`); + if (lastResult?.code != null) { + connection.console.log(lastResult.code); + } + return lens; }); documents.listen(connection); From ebc22ef7e15bf38dc91b7033782cedc2f43f7d6e Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 25 Feb 2025 19:09:21 -0500 Subject: [PATCH 062/300] [forgive][ez] Ignore test file (#32477) --- compiler/packages/react-forgive/.vscodeignore | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/packages/react-forgive/.vscodeignore b/compiler/packages/react-forgive/.vscodeignore index 91f9572794cd5..9ef76302ed597 100644 --- a/compiler/packages/react-forgive/.vscodeignore +++ b/compiler/packages/react-forgive/.vscodeignore @@ -2,3 +2,4 @@ client server scripts +.vscode-test.mjs From 2df96224779237f532ca64c8c7e8a8605c06f067 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 27 Feb 2025 11:24:30 -0500 Subject: [PATCH 063/300] [release] Update publishing scripts to make publishing allowlisted packages easier (#32486) It's getting unwieldy to list every single package to skip in these commands when you only want to publish one, ie eslint-plugin-react-hooks. This adds a new `onlyPackages` and `publishVersion` option to the publish commands to make that easier. --- .../parse-params.js | 7 +++++++ scripts/release/prepare-release-from-npm.js | 3 +++ scripts/release/publish-commands/parse-params.js | 12 ++++++++++++ scripts/release/publish.js | 12 +++++++++--- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/scripts/release/prepare-release-from-npm-commands/parse-params.js b/scripts/release/prepare-release-from-npm-commands/parse-params.js index b9aa1a4f5e7e8..b08d81e89dc18 100644 --- a/scripts/release/prepare-release-from-npm-commands/parse-params.js +++ b/scripts/release/prepare-release-from-npm-commands/parse-params.js @@ -13,6 +13,13 @@ const paramDefinitions = [ 'Skip NPM and use the build already present in "build/node_modules".', defaultValue: false, }, + { + name: 'onlyPackages', + type: String, + multiple: true, + description: 'Packages to include in publishing', + defaultValue: [], + }, { name: 'skipPackages', type: String, diff --git a/scripts/release/prepare-release-from-npm.js b/scripts/release/prepare-release-from-npm.js index e23ffb39bc1c0..8220fda220b65 100755 --- a/scripts/release/prepare-release-from-npm.js +++ b/scripts/release/prepare-release-from-npm.js @@ -28,6 +28,9 @@ const run = async () => { params.packages = await getPublicPackages(isExperimental); params.packages = params.packages.filter(packageName => { + if (params.onlyPackages.length > 0) { + return params.onlyPackages.includes(packageName); + } return !params.skipPackages.includes(packageName); }); diff --git a/scripts/release/publish-commands/parse-params.js b/scripts/release/publish-commands/parse-params.js index b7196447d4da8..ce9a9b7825c1a 100644 --- a/scripts/release/publish-commands/parse-params.js +++ b/scripts/release/publish-commands/parse-params.js @@ -19,6 +19,13 @@ const paramDefinitions = [ description: 'NPM tags to point to the new release.', defaultValue: ['untagged'], }, + { + name: 'onlyPackages', + type: String, + multiple: true, + description: 'Packages to include in publishing', + defaultValue: [], + }, { name: 'skipPackages', type: String, @@ -32,6 +39,11 @@ const paramDefinitions = [ description: 'Run in automated environment, without interactive prompts.', defaultValue: false, }, + { + name: 'publishVersion', + type: String, + description: 'Version to publish', + }, ]; module.exports = () => { diff --git a/scripts/release/publish.js b/scripts/release/publish.js index dcd31f1de7796..87b9940636b18 100755 --- a/scripts/release/publish.js +++ b/scripts/release/publish.js @@ -23,14 +23,20 @@ const run = async () => { try { const params = parseParams(); - const version = readJsonSync( - './build/node_modules/react/package.json' - ).version; + const version = + params.publishVersion ?? + readJsonSync('./build/node_modules/react/package.json').version; const isExperimental = version.includes('experimental'); params.cwd = join(__dirname, '..', '..'); params.packages = await getPublicPackages(isExperimental); + if (params.onlyPackages.length > 0) { + params.packages = params.packages.filter(packageName => { + return params.onlyPackages.includes(packageName); + }); + } + // Pre-filter any skipped packages to simplify the following commands. // As part of doing this we can also validate that none of the skipped packages were misspelled. params.skipPackages.forEach(packageName => { From 227e8414cc5af227b5de339cace2447d4a81c995 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 27 Feb 2025 13:07:52 -0500 Subject: [PATCH 064/300] [ci] Add workflow to publish releases (#32487) Adds a new workflow to publish runtime releases from NPM. Note that I commented out the actual publish command so I can test it out first. --- .../runtime_releases_from_npm_manual.yml | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 .github/workflows/runtime_releases_from_npm_manual.yml diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml new file mode 100644 index 0000000000000..0851b38ff97b0 --- /dev/null +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -0,0 +1,91 @@ +name: (Runtime) Publish Releases from NPM Manual + +on: + workflow_dispatch: + inputs: + version_to_promote: + required: true + description: Current npm version (non-experimental) to promote + type: string + version_to_publish: + required: true + description: Version to publish for the specified packages + type: string + only_packages: + description: Space separated list of packages to publish on NPM. Use this OR skip_packages, not together. + type: string + skip_packages: + description: Space separated list of packages to NOT publish on NPM. Use this OR only_packages, not together. + type: string + tags: + description: Space separated list of tags to tag the release with on NPM + type: string + default: "['untagged']" + dry: + required: true + description: Don't actually publish, just run a dry run + type: boolean + default: true + force_notify: + description: Force a Discord notification + type: boolean + default: false + +env: + TZ: /usr/share/zoneinfo/America/Los_Angeles + # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 + GH_TOKEN: ${{ github.token }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + +jobs: + notify: + if: ${{ inputs.force_notify || inputs.dry == false || inputs.dry == 'false' }} + runs-on: ubuntu-latest + steps: + - name: Discord Webhook Action + uses: tsickert/discord-webhook@v6.0.0 + with: + webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} + embed-author-name: ${{ github.event.sender.login }} + embed-author-url: ${{ github.event.sender.html_url }} + embed-author-icon-url: ${{ github.event.sender.avatar_url }} + embed-title: '⚠️ Publishing release from NPM' + embed-description: | + ``` + inputs: ${{ toJson(inputs) }} + ``` + embed-url: https://github.com/facebook/react/actions/runs/${{ github.run_id }} + + publish: + name: Publish releases + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: yarn + cache-dependency-path: yarn.lock + - name: Restore cached node_modules + uses: actions/cache@v4 + id: node_modules + with: + path: "**/node_modules" + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + - name: Ensure clean build directory + run: rm -rf build + - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: scripts/release + - run: cp ./scripts/release/ci-npmrc ~/.npmrc + - if: '${{ inputs.only_packages }}' + run: | + scripts/release/prepare-release-from-npm.js --skipTests --version=${{ inputs.version_to_promote }} --onlyPackages=${{ inputs.only_packages }} + ls -R build/node_modules + # scripts/release/publish.js --ci --tags=${{ inputs.tags }} --publishVersion=${{ inputs.version_to_publish }} --onlyPackages=${{ inputs.only_packages }} --dry=${{ inputs.dry || 'false' }} + - if: '${{ inputs.skip_packages }}' + run: | + scripts/release/prepare-release-from-npm.js --skipTests --version=${{ inputs.version_to_promote }} --skipPackages=${{ inputs.skip_packages }} + ls -R build/node_modules + # scripts/release/publish.js --ci --tags=${{ inputs.tags }} --publishVersion=${{ inputs.version_to_publish }} --skipPackages=${{ inputs.skip_packages }} --dry=${{ inputs.dry || 'false' }} From 4c9392b43e9f39e17c18ef1c2cd0f0a14e85669c Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 27 Feb 2025 15:24:57 -0500 Subject: [PATCH 065/300] [ci] Prepare publish workflow (#32488) Fixes up a few things in the script and workflow to make it possible to run in CI without interactive prompts. --- .../runtime_releases_from_npm_manual.yml | 56 +++++++++++---- .../confirm-stable-version-numbers.js | 72 ++++++++++--------- .../guess-stable-version-numbers.js | 56 +++++++++------ .../parse-params.js | 11 +++ .../update-stable-version-numbers.js | 10 ++- scripts/release/prepare-release-from-npm.js | 7 ++ .../confirm-version-and-tags.js | 3 + .../publish-commands/publish-to-npm.js | 4 +- scripts/release/publish.js | 7 ++ 9 files changed, 154 insertions(+), 72 deletions(-) diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index 0851b38ff97b0..2759b4e4d8737 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -12,22 +12,22 @@ on: description: Version to publish for the specified packages type: string only_packages: - description: Space separated list of packages to publish on NPM. Use this OR skip_packages, not together. + description: Packages to publish (space separated) type: string skip_packages: - description: Space separated list of packages to NOT publish on NPM. Use this OR only_packages, not together. + description: Packages to NOT publish (space separated) type: string tags: - description: Space separated list of tags to tag the release with on NPM + description: NPM tags (space separated) type: string - default: "['untagged']" + default: untagged dry: required: true - description: Don't actually publish, just run a dry run + description: Dry run instead of publish? type: boolean default: true force_notify: - description: Force a Discord notification + description: Force a Discord notification? type: boolean default: false @@ -52,8 +52,8 @@ jobs: embed-author-icon-url: ${{ github.event.sender.avatar_url }} embed-title: '⚠️ Publishing release from NPM' embed-description: | - ``` - inputs: ${{ toJson(inputs) }} + ```json + ${{ toJson(inputs) }} ``` embed-url: https://github.com/facebook/react/actions/runs/${{ github.run_id }} @@ -80,12 +80,44 @@ jobs: working-directory: scripts/release - run: cp ./scripts/release/ci-npmrc ~/.npmrc - if: '${{ inputs.only_packages }}' + name: 'Prepare and publish ${{ inputs.only_packages }}' run: | - scripts/release/prepare-release-from-npm.js --skipTests --version=${{ inputs.version_to_promote }} --onlyPackages=${{ inputs.only_packages }} + echo -e "===== Preparing release from NPM =====\n" + scripts/release/prepare-release-from-npm.js \ + --ci \ + --skipTests \ + --version=${{ inputs.version_to_promote }} \ + --publishVersion=${{ inputs.version_to_publish }} \ + --onlyPackages=${{ inputs.only_packages }} + + echo -e "\n\n===== Check prepared files =====\n" ls -R build/node_modules - # scripts/release/publish.js --ci --tags=${{ inputs.tags }} --publishVersion=${{ inputs.version_to_publish }} --onlyPackages=${{ inputs.only_packages }} --dry=${{ inputs.dry || 'false' }} + + echo -e "\n\n===== Publishing to NPM =====\n" + scripts/release/publish.js \ + --ci \ + --tags=${{ inputs.tags }} \ + --publishVersion=${{ inputs.version_to_publish }} \ + --onlyPackages=${{ inputs.only_packages }} \ + --dry=${{ inputs.dry }} - if: '${{ inputs.skip_packages }}' + name: 'Prepare and publish all packages EXCEPT ${{ inputs.skip_packages }}' run: | - scripts/release/prepare-release-from-npm.js --skipTests --version=${{ inputs.version_to_promote }} --skipPackages=${{ inputs.skip_packages }} + echo -e "===== Preparing release from NPM =====\n" + scripts/release/prepare-release-from-npm.js \ + --ci \ + --skipTests \ + --version=${{ inputs.version_to_promote }} \ + --publishVersion=${{ inputs.version_to_publish }} \ + --skipPackages=${{ inputs.skip_packages }} + + echo -e "\n\n===== Check prepared files =====\n" ls -R build/node_modules - # scripts/release/publish.js --ci --tags=${{ inputs.tags }} --publishVersion=${{ inputs.version_to_publish }} --skipPackages=${{ inputs.skip_packages }} --dry=${{ inputs.dry || 'false' }} + + echo -e "\n\n===== Publishing to NPM =====\n" + scripts/release/publish.js \ + --ci \ + --tags=${{ inputs.tags }} \ + --publishVersion=${{ inputs.version_to_publish }} \ + --skipPackages=${{ inputs.skip_packages }} \ + --dry=${{ inputs.dry }} diff --git a/scripts/release/prepare-release-from-npm-commands/confirm-stable-version-numbers.js b/scripts/release/prepare-release-from-npm-commands/confirm-stable-version-numbers.js index 0b93fba2fc12b..6bf49132149a8 100644 --- a/scripts/release/prepare-release-from-npm-commands/confirm-stable-version-numbers.js +++ b/scripts/release/prepare-release-from-npm-commands/confirm-stable-version-numbers.js @@ -7,7 +7,7 @@ const semver = require('semver'); const theme = require('../theme'); const {confirm} = require('../utils'); -const run = async ({skipPackages}, versionsMap) => { +const run = async ({ci, skipPackages}, versionsMap) => { const groupedVersionsMap = new Map(); // Group packages with the same source versions. @@ -22,44 +22,46 @@ const run = async ({skipPackages}, versionsMap) => { } }); - // Prompt user to confirm or override each version group. - const entries = [...groupedVersionsMap.entries()]; - for (let i = 0; i < entries.length; i++) { - const [bestGuessVersion, packages] = entries[i]; - const packageNames = packages.map(name => theme.package(name)).join(', '); + if (ci !== true) { + // Prompt user to confirm or override each version group if not running in CI. + const entries = [...groupedVersionsMap.entries()]; + for (let i = 0; i < entries.length; i++) { + const [bestGuessVersion, packages] = entries[i]; + const packageNames = packages.map(name => theme.package(name)).join(', '); - let version = bestGuessVersion; - if ( - skipPackages.some(skipPackageName => packages.includes(skipPackageName)) - ) { - await confirm( - theme`{spinnerSuccess ✓} Version for ${packageNames} will remain {version ${bestGuessVersion}}` - ); - } else { - const defaultVersion = bestGuessVersion - ? theme.version(` (default ${bestGuessVersion})`) - : ''; - version = - (await prompt( - theme`{spinnerSuccess ✓} Version for ${packageNames}${defaultVersion}: ` - )) || bestGuessVersion; - prompt.done(); - } + let version = bestGuessVersion; + if ( + skipPackages.some(skipPackageName => packages.includes(skipPackageName)) + ) { + await confirm( + theme`{spinnerSuccess ✓} Version for ${packageNames} will remain {version ${bestGuessVersion}}` + ); + } else { + const defaultVersion = bestGuessVersion + ? theme.version(` (default ${bestGuessVersion})`) + : ''; + version = + (await prompt( + theme`{spinnerSuccess ✓} Version for ${packageNames}${defaultVersion}: ` + )) || bestGuessVersion; + prompt.done(); + } - // Verify a valid version has been supplied. - try { - semver(version); + // Verify a valid version has been supplied. + try { + semver(version); - packages.forEach(packageName => { - versionsMap.set(packageName, version); - }); - } catch (error) { - console.log( - theme`{spinnerError ✘} Version {version ${version}} is invalid.` - ); + packages.forEach(packageName => { + versionsMap.set(packageName, version); + }); + } catch (error) { + console.log( + theme`{spinnerError ✘} Version {version ${version}} is invalid.` + ); - // Prompt again - i--; + // Prompt again + i--; + } } } }; diff --git a/scripts/release/prepare-release-from-npm-commands/guess-stable-version-numbers.js b/scripts/release/prepare-release-from-npm-commands/guess-stable-version-numbers.js index a3cde5d1c4fce..c0072b6637177 100644 --- a/scripts/release/prepare-release-from-npm-commands/guess-stable-version-numbers.js +++ b/scripts/release/prepare-release-from-npm-commands/guess-stable-version-numbers.js @@ -5,7 +5,10 @@ const semver = require('semver'); const {execRead, logPromise} = require('../utils'); -const run = async ({cwd, packages, skipPackages}, versionsMap) => { +const run = async ( + {cwd, packages, skipPackages, ci, publishVersion}, + versionsMap +) => { const branch = await execRead('git branch | grep \\* | cut -d " " -f2', { cwd, }); @@ -13,30 +16,41 @@ const run = async ({cwd, packages, skipPackages}, versionsMap) => { for (let i = 0; i < packages.length; i++) { const packageName = packages[i]; - try { - // In case local package JSONs are outdated, - // guess the next version based on the latest NPM release. - const version = await execRead(`npm show ${packageName} version`); - - if (skipPackages.includes(packageName)) { - versionsMap.set(packageName, version); + if (ci === true) { + if (publishVersion != null) { + versionsMap.set(packageName, publishVersion); } else { - const {major, minor, patch} = semver(version); - - // Guess the next version by incrementing patch. - // The script will confirm this later. - // By default, new releases from mains should increment the minor version number, - // and patch releases should be done from branches. - if (branch === 'main') { - versionsMap.set(packageName, `${major}.${minor + 1}.0`); + console.error( + 'When running in CI mode, a publishVersion must be supplied' + ); + process.exit(1); + } + } else { + try { + // In case local package JSONs are outdated, + // guess the next version based on the latest NPM release. + const version = await execRead(`npm show ${packageName} version`); + + if (skipPackages.includes(packageName)) { + versionsMap.set(packageName, version); } else { - versionsMap.set(packageName, `${major}.${minor}.${patch + 1}`); + const {major, minor, patch} = semver(version); + + // Guess the next version by incrementing patch. + // The script will confirm this later. + // By default, new releases from mains should increment the minor version number, + // and patch releases should be done from branches. + if (branch === 'main') { + versionsMap.set(packageName, `${major}.${minor + 1}.0`); + } else { + versionsMap.set(packageName, `${major}.${minor}.${patch + 1}`); + } } + } catch (error) { + // If the package has not yet been published, + // we'll require a version number to be entered later. + versionsMap.set(packageName, null); } - } catch (error) { - // If the package has not yet been published, - // we'll require a version number to be entered later. - versionsMap.set(packageName, null); } } }; diff --git a/scripts/release/prepare-release-from-npm-commands/parse-params.js b/scripts/release/prepare-release-from-npm-commands/parse-params.js index b08d81e89dc18..ef9c4979b2030 100644 --- a/scripts/release/prepare-release-from-npm-commands/parse-params.js +++ b/scripts/release/prepare-release-from-npm-commands/parse-params.js @@ -39,6 +39,17 @@ const paramDefinitions = [ description: 'Version of published "next" release (e.g. 0.0.0-0e526bcec-20210202)', }, + { + name: 'publishVersion', + type: String, + description: 'Version to publish', + }, + { + name: 'ci', + type: Boolean, + description: 'Run in automated environment, without interactive prompts.', + defaultValue: false, + }, ]; module.exports = () => { diff --git a/scripts/release/prepare-release-from-npm-commands/update-stable-version-numbers.js b/scripts/release/prepare-release-from-npm-commands/update-stable-version-numbers.js index a41d83ac69dbb..f201118890456 100644 --- a/scripts/release/prepare-release-from-npm-commands/update-stable-version-numbers.js +++ b/scripts/release/prepare-release-from-npm-commands/update-stable-version-numbers.js @@ -9,7 +9,7 @@ const {join, relative} = require('path'); const {confirm, execRead, printDiff} = require('../utils'); const theme = require('../theme'); -const run = async ({cwd, packages, version}, versionsMap) => { +const run = async ({cwd, packages, version, ci}, versionsMap) => { const nodeModulesPath = join(cwd, 'build/node_modules'); // Cache all package JSONs for easy lookup below. @@ -107,7 +107,9 @@ const run = async ({cwd, packages, version}, versionsMap) => { printDependencies(packageJSON.dependencies, 'dependency'); printDependencies(packageJSON.peerDependencies, 'peer'); } - await confirm('Do the versions above look correct?'); + if (ci !== true) { + await confirm('Do the versions above look correct?'); + } clear(); @@ -167,7 +169,9 @@ const run = async ({cwd, packages, version}, versionsMap) => { console.log( theme`A full diff is available at {path ${relative(cwd, diffPath)}}.` ); - await confirm('Do the changes above look correct?'); + if (ci !== true) { + await confirm('Do the changes above look correct?'); + } } else { console.log( theme`Skipping React renderer version update because React is not included in the release.` diff --git a/scripts/release/prepare-release-from-npm.js b/scripts/release/prepare-release-from-npm.js index 8220fda220b65..bb67fddd37803 100755 --- a/scripts/release/prepare-release-from-npm.js +++ b/scripts/release/prepare-release-from-npm.js @@ -26,6 +26,13 @@ const run = async () => { params.version = await getLatestNextVersion(); } + if (params.onlyPackages.length > 0 && params.skipPackages.length > 0) { + console.error( + '--onlyPackages and --skipPackages cannot be used together' + ); + process.exit(1); + } + params.packages = await getPublicPackages(isExperimental); params.packages = params.packages.filter(packageName => { if (params.onlyPackages.length > 0) { diff --git a/scripts/release/publish-commands/confirm-version-and-tags.js b/scripts/release/publish-commands/confirm-version-and-tags.js index 6900878da02f6..2c6fed58d1937 100644 --- a/scripts/release/publish-commands/confirm-version-and-tags.js +++ b/scripts/release/publish-commands/confirm-version-and-tags.js @@ -38,6 +38,9 @@ const run = async ({cwd, packages, tags, ci}) => { console.log( theme`• {package ${packageName}} {version ${packageJSON.version}}` ); + if (ci) { + console.log(packageJSON); + } } if (!ci) { diff --git a/scripts/release/publish-commands/publish-to-npm.js b/scripts/release/publish-commands/publish-to-npm.js index d9e3b1902383f..f1e62657c0c38 100644 --- a/scripts/release/publish-commands/publish-to-npm.js +++ b/scripts/release/publish-commands/publish-to-npm.js @@ -27,7 +27,9 @@ const run = async ({cwd, dry, tags, ci}, packageName, otp) => { await confirm('Is this expected?'); } } else { - console.log(theme`{spinnerSuccess ✓} Publishing {package ${packageName}}`); + console.log( + theme`{spinnerSuccess ✓} Publishing {package ${packageName}}${dry ? ' (dry-run)' : ''}` + ); // Publish the package and tag it. if (!dry) { diff --git a/scripts/release/publish.js b/scripts/release/publish.js index 87b9940636b18..f9e450b559208 100755 --- a/scripts/release/publish.js +++ b/scripts/release/publish.js @@ -31,6 +31,13 @@ const run = async () => { params.cwd = join(__dirname, '..', '..'); params.packages = await getPublicPackages(isExperimental); + if (params.onlyPackages.length > 0 && params.skipPackages.length > 0) { + console.error( + '--onlyPackages and --skipPackages cannot be used together' + ); + process.exit(1); + } + if (params.onlyPackages.length > 0) { params.packages = params.packages.filter(packageName => { return params.onlyPackages.includes(packageName); From 5eb20b3007a8fafaf032c2e028c335ab09217d9f Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 27 Feb 2025 16:01:31 -0500 Subject: [PATCH 066/300] [ci] Fix --dry not being passed correctly (#32489) Boolean params for dry runs are true if the param exists at all, so only add it if we're in dry run mode. --- .../runtime_releases_from_npm_manual.yml | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index 2759b4e4d8737..3c8cec7f7ac1b 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -50,7 +50,7 @@ jobs: embed-author-name: ${{ github.event.sender.login }} embed-author-url: ${{ github.event.sender.html_url }} embed-author-icon-url: ${{ github.event.sender.avatar_url }} - embed-title: '⚠️ Publishing release from NPM' + embed-title: "⚠️ Publishing release from NPM${{ inputs.dry && ' (dry run)' }}" embed-description: | ```json ${{ toJson(inputs) }} @@ -80,44 +80,40 @@ jobs: working-directory: scripts/release - run: cp ./scripts/release/ci-npmrc ~/.npmrc - if: '${{ inputs.only_packages }}' - name: 'Prepare and publish ${{ inputs.only_packages }}' + name: 'Prepare ${{ inputs.only_packages }} from NPM' run: | - echo -e "===== Preparing release from NPM =====\n" scripts/release/prepare-release-from-npm.js \ --ci \ --skipTests \ --version=${{ inputs.version_to_promote }} \ --publishVersion=${{ inputs.version_to_publish }} \ --onlyPackages=${{ inputs.only_packages }} - - echo -e "\n\n===== Check prepared files =====\n" - ls -R build/node_modules - - echo -e "\n\n===== Publishing to NPM =====\n" - scripts/release/publish.js \ - --ci \ - --tags=${{ inputs.tags }} \ - --publishVersion=${{ inputs.version_to_publish }} \ - --onlyPackages=${{ inputs.only_packages }} \ - --dry=${{ inputs.dry }} - if: '${{ inputs.skip_packages }}' - name: 'Prepare and publish all packages EXCEPT ${{ inputs.skip_packages }}' + name: 'Prepare all packages EXCEPT ${{ inputs.skip_packages }} from NPM' run: | - echo -e "===== Preparing release from NPM =====\n" scripts/release/prepare-release-from-npm.js \ --ci \ --skipTests \ --version=${{ inputs.version_to_promote }} \ --publishVersion=${{ inputs.version_to_publish }} \ --skipPackages=${{ inputs.skip_packages }} - - echo -e "\n\n===== Check prepared files =====\n" - ls -R build/node_modules - - echo -e "\n\n===== Publishing to NPM =====\n" + - name: Check prepared files + run: ls -R build/node_modules + - if: '${{ inputs.only_packages }}' + name: 'Publish ${{ inputs.only_packages }}' + run: | + scripts/release/publish.js \ + --ci \ + --tags=${{ inputs.tags }} \ + --publishVersion=${{ inputs.version_to_publish }} \ + --onlyPackages=${{ inputs.only_packages }} ${{ (inputs.dry && '') || '\'}} + ${{ inputs.dry && '--dry'}} + - if: '${{ inputs.skip_packages }}' + name: 'Publish all packages EXCEPT ${{ inputs.skip_packages }}' + run: | scripts/release/publish.js \ --ci \ --tags=${{ inputs.tags }} \ --publishVersion=${{ inputs.version_to_publish }} \ - --skipPackages=${{ inputs.skip_packages }} \ - --dry=${{ inputs.dry }} + --skipPackages=${{ inputs.skip_packages }} ${{ (inputs.dry && '') || '\'}} + ${{ inputs.dry && '--dry'}} From 3607f4838a8f4a87160da36aa26bb1432d7a5f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Thu, 27 Feb 2025 16:45:18 -0500 Subject: [PATCH 067/300] Add Commit Scaffolding for Gestures (#32451) This adds a `ReactFiberApplyGesture` which is basically intended to be a fork of the phases in `ReactFiberCommitWork` except for the fake commit that `useSwipeTransition` does. So far none of the phases are actually implemented yet. This is just the scaffolding around them so I can fill them in later. The important bit is that we call `startViewTransition` (via the `startGestureTransition` Config) when a gesture starts. We add a paused animation to prevent the transition from committing (even if the ScrollTimeline goes to 100%). This also locks the documents so that we can't commit any other Transitions until it completes. When the gesture completes (scroll end) then we stop the gesture View Transition. If there's no new work scheduled we do that immediately but if there was any new work already scheduled, then we assume that this will potentially commit the new state. So we wait for that to finish. This lets us lock the animation in its state instead of snapping back and then applying the real update. Using this technique we can't actually run a View Transition from the current state to the actual committed state because it would snap back to the beginning and then run the View Transition from there. Therefore any new commit needs to skip View Transitions even if it should've technically animated to that state. We assume that the new state is the same as the optimistic state you already swiped to. An alternative to this technique could be to commit the optimistic state when we cancel and then apply any new updates o top of that. I might explore that in the future. Regardless it's important that the `action` associated with the swipe schedules some work before we cancel. Otherwise it risks reverting first. So I had to update this in the fixture. --- .../src/components/SwipeRecognizer.js | 10 +- packages/react-art/src/ReactFiberConfigART.js | 6 + .../src/client/ReactFiberConfigDOM.js | 74 ++++++- .../src/ReactFiberConfigNative.js | 15 ++ .../src/createReactNoop.js | 15 ++ .../src/ReactFiberApplyGesture.js | 42 ++++ .../src/ReactFiberConfigWithNoMutation.js | 3 + .../src/ReactFiberGestureScheduler.js | 80 ++++++-- .../react-reconciler/src/ReactFiberHooks.js | 2 +- .../react-reconciler/src/ReactFiberRoot.js | 3 +- .../src/ReactFiberWorkLoop.js | 180 +++++++++++++++--- .../src/ReactInternalTypes.js | 3 +- .../src/forks/ReactFiberConfig.custom.js | 3 + .../src/ReactFiberConfigTestHost.js | 15 ++ 14 files changed, 401 insertions(+), 50 deletions(-) create mode 100644 packages/react-reconciler/src/ReactFiberApplyGesture.js diff --git a/fixtures/view-transition/src/components/SwipeRecognizer.js b/fixtures/view-transition/src/components/SwipeRecognizer.js index f332796fc8cdf..8e913dfb4e55f 100644 --- a/fixtures/view-transition/src/components/SwipeRecognizer.js +++ b/fixtures/view-transition/src/components/SwipeRecognizer.js @@ -33,11 +33,6 @@ export default function SwipeRecognizer({ }); } function onScrollEnd() { - if (activeGesture.current !== null) { - const cancelGesture = activeGesture.current; - activeGesture.current = null; - cancelGesture(); - } let changed; const scrollElement = scrollRef.current; if (axis === 'x') { @@ -60,6 +55,11 @@ export default function SwipeRecognizer({ // Trigger side-effects startTransition(action); } + if (activeGesture.current !== null) { + const cancelGesture = activeGesture.current; + activeGesture.current = null; + cancelGesture(); + } } useEffect(() => { diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index a011bdf5c71c7..dc746b0e17d88 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -500,6 +500,12 @@ export function startViewTransition() { return false; } +export type RunningGestureTransition = null; + +export function startGestureTransition() {} + +export function stopGestureTransition(transition: RunningGestureTransition) {} + export type ViewTransitionInstance = null | {name: string, ...}; export function createViewTransitionInstance( diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 52560d0b006b0..2b288efe46c3f 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1394,7 +1394,10 @@ export function startViewTransition( transition.ready.then(spawnedWorkCallback, spawnedWorkCallback); transition.finished.then(() => { // $FlowFixMe[prop-missing] - ownerDocument.__reactViewTransition = null; + if (ownerDocument.__reactViewTransition === transition) { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + } passiveCallback(); }); return true; @@ -1409,6 +1412,75 @@ export function startViewTransition( } } +export type RunningGestureTransition = { + skipTransition(): void, + ... +}; + +export function startGestureTransition( + rootContainer: Container, + transitionTypes: null | TransitionTypes, + mutationCallback: () => void, + animateCallback: () => void, +): null | RunningGestureTransition { + const ownerDocument: Document = + rootContainer.nodeType === DOCUMENT_NODE + ? (rootContainer: any) + : rootContainer.ownerDocument; + try { + // $FlowFixMe[prop-missing] + const transition = ownerDocument.startViewTransition({ + update: mutationCallback, + types: transitionTypes, + }); + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = transition; + let blockingAnim = null; + const readyCallback = () => { + // View Transitions with ScrollTimeline has a quirk where they end if the + // ScrollTimeline ever reaches 100% but that doesn't mean we're done because + // you can swipe back again. We can prevent this by adding a paused Animation + // that never stops. This seems to keep all running Animations alive until + // we explicitly abort (or something forces the View Transition to cancel). + const documentElement: Element = (ownerDocument.documentElement: any); + blockingAnim = documentElement.animate([{}, {}], { + pseudoElement: '::view-transition', + duration: 1, + }); + blockingAnim.pause(); + animateCallback(); + }; + transition.ready.then(readyCallback, readyCallback); + transition.finished.then(() => { + if (blockingAnim !== null) { + // In Safari, we need to manually clear this or it'll block future transitions. + blockingAnim.cancel(); + } + // $FlowFixMe[prop-missing] + if (ownerDocument.__reactViewTransition === transition) { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + } + }); + return transition; + } catch (x) { + // We use the error as feature detection. + // The only thing that should throw is if startViewTransition is missing + // or if it doesn't accept the object form. Other errors are async. + // I.e. it's before the View Transitions v2 spec. We only support View + // Transitions v2 otherwise we fallback to not animating to ensure that + // we're not animating with the wrong animation mapped. + // Run through the sequence to put state back into a consistent state. + mutationCallback(); + animateCallback(); + return null; + } +} + +export function stopGestureTransition(transition: RunningGestureTransition) { + transition.skipTransition(); +} + interface ViewTransitionPseudoElementType extends Animatable { _scope: HTMLElement; _selector: string; diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 00385547f23e7..4a9064c82a83b 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -597,6 +597,21 @@ export function startViewTransition( return false; } +export type RunningGestureTransition = null; + +export function startGestureTransition( + rootContainer: Container, + transitionTypes: null | TransitionTypes, + mutationCallback: () => void, + animateCallback: () => void, +): RunningGestureTransition { + mutationCallback(); + animateCallback(); + return null; +} + +export function stopGestureTransition(transition: RunningGestureTransition) {} + export type ViewTransitionInstance = null | {name: string, ...}; export function createViewTransitionInstance( diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 43853a864fb5e..e392ad7b0c04f 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -93,6 +93,8 @@ export type TransitionStatus = mixed; export type FormInstance = Instance; +export type RunningGestureTransition = null; + export type ViewTransitionInstance = null | {name: string, ...}; export type GestureTimeline = null; @@ -792,6 +794,19 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return false; }, + startGestureTransition( + rootContainer: Container, + transitionTypes: null | TransitionTypes, + mutationCallback: () => void, + animateCallback: () => void, + ): RunningGestureTransition { + mutationCallback(); + animateCallback(); + return null; + }, + + stopGestureTransition(transition: RunningGestureTransition) {}, + createViewTransitionInstance(name: string): ViewTransitionInstance { return null; }, diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js new file mode 100644 index 0000000000000..46af2ca4309fa --- /dev/null +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -0,0 +1,42 @@ +/** + * 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 type {Fiber, FiberRoot} from './ReactInternalTypes'; + +import { + cancelRootViewTransitionName, + restoreRootViewTransitionName, +} from './ReactFiberConfig'; + +// Clone View Transition boundaries that have any mutations or might have had their +// layout affected by child insertions. +export function insertDestinationClones( + root: FiberRoot, + finishedWork: Fiber, +): void { + // TODO +} + +// Revert insertions and apply view transition names to the "new" (current) state. +export function applyDepartureTransitions( + root: FiberRoot, + finishedWork: Fiber, +): void { + // TODO + cancelRootViewTransitionName(root.containerInfo); +} + +// Revert transition names and start/adjust animations on the started View Transition. +export function startGestureAnimations( + root: FiberRoot, + finishedWork: Fiber, +): void { + // TODO + restoreRootViewTransitionName(root.containerInfo); +} diff --git a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js index da34b26084de5..430ad02abc1b1 100644 --- a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js +++ b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js @@ -46,6 +46,9 @@ export const wasInstanceInViewport = shim; export const hasInstanceChanged = shim; export const hasInstanceAffectedParent = shim; export const startViewTransition = shim; +export type RunningGestureTransition = null; +export const startGestureTransition = shim; +export const stopGestureTransition = shim; export type ViewTransitionInstance = null | {name: string, ...}; export const createViewTransitionInstance = shim; export type GestureTimeline = any; diff --git a/packages/react-reconciler/src/ReactFiberGestureScheduler.js b/packages/react-reconciler/src/ReactFiberGestureScheduler.js index fffb7d524d9e4..33d477f07daec 100644 --- a/packages/react-reconciler/src/ReactFiberGestureScheduler.js +++ b/packages/react-reconciler/src/ReactFiberGestureScheduler.js @@ -8,11 +8,21 @@ */ import type {FiberRoot} from './ReactInternalTypes'; -import type {GestureTimeline} from './ReactFiberConfig'; +import type { + GestureTimeline, + RunningGestureTransition, +} from './ReactFiberConfig'; -import {GestureLane} from './ReactFiberLane'; +import { + GestureLane, + includesBlockingLane, + includesTransitionLane, +} from './ReactFiberLane'; import {ensureRootIsScheduled} from './ReactFiberRootScheduler'; -import {subscribeToGestureDirection} from './ReactFiberConfig'; +import { + subscribeToGestureDirection, + stopGestureTransition, +} from './ReactFiberConfig'; // This type keeps track of any scheduled or active gestures. export type ScheduledGesture = { @@ -23,6 +33,7 @@ export type ScheduledGesture = { rangeCurrent: number, // The starting offset along the timeline. rangeNext: number, // The end along the timeline where the next state is reached. cancel: () => void, // Cancel the subscription to direction change. + running: null | RunningGestureTransition, // Used to cancel the running transition after we're done. prev: null | ScheduledGesture, // The previous scheduled gesture in the queue for this root. next: null | ScheduledGesture, // The next scheduled gesture in the queue for this root. }; @@ -35,7 +46,7 @@ export function scheduleGesture( rangeCurrent: number, rangeNext: number, ): ScheduledGesture { - let prev = root.gestures; + let prev = root.pendingGestures; while (prev !== null) { if (prev.provider === provider) { // Existing instance found. @@ -59,16 +70,16 @@ export function scheduleGesture( } if (gesture.direction !== direction) { gesture.direction = direction; - if (gesture.prev === null && root.gestures !== gesture) { + if (gesture.prev === null && root.pendingGestures !== gesture) { // This gesture is not in the schedule, meaning it was already rendered. // We need to rerender in the new direction. Insert it into the first slot // in case other gestures are queued after the on-going one. - const existing = root.gestures; + const existing = root.pendingGestures; gesture.next = existing; if (existing !== null) { existing.prev = gesture; } - root.gestures = gesture; + root.pendingGestures = gesture; // Schedule the lane on the root. The Fibers will already be marked as // long as the gesture is active on that Hook. root.pendingLanes |= GestureLane; @@ -86,11 +97,12 @@ export function scheduleGesture( rangeCurrent: rangeCurrent, rangeNext: rangeNext, cancel: cancel, + running: null, prev: prev, next: null, }; if (prev === null) { - root.gestures = gesture; + root.pendingGestures = gesture; } else { prev.next = gesture; } @@ -106,10 +118,35 @@ export function cancelScheduledGesture( if (gesture.count === 0) { const cancelDirectionSubscription = gesture.cancel; cancelDirectionSubscription(); - // Delete the scheduled gesture from the queue. + // Delete the scheduled gesture from the pending queue. deleteScheduledGesture(root, gesture); // TODO: If we're currently rendering this gesture, we need to restart the render // on a different gesture or cancel the render.. + // TODO: We might want to pause the View Transition at this point since you should + // no longer be able to update the position of anything but it might be better to + // just commit the gesture state. + const runningTransition = gesture.running; + if (runningTransition !== null) { + const pendingLanesExcludingGestureLane = root.pendingLanes & ~GestureLane; + if ( + includesBlockingLane(pendingLanesExcludingGestureLane) || + includesTransitionLane(pendingLanesExcludingGestureLane) + ) { + // If we have pending work we schedule the gesture to be stopped at the next commit. + // This ensures that we don't snap back to the previous state until we have + // had a chance to commit any resulting updates. + const existing = root.stoppingGestures; + if (existing !== null) { + gesture.next = existing; + existing.prev = gesture; + } + root.stoppingGestures = gesture; + } else { + gesture.running = null; + // If there's no work scheduled so we can stop the View Transition right away. + stopGestureTransition(runningTransition); + } + } } } @@ -118,15 +155,19 @@ export function deleteScheduledGesture( gesture: ScheduledGesture, ): void { if (gesture.prev === null) { - if (root.gestures === gesture) { - root.gestures = gesture.next; - if (root.gestures === null) { + if (root.pendingGestures === gesture) { + root.pendingGestures = gesture.next; + if (root.pendingGestures === null) { // Gestures don't clear their lanes while the gesture is still active but it // might not be scheduled to do any more renders and so we shouldn't schedule // any more gesture lane work until a new gesture is scheduled. root.pendingLanes &= ~GestureLane; } } + if (root.stoppingGestures === gesture) { + // This should not really happen the way we use it now but just in case we start. + root.stoppingGestures = gesture.next; + } } else { gesture.prev.next = gesture.next; if (gesture.next !== null) { @@ -136,3 +177,18 @@ export function deleteScheduledGesture( gesture.next = null; } } + +export function stopCompletedGestures(root: FiberRoot) { + let gesture = root.stoppingGestures; + root.stoppingGestures = null; + while (gesture !== null) { + if (gesture.running !== null) { + stopGestureTransition(gesture.running); + gesture.running = null; + } + const nextGesture = gesture.next; + gesture.next = null; + gesture.prev = null; + gesture = nextGesture; + } +} diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 50424e9d7b4db..270cd87d4941c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -4126,7 +4126,7 @@ function updateSwipeTransition( ); } // We assume that the currently rendering gesture is the one first in the queue. - const rootRenderGesture = root.gestures; + const rootRenderGesture = root.pendingGestures; if (rootRenderGesture !== null) { let update = queue.pending; while (update !== null) { diff --git a/packages/react-reconciler/src/ReactFiberRoot.js b/packages/react-reconciler/src/ReactFiberRoot.js index 03ddde7a5ab5b..41401bac2bd8f 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.js +++ b/packages/react-reconciler/src/ReactFiberRoot.js @@ -99,7 +99,8 @@ function FiberRootNode( this.formState = formState; if (enableSwipeTransition) { - this.gestures = null; + this.pendingGestures = null; + this.stoppingGestures = null; } this.incompleteTransitions = new Map(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index d5ef003e3dc61..bf611f73275a7 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -100,6 +100,7 @@ import { resolveUpdatePriority, trackSchedulerEvent, startViewTransition, + startGestureTransition, createViewTransitionInstance, } from './ReactFiberConfig'; @@ -228,6 +229,11 @@ import { accumulateSuspenseyCommit, } from './ReactFiberCommitWork'; import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions'; +import { + insertDestinationClones, + applyDepartureTransitions, + startGestureAnimations, +} from './ReactFiberApplyGesture'; import {enqueueUpdate} from './ReactFiberClassUpdateQueue'; import {resetContextDependencies} from './ReactFiberNewContext'; import { @@ -341,7 +347,10 @@ import { import {getMaskedContext, getUnmaskedContext} from './ReactFiberContext'; import {peekEntangledActionLane} from './ReactFiberAsyncAction'; import {logUncaughtError} from './ReactFiberErrorLogger'; -import {deleteScheduledGesture} from './ReactFiberGestureScheduler'; +import { + deleteScheduledGesture, + stopCompletedGestures, +} from './ReactFiberGestureScheduler'; const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; @@ -644,7 +653,9 @@ const PENDING_LAYOUT_PHASE = 2; const PENDING_AFTER_MUTATION_PHASE = 3; const PENDING_SPAWNED_WORK = 4; const PENDING_PASSIVE_PHASE = 5; -let pendingEffectsStatus: 0 | 1 | 2 | 3 | 4 | 5 = 0; +const PENDING_GESTURE_MUTATION_PHASE = 6; +const PENDING_GESTURE_ANIMATION_PHASE = 7; +let pendingEffectsStatus: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0; let pendingEffectsRoot: FiberRoot = (null: any); let pendingFinishedWork: Fiber = (null: any); let pendingEffectsLanes: Lanes = NoLanes; @@ -1424,11 +1435,12 @@ function commitRootWhenReady( const subtreeFlags = finishedWork.subtreeFlags; const isViewTransitionEligible = enableViewTransition && includesOnlyViewTransitionEligibleLanes(lanes); // TODO: Use a subtreeFlag to optimize. + const isGestureTransition = enableSwipeTransition && isGestureRender(lanes); const maySuspendCommit = subtreeFlags & ShouldSuspendCommit || (subtreeFlags & BothVisibilityAndMaySuspendCommit) === BothVisibilityAndMaySuspendCommit; - if (isViewTransitionEligible || maySuspendCommit) { + if (isViewTransitionEligible || maySuspendCommit || isGestureTransition) { // Before committing, ask the renderer whether the host tree is ready. // If it's not, we'll wait until it notifies us. startSuspendingCommit(); @@ -1439,8 +1451,12 @@ function commitRootWhenReady( // This will also track any newly added or appearing ViewTransition // components for the purposes of forming pairs. accumulateSuspenseyCommit(finishedWork); - if (isViewTransitionEligible) { - suspendOnActiveViewTransition(root.containerInfo); + if (isViewTransitionEligible || isGestureTransition) { + // If we're stopping gestures we don't have to wait for any pending + // view transition. We'll stop it when we commit. + if (!enableSwipeTransition || root.stoppingGestures === null) { + suspendOnActiveViewTransition(root.containerInfo); + } } // At the end, ask the renderer if it's ready to commit, or if we should // suspend. If it's not ready, it will return a callback to subscribe to @@ -3263,6 +3279,12 @@ function commitRoot( if (enableSchedulingProfiler) { markCommitStopped(); } + if (enableSwipeTransition) { + // Stop any gestures that were completed and is now being reverted. + if (root.stoppingGestures !== null) { + stopCompletedGestures(root); + } + } return; } else { if (__DEV__) { @@ -3291,7 +3313,7 @@ function commitRoot( const concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes(); remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes); - if (enableSwipeTransition && root.gestures === null) { + if (enableSwipeTransition && root.pendingGestures === null) { // Gestures don't clear their lanes while the gesture is still active but it // might not be scheduled to do any more renders and so we shouldn't schedule // any more gesture lane work until a new gesture is scheduled. @@ -3321,21 +3343,6 @@ function commitRoot( // times out. } - if (enableSwipeTransition && isGestureRender(lanes)) { - // This is a special kind of render that doesn't commit regular effects. - commitGestureOnRoot( - root, - finishedWork, - recoverableErrors, - enableProfilerTimer - ? suspendedCommitReason === IMMEDIATE_COMMIT - ? completedRenderEndTime - : commitStartTime - : 0, - ); - return; - } - // workInProgressX might be overwritten, so we want // to store it in pendingPassiveX until they get processed // We need to pass this through as an argument to commitRoot @@ -3354,6 +3361,21 @@ function commitRoot( pendingSuspendedCommitReason = suspendedCommitReason; } + if (enableSwipeTransition && isGestureRender(lanes)) { + // This is a special kind of render that doesn't commit regular effects. + commitGestureOnRoot( + root, + finishedWork, + recoverableErrors, + enableProfilerTimer + ? suspendedCommitReason === IMMEDIATE_COMMIT + ? completedRenderEndTime + : commitStartTime + : 0, + ); + return; + } + // If there are pending passive effects, schedule a callback to process them. // Do this as early as possible, so it is queued before anything else that // might get scheduled in the commit phase. (See #16714.) @@ -3461,10 +3483,23 @@ function commitRoot( ReactSharedInternals.T = prevTransition; } } + + let willStartViewTransition = shouldStartViewTransition; + if (enableSwipeTransition) { + // Stop any gestures that were completed and is now being committed. + if (root.stoppingGestures !== null) { + stopCompletedGestures(root); + // If we are in the process of stopping some gesture we shouldn't start + // a View Transition because that would start from the previous state to + // the next state. + willStartViewTransition = false; + } + } + pendingEffectsStatus = PENDING_MUTATION_PHASE; const startedViewTransition = enableViewTransition && - shouldStartViewTransition && + willStartViewTransition && startViewTransition( root.containerInfo, pendingTransitionTypes, @@ -3633,6 +3668,7 @@ function flushSpawnedWork(): void { } else { pendingEffectsStatus = NO_PENDING_EFFECTS; pendingEffectsRoot = (null: any); // Clear for GC purposes. + pendingFinishedWork = (null: any); // Clear for GC purposes. // There were no passive effects, so we can immediately release the cache // pool for this render. releaseRootPooledCache(root, root.pendingLanes); @@ -3830,20 +3866,103 @@ function flushSpawnedWork(): void { function commitGestureOnRoot( root: FiberRoot, - finishedWork: null | Fiber, + finishedWork: Fiber, recoverableErrors: null | Array>, renderEndTime: number, // Profiling-only ): void { // We assume that the gesture we just rendered was the first one in the queue. - const finishedGesture = root.gestures; + const finishedGesture = root.pendingGestures; if (finishedGesture === null) { - throw new Error( - 'Finished rendering the gesture lane but there were no pending gestures. ' + - 'React should not have started a render in this case. This is a bug in React.', - ); + // We must have already cancelled this gesture before we had a chance to + // render it. Let's schedule work on the next set of lanes. + ensureRootIsScheduled(root); + return; } deleteScheduledGesture(root, finishedGesture); - // TODO: Run the gesture + + const prevTransition = ReactSharedInternals.T; + ReactSharedInternals.T = null; + const previousPriority = getCurrentUpdatePriority(); + setCurrentUpdatePriority(DiscreteEventPriority); + const prevExecutionContext = executionContext; + executionContext |= CommitContext; + try { + insertDestinationClones(root, finishedWork); + } finally { + // Reset the priority to the previous non-sync value. + executionContext = prevExecutionContext; + setCurrentUpdatePriority(previousPriority); + ReactSharedInternals.T = prevTransition; + } + // TODO: Collect transition types. + pendingTransitionTypes = null; + pendingEffectsStatus = PENDING_GESTURE_MUTATION_PHASE; + + finishedGesture.running = startGestureTransition( + root.containerInfo, + pendingTransitionTypes, + flushGestureMutations, + flushGestureAnimations, + ); +} + +function flushGestureMutations(): void { + if (pendingEffectsStatus !== PENDING_GESTURE_MUTATION_PHASE) { + return; + } + pendingEffectsStatus = NO_PENDING_EFFECTS; + const root = pendingEffectsRoot; + const finishedWork = pendingFinishedWork; + + const prevTransition = ReactSharedInternals.T; + ReactSharedInternals.T = null; + const previousPriority = getCurrentUpdatePriority(); + setCurrentUpdatePriority(DiscreteEventPriority); + const prevExecutionContext = executionContext; + executionContext |= CommitContext; + try { + applyDepartureTransitions(root, finishedWork); + } finally { + // Reset the priority to the previous non-sync value. + executionContext = prevExecutionContext; + setCurrentUpdatePriority(previousPriority); + ReactSharedInternals.T = prevTransition; + } + + pendingEffectsStatus = PENDING_GESTURE_ANIMATION_PHASE; +} + +function flushGestureAnimations(): void { + // If we get canceled before we start we might not have applied + // mutations yet. We need to apply them first. + flushGestureMutations(); + if (pendingEffectsStatus !== PENDING_GESTURE_ANIMATION_PHASE) { + return; + } + pendingEffectsStatus = NO_PENDING_EFFECTS; + const root = pendingEffectsRoot; + const finishedWork = pendingFinishedWork; + pendingEffectsRoot = (null: any); // Clear for GC purposes. + pendingFinishedWork = (null: any); // Clear for GC purposes. + pendingEffectsLanes = NoLanes; + + const prevTransition = ReactSharedInternals.T; + ReactSharedInternals.T = null; + const previousPriority = getCurrentUpdatePriority(); + setCurrentUpdatePriority(DiscreteEventPriority); + const prevExecutionContext = executionContext; + executionContext |= CommitContext; + try { + startGestureAnimations(root, finishedWork); + } finally { + // Reset the priority to the previous non-sync value. + executionContext = prevExecutionContext; + setCurrentUpdatePriority(previousPriority); + ReactSharedInternals.T = prevTransition; + } + + // Now that we've rendered this lane. Start working on the next lane. + ensureRootIsScheduled(root); } function makeErrorInfo(componentStack: ?string) { @@ -3879,6 +3998,8 @@ function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) { export function flushPendingEffects(wasDelayedCommit?: boolean): boolean { // Returns whether passive effects were flushed. + flushGestureMutations(); + flushGestureAnimations(); flushMutationEffects(); flushLayoutEffects(); // Skip flushAfterMutation if we're forcing this early. @@ -3932,6 +4053,7 @@ function flushPassiveEffectsImpl(wasDelayedCommit: void | boolean) { const lanes = pendingEffectsLanes; pendingEffectsStatus = NO_PENDING_EFFECTS; pendingEffectsRoot = (null: any); // Clear for GC purposes. + pendingFinishedWork = (null: any); // Clear for GC purposes. // TODO: This is sometimes out of sync with pendingEffectsRoot. // Figure out why and fix it. It's not causing any known issues (probably // because it's only used for profiling), but it's a refactor hazard. diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 3479eba1b9dd3..be5e9a5c0da69 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -284,7 +284,8 @@ type BaseFiberRootProperties = { formState: ReactFormState | null, // enableSwipeTransition only - gestures: null | ScheduledGesture, + pendingGestures: null | ScheduledGesture, + stoppingGestures: null | ScheduledGesture, }; // The following attributes are only used by DevTools and are only present in DEV builds. diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 6b2781c0ab2a5..9569b4a896cee 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -40,6 +40,7 @@ export opaque type NoTimeout = mixed; export opaque type RendererInspectionConfig = mixed; export opaque type TransitionStatus = mixed; export opaque type FormInstance = mixed; +export type RunningGestureTransition = mixed; export type ViewTransitionInstance = null | {name: string, ...}; export opaque type InstanceMeasurement = mixed; export type EventResponder = any; @@ -145,6 +146,8 @@ export const wasInstanceInViewport = $$$config.wasInstanceInViewport; export const hasInstanceChanged = $$$config.hasInstanceChanged; export const hasInstanceAffectedParent = $$$config.hasInstanceAffectedParent; export const startViewTransition = $$$config.startViewTransition; +export const startGestureTransition = $$$config.startGestureTransition; +export const stopGestureTransition = $$$config.stopGestureTransition; export const getCurrentGestureOffset = $$$config.getCurrentGestureOffset; export const subscribeToGestureDirection = $$$config.subscribeToGestureDirection; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index 8f4f81cabb9ce..48939e9ff45ad 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -375,6 +375,21 @@ export function startViewTransition( return false; } +export type RunningGestureTransition = null; + +export function startGestureTransition( + rootContainer: Container, + transitionTypes: null | TransitionTypes, + mutationCallback: () => void, + animateCallback: () => void, +): RunningGestureTransition { + mutationCallback(); + animateCallback(); + return null; +} + +export function stopGestureTransition(transition: RunningGestureTransition) {} + export type ViewTransitionInstance = null | {name: string, ...}; export function createViewTransitionInstance( From 7e2ea902f839264fd327b0df5fae4f6ad8359952 Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 28 Feb 2025 10:58:28 -0500 Subject: [PATCH 068/300] [ci] Fix discord notification title (#32491) fun times --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32491). * #32492 * __->__ #32491 --- .github/workflows/runtime_releases_from_npm_manual.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index 3c8cec7f7ac1b..2e1047b55df50 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -50,7 +50,7 @@ jobs: embed-author-name: ${{ github.event.sender.login }} embed-author-url: ${{ github.event.sender.html_url }} embed-author-icon-url: ${{ github.event.sender.avatar_url }} - embed-title: "⚠️ Publishing release from NPM${{ inputs.dry && ' (dry run)' }}" + embed-title: "⚠️ Publishing release from NPM${{ (inputs.dry && ' (dry run)') || '' }}" embed-description: | ```json ${{ toJson(inputs) }} From 56c7d1070aabe42b955ea0006477ea1ca02dd0c5 Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 28 Feb 2025 10:58:39 -0500 Subject: [PATCH 069/300] [ci] Upload release for easier debugging (#32492) Uploads the releases that were published in case to help with debugging or verifying a dry run. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32492). * __->__ #32492 * #32491 --- .github/workflows/runtime_releases_from_npm_manual.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index 2e1047b55df50..c0b3867e03da0 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -117,3 +117,9 @@ jobs: --publishVersion=${{ inputs.version_to_publish }} \ --skipPackages=${{ inputs.skip_packages }} ${{ (inputs.dry && '') || '\'}} ${{ inputs.dry && '--dry'}} + - name: Archive released package for debugging + uses: actions/upload-artifact@v4 + with: + name: build + path: | + ./build/node_modules From 11ca4f6b6934292168f49cc78d3b2360a602febe Mon Sep 17 00:00:00 2001 From: michael faith Date: Fri, 28 Feb 2025 10:07:13 -0600 Subject: [PATCH 070/300] feat(eslint-plugin-react-hooks): update `engines` declaration (#32458) In preparation for the merging of the compiler plugin into this one (#32416), this change proactively updates the plugin's `engines` declaration to require Node versions greater than or equal to 18 BREAKING CHANGE Co-authored-by: lauren --- packages/eslint-plugin-react-hooks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin-react-hooks/package.json b/packages/eslint-plugin-react-hooks/package.json index d3717b9dee61c..48cb7ae452a8d 100644 --- a/packages/eslint-plugin-react-hooks/package.json +++ b/packages/eslint-plugin-react-hooks/package.json @@ -31,7 +31,7 @@ "main": "./index.js", "types": "./index.d.ts", "engines": { - "node": ">=10" + "node": ">=18" }, "homepage": "https://react.dev/", "peerDependencies": { From d55cc79bcf5cd3bd4cb406381b067d72842f368e Mon Sep 17 00:00:00 2001 From: michael faith Date: Fri, 28 Feb 2025 10:12:10 -0600 Subject: [PATCH 071/300] refactor(eslint-plugin-react-hooks): move rules to `rules` folder (#32411) Since the compiler plugin is going to be merged into the hooks plugin, and ultimately decomposed into several more rules, it would be good to start creating a more traditional folder structure for the plugin. This change just moves the rules into a `rules` folder. Co-authored-by: lauren --- packages/eslint-plugin-react-hooks/README.md | 4 ++-- packages/eslint-plugin-react-hooks/src/index.ts | 4 ++-- .../src/{ => rules}/ExhaustiveDeps.ts | 0 .../eslint-plugin-react-hooks/src/{ => rules}/RulesOfHooks.ts | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename packages/eslint-plugin-react-hooks/src/{ => rules}/ExhaustiveDeps.ts (100%) rename packages/eslint-plugin-react-hooks/src/{ => rules}/RulesOfHooks.ts (100%) diff --git a/packages/eslint-plugin-react-hooks/README.md b/packages/eslint-plugin-react-hooks/README.md index 36f63722f7268..cf54caec986f5 100644 --- a/packages/eslint-plugin-react-hooks/README.md +++ b/packages/eslint-plugin-react-hooks/README.md @@ -36,7 +36,7 @@ If you are still using ESLint below 9.0.0, please continue to use `recommended-l For [ESLint 9.0.0 and above](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/) users, add the `recommended-latest` config. ```js -import reactHooks from 'eslint-plugin-react-hooks'; +import * as reactHooks from 'eslint-plugin-react-hooks'; export default [ // ... @@ -67,7 +67,7 @@ If you want more fine-grained configuration, you can instead add a snippet like #### Flat Config (eslint.config.js) ```js -import reactHooks from 'eslint-plugin-react-hooks'; +import * as reactHooks from 'eslint-plugin-react-hooks'; export default [ { diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts index 42b55320fcd89..5ddbbcbb806fb 100644 --- a/packages/eslint-plugin-react-hooks/src/index.ts +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -4,8 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import RulesOfHooks from './RulesOfHooks'; -import ExhaustiveDeps from './ExhaustiveDeps'; +import RulesOfHooks from './rules/RulesOfHooks'; +import ExhaustiveDeps from './rules/ExhaustiveDeps'; import type {ESLint, Linter, Rule} from 'eslint'; // All rules diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts b/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts similarity index 100% rename from packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.ts rename to packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts diff --git a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts b/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts similarity index 100% rename from packages/eslint-plugin-react-hooks/src/RulesOfHooks.ts rename to packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts From ca12911d1fbe755b9b2b7d1bf548589317311a82 Mon Sep 17 00:00:00 2001 From: michael faith Date: Fri, 28 Feb 2025 10:22:08 -0600 Subject: [PATCH 072/300] feat(eslint-plugin-react-hooks): make flat config the `recommended` config (#32457) This change swaps which config `recommended` is aliasing. In 5.2.0, the new flat config was introduced as `recommended-latest`, while `recommended` still pointed at the legacy rc-based config, with a note that in the next major version `recommended` would be updated to point at `recommend-latest`. This change makes that swap, and make the default `recommended` experience the flat config. To continue using the legacy rc recommended config, please make the following change in your config ```diff - extends: ['plugin:react-hooks/recommended'] + extends: ['plugin:react-hooks/recommended-legacy'] ``` This change also deprecates `recommended-latest` in favor of `recommended`. `recommended-latest` will be removed in a future major version. The README has been updated to reflect the new usage, and to put the flat config sections before the legacy config sections. I also took the opportunity to change the v9 fixture to use a typescript config, serving as a demonstration for usage as well as a way to validate the types are correct. BREAKING CHANGE --------- Co-authored-by: lauren --- .../{eslint.config.mjs => eslint.config.ts} | 5 +- fixtures/eslint-v9/package.json | 5 +- fixtures/eslint-v9/tsconfig.json | 2 + fixtures/eslint-v9/yarn.lock | 76 +++++++++---------- packages/eslint-plugin-react-hooks/README.md | 59 +++++++------- .../eslint-plugin-react-hooks/src/index.ts | 35 +++++---- 6 files changed, 91 insertions(+), 91 deletions(-) rename fixtures/eslint-v9/{eslint.config.mjs => eslint.config.ts} (76%) create mode 100644 fixtures/eslint-v9/tsconfig.json diff --git a/fixtures/eslint-v9/eslint.config.mjs b/fixtures/eslint-v9/eslint.config.ts similarity index 76% rename from fixtures/eslint-v9/eslint.config.mjs rename to fixtures/eslint-v9/eslint.config.ts index ffc8375265c2b..62ef68671639e 100644 --- a/fixtures/eslint-v9/eslint.config.mjs +++ b/fixtures/eslint-v9/eslint.config.ts @@ -1,3 +1,4 @@ +import type {Linter} from 'eslint'; import * as reactHooks from 'eslint-plugin-react-hooks'; export default [ @@ -12,10 +13,10 @@ export default [ }, }, }, - reactHooks.configs['recommended-latest'], + reactHooks.configs['recommended'], { rules: { 'react-hooks/exhaustive-deps': 'error', }, }, -]; +] satisfies Linter.Config[]; diff --git a/fixtures/eslint-v9/package.json b/fixtures/eslint-v9/package.json index 58d0a94c0c58c..80827a0d1730a 100644 --- a/fixtures/eslint-v9/package.json +++ b/fixtures/eslint-v9/package.json @@ -2,8 +2,9 @@ "private": true, "name": "eslint-v9", "dependencies": { - "eslint": "^9", - "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks" + "eslint": "^9.18.0", + "eslint-plugin-react-hooks": "link:../../build/oss-stable/eslint-plugin-react-hooks", + "jiti": "^2.4.2" }, "scripts": { "build": "node build.mjs && yarn", diff --git a/fixtures/eslint-v9/tsconfig.json b/fixtures/eslint-v9/tsconfig.json new file mode 100644 index 0000000000000..2c63c0851048d --- /dev/null +++ b/fixtures/eslint-v9/tsconfig.json @@ -0,0 +1,2 @@ +{ +} diff --git a/fixtures/eslint-v9/yarn.lock b/fixtures/eslint-v9/yarn.lock index 1583b73f14e21..a473b3a4ce165 100644 --- a/fixtures/eslint-v9/yarn.lock +++ b/fixtures/eslint-v9/yarn.lock @@ -14,7 +14,7 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.19.0": +"@eslint/config-array@^0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== @@ -23,24 +23,17 @@ debug "^4.3.1" minimatch "^3.1.2" -"@eslint/core@^0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091" - integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw== +"@eslint/core@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e" + integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg== dependencies: "@types/json-schema" "^7.0.15" -"@eslint/core@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.11.0.tgz#7a9226e850922e42cbd2ba71361eacbe74352a12" - integrity sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/eslintrc@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" - integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== +"@eslint/eslintrc@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846" + integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -52,22 +45,22 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.20.0": - version "9.20.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.20.0.tgz#7421bcbe74889fcd65d1be59f00130c289856eb4" - integrity sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ== +"@eslint/js@9.21.0": + version "9.21.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08" + integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw== "@eslint/object-schema@^2.1.6": version "2.1.6" resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== -"@eslint/plugin-kit@^0.2.5": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81" - integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A== +"@eslint/plugin-kit@^0.2.7": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27" + integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g== dependencies: - "@eslint/core" "^0.10.0" + "@eslint/core" "^0.12.0" levn "^0.4.1" "@humanfs/core@^0.19.1": @@ -93,10 +86,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== -"@humanwhocodes/retry@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" - integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== +"@humanwhocodes/retry@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161" + integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== "@types/estree@^1.0.6": version "1.0.6" @@ -231,21 +224,21 @@ eslint-visitor-keys@^4.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== -eslint@^9: - version "9.20.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6" - integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g== +eslint@^9.18.0: + version "9.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591" + integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.19.0" - "@eslint/core" "^0.11.0" - "@eslint/eslintrc" "^3.2.0" - "@eslint/js" "9.20.0" - "@eslint/plugin-kit" "^0.2.5" + "@eslint/config-array" "^0.19.2" + "@eslint/core" "^0.12.0" + "@eslint/eslintrc" "^3.3.0" + "@eslint/js" "9.21.0" + "@eslint/plugin-kit" "^0.2.7" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.1" + "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" "@types/json-schema" "^7.0.15" ajv "^6.12.4" @@ -399,6 +392,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +jiti@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" + integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== + js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" diff --git a/packages/eslint-plugin-react-hooks/README.md b/packages/eslint-plugin-react-hooks/README.md index cf54caec986f5..e4223c18b20e0 100644 --- a/packages/eslint-plugin-react-hooks/README.md +++ b/packages/eslint-plugin-react-hooks/README.md @@ -18,9 +18,22 @@ npm install eslint-plugin-react-hooks --save-dev yarn add eslint-plugin-react-hooks --dev ``` +### Flat Config (eslint.config.js|ts) + +For [ESLint 9.0.0 and above](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/), add the `recommended` config. + +```js +import * as reactHooks from 'eslint-plugin-react-hooks'; + +export default [ + // ... + reactHooks.configs['recommended'], +]; +``` + ### Legacy Config (.eslintrc) -If you are still using ESLint below 9.0.0, please continue to use `recommended-legacy`. To avoid breaking changes, we still support `recommended` as well, but note that this will be changed to alias the flat recommended config in v6. +If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for accessing the recommended config. ```js { @@ -31,25 +44,29 @@ If you are still using ESLint below 9.0.0, please continue to use `recommended-l } ``` -### Flat Config (eslint.config.js) +### Custom Configuration -For [ESLint 9.0.0 and above](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/) users, add the `recommended-latest` config. +If you want more fine-grained configuration, you can instead add a snippet like this to your ESLint configuration file: + +#### Flat Config (eslint.config.js|ts) ```js import * as reactHooks from 'eslint-plugin-react-hooks'; export default [ - // ... - reactHooks.configs['recommended-latest'], + { + files: ['**/*.{js,jsx}'], + plugins: { 'react-hooks': reactHooks }, + // ... + rules: { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + } + }, ]; ``` -### Custom Configuration - -If you want more fine-grained configuration, you can instead add a snippet like this to your ESLint configuration file: - #### Legacy Config (.eslintrc) - ```js { "plugins": [ @@ -64,24 +81,6 @@ If you want more fine-grained configuration, you can instead add a snippet like } ``` -#### Flat Config (eslint.config.js) - -```js -import * as reactHooks from 'eslint-plugin-react-hooks'; - -export default [ - { - files: ['**/*.{js,jsx}'], - plugins: { 'react-hooks': reactHooks }, - // ... - rules: { - 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'warn', - } - }, -]; -``` - ## Advanced Configuration `exhaustive-deps` can be configured to validate dependencies of custom Hooks with the `additionalHooks` option. @@ -89,10 +88,10 @@ This option accepts a regex to match the names of custom Hooks that have depende ```js { - "rules": { + rules: { // ... "react-hooks/exhaustive-deps": ["warn", { - "additionalHooks": "(useMyCustomHook|useMyOtherCustomHook)" + additionalHooks: "(useMyCustomHook|useMyOtherCustomHook)" }] } } diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts index 5ddbbcbb806fb..61ac62912ddc2 100644 --- a/packages/eslint-plugin-react-hooks/src/index.ts +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -20,11 +20,16 @@ const configRules = { 'react-hooks/exhaustive-deps': 'warn', } satisfies Linter.RulesRecord; -// Legacy config -const legacyRecommendedConfig = { - plugins: ['react-hooks'], +// Flat config +const recommendedConfig = { + name: 'react-hooks/recommended', + plugins: { + get 'react-hooks'(): ESLint.Plugin { + return plugin; + }, + }, rules: configRules, -} satisfies Linter.LegacyConfig; +}; // Plugin object const plugin = { @@ -34,24 +39,18 @@ const plugin = { rules, configs: { /** Legacy recommended config, to be used with rc-based configurations */ - 'recommended-legacy': legacyRecommendedConfig, + 'recommended-legacy': { + plugins: ['react-hooks'], + rules: configRules, + }, /** - * 'recommended' is currently aliased to the legacy / rc recommended config) to maintain backwards compatibility. - * This is deprecated and in v6, it will switch to alias the flat recommended config. + * Recommended config, to be used with flat configs. */ - recommended: legacyRecommendedConfig, + recommended: recommendedConfig, - /** Latest recommended config, to be used with flat configurations */ - 'recommended-latest': { - name: 'react-hooks/recommended', - plugins: { - get 'react-hooks'(): ESLint.Plugin { - return plugin; - }, - }, - rules: configRules, - }, + /** @deprecated please use `recommended`; will be removed in v7 */ + 'recommended-latest': recommendedConfig, }, } satisfies ESLint.Plugin; From eda36a1c75ff8ac09fb127f6e04d4af16e49f50f Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 28 Feb 2025 13:06:40 -0500 Subject: [PATCH 073/300] [ci] Don't erroneously mark failures as successes (#32493) Randomly noticed this when I looked at a recent [DevTools regression test run](https://github.com/facebook/react/actions/runs/13578385011). I don't recall why we added `continue-on-error` previously, but I believe it was to keep all jobs in the matrix running even if one were to fail, in order to fully identify any failures from code changes like build or test failures. There is now a `fail-fast` option which does this. [`continue-on-error`](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idcontinue-on-error) now means: > Prevents a workflow run from failing when a job fails. Set to true to allow a workflow run to pass when this job fails. so it's not correct to use it. --- .github/workflows/compiler_typescript.yml | 2 +- .github/workflows/devtools_regression_tests.yml | 4 ++-- .github/workflows/runtime_build_and_test.yml | 8 +++++--- .github/workflows/runtime_eslint_plugin_e2e.yml | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index 6d9fa973350af..65fb789eccf17 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -75,8 +75,8 @@ jobs: name: Test ${{ matrix.workspace_name }} needs: discover_yarn_workspaces runs-on: ubuntu-latest - continue-on-error: true strategy: + fail-fast: false matrix: workspace_name: ${{ fromJSON(needs.discover_yarn_workspaces.outputs.matrix) }} steps: diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 64d6707aa3688..4babfeefb0f2c 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -100,6 +100,7 @@ jobs: needs: build_devtools_and_process_artifacts runs-on: ubuntu-latest strategy: + fail-fast: false matrix: version: - "16.0" @@ -108,7 +109,6 @@ jobs: - "17.0" - "18.0" - "18.2" # compiler polyfill - continue-on-error: true steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -135,6 +135,7 @@ jobs: needs: build_devtools_and_process_artifacts runs-on: ubuntu-latest strategy: + fail-fast: false matrix: version: - "16.0" @@ -142,7 +143,6 @@ jobs: - "16.8" # hooks - "17.0" - "18.0" - continue-on-error: true steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 12e341a86fd96..d53e352fbb266 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -38,8 +38,8 @@ jobs: name: Flow check ${{ matrix.flow_inline_config_shortname }} needs: discover_flow_inline_configs runs-on: ubuntu-latest - continue-on-error: true strategy: + fail-fast: false matrix: flow_inline_config_shortname: ${{ fromJSON(needs.discover_flow_inline_configs.outputs.matrix) }} steps: @@ -117,6 +117,7 @@ jobs: name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) runs-on: ubuntu-latest strategy: + fail-fast: false matrix: params: - "-r=stable --env=development" @@ -144,7 +145,6 @@ jobs: - 3/5 - 4/5 - 5/5 - continue-on-error: true steps: - uses: actions/checkout@v4 with: @@ -170,6 +170,7 @@ jobs: name: yarn build and lint runs-on: ubuntu-latest strategy: + fail-fast: false matrix: # yml is dumb. update the --total arg to yarn build if you change the number of workers worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] @@ -215,6 +216,7 @@ jobs: name: yarn test-build needs: build_and_lint strategy: + fail-fast: false matrix: test_params: [ # Intentionally passing these as strings instead of creating a @@ -250,7 +252,6 @@ jobs: - 1/3 - 2/3 - 3/3 - continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -500,6 +501,7 @@ jobs: needs: build_and_lint runs-on: ubuntu-latest strategy: + fail-fast: false matrix: browser: [chrome, firefox, edge] steps: diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index edc188f38622e..f8878548c0597 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -20,13 +20,13 @@ jobs: name: ESLint v${{ matrix.eslint_major }} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: eslint_major: - "6" - "7" - "8" - "9" - continue-on-error: true steps: - uses: actions/checkout@v4 with: From 2980f27779cf37a9656b25418a3c5cfca989e244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Sat, 1 Mar 2025 18:26:48 -0500 Subject: [PATCH 074/300] Add a shorthand for building the view-transition fixture (#32496) I end up rebuilding for testing the view-transition fixture a lot. It doesn't need everything that flight needs so this just adds a short hand that's a little faster to rebuild. --------- Co-authored-by: Hendrik Liebau --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0a2b6c3024c93..156eba91a7a05 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV", "build-for-devtools-prod": "yarn build-for-devtools --type=NODE_PROD", "build-for-flight-dev": "cross-env RELEASE_CHANNEL=experimental node ./scripts/rollup/build.js react/index,react/jsx,react.react-server,react-dom/index,react-dom/client,react-dom/server,react-dom.react-server,react-dom-server.node,react-dom-server-legacy.node,scheduler,react-server-dom-webpack/ --type=NODE_DEV,ESM_PROD,NODE_ES2015 && mv ./build/node_modules ./build/oss-experimental", + "build-for-vt-dev": "cross-env RELEASE_CHANNEL=experimental node ./scripts/rollup/build.js react/index,react/jsx,react-dom/index,react-dom/client,react-dom/server,react-dom-server.node,react-dom-server-legacy.node,scheduler --type=NODE_DEV && mv ./build/node_modules ./build/oss-experimental", "linc": "node ./scripts/tasks/linc.js", "lint": "node ./scripts/tasks/eslint.js", "lint-build": "node ./scripts/rollup/validate/index.js", From 605a880c8c5191e9f8c52468458709cd17a486c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 3 Mar 2025 14:24:37 -0500 Subject: [PATCH 075/300] Polyfill onScrollEnd Event in Safari (#32427) We added support for `onScrollEnd` in #26789 but it only works in Chrome and Firefox. Safari still doesn't support `scrollend` and there's no indication that they will anytime soon so this polyfills it. While I don't particularly love our synthetic event system this tries to stay within the realm of how our other polyfills work. This implements all `onScrollEnd` events as a plugin. The basic principle is to first feature detect the `onscrollend` DOM property to see if there's native support and otherwise just use the native event. Then we listen to `scroll` events and set a timeout. If we don't get any more scroll events before the timeout we fire `onScrollEnd`. Basically debouncing it. If we're currently pressing down on touch or a mouse then we wait until it is lifted such as if you're scrolling with a finger or using the scrollbars on desktop but isn't currently moving. If we do get any native events even though we're in polyfilling mode, we use that as an indication to fire the `onScrollEnd` early. Part of the motivation is that this becomes extra useful pair for https://github.com/facebook/react/pull/32422. We also probably need these events to coincide with other gesture related internals so you're better off using our polyfill so they're synced. --- .../src/client/ReactDOMComponent.js | 17 +- .../src/client/ReactDOMComponentTree.js | 13 ++ .../src/events/DOMEventProperties.js | 10 +- .../src/events/DOMPluginEventSystem.js | 24 +- .../events/plugins/ScrollEndEventPlugin.js | 212 ++++++++++++++++++ packages/shared/ReactFeatureFlags.js | 2 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + ...actFeatureFlags.test-renderer.native-fb.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 2 + 12 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 packages/react-dom-bindings/src/events/plugins/ScrollEndEventPlugin.js diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponent.js b/packages/react-dom-bindings/src/client/ReactDOMComponent.js index 15a4ba675b5e0..efdfc66ff21f9 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMComponent.js +++ b/packages/react-dom-bindings/src/client/ReactDOMComponent.js @@ -65,7 +65,10 @@ import sanitizeURL from '../shared/sanitizeURL'; import {trackHostMutation} from 'react-reconciler/src/ReactFiberMutationTracking'; -import {enableTrustedTypesIntegration} from 'shared/ReactFeatureFlags'; +import { + enableScrollEndPolyfill, + enableTrustedTypesIntegration, +} from 'shared/ReactFeatureFlags'; import { mediaEventTypes, listenToNonDelegatedEvent, @@ -545,6 +548,10 @@ function setProp( warnForInvalidEventListener(key, value); } listenToNonDelegatedEvent('scrollend', domElement); + if (enableScrollEndPolyfill) { + // For use by the polyfill. + listenToNonDelegatedEvent('scroll', domElement); + } } return; } @@ -955,6 +962,10 @@ function setPropOnCustomElement( warnForInvalidEventListener(key, value); } listenToNonDelegatedEvent('scrollend', domElement); + if (enableScrollEndPolyfill) { + // For use by the polyfill. + listenToNonDelegatedEvent('scroll', domElement); + } } return; } @@ -3058,6 +3069,10 @@ export function hydrateProperties( if (props.onScrollEnd != null) { listenToNonDelegatedEvent('scrollend', domElement); + if (enableScrollEndPolyfill) { + // For use by the polyfill. + listenToNonDelegatedEvent('scroll', domElement); + } } if (props.onClick != null) { diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js b/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js index 39d47a369ecc6..3d60caeda000d 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js +++ b/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js @@ -45,6 +45,7 @@ const internalEventHandlerListenersKey = '__reactListeners$' + randomKey; const internalEventHandlesSetKey = '__reactHandles$' + randomKey; const internalRootNodeResourcesKey = '__reactResources$' + randomKey; const internalHoistableMarker = '__reactMarker$' + randomKey; +const internalScrollTimer = '__reactScroll$' + randomKey; export function detachDeletedInstance(node: Instance): void { // TODO: This function is only called on host components. I don't think all of @@ -293,6 +294,18 @@ export function markNodeAsHoistable(node: Node) { (node: any)[internalHoistableMarker] = true; } +export function getScrollEndTimer(node: EventTarget): ?TimeoutID { + return (node: any)[internalScrollTimer]; +} + +export function setScrollEndTimer(node: EventTarget, timer: TimeoutID): void { + (node: any)[internalScrollTimer] = timer; +} + +export function clearScrollEndTimer(node: EventTarget): void { + (node: any)[internalScrollTimer] = undefined; +} + export function isOwnedInstance(node: Node): boolean { return !!( (node: any)[internalHoistableMarker] || (node: any)[internalInstanceKey] diff --git a/packages/react-dom-bindings/src/events/DOMEventProperties.js b/packages/react-dom-bindings/src/events/DOMEventProperties.js index 7dc4bee901076..534cae3045e37 100644 --- a/packages/react-dom-bindings/src/events/DOMEventProperties.js +++ b/packages/react-dom-bindings/src/events/DOMEventProperties.js @@ -20,7 +20,10 @@ import { TRANSITION_END, } from './DOMEventNames'; -import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags'; +import { + enableCreateEventHandleAPI, + enableScrollEndPolyfill, +} from 'shared/ReactFeatureFlags'; export const topLevelEventsToReactNames: Map = new Map(); @@ -100,13 +103,16 @@ const simpleEventPluginEvents = [ 'touchStart', 'volumeChange', 'scroll', - 'scrollEnd', 'toggle', 'touchMove', 'waiting', 'wheel', ]; +if (!enableScrollEndPolyfill) { + simpleEventPluginEvents.push('scrollEnd'); +} + if (enableCreateEventHandleAPI) { // Special case: these two events don't have on* React handler // and are only accessible via the createEventHandle API. diff --git a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js index c99115df90f56..c813ee16157a1 100644 --- a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js @@ -54,6 +54,7 @@ import { enableScopeAPI, enableOwnerStacks, disableCommentsAsDOMContainers, + enableScrollEndPolyfill, } from 'shared/ReactFeatureFlags'; import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener'; import { @@ -69,6 +70,7 @@ import * as EnterLeaveEventPlugin from './plugins/EnterLeaveEventPlugin'; import * as SelectEventPlugin from './plugins/SelectEventPlugin'; import * as SimpleEventPlugin from './plugins/SimpleEventPlugin'; import * as FormActionEventPlugin from './plugins/FormActionEventPlugin'; +import * as ScrollEndEventPlugin from './plugins/ScrollEndEventPlugin'; import reportGlobalError from 'shared/reportGlobalError'; @@ -93,6 +95,9 @@ EnterLeaveEventPlugin.registerEvents(); ChangeEventPlugin.registerEvents(); SelectEventPlugin.registerEvents(); BeforeInputEventPlugin.registerEvents(); +if (enableScrollEndPolyfill) { + ScrollEndEventPlugin.registerEvents(); +} function extractEvents( dispatchQueue: DispatchQueue, @@ -184,6 +189,17 @@ function extractEvents( targetContainer, ); } + if (enableScrollEndPolyfill) { + ScrollEndEventPlugin.extractEvents( + dispatchQueue, + domEventName, + targetInst, + nativeEvent, + nativeEventTarget, + eventSystemFlags, + targetContainer, + ); + } } // List of events that need to be individually attached to media elements. @@ -811,6 +827,7 @@ export function accumulateSinglePhaseListeners( // - BeforeInputEventPlugin // - ChangeEventPlugin // - SelectEventPlugin +// - ScrollEndEventPlugin // This is because we only process these plugins // in the bubble phase, so we need to accumulate two // phase event listeners (via emulation). @@ -846,9 +863,14 @@ export function accumulateTwoPhaseListeners( ); } } + if (instance.tag === HostRoot) { + return listeners; + } instance = instance.return; } - return listeners; + // If we didn't reach the root it means we're unmounted and shouldn't + // dispatch any events on the target. + return []; } function getParent(inst: Fiber | null): Fiber | null { diff --git a/packages/react-dom-bindings/src/events/plugins/ScrollEndEventPlugin.js b/packages/react-dom-bindings/src/events/plugins/ScrollEndEventPlugin.js new file mode 100644 index 0000000000000..6bb1c68ab5585 --- /dev/null +++ b/packages/react-dom-bindings/src/events/plugins/ScrollEndEventPlugin.js @@ -0,0 +1,212 @@ +/** + * 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 type {AnyNativeEvent} from '../PluginModuleType'; +import type {DOMEventName} from '../DOMEventNames'; +import type {DispatchQueue} from '../DOMPluginEventSystem'; +import type {EventSystemFlags} from '../EventSystemFlags'; +import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; +import type {ReactSyntheticEvent} from '../ReactSyntheticEventType'; + +import {registerTwoPhaseEvent} from '../EventRegistry'; +import {SyntheticUIEvent} from '../SyntheticEvent'; + +import {canUseDOM} from 'shared/ExecutionEnvironment'; +import isEventSupported from '../isEventSupported'; + +import {IS_CAPTURE_PHASE} from '../EventSystemFlags'; + +import {batchedUpdates} from '../ReactDOMUpdateBatching'; +import { + processDispatchQueue, + accumulateSinglePhaseListeners, + accumulateTwoPhaseListeners, +} from '../DOMPluginEventSystem'; + +import { + getScrollEndTimer, + setScrollEndTimer, + clearScrollEndTimer, +} from '../../client/ReactDOMComponentTree'; + +import {enableScrollEndPolyfill} from 'shared/ReactFeatureFlags'; + +const isScrollEndEventSupported = + enableScrollEndPolyfill && canUseDOM && isEventSupported('scrollend'); + +let isTouchStarted = false; +let isMouseDown = false; + +function registerEvents() { + registerTwoPhaseEvent('onScrollEnd', [ + 'scroll', + 'scrollend', + 'touchstart', + 'touchcancel', + 'touchend', + 'mousedown', + 'mouseup', + ]); +} + +function manualDispatchScrollEndEvent( + inst: Fiber, + nativeEvent: AnyNativeEvent, + target: EventTarget, +) { + const dispatchQueue: DispatchQueue = []; + const listeners = accumulateTwoPhaseListeners(inst, 'onScrollEnd'); + if (listeners.length > 0) { + const event: ReactSyntheticEvent = new SyntheticUIEvent( + 'onScrollEnd', + 'scrollend', + null, + nativeEvent, // This will be the "scroll" event. + target, + ); + dispatchQueue.push({event, listeners}); + } + batchedUpdates(runEventInBatch, dispatchQueue); +} + +function runEventInBatch(dispatchQueue: DispatchQueue) { + processDispatchQueue(dispatchQueue, 0); +} + +function fireScrollEnd( + targetInst: Fiber, + nativeEvent: AnyNativeEvent, + nativeEventTarget: EventTarget, +): void { + clearScrollEndTimer(nativeEventTarget); + if (isMouseDown || isTouchStarted) { + // If mouse or touch is down, try again later in case this is due to having an + // active scroll but it's not currently moving. + debounceScrollEnd(targetInst, nativeEvent, nativeEventTarget); + return; + } + manualDispatchScrollEndEvent(targetInst, nativeEvent, nativeEventTarget); +} + +// When scrolling slows down the frequency of new scroll events can be quite low. +// This timeout seems high enough to cover those cases but short enough to not +// fire the event way too late. +const DEBOUNCE_TIMEOUT = 200; + +function debounceScrollEnd( + targetInst: null | Fiber, + nativeEvent: AnyNativeEvent, + nativeEventTarget: EventTarget, +) { + const existingTimer = getScrollEndTimer(nativeEventTarget); + if (existingTimer != null) { + clearTimeout(existingTimer); + } + if (targetInst !== null) { + const newTimer = setTimeout( + fireScrollEnd.bind(null, targetInst, nativeEvent, nativeEventTarget), + DEBOUNCE_TIMEOUT, + ); + setScrollEndTimer(nativeEventTarget, newTimer); + } +} + +/** + * This plugin creates an `onScrollEnd` event polyfill when the native one + * is not available. + */ +function extractEvents( + dispatchQueue: DispatchQueue, + domEventName: DOMEventName, + targetInst: null | Fiber, + nativeEvent: AnyNativeEvent, + nativeEventTarget: null | EventTarget, + eventSystemFlags: EventSystemFlags, + targetContainer: null | EventTarget, +) { + if (!enableScrollEndPolyfill) { + return; + } + + const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0; + + if (domEventName !== 'scrollend') { + if (!isScrollEndEventSupported && inCapturePhase) { + switch (domEventName) { + case 'scroll': { + if (nativeEventTarget !== null) { + debounceScrollEnd(targetInst, nativeEvent, nativeEventTarget); + } + break; + } + case 'touchstart': { + isTouchStarted = true; + break; + } + case 'touchcancel': + case 'touchend': { + // Note we cannot use pointer events for this because they get + // cancelled when native scrolling takes control. + isTouchStarted = false; + break; + } + case 'mousedown': { + isMouseDown = true; + break; + } + case 'mouseup': { + isMouseDown = false; + break; + } + } + } + return; + } + + if (!isScrollEndEventSupported && nativeEventTarget !== null) { + const existingTimer = getScrollEndTimer(nativeEventTarget); + if (existingTimer != null) { + // If we do get a native scrollend event fired, we cancel the polyfill. + // This could happen if our feature detection is broken or if there's another + // polyfill calling dispatchEvent to fire it before we fire ours. + clearTimeout(existingTimer); + clearScrollEndTimer(nativeEventTarget); + } else { + // If we didn't receive a 'scroll' event first, we ignore this event to avoid + // double firing. Such as if we fired our onScrollEnd polyfill and then + // we also observed a native one afterwards. + return; + } + } + + // In React onScrollEnd doesn't bubble. + const accumulateTargetOnly = !inCapturePhase; + + const listeners = accumulateSinglePhaseListeners( + targetInst, + 'onScrollEnd', + 'scrollend', + inCapturePhase, + accumulateTargetOnly, + nativeEvent, + ); + + if (listeners.length > 0) { + // Intentionally create event lazily. + const event: ReactSyntheticEvent = new SyntheticUIEvent( + 'onScrollEnd', + 'scrollend', + null, + nativeEvent, + nativeEventTarget, + ); + dispatchQueue.push({event, listeners}); + } +} + +export {registerEvents, extractEvents}; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 1a2c260b5ba2d..93cd9a4a80edb 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -94,6 +94,8 @@ export const enableViewTransition = __EXPERIMENTAL__; export const enableSwipeTransition = __EXPERIMENTAL__; +export const enableScrollEndPolyfill = __EXPERIMENTAL__; + /** * Switches the Fabric API from doing layout in commit work instead of complete work. */ diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 8d72f54fc94cd..81f830cb6e3f4 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -85,6 +85,7 @@ export const enableYieldingBeforePassive = false; export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableSwipeTransition = false; +export const enableScrollEndPolyfill = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index ea29191274af1..3a3874f6e2b22 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -75,6 +75,7 @@ export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; +export const enableScrollEndPolyfill = true; // Profiling Only export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 821a577026c32..3212b679c0279 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -74,6 +74,7 @@ export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = true; export const enableLazyPublicInstanceInFabric = false; +export const enableScrollEndPolyfill = true; // TODO: This must be in sync with the main ReactFeatureFlags file because // the Test Renderer's value must be the same as the one used by the diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index c239f61b78b6b..f024f9ef0f972 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -71,6 +71,7 @@ export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; +export const enableScrollEndPolyfill = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 443bed04b790f..30ab2da252a57 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -86,6 +86,7 @@ export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; +export const enableScrollEndPolyfill = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index de1f2f316bb53..0f790135d2aff 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -116,5 +116,7 @@ export const enableLazyPublicInstanceInFabric = false; export const enableSwipeTransition = false; +export const enableScrollEndPolyfill = false; + // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); From a1f157e9a9b7f90fce9d696add6cee04a6fb5260 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 3 Mar 2025 14:39:03 -0500 Subject: [PATCH 076/300] [compiler][ez] Add validation for auto-deps config (#31813) numRequiredArgs has to be more than 0 and the pass depends on that -- --- .../src/HIR/Environment.ts | 2 +- .../src/__tests__/envConfig-test.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 8de9218a662e2..b61243f0424ad 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -285,7 +285,7 @@ const EnvironmentConfigSchema = z.object({ z.array( z.object({ function: ExternalFunctionSchema, - numRequiredArgs: z.number(), + numRequiredArgs: z.number().min(1, 'numRequiredArgs must be > 0'), }), ), ) diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts index 02715601bdc63..a96af5b3918be 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/envConfig-test.ts @@ -24,6 +24,24 @@ describe('parseConfigPragma()', () => { ); }); + it('effect autodeps config must have at least 1 required argument', () => { + expect(() => { + validateEnvironmentConfig({ + inferEffectDependencies: [ + { + function: { + source: 'react', + importSpecifierName: 'useEffect', + }, + numRequiredArgs: 0, + }, + ], + } as any); + }).toThrowErrorMatchingInlineSnapshot( + `"InvalidConfig: Could not validate environment config. Update React Compiler config to fix the error. Validation error: numRequiredArgs must be > 0 at "inferEffectDependencies[0].numRequiredArgs""`, + ); + }); + it('can parse stringy enums', () => { const stringyHook = { effectKind: 'freeze', From bdce84a53960c267f92a08241dc7b1924f30be55 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 3 Mar 2025 15:20:41 -0500 Subject: [PATCH 077/300] [autodeps] Support namespaces (#32162) Summary: Correctly supports React.useEffect when React is imported as `import * as React from 'react'` (as well as other namespaces as specified in the config). --- .../src/Inference/InferEffectDependencies.ts | 172 ++++++++++-------- .../import-namespace-useEffect.expect.md | 59 ++++++ .../import-namespace-useEffect.js | 9 + ...port-default-property-useEffect.expect.md} | 10 +- ...todo.import-default-property-useEffect.js} | 5 +- 5 files changed, 172 insertions(+), 83 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/{todo-import-namespace-useEffect.expect.md => todo.import-default-property-useEffect.expect.md} (82%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/{todo-import-namespace-useEffect.js => todo.import-default-property-useEffect.js} (65%) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts index 8c9823765bbb6..7e317e6907961 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts @@ -49,6 +49,7 @@ export function inferEffectDependencies(fn: HIRFunction): void { ); } const autodepFnLoads = new Map(); + const autodepModuleLoads = new Map>(); const scopeInfos = new Map< ScopeId, @@ -89,9 +90,34 @@ export function inferEffectDependencies(fn: HIRFunction): void { lvalue.identifier.id, instr as TInstruction, ); + } else if (value.kind === 'PropertyLoad') { + if ( + typeof value.property === 'string' && + autodepModuleLoads.has(value.object.identifier.id) + ) { + const moduleTargets = autodepModuleLoads.get( + value.object.identifier.id, + )!; + const propertyName = value.property; + const numRequiredArgs = moduleTargets.get(propertyName); + if (numRequiredArgs != null) { + autodepFnLoads.set(lvalue.identifier.id, numRequiredArgs); + } + } } else if (value.kind === 'LoadGlobal') { loadGlobals.add(lvalue.identifier.id); + /* + * TODO: Handle properties on default exports, like + * import React from 'react'; + * React.useEffect(...); + */ + if (value.binding.kind === 'ImportNamespace') { + const moduleTargets = autodepFnConfigs.get(value.binding.module); + if (moduleTargets != null) { + autodepModuleLoads.set(lvalue.identifier.id, moduleTargets); + } + } if ( value.binding.kind === 'ImportSpecifier' || value.binding.kind === 'ImportDefault' @@ -109,84 +135,88 @@ export function inferEffectDependencies(fn: HIRFunction): void { } } } else if ( - /* - * TODO: Handle method calls - */ - value.kind === 'CallExpression' && - autodepFnLoads.get(value.callee.identifier.id) === value.args.length && - value.args[0].kind === 'Identifier' + value.kind === 'CallExpression' || + value.kind === 'MethodCall' ) { - const effectDeps: Array = []; - const newInstructions: Array = []; - const deps: ArrayExpression = { - kind: 'ArrayExpression', - elements: effectDeps, - loc: GeneratedSource, - }; - const depsPlace = createTemporaryPlace(fn.env, GeneratedSource); - depsPlace.effect = Effect.Read; + const callee = + value.kind === 'CallExpression' ? value.callee : value.property; + if ( + value.args.length === autodepFnLoads.get(callee.identifier.id) && + value.args[0].kind === 'Identifier' + ) { + // We have a useEffect call with no deps array, so we need to infer the deps + const effectDeps: Array = []; + const newInstructions: Array = []; + const deps: ArrayExpression = { + kind: 'ArrayExpression', + elements: effectDeps, + loc: GeneratedSource, + }; + const depsPlace = createTemporaryPlace(fn.env, GeneratedSource); + depsPlace.effect = Effect.Read; + + const fnExpr = fnExpressions.get(value.args[0].identifier.id); + if (fnExpr != null) { + // We have a function expression, so we can infer its dependencies + const scopeInfo = + fnExpr.lvalue.identifier.scope != null + ? scopeInfos.get(fnExpr.lvalue.identifier.scope.id) + : null; + CompilerError.invariant(scopeInfo != null, { + reason: 'Expected function expression scope to exist', + loc: value.loc, + }); + if (scopeInfo.pruned || !scopeInfo.hasSingleInstr) { + /** + * TODO: retry pipeline that ensures effect function expressions + * are placed into their own scope + */ + CompilerError.throwTodo({ + reason: + '[InferEffectDependencies] Expected effect function to have non-pruned scope and its scope to have exactly one instruction', + loc: fnExpr.loc, + }); + } - const fnExpr = fnExpressions.get(value.args[0].identifier.id); - if (fnExpr != null) { - // We have a function expression, so we can infer its dependencies - const scopeInfo = - fnExpr.lvalue.identifier.scope != null - ? scopeInfos.get(fnExpr.lvalue.identifier.scope.id) - : null; - CompilerError.invariant(scopeInfo != null, { - reason: 'Expected function expression scope to exist', - loc: value.loc, - }); - if (scopeInfo.pruned || !scopeInfo.hasSingleInstr) { /** - * TODO: retry pipeline that ensures effect function expressions - * are placed into their own scope + * Step 1: push dependencies to the effect deps array + * + * Note that it's invalid to prune non-reactive deps in this pass, see + * the `infer-effect-deps/pruned-nonreactive-obj` fixture for an + * explanation. */ - CompilerError.throwTodo({ - reason: - '[InferEffectDependencies] Expected effect function to have non-pruned scope and its scope to have exactly one instruction', - loc: fnExpr.loc, + for (const dep of scopeInfo.deps) { + const {place, instructions} = writeDependencyToInstructions( + dep, + reactiveIds.has(dep.identifier.id), + fn.env, + fnExpr.loc, + ); + newInstructions.push(...instructions); + effectDeps.push(place); + } + + newInstructions.push({ + id: makeInstructionId(0), + loc: GeneratedSource, + lvalue: {...depsPlace, effect: Effect.Mutate}, + value: deps, }); - } - /** - * Step 1: push dependencies to the effect deps array - * - * Note that it's invalid to prune non-reactive deps in this pass, see - * the `infer-effect-deps/pruned-nonreactive-obj` fixture for an - * explanation. - */ - for (const dep of scopeInfo.deps) { - const {place, instructions} = writeDependencyToInstructions( - dep, - reactiveIds.has(dep.identifier.id), - fn.env, - fnExpr.loc, - ); - newInstructions.push(...instructions); - effectDeps.push(place); + // Step 2: push the inferred deps array as an argument of the useEffect + value.args.push({...depsPlace, effect: Effect.Freeze}); + rewriteInstrs.set(instr.id, newInstructions); + } else if (loadGlobals.has(value.args[0].identifier.id)) { + // Global functions have no reactive dependencies, so we can insert an empty array + newInstructions.push({ + id: makeInstructionId(0), + loc: GeneratedSource, + lvalue: {...depsPlace, effect: Effect.Mutate}, + value: deps, + }); + value.args.push({...depsPlace, effect: Effect.Freeze}); + rewriteInstrs.set(instr.id, newInstructions); } - - newInstructions.push({ - id: makeInstructionId(0), - loc: GeneratedSource, - lvalue: {...depsPlace, effect: Effect.Mutate}, - value: deps, - }); - - // Step 2: push the inferred deps array as an argument of the useEffect - value.args.push({...depsPlace, effect: Effect.Freeze}); - rewriteInstrs.set(instr.id, newInstructions); - } else if (loadGlobals.has(value.args[0].identifier.id)) { - // Global functions have no reactive dependencies, so we can insert an empty array - newInstructions.push({ - id: makeInstructionId(0), - loc: GeneratedSource, - lvalue: {...depsPlace, effect: Effect.Mutate}, - value: deps, - }); - value.args.push({...depsPlace, effect: Effect.Freeze}); - rewriteInstrs.set(instr.id, newInstructions); } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.expect.md new file mode 100644 index 0000000000000..3ae6766fc7e29 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +// @inferEffectDependencies +import * as React from 'react'; +import * as SharedRuntime from 'shared-runtime'; + +function NonReactiveDepInEffect() { + const obj = makeObject_Primitives(); + React.useEffect(() => print(obj)); + SharedRuntime.useSpecialEffect(() => print(obj), [obj]); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies +import * as React from "react"; +import * as SharedRuntime from "shared-runtime"; + +function NonReactiveDepInEffect() { + const $ = _c(4); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = makeObject_Primitives(); + $[0] = t0; + } else { + t0 = $[0]; + } + const obj = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => print(obj); + $[1] = t1; + } else { + t1 = $[1]; + } + React.useEffect(t1, [obj]); + let t2; + let t3; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => print(obj); + t3 = [obj]; + $[2] = t2; + $[3] = t3; + } else { + t2 = $[2]; + t3 = $[3]; + } + SharedRuntime.useSpecialEffect(t2, t3, [obj]); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.js new file mode 100644 index 0000000000000..e440cfcc2302a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/import-namespace-useEffect.js @@ -0,0 +1,9 @@ +// @inferEffectDependencies +import * as React from 'react'; +import * as SharedRuntime from 'shared-runtime'; + +function NonReactiveDepInEffect() { + const obj = makeObject_Primitives(); + React.useEffect(() => print(obj)); + SharedRuntime.useSpecialEffect(() => print(obj), [obj]); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md similarity index 82% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md index 6302067a5a5eb..a5a576c83067a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md @@ -3,11 +3,8 @@ ```javascript // @inferEffectDependencies -import * as React from 'react'; +import React from 'react'; -/** - * TODO: recognize import namespace - */ function NonReactiveDepInEffect() { const obj = makeObject_Primitives(); React.useEffect(() => print(obj)); @@ -19,11 +16,8 @@ function NonReactiveDepInEffect() { ```javascript import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies -import * as React from "react"; +import React from "react"; -/** - * TODO: recognize import namespace - */ function NonReactiveDepInEffect() { const $ = _c(2); let t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js similarity index 65% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js index 4c9eec898614f..0dbae754ecf76 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js @@ -1,9 +1,6 @@ // @inferEffectDependencies -import * as React from 'react'; +import React from 'react'; -/** - * TODO: recognize import namespace - */ function NonReactiveDepInEffect() { const obj = makeObject_Primitives(); React.useEffect(() => print(obj)); From d4e24b349e6530a8e6c95d79ad40b32f93b47070 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 3 Mar 2025 15:26:57 -0500 Subject: [PATCH 078/300] [autodeps] Do not include nonreactive refs or setStates in inferred deps (#32236) --- .../src/Inference/InferEffectDependencies.ts | 13 +++- .../helper-nonreactive.expect.md | 49 ++++++++++++++ .../helper-nonreactive.js | 12 ++++ .../infer-effect-dependencies.expect.md | 1 - .../nonreactive-ref.expect.md | 2 +- .../nonreactive-setState.expect.md | 51 ++++++++++++++ .../nonreactive-setState.js | 14 ++++ .../reactive-ref.expect.md | 66 +++++++++++++++++++ .../infer-effect-dependencies/reactive-ref.js | 18 +++++ .../reactive-setState.expect.md | 60 +++++++++++++++++ .../reactive-setState.js | 18 +++++ 11 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts index 7e317e6907961..4d295aad060f9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts @@ -15,6 +15,8 @@ import { ReactiveScopeDependency, Place, ReactiveScopeDependencies, + isUseRefType, + isSetStateType, } from '../HIR'; import {DEFAULT_EXPORT} from '../HIR/Environment'; import { @@ -181,11 +183,20 @@ export function inferEffectDependencies(fn: HIRFunction): void { /** * Step 1: push dependencies to the effect deps array * - * Note that it's invalid to prune non-reactive deps in this pass, see + * Note that it's invalid to prune all non-reactive deps in this pass, see * the `infer-effect-deps/pruned-nonreactive-obj` fixture for an * explanation. */ for (const dep of scopeInfo.deps) { + if ( + (isUseRefType(dep.identifier) || + isSetStateType(dep.identifier)) && + !reactiveIds.has(dep.identifier.id) + ) { + // exclude non-reactive hook results, which will never be in a memo block + continue; + } + const {place, instructions} = writeDependencyToInstructions( dep, reactiveIds.has(dep.identifier.id), diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.expect.md new file mode 100644 index 0000000000000..8ae87923437df --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.expect.md @@ -0,0 +1,49 @@ + +## Input + +```javascript +// @inferEffectDependencies +import {useEffect, useRef} from 'react'; +function useCustomRef() { + const ref = useRef(); + return ref; +} +function NonReactiveWrapper() { + const ref = useCustomRef(); + useEffect(() => { + print(ref); + }); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies +import { useEffect, useRef } from "react"; +function useCustomRef() { + const ref = useRef(); + return ref; +} + +function NonReactiveWrapper() { + const $ = _c(2); + const ref = useCustomRef(); + let t0; + if ($[0] !== ref) { + t0 = () => { + print(ref); + }; + $[0] = ref; + $[1] = t0; + } else { + t0 = $[1]; + } + useEffect(t0, [ref]); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.js new file mode 100644 index 0000000000000..cdbc7b748ea49 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/helper-nonreactive.js @@ -0,0 +1,12 @@ +// @inferEffectDependencies +import {useEffect, useRef} from 'react'; +function useCustomRef() { + const ref = useRef(); + return ref; +} +function NonReactiveWrapper() { + const ref = useCustomRef(); + useEffect(() => { + print(ref); + }); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md index 42dd999b3f6e8..89da346a72c50 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md @@ -101,7 +101,6 @@ function Component(t0) { useEffect(t3, [ foo, bar, - ref, localNonPrimitiveReactive, localNonPrimitiveNonreactive, ]); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-ref.expect.md index cf1ebd9252f6a..ebe1a5f780387 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-ref.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-ref.expect.md @@ -42,7 +42,7 @@ function NonReactiveRefInEffect() { } else { t0 = $[0]; } - useEffect(t0, [ref]); + useEffect(t0, []); } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.expect.md new file mode 100644 index 0000000000000..12935c3b950a0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.expect.md @@ -0,0 +1,51 @@ + +## Input + +```javascript +// @inferEffectDependencies +import {useEffect, useState} from 'react'; +import {print} from 'shared-runtime'; + +/** + * Special case of `infer-effect-deps/nonreactive-dep`. + * + * We know that local `useRef` return values are stable, regardless of + * inferred memoization. + */ +function NonReactiveSetStateInEffect() { + const [_, setState] = useState('initial value'); + useEffect(() => print(setState)); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies +import { useEffect, useState } from "react"; +import { print } from "shared-runtime"; + +/** + * Special case of `infer-effect-deps/nonreactive-dep`. + * + * We know that local `useRef` return values are stable, regardless of + * inferred memoization. + */ +function NonReactiveSetStateInEffect() { + const $ = _c(1); + const [, setState] = useState("initial value"); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => print(setState); + $[0] = t0; + } else { + t0 = $[0]; + } + useEffect(t0, []); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.js new file mode 100644 index 0000000000000..88d4852aca40b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-setState.js @@ -0,0 +1,14 @@ +// @inferEffectDependencies +import {useEffect, useState} from 'react'; +import {print} from 'shared-runtime'; + +/** + * Special case of `infer-effect-deps/nonreactive-dep`. + * + * We know that local `useRef` return values are stable, regardless of + * inferred memoization. + */ +function NonReactiveSetStateInEffect() { + const [_, setState] = useState('initial value'); + useEffect(() => print(setState)); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.expect.md new file mode 100644 index 0000000000000..300408d5d2ec4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.expect.md @@ -0,0 +1,66 @@ + +## Input + +```javascript +// @inferEffectDependencies +import {useEffect, useRef} from 'react'; +import {print} from 'shared-runtime'; + +/* + * Ref types are not enough to determine to omit from deps. Must also take reactivity into account. + */ +function ReactiveRefInEffect(props) { + const ref1 = useRef('initial value'); + const ref2 = useRef('initial value'); + let ref; + if (props.foo) { + ref = ref1; + } else { + ref = ref2; + } + useEffect(() => print(ref)); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies +import { useEffect, useRef } from "react"; +import { print } from "shared-runtime"; + +/* + * Ref types are not enough to determine to omit from deps. Must also take reactivity into account. + */ +function ReactiveRefInEffect(props) { + const $ = _c(4); + const ref1 = useRef("initial value"); + const ref2 = useRef("initial value"); + let ref; + if ($[0] !== props.foo) { + if (props.foo) { + ref = ref1; + } else { + ref = ref2; + } + $[0] = props.foo; + $[1] = ref; + } else { + ref = $[1]; + } + let t0; + if ($[2] !== ref) { + t0 = () => print(ref); + $[2] = ref; + $[3] = t0; + } else { + t0 = $[3]; + } + useEffect(t0, [ref]); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.js new file mode 100644 index 0000000000000..2a51eae6f3183 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-ref.js @@ -0,0 +1,18 @@ +// @inferEffectDependencies +import {useEffect, useRef} from 'react'; +import {print} from 'shared-runtime'; + +/* + * Ref types are not enough to determine to omit from deps. Must also take reactivity into account. + */ +function ReactiveRefInEffect(props) { + const ref1 = useRef('initial value'); + const ref2 = useRef('initial value'); + let ref; + if (props.foo) { + ref = ref1; + } else { + ref = ref2; + } + useEffect(() => print(ref)); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md new file mode 100644 index 0000000000000..3af2b9b8b1c89 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md @@ -0,0 +1,60 @@ + +## Input + +```javascript +// @inferEffectDependencies +import {useEffect, useState} from 'react'; +import {print} from 'shared-runtime'; + +/* + * setState types are not enough to determine to omit from deps. Must also take reactivity into account. + */ +function ReactiveRefInEffect(props) { + const [_state1, setState1] = useRef('initial value'); + const [_state2, setState2] = useRef('initial value'); + let setState; + if (props.foo) { + setState = setState1; + } else { + setState = setState2; + } + useEffect(() => print(setState)); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies +import { useEffect, useState } from "react"; +import { print } from "shared-runtime"; + +/* + * setState types are not enough to determine to omit from deps. Must also take reactivity into account. + */ +function ReactiveRefInEffect(props) { + const $ = _c(2); + const [, setState1] = useRef("initial value"); + const [, setState2] = useRef("initial value"); + let setState; + if (props.foo) { + setState = setState1; + } else { + setState = setState2; + } + let t0; + if ($[0] !== setState) { + t0 = () => print(setState); + $[0] = setState; + $[1] = t0; + } else { + t0 = $[1]; + } + useEffect(t0, [setState]); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.js new file mode 100644 index 0000000000000..46a83d8ad42dd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.js @@ -0,0 +1,18 @@ +// @inferEffectDependencies +import {useEffect, useState} from 'react'; +import {print} from 'shared-runtime'; + +/* + * setState types are not enough to determine to omit from deps. Must also take reactivity into account. + */ +function ReactiveRefInEffect(props) { + const [_state1, setState1] = useRef('initial value'); + const [_state2, setState2] = useRef('initial value'); + let setState; + if (props.foo) { + setState = setState1; + } else { + setState = setState2; + } + useEffect(() => print(setState)); +} From 443b7ff2a8437f7736491ae7136c21d75d5a2019 Mon Sep 17 00:00:00 2001 From: michael faith Date: Mon, 3 Mar 2025 19:57:05 -0600 Subject: [PATCH 079/300] docs(eslint-plugin-react-hooks): clarify config details for prior versions (#32498) This change adds more details about prior versions of the plugin's config, to help people as they migrate from legacy to flat configs across multiple versions of this plugin. At some point in the 6.0 or 7.0 cycle, it would probably make sense to re-consolidate this into a single version. Closes #32494 --- packages/eslint-plugin-react-hooks/README.md | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/README.md b/packages/eslint-plugin-react-hooks/README.md index e4223c18b20e0..138cff733be6b 100644 --- a/packages/eslint-plugin-react-hooks/README.md +++ b/packages/eslint-plugin-react-hooks/README.md @@ -20,20 +20,24 @@ yarn add eslint-plugin-react-hooks --dev ### Flat Config (eslint.config.js|ts) -For [ESLint 9.0.0 and above](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/), add the `recommended` config. +#### 5.2.0 + +For users of 5.2.0 (the first version with flat config support), add the `recommended-latest` config. ```js import * as reactHooks from 'eslint-plugin-react-hooks'; export default [ // ... - reactHooks.configs['recommended'], + reactHooks.configs['recommended-latest'], ]; ``` ### Legacy Config (.eslintrc) -If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for accessing the recommended config. +#### >= 5.2.0 + +If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for accessing a legacy version of the recommended config. ```js { @@ -44,6 +48,19 @@ If you are still using ESLint below 9.0.0, you can use `recommended-legacy` for } ``` +#### < 5.2.0 + +If you're using a version earlier than 5.2.0, the legacy config was simply `recommended`. + +```js +{ + "extends": [ + // ... + "plugin:react-hooks/recommended" + ] +} +``` + ### Custom Configuration If you want more fine-grained configuration, you can instead add a snippet like this to your ESLint configuration file: From d48c69246c9acd3b39c1eec4f3c12103e95cdd66 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 4 Mar 2025 11:55:34 -0500 Subject: [PATCH 080/300] [flags] make enableScrollEndPolyfill dynamic (#32517) Will roll this out in www --- packages/shared/forks/ReactFeatureFlags.www-dynamic.js | 1 + packages/shared/forks/ReactFeatureFlags.www.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index e0f431fe7f10c..eba6c45aba5bf 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -39,6 +39,7 @@ export const enableUseEffectCRUDOverload = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = false; export const enableViewTransition = __VARIANT__; +export const enableScrollEndPolyfill = __VARIANT__; // TODO: These flags are hard-coded to the default values used in open source. // Update the tests so that they pass in either mode, then set these diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 0f790135d2aff..126b9ee373bd2 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -37,6 +37,7 @@ export const { transitionLaneExpirationMs, enableFastAddPropertiesInDiffing, enableViewTransition, + enableScrollEndPolyfill, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build. @@ -116,7 +117,5 @@ export const enableLazyPublicInstanceInFabric = false; export const enableSwipeTransition = false; -export const enableScrollEndPolyfill = false; - // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); From e0fe3479671555e01531dbc3d2fd85d5bd4c5a56 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 4 Mar 2025 12:34:34 -0500 Subject: [PATCH 081/300] [flags] remove enableOwnerStacks (#32426) Bassed off: https://github.com/facebook/react/pull/32425 Wait to land internally. [Commit to review.](https://github.com/facebook/react/pull/32426/commits/66aa6a4dbb78106b4f3d3eb367f5c27eb8f30c66) This has landed everywhere --- .../__tests__/ReactInternalTestUtils-test.js | 167 +---- packages/internal-test-utils/consoleMock.js | 18 +- .../__tests__/ReactCacheOld-test.internal.js | 6 +- .../react-client/src/ReactFlightClient.js | 234 +++---- .../src/__tests__/ReactFlight-test.js | 641 ++++++------------ .../ReactHooksInspectionIntegration-test.js | 5 +- .../src/backend/profilingHooks.js | 2 +- .../src/client/validateDOMNesting.js | 4 +- .../src/events/DOMPluginEventSystem.js | 5 +- .../__tests__/ReactChildReconciler-test.js | 13 +- .../src/__tests__/ReactComponent-test.js | 55 -- .../__tests__/ReactCompositeComponent-test.js | 3 - .../react-dom/src/__tests__/ReactDOM-test.js | 2 - .../src/__tests__/ReactDOMComponent-test.js | 524 +++++--------- .../src/__tests__/ReactDOMFiber-test.js | 1 - .../src/__tests__/ReactDOMFizzForm-test.js | 1 - .../src/__tests__/ReactDOMFizzServer-test.js | 209 ++---- .../src/__tests__/ReactDOMFloat-test.js | 80 +-- .../src/__tests__/ReactDOMForm-test.js | 7 +- .../src/__tests__/ReactDOMOption-test.js | 10 +- .../src/__tests__/ReactDOMSelect-test.js | 61 +- .../ReactDOMServerIntegrationElements-test.js | 35 - .../ReactDOMServerLifecycles-test.js | 1 - ...DOMServerPartialHydration-test.internal.js | 5 - .../ReactDOMSingletonComponents-test.js | 3 +- .../ReactDeprecationWarnings-test.js | 14 +- .../ReactErrorBoundaries-test.internal.js | 23 - .../__tests__/ReactFunctionComponent-test.js | 2 - .../ReactLegacyCompositeComponent-test.js | 19 +- ...eactLegacyContextDisabled-test.internal.js | 20 +- ...eactLegacyErrorBoundaries-test.internal.js | 20 - .../src/__tests__/ReactMultiChild-test.js | 16 +- .../__tests__/ReactServerRendering-test.js | 91 +-- .../src/__tests__/ReactUpdates-test.js | 10 +- .../src/__tests__/validateDOMNesting-test.js | 74 +- .../src/__tests__/ReactMarkupClient-test.js | 4 +- .../src/__tests__/ReactMarkupServer-test.js | 4 +- .../__tests__/ReactFabric-test.internal.js | 5 +- .../src/legacy-events/EventPluginUtils.js | 6 +- .../react-reconciler/src/ReactChildFiber.js | 33 +- .../react-reconciler/src/ReactCurrentFiber.js | 21 +- packages/react-reconciler/src/ReactFiber.js | 25 +- .../src/ReactFiberBeginWork.js | 7 +- .../src/ReactFiberComponentStack.js | 7 +- .../src/ReactFiberErrorLogger.js | 34 +- ...rorBoundaryReconciliation-test.internal.js | 6 - .../src/__tests__/ReactErrorStacks-test.js | 4 +- .../src/__tests__/ReactFragment-test.js | 19 +- .../src/__tests__/ReactHooks-test.internal.js | 24 +- .../src/__tests__/ReactIncremental-test.js | 99 +-- ...tIncrementalErrorHandling-test.internal.js | 49 +- .../ReactIncrementalErrorLogging-test.js | 25 +- .../src/__tests__/ReactLazy-test.internal.js | 358 ++++++++-- .../src/__tests__/ReactMemo-test.js | 52 +- .../src/__tests__/ReactOwnerStacks-test.js | 4 +- .../src/__tests__/ReactUse-test.js | 74 +- .../__tests__/ReactFlightDOMBrowser-test.js | 22 +- .../src/__tests__/ReactFlightDOMEdge-test.js | 2 +- .../src/ReactFizzComponentStack.js | 7 +- packages/react-server/src/ReactFizzServer.js | 114 ++-- .../react-server/src/ReactFlightServer.js | 219 +++--- packages/react/index.fb.js | 4 +- packages/react/src/ReactOwnerStack.js | 19 +- packages/react/src/ReactServer.fb.js | 4 +- .../react/src/__tests__/ReactChildren-test.js | 78 +-- .../ReactCoffeeScriptClass-test.coffee | 55 +- .../__tests__/ReactContextValidator-test.js | 17 +- .../src/__tests__/ReactCreateElement-test.js | 8 +- .../src/__tests__/ReactCreateRef-test.js | 10 +- .../react/src/__tests__/ReactES6Class-test.js | 6 - .../ReactElementValidator-test.internal.js | 118 +--- .../ReactJSXElementValidator-test.js | 56 +- .../src/__tests__/ReactJSXRuntime-test.js | 9 +- .../src/__tests__/ReactStrictMode-test.js | 85 +-- .../__tests__/ReactTypeScriptClass-test.ts | 10 +- .../createReactClassIntegration-test.js | 12 +- .../react/src/__tests__/forwardRef-test.js | 16 +- packages/react/src/jsx/ReactJSXElement.js | 356 ++-------- packages/shared/ReactComponentInfoStack.js | 4 +- packages/shared/ReactComponentStackFrame.js | 63 -- packages/shared/ReactFeatureFlags.js | 2 - .../forks/ReactFeatureFlags.native-fb.js | 3 - .../forks/ReactFeatureFlags.native-oss.js | 1 - .../forks/ReactFeatureFlags.test-renderer.js | 1 - ...actFeatureFlags.test-renderer.native-fb.js | 1 - .../ReactFeatureFlags.test-renderer.www.js | 1 - .../shared/forks/ReactFeatureFlags.www.js | 3 - packages/shared/isValidElementType.js | 2 +- scripts/jest/setupTests.www.js | 1 - scripts/jest/setupTests.xplat.js | 2 - 90 files changed, 1361 insertions(+), 3191 deletions(-) diff --git a/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js b/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js index 02ff7c50f0f4f..3c10125186832 100644 --- a/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js +++ b/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js @@ -857,17 +857,6 @@ describe('ReactInternalTestUtils console assertions', () => { You must call one of the assertConsoleDev helpers between each act call." `); - } else if (gate(flags => flags.enableOwnerStacks)) { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.log was called without assertConsoleLogDev: - + Not asserted - + Not asserted - + Not asserted - - You must call one of the assertConsoleDev helpers between each act call." - `); } else { expect(message).toMatchInlineSnapshot(` "asserConsoleLogsCleared(expected) @@ -937,20 +926,6 @@ describe('ReactInternalTestUtils console assertions', () => { + B + C - You must call one of the assertConsoleDev helpers between each act call." - `); - } else if (gate(flags => flags.enableOwnerStacks)) { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.warn was called without assertConsoleWarnDev: - + A%s, - + in App (at **) - + B%s, - + in App (at **) - + C%s, - + in App (at **) - You must call one of the assertConsoleDev helpers between each act call." `); } else { @@ -959,16 +934,10 @@ describe('ReactInternalTestUtils console assertions', () => { console.warn was called without assertConsoleWarnDev: + A%s, - + in Yield (at **) - + in div (at **) + in App (at **) + B%s, - + in Yield (at **) - + in div (at **) + in App (at **) + C%s, - + in Yield (at **) - + in div (at **) + in App (at **) You must call one of the assertConsoleDev helpers between each act call." @@ -1023,33 +992,6 @@ describe('ReactInternalTestUtils console assertions', () => { + B + C - You must call one of the assertConsoleDev helpers between each act call." - `); - } else if (gate(flags => flags.enableOwnerStacks)) { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.log was called without assertConsoleLogDev: - + A - + B - + C - - console.warn was called without assertConsoleWarnDev: - + A%s, - + in App (at **) - + B%s, - + in App (at **) - + C%s, - + in App (at **) - - console.error was called without assertConsoleErrorDev: - + A%s, - + in App (at **) - + B%s, - + in App (at **) - + C%s, - + in App (at **) - You must call one of the assertConsoleDev helpers between each act call." `); } else { @@ -1063,30 +1005,18 @@ describe('ReactInternalTestUtils console assertions', () => { console.warn was called without assertConsoleWarnDev: + A%s, - + in Yield (at **) - + in div (at **) + in App (at **) + B%s, - + in Yield (at **) - + in div (at **) + in App (at **) + C%s, - + in Yield (at **) - + in div (at **) + in App (at **) console.error was called without assertConsoleErrorDev: + A%s, - + in Yield (at **) - + in div (at **) + in App (at **) + B%s, - + in Yield (at **) - + in div (at **) + in App (at **) + C%s, - + in Yield (at **) - + in div (at **) + in App (at **) You must call one of the assertConsoleDev helpers between each act call." @@ -1927,20 +1857,6 @@ describe('ReactInternalTestUtils console assertions', () => { + Not asserted + Not asserted - You must call one of the assertConsoleDev helpers between each act call." - `); - } else if (gate(flags => flags.enableOwnerStacks)) { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.warn was called without assertConsoleWarnDev: - + Not asserted%s, - + in Yield (at **) - + Not asserted%s, - + in Yield (at **) - + Not asserted%s, - + in Yield (at **) - You must call one of the assertConsoleDev helpers between each act call." `); } else { @@ -1950,13 +1866,10 @@ describe('ReactInternalTestUtils console assertions', () => { console.warn was called without assertConsoleWarnDev: + Not asserted%s, + in Yield (at **) - + in div (at **) + Not asserted%s, + in Yield (at **) - + in div (at **) + Not asserted%s, + in Yield (at **) - + in div (at **) You must call one of the assertConsoleDev helpers between each act call." `); @@ -2020,7 +1933,7 @@ describe('ReactInternalTestUtils console assertions', () => { You must call one of the assertConsoleDev helpers between each act call." `); - } else if (gate(flags => flags.enableOwnerStacks)) { + } else { expect(message).toMatchInlineSnapshot(` "asserConsoleLogsCleared(expected) @@ -2034,26 +1947,6 @@ describe('ReactInternalTestUtils console assertions', () => { You must call one of the assertConsoleDev helpers between each act call." `); - } else { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.error was called without assertConsoleErrorDev: - + A%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - + B%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - + C%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - - You must call one of the assertConsoleDev helpers between each act call." - `); } }); @@ -2106,7 +1999,7 @@ describe('ReactInternalTestUtils console assertions', () => { You must call one of the assertConsoleDev helpers between each act call." `); - } else if (gate(flags => flags.enableOwnerStacks)) { + } else { expect(message).toMatchInlineSnapshot(` "asserConsoleLogsCleared(expected) @@ -2133,45 +2026,6 @@ describe('ReactInternalTestUtils console assertions', () => { You must call one of the assertConsoleDev helpers between each act call." `); - } else { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.log was called without assertConsoleLogDev: - + A - + B - + C - - console.warn was called without assertConsoleWarnDev: - + A%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - + B%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - + C%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - - console.error was called without assertConsoleErrorDev: - + A%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - + B%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - + C%s, - + in Yield (at **) - + in div (at **) - + in App (at **) - - You must call one of the assertConsoleDev helpers between each act call." - `); } }); @@ -3052,20 +2906,6 @@ describe('ReactInternalTestUtils console assertions', () => { + Not asserted + Not asserted - You must call one of the assertConsoleDev helpers between each act call." - `); - } else if (gate(flags => flags.enableOwnerStacks)) { - expect(message).toMatchInlineSnapshot(` - "asserConsoleLogsCleared(expected) - - console.error was called without assertConsoleErrorDev: - + Not asserted%s, - + in Yield (at **) - + Not asserted%s, - + in Yield (at **) - + Not asserted%s, - + in Yield (at **) - You must call one of the assertConsoleDev helpers between each act call." `); } else { @@ -3075,13 +2915,10 @@ describe('ReactInternalTestUtils console assertions', () => { console.error was called without assertConsoleErrorDev: + Not asserted%s, + in Yield (at **) - + in div (at **) + Not asserted%s, + in Yield (at **) - + in div (at **) + Not asserted%s, + in Yield (at **) - + in div (at **) You must call one of the assertConsoleDev helpers between each act call." `); diff --git a/packages/internal-test-utils/consoleMock.js b/packages/internal-test-utils/consoleMock.js index 0ab69bfa3a922..9bb797bac395b 100644 --- a/packages/internal-test-utils/consoleMock.js +++ b/packages/internal-test-utils/consoleMock.js @@ -6,6 +6,7 @@ */ /* eslint-disable react-internal/no-production-logging */ + const chalk = require('chalk'); const util = require('util'); const shouldIgnoreConsoleError = require('./shouldIgnoreConsoleError'); @@ -38,25 +39,16 @@ const patchConsoleMethod = (methodName, logged) => { (methodName === 'error' || methodName === 'warn') ) { const React = require('react'); + + // Ideally we could remove this check, but we have some tests like + // useSyncExternalStoreShared-test that tests against React 17, + // which doesn't have the captureOwnerStack method. if (React.captureOwnerStack) { - // enableOwnerStacks enabled. When it's always on, we can assume this case. const stack = React.captureOwnerStack(); if (stack) { format += '%s'; args.push(stack); } - } else { - // Otherwise we have to use internals to emulate parent stacks. - const ReactSharedInternals = - React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE || - React.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; - if (ReactSharedInternals && ReactSharedInternals.getCurrentStack) { - const stack = ReactSharedInternals.getCurrentStack(); - if (stack !== '') { - format += '%s'; - args.push(stack); - } - } } } diff --git a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js index 554e6e4bfb29a..0c75abf06b710 100644 --- a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js +++ b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js @@ -203,11 +203,7 @@ describe('ReactCache', () => { "boolean, but instead received: [ 'Hi', 100 ]\n\n" + 'To use non-primitive values as keys, you must pass a hash ' + 'function as the second argument to createResource().\n' + - ' in App (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in Suspense (at **)'), - + ' in App (at **)', ...(gate('enableSiblingPrerendering') ? [ 'Invalid key type. Expected a string, number, symbol, or ' + diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 3d125cc3104ea..0eaf513a676dd 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -45,7 +45,6 @@ import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences'; import { enablePostpone, - enableOwnerStacks, enableProfilerTimer, enableComponentPerformanceTrack, } from 'shared/ReactFeatureFlags'; @@ -755,7 +754,7 @@ function createElement( configurable: false, enumerable: false, writable: true, - value: enableOwnerStacks ? validated : 1, // Whether the element has already been validated on the server. + value: validated, // Whether the element has already been validated on the server. }); // debugInfo contains Server Component debug information. Object.defineProperty(element, '_debugInfo', { @@ -765,78 +764,67 @@ function createElement( value: null, }); let env = response._rootEnvironmentName; - if (enableOwnerStacks) { - if (owner !== null && owner.env != null) { - // Interestingly we don't actually have the environment name of where - // this JSX was created if it doesn't have an owner but if it does - // it must be the same environment as the owner. We could send it separately - // but it seems a bit unnecessary for this edge case. - env = owner.env; - } - let normalizedStackTrace: null | Error = null; - if (owner === null && response._debugRootStack != null) { - // We override the stack if we override the owner since the stack where the root JSX - // was created on the server isn't very useful but where the request was made is. - normalizedStackTrace = response._debugRootStack; - } else if (stack !== null) { - // We create a fake stack and then create an Error object inside of it. - // This means that the stack trace is now normalized into the native format - // of the browser and the stack frames will have been registered with - // source mapping information. - // This can unfortunately happen within a user space callstack which will - // remain on the stack. - normalizedStackTrace = createFakeJSXCallStackInDEV( - response, - stack, - env, - ); - } - Object.defineProperty(element, '_debugStack', { - configurable: false, - enumerable: false, - writable: true, - value: normalizedStackTrace, - }); + if (owner !== null && owner.env != null) { + // Interestingly we don't actually have the environment name of where + // this JSX was created if it doesn't have an owner but if it does + // it must be the same environment as the owner. We could send it separately + // but it seems a bit unnecessary for this edge case. + env = owner.env; + } + let normalizedStackTrace: null | Error = null; + if (owner === null && response._debugRootStack != null) { + // We override the stack if we override the owner since the stack where the root JSX + // was created on the server isn't very useful but where the request was made is. + normalizedStackTrace = response._debugRootStack; + } else if (stack !== null) { + // We create a fake stack and then create an Error object inside of it. + // This means that the stack trace is now normalized into the native format + // of the browser and the stack frames will have been registered with + // source mapping information. + // This can unfortunately happen within a user space callstack which will + // remain on the stack. + normalizedStackTrace = createFakeJSXCallStackInDEV(response, stack, env); + } + Object.defineProperty(element, '_debugStack', { + configurable: false, + enumerable: false, + writable: true, + value: normalizedStackTrace, + }); - let task: null | ConsoleTask = null; - if (supportsCreateTask && stack !== null) { - const createTaskFn = (console: any).createTask.bind( - console, - getTaskName(type), - ); - const callStack = buildFakeCallStack( - response, - stack, - env, - createTaskFn, - ); - // This owner should ideally have already been initialized to avoid getting - // user stack frames on the stack. - const ownerTask = - owner === null ? null : initializeFakeTask(response, owner, env); - if (ownerTask === null) { - const rootTask = response._debugRootTask; - if (rootTask != null) { - task = rootTask.run(callStack); - } else { - task = callStack(); - } + let task: null | ConsoleTask = null; + if (supportsCreateTask && stack !== null) { + const createTaskFn = (console: any).createTask.bind( + console, + getTaskName(type), + ); + const callStack = buildFakeCallStack(response, stack, env, createTaskFn); + // This owner should ideally have already been initialized to avoid getting + // user stack frames on the stack. + const ownerTask = + owner === null ? null : initializeFakeTask(response, owner, env); + if (ownerTask === null) { + const rootTask = response._debugRootTask; + if (rootTask != null) { + task = rootTask.run(callStack); } else { - task = ownerTask.run(callStack); + task = callStack(); } + } else { + task = ownerTask.run(callStack); } - Object.defineProperty(element, '_debugTask', { - configurable: false, - enumerable: false, - writable: true, - value: task, - }); + } + Object.defineProperty(element, '_debugTask', { + configurable: false, + enumerable: false, + writable: true, + value: task, + }); - // This owner should ideally have already been initialized to avoid getting - // user stack frames on the stack. - if (owner !== null) { - initializeFakeStack(response, owner); - } + // This owner should ideally have already been initialized to avoid getting + // user stack frames on the stack. + if (owner !== null) { + initializeFakeStack(response, owner); } } @@ -863,13 +851,11 @@ function createElement( name: getComponentNameFromType(element.type) || '', owner: element._owner, }; - if (enableOwnerStacks) { + // $FlowFixMe[cannot-write] + erroredComponent.debugStack = element._debugStack; + if (supportsCreateTask) { // $FlowFixMe[cannot-write] - erroredComponent.debugStack = element._debugStack; - if (supportsCreateTask) { - // $FlowFixMe[cannot-write] - erroredComponent.debugTask = element._debugTask; - } + erroredComponent.debugTask = element._debugTask; } erroredChunk._debugInfo = [erroredComponent]; } @@ -1057,13 +1043,11 @@ function waitForReference( name: getComponentNameFromType(element.type) || '', owner: element._owner, }; - if (enableOwnerStacks) { + // $FlowFixMe[cannot-write] + erroredComponent.debugStack = element._debugStack; + if (supportsCreateTask) { // $FlowFixMe[cannot-write] - erroredComponent.debugStack = element._debugStack; - if (supportsCreateTask) { - // $FlowFixMe[cannot-write] - erroredComponent.debugTask = element._debugTask; - } + erroredComponent.debugTask = element._debugTask; } const chunkDebugInfo: ReactDebugInfo = chunk._debugInfo || (chunk._debugInfo = []); @@ -1223,13 +1207,11 @@ function loadServerReference, T>( name: getComponentNameFromType(element.type) || '', owner: element._owner, }; - if (enableOwnerStacks) { + // $FlowFixMe[cannot-write] + erroredComponent.debugStack = element._debugStack; + if (supportsCreateTask) { // $FlowFixMe[cannot-write] - erroredComponent.debugStack = element._debugStack; - if (supportsCreateTask) { - // $FlowFixMe[cannot-write] - erroredComponent.debugTask = element._debugTask; - } + erroredComponent.debugTask = element._debugTask; } const chunkDebugInfo: ReactDebugInfo = chunk._debugInfo || (chunk._debugInfo = []); @@ -1609,8 +1591,8 @@ function parseModelTuple( tuple[2], tuple[3], __DEV__ ? (tuple: any)[4] : null, - __DEV__ && enableOwnerStacks ? (tuple: any)[5] : null, - __DEV__ && enableOwnerStacks ? (tuple: any)[6] : 0, + __DEV__ ? (tuple: any)[5] : null, + __DEV__ ? (tuple: any)[6] : 0, ); } return value; @@ -2083,27 +2065,6 @@ function stopStream( controller.close(row === '' ? '"$undefined"' : row); } -function formatV8Stack( - errorName: string, - errorMessage: string, - stack: null | ReactStackTrace, -): string { - let v8StyleStack = errorName + ': ' + errorMessage; - if (stack) { - for (let i = 0; i < stack.length; i++) { - const frame = stack[i]; - const [name, filename, line, col] = frame; - if (!name) { - v8StyleStack += '\n at ' + filename + ':' + line + ':' + col; - } else { - v8StyleStack += - '\n at ' + name + ' (' + filename + ':' + line + ':' + col + ')'; - } - } - } - return v8StyleStack; -} - type ErrorWithDigest = Error & {digest?: string}; function resolveErrorProd(response: Response): Error { if (__DEV__) { @@ -2202,34 +2163,20 @@ function resolvePostponeDev( ); } let postponeInstance: Postpone; - if (!enableOwnerStacks) { - // Executing Error within a native stack isn't really limited to owner stacks - // but we gate it behind the same flag for now while iterating. - // eslint-disable-next-line react-internal/prod-error-codes - postponeInstance = (Error(reason || ''): any); - postponeInstance.$$typeof = REACT_POSTPONE_TYPE; - // For backwards compat we use the V8 formatting when the flag is off. - postponeInstance.stack = formatV8Stack( - postponeInstance.name, - postponeInstance.message, - stack, - ); + const callStack = buildFakeCallStack( + response, + stack, + env, + // $FlowFixMe[incompatible-use] + Error.bind(null, reason || ''), + ); + const rootTask = response._debugRootTask; + if (rootTask != null) { + postponeInstance = rootTask.run(callStack); } else { - const callStack = buildFakeCallStack( - response, - stack, - env, - // $FlowFixMe[incompatible-use] - Error.bind(null, reason || ''), - ); - const rootTask = response._debugRootTask; - if (rootTask != null) { - postponeInstance = rootTask.run(callStack); - } else { - postponeInstance = callStack(); - } - postponeInstance.$$typeof = REACT_POSTPONE_TYPE; + postponeInstance = callStack(); } + postponeInstance.$$typeof = REACT_POSTPONE_TYPE; const chunks = response._chunks; const chunk = chunks.get(id); if (!chunk) { @@ -2248,8 +2195,7 @@ function resolveHint( dispatchHint(code, hintModel); } -const supportsCreateTask = - __DEV__ && enableOwnerStacks && !!(console: any).createTask; +const supportsCreateTask = __DEV__ && !!(console: any).createTask; type FakeFunction = (() => T) => T; const fakeFunctionCache: Map> = __DEV__ @@ -2590,15 +2536,11 @@ function resolveDebugInfo( let currentOwnerInDEV: null | ReactComponentInfo = null; function getCurrentStackInDEV(): string { if (__DEV__) { - if (enableOwnerStacks) { - const owner: null | ReactComponentInfo = currentOwnerInDEV; - if (owner === null) { - return ''; - } - return getOwnerStackByComponentInfoInDev(owner); + const owner: null | ReactComponentInfo = currentOwnerInDEV; + if (owner === null) { + return ''; } - // We don't have Parent Stacks in Flight. - return ''; + return getOwnerStackByComponentInfoInDev(owner); } return ''; } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 25bbd0e83acba..22e03527bcba2 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -321,9 +321,7 @@ describe('ReactFlight', () => { env: 'Server', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: { firstName: 'Seb', lastName: 'Smith', @@ -367,9 +365,7 @@ describe('ReactFlight', () => { env: 'Server', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: { firstName: 'Seb', lastName: 'Smith', @@ -1400,30 +1396,19 @@ describe('ReactFlight', () => { environmentName: 'Server', }, ], - findSourceMapURLCalls: gate(flags => flags.enableOwnerStacks) - ? [ - [__filename, 'Server'], - [__filename, 'Server'], - // TODO: What should we request here? The outer () or the inner (inspected-page.html)? - ['inspected-page.html:29:11), ', 'Server'], - [ - 'file://~/(some)(really)(exotic-directory)/ReactFlight-test.js', - 'Server', - ], - ['file:///testing.js', 'Server'], - ['', 'Server'], - [__filename, 'Server'], - ] - : [ - // TODO: What should we request here? The outer () or the inner (inspected-page.html)? - ['inspected-page.html:29:11), ', 'Server'], - [ - 'file://~/(some)(really)(exotic-directory)/ReactFlight-test.js', - 'Server', - ], - ['file:///testing.js', 'Server'], - ['', 'Server'], - ], + findSourceMapURLCalls: [ + [__filename, 'Server'], + [__filename, 'Server'], + // TODO: What should we request here? The outer () or the inner (inspected-page.html)? + ['inspected-page.html:29:11), ', 'Server'], + [ + 'file://~/(some)(really)(exotic-directory)/ReactFlight-test.js', + 'Server', + ], + ['file:///testing.js', 'Server'], + ['', 'Server'], + [__filename, 'Server'], + ], }); } else { expect(errors.map(getErrorForJestMatcher)).toEqual([ @@ -1475,9 +1460,6 @@ describe('ReactFlight', () => { 'Check the render method of `Component`. See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + ' in Component (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Indirection (at **)\n') + ' in App (at **)', ]); }); @@ -1538,46 +1520,26 @@ describe('ReactFlight', () => { }, }; const transport = ReactNoopFlightServer.render(); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' \n' + - ' ^^^^^^^^^^^^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with toJSON methods are not supported. ' + 'Convert it manually to a simple value before passing it to props.\n' + ' \n' + - ' ^^^^^^^^^^^^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' \n' + - ' ^^^^^^^^^^^^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' \n' + - ' ^^^^^^^^^^^^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^^^^^^^^^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Objects with toJSON methods are not supported. ' + + 'Convert it manually to a simple value before passing it to props.\n' + + ' \n' + + ' ^^^^^^^^^^^^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if a toJSON instance is passed to a host component child', () => { @@ -1589,120 +1551,68 @@ describe('ReactFlight', () => { const transport = ReactNoopFlightServer.render(
Womp womp: {new MyError('spaghetti')}
, ); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Error objects cannot be rendered as text children. Try formatting it using toString().\n' + - '
Womp womp: {Error}
\n' + - ' ^^^^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Error objects cannot be rendered as text children. Try formatting it using toString().\n' + '
Womp womp: {Error}
\n' + - ' ^^^^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Error objects cannot be rendered as text children. Try formatting it using toString().\n' + - '
Womp womp: {Error}
\n' + - ' ^^^^^^^', - 'Error objects cannot be rendered as text children. Try formatting it using toString().\n' + - '
Womp womp: {Error}
\n' + - ' ^^^^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Error objects cannot be rendered as text children. Try formatting it using toString().\n' + + '
Womp womp: {Error}
\n' + + ' ^^^^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if a special object is passed to a host component', () => { const transport = ReactNoopFlightServer.render(); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' \n' + - ' ^^^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Math objects are not supported.\n' + ' \n' + - ' ^^^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' \n' + - ' ^^^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' \n' + - ' ^^^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Math objects are not supported.\n' + + ' \n' + + ' ^^^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if an object with symbols is passed to a host component', () => { const transport = ReactNoopFlightServer.render( , ); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' \n' + - ' ^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with symbol properties like Symbol.iterator are not supported.\n' + ' \n' + - ' ^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' \n' + - ' ^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' \n' + - ' ^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Objects with symbol properties like Symbol.iterator are not supported.\n' + + ' \n' + + ' ^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if a toJSON instance is passed to a Client Component', () => { @@ -1716,46 +1626,26 @@ describe('ReactFlight', () => { } const Client = clientReference(ClientImpl); const transport = ReactNoopFlightServer.render(); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' <... value={{toJSON: ...}}>\n' + - ' ^^^^^^^^^^^^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with toJSON methods are not supported. ' + 'Convert it manually to a simple value before passing it to props.\n' + ' <... value={{toJSON: ...}}>\n' + - ' ^^^^^^^^^^^^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' <... value={{toJSON: ...}}>\n' + - ' ^^^^^^^^^^^^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' <... value={{toJSON: ...}}>\n' + - ' ^^^^^^^^^^^^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^^^^^^^^^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Objects with toJSON methods are not supported. ' + + 'Convert it manually to a simple value before passing it to props.\n' + + ' <... value={{toJSON: ...}}>\n' + + ' ^^^^^^^^^^^^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if a toJSON instance is passed to a Client Component child', () => { @@ -1771,46 +1661,26 @@ describe('ReactFlight', () => { const transport = ReactNoopFlightServer.render( Current date: {obj}, ); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' <>Current date: {{toJSON: ...}}\n' + - ' ^^^^^^^^^^^^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with toJSON methods are not supported. ' + 'Convert it manually to a simple value before passing it to props.\n' + ' <>Current date: {{toJSON: ...}}\n' + - ' ^^^^^^^^^^^^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' <>Current date: {{toJSON: ...}}\n' + - ' ^^^^^^^^^^^^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with toJSON methods are not supported. ' + - 'Convert it manually to a simple value before passing it to props.\n' + - ' <>Current date: {{toJSON: ...}}\n' + - ' ^^^^^^^^^^^^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^^^^^^^^^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Objects with toJSON methods are not supported. ' + + 'Convert it manually to a simple value before passing it to props.\n' + + ' <>Current date: {{toJSON: ...}}\n' + + ' ^^^^^^^^^^^^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if a special object is passed to a Client Component', () => { @@ -1819,43 +1689,24 @@ describe('ReactFlight', () => { } const Client = clientReference(ClientImpl); const transport = ReactNoopFlightServer.render(); - - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' <... value={Math}>\n' + - ' ^^^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Math objects are not supported.\n' + ' <... value={Math}>\n' + - ' ^^^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' <... value={Math}>\n' + - ' ^^^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' <... value={Math}>\n' + - ' ^^^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Math objects are not supported.\n' + + ' <... value={Math}>\n' + + ' ^^^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if an object with symbols is passed to a Client Component', () => { @@ -1867,42 +1718,24 @@ describe('ReactFlight', () => { const transport = ReactNoopFlightServer.render( , ); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' <... value={{}}>\n' + - ' ^^^^', - ], - {withoutStack: true}, - ); - } - - ReactNoopFlightClient.read(transport); - - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ + assertConsoleErrorDev( + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with symbol properties like Symbol.iterator are not supported.\n' + ' <... value={{}}>\n' + - ' ^^^^\n', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' <... value={{}}>\n' + - ' ^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' <... value={{}}>\n' + - ' ^^^^', - ], - {withoutStack: true}, - ); - } + ' ^^^^', + ], + {withoutStack: true}, + ); + + ReactNoopFlightClient.read(transport); + + assertConsoleErrorDev([ + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Objects with symbol properties like Symbol.iterator are not supported.\n' + + ' <... value={{}}>\n' + + ' ^^^^\n', + ]); }); it('should warn in DEV if a special object is passed to a nested object in Client Component', () => { @@ -1915,36 +1748,20 @@ describe('ReactFlight', () => { ); ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' <... value={{}}>\n' + - ' ^^^^', - {withoutStack: true}, - ], + assertConsoleErrorDev([ + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with symbol properties like Symbol.iterator are not supported.\n' + ' <... value={{}}>\n' + - ' ^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' <... value={{}}>\n' + - ' ^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Objects with symbol properties like Symbol.iterator are not supported.\n' + - ' <... value={{}}>\n' + - ' ^^^^', - ], + ' ^^^^', {withoutStack: true}, - ); - } + ], + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Objects with symbol properties like Symbol.iterator are not supported.\n' + + ' <... value={{}}>\n' + + ' ^^^^\n' + + ' at ()', + ]); }); it('should warn in DEV if a special object is passed to a nested array in Client Component', () => { @@ -1956,36 +1773,20 @@ describe('ReactFlight', () => { hi]} />, ); ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' [..., Math,

]\n' + - ' ^^^^', - {withoutStack: true}, - ], + assertConsoleErrorDev([ + [ 'Only plain objects can be passed to Client Components from Server Components. ' + 'Math objects are not supported.\n' + ' [..., Math,

]\n' + - ' ^^^^\n' + - ' at ()', - ]); - } else { - assertConsoleErrorDev( - [ - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' [..., Math,

]\n' + - ' ^^^^', - 'Only plain objects can be passed to Client Components from Server Components. ' + - 'Math objects are not supported.\n' + - ' [..., Math,

]\n' + - ' ^^^^', - ], + ' ^^^^', {withoutStack: true}, - ); - } + ], + 'Only plain objects can be passed to Client Components from Server Components. ' + + 'Math objects are not supported.\n' + + ' [..., Math,

]\n' + + ' ^^^^\n' + + ' at ()', + ]); }); it('should NOT warn in DEV for key getters', () => { @@ -2012,26 +1813,16 @@ describe('ReactFlight', () => { jest.resetModules(); jest.mock('react', () => React); ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in NoKey (at **)', - 'Each child in a list should have a unique "key" prop. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in NoKey (at **)', - ]); - } else { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using
. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in NoKey (at **)', - ]); - } + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in NoKey (at **)', + 'Each child in a list should have a unique "key" prop. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in NoKey (at **)', + ]); }); - // @gate !__DEV__ || enableOwnerStacks it('should warn in DEV a child is missing keys on a fragment', () => { // While we're on the server we need to have the Server version active to track component stacks. jest.resetModules(); @@ -2046,23 +1837,14 @@ describe('ReactFlight', () => { jest.resetModules(); jest.mock('react', () => React); ReactNoopFlightClient.read(transport); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Fragment (at **)', - 'Each child in a list should have a unique "key" prop. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Fragment (at **)', - ]); - } else { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using
. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Fragment (at **)', - ]); - } + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in Fragment (at **)', + 'Each child in a list should have a unique "key" prop. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in Fragment (at **)', + ]); }); it('should warn in DEV a child is missing keys in client component', async () => { @@ -2079,20 +1861,12 @@ describe('ReactFlight', () => { ReactNoop.render(await ReactNoopFlightClient.read(transport)); }); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using . ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ]); - } else { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ]); - } + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the top-level render call using . ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in div (at **)', + ]); }); it('should error if a class instance is passed to a host component', () => { @@ -3039,9 +2813,7 @@ describe('ReactFlight', () => { env: 'Server', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: { transport: expect.arrayContaining([]), }, @@ -3063,9 +2835,7 @@ describe('ReactFlight', () => { env: 'third-party', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: {}, }, {time: 14}, @@ -3082,9 +2852,7 @@ describe('ReactFlight', () => { env: 'third-party', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in myLazy (at **)\n in lazyInitializer (at **)' - : undefined, + stack: ' in myLazy (at **)\n in lazyInitializer (at **)', props: {}, }, {time: 16}, @@ -3100,9 +2868,7 @@ describe('ReactFlight', () => { env: 'third-party', key: '3', owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: {}, }, {time: 12}, @@ -3176,9 +2942,7 @@ describe('ReactFlight', () => { env: 'Server', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: { transport: expect.arrayContaining([]), }, @@ -3198,9 +2962,7 @@ describe('ReactFlight', () => { env: 'Server', key: 'keyed', owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in ServerComponent (at **)' - : undefined, + stack: ' in ServerComponent (at **)', props: { children: {}, }, @@ -3219,9 +2981,7 @@ describe('ReactFlight', () => { env: 'third-party', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: {}, }, {time: 12}, @@ -3335,19 +3095,13 @@ describe('ReactFlight', () => { }); expect(sawReactPrefix).toBe(false); - if (__DEV__ && gate(flags => flags.enableOwnerStacks)) { + if (__DEV__) { expect(environments.slice(0, 4)).toEqual([ 'Server', 'third-party', 'third-party', 'third-party', ]); - } else if (__DEV__) { - expect(environments.slice(0, 3)).toEqual([ - 'third-party', - 'third-party', - 'third-party', - ]); } else { expect(environments).toEqual([]); } @@ -3384,9 +3138,7 @@ describe('ReactFlight', () => { env: 'A', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: {}, }, { @@ -3402,7 +3154,7 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(
hi
); }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('replays logs, but not onError logs', async () => { function foo() { return 'hello'; @@ -3574,9 +3326,7 @@ describe('ReactFlight', () => { env: 'Server', key: null, owner: null, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Object. (at **)' - : undefined, + stack: ' in Object. (at **)', props: { firstName: 'Seb', }, @@ -3590,9 +3340,7 @@ describe('ReactFlight', () => { env: 'Server', key: null, owner: greetInfo, - stack: gate(flag => flag.enableOwnerStacks) - ? ' in Greeting (at **)' - : undefined, + stack: ' in Greeting (at **)', props: { children: expect.objectContaining({ type: 'span', @@ -3617,7 +3365,7 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(Hello, Seb); }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can get the component owner stacks during rendering in dev', () => { let stack; @@ -3649,7 +3397,7 @@ describe('ReactFlight', () => { ); }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can track owner for a flight response created in another render', async () => { jest.resetModules(); jest.mock('react', () => ReactServer); @@ -3712,7 +3460,7 @@ describe('ReactFlight', () => { ); }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can get the component owner stacks for onError in dev', async () => { const thrownError = new Error('hi'); let caughtError; @@ -3754,7 +3502,6 @@ describe('ReactFlight', () => { ); }); - // @gate (enableOwnerStacks) || !__DEV__ it('should include only one component stack in replayed logs (if DevTools or polyfill adds them)', () => { class MyError extends Error { toJSON() { diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index 79ec20c3c2975..efa59d8605ed2 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -2347,10 +2347,7 @@ describe('ReactHooksInspectionIntegration', () => { await act(async () => await LazyFoo); assertConsoleErrorDev([ - 'Foo: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in Foo (at **)\n' + ' in Suspense (at **)'), + 'Foo: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', ]); const childFiber = renderer.root._currentFiber(); diff --git a/packages/react-devtools-shared/src/backend/profilingHooks.js b/packages/react-devtools-shared/src/backend/profilingHooks.js index a8111713c9fb2..c84bb88250375 100644 --- a/packages/react-devtools-shared/src/backend/profilingHooks.js +++ b/packages/react-devtools-shared/src/backend/profilingHooks.js @@ -917,7 +917,7 @@ export function createProfilingHooks({ // Creating a cache of component stacks won't help, generating a single stack is already expensive enough. // We should find a way to lazily generate component stacks on demand, when user inspects a specific event. // If we succeed with moving React DevTools Timeline Profiler to Performance panel, then Timeline Profiler would probably be removed. - // If not, then once enableOwnerStacks is adopted, revisit this again and cache component stacks per Fiber, + // Now that owner stacks are adopted, revisit this again and cache component stacks per Fiber, // but only return them when needed, sending hundreds of component stacks is beyond the Bridge's bandwidth. // Postprocess Profile data diff --git a/packages/react-dom-bindings/src/client/validateDOMNesting.js b/packages/react-dom-bindings/src/client/validateDOMNesting.js index 48ea32d7f3ffc..47aef9353ba85 100644 --- a/packages/react-dom-bindings/src/client/validateDOMNesting.js +++ b/packages/react-dom-bindings/src/client/validateDOMNesting.js @@ -10,8 +10,6 @@ import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type {HydrationDiffNode} from 'react-reconciler/src/ReactFiberHydrationDiffs'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; - import { current, runWithFiberInDEV, @@ -615,7 +613,7 @@ function validateDOMNesting( ancestorDescription, ); } - if (enableOwnerStacks && child) { + if (child) { // For debugging purposes find the nearest ancestor that caused the issue. // The stack trace of this ancestor can be useful to find the cause. // If the parent is a direct parent in the same owner, we don't bother. diff --git a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js index c813ee16157a1..b4733c7781f8a 100644 --- a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js @@ -52,7 +52,6 @@ import { enableLegacyFBSupport, enableCreateEventHandleAPI, enableScopeAPI, - enableOwnerStacks, disableCommentsAsDOMContainers, enableScrollEndPolyfill, } from 'shared/ReactFeatureFlags'; @@ -275,7 +274,7 @@ function processDispatchQueueItemsInOrder( if (instance !== previousInstance && event.isPropagationStopped()) { return; } - if (__DEV__ && enableOwnerStacks && instance !== null) { + if (__DEV__ && instance !== null) { runWithFiberInDEV( instance, executeDispatch, @@ -294,7 +293,7 @@ function processDispatchQueueItemsInOrder( if (instance !== previousInstance && event.isPropagationStopped()) { return; } - if (__DEV__ && enableOwnerStacks && instance !== null) { + if (__DEV__ && instance !== null) { runWithFiberInDEV( instance, executeDispatch, diff --git a/packages/react-dom/src/__tests__/ReactChildReconciler-test.js b/packages/react-dom/src/__tests__/ReactChildReconciler-test.js index ba0c028fa863a..d173d26e67989 100644 --- a/packages/react-dom/src/__tests__/ReactChildReconciler-test.js +++ b/packages/react-dom/src/__tests__/ReactChildReconciler-test.js @@ -75,8 +75,7 @@ describe('ReactChildReconciler', () => { 'This may happen if you return fn instead of from render. ' + 'Or maybe you meant to call this function rather than return it.\n' + '

{fn}

\n' + - ' in h1 (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in div (at **)'), + ' in h1 (at **)', ]); const node = container.firstChild; @@ -100,7 +99,6 @@ describe('ReactChildReconciler', () => { 'Keys should be unique so that components maintain their identity across updates. ' + 'Non-unique keys may cause children to be duplicated and/or omitted — ' + 'the behavior is unsupported and could change in a future version.\n' + - (gate('enableOwnerStacks') ? '' : ' in div (at **)\n') + ' in div (at **)\n' + ' in Component (at **)', ]); @@ -137,11 +135,7 @@ describe('ReactChildReconciler', () => { 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.\n' + ' in div (at **)\n' + - (gate(flags => flags.enableOwnerStacks) ? '' : ' in div (at **)\n') + ' in Component (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Parent (at **)\n') + ' in GrandParent (at **)', ]); }); @@ -165,7 +159,6 @@ describe('ReactChildReconciler', () => { 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.\n' + ' in div (at **)\n' + - (gate(flags => flags.enableOwnerStacks) ? '' : ' in div (at **)\n') + ' in Component (at **)', ]); }); @@ -201,11 +194,7 @@ describe('ReactChildReconciler', () => { 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.\n' + ' in div (at **)\n' + - (gate(flags => flags.enableOwnerStacks) ? '' : ' in div (at **)\n') + ' in Component (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Parent (at **)\n') + ' in GrandParent (at **)', ]); }); diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js index c59ba61e01c44..8990711cf1c58 100644 --- a/packages/react-dom/src/__tests__/ReactComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactComponent-test.js @@ -407,17 +407,6 @@ describe('ReactComponent', () => { const X = undefined; const XElement = ; - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.', - ], - {withoutStack: true}, - ); - } await expect(async () => { await act(() => { root.render(XElement); @@ -433,15 +422,6 @@ describe('ReactComponent', () => { const Y = null; const YElement = ; - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: null.', - ], - {withoutStack: true}, - ); - } await expect(async () => { await act(() => { root.render(YElement); @@ -453,15 +433,6 @@ describe('ReactComponent', () => { const Z = true; const ZElement = ; - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: boolean.', - ], - {withoutStack: true}, - ); - } await expect(async () => { await act(() => { root.render(ZElement); @@ -506,26 +477,6 @@ describe('ReactComponent', () => { '\n\nCheck the render method of `Bar`.' : ''), ); - if (!gate('enableOwnerStacks')) { - assertConsoleErrorDev([ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined.' + - (__DEV__ - ? " You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.\n' + - ' in Bar (at **)\n' + - ' in Foo (at **)' - : ''), - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined.' + - (__DEV__ - ? " You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.\n' + - ' in Bar (at **)\n' + - ' in Foo (at **)' - : ''), - ]); - } }); it('throws if a plain object is used as a child', async () => { @@ -693,9 +644,6 @@ describe('ReactComponent', () => { 'Or maybe you meant to call this function rather than return it.\n' + ' {Foo}\n' + ' in span (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in div (at **)\n') + ' in Foo (at **)', ]); }); @@ -753,9 +701,6 @@ describe('ReactComponent', () => { 'Or maybe you meant to call this function rather than return it.\n' + ' {Foo}\n' + ' in span (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in div (at **)\n') + ' in Foo (at **)', ]); await act(() => { diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js index 3d5f61c1ed520..5a70daef069ef 100644 --- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js @@ -1396,9 +1396,6 @@ describe('ReactCompositeComponent', () => { 'Cannot update a component (`A`) while rendering a different component (`B`). ' + 'To locate the bad setState() call inside `B`, ' + 'follow the stack trace as described in https://react.dev/link/setstate-in-render\n' + - (gate('enableOwnerStacks') - ? '' - : ' in B (at **)\n' + ' in div (at **)\n') + ' in Parent (at **)', ]); diff --git a/packages/react-dom/src/__tests__/ReactDOM-test.js b/packages/react-dom/src/__tests__/ReactDOM-test.js index 0d4a758a3c298..ff8296544546e 100644 --- a/packages/react-dom/src/__tests__/ReactDOM-test.js +++ b/packages/react-dom/src/__tests__/ReactDOM-test.js @@ -545,7 +545,6 @@ describe('ReactDOM', () => { // ReactDOM(App > div > span) 'Invalid ARIA attribute `ariaTypo`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + ' in span (at **)\n' + - (gate(flags => flags.enableOwnerStacks) ? '' : ' in div (at **)\n') + ' in App (at **)', // ReactDOM(App > div > ServerEntry) >>> ReactDOMServer(Child) >>> ReactDOMServer(App2) >>> ReactDOMServer(blink) 'Invalid ARIA attribute `ariaTypo2`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + @@ -562,7 +561,6 @@ describe('ReactDOM', () => { // ReactDOM(App > div > font) 'Invalid ARIA attribute `ariaTypo5`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + ' in font (at **)\n' + - (gate(flags => flags.enableOwnerStacks) ? '' : ' in div (at **)\n') + ' in App (at **)', ]); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js index 65f82dcd3690a..0f0986dde8e38 100644 --- a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js @@ -1845,8 +1845,7 @@ describe('ReactDOMComponent', () => { assertConsoleErrorDev([ 'The tag is unrecognized in this browser. ' + 'If you meant to render a React component, start its name with an uppercase letter.\n' + - ' in menuitem (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in menu (at **)'), + ' in menuitem (at **)', ]); }); @@ -2242,10 +2241,7 @@ describe('ReactDOMComponent', () => { '>
\n' + '> \n' + ' ...\n' + - '\n in tr (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in div (at **)'), + '\n in tr (at **)', ]); }); @@ -2264,10 +2260,7 @@ describe('ReactDOMComponent', () => { 'In HTML,

cannot be a descendant of

.\n' + 'This will cause a hydration error.' + // There is no outer `p` here because root container is not part of the stack. - '\n in p (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in span (at **)'), + '\n in p (at **)', ]); }); @@ -2294,92 +2287,48 @@ describe('ReactDOMComponent', () => { await act(() => { root.render(); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a child of ' + - '. Add a , or to your code to match the DOM tree generated ' + - 'by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '>
\n' + - ' \n' + - '> \n' + - ' ...\n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in Foo (at **)', - '
cannot contain a nested .\nSee this log for the ancestor stack trace.' + - '\n in table (at **)' + - '\n in Foo (at **)', - 'In HTML, text nodes cannot be a ' + - 'child of .\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '
\n' + - ' \n' + - ' \n' + - '> x\n' + - ' ...\n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in Foo (at **)', - 'In HTML, whitespace text nodes cannot ' + - "be a child of
. Make sure you don't have any extra " + - 'whitespace between tags on each line of your source code.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '>
\n' + - ' \n' + - '> {" "}\n' + - '\n in table (at **)' + - '\n in Foo (at **)', - ] - : [ - 'In HTML, cannot be a child of ' + - '
. Add a , or to your code to match the DOM tree generated ' + - 'by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '>
\n' + - ' \n' + - '> \n' + - ' ...\n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in table (at **)' + - '\n in Foo (at **)', - 'In HTML, text nodes cannot be a ' + - 'child of .\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '
\n' + - ' \n' + - ' \n' + - '> x\n' + - ' ...\n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in table (at **)' + - '\n in Foo (at **)', - 'In HTML, whitespace text nodes cannot ' + - "be a child of
. Make sure you don't have any extra " + - 'whitespace between tags on each line of your source code.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '>
\n' + - ' \n' + - '> {" "}\n' + - '\n in table (at **)' + - '\n in Foo (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a child of ' + + '
. Add a , or to your code to match the DOM tree generated ' + + 'by the browser.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + '>
\n' + + ' \n' + + '> \n' + + ' ...\n' + + '\n in tr (at **)' + + '\n in Row (at **)' + + '\n in Foo (at **)', + '
cannot contain a nested .\nSee this log for the ancestor stack trace.' + + '\n in table (at **)' + + '\n in Foo (at **)', + 'In HTML, text nodes cannot be a ' + + 'child of .\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + '
\n' + + ' \n' + + ' \n' + + '> x\n' + + ' ...\n' + + '\n in tr (at **)' + + '\n in Row (at **)' + + '\n in Foo (at **)', + 'In HTML, whitespace text nodes cannot ' + + "be a child of
. Make sure you don't have any extra " + + 'whitespace between tags on each line of your source code.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + '>
\n' + + ' \n' + + '> {" "}\n' + + '\n in table (at **)' + + '\n in Foo (at **)', + ]); }); it('warns nicely for updating table rows to use text', async () => { @@ -2445,12 +2394,7 @@ describe('ReactDOMComponent', () => { ' \n' + '> text\n' + '\n in tr (at **)' + - '\n in Row (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in tbody (at **)' + - '\n in table (at **)' + - '\n in Foo (at **)'), + '\n in Row (at **)', ]); }); @@ -2477,49 +2421,28 @@ describe('ReactDOMComponent', () => { await act(() => { root.render(); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - ' \n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)' + - '\n in Viz1 (at **)' + - '\n in App1 (at **)', - '
cannot contain a nested .\n' + - 'See this log for the ancestor stack trace.\n' + - ' in table (at **)\n' + - ' in Viz1 (at **)\n' + - ' in App1 (at **)', - ] - : [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - ' \n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)' + - '\n in table (at **)' + - '\n in Viz1 (at **)' + - '\n in App1 (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a child of
. ' + + 'Add a , or to your code to match the DOM tree generated by the browser.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + ' \n' + + '>
\n' + + ' \n' + + ' \n' + + '> \n' + + '\n in tr (at **)' + + '\n in Row (at **)' + + '\n in FancyRow (at **)' + + '\n in Viz1 (at **)' + + '\n in App1 (at **)', + '
cannot contain a nested .\n' + + 'See this log for the ancestor stack trace.\n' + + ' in table (at **)\n' + + ' in Viz1 (at **)\n' + + ' in App1 (at **)', + ]); }); it('gives useful context in warnings 2', async () => { @@ -2558,57 +2481,32 @@ describe('ReactDOMComponent', () => { await act(() => { root.render(); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - ' \n' + - ' \n' + - '
\n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)' + - '\n in Viz2 (at **)' + - '\n in App2 (at **)', - '
cannot contain a nested .\n' + - 'See this log for the ancestor stack trace.\n' + - ' in table (at **)\n' + - ' in Table (at **)\n' + - ' in FancyTable (at **)\n' + - ' in Viz2 (at **)\n' + - ' in App2 (at **)', - ] - : [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - ' \n' + - ' \n' + - '
\n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)' + - '\n in table (at **)' + - '\n in Table (at **)' + - '\n in FancyTable (at **)' + - '\n in Viz2 (at **)' + - '\n in App2 (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a child of
. ' + + 'Add a , or to your code to match the DOM tree generated by the browser.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + ' \n' + + ' \n' + + '
\n' + + '>
\n' + + ' \n' + + ' \n' + + '> \n' + + '\n in tr (at **)' + + '\n in Row (at **)' + + '\n in FancyRow (at **)' + + '\n in Viz2 (at **)' + + '\n in App2 (at **)', + '
cannot contain a nested .\n' + + 'See this log for the ancestor stack trace.\n' + + ' in table (at **)\n' + + ' in Table (at **)\n' + + ' in FancyTable (at **)\n' + + ' in Viz2 (at **)\n' + + ' in App2 (at **)', + ]); }); it('gives useful context in warnings 3', async () => { @@ -2640,47 +2538,26 @@ describe('ReactDOMComponent', () => { , ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '
\n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)', - '
cannot contain a nested .\n' + - 'See this log for the ancestor stack trace.' + - '\n in table (at **)' + - '\n in Table (at **)' + - '\n in FancyTable (at **)', - ] - : [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '
\n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)' + - '\n in table (at **)' + - '\n in Table (at **)' + - '\n in FancyTable (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a child of
. ' + + 'Add a , or to your code to match the DOM tree generated by the browser.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + '
\n' + + '>
\n' + + ' \n' + + ' \n' + + '> \n' + + '\n in tr (at **)' + + '\n in Row (at **)' + + '\n in FancyRow (at **)', + '
cannot contain a nested .\n' + + 'See this log for the ancestor stack trace.' + + '\n in table (at **)' + + '\n in Table (at **)' + + '\n in FancyTable (at **)', + ]); }); it('gives useful context in warnings 4', async () => { @@ -2701,39 +2578,22 @@ describe('ReactDOMComponent', () => {
, ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a child of . ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)', - '
cannot contain a nested .\n' + - 'See this log for the ancestor stack trace.' + - '\n in table (at **)', - ] - : [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - '>
\n' + - ' \n' + - ' \n' + - '> \n' + - '\n in tr (at **)' + - '\n in Row (at **)' + - '\n in FancyRow (at **)' + - '\n in table (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a child of
. ' + + 'Add a , or to your code to match the DOM tree generated by the browser.\n' + + 'This will cause a hydration error.\n' + + '\n' + + '>
\n' + + ' \n' + + ' \n' + + '> \n' + + '\n in tr (at **)' + + '\n in Row (at **)' + + '\n in FancyRow (at **)', + '
cannot contain a nested .\n' + + 'See this log for the ancestor stack trace.' + + '\n in table (at **)', + ]); }); it('gives useful context in warnings 5', async () => { @@ -2758,39 +2618,22 @@ describe('ReactDOMComponent', () => { , ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '
\n' + - '>
\n' + - '> \n' + - '\n in tr (at **)', - '
cannot contain a nested .\n' + - 'See this log for the ancestor stack trace.' + - '\n in table (at **)' + - '\n in Table (at **)' + - '\n in FancyTable (at **)', - ] - : [ - 'In HTML, cannot be a child of
. ' + - 'Add a , or to your code to match the DOM tree generated by the browser.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '
\n' + - '>
\n' + - '> \n' + - '\n in tr (at **)' + - '\n in table (at **)' + - '\n in Table (at **)' + - '\n in FancyTable (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a child of
. ' + + 'Add a , or to your code to match the DOM tree generated by the browser.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + '
\n' + + '>
\n' + + '> \n' + + '\n in tr (at **)', + '
cannot contain a nested .\n' + + 'See this log for the ancestor stack trace.' + + '\n in table (at **)' + + '\n in Table (at **)' + + '\n in FancyTable (at **)', + ]); class Link extends React.Component { render() { @@ -2807,40 +2650,22 @@ describe('ReactDOMComponent', () => { , ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, cannot be a descendant of .\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '> \n' + - '
\n' + - ' \n' + - '> \n' + - '\n in a (at **)' + - '\n in Link (at **)', - ' cannot contain a nested .\n' + - 'See this log for the ancestor stack trace.' + - '\n in a (at **)' + - '\n in Link (at **)', - ] - : [ - 'In HTML, cannot be a descendant of .\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' \n' + - '> \n' + - '
\n' + - ' \n' + - '> \n' + - '\n in a (at **)' + - '\n in Link (at **)' + - '\n in div (at **)' + - '\n in a (at **)' + - '\n in Link (at **)', - ], - ); + assertConsoleErrorDev([ + 'In HTML, cannot be a descendant of .\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' \n' + + '> \n' + + '
\n' + + ' \n' + + '> \n' + + '\n in a (at **)' + + '\n in Link (at **)', + ' cannot contain a nested .\n' + + 'See this log for the ancestor stack trace.' + + '\n in a (at **)' + + '\n in Link (at **)', + ]); }); it('should warn about incorrect casing on properties (ssr)', () => { @@ -3099,11 +2924,9 @@ describe('ReactDOMComponent', () => { }); assertConsoleErrorDev([ 'Invalid DOM property `class`. Did you mean `className`?\n' + - ' in span (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in div (at **)'), + ' in span (at **)', 'Invalid event handler property `onclick`. Did you mean `onClick`?\n' + - ' in strong (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in div (at **)'), + ' in strong (at **)', ]); }); @@ -3119,12 +2942,10 @@ describe('ReactDOMComponent', () => { ); assertConsoleErrorDev([ 'Invalid DOM property `class`. Did you mean `className`?\n' + - ' in span (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in div (at **)'), + ' in span (at **)', 'Invalid event handler property `onclick`. ' + 'React events use the camelCase naming convention, for example `onClick`.\n' + - ' in strong (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in div (at **)'), + ' in strong (at **)', ]); }); @@ -3175,12 +2996,10 @@ describe('ReactDOMComponent', () => { 'Invalid DOM property `class`. Did you mean `className`?\n' + ' in span (at **)\n' + ' in Child1 (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in div (at **)\n') + ' in Parent (at **)', 'Invalid event handler property `onclick`. Did you mean `onClick`?\n' + ' in strong (at **)\n' + ' in Child3 (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in div (at **)\n') + ' in Parent (at **)', ]); }); @@ -3230,13 +3049,11 @@ describe('ReactDOMComponent', () => { 'Invalid DOM property `class`. Did you mean `className`?\n' + ' in span (at **)\n' + ' in Child1 (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in div (at **)\n') + ' in Parent (at **)', 'Invalid event handler property `onclick`. ' + 'React events use the camelCase naming convention, for example `onClick`.\n' + ' in strong (at **)\n' + ' in Child3 (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in div (at **)\n') + ' in Parent (at **)', ]); }); @@ -3362,8 +3179,7 @@ describe('ReactDOMComponent', () => { }); assertConsoleErrorDev([ 'Invalid DOM property `arabic-form`. Did you mean `arabicForm`?\n' + - ' in text (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in svg (at **)'), + ' in text (at **)', ]); const text = el.querySelector('text'); @@ -3844,8 +3660,7 @@ describe('ReactDOMComponent', () => { }); assertConsoleErrorDev([ 'Invalid DOM property `x-height`. Did you mean `xHeight`?\n' + - ' in font-face (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in svg (at **)'), + ' in font-face (at **)', ]); expect(el.querySelector('font-face').hasAttribute('x-height')).toBe( @@ -3871,8 +3686,7 @@ describe('ReactDOMComponent', () => { 'whatever="false" or whatever={value.toString()}.\n\n' + 'If you used to conditionally omit it with whatever={condition && value}, ' + 'pass whatever={condition ? value : undefined} instead.\n' + - ' in font-face (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in svg (at **)'), + ' in font-face (at **)', ]); expect(el.querySelector('font-face').hasAttribute('whatever')).toBe( diff --git a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js index 9784e4f849897..87cc136530393 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js @@ -751,7 +751,6 @@ describe('ReactDOMFiber', () => { ' in Parent (at **)', 'Component uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Component (at **)\n') + ' in Parent (at **)', ]); expect(container.innerHTML).toBe(''); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js index 5b88ac54c794b..f64cdd8bb8e39 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js @@ -386,7 +386,6 @@ describe('ReactDOMFizzForm', () => { 'Cannot specify a formTarget for a button that specifies a function as a formAction. ' + 'The function will always be executed in the same window.\n' + ' in input (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in form (at **)\n') + ' in App (at **)', ]); let root; diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 38cb7798d5f6f..5b44d9e02e855 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -1843,15 +1843,9 @@ describe('ReactDOMFizzServer', () => { assertConsoleErrorDev([ ' is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.' + '\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in inCorrectTag (at **)\n' + - ' in C (at **)\n' + - ' in A (at **)' - : ' in inCorrectTag (at **)\n' + - ' in C (at **)\n' + - ' in Suspense (at **)\n' + - ' in div (at **)\n' + - ' in A (at **)'), + ' in inCorrectTag (at **)\n' + + ' in C (at **)\n' + + ' in A (at **)', ]); await act(() => { @@ -1862,17 +1856,11 @@ describe('ReactDOMFizzServer', () => { assertConsoleErrorDev([ 'Each child in a list should have a unique "key" prop.\n\nCheck the render method of `B`.' + ' See https://react.dev/link/warning-keys for more information.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in span (at **)\n' + - ' in mapper (at **)\n' + - ' in Array.map (at **)\n' + - ' in B (at **)\n' + - ' in A (at **)' - : ' in span (at **)\n' + - ' in B (at **)\n' + - ' in Suspense (at **)\n' + - ' in div (at **)\n' + - ' in A (at **)'), + ' in span (at **)\n' + + ' in mapper (at **)\n' + + ' in Array.map (at **)\n' + + ' in B (at **)\n' + + ' in A (at **)', ]); expect(getVisibleChildren(container)).toEqual( @@ -1954,13 +1942,7 @@ describe('ReactDOMFizzServer', () => { ' in TestProvider (at **)', 'TestConsumer uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in TestConsumer (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in TestProvider (at **)' + - '\n in Suspense (at **)' + - '\n in div (at **)' + - '\n in TestProvider (at **)'), + ' in TestConsumer (at **)', ]); expect(getVisibleChildren(container)).toEqual(
@@ -5871,7 +5853,6 @@ describe('ReactDOMFizzServer', () => { 'If your `children` prop is using this form try rewriting it using a template string: ' + '{`hello ${nameOfUser}`}.\n' + ' in title (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in head (at **)\n') + ' in App (at **)', ]); @@ -5917,7 +5898,6 @@ describe('ReactDOMFizzServer', () => { 'React Component try moving the tag into that component. ' + 'If the `children` of <title> is some HTML markup change it to be Text only to be valid HTML.\n' + ' in title (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in head (at **)\n') + ' in App (at **)', ]); // object titles are toStringed when float is on @@ -5961,7 +5941,6 @@ describe('ReactDOMFizzServer', () => { 'string or number value if so. Otherwise implement a `toString` method that React can ' + 'use to produce a valid <title>.\n' + ' in title (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in head (at **)\n') + ' in App (at **)', ]); // object titles are toStringed when float is on @@ -6545,23 +6524,11 @@ describe('ReactDOMFizzServer', () => { assertConsoleErrorDev([ 'A script element was rendered with a number for children. If script element has children it must be a single string. Consider using dangerouslySetInnerHTML or passing a plain string as children.' + - componentStack( - gate(flags => flags.enableOwnerStacks) - ? ['script', 'App'] - : ['script', 'body', 'html', 'App'], - ), + componentStack(['script', 'App']), 'A script element was rendered with an array for children. If script element has children it must be a single string. Consider using dangerouslySetInnerHTML or passing a plain string as children.' + - componentStack( - gate(flags => flags.enableOwnerStacks) - ? ['script', 'App'] - : ['script', 'body', 'html', 'App'], - ), + componentStack(['script', 'App']), 'A script element was rendered with something unexpected for children. If script element has children it must be a single string. Consider using dangerouslySetInnerHTML or passing a plain string as children.' + - componentStack( - gate(flags => flags.enableOwnerStacks) - ? ['script', 'App'] - : ['script', 'body', 'html', 'App'], - ), + componentStack(['script', 'App']), ]); }); @@ -8544,7 +8511,7 @@ describe('ReactDOMFizzServer', () => { expect(document.body.textContent).toBe('HelloWorld'); }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can get the component owner stacks during rendering in dev', async () => { let stack; @@ -8577,7 +8544,7 @@ describe('ReactDOMFizzServer', () => { ); }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can get the component owner stacks for onError in dev', async () => { const thrownError = new Error('hi'); let caughtError; @@ -9111,56 +9078,30 @@ describe('ReactDOMFizzServer', () => { </body> </html>, ); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - [ - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', - {withoutStack: true}, - ], - 'In HTML, <meta> cannot be a child of <html>.\nThis will cause a hydration error.' + - '\n' + - '\n <App>' + - '\n> <html>' + - '\n <Suspense fallback="this fallb...">' + - '\n <meta>' + - '\n> <meta itemProp="" content="before">' + - '\n ...' + - '\n' + - '\n in meta (at **)' + - '\n in App (at **)', - '<html> cannot contain a nested <meta>.\nSee this log for the ancestor stack trace.' + - '\n in html (at **)' + - '\n in App (at **)', - [ - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', - {withoutStack: true}, - ], - ]); - } else { - assertConsoleErrorDev([ - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.' + - '\n in Suspense (at **)' + - '\n in html (at **)' + - '\n in App (at **)', - 'In HTML, <meta> cannot be a child of <html>.\nThis will cause a hydration error.' + - '\n' + - '\n <App>' + - '\n> <html>' + - '\n <Suspense fallback="this fallb...">' + - '\n <meta>' + - '\n> <meta itemProp="" content="before">' + - '\n ...' + - '\n' + - '\n in meta (at **)' + - '\n in Suspense (at **)' + - '\n in html (at **)' + - '\n in App (at **)', - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.' + - '\n in Suspense (at **)' + - '\n in html (at **)' + - '\n in App (at **)', - ]); - } + assertConsoleErrorDev([ + [ + 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', + {withoutStack: true}, + ], + 'In HTML, <meta> cannot be a child of <html>.\nThis will cause a hydration error.' + + '\n' + + '\n <App>' + + '\n> <html>' + + '\n <Suspense fallback="this fallb...">' + + '\n <meta>' + + '\n> <meta itemProp="" content="before">' + + '\n ...' + + '\n' + + '\n in meta (at **)' + + '\n in App (at **)', + '<html> cannot contain a nested <meta>.\nSee this log for the ancestor stack trace.' + + '\n in html (at **)' + + '\n in App (at **)', + [ + 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', + {withoutStack: true}, + ], + ]); await root.unmount(); expect(getVisibleChildren(document)).toEqual( @@ -9253,56 +9194,30 @@ describe('ReactDOMFizzServer', () => { </body> </html>, ); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - [ - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', - {withoutStack: true}, - ], - 'In HTML, <meta> cannot be a child of <html>.\nThis will cause a hydration error.' + - '\n' + - '\n <App>' + - '\n> <html>' + - '\n <Suspense fallback="this fallb...">' + - '\n <meta>' + - '\n> <meta itemProp="" content="before">' + - '\n ...' + - '\n' + - '\n in meta (at **)' + - '\n in App (at **)', - '<html> cannot contain a nested <meta>.\nSee this log for the ancestor stack trace.' + - '\n in html (at **)' + - '\n in App (at **)', - [ - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', - {withoutStack: true}, - ], - ]); - } else { - assertConsoleErrorDev([ - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.' + - '\n in Suspense (at **)' + - '\n in html (at **)' + - '\n in App (at **)', - 'In HTML, <meta> cannot be a child of <html>.\nThis will cause a hydration error.' + - '\n' + - '\n <App>' + - '\n> <html>' + - '\n <Suspense fallback="this fallb...">' + - '\n <meta>' + - '\n> <meta itemProp="" content="before">' + - '\n ...' + - '\n' + - '\n in meta (at **)' + - '\n in Suspense (at **)' + - '\n in html (at **)' + - '\n in App (at **)', - 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.' + - '\n in Suspense (at **)' + - '\n in html (at **)' + - '\n in App (at **)', - ]); - } + assertConsoleErrorDev([ + [ + 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', + {withoutStack: true}, + ], + 'In HTML, <meta> cannot be a child of <html>.\nThis will cause a hydration error.' + + '\n' + + '\n <App>' + + '\n> <html>' + + '\n <Suspense fallback="this fallb...">' + + '\n <meta>' + + '\n> <meta itemProp="" content="before">' + + '\n ...' + + '\n' + + '\n in meta (at **)' + + '\n in App (at **)', + '<html> cannot contain a nested <meta>.\nSee this log for the ancestor stack trace.' + + '\n in html (at **)' + + '\n in App (at **)', + [ + 'Cannot render a <meta> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <meta> remove the `itemProp` prop. Otherwise, try moving this tag into the <head> or <body> of the Document.', + {withoutStack: true}, + ], + ]); await root.unmount(); expect(getVisibleChildren(document)).toEqual( diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index 4375ddb7d8deb..0eb75f866c8c2 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -529,8 +529,7 @@ describe('ReactDOMFloat', () => { '> <template>\n' + ' ...\n' + '\n' + - ' in template (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in template (at **)', ]); root.render( @@ -555,8 +554,7 @@ describe('ReactDOMFloat', () => { ' <body>\n' + '> <style>\n' + '\n' + - ' in style (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in style (at **)', ]); root.render( @@ -596,8 +594,7 @@ describe('ReactDOMFloat', () => { ' <body>\n' + '> <script href="foo">\n' + '\n' + - ' in script (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in script (at **)', ]); root.render( @@ -2269,21 +2266,11 @@ body { 'spell it as lowercase `nonstandardattr` instead. If you accidentally passed it from a ' + 'parent component, remove it from the DOM element.\n' + ' in link (at **)\n' + - (gate('enableOwnerStacks') - ? '' - : ' in div (at **)\n' + - ' in body (at **)\n' + - ' in html (at **)\n') + ' in App (at **)', 'Invalid values for props `shouldnotincludefunctions`, `norsymbols` on <link> tag. ' + 'Either remove them from the element, or pass a string or number value to keep them in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + ' in link (at **)\n' + - (gate('enableOwnerStacks') - ? '' - : ' in div (at **)\n' + - ' in body (at **)\n' + - ' in html (at **)\n') + ' in App (at **)', ]); @@ -2642,8 +2629,7 @@ body { '> <html>\n' + '> <meta itemProp="foo">' + '\n' + - '\n in meta (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + '\n in meta (at **)', ]); }); @@ -2668,8 +2654,7 @@ body { '> <html>\n' + '> <title itemProp="foo">' + '\n' + - '\n in title (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + '\n in title (at **)', ]); }); @@ -2694,8 +2679,7 @@ body { '> <html>\n' + '> <style itemProp="foo">' + '\n' + - '\n in style (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + '\n in style (at **)', ]); }); @@ -2720,8 +2704,7 @@ body { '> <html>\n' + '> <link itemProp="foo">\n' + '\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in link (at **)', ]); }); @@ -2746,8 +2729,7 @@ body { '> <html>\n' + '> <script itemProp="foo">\n' + '\n' + - ' in script (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in script (at **)', ]); }); @@ -4844,9 +4826,6 @@ body { 'React encountered a hoistable style tag for the same href as a preload: "foo". ' + 'When using a style tag to inline styles you should not also preload it as a stylsheet.\n' + ' in style (at **)\n' + - (gate('enableOwnerStacks') - ? '' - : ' in body (at **)\n' + ' in html (at **)\n') + ' in App (at **)', ]); @@ -7753,64 +7732,43 @@ body { 'If your intent was to have React hoist and deduplciate this stylesheet using the ' + '`precedence` prop ensure there is a non-empty string `href` prop as well, ' + 'otherwise remove the `precedence` prop.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and ' + 'expected the `href` prop to be a non-empty string but ecountered an empty string instead. ' + 'If your intent was to have React hoist and deduplciate this stylesheet using the ' + '`precedence` prop ensure there is a non-empty string `href` prop as well, ' + 'otherwise remove the `precedence` prop.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', 'An empty string ("") was passed to the href attribute. ' + 'To fix this, either do not render the element at all or ' + 'pass null to href instead of an empty string.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and ' + '`onLoad` and `onError` props. The presence of loading and error handlers indicates ' + 'an intent to manage the stylesheet loading state from your from your Component code ' + 'and React will not hoist or deduplicate this stylesheet. ' + 'If your intent was to have React hoist and deduplciate this stylesheet using the ' + '`precedence` prop remove the `onLoad` and `onError` props, otherwise remove the `precedence` prop.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and ' + '`onLoad` prop. The presence of loading and error handlers indicates an intent to ' + 'manage the stylesheet loading state from your from your Component code and ' + 'React will not hoist or deduplicate this stylesheet. ' + 'If your intent was to have React hoist and deduplciate this stylesheet using the ' + '`precedence` prop remove the `onLoad` prop, otherwise remove the `precedence` prop.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and `onError` prop. ' + 'The presence of loading and error handlers indicates an intent to manage the stylesheet loading state ' + 'from your from your Component code and React will not hoist or deduplicate this stylesheet. ' + 'If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` ' + 'prop remove the `onError` prop, otherwise remove the `precedence` prop.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and a `disabled` prop. ' + 'The presence of the `disabled` prop indicates an intent to manage the stylesheet active state from ' + 'your from your Component code and React will not hoist or deduplicate this stylesheet. ' + 'If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` ' + 'prop remove the `disabled` prop, otherwise remove the `precedence` prop.\n' + - ' in link (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in link (at **)', ].filter(Boolean), ); @@ -7836,8 +7794,7 @@ body { 'loading state from your from your Component code and React will not hoist or deduplicate this stylesheet. ' + 'If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` ' + 'prop remove the `onLoad` and `onError` props, otherwise remove the `precedence` prop.\n' + - ' in body (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in body (at **)', ]); }); @@ -8429,10 +8386,7 @@ background-color: green; 'using the `precedence` prop to not have any spaces but ecountered spaces instead. ' + 'using spaces in this prop will cause hydration of this style to fail on the client. ' + 'The href for the <style> where this ocurred is "foo bar".\n' + - ' in style (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in body (at **)' + '\n in html (at **)'), + ' in style (at **)', ]); }); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMForm-test.js b/packages/react-dom/src/__tests__/ReactDOMForm-test.js index d0977fa9926da..93edccf9bcef3 100644 --- a/packages/react-dom/src/__tests__/ReactDOMForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMForm-test.js @@ -391,8 +391,7 @@ describe('ReactDOMForm', () => { '> <form action={function outerAction}>\n' + ' <input>\n' + '> <form action={function innerAction} ref={{current:null}}>\n' + - '\n in form (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in form (at **)'), + '\n in form (at **)', ]); await submit(ref.current); @@ -588,8 +587,7 @@ describe('ReactDOMForm', () => { 'Cannot specify a "name" prop for a button that specifies a function as a formAction. ' + 'React needs it to encode which action should be invoked. ' + 'It will get overridden.\n' + - ' in input (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in form (at **)'), + ' in input (at **)', ]); await submit(inputRef.current); @@ -1971,7 +1969,6 @@ describe('ReactDOMForm', () => { 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + ' in button (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in form (at **)\n') + ' in App (at **)', ]); await submit(buttonRef.current); diff --git a/packages/react-dom/src/__tests__/ReactDOMOption-test.js b/packages/react-dom/src/__tests__/ReactDOMOption-test.js index cb4270cd1ea20..9859fb67f1270 100644 --- a/packages/react-dom/src/__tests__/ReactDOMOption-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMOption-test.js @@ -59,10 +59,7 @@ describe('ReactDOMOption', () => { '> <div>\n' + ' ...\n' + '\n' + - ' in div (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in option (at **)'), + ' in div (at **)', ]); expect(container.firstChild.innerHTML).toBe('1 <div></div> 2'); await renderIntoDocument(el); @@ -279,10 +276,7 @@ describe('ReactDOMOption', () => { '> <div ref={{current:null}}>\n' + ' ...\n' + '\n' + - ' in div (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in option (at **)' + '\n in select (at **)'), + ' in div (at **)', ]); option = container.firstChild.firstChild; diff --git a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js index d36700ff124cc..f05fb3e372697 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js @@ -794,7 +794,6 @@ describe('ReactDOMSelect', () => { 'Use the `defaultValue` or `value` props on <select> instead of ' + 'setting `selected` on <option>.\n' + ' in option (at **)\n' + - (gate('enableOwnerStacks') ? '' : ' in select (at **)\n') + ' in App (at **)', ]); @@ -1072,8 +1071,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); const node = container.firstChild; @@ -1096,8 +1094,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); let node = container.firstChild; @@ -1134,8 +1131,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); const node = container.firstChild; @@ -1158,8 +1154,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); let node = container.firstChild; @@ -1199,8 +1194,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); const node = container.firstChild; @@ -1223,8 +1217,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); const node = container.firstChild; @@ -1247,8 +1240,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); let node = container.firstChild; @@ -1284,8 +1276,7 @@ describe('ReactDOMSelect', () => { 'Invalid value for prop `value` on <option> tag. ' + 'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' + 'For details, see https://react.dev/link/attribute-behavior \n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); let node = container.firstChild; @@ -1366,12 +1357,10 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); }); @@ -1395,12 +1384,10 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); }); @@ -1467,8 +1454,7 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); }); @@ -1508,8 +1494,7 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', 'Form field values (value, checked, defaultValue, or defaultChecked props)' + ' must be strings, not TemporalLike. ' + 'This value must be coerced to a string before using it here.\n' + @@ -1563,12 +1548,10 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); }); @@ -1591,12 +1574,10 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); }); @@ -1670,12 +1651,10 @@ describe('ReactDOMSelect', () => { assertConsoleErrorDev([ 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', 'The provided `value` attribute is an unsupported type TemporalLike.' + ' This value must be coerced to a string before using it here.\n' + - ' in option (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in select (at **)'), + ' in option (at **)', ]); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js index 50d189cc0860f..04475485eb879 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js @@ -995,18 +995,6 @@ describe('ReactDOMServerIntegration', () => { async render => { let EmptyComponent = {}; EmptyComponent = <EmptyComponent />; - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [] - : [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: object. You likely forgot to export your ' + - "component from the file it's defined in, or you might have mixed up " + - 'default and named imports.', - ], - {withoutStack: true}, - ); await render(EmptyComponent); }, 'Element type is invalid: expected a string (for built-in components) or a class/function ' + @@ -1022,16 +1010,6 @@ describe('ReactDOMServerIntegration', () => { async render => { let NullComponent = null; NullComponent = <NullComponent />; - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [] - : [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: null.', - ], - {withoutStack: true}, - ); await render(NullComponent); }, 'Element type is invalid: expected a string (for built-in components) or a class/function ' + @@ -1043,19 +1021,6 @@ describe('ReactDOMServerIntegration', () => { async render => { let UndefinedComponent = undefined; UndefinedComponent = <UndefinedComponent />; - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [] - : [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: undefined. You likely forgot to export your ' + - "component from the file it's defined in, or you might have mixed up " + - 'default and named imports.', - ], - {withoutStack: true}, - ); - await render(UndefinedComponent); }, 'Element type is invalid: expected a string (for built-in components) or a class/function ' + diff --git a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js index 0c39a0b7fb339..f72a4e9eecdbc 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js @@ -286,7 +286,6 @@ describe('ReactDOMServerLifecycles', () => { 'usually means you called setState() outside componentWillMount() on ' + 'the server. This is a no-op.\n\n' + 'Please check the code for the Outer component.\n' + - (gate('enableOwnerStacks') ? '' : ' in Inner (at **)\n') + ' in Outer (at **)', ]); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js index 1506d4881accb..5900a2f448590 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js @@ -1923,11 +1923,6 @@ describe('ReactDOMServerPartialHydration', () => { "Can't perform a React state update on a component that hasn't mounted yet. " + 'This indicates that you have a side-effect in your render function that ' + 'asynchronously later calls tries to update the component. Move this work to useEffect instead.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Child (at **)\n' + - ' in Suspense (at **)\n' + - ' in div (at **)\n') + ' in App (at **)', ]); diff --git a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js index 737df5ccc9b28..b47014dee9831 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js @@ -171,8 +171,7 @@ describe('ReactDOM HostSingleton', () => { 'children of these components will likely fail in unpredictable ways. ' + 'Please only render a single instance of <head> and if you need to mount a new one, ' + 'ensure any previous ones have unmounted first.\n' + - ' in head (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in html (at **)'), + ' in head (at **)', ]); expect(getVisibleChildren(document)).toEqual( <html> diff --git a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js index a617770cf5f3b..523f1167ab612 100644 --- a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js +++ b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js @@ -60,11 +60,13 @@ describe('ReactDeprecationWarnings', () => { </div>, ); await waitForAll([]); - assertConsoleErrorDev([ - 'FunctionalComponent: Support for defaultProps ' + - 'will be removed from memo components in a future major ' + - 'release. Use JavaScript default parameters instead.\n' + - ' in div (at **)', - ]); + assertConsoleErrorDev( + [ + 'FunctionalComponent: Support for defaultProps ' + + 'will be removed from memo components in a future major ' + + 'release. Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); }); }); diff --git a/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js index 5b9de4c97357a..28b85d359791b 100644 --- a/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js @@ -2381,17 +2381,6 @@ describe('ReactErrorBoundaries', () => { 'class/function (for composite components) but got: null.', ); - if (!gate('enableOwnerStacks')) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function ' + - '(for composite components) but got: null.', - ], - {withoutStack: true}, - ); - } - await expect(async () => { const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); @@ -2403,18 +2392,6 @@ describe('ReactErrorBoundaries', () => { 'expected a string (for built-in components) or a ' + 'class/function (for composite components) but got: undefined.', ); - if (!gate('enableOwnerStacks')) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function ' + - '(for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.', - ], - {withoutStack: true}, - ); - } }); it('renders empty output if error boundary does not handle the error', async () => { diff --git a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js index 7b69fad3a7649..dc117f544cdf7 100644 --- a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js @@ -116,7 +116,6 @@ describe('ReactFunctionComponent', () => { ' in GrandParent (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Child (at **)\n') + ' in Parent (at **)\n' + ' in GrandParent (at **)', ]); @@ -260,7 +259,6 @@ describe('ReactFunctionComponent', () => { ' in Parent (at **)', 'Child uses the legacy contextTypes API which will be removed soon. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in Child (at **)\n' + ' in Parent (at **)', ]); expect(el.textContent).toBe('en'); diff --git a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js index 40a6e9ee7bfea..1b0776f62655c 100644 --- a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js @@ -121,13 +121,9 @@ describe('ReactLegacyCompositeComponent', () => { assertConsoleErrorDev([ 'Child uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Child (at **)\n') + ' in Parent (at **)', 'Grandchild uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Grandchild (at **)\n' + ' in Child (at **)\n') + ' in Parent (at **)', ]); expect(findDOMNode(component).innerHTML).toBe('bar'); @@ -200,10 +196,7 @@ describe('ReactLegacyCompositeComponent', () => { ' in Parent (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Child (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in Middle (at **)' + '\n in Parent (at **)'), + ' in Child (at **)', ]); await act(() => { @@ -268,15 +261,9 @@ describe('ReactLegacyCompositeComponent', () => { assertConsoleErrorDev([ 'Parent uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Parent (at **)\n') + ' in Wrapper (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Child (at **)\n' + - ' in div (at **)\n' + - ' in Parent (at **)\n') + ' in Wrapper (at **)', ]); @@ -361,15 +348,12 @@ describe('ReactLegacyCompositeComponent', () => { ' in Parent (at **)', 'Child uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Child (at **)\n') + ' in Parent (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Child (at **)\n') + ' in Parent (at **)', 'Grandchild uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Grandchild (at **)\n') + ' in Child (at **)\n' + ' in Parent (at **)', ]); @@ -441,7 +425,6 @@ describe('ReactLegacyCompositeComponent', () => { assertConsoleErrorDev([ 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Child (at **)\n') + ' in Parent (at **)', ]); diff --git a/packages/react-dom/src/__tests__/ReactLegacyContextDisabled-test.internal.js b/packages/react-dom/src/__tests__/ReactLegacyContextDisabled-test.internal.js index 04c438fe716cf..98abd05ff4b52 100644 --- a/packages/react-dom/src/__tests__/ReactLegacyContextDisabled-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactLegacyContextDisabled-test.internal.js @@ -104,16 +104,10 @@ describe('ReactLegacyContextDisabled', () => { ' in LegacyProvider (at **)', 'LegacyClsConsumer uses the legacy contextTypes API which was removed in React 19. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in LegacyClsConsumer (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n' + ' in span (at **)\n' + ' in LegacyProvider (at **)'), + ' in LegacyClsConsumer (at **)', 'LegacyFnConsumer uses the legacy contextTypes API which was removed in React 19. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in LegacyFnConsumer (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n' + ' in span (at **)\n' + ' in LegacyProvider (at **)'), + ' in LegacyFnConsumer (at **)', ]); expect(container.textContent).toBe('{}undefinedundefined'); expect(lifecycleContextLog).toEqual([]); @@ -151,16 +145,10 @@ describe('ReactLegacyContextDisabled', () => { ' in LegacyProvider (at **)', 'LegacyClsConsumer uses the legacy contextTypes API which was removed in React 19. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in LegacyClsConsumer (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n' + ' in span (at **)\n' + ' in LegacyProvider (at **)'), + ' in LegacyClsConsumer (at **)', 'LegacyFnConsumer uses the legacy contextTypes API which was removed in React 19. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in LegacyFnConsumer (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n' + ' in span (at **)\n' + ' in LegacyProvider (at **)'), + ' in LegacyFnConsumer (at **)', ]); expect(text).toBe('<span>{}<!-- -->undefined<!-- -->undefined</span>'); expect(lifecycleContextLog).toEqual([{}, {}, {}]); diff --git a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js index 9e05838202b76..8c0505db92d9b 100644 --- a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js @@ -2112,16 +2112,6 @@ describe('ReactLegacyErrorBoundaries', () => { ReactDOM.render(<X />, container); }); }).rejects.toThrow('got: null'); - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function ' + - '(for composite components) but got: null.', - ], - {withoutStack: true}, - ); - } await expect(async () => { const container = document.createElement('div'); @@ -2129,16 +2119,6 @@ describe('ReactLegacyErrorBoundaries', () => { ReactDOM.render(<Y />, container); }); }).rejects.toThrow('got: undefined'); - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function ' + - '(for composite components) but got: undefined.', - ], - {withoutStack: true}, - ); - } }); // @gate !disableLegacyMode diff --git a/packages/react-dom/src/__tests__/ReactMultiChild-test.js b/packages/react-dom/src/__tests__/ReactMultiChild-test.js index 5fcd6ce6d526c..31c4e739b68d8 100644 --- a/packages/react-dom/src/__tests__/ReactMultiChild-test.js +++ b/packages/react-dom/src/__tests__/ReactMultiChild-test.js @@ -228,13 +228,7 @@ describe('ReactMultiChild', () => { 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.\n' + - ' in div (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in div (at **)' + - '\n in WrapperComponent (at **)' + - '\n in div (at **)' + - '\n in Parent (at **)'), + ' in div (at **)', ]); }); @@ -290,13 +284,7 @@ describe('ReactMultiChild', () => { 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.\n' + - ' in div (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in div (at **)' + - '\n in WrapperComponent (at **)' + - '\n in div (at **)' + - '\n in Parent (at **)'), + ' in div (at **)', ]); }); }); diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js index cbcf06892ab2e..2bf917d3c3d39 100644 --- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js @@ -758,20 +758,12 @@ describe('ReactDOMServer', () => { '<inPUT /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.\n' + - ' in inPUT (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in div (at **)'), + ' in inPUT (at **)', // linearGradient doesn't warn '<iFrame /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.\n' + - ' in iFrame (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in foreignObject (at **)' + - '\n in g (at **)' + - '\n in CompositeG (at **)' + - '\n in svg (at **)' + - '\n in div (at **)'), + ' in iFrame (at **)', ]); }); @@ -869,30 +861,14 @@ describe('ReactDOMServer', () => { ReactDOMServer.renderToString(<App />); assertConsoleErrorDev([ 'Invalid ARIA attribute `ariaTypo`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in span (at **)\n' + - ' in B (at **)\n' + - ' in Child (at **)\n' + - ' in App (at **)' - : ' in span (at **)\n' + - ' in b (at **)\n' + - ' in C (at **)\n' + - ' in font (at **)\n' + - ' in B (at **)\n' + - ' in Child (at **)\n' + - ' in span (at **)\n' + - ' in div (at **)\n' + - ' in App (at **)'), + ' in span (at **)\n' + + ' in B (at **)\n' + + ' in Child (at **)\n' + + ' in App (at **)', 'Invalid ARIA attribute `ariaTypo2`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in span (at **)\n' + - ' in Child (at **)\n' + - ' in App (at **)' - : ' in span (at **)\n' + - ' in Child (at **)\n' + - ' in span (at **)\n' + - ' in div (at **)\n' + - ' in App (at **)'), + ' in span (at **)\n' + + ' in Child (at **)\n' + + ' in App (at **)', ]); }); @@ -929,47 +905,30 @@ describe('ReactDOMServer', () => { assertConsoleErrorDev([ // ReactDOMServer(App > div > span) 'Invalid ARIA attribute `ariaTypo`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in span (at **)\n' + ' in App (at **)' - : ' in span (at **)\n' + - ' in div (at **)\n' + - ' in App (at **)'), + ' in span (at **)\n' + + ' in App (at **)', // ReactDOMServer(App > div > Child) >>> ReactDOMServer(App2) >>> ReactDOMServer(blink) 'Invalid ARIA attribute `ariaTypo2`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in blink (at **)\n' + - ' in App2 (at **)\n' + - ' in Child (at **)\n' + - ' in App (at **)' - : ' in blink (at **)'), + ' in blink (at **)\n' + + ' in App2 (at **)\n' + + ' in Child (at **)\n' + + ' in App (at **)', // ReactDOMServer(App > div > Child) >>> ReactDOMServer(App2 > Child2 > span) 'Invalid ARIA attribute `ariaTypo3`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in span (at **)\n' + - ' in Child2 (at **)\n' + - ' in App2 (at **)\n' + - ' in Child (at **)\n' + - ' in App (at **)' - : ' in span (at **)\n' + - ' in Child2 (at **)\n' + - ' in App2 (at **)'), + ' in span (at **)\n' + + ' in Child2 (at **)\n' + + ' in App2 (at **)\n' + + ' in Child (at **)\n' + + ' in App (at **)', // ReactDOMServer(App > div > Child > span) 'Invalid ARIA attribute `ariaTypo4`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in span (at **)\n' + - ' in Child (at **)\n' + - ' in App (at **)' - : ' in span (at **)\n' + - ' in Child (at **)\n' + - ' in div (at **)\n' + - ' in App (at **)'), + ' in span (at **)\n' + + ' in Child (at **)\n' + + ' in App (at **)', // ReactDOMServer(App > div > font) 'Invalid ARIA attribute `ariaTypo5`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in font (at **)\n' + ' in App (at **)' - : ' in font (at **)\n' + - ' in div (at **)\n' + - ' in App (at **)'), + ' in font (at **)\n' + + ' in App (at **)', ]); }); diff --git a/packages/react-dom/src/__tests__/ReactUpdates-test.js b/packages/react-dom/src/__tests__/ReactUpdates-test.js index fa1900002a228..bc7767b12efd1 100644 --- a/packages/react-dom/src/__tests__/ReactUpdates-test.js +++ b/packages/react-dom/src/__tests__/ReactUpdates-test.js @@ -1877,9 +1877,7 @@ describe('ReactUpdates', () => { const originalConsoleError = console.error; console.error = e => { error = e; - ownerStack = gate(flags => flags.enableOwnerStacks) - ? React.captureOwnerStack() - : null; + ownerStack = React.captureOwnerStack(); debugStack = new Error().stack; Scheduler.log('stop'); }; @@ -1895,11 +1893,7 @@ describe('ReactUpdates', () => { expect(error).toContain('Maximum update depth exceeded'); // The currently executing effect should be on the native stack expect(debugStack).toContain('at myEffect'); - if (gate(flags => flags.enableOwnerStacks)) { - expect(ownerStack).toContain('at App'); - } else { - expect(ownerStack).toBe(null); - } + expect(ownerStack).toContain('at App'); }); it('can have nested updates if they do not cross the limit', async () => { diff --git a/packages/react-dom/src/__tests__/validateDOMNesting-test.js b/packages/react-dom/src/__tests__/validateDOMNesting-test.js index 1f90b2d008b48..0c5de0cc259dd 100644 --- a/packages/react-dom/src/__tests__/validateDOMNesting-test.js +++ b/packages/react-dom/src/__tests__/validateDOMNesting-test.js @@ -121,34 +121,20 @@ describe('validateDOMNesting', () => { ); expectWarnings( ['div', 'ul', 'li', 'div', 'li'], - gate(flags => flags.enableOwnerStacks) - ? [ - 'In HTML, <li> cannot be a descendant of <li>.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' <ul>\n' + - '> <li>\n' + - ' <div>\n' + - '> <li>\n' + - '\n' + - ' in li (at **)', - '<li> cannot contain a nested <li>.\nSee this log for the ancestor stack trace.\n' + - ' in li (at **)', - ] - : [ - 'In HTML, <li> cannot be a descendant of <li>.\n' + - 'This will cause a hydration error.\n' + - '\n' + - ' <ul>\n' + - '> <li>\n' + - ' <div>\n' + - '> <li>\n' + - '\n' + - ' in li (at **)\n' + - ' in div (at **)\n' + - ' in li (at **)\n' + - ' in ul (at **)', - ], + + [ + 'In HTML, <li> cannot be a descendant of <li>.\n' + + 'This will cause a hydration error.\n' + + '\n' + + ' <ul>\n' + + '> <li>\n' + + ' <div>\n' + + '> <li>\n' + + '\n' + + ' in li (at **)', + '<li> cannot contain a nested <li>.\nSee this log for the ancestor stack trace.\n' + + ' in li (at **)', + ], ); expectWarnings( ['div', 'html'], @@ -208,28 +194,16 @@ describe('validateDOMNesting', () => { ); expectWarnings( ['svg', 'foreignObject', 'body', 'p'], - gate(flags => flags.enableOwnerStacks) - ? [ - // TODO, this should say "In SVG", - 'In HTML, <body> cannot be a child of <foreignObject>.\n' + - 'This will cause a hydration error.\n' + - '\n' + - '> <foreignObject>\n' + - '> <body>\n' + - '\n' + - ' in body (at **)', - ] - : [ - // TODO, this should say "In SVG", - 'In HTML, <body> cannot be a child of <foreignObject>.\n' + - 'This will cause a hydration error.\n' + - '\n' + - '> <foreignObject>\n' + - '> <body>\n' + - '\n' + - ' in body (at **)\n' + - ' in foreignObject (at **)', - ], + [ + // TODO, this should say "In SVG", + 'In HTML, <body> cannot be a child of <foreignObject>.\n' + + 'This will cause a hydration error.\n' + + '\n' + + '> <foreignObject>\n' + + '> <body>\n' + + '\n' + + ' in body (at **)', + ], ); }); diff --git a/packages/react-markup/src/__tests__/ReactMarkupClient-test.js b/packages/react-markup/src/__tests__/ReactMarkupClient-test.js index e1e8eab4068e2..e5f352bf40dff 100644 --- a/packages/react-markup/src/__tests__/ReactMarkupClient-test.js +++ b/packages/react-markup/src/__tests__/ReactMarkupClient-test.js @@ -233,9 +233,7 @@ if (!__EXPERIMENTAL__) { '\n in div (at **)', ); expect(normalizeCodeLocInfo(caughtErrors[0].ownerStack)).toBe( - __DEV__ && gate(flags => flags.enableOwnerStacks) - ? '\n in Bar (at **)' + '\n in Foo (at **)' - : null, + __DEV__ ? '\n in Bar (at **)' + '\n in Foo (at **)' : null, ); }); }); diff --git a/packages/react-markup/src/__tests__/ReactMarkupServer-test.js b/packages/react-markup/src/__tests__/ReactMarkupServer-test.js index afd6ea5df8f26..e334141972ea5 100644 --- a/packages/react-markup/src/__tests__/ReactMarkupServer-test.js +++ b/packages/react-markup/src/__tests__/ReactMarkupServer-test.js @@ -269,9 +269,7 @@ if (!__EXPERIMENTAL__) { : '\n in div (at **)' + '\n in div (at **)', ); expect(normalizeCodeLocInfo(caughtErrors[0].ownerStack)).toBe( - __DEV__ && gate(flags => flags.enableOwnerStacks) - ? '\n in Bar (at **)' + '\n in Foo (at **)' - : null, + __DEV__ ? '\n in Bar (at **)' + '\n in Foo (at **)' : null, ); }); }); diff --git a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js index 05116be30110f..5fbf5a9f2cdf8 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js @@ -875,10 +875,7 @@ describe('ReactFabric', () => { }); assertConsoleErrorDev([ 'Text strings must be rendered within a <Text> component.\n' + - ' in RCTScrollView (at **)' + - (gate(flags => !flags.enableOwnerStacks) - ? '\n in RCTText (at **)' - : ''), + ' in RCTScrollView (at **)', ]); }); diff --git a/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js b/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js index 97d15eb48e60a..64a05cef33fad 100644 --- a/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js +++ b/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js @@ -7,8 +7,6 @@ import isArray from 'shared/isArray'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; - import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber'; let hasError = false; @@ -99,7 +97,7 @@ export function executeDispatchesInOrder(event) { // Listeners and Instances are two parallel arrays that are always in sync. const listener = dispatchListeners[i]; const instance = dispatchInstances[i]; - if (__DEV__ && enableOwnerStacks && instance !== null) { + if (__DEV__ && instance !== null) { runWithFiberInDEV(instance, executeDispatch, event, listener, instance); } else { executeDispatch(event, listener, instance); @@ -108,7 +106,7 @@ export function executeDispatchesInOrder(event) { } else if (dispatchListeners) { const listener = dispatchListeners; const instance = dispatchInstances; - if (__DEV__ && enableOwnerStacks && instance !== null) { + if (__DEV__ && instance !== null) { runWithFiberInDEV(instance, executeDispatch, event, listener, instance); } else { executeDispatch(event, listener, instance); diff --git a/packages/react-reconciler/src/ReactChildFiber.js b/packages/react-reconciler/src/ReactChildFiber.js index 6613c8d43bba0..9ced8912b977b 100644 --- a/packages/react-reconciler/src/ReactChildFiber.js +++ b/packages/react-reconciler/src/ReactChildFiber.js @@ -47,7 +47,6 @@ import isArray from 'shared/isArray'; import { enableAsyncIterableChildren, disableLegacyMode, - enableOwnerStacks, } from 'shared/ReactFeatureFlags'; import { @@ -488,9 +487,7 @@ function createChildReconciler( if (__DEV__) { // We treat the parent as the owner for stack purposes. created._debugOwner = returnFiber; - if (enableOwnerStacks) { - created._debugTask = returnFiber._debugTask; - } + created._debugTask = returnFiber._debugTask; created._debugInfo = currentDebugInfo; } return created; @@ -609,9 +606,7 @@ function createChildReconciler( if (__DEV__) { // We treat the parent as the owner for stack purposes. created._debugOwner = returnFiber; - if (enableOwnerStacks) { - created._debugTask = returnFiber._debugTask; - } + created._debugTask = returnFiber._debugTask; created._debugInfo = currentDebugInfo; } return created; @@ -649,9 +644,7 @@ function createChildReconciler( if (__DEV__) { // We treat the parent as the owner for stack purposes. created._debugOwner = returnFiber; - if (enableOwnerStacks) { - created._debugTask = returnFiber._debugTask; - } + created._debugTask = returnFiber._debugTask; created._debugInfo = currentDebugInfo; } return created; @@ -718,9 +711,7 @@ function createChildReconciler( if (__DEV__) { // We treat the parent as the owner for stack purposes. created._debugOwner = returnFiber; - if (enableOwnerStacks) { - created._debugTask = returnFiber._debugTask; - } + created._debugTask = returnFiber._debugTask; const prevDebugInfo = pushDebugInfo(newChild._debugInfo); created._debugInfo = currentDebugInfo; currentDebugInfo = prevDebugInfo; @@ -1605,9 +1596,7 @@ function createChildReconciler( if (__DEV__) { // We treat the parent as the owner for stack purposes. created._debugOwner = returnFiber; - if (enableOwnerStacks) { - created._debugTask = returnFiber._debugTask; - } + created._debugTask = returnFiber._debugTask; created._debugInfo = currentDebugInfo; } return created; @@ -1685,9 +1674,7 @@ function createChildReconciler( if (__DEV__) { // We treat the parent as the owner for stack purposes. created._debugOwner = returnFiber; - if (enableOwnerStacks) { - created._debugTask = returnFiber._debugTask; - } + created._debugTask = returnFiber._debugTask; created._debugInfo = currentDebugInfo; } validateFragmentProps(element, created, returnFiber); @@ -1980,16 +1967,12 @@ function createChildReconciler( // thing when it's thrown from the same async component but not if you await // a promise started from a different component/task. throwFiber._debugOwner = returnFiber._debugOwner; - if (enableOwnerStacks) { - throwFiber._debugTask = returnFiber._debugTask; - } + throwFiber._debugTask = returnFiber._debugTask; if (debugInfo != null) { for (let i = debugInfo.length - 1; i >= 0; i--) { if (typeof debugInfo[i].stack === 'string') { throwFiber._debugOwner = (debugInfo[i]: any); - if (enableOwnerStacks) { - throwFiber._debugTask = debugInfo[i].debugTask; - } + throwFiber._debugTask = debugInfo[i].debugTask; break; } } diff --git a/packages/react-reconciler/src/ReactCurrentFiber.js b/packages/react-reconciler/src/ReactCurrentFiber.js index f65ae5f19232c..88d0e28512d39 100644 --- a/packages/react-reconciler/src/ReactCurrentFiber.js +++ b/packages/react-reconciler/src/ReactCurrentFiber.js @@ -10,12 +10,8 @@ import type {Fiber} from './ReactInternalTypes'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import { - getStackByFiberInDevAndProd, - getOwnerStackByFiberInDev, -} from './ReactFiberComponentStack'; +import {getOwnerStackByFiberInDev} from './ReactFiberComponentStack'; import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; export let current: Fiber | null = null; export let isRendering: boolean = false; @@ -42,10 +38,7 @@ function getCurrentFiberStackInDev(): string { // and it is guaranteed to be the work-in-progress version. // TODO: The above comment is not actually true. We might be // in a commit phase or preemptive set state callback. - if (enableOwnerStacks) { - return getOwnerStackByFiberInDev(current); - } - return getStackByFiberInDevAndProd(current); + return getOwnerStackByFiberInDev(current); } return ''; } @@ -63,12 +56,10 @@ export function runWithFiberInDEV<A0, A1, A2, A3, A4, T>( const previousFiber = current; setCurrentFiber(fiber); try { - if (enableOwnerStacks) { - if (fiber !== null && fiber._debugTask) { - return fiber._debugTask.run( - callback.bind(null, arg0, arg1, arg2, arg3, arg4), - ); - } + if (fiber !== null && fiber._debugTask) { + return fiber._debugTask.run( + callback.bind(null, arg0, arg1, arg2, arg3, arg4), + ); } return callback(arg0, arg1, arg2, arg3, arg4); } finally { diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 1ebe08b76963e..70cb3ed9de023 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -40,7 +40,6 @@ import { enableRenderableContext, disableLegacyMode, enableObjectFiber, - enableOwnerStacks, enableViewTransition, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; @@ -202,10 +201,8 @@ function FiberNode( // This isn't directly used but is handy for debugging internals: this._debugInfo = null; this._debugOwner = null; - if (enableOwnerStacks) { - this._debugStack = null; - this._debugTask = null; - } + this._debugStack = null; + this._debugTask = null; this._debugNeedsRemount = false; this._debugHookTypes = null; if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { @@ -293,10 +290,8 @@ function createFiberImplObject( // This isn't directly used but is handy for debugging internals: fiber._debugInfo = null; fiber._debugOwner = null; - if (enableOwnerStacks) { - fiber._debugStack = null; - fiber._debugTask = null; - } + fiber._debugStack = null; + fiber._debugTask = null; fiber._debugNeedsRemount = false; fiber._debugHookTypes = null; if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { @@ -352,10 +347,8 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber { // DEV-only fields workInProgress._debugOwner = current._debugOwner; - if (enableOwnerStacks) { - workInProgress._debugStack = current._debugStack; - workInProgress._debugTask = current._debugTask; - } + workInProgress._debugStack = current._debugStack; + workInProgress._debugTask = current._debugTask; workInProgress._debugHookTypes = current._debugHookTypes; } @@ -766,10 +759,8 @@ export function createFiberFromElement( ); if (__DEV__) { fiber._debugOwner = element._owner; - if (enableOwnerStacks) { - fiber._debugStack = element._debugStack; - fiber._debugTask = element._debugTask; - } + fiber._debugStack = element._debugStack; + fiber._debugTask = element._debugTask; } return fiber; } diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 6f4acf97cf455..9c88a5de870cf 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -114,7 +114,6 @@ import { enableRenderableContext, disableLegacyMode, disableDefaultPropsExceptForClasses, - enableOwnerStacks, enableHydrationLaneScheduling, enableViewTransition, } from 'shared/ReactFeatureFlags'; @@ -3782,10 +3781,8 @@ function beginWork( workInProgress.mode, workInProgress.lanes, ); - if (enableOwnerStacks) { - copiedFiber._debugStack = workInProgress._debugStack; - copiedFiber._debugTask = workInProgress._debugTask; - } + copiedFiber._debugStack = workInProgress._debugStack; + copiedFiber._debugTask = workInProgress._debugTask; return remountFiber(current, workInProgress, copiedFiber); } } diff --git a/packages/react-reconciler/src/ReactFiberComponentStack.js b/packages/react-reconciler/src/ReactFiberComponentStack.js index 8385cbf63e74a..f44dff91c7a10 100644 --- a/packages/react-reconciler/src/ReactFiberComponentStack.js +++ b/packages/react-reconciler/src/ReactFiberComponentStack.js @@ -7,10 +7,7 @@ * @flow */ -import { - enableOwnerStacks, - enableViewTransition, -} from 'shared/ReactFeatureFlags'; +import {enableViewTransition} from 'shared/ReactFeatureFlags'; import type {Fiber} from './ReactInternalTypes'; import type {ReactComponentInfo} from 'shared/ReactTypes'; @@ -101,7 +98,7 @@ function describeFunctionComponentFrameWithoutLineNumber(fn: Function): string { } export function getOwnerStackByFiberInDev(workInProgress: Fiber): string { - if (!enableOwnerStacks || !__DEV__) { + if (!__DEV__) { return ''; } try { diff --git a/packages/react-reconciler/src/ReactFiberErrorLogger.js b/packages/react-reconciler/src/ReactFiberErrorLogger.js index 2b53783a77daf..fe8224bbdca7f 100644 --- a/packages/react-reconciler/src/ReactFiberErrorLogger.js +++ b/packages/react-reconciler/src/ReactFiberErrorLogger.js @@ -18,8 +18,6 @@ import reportGlobalError from 'shared/reportGlobalError'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; - import {bindToConsole} from './ReactFiberConfig'; // Side-channel since I'm not sure we want to make this part of the public API @@ -46,18 +44,6 @@ export function defaultOnUncaughtError( 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.'; - const prevGetCurrentStack = ReactSharedInternals.getCurrentStack; - if (!enableOwnerStacks) { - // The current Fiber is disconnected at this point which means that console printing - // cannot add a component stack since it terminates at the deletion node. This is not - // a problem for owner stacks which are not disconnected but for the parent component - // stacks we need to use the snapshot we've previously extracted. - const componentStack = - errorInfo.componentStack != null ? errorInfo.componentStack : ''; - ReactSharedInternals.getCurrentStack = function () { - return componentStack; - }; - } try { console.warn( '%s\n\n%s\n', @@ -66,9 +52,7 @@ export function defaultOnUncaughtError( // We let our console.error wrapper add the component stack to the end. ); } finally { - if (!enableOwnerStacks) { - ReactSharedInternals.getCurrentStack = prevGetCurrentStack; - } + // ignore } } } @@ -97,18 +81,6 @@ export function defaultOnCaughtError( errorBoundaryName || 'Anonymous' }.`; - const prevGetCurrentStack = ReactSharedInternals.getCurrentStack; - if (!enableOwnerStacks) { - // The current Fiber is disconnected at this point which means that console printing - // cannot add a component stack since it terminates at the deletion node. This is not - // a problem for owner stacks which are not disconnected but for the parent component - // stacks we need to use the snapshot we've previously extracted. - const componentStack = - errorInfo.componentStack != null ? errorInfo.componentStack : ''; - ReactSharedInternals.getCurrentStack = function () { - return componentStack; - }; - } try { if ( typeof error === 'object' && @@ -138,9 +110,7 @@ export function defaultOnCaughtError( ); } } finally { - if (!enableOwnerStacks) { - ReactSharedInternals.getCurrentStack = prevGetCurrentStack; - } + // ignore } } else { // In production, we print the error directly. diff --git a/packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js b/packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js index 43f58ffc4bfd5..869cc308a985f 100644 --- a/packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js @@ -6,7 +6,6 @@ describe('ErrorBoundaryReconciliation', () => { let ReactTestRenderer; let span; let act; - let assertConsoleErrorDev; beforeEach(() => { jest.resetModules(); @@ -14,8 +13,6 @@ describe('ErrorBoundaryReconciliation', () => { ReactTestRenderer = require('react-test-renderer'); React = require('react'); act = require('internal-test-utils').act; - assertConsoleErrorDev = - require('internal-test-utils').assertConsoleErrorDev; DidCatchErrorBoundary = class extends React.Component { state = {error: null}; componentDidCatch(error) { @@ -68,9 +65,6 @@ describe('ErrorBoundaryReconciliation', () => { </ErrorBoundary>, ); }); - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev(['invalid', 'invalid']); - } const Fallback = fallbackTagName; expect(renderer).toMatchRenderedOutput(<Fallback prop="ErrorBoundary" />); diff --git a/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js b/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js index 3098dacfedfd4..b49f1d4aec5e3 100644 --- a/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js @@ -168,9 +168,7 @@ describe('ReactFragment', () => { 'Foo', 'CatchingBoundary', ]), - gate(flags => flags.enableOwnerStacks) && __DEV__ - ? componentStack(['Bar', 'Foo']) - : null, + __DEV__ ? componentStack(['Bar', 'Foo']) : null, ]); }); }); diff --git a/packages/react-reconciler/src/__tests__/ReactFragment-test.js b/packages/react-reconciler/src/__tests__/ReactFragment-test.js index 559ede0dd9103..a9dbabca24492 100644 --- a/packages/react-reconciler/src/__tests__/ReactFragment-test.js +++ b/packages/react-reconciler/src/__tests__/ReactFragment-test.js @@ -744,19 +744,12 @@ describe('ReactFragment', () => { ReactNoop.render(<Foo condition={false} />); await waitForAll([]); assertConsoleErrorDev([ - gate('enableOwnerStacks') - ? 'Each child in a list should have a unique "key" prop.\n' + - '\n' + - 'Check the render method of `div`. ' + - 'It was passed a child from Foo. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Foo (at **)' - : 'Each child in a list should have a unique "key" prop.\n' + - '\n' + - 'Check the render method of `Foo`. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Stateful (at **)\n' + - ' in Foo (at **)', + 'Each child in a list should have a unique "key" prop.\n' + + '\n' + + 'Check the render method of `div`. ' + + 'It was passed a child from Foo. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in Foo (at **)', ]); expect(ops).toEqual([]); diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index 367d83f38a57c..0bd0a25534202 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -1236,24 +1236,20 @@ describe('ReactHooks', () => { 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, ' + 'but not inside Hooks like useReducer() or useMemo().\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see https://react.dev/link/rules-of-hooks\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', 'Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, ' + 'but not inside Hooks like useReducer() or useMemo().\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see https://react.dev/link/rules-of-hooks\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', ]); function Valid() { @@ -1293,24 +1289,20 @@ describe('ReactHooks', () => { 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, ' + 'but not inside Hooks like useReducer() or useMemo().\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see https://react.dev/link/rules-of-hooks\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', 'Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, ' + 'but not inside Hooks like useReducer() or useMemo().\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see https://react.dev/link/rules-of-hooks\n' + - ' in App (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Boundary (at **)'), + ' in App (at **)', ]); }); diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js index cf71014cce4fc..781a8f7579f81 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js @@ -1803,14 +1803,10 @@ describe('ReactIncremental', () => { ' in Intl (at **)', 'ShowLocale uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowLocale (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Intl (at **)'), + ' in ShowLocale (at **)', 'ShowBoth uses the legacy contextTypes API which will be removed soon. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowBoth (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in div (at **)' + '\n in Intl (at **)'), + ' in ShowBoth (at **)', ]); ReactNoop.render( @@ -1865,16 +1861,10 @@ describe('ReactIncremental', () => { assertConsoleErrorDev([ 'Router uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in Router (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Intl (at **)'), + ' in Router (at **)', 'ShowRoute uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') - ? ' in Indirection (at **)' - : ' in ShowRoute (at **)\n' + - ' in Indirection (at **)\n' + - ' in Router (at **)\n' + - ' in Intl (at **)'), + ' in Indirection (at **)', ]); }); @@ -1972,8 +1962,7 @@ describe('ReactIncremental', () => { ' in Intl (at **)', 'ShowLocale uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowLocale (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Intl (at **)'), + ' in ShowLocale (at **)', ]); await waitForAll([ @@ -2068,22 +2057,10 @@ describe('ReactIncremental', () => { ' in Intl (at **)', 'ShowLocaleClass uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowLocaleClass (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in Stateful (at **)' + - '\n in IndirectionClass (at **)' + - '\n in IndirectionFn (at **)' + - ' in Intl (at **)'), + ' in ShowLocaleClass (at **)', 'ShowLocaleFn uses the legacy contextTypes API which will be removed soon. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowLocaleFn (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in Stateful (at **)' + - '\n in IndirectionClass (at **)' + - '\n in IndirectionFn (at **)' + - ' in Intl (at **)'), + ' in ShowLocaleFn (at **)', ]); statefulInst.setState({x: 1}); @@ -2174,26 +2151,14 @@ describe('ReactIncremental', () => { assertConsoleErrorDev([ 'Intl uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Intl (at **)\n') + ' in Stateful (at **)', 'ShowLocaleClass uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowLocaleClass (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in IndirectionClass (at **)' + - '\n in IndirectionFn (at **)' + - '\n in Intl (at **)' + - '\n in Stateful (at **)'), + ' in ShowLocaleClass (at **)', + 'ShowLocaleFn uses the legacy contextTypes API which will be removed soon. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in ShowLocaleFn (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in IndirectionClass (at **)' + - '\n in IndirectionFn (at **)' + - '\n in Intl (at **)' + - '\n in Stateful (at **)'), + ' in ShowLocaleFn (at **)', ]); statefulInst.setState({locale: 'gr'}); @@ -2255,7 +2220,6 @@ describe('ReactIncremental', () => { assertConsoleErrorDev([ 'Child uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in Child (at **)\n') + ' in Middle (at **)\n' + ' in Root (at **)', ]); @@ -2309,7 +2273,6 @@ describe('ReactIncremental', () => { assertConsoleErrorDev([ 'ContextProvider uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in ContextProvider (at **)\n') + ' in Root (at **)', ]); @@ -2511,10 +2474,7 @@ describe('ReactIncremental', () => { ' in TopContextProvider (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Child (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in Middle (at **)' + '\n in TopContextProvider (at **)'), + ' in Child (at **)', ]); instance.updateCount(); await waitForAll(['count:1']); @@ -2578,17 +2538,10 @@ describe('ReactIncremental', () => { ' in TopContextProvider (at **)', 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in MiddleContextProvider (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in TopContextProvider (at **)'), + ' in MiddleContextProvider (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Child (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in MiddleContextProvider (at **)' + - '\n in TopContextProvider (at **)'), + ' in Child (at **)', ]); instance.updateCount(); await waitForAll(['count:1']); @@ -2661,19 +2614,10 @@ describe('ReactIncremental', () => { ' in TopContextProvider (at **)', 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in MiddleContextProvider (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in MiddleScu (at **)' + - '\n in TopContextProvider (at **)'), + ' in MiddleContextProvider (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Child (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in MiddleContextProvider (at **)' + - '\n in MiddleScu (at **)' + - '\n in TopContextProvider (at **)'), + ' in Child (at **)', ]); instance.updateCount(); await waitForAll([]); @@ -2756,19 +2700,10 @@ describe('ReactIncremental', () => { ' in TopContextProvider (at **)', 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in MiddleContextProvider (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in MiddleScu (at **)' + - '\n in TopContextProvider (at **)'), + ' in MiddleContextProvider (at **)', 'Child uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Child (at **)' + - (gate('enableOwnerStacks') - ? '' - : '\n in MiddleContextProvider (at **)' + - '\n in MiddleScu (at **)' + - '\n in TopContextProvider (at **)'), + ' in Child (at **)', ]); topInstance.updateCount(); await waitForAll([]); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js index 028f80d3de41a..f81a83379f8d0 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js @@ -1222,8 +1222,7 @@ describe('ReactIncrementalErrorHandling', () => { ' in Provider (at **)', 'Connector uses the legacy contextTypes API which will be removed soon. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in Connector (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in Provider (at **)'), + ' in Connector (at **)', ]); // If the context stack does not unwind, span will get 'abcde' @@ -1254,23 +1253,6 @@ describe('ReactIncrementalErrorHandling', () => { </ErrorBoundary>, ); await waitForAll([]); - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.\n' + - ' in BrokenRender (at **)\n' + - ' in ErrorBoundary (at **)', - // React retries once on error - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.\n' + - ' in BrokenRender (at **)\n' + - ' in ErrorBoundary (at **)', - ]); - } expect(ReactNoop).toMatchRenderedOutput( <span @@ -1319,23 +1301,7 @@ describe('ReactIncrementalErrorHandling', () => { </ErrorBoundary>, ); await waitForAll([]); - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.\n' + - ' in BrokenRender (at **)\n' + - ' in ErrorBoundary (at **)', - // React retries once on error - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.\n' + - ' in BrokenRender (at **)\n' + - ' in ErrorBoundary (at **)', - ]); - } + expect(ReactNoop).toMatchRenderedOutput( <span prop={ @@ -1354,17 +1320,6 @@ describe('ReactIncrementalErrorHandling', () => { it('recovers from uncaught reconciler errors', async () => { const InvalidType = undefined; ReactNoop.render(<InvalidType />); - if (gate(flags => !flags.enableOwnerStacks)) { - assertConsoleErrorDev( - [ - 'React.jsx: type is invalid -- expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: undefined. ' + - "You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.', - ], - {withoutStack: true}, - ); - } await waitForThrow( 'Element type is invalid: expected a string (for built-in components) or ' + diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js index 75ca5f6b131dc..3dde9c75bf038 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js @@ -94,11 +94,7 @@ describe('ReactIncrementalErrorLogging', () => { // The component stack is not added without the polyfill/devtools. // expect.stringMatching( // new RegExp( - // gate(flags => flags.enableOwnerStacks) - // ? '\\s+(in|at) ErrorThrowingComponent' - // : '\\s+(in|at) ErrorThrowingComponent (.*)\n' + - // '\\s+(in|at) span(.*)\n' + - // '\\s+(in|at) div(.*)', + // '\\s+(in|at) ErrorThrowingComponent' // ), // ), ); @@ -143,11 +139,7 @@ describe('ReactIncrementalErrorLogging', () => { // The component stack is not added without the polyfill/devtools. // expect.stringMatching( // new RegExp( - // gate(flags => flags.enableOwnerStacks) - // ? '\\s+(in|at) ErrorThrowingComponent' - // : '\\s+(in|at) ErrorThrowingComponent (.*)\n' + - // '\\s+(in|at) span(.*)\n' + - // '\\s+(in|at) div(.*)', + // '\\s+(in|at) ErrorThrowingComponent' // ), // ), ); @@ -204,12 +196,7 @@ describe('ReactIncrementalErrorLogging', () => { // The component stack is not added without the polyfill/devtools. // expect.stringMatching( // new RegExp( - // gate(flags => flags.enableOwnerStacks) - // ? '\\s+(in|at) ErrorThrowingComponent' - // : '\\s+(in|at) ErrorThrowingComponent (.*)\n' + - // '\\s+(in|at) span(.*)\n' + - // '\\s+(in|at) ErrorBoundary(.*)\n' + - // '\\s+(in|at) div(.*)', + // '\\s+(in|at) ErrorThrowingComponent' // ), // ), ); @@ -287,11 +274,7 @@ describe('ReactIncrementalErrorLogging', () => { ), // The component stack is not added without the polyfill/devtools. // expect.stringMatching( - // gate(flag => flag.enableOwnerStacks) - // ? new RegExp('\\s+(in|at) Foo') - // : new RegExp( - // '\\s+(in|at) Foo (.*)\n' + '\\s+(in|at) ErrorBoundary(.*)', - // ), + // new RegExp('\\s+(in|at) Foo') // ), ); } else { diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 748bfff6b0e7a..5472c9c89fb6b 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -240,9 +240,6 @@ describe('ReactLazy', () => { ' }\n\n' + 'Your code should look like: \n ' + "const MyComponent = lazy(() => import('./MyComponent'))\n" + - (gate('enableOwnerStacks') - ? '' - : ' in Lazy (at **)\n' + ' in Suspense (at **)\n') + ' in App (at **)', 'lazy: Expected the result of a dynamic import() call. ' + 'Instead received: function Text(props) {\n' + @@ -251,9 +248,6 @@ describe('ReactLazy', () => { ' }\n\n' + 'Your code should look like: \n ' + "const MyComponent = lazy(() => import('./MyComponent'))\n" + - (gate('enableOwnerStacks') - ? '' - : ' in Lazy (at **)\n' + ' in Suspense (at **)\n') + ' in App (at **)', ]); expect(root).not.toMatchRenderedOutput('Hi'); @@ -749,8 +743,7 @@ describe('ReactLazy', () => { 'T: Support for defaultProps ' + 'will be removed from function components in a future major ' + 'release. Use JavaScript default parameters instead.\n' + - ' in T (at **)\n' + - ' in Suspense (at **)', + ' in T (at **)', ]); expect(root).toMatchRenderedOutput('Hi Bye'); @@ -855,11 +848,15 @@ describe('ReactLazy', () => { ); }); - async function verifyResolvesProps( - Add, - shouldWarnAboutFunctionDefaultProps, - shouldWarnAboutMemoDefaultProps, - ) { + // @gate !disableDefaultPropsExceptForClasses + it('resolves props for function component with defaultProps', async () => { + function Add(props) { + expect(props.innerWithDefault).toBe(42); + return props.inner + props.outer; + } + Add.defaultProps = { + innerWithDefault: 42, + }; const LazyAdd = lazy(() => fakeImport(Add)); const root = ReactTestRenderer.create( <Suspense fallback={<Text text="Loading..." />}> @@ -876,18 +873,10 @@ describe('ReactLazy', () => { // Mount await act(() => resolveFakeImport(Add)); - if (shouldWarnAboutFunctionDefaultProps) { - assertConsoleErrorDev([ - 'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.\n' + - ' in Add (at **)\n' + - ' in Suspense (at **)', - ]); - } else if (shouldWarnAboutMemoDefaultProps) { - assertConsoleErrorDev([ - 'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.\n' + - ' in Suspense (at **)', - ]); - } + assertConsoleErrorDev([ + 'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.\n' + + ' in Add (at **)', + ]); expect(root).toMatchRenderedOutput('22'); @@ -899,25 +888,38 @@ describe('ReactLazy', () => { ); await waitForAll([]); expect(root).toMatchRenderedOutput('0'); - } - - // @gate !disableDefaultPropsExceptForClasses - it('resolves props for function component with defaultProps', async () => { - function Add(props) { - expect(props.innerWithDefault).toBe(42); - return props.inner + props.outer; - } - Add.defaultProps = { - innerWithDefault: 42, - }; - await verifyResolvesProps(Add, true); }); it('resolves props for function component without defaultProps', async () => { function Add(props) { return props.inner + props.outer; } - await verifyResolvesProps(Add); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); it('resolves props for class component with defaultProps', async () => { @@ -930,7 +932,32 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyResolvesProps(Add); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); it('resolves props for class component without defaultProps', async () => { @@ -939,7 +966,32 @@ describe('ReactLazy', () => { return this.props.inner + this.props.outer; } } - await verifyResolvesProps(Add); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); // @gate !disableDefaultPropsExceptForClasses @@ -952,7 +1004,32 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyResolvesProps(Add); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); it('resolves props for forwardRef component without defaultProps', async () => { @@ -960,7 +1037,33 @@ describe('ReactLazy', () => { return props.inner + props.outer; }); Add.displayName = 'Add'; - await verifyResolvesProps(Add); + + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); // @gate !disableDefaultPropsExceptForClasses @@ -973,7 +1076,39 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyResolvesProps(Add, false, true); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + assertConsoleErrorDev( + [ + 'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); it('resolves props for outer memo component without defaultProps', async () => { @@ -981,7 +1116,32 @@ describe('ReactLazy', () => { return props.inner + props.outer; }; Add = React.memo(Add); - await verifyResolvesProps(Add); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); // @gate !disableDefaultPropsExceptForClasses @@ -994,7 +1154,40 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyResolvesProps(React.memo(Add), true); + const MemoAdd = React.memo(Add); + const LazyAdd = lazy(() => fakeImport(MemoAdd)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(MemoAdd)); + + assertConsoleErrorDev( + [ + 'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); it('resolves props for inner memo component without defaultProps', async () => { @@ -1002,7 +1195,32 @@ describe('ReactLazy', () => { return props.inner + props.outer; }; Add.displayName = 'Add'; - await verifyResolvesProps(React.memo(Add)); + const LazyAdd = lazy(() => fakeImport(Add)); + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner="2" outer="2" /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); + + // Mount + await act(() => resolveFakeImport(Add)); + + expect(root).toMatchRenderedOutput('22'); + + // Update + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <LazyAdd inner={false} outer={false} /> + </Suspense>, + ); + await waitForAll([]); + expect(root).toMatchRenderedOutput('0'); }); // @gate !disableDefaultPropsExceptForClasses @@ -1030,12 +1248,13 @@ describe('ReactLazy', () => { // Mount await act(() => resolveFakeImport(T)); assertLog(['Inner default text']); - assertConsoleErrorDev([ - 'T: Support for defaultProps will be removed from function components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in T (at **)\n' + - ' in Suspense (at **)', - ]); + assertConsoleErrorDev( + [ + 'T: Support for defaultProps will be removed from function components in a future major release. ' + + 'Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); expect(root).toMatchRenderedOutput('Inner default text'); // Update @@ -1074,11 +1293,7 @@ describe('ReactLazy', () => { '\n' + 'Check the render method of `Foo`. ' + 'See https://react.dev/link/warning-keys for more information.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in Foo (at **)' - : ' in Text (at **)\n' + - ' in Foo (at **)\n' + - ' in Suspense (at **)'), + ' in Foo (at **)', ]); expect(root).toMatchRenderedOutput(<div>AB</div>); }); @@ -1154,11 +1369,13 @@ describe('ReactLazy', () => { // Mount await act(() => resolveFakeImport(Add)); - assertConsoleErrorDev([ - 'Unknown: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in Suspense (at **)', - ]); + assertConsoleErrorDev( + [ + 'Unknown: Support for defaultProps will be removed from memo components in a future major release. ' + + 'Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); expect(root).toMatchRenderedOutput('4'); // Update (shallowly equal) @@ -1243,14 +1460,15 @@ describe('ReactLazy', () => { // Mount await act(() => resolveFakeImport(Add)); - assertConsoleErrorDev([ - 'Memo: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in Suspense (at **)', - 'Unknown: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in Suspense (at **)', - ]); + assertConsoleErrorDev( + [ + 'Memo: Support for defaultProps will be removed from memo components in a future major release. ' + + 'Use JavaScript default parameters instead.', + 'Unknown: Support for defaultProps will be removed from memo components in a future major release. ' + + 'Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); expect(root).toMatchRenderedOutput('4'); // Update diff --git a/packages/react-reconciler/src/__tests__/ReactMemo-test.js b/packages/react-reconciler/src/__tests__/ReactMemo-test.js index 2a0c21d8dd9a4..02f5e6ef18813 100644 --- a/packages/react-reconciler/src/__tests__/ReactMemo-test.js +++ b/packages/react-reconciler/src/__tests__/ReactMemo-test.js @@ -407,12 +407,22 @@ describe('memo', () => { ); }); assertLog(['Loading...', 15]); - assertConsoleErrorDev([ - 'Counter: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - (label === 'lazy' ? '' : ' in Indirection (at **)\n') + - ' in Suspense (at **)', - ]); + if (label === 'lazy') { + assertConsoleErrorDev( + [ + 'Counter: Support for defaultProps will be removed from memo components in a future major release. ' + + 'Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); + } else { + assertConsoleErrorDev([ + 'Counter: Support for defaultProps will be removed from memo components in a future major release. ' + + 'Use JavaScript default parameters instead.\n' + + ' in Indirection (at **)', + ]); + } + expect(ReactNoop).toMatchRenderedOutput(<span prop={15} />); // Should bail out because props have not changed @@ -475,12 +485,14 @@ describe('memo', () => { </div>, ); }); - assertConsoleErrorDev([ - 'Inner: ' + - 'Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in div (at **)', - ]); + assertConsoleErrorDev( + [ + 'Inner: ' + + 'Support for defaultProps will be removed from memo components in a future major release. ' + + 'Use JavaScript default parameters instead.', + ], + {withoutStack: true}, + ); expect(root).toMatchRenderedOutput(<div>111</div>); await act(async () => { @@ -576,9 +588,7 @@ describe('memo', () => { 'Each child in a list should have a unique "key" prop. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - (gate('enableOwnerStacks') - ? ' in **/ReactMemo-test.js:**:** (at **)' - : ' in p (at **)'), + ' in **/ReactMemo-test.js:**:** (at **)', ]); }); @@ -597,8 +607,7 @@ describe('memo', () => { '\n\nCheck the top-level render call using <Inner>. It was passed a child from Inner. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Inner (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Inner (at **)', ]); }); @@ -619,8 +628,7 @@ describe('memo', () => { '\n\nCheck the top-level render call using <Inner>. It was passed a child from Inner. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Inner (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Inner (at **)', ]); }); @@ -640,8 +648,7 @@ describe('memo', () => { '\n\nCheck the top-level render call using <Outer>. It was passed a child from Outer. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Outer (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Outer (at **)', ]); }); @@ -663,8 +670,7 @@ describe('memo', () => { '\n\nCheck the top-level render call using <Inner>. It was passed a child from Inner. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Inner (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Inner (at **)', ]); }); } diff --git a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js index 42294269e7b14..49a80ceb84239 100644 --- a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js @@ -41,7 +41,7 @@ describe('ReactOwnerStacks', () => { } }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can get the component owner stacks during rendering in dev', async () => { let stack; @@ -75,7 +75,7 @@ describe('ReactOwnerStacks', () => { it('returns null outside of render', async () => { // Awkward to gate since some builds will have `captureOwnerStack` return null in prod - if (__DEV__ && gate('enableOwnerStacks')) { + if (__DEV__) { expect(React.captureOwnerStack()).toBe(null); await act(() => { diff --git a/packages/react-reconciler/src/__tests__/ReactUse-test.js b/packages/react-reconciler/src/__tests__/ReactUse-test.js index fbe247c029a50..de0773076c4b5 100644 --- a/packages/react-reconciler/src/__tests__/ReactUse-test.js +++ b/packages/react-reconciler/src/__tests__/ReactUse-test.js @@ -257,9 +257,6 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. Creating ' + 'promises inside a Client Component or hook is not yet ' + 'supported, except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + ' in Suspense (at **)\n') + ' in App (at **)', ]); assertLog(['ABC']); @@ -430,20 +427,10 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. Creating ' + 'promises inside a Client Component or hook is not yet ' + 'supported, except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + - ' in ErrorBoundary (at **)\n' + - ' in Suspense (at **)\n') + ' in App (at **)', 'A component was suspended by an uncached promise. Creating ' + 'promises inside a Client Component or hook is not yet ' + 'supported, except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + - ' in ErrorBoundary (at **)\n' + - ' in Suspense (at **)\n') + ' in App (at **)', ]); assertLog([ @@ -617,8 +604,7 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - ' in App (at **)\n' + - ' in Suspense (at **)', + ' in App (at **)', ]); expect(root).toMatchRenderedOutput('Async'); }); @@ -667,8 +653,7 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - ' in App (at **)\n' + - ' in Suspense (at **)', + ' in App (at **)', ]); expect(root).toMatchRenderedOutput('Async'); }); @@ -1216,8 +1201,7 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - ' in AsyncText (at **)\n' + - ' in Suspense (at **)', + ' in AsyncText (at **)', ]); expect(root).toMatchRenderedOutput('A(Loading B...)'); @@ -1243,9 +1227,7 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - ' in AsyncText (at **)\n' + - ' in Suspense (at **)\n' + - ' in Suspense (at **)', + ' in AsyncText (at **)', ]); expect(root).toMatchRenderedOutput('AB(Loading C...)'); @@ -1263,10 +1245,7 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - ' in AsyncText (at **)\n' + - ' in Suspense (at **)\n' + - ' in Suspense (at **)\n' + - ' in Suspense (at **)', + ' in AsyncText (at **)', ]); expect(root).toMatchRenderedOutput('ABC'); }); @@ -1301,16 +1280,10 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + ' in Suspense (at **)\n') + ' in App (at **)', 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + ' in Suspense (at **)\n') + ' in App (at **)', ]); expect(root).toMatchRenderedOutput('A1'); @@ -1685,10 +1658,6 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in **/ReactUse-test.js:**:** (at **)\n' + - ' in Suspense (at **)\n') + ' in App (at **)', ]); expect(root).toMatchRenderedOutput('Async'); @@ -1723,10 +1692,6 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in **/ReactUse-test.js:**:** (at **)\n' + - ' in Suspense (at **)\n') + ' in App (at **)', ]); expect(root).toMatchRenderedOutput('Async'); @@ -1752,10 +1717,6 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in **/ReactUse-test.js:**:** (at **)\n' + - ' in Suspense (at **)\n') + ' in App (at **)', ]); expect(root).toMatchRenderedOutput('Async!'); @@ -1810,36 +1771,17 @@ describe('ReactUse', () => { assertConsoleErrorDev([ 'ContextProvider uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') ? '' : ' in ContextProvider (at **)\n') + ' in App (at **)', 'Async uses the legacy contextTypes API which will be removed soon. ' + 'Use React.createContext() with React.useContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + - ' in div (at **)\n' + - ' in Suspense (at **)\n' + - ' in ContextProvider (at **)\n') + ' in App (at **)', 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + - ' in div (at **)\n' + - ' in Suspense (at **)\n' + - ' in ContextProvider (at **)\n') + ' in App (at **)', 'A component was suspended by an uncached promise. ' + 'Creating promises inside a Client Component or hook is not yet supported, ' + 'except via a Suspense-compatible library or framework.\n' + - (gate('enableOwnerStacks') - ? '' - : ' in Async (at **)\n' + - ' in div (at **)\n' + - ' in Suspense (at **)\n' + - ' in ContextProvider (at **)\n') + ' in App (at **)', ]); expect(root).toMatchRenderedOutput( @@ -1921,8 +1863,7 @@ describe('ReactUse', () => { 'Only Server Components can be async at the moment. ' + "This error is often caused by accidentally adding `'use client'` " + 'to a module that was originally written for the server.\n' + - ' in AsyncClientComponent (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in ErrorBoundary (at **)'), + ' in AsyncClientComponent (at **)', ]); assertLog([ 'An unknown Component is an async Client Component. ' + @@ -1977,8 +1918,7 @@ describe('ReactUse', () => { 'Only Server Components can be async at the moment. ' + "This error is often caused by accidentally adding `'use client'` " + 'to a module that was originally written for the server.\n' + - ' in AsyncClientComponent (at **)' + - (gate('enableOwnerStacks') ? '' : '\n in ErrorBoundary (at **)'), + ' in AsyncClientComponent (at **)', ]); assertLog([ 'An unknown Component is an async Client Component. ' + diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index 040bb046b5518..f3fa444fc1528 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -1170,25 +1170,15 @@ describe('ReactFlightDOMBrowser', () => { ); const result = await ReactServerDOMClient.createFromReadableStream(stream); - if (!gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ]); - } - await act(() => { root.render(result); }); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <ParentClient>. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ]); - } + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the top-level render call using <ParentClient>. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in div (at **)', + ]); }); it('basic use(promise)', async () => { diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 603dbbf09e5ca..ba1ae3b64a850 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -1020,7 +1020,7 @@ describe('ReactFlightDOMEdge', () => { } }); - // @gate __DEV__ && enableOwnerStacks + // @gate __DEV__ it('can get the component owner stacks asynchronously', async () => { let stack; diff --git a/packages/react-server/src/ReactFizzComponentStack.js b/packages/react-server/src/ReactFizzComponentStack.js index 6131a1c670e87..c9d3998500e17 100644 --- a/packages/react-server/src/ReactFizzComponentStack.js +++ b/packages/react-server/src/ReactFizzComponentStack.js @@ -26,10 +26,7 @@ import { REACT_VIEW_TRANSITION_TYPE, } from 'shared/ReactSymbols'; -import { - enableOwnerStacks, - enableViewTransition, -} from 'shared/ReactFeatureFlags'; +import {enableViewTransition} from 'shared/ReactFeatureFlags'; import {formatOwnerStack} from 'shared/ReactOwnerStackFrames'; @@ -134,7 +131,7 @@ function describeFunctionComponentFrameWithoutLineNumber(fn: Function): string { export function getOwnerStackByComponentStackNodeInDev( componentStack: ComponentStackNode, ): string { - if (!enableOwnerStacks || !__DEV__) { + if (!__DEV__) { return ''; } try { diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 041ef31e2bd3d..b87b8955f3654 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -165,7 +165,6 @@ import { enableRenderableContext, disableDefaultPropsExceptForClasses, enableAsyncIterableChildren, - enableOwnerStacks, enableViewTransition, } from 'shared/ReactFeatureFlags'; @@ -787,7 +786,7 @@ function createRenderTask( if (!disableLegacyContext) { task.legacyContext = legacyContext; } - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = debugTask; } abortSet.add(task); @@ -840,7 +839,7 @@ function createReplayTask( if (!disableLegacyContext) { task.legacyContext = legacyContext; } - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = debugTask; } abortSet.add(task); @@ -875,12 +874,9 @@ function getCurrentStackInDEV(): string { if (currentTaskInDEV === null || currentTaskInDEV.componentStack === null) { return ''; } - if (enableOwnerStacks) { - return getOwnerStackByComponentStackNodeInDev( - currentTaskInDEV.componentStack, - ); - } - return getStackByComponentStackNode(currentTaskInDEV.componentStack); + return getOwnerStackByComponentStackNodeInDev( + currentTaskInDEV.componentStack, + ); } return ''; } @@ -907,18 +903,16 @@ function pushServerComponentStack( if (typeof componentInfo.name !== 'string') { continue; } - if (enableOwnerStacks && componentInfo.debugStack === undefined) { + if (componentInfo.debugStack === undefined) { continue; } task.componentStack = { parent: task.componentStack, type: componentInfo, owner: componentInfo.owner, - stack: enableOwnerStacks ? componentInfo.debugStack : null, + stack: componentInfo.debugStack, }; - if (enableOwnerStacks) { - task.debugTask = (componentInfo.debugTask: any); - } + task.debugTask = (componentInfo.debugTask: any); } } } @@ -934,12 +928,10 @@ function pushComponentStack(task: Task): void { const element: any = node; const type = element.type; const owner = __DEV__ ? element._owner : null; - const stack = __DEV__ && enableOwnerStacks ? element._debugStack : null; + const stack = __DEV__ ? element._debugStack : null; if (__DEV__) { pushServerComponentStack(task, element._debugInfo); - if (enableOwnerStacks) { - task.debugTask = element._debugTask; - } + task.debugTask = element._debugTask; } task.componentStack = createComponentStackFromType( task.componentStack, @@ -1056,7 +1048,7 @@ function logPostpone( // If this callback errors, we intentionally let that error bubble up to become a fatal error // so that someone fixes the error reporting instead of hiding it. const onPostpone = request.onPostpone; - if (__DEV__ && enableOwnerStacks && debugTask) { + if (__DEV__ && debugTask) { debugTask.run(onPostpone.bind(null, reason, postponeInfo)); } else { onPostpone(reason, postponeInfo); @@ -1073,7 +1065,7 @@ function logRecoverableError( // so that someone fixes the error reporting instead of hiding it. const onError = request.onError; const errorDigest = - __DEV__ && enableOwnerStacks && debugTask + __DEV__ && debugTask ? debugTask.run(onError.bind(null, error, errorInfo)) : onError(error, errorInfo); if (errorDigest != null && typeof errorDigest !== 'string') { @@ -1101,7 +1093,7 @@ function fatalError( // It's also called if React itself or its host configs errors. const onShellError = request.onShellError; const onFatalError = request.onFatalError; - if (__DEV__ && enableOwnerStacks && debugTask) { + if (__DEV__ && debugTask) { debugTask.run(onShellError.bind(null, error)); debugTask.run(onFatalError.bind(null, error)); } else { @@ -1261,7 +1253,7 @@ function renderSuspenseBoundary( task.componentStack, task.isFallback, !disableLegacyContext ? task.legacyContext : emptyContextObject, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); pushComponentStack(suspendedPrimaryTask); request.pingedTasks.push(suspendedPrimaryTask); @@ -1334,7 +1326,7 @@ function renderSuspenseBoundary( request, postponeInstance.message, thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; @@ -1343,7 +1335,7 @@ function renderSuspenseBoundary( request, error, thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } encodeErrorForBoundary( @@ -1387,7 +1379,7 @@ function renderSuspenseBoundary( task.componentStack, true, !disableLegacyContext ? task.legacyContext : emptyContextObject, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); pushComponentStack(suspendedFallbackTask); // TODO: This should be queued at a separate lower priority queue so that we only work @@ -1485,7 +1477,7 @@ function replaySuspenseBoundary( request, postponeInstance.message, thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; @@ -1494,7 +1486,7 @@ function replaySuspenseBoundary( request, error, thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } encodeErrorForBoundary( @@ -1545,7 +1537,7 @@ function replaySuspenseBoundary( task.componentStack, true, !disableLegacyContext ? task.legacyContext : emptyContextObject, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); pushComponentStack(suspendedFallbackTask); @@ -1587,7 +1579,7 @@ function renderPreamble( task.componentStack, task.isFallback, !disableLegacyContext ? task.legacyContext : emptyContextObject, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); pushComponentStack(preambleTask); request.pingedTasks.push(preambleTask); @@ -2489,7 +2481,7 @@ function replayElement( thrownInfo, childNodes, childSlots, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } task.replay = replay; @@ -2654,15 +2646,14 @@ function renderNodeDestructive( task.childIndex = childIndex; const previousComponentStack = task.componentStack; - const previousDebugTask = - __DEV__ && enableOwnerStacks ? task.debugTask : null; + const previousDebugTask = __DEV__ ? task.debugTask : null; pushComponentStack(task); retryNode(request, task); task.componentStack = previousComponentStack; - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = previousDebugTask; } } @@ -2689,8 +2680,7 @@ function retryNode(request: Request, task: Task): void { const refProp = props.ref; const ref = refProp !== undefined ? refProp : null; - const debugTask: null | ConsoleTask = - __DEV__ && enableOwnerStacks ? task.debugTask : null; + const debugTask: null | ConsoleTask = __DEV__ ? task.debugTask : null; const name = getComponentNameFromType(type); const keyOrIndex = @@ -3002,7 +2992,7 @@ function replayFragment( thrownInfo, childNodes, childSlots, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } task.replay = replay; @@ -3094,7 +3084,7 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void { task.componentStack, (child: any).type, (child: any)._owner, - enableOwnerStacks ? (child: any)._debugStack : null, + (child: any)._debugStack, ); task.componentStack = stackFrame; console.error( @@ -3117,9 +3107,7 @@ function renderChildrenArray( const previousComponentStack = task.componentStack; let previousDebugTask = null; if (__DEV__) { - if (enableOwnerStacks) { - previousDebugTask = task.debugTask; - } + previousDebugTask = task.debugTask; // We read debugInfo from task.node instead of children because it might have been an // unwrapped iterable so we read from the original node. pushServerComponentStack(task, (task.node: any)._debugInfo); @@ -3137,9 +3125,7 @@ function renderChildrenArray( task.keyPath = prevKeyPath; if (__DEV__) { task.componentStack = previousComponentStack; - if (enableOwnerStacks) { - task.debugTask = previousDebugTask; - } + task.debugTask = previousDebugTask; } return; } @@ -3173,9 +3159,7 @@ function renderChildrenArray( task.keyPath = prevKeyPath; if (__DEV__) { task.componentStack = previousComponentStack; - if (enableOwnerStacks) { - task.debugTask = previousDebugTask; - } + task.debugTask = previousDebugTask; } return; } @@ -3198,9 +3182,7 @@ function renderChildrenArray( task.keyPath = prevKeyPath; if (__DEV__) { task.componentStack = previousComponentStack; - if (enableOwnerStacks) { - task.debugTask = previousDebugTask; - } + task.debugTask = previousDebugTask; } } @@ -3394,12 +3376,7 @@ function injectPostponedHole( reason: string, thrownInfo: ThrownInfo, ): Segment { - logPostpone( - request, - reason, - thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, - ); + logPostpone(request, reason, thrownInfo, __DEV__ ? task.debugTask : null); // Something suspended, we'll need to create a new segment and resolve it later. const segment = task.blockedSegment; const insertionIndex = segment.chunks.length; @@ -3440,7 +3417,7 @@ function spawnNewSuspendedReplayTask( task.componentStack, task.isFallback, !disableLegacyContext ? task.legacyContext : emptyContextObject, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } @@ -3482,7 +3459,7 @@ function spawnNewSuspendedRenderTask( task.componentStack, task.isFallback, !disableLegacyContext ? task.legacyContext : emptyContextObject, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } @@ -3504,8 +3481,7 @@ function renderNode( const previousKeyPath = task.keyPath; const previousTreeContext = task.treeContext; const previousComponentStack = task.componentStack; - const previousDebugTask = - __DEV__ && enableOwnerStacks ? task.debugTask : null; + const previousDebugTask = __DEV__ ? task.debugTask : null; let x; // Store how much we've pushed at this point so we can reset it in case something // suspended partially through writing something. @@ -3551,7 +3527,7 @@ function renderNode( task.keyPath = previousKeyPath; task.treeContext = previousTreeContext; task.componentStack = previousComponentStack; - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = previousDebugTask; } // Restore all active ReactContexts to what they were before. @@ -3584,7 +3560,7 @@ function renderNode( task.keyPath = previousKeyPath; task.treeContext = previousTreeContext; task.componentStack = previousComponentStack; - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = previousDebugTask; } // Restore all active ReactContexts to what they were before. @@ -3642,7 +3618,7 @@ function renderNode( task.keyPath = previousKeyPath; task.treeContext = previousTreeContext; task.componentStack = previousComponentStack; - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = previousDebugTask; } // Restore all active ReactContexts to what they were before. @@ -3681,7 +3657,7 @@ function renderNode( task.keyPath = previousKeyPath; task.treeContext = previousTreeContext; task.componentStack = previousComponentStack; - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = previousDebugTask; } // Restore all active ReactContexts to what they were before. @@ -3714,7 +3690,7 @@ function renderNode( task.keyPath = previousKeyPath; task.treeContext = previousTreeContext; task.componentStack = previousComponentStack; - if (__DEV__ && enableOwnerStacks) { + if (__DEV__) { task.debugTask = previousDebugTask; } // Restore all active ReactContexts to what they were before. @@ -4421,14 +4397,14 @@ function retryRenderTask( request, postponeInstance.message, thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } else { logRecoverableError( request, x, thrownInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); } @@ -4464,7 +4440,7 @@ function retryRenderTask( request, postponeInstance.message, postponeInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); trackPostpone(request, trackedPostpones, task, segment); finishedTask(request, task.blockedBoundary, segment); @@ -4481,7 +4457,7 @@ function retryRenderTask( task.blockedBoundary, x, errorInfo, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); return; } finally { @@ -4560,7 +4536,7 @@ function retryReplayTask(request: Request, task: ReplayTask): void { errorInfo, task.replay.nodes, task.replay.slots, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugTask : null, ); request.pendingRootTasks--; if (request.pendingRootTasks === 0) { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 0c7d12219c32c..b737c23ee045f 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -17,7 +17,6 @@ import { enablePostpone, enableHalt, enableTaint, - enableOwnerStacks, enableProfilerTimer, enableComponentPerformanceTrack, } from 'shared/ReactFeatureFlags'; @@ -254,15 +253,11 @@ if (__DEV__ && typeof console === 'object' && console !== null) { function getCurrentStackInDEV(): string { if (__DEV__) { - if (enableOwnerStacks) { - const owner: null | ReactComponentInfo = resolveOwner(); - if (owner === null) { - return ''; - } - return getOwnerStackByComponentInfoInDev(owner); + const owner: null | ReactComponentInfo = resolveOwner(); + if (owner === null) { + return ''; } - // We don't have Parent Stacks in Flight. - return ''; + return getOwnerStackByComponentInfoInDev(owner); } return ''; } @@ -624,8 +619,8 @@ function serializeThenable( task.implicitSlot, request.abortableTasks, __DEV__ ? task.debugOwner : null, - __DEV__ && enableOwnerStacks ? task.debugStack : null, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugStack : null, + __DEV__ ? task.debugTask : null, ); if (__DEV__) { // If this came from Flight, forward any debug info into this new row. @@ -744,8 +739,8 @@ function serializeReadableStream( task.implicitSlot, request.abortableTasks, __DEV__ ? task.debugOwner : null, - __DEV__ && enableOwnerStacks ? task.debugStack : null, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugStack : null, + __DEV__ ? task.debugTask : null, ); request.abortableTasks.delete(streamTask); @@ -834,8 +829,8 @@ function serializeAsyncIterable( task.implicitSlot, request.abortableTasks, __DEV__ ? task.debugOwner : null, - __DEV__ && enableOwnerStacks ? task.debugStack : null, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugStack : null, + __DEV__ ? task.debugTask : null, ); request.abortableTasks.delete(streamTask); @@ -1035,23 +1030,21 @@ function callWithDebugContextInDEV<A, T>( key: null, owner: task.debugOwner, }; - if (enableOwnerStacks) { - // $FlowFixMe[cannot-write] - componentDebugInfo.stack = - task.debugStack === null - ? null - : filterStackTrace(request, task.debugStack, 1); - // $FlowFixMe[cannot-write] - componentDebugInfo.debugStack = task.debugStack; - // $FlowFixMe[cannot-write] - componentDebugInfo.debugTask = task.debugTask; - } + // $FlowFixMe[cannot-write] + componentDebugInfo.stack = + task.debugStack === null + ? null + : filterStackTrace(request, task.debugStack, 1); + // $FlowFixMe[cannot-write] + componentDebugInfo.debugStack = task.debugStack; + // $FlowFixMe[cannot-write] + componentDebugInfo.debugTask = task.debugTask; const debugTask = task.debugTask; // We don't need the async component storage context here so we only set the // synchronous tracking of owner. setCurrentOwner(componentDebugInfo); try { - if (enableOwnerStacks && debugTask) { + if (debugTask) { return debugTask.run(callback.bind(null, arg)); } return callback(arg); @@ -1238,22 +1231,18 @@ function renderFunctionComponent<Props>( key: key, owner: task.debugOwner, }: ReactComponentInfo); - if (enableOwnerStacks) { - // $FlowFixMe[cannot-write] - componentDebugInfo.stack = - task.debugStack === null - ? null - : filterStackTrace(request, task.debugStack, 1); - // $FlowFixMe[cannot-write] - componentDebugInfo.props = props; - // $FlowFixMe[cannot-write] - componentDebugInfo.debugStack = task.debugStack; - // $FlowFixMe[cannot-write] - componentDebugInfo.debugTask = task.debugTask; - } else { - // $FlowFixMe[cannot-write] - componentDebugInfo.props = props; - } + // $FlowFixMe[cannot-write] + componentDebugInfo.stack = + task.debugStack === null + ? null + : filterStackTrace(request, task.debugStack, 1); + // $FlowFixMe[cannot-write] + componentDebugInfo.props = props; + // $FlowFixMe[cannot-write] + componentDebugInfo.debugStack = task.debugStack; + // $FlowFixMe[cannot-write] + componentDebugInfo.debugTask = task.debugTask; + // We outline this model eagerly so that we can refer to by reference as an owner. // If we had a smarter way to dedupe we might not have to do this if there ends up // being no references to this as an owner. @@ -1271,14 +1260,14 @@ function renderFunctionComponent<Props>( // We've emitted the latest environment for this task so we track that. task.environmentName = componentEnv; - if (enableOwnerStacks && validated === 2) { + if (validated === 2) { warnForMissingKey(request, key, componentDebugInfo, task.debugTask); } } prepareToUseHooksForComponent(prevThenableState, componentDebugInfo); if (supportsComponentStorage) { // Run the component in an Async Context that tracks the current owner. - if (enableOwnerStacks && task.debugTask) { + if (task.debugTask) { result = task.debugTask.run( // $FlowFixMe[method-unbinding] componentStorage.run.bind( @@ -1300,7 +1289,7 @@ function renderFunctionComponent<Props>( ); } } else { - if (enableOwnerStacks && task.debugTask) { + if (task.debugTask) { result = task.debugTask.run( callComponentInDEV.bind(null, Component, props, componentDebugInfo), ); @@ -1387,7 +1376,7 @@ function warnForMissingKey( if (supportsComponentStorage) { // Run the component in an Async Context that tracks the current owner. - if (enableOwnerStacks && debugTask) { + if (debugTask) { debugTask.run( // $FlowFixMe[method-unbinding] componentStorage.run.bind( @@ -1409,7 +1398,7 @@ function warnForMissingKey( ); } } else { - if (enableOwnerStacks && debugTask) { + if (debugTask) { debugTask.run( callComponentInDEV.bind(null, logKeyError, null, componentDebugInfo), ); @@ -1445,23 +1434,15 @@ function renderFragment( // We have a Server Component that specifies a key but we're now splitting // the tree using a fragment. const fragment = __DEV__ - ? enableOwnerStacks - ? [ - REACT_ELEMENT_TYPE, - REACT_FRAGMENT_TYPE, - task.keyPath, - {children}, - null, - null, - 0, - ] - : [ - REACT_ELEMENT_TYPE, - REACT_FRAGMENT_TYPE, - task.keyPath, - {children}, - null, - ] + ? [ + REACT_ELEMENT_TYPE, + REACT_FRAGMENT_TYPE, + task.keyPath, + {children}, + null, + null, + 0, + ] : [REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, task.keyPath, {children}]; if (!task.implicitSlot) { // If this was keyed inside a set. I.e. the outer Server Component was keyed @@ -1517,23 +1498,15 @@ function renderAsyncFragment( // We have a Server Component that specifies a key but we're now splitting // the tree using a fragment. const fragment = __DEV__ - ? enableOwnerStacks - ? [ - REACT_ELEMENT_TYPE, - REACT_FRAGMENT_TYPE, - task.keyPath, - {children}, - null, - null, - 0, - ] - : [ - REACT_ELEMENT_TYPE, - REACT_FRAGMENT_TYPE, - task.keyPath, - {children}, - null, - ] + ? [ + REACT_ELEMENT_TYPE, + REACT_FRAGMENT_TYPE, + task.keyPath, + {children}, + null, + null, + 0, + ] : [REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, task.keyPath, {children}]; if (!task.implicitSlot) { // If this was keyed inside a set. I.e. the outer Server Component was keyed @@ -1586,19 +1559,17 @@ function renderClientElement( } } const element = __DEV__ - ? enableOwnerStacks - ? [ - REACT_ELEMENT_TYPE, - type, - key, - props, - task.debugOwner, - task.debugStack === null - ? null - : filterStackTrace(request, task.debugStack, 1), - validated, - ] - : [REACT_ELEMENT_TYPE, type, key, props, task.debugOwner] + ? [ + REACT_ELEMENT_TYPE, + type, + key, + props, + task.debugOwner, + task.debugStack === null + ? null + : filterStackTrace(request, task.debugStack, 1), + validated, + ] : [REACT_ELEMENT_TYPE, type, key, props]; if (task.implicitSlot && key !== null) { // The root Server Component had no key so it was in an implicit slot. @@ -1626,8 +1597,8 @@ function outlineTask(request: Request, task: Task): ReactJSONValue { task.implicitSlot, request.abortableTasks, __DEV__ ? task.debugOwner : null, - __DEV__ && enableOwnerStacks ? task.debugStack : null, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugStack : null, + __DEV__ ? task.debugTask : null, ); retryTask(request, newTask); @@ -1694,7 +1665,7 @@ function renderElement( } else if (type === REACT_FRAGMENT_TYPE && key === null) { // For key-less fragments, we add a small optimization to avoid serializing // it as a wrapper. - if (__DEV__ && enableOwnerStacks && validated === 2) { + if (__DEV__ && validated === 2) { // Create a fake owner node for the error stack. const componentDebugInfo: ReactComponentInfo = { name: 'Fragment', @@ -1900,10 +1871,8 @@ function createTask( if (__DEV__) { task.environmentName = request.environmentName(); task.debugOwner = debugOwner; - if (enableOwnerStacks) { - task.debugStack = debugStack; - task.debugTask = debugTask; - } + task.debugStack = debugStack; + task.debugTask = debugTask; } abortSet.add(task); return task; @@ -2348,8 +2317,8 @@ function renderModel( task.implicitSlot, request.abortableTasks, __DEV__ ? task.debugOwner : null, - __DEV__ && enableOwnerStacks ? task.debugStack : null, - __DEV__ && enableOwnerStacks ? task.debugTask : null, + __DEV__ ? task.debugStack : null, + __DEV__ ? task.debugTask : null, ); const ping = newTask.ping; (x: any).then(ping, ping); @@ -2487,10 +2456,8 @@ function renderModelDestructive( if (__DEV__) { task.debugOwner = element._owner; - if (enableOwnerStacks) { - task.debugStack = element._debugStack; - task.debugTask = element._debugTask; - } + task.debugStack = element._debugStack; + task.debugTask = element._debugTask; // TODO: Pop this. Since we currently don't have a point where we can pop the stack // this debug information will be used for errors inside sibling properties that // are not elements. Leading to the wrong attribution on the server. We could fix @@ -2506,7 +2473,7 @@ function renderModelDestructive( element.key, ref, props, - __DEV__ && enableOwnerStacks ? element._store.validated : 0, + __DEV__ ? element._store.validated : 0, ); if ( typeof newChild === 'object' && @@ -3291,10 +3258,8 @@ function outlineComponentInfo( key: componentInfo.key, owner: componentInfo.owner, }; - if (enableOwnerStacks) { - // $FlowFixMe[cannot-write] - componentDebugInfo.stack = componentInfo.stack; - } + // $FlowFixMe[cannot-write] + componentDebugInfo.stack = componentInfo.stack; // Ensure we serialize props after the stack to favor the stack being complete. // $FlowFixMe[cannot-write] componentDebugInfo.props = componentInfo.props; @@ -3435,33 +3400,23 @@ function renderConsoleValue( doNotLimit.add(element._owner); } - if (enableOwnerStacks) { - let debugStack: null | ReactStackTrace = null; - if (element._debugStack != null) { - // Outline the debug stack so that it doesn't get cut off. - debugStack = filterStackTrace(request, element._debugStack, 1); - doNotLimit.add(debugStack); - for (let i = 0; i < debugStack.length; i++) { - doNotLimit.add(debugStack[i]); - } + let debugStack: null | ReactStackTrace = null; + if (element._debugStack != null) { + // Outline the debug stack so that it doesn't get cut off. + debugStack = filterStackTrace(request, element._debugStack, 1); + doNotLimit.add(debugStack); + for (let i = 0; i < debugStack.length; i++) { + doNotLimit.add(debugStack[i]); } - return [ - REACT_ELEMENT_TYPE, - element.type, - element.key, - element.props, - element._owner, - debugStack, - element._store.validated, - ]; } - return [ REACT_ELEMENT_TYPE, element.type, element.key, element.props, element._owner, + debugStack, + element._store.validated, ]; } } diff --git a/packages/react/index.fb.js b/packages/react/index.fb.js index 29133df24f3a7..828db7a48a542 100644 --- a/packages/react/index.fb.js +++ b/packages/react/index.fb.js @@ -7,8 +7,8 @@ * @flow */ -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; import {captureOwnerStack as captureOwnerStackImpl} from './src/ReactClient'; + export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __COMPILER_RUNTIME, @@ -72,7 +72,7 @@ export {useMemoCache as c} from './src/ReactHooks'; // Only export captureOwnerStack in development. let captureOwnerStack: ?() => null | string; -if (__DEV__ && enableOwnerStacks) { +if (__DEV__) { captureOwnerStack = captureOwnerStackImpl; } diff --git a/packages/react/src/ReactOwnerStack.js b/packages/react/src/ReactOwnerStack.js index 4746c3a7c6f48..7c0776be406d1 100644 --- a/packages/react/src/ReactOwnerStack.js +++ b/packages/react/src/ReactOwnerStack.js @@ -7,18 +7,17 @@ * @flow */ -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; export function captureOwnerStack(): null | string { - if (!enableOwnerStacks || !__DEV__) { - return null; + if (__DEV__) { + const getCurrentStack = ReactSharedInternals.getCurrentStack; + if (getCurrentStack === null) { + return null; + } + // The current stack will be the owner stack which it is always here. + return getCurrentStack(); } - const getCurrentStack = ReactSharedInternals.getCurrentStack; - if (getCurrentStack === null) { - return null; - } - // The current stack will be the owner stack if enableOwnerStacks is true - // which it is always here. Otherwise it's the parent stack. - return getCurrentStack(); + + return null; } diff --git a/packages/react/src/ReactServer.fb.js b/packages/react/src/ReactServer.fb.js index fe6260a799a2c..634cb077af195 100644 --- a/packages/react/src/ReactServer.fb.js +++ b/packages/react/src/ReactServer.fb.js @@ -10,7 +10,6 @@ export {default as __SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './ReactSharedInternalsServer'; import {forEach, map, count, toArray, only} from './ReactChildren'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; import {captureOwnerStack as captureOwnerStackImpl} from './ReactOwnerStack'; import { REACT_FRAGMENT_TYPE, @@ -39,9 +38,8 @@ const Children = { only, }; -// Only export captureOwnerStack if the flag is on, to support feature detection. let captureOwnerStack: ?() => null | string; -if (__DEV__ && enableOwnerStacks) { +if (__DEV__) { captureOwnerStack = captureOwnerStackImpl; } diff --git a/packages/react/src/__tests__/ReactChildren-test.js b/packages/react/src/__tests__/ReactChildren-test.js index 7843d203b0a15..a767a38c2f3f5 100644 --- a/packages/react/src/__tests__/ReactChildren-test.js +++ b/packages/react/src/__tests__/ReactChildren-test.js @@ -333,16 +333,6 @@ describe('ReactChildren', () => { } const instance = <div>{threeDivIterable}</div>; - assertConsoleErrorDev( - // With the flag on this doesn't warn eagerly but only when rendered - gate(flag => flag.enableOwnerStacks) - ? [] - : [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <div>. See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ], - ); React.Children.forEach(instance.props.children, callback, context); @@ -369,8 +359,7 @@ describe('ReactChildren', () => { 'Each child in a list should have a unique "key" prop.\n\n' + 'Check the top-level render call using <div>. It was passed a child from div.' + ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)' + - (gate(flag => flag.enableOwnerStacks) ? '' : '\n in div (at **)'), + ' in div (at **)', ]); }); @@ -894,22 +883,13 @@ describe('ReactChildren', () => { </ComponentRenderingMappedChildren>, ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentRenderingMappedChildren`.' + - ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)\n' + - ' in **/ReactChildren-test.js:**:** (at **)', - ] - : [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <ComponentRenderingMappedChildren>.' + - ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ], - ); + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `ComponentRenderingMappedChildren`.' + + ' See https://react.dev/link/warning-keys for more information.\n' + + ' in div (at **)\n' + + ' in **/ReactChildren-test.js:**:** (at **)', + ]); }); it('does not warn for mapped static children without keys', async () => { @@ -953,21 +933,12 @@ describe('ReactChildren', () => { </ComponentRenderingClonedChildren>, ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentRenderingClonedChildren`.' + - ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ] - : [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <ComponentRenderingClonedChildren>.' + - ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ], - ); + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `ComponentRenderingClonedChildren`.' + + ' See https://react.dev/link/warning-keys for more information.\n' + + ' in div (at **)', + ]); }); it('does not warn for cloned static children without keys', async () => { @@ -1005,21 +976,12 @@ describe('ReactChildren', () => { </ComponentRenderingFlattenedChildren>, ); }); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentRenderingFlattenedChildren`.' + - ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ] - : [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <ComponentRenderingFlattenedChildren>.' + - ' See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', - ], - ); + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `ComponentRenderingFlattenedChildren`.' + + ' See https://react.dev/link/warning-keys for more information.\n' + + ' in div (at **)', + ]); }); it('does not warn for flattened static children without keys', async () => { diff --git a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee index 5fab9d7d0bbde..41887b259f5ef 100644 --- a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee +++ b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee @@ -266,26 +266,14 @@ describe 'ReactCoffeeScriptClass', -> test React.createElement(Outer), 'SPAN', 'foo' - if featureFlags.enableOwnerStacks - assertConsoleErrorDev([ - 'Outer uses the legacy childContextTypes API which will soon be removed. - Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in Outer (at **)', - 'Foo uses the legacy contextTypes API which will soon be removed. - Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Outer (at **)', - ]); - else - assertConsoleErrorDev([ - 'Outer uses the legacy childContextTypes API which will soon be removed. - Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - ' in Outer (at **)', - 'Foo uses the legacy contextTypes API which will soon be removed. - Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - ' in Foo (at **)\n' + - ' in Outer (at **)', - ]); - + assertConsoleErrorDev([ + 'Outer uses the legacy childContextTypes API which will soon be removed. + Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + + ' in Outer (at **)', + 'Foo uses the legacy contextTypes API which will soon be removed. + Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + + ' in Outer (at **)', + ]); it 'renders only once when setting state in componentWillMount', -> renderCount = 0 @@ -578,24 +566,13 @@ describe 'ReactCoffeeScriptClass', -> React.createElement Bar test React.createElement(Foo), 'DIV', 'bar-through-context' - if featureFlags.enableOwnerStacks - assertConsoleErrorDev [ - 'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead. - (https://react.dev/link/legacy-context)\n' + - ' in Foo (at **)', - 'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead. - (https://react.dev/link/legacy-context)\n' + - ' in Foo (at **)' - ] - else - assertConsoleErrorDev [ - 'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead. - (https://react.dev/link/legacy-context)\n' + - ' in Foo (at **)', - 'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead. - (https://react.dev/link/legacy-context)\n' + - ' in Bar (at **)\n' + - ' in Foo (at **)' - ] + assertConsoleErrorDev [ + 'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead. + (https://react.dev/link/legacy-context)\n' + + ' in Foo (at **)', + 'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead. + (https://react.dev/link/legacy-context)\n' + + ' in Foo (at **)' + ] undefined diff --git a/packages/react/src/__tests__/ReactContextValidator-test.js b/packages/react/src/__tests__/ReactContextValidator-test.js index 66597f0dd52d0..96e46fa52804f 100644 --- a/packages/react/src/__tests__/ReactContextValidator-test.js +++ b/packages/react/src/__tests__/ReactContextValidator-test.js @@ -78,9 +78,7 @@ describe('ReactContextValidator', () => { ' in ComponentInFooBarContext (at **)', 'Component uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in ComponentInFooBarContext (at **)' - : ' in Component (at **)'), + ' in ComponentInFooBarContext (at **)', ]); expect(instance.childRef.current.context).toEqual({foo: 'abc'}); }); @@ -159,9 +157,7 @@ describe('ReactContextValidator', () => { ' in Parent (at **)', 'Component uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in Parent (at **)' - : ' in Component (at **)'), + ' in Parent (at **)', ]); expect(constructorContext).toEqual({foo: 'abc'}); @@ -282,21 +278,12 @@ describe('ReactContextValidator', () => { ' in ParentContextProvider (at **)', 'MiddleMissingContext uses the legacy childContextTypes API which will soon be removed. ' + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in MiddleMissingContext (at **)\n') + ' in ParentContextProvider (at **)', 'MiddleMissingContext.childContextTypes is specified but there is no getChildContext() method on the instance. ' + 'You can either define getChildContext() on MiddleMissingContext or remove childContextTypes from it.\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in MiddleMissingContext (at **)\n') + ' in ParentContextProvider (at **)', 'ChildContextConsumer uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in ChildContextConsumer (at **)\n') + ' in MiddleMissingContext (at **)\n' + ' in ParentContextProvider (at **)', ]); diff --git a/packages/react/src/__tests__/ReactCreateElement-test.js b/packages/react/src/__tests__/ReactCreateElement-test.js index 44952536ac470..4aa76348e56b5 100644 --- a/packages/react/src/__tests__/ReactCreateElement-test.js +++ b/packages/react/src/__tests__/ReactCreateElement-test.js @@ -77,13 +77,7 @@ describe('ReactCreateElement', () => { 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://react.dev/link/special-props)\n' + - (gate(flags => flags.enableOwnerStacks) - ? [' in Parent (at **)'] - : [ - ' in Child (at **)\n' + - ' in div (at **)\n' + - ' in Parent (at **)', - ]), + ' in Parent (at **)', ]); }); diff --git a/packages/react/src/__tests__/ReactCreateRef-test.js b/packages/react/src/__tests__/ReactCreateRef-test.js index bf7166d8cf80a..3dedb3ccdcee1 100644 --- a/packages/react/src/__tests__/ReactCreateRef-test.js +++ b/packages/react/src/__tests__/ReactCreateRef-test.js @@ -46,10 +46,7 @@ describe('ReactCreateRef', () => { assertConsoleErrorDev([ 'Unexpected ref object provided for div. ' + 'Use either a ref-setter function or React.createRef().\n' + - ' in div (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in Wrapper (at **)'), + ' in div (at **)', ]); ReactDOM.flushSync(() => { @@ -62,10 +59,7 @@ describe('ReactCreateRef', () => { assertConsoleErrorDev([ 'Unexpected ref object provided for ExampleComponent. ' + 'Use either a ref-setter function or React.createRef().\n' + - ' in ExampleComponent (at **)' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : '\n in Wrapper (at **)'), + ' in ExampleComponent (at **)', ]); }); }); diff --git a/packages/react/src/__tests__/ReactES6Class-test.js b/packages/react/src/__tests__/ReactES6Class-test.js index 640ae7ef37542..a33a7b06682a8 100644 --- a/packages/react/src/__tests__/ReactES6Class-test.js +++ b/packages/react/src/__tests__/ReactES6Class-test.js @@ -305,9 +305,6 @@ describe('ReactES6Class', () => { ' in Outer (at **)', 'Foo uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Foo (at **)\n') + ' in Outer (at **)', ]); }); @@ -638,9 +635,6 @@ describe('ReactES6Class', () => { ' in Foo (at **)', 'Bar uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Bar (at **)\n') + ' in Foo (at **)', ]); }); diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js index 1191aa7ec2743..bbb06411b03e2 100644 --- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js +++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js @@ -46,19 +46,11 @@ describe('ReactElementValidator', () => { ]), ), ); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentClass`. See https://react.dev/link/warning-keys for more information.\n' + - ' in ComponentClass (at **)', - ] - : [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <ComponentClass>. See https://react.dev/link/warning-keys for more information.\n' + - ' in ComponentClass (at **)', - ], - ); + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `ComponentClass`. See https://react.dev/link/warning-keys for more information.\n' + + ' in ComponentClass (at **)', + ]); }); it('warns for keys for arrays of elements with owner info', async () => { @@ -83,18 +75,10 @@ describe('ReactElementValidator', () => { await act(() => root.render(React.createElement(ComponentWrapper))); assertConsoleErrorDev([ 'Each child in a list should have a unique "key" prop.' + - '\n\nCheck the render method of `' + - (gate(flags => flags.enableOwnerStacks) - ? 'ComponentClass' - : 'InnerClass') + - '`. ' + + '\n\nCheck the render method of `ComponentClass`. ' + 'It was passed a child from ComponentWrapper. ' + 'See https://react.dev/link/warning-keys for more information.\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in ComponentWrapper (at **)' - : ' in ComponentClass (at **)\n' + - ' in InnerClass (at **)\n' + - ' in ComponentWrapper (at **)'), + ' in ComponentWrapper (at **)', ]); }); @@ -109,16 +93,12 @@ describe('ReactElementValidator', () => { const root = ReactDOMClient.createRoot(document.createElement('div')); await act(() => root.render(<Anonymous>{divs}</Anonymous>)); assertConsoleErrorDev([ - gate(flags => flags.enableOwnerStacks) - ? // For owner stacks the parent being validated is the div. - 'Each child in a list should have a unique ' + - '"key" prop.' + - '\n\nCheck the top-level render call using <div>. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)' - : 'Each child in a list should have a unique ' + - '"key" prop. See https://react.dev/link/warning-keys for more information.\n' + - ' in div (at **)', + // For owner stacks the parent being validated is the div. + 'Each child in a list should have a unique ' + + '"key" prop.' + + '\n\nCheck the top-level render call using <div>. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in div (at **)', ]); }); @@ -158,9 +138,6 @@ describe('ReactElementValidator', () => { 'https://react.dev/link/warning-keys for more information.\n' + ' in div (at **)\n' + ' in Component (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Parent (at **)\n') + ' in GrandParent (at **)', ]); }); @@ -206,35 +183,12 @@ describe('ReactElementValidator', () => { await act(() => root.render(React.createElement(ComponentClass, null, iterable)), ); - assertConsoleErrorDev( - gate(flag => flag.enableOwnerStacks) - ? [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentClass`. It was passed a child from div. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in ComponentClass (at **)', - ] - : // Since each pass generates a new element, it doesn't get marked as - // validated and it gets rechecked each time. - [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <ComponentClass>. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in ComponentClass (at **)', - - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentClass`. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in ComponentClass (at **)\n' + - ' in ComponentClass (at **)', - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `ComponentClass`. It was passed a child from div. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in ComponentClass (at **)\n' + - ' in div (at **)\n' + - ' in ComponentClass (at **)', - ], - ); + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `ComponentClass`. It was passed a child from div. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in ComponentClass (at **)', + ]); }); it('does not warns for arrays of elements with keys', () => { @@ -373,13 +327,6 @@ describe('ReactElementValidator', () => { ]; for (let i = 0; i < cases.length; i++) { await act(async () => root.render(cases[i][0]())); - assertConsoleErrorDev( - gate(flag => flag.enableOwnerStacks) - ? // We don't need these extra warnings because we already have the errors. - [] - : [cases[i][1]], - {withoutStack: true}, - ); } expect(errors).toEqual( @@ -468,21 +415,6 @@ describe('ReactElementValidator', () => { 'or a class/function (for composite components) but got: null.' + (__DEV__ ? '\n\nCheck the render method of `ParentComp`.' : ''), ); - assertConsoleErrorDev( - gate(flag => flag.enableOwnerStacks) - ? // We don't need these extra warnings because we already have the errors. - [] - : [ - 'React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: null.\n' + - ' in ParentComp (at **)', - 'React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: null.\n' + - ' in ParentComp (at **)', - ], - ); }); it('warns for fragments with illegal attributes', async () => { @@ -559,18 +491,6 @@ describe('ReactElementValidator', () => { it('does not blow up on key warning with undefined type', () => { const Foo = undefined; void (<Foo>{[<div />]}</Foo>); - assertConsoleErrorDev( - gate(flags => flags.enableOwnerStacks) - ? [] - : [ - 'React.jsx: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite ' + - 'components) but got: undefined. You likely forgot to export your ' + - "component from the file it's defined in, or you might have mixed up " + - 'default and named imports.', - ], - {withoutStack: true}, - ); }); it('does not call lazy initializers eagerly', () => { diff --git a/packages/react/src/__tests__/ReactJSXElementValidator-test.js b/packages/react/src/__tests__/ReactJSXElementValidator-test.js index d4e78f1b1cd46..041191e2ab5fd 100644 --- a/packages/react/src/__tests__/ReactJSXElementValidator-test.js +++ b/packages/react/src/__tests__/ReactJSXElementValidator-test.js @@ -49,13 +49,9 @@ describe('ReactJSXElementValidator', () => { root.render(<Component>{[<Component />, <Component />]}</Component>); }); assertConsoleErrorDev([ - gate(flags => flags.enableOwnerStacks) - ? 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `Component`. See https://react.dev/link/warning-keys for more information.\n' + - ' in Component (at **)' - : 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <Component>. See https://react.dev/link/warning-keys for more information.\n' + - ' in Component (at **)', + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `Component`. See https://react.dev/link/warning-keys for more information.\n' + + ' in Component (at **)', ]); }); @@ -79,17 +75,9 @@ describe('ReactJSXElementValidator', () => { }); assertConsoleErrorDev([ 'Each child in a list should have a unique "key" prop.' + - '\n\nCheck the render method of `' + - (gate(flag => flag.enableOwnerStacks) - ? 'Component' - : 'InnerComponent') + - '`. ' + + '\n\nCheck the render method of `Component`. ' + 'It was passed a child from ComponentWrapper. See https://react.dev/link/warning-keys for more information.\n' + - (gate(flag => flag.enableOwnerStacks) - ? ' in ComponentWrapper (at **)' - : ' in Component (at **)\n' + - ' in InnerComponent (at **)\n' + - ' in ComponentWrapper (at **)'), + ' in ComponentWrapper (at **)', ]); }); @@ -112,34 +100,12 @@ describe('ReactJSXElementValidator', () => { await act(() => { root.render(<Component>{iterable}</Component>); }); - assertConsoleErrorDev( - gate(flag => flag.enableOwnerStacks) - ? [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `Component`. It was passed a child from div. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Component (at **)', - ] - : // Since each pass generates a new element, it doesn't get marked as - // validated and it gets rechecked each time. - [ - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the top-level render call using <Component>. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Component (at **)', - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `Component`. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Component (at **)\n' + - ' in Component (at **)', - 'Each child in a list should have a unique "key" prop.\n\n' + - 'Check the render method of `Component`. It was passed a child from div. ' + - 'See https://react.dev/link/warning-keys for more information.\n' + - ' in Component (at **)\n' + - ' in div (at **)\n' + - ' in Component (at **)', - ], - ); + assertConsoleErrorDev([ + 'Each child in a list should have a unique "key" prop.\n\n' + + 'Check the render method of `Component`. It was passed a child from div. ' + + 'See https://react.dev/link/warning-keys for more information.\n' + + ' in Component (at **)', + ]); }); it('does not warn for arrays of elements with keys', async () => { diff --git a/packages/react/src/__tests__/ReactJSXRuntime-test.js b/packages/react/src/__tests__/ReactJSXRuntime-test.js index 663c935caf1da..3553561086bef 100644 --- a/packages/react/src/__tests__/ReactJSXRuntime-test.js +++ b/packages/react/src/__tests__/ReactJSXRuntime-test.js @@ -215,11 +215,7 @@ describe('ReactJSXRuntime', () => { 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://react.dev/link/special-props)\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in Parent (at **)' - : ' in Child (at **)\n' + - ' in div (at **)\n' + - ' in Parent (at **)'), + ' in Parent (at **)', ]); }); @@ -279,9 +275,6 @@ describe('ReactJSXRuntime', () => { assertConsoleErrorDev([ 'Each child in a list should have a unique "key" prop.\n\n' + 'Check the render method of `Parent`. See https://react.dev/link/warning-keys for more information.\n' + - (gate(flags => flags.enableOwnerStacks) - ? '' - : ' in Child (at **)\n') + ' in Parent (at **)', ]); }); diff --git a/packages/react/src/__tests__/ReactStrictMode-test.js b/packages/react/src/__tests__/ReactStrictMode-test.js index 6d44a28881fad..04046d88978ea 100644 --- a/packages/react/src/__tests__/ReactStrictMode-test.js +++ b/packages/react/src/__tests__/ReactStrictMode-test.js @@ -1042,67 +1042,30 @@ describe('context legacy', () => { root.render(<Root />); }); - if (gate(flags => flags.enableOwnerStacks)) { - assertConsoleErrorDev([ - 'LegacyContextProvider uses the legacy childContextTypes API ' + - 'which will soon be removed. Use React.createContext() instead. ' + - '(https://react.dev/link/legacy-context)' + - '\n in Root (at **)', - 'LegacyContextConsumer uses the legacy contextTypes API which ' + - 'will soon be removed. Use React.createContext() with static ' + - 'contextType instead. (https://react.dev/link/legacy-context)' + - '\n in LegacyContextProvider (at **)' + - '\n in Root (at **)', - 'FunctionalLegacyContextConsumer uses the legacy contextTypes ' + - 'API which will be removed soon. Use React.createContext() ' + - 'with React.useContext() instead. (https://react.dev/link/legacy-context)' + - '\n in LegacyContextProvider (at **)' + - '\n in Root (at **)', - 'Legacy context API has been detected within a strict-mode tree.' + - '\n\nThe old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.' + - '\n\nPlease update the following components: ' + - 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' + - '\n\nLearn more about this warning here: ' + - 'https://react.dev/link/legacy-context' + - '\n in Root (at **)', - ]); - } else { - assertConsoleErrorDev([ - 'LegacyContextProvider uses the legacy childContextTypes API ' + - 'which will soon be removed. Use React.createContext() instead. ' + - '(https://react.dev/link/legacy-context)' + - '\n in LegacyContextProvider (at **)' + - '\n in div (at **)' + - '\n in Root (at **)', - 'LegacyContextConsumer uses the legacy contextTypes API which ' + - 'will soon be removed. Use React.createContext() with static ' + - 'contextType instead. (https://react.dev/link/legacy-context)' + - '\n in LegacyContextConsumer (at **)' + - '\n in div (at **)' + - '\n in LegacyContextProvider (at **)' + - '\n in div (at **)' + - '\n in Root (at **)', - 'FunctionalLegacyContextConsumer uses the legacy contextTypes ' + - 'API which will be removed soon. Use React.createContext() ' + - 'with React.useContext() instead. (https://react.dev/link/legacy-context)' + - '\n in FunctionalLegacyContextConsumer (at **)' + - '\n in div (at **)' + - '\n in LegacyContextProvider (at **)' + - '\n in div (at **)' + - '\n in Root (at **)', - 'Legacy context API has been detected within a strict-mode tree.' + - '\n\nThe old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.' + - '\n\nPlease update the following components: ' + - 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' + - '\n\nLearn more about this warning here: ' + - 'https://react.dev/link/legacy-context' + - '\n in LegacyContextProvider (at **)' + - '\n in div (at **)' + - '\n in Root (at **)', - ]); - } + assertConsoleErrorDev([ + 'LegacyContextProvider uses the legacy childContextTypes API ' + + 'which will soon be removed. Use React.createContext() instead. ' + + '(https://react.dev/link/legacy-context)' + + '\n in Root (at **)', + 'LegacyContextConsumer uses the legacy contextTypes API which ' + + 'will soon be removed. Use React.createContext() with static ' + + 'contextType instead. (https://react.dev/link/legacy-context)' + + '\n in LegacyContextProvider (at **)' + + '\n in Root (at **)', + 'FunctionalLegacyContextConsumer uses the legacy contextTypes ' + + 'API which will be removed soon. Use React.createContext() ' + + 'with React.useContext() instead. (https://react.dev/link/legacy-context)' + + '\n in LegacyContextProvider (at **)' + + '\n in Root (at **)', + 'Legacy context API has been detected within a strict-mode tree.' + + '\n\nThe old API will be supported in all 16.x releases, but applications ' + + 'using it should migrate to the new version.' + + '\n\nPlease update the following components: ' + + 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' + + '\n\nLearn more about this warning here: ' + + 'https://react.dev/link/legacy-context' + + '\n in Root (at **)', + ]); // Dedupe await act(() => { diff --git a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts index 603edb86ea495..254f686184a20 100644 --- a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts +++ b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts @@ -528,10 +528,7 @@ describe('ReactTypeScriptClass', function () { ' in ProvideChildContextTypes (at **)', 'StateBasedOnContext uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (ReactFeatureFlags.enableOwnerStacks - ? ' in ProvideChildContextTypes.createElement (at **)' - : ' in StateBasedOnContext (at **)\n') + - ' in ProvideChildContextTypes (at **)', + ' in ProvideChildContextTypes.createElement (at **)', ]); }); } @@ -724,10 +721,7 @@ describe('ReactTypeScriptClass', function () { ' in ProvideContext (at **)', 'ReadContext uses the legacy contextTypes API which will soon be removed. ' + 'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)\n' + - (ReactFeatureFlags.enableOwnerStacks - ? ' in ProvideContext.createElement (at **)' - : ' in ReadContext (at **)\n') + - ' in ProvideContext (at **)', + ' in ProvideContext.createElement (at **)', ]); }); } diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.js b/packages/react/src/__tests__/createReactClassIntegration-test.js index 6bd3f0692168c..b73c8d06eda85 100644 --- a/packages/react/src/__tests__/createReactClassIntegration-test.js +++ b/packages/react/src/__tests__/createReactClassIntegration-test.js @@ -348,14 +348,10 @@ describe('create-react-class-integration', () => { root.render(<Outer />); }); assertConsoleErrorDev([ - gate(flags => - flags.enableOwnerStacks - ? [ - 'Component uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.', - {withoutStack: true}, - ] - : 'Component uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.', - ), + [ + 'Component uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.', + {withoutStack: true}, + ], 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.', ]); expect(container.firstChild.className).toBe('foo'); diff --git a/packages/react/src/__tests__/forwardRef-test.js b/packages/react/src/__tests__/forwardRef-test.js index e08990c806355..aa151fc6d1c81 100644 --- a/packages/react/src/__tests__/forwardRef-test.js +++ b/packages/react/src/__tests__/forwardRef-test.js @@ -213,9 +213,7 @@ describe('forwardRef', () => { '\n\nCheck the top-level render call using <ForwardRef>. It was passed a child from ForwardRef. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - (gate(flags => flags.enableOwnerStacks) - ? ' in **/forwardRef-test.js:**:** (at **)' - : ' in p (at **)'), + ' in **/forwardRef-test.js:**:** (at **)', ]); }); @@ -235,8 +233,7 @@ describe('forwardRef', () => { '\n\nCheck the top-level render call using <ForwardRef(Inner)>. It was passed a child from ForwardRef(Inner). ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Inner (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Inner (at **)', ]); }); @@ -257,8 +254,7 @@ describe('forwardRef', () => { '\n\nCheck the top-level render call using <ForwardRef(Inner)>. It was passed a child from ForwardRef(Inner). ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Inner (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Inner (at **)', ]); }); @@ -278,8 +274,7 @@ describe('forwardRef', () => { '\n\nCheck the top-level render call using <Outer>. It was passed a child from Outer. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Outer (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Outer (at **)', ]); }); @@ -301,8 +296,7 @@ describe('forwardRef', () => { '\n\nCheck the top-level render call using <Outer>. It was passed a child from Outer. ' + 'See https://react.dev/link/warning-keys for more information.\n' + ' in span (at **)\n' + - ' in Inner (at **)' + - (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'), + ' in Inner (at **)', ]); }); diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index 5edcb333ea571..0f5fb1cd43c3b 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -10,25 +10,17 @@ import ReactSharedInternals from 'shared/ReactSharedInternals'; import hasOwnProperty from 'shared/hasOwnProperty'; import assign from 'shared/assign'; import { - getIteratorFn, REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, REACT_LAZY_TYPE, } from 'shared/ReactSymbols'; import {checkKeyStringCoercion} from 'shared/CheckStringCoercion'; -import isValidElementType from 'shared/isValidElementType'; import isArray from 'shared/isArray'; -import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame'; -import { - disableDefaultPropsExceptForClasses, - enableOwnerStacks, -} from 'shared/ReactFeatureFlags'; - -const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference'); +import {disableDefaultPropsExceptForClasses} from 'shared/ReactFeatureFlags'; const createTask = // eslint-disable-next-line react-internal/no-production-logging - __DEV__ && enableOwnerStacks && console.createTask + __DEV__ && console.createTask ? // eslint-disable-next-line react-internal/no-production-logging console.createTask : () => null; @@ -261,20 +253,18 @@ function ReactElement( writable: true, value: null, }); - if (enableOwnerStacks) { - Object.defineProperty(element, '_debugStack', { - configurable: false, - enumerable: false, - writable: true, - value: debugStack, - }); - Object.defineProperty(element, '_debugTask', { - configurable: false, - enumerable: false, - writable: true, - value: debugTask, - }); - } + Object.defineProperty(element, '_debugStack', { + configurable: false, + enumerable: false, + writable: true, + value: debugStack, + }); + Object.defineProperty(element, '_debugTask', { + configurable: false, + enumerable: false, + writable: true, + value: debugTask, + }); if (Object.freeze) { Object.freeze(element.props); Object.freeze(element); @@ -390,8 +380,8 @@ export function jsxProdSignatureRunningInDevWithDynamicChildren( isStaticChildren, source, self, - __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined, - __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined, + __DEV__ && Error('react-stack-top-frame'), + __DEV__ && createTask(getTaskName(type)), ); } } @@ -412,8 +402,8 @@ export function jsxProdSignatureRunningInDevWithStaticChildren( isStaticChildren, source, self, - __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined, - __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined, + __DEV__ && Error('react-stack-top-frame'), + __DEV__ && createTask(getTaskName(type)), ); } } @@ -434,8 +424,8 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) { isStaticChildren, source, self, - __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined, - __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined, + __DEV__ && Error('react-stack-top-frame'), + __DEV__ && createTask(getTaskName(type)), ); } @@ -450,80 +440,37 @@ function jsxDEVImpl( debugTask, ) { if (__DEV__) { - if (!enableOwnerStacks && !isValidElementType(type)) { - // This is an invalid element type. - // - // We warn here so that we can get better stack traces but with enableOwnerStacks - // enabled we don't need this because we get good stacks if we error in the - // renderer anyway. The renderer is the only one that knows what types are valid - // for this particular renderer so we let it error there instead. - // - // We warn in this case but don't throw. We expect the element creation to - // succeed and there will likely be errors in render. - let info = ''; - if ( - type === undefined || - (typeof type === 'object' && - type !== null && - Object.keys(type).length === 0) - ) { - info += - ' You likely forgot to export your component from the file ' + - "it's defined in, or you might have mixed up default and named imports."; - } - - let typeString; - if (type === null) { - typeString = 'null'; - } else if (isArray(type)) { - typeString = 'array'; - } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) { - typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`; - info = - ' Did you accidentally export a JSX literal instead of a component?'; - } else { - typeString = typeof type; - } + // We don't warn for invalid element type here because with owner stacks, + // we error in the renderer. The renderer is the only one that knows what + // types are valid for this particular renderer so we let it error there. + + // Skip key warning if the type isn't valid since our key validation logic + // doesn't expect a non-string/function type and can throw confusing + // errors. We don't want exception behavior to differ between dev and + // prod. (Rendering will throw with a helpful message and as soon as the + // type is fixed, the key warnings will appear.) + // With owner stacks, we no longer need the type here so this comment is + // no longer true. Which is why we can run this even for invalid types. + const children = config.children; + if (children !== undefined) { + if (isStaticChildren) { + if (isArray(children)) { + for (let i = 0; i < children.length; i++) { + validateChildKeys(children[i], type); + } - console.error( - 'React.jsx: type is invalid -- expected a string (for ' + - 'built-in components) or a class/function (for composite ' + - 'components) but got: %s.%s', - typeString, - info, - ); - } else { - // This is a valid element type. - - // Skip key warning if the type isn't valid since our key validation logic - // doesn't expect a non-string/function type and can throw confusing - // errors. We don't want exception behavior to differ between dev and - // prod. (Rendering will throw with a helpful message and as soon as the - // type is fixed, the key warnings will appear.) - // When enableOwnerStacks is on, we no longer need the type here so this - // comment is no longer true. Which is why we can run this even for invalid - // types. - const children = config.children; - if (children !== undefined) { - if (isStaticChildren) { - if (isArray(children)) { - for (let i = 0; i < children.length; i++) { - validateChildKeys(children[i], type); - } - - if (Object.freeze) { - Object.freeze(children); - } - } else { - console.error( - 'React.jsx: Static children should always be an array. ' + - 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + - 'Use the Babel transform instead.', - ); + if (Object.freeze) { + Object.freeze(children); } } else { - validateChildKeys(children, type); + console.error( + 'React.jsx: Static children should always be an array. ' + + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + + 'Use the Babel transform instead.', + ); } + } else { + validateChildKeys(children, type); } } @@ -640,59 +587,17 @@ function jsxDEVImpl( */ export function createElement(type, config, children) { if (__DEV__) { - if (!enableOwnerStacks && !isValidElementType(type)) { - // This is just an optimistic check that provides a better stack trace before - // owner stacks. It's really up to the renderer if it's a valid element type. - // When owner stacks are enabled, we instead warn in the renderer and it'll - // have the stack trace of the JSX element anyway. - // - // This is an invalid element type. - // - // We warn in this case but don't throw. We expect the element creation to - // succeed and there will likely be errors in render. - let info = ''; - if ( - type === undefined || - (typeof type === 'object' && - type !== null && - Object.keys(type).length === 0) - ) { - info += - ' You likely forgot to export your component from the file ' + - "it's defined in, or you might have mixed up default and named imports."; - } + // We don't warn for invalid element type here because with owner stacks, + // we error in the renderer. The renderer is the only one that knows what + // types are valid for this particular renderer so we let it error there. - let typeString; - if (type === null) { - typeString = 'null'; - } else if (isArray(type)) { - typeString = 'array'; - } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) { - typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`; - info = - ' Did you accidentally export a JSX literal instead of a component?'; - } else { - typeString = typeof type; - } - - console.error( - 'React.createElement: type is invalid -- expected a string (for ' + - 'built-in components) or a class/function (for composite ' + - 'components) but got: %s.%s', - typeString, - info, - ); - } else { - // This is a valid element type. - - // Skip key warning if the type isn't valid since our key validation logic - // doesn't expect a non-string/function type and can throw confusing - // errors. We don't want exception behavior to differ between dev and - // prod. (Rendering will throw with a helpful message and as soon as the - // type is fixed, the key warnings will appear.) - for (let i = 2; i < arguments.length; i++) { - validateChildKeys(arguments[i], type); - } + // Skip key warning if the type isn't valid since our key validation logic + // doesn't expect a non-string/function type and can throw confusing + // errors. We don't want exception behavior to differ between dev and + // prod. (Rendering will throw with a helpful message and as soon as the + // type is fixed, the key warnings will appear.) + for (let i = 2; i < arguments.length; i++) { + validateChildKeys(arguments[i], type); } // Unlike the jsx() runtime, createElement() doesn't warn about key spread. @@ -795,8 +700,8 @@ export function createElement(type, config, children) { undefined, getOwner(), props, - __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined, - __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined, + __DEV__ && Error('react-stack-top-frame'), + __DEV__ && createTask(getTaskName(type)), ); } @@ -808,8 +713,8 @@ export function cloneAndReplaceKey(oldElement, newKey) { undefined, !__DEV__ ? undefined : oldElement._owner, oldElement.props, - __DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined, - __DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined, + __DEV__ && oldElement._debugStack, + __DEV__ && oldElement._debugTask, ); if (__DEV__) { // The cloned element should inherit the original element's key validation. @@ -914,8 +819,8 @@ export function cloneElement(element, config, children) { undefined, owner, props, - __DEV__ && enableOwnerStacks ? element._debugStack : undefined, - __DEV__ && enableOwnerStacks ? element._debugTask : undefined, + __DEV__ && element._debugStack, + __DEV__ && element._debugTask, ); for (let i = 2; i < arguments.length; i++) { @@ -936,51 +841,13 @@ export function cloneElement(element, config, children) { */ function validateChildKeys(node, parentType) { if (__DEV__) { - if (enableOwnerStacks) { - // When owner stacks is enabled no warnings happens. All we do is - // mark elements as being in a valid static child position so they - // don't need keys. - if (isValidElement(node)) { - if (node._store) { - node._store.validated = 1; - } - } - return; - } - if (typeof node !== 'object' || !node) { - return; - } - if (node.$$typeof === REACT_CLIENT_REFERENCE) { - // This is a reference to a client component so it's unknown. - } else if (isArray(node)) { - for (let i = 0; i < node.length; i++) { - const child = node[i]; - if (isValidElement(child)) { - validateExplicitKey(child, parentType); - } - } - } else if (isValidElement(node)) { - // This element was passed in a valid location. + // With owner stacks is, no warnings happens. All we do is + // mark elements as being in a valid static child position so they + // don't need keys. + if (isValidElement(node)) { if (node._store) { node._store.validated = 1; } - } else { - const iteratorFn = getIteratorFn(node); - if (typeof iteratorFn === 'function') { - // Entry iterators used to provide implicit keys, - // but now we print a separate warning for them later. - if (iteratorFn !== node.entries) { - const iterator = iteratorFn.call(node); - if (iterator !== node) { - let step; - while (!(step = iterator.next()).done) { - if (isValidElement(step.value)) { - validateExplicitKey(step.value, parentType); - } - } - } - } - } } } } @@ -999,92 +866,3 @@ export function isValidElement(object) { object.$$typeof === REACT_ELEMENT_TYPE ); } - -const ownerHasKeyUseWarning = {}; - -/** - * Warn if the element doesn't have an explicit key assigned to it. - * This element is in an array. The array could grow and shrink or be - * reordered. All children that haven't already been validated are required to - * have a "key" property assigned to it. Error statuses are cached so a warning - * will only be shown once. - * - * @internal - * @param {ReactElement} element Element that requires a key. - * @param {*} parentType element's parent's type. - */ -function validateExplicitKey(element, parentType) { - if (enableOwnerStacks) { - // Skip. Will verify in renderer instead. - return; - } - if (__DEV__) { - if (!element._store || element._store.validated || element.key != null) { - return; - } - element._store.validated = 1; - - const currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); - if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { - return; - } - ownerHasKeyUseWarning[currentComponentErrorInfo] = true; - - // Usually the current owner is the offender, but if it accepts children as a - // property, it may be the creator of the child that's responsible for - // assigning it a key. - let childOwner = ''; - if (element && element._owner != null && element._owner !== getOwner()) { - let ownerName = null; - if (typeof element._owner.tag === 'number') { - ownerName = getComponentNameFromType(element._owner.type); - } else if (typeof element._owner.name === 'string') { - ownerName = element._owner.name; - } - // Give the component that originally created this child. - childOwner = ` It was passed a child from ${ownerName}.`; - } - - const prevGetCurrentStack = ReactSharedInternals.getCurrentStack; - ReactSharedInternals.getCurrentStack = function () { - const owner = element._owner; - // Add an extra top frame while an element is being validated - let stack = describeUnknownElementTypeFrameInDEV( - element.type, - owner ? owner.type : null, - ); - // Delegate to the injected renderer-specific implementation - if (prevGetCurrentStack) { - stack += prevGetCurrentStack() || ''; - } - return stack; - }; - console.error( - 'Each child in a list should have a unique "key" prop.' + - '%s%s See https://react.dev/link/warning-keys for more information.', - currentComponentErrorInfo, - childOwner, - ); - ReactSharedInternals.getCurrentStack = prevGetCurrentStack; - } -} - -function getCurrentComponentErrorInfo(parentType) { - if (__DEV__) { - let info = ''; - const owner = getOwner(); - if (owner) { - const name = getComponentNameFromType(owner.type); - if (name) { - info = '\n\nCheck the render method of `' + name + '`.'; - } - } - if (!info) { - const parentName = getComponentNameFromType(parentType); - if (parentName) { - info = `\n\nCheck the top-level render call using <${parentName}>.`; - } - } - return info; - } -} diff --git a/packages/shared/ReactComponentInfoStack.js b/packages/shared/ReactComponentInfoStack.js index e4e545edaef76..efc3f6b39de32 100644 --- a/packages/shared/ReactComponentInfoStack.js +++ b/packages/shared/ReactComponentInfoStack.js @@ -11,14 +11,12 @@ import type {ReactComponentInfo} from 'shared/ReactTypes'; import {describeBuiltInComponentFrame} from 'shared/ReactComponentStackFrame'; -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; - import {formatOwnerStack} from 'shared/ReactOwnerStackFrames'; export function getOwnerStackByComponentInfoInDev( componentInfo: ReactComponentInfo, ): string { - if (!enableOwnerStacks || !__DEV__) { + if (!__DEV__) { return ''; } try { diff --git a/packages/shared/ReactComponentStackFrame.js b/packages/shared/ReactComponentStackFrame.js index a915fa48338ca..f8a27546b027a 100644 --- a/packages/shared/ReactComponentStackFrame.js +++ b/packages/shared/ReactComponentStackFrame.js @@ -7,25 +7,12 @@ * @flow */ -import type {LazyComponent} from 'react/src/ReactLazy'; - -import { - REACT_SUSPENSE_TYPE, - REACT_SUSPENSE_LIST_TYPE, - REACT_FORWARD_REF_TYPE, - REACT_MEMO_TYPE, - REACT_LAZY_TYPE, - REACT_VIEW_TRANSITION_TYPE, -} from 'shared/ReactSymbols'; - import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import DefaultPrepareStackTrace from 'shared/DefaultPrepareStackTrace'; -import {enableViewTransition} from 'shared/ReactFeatureFlags'; - let prefix; let suffix; export function describeBuiltInComponentFrame(name: string): string { @@ -300,53 +287,3 @@ export function describeClassComponentFrame(ctor: Function): string { export function describeFunctionComponentFrame(fn: Function): string { return describeNativeComponentFrame(fn, false); } - -function shouldConstruct(Component: Function) { - const prototype = Component.prototype; - return !!(prototype && prototype.isReactComponent); -} - -// TODO: Delete this once the key warning no longer uses it. I.e. when enableOwnerStacks ship. -export function describeUnknownElementTypeFrameInDEV(type: any): string { - if (!__DEV__) { - return ''; - } - if (type == null) { - return ''; - } - if (typeof type === 'function') { - return describeNativeComponentFrame(type, shouldConstruct(type)); - } - if (typeof type === 'string') { - return describeBuiltInComponentFrame(type); - } - switch (type) { - case REACT_SUSPENSE_TYPE: - return describeBuiltInComponentFrame('Suspense'); - case REACT_SUSPENSE_LIST_TYPE: - return describeBuiltInComponentFrame('SuspenseList'); - case REACT_VIEW_TRANSITION_TYPE: - if (enableViewTransition) { - return describeBuiltInComponentFrame('ViewTransition'); - } - } - if (typeof type === 'object') { - switch (type.$$typeof) { - case REACT_FORWARD_REF_TYPE: - return describeFunctionComponentFrame(type.render); - case REACT_MEMO_TYPE: - // Memo may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(type.type); - case REACT_LAZY_TYPE: { - const lazyComponent: LazyComponent<any, any> = (type: any); - const payload = lazyComponent._payload; - const init = lazyComponent._init; - try { - // Lazy may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(init(payload)); - } catch (x) {} - } - } - } - return ''; -} diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 93cd9a4a80edb..5588004bdf278 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -136,8 +136,6 @@ export const passChildrenWhenCloningPersistedNodes = false; */ export const enablePersistedModeClonedFlag = false; -export const enableOwnerStacks = true; - export const enableShallowPropDiffing = false; export const enableSiblingPrerendering = true; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 81f830cb6e3f4..f996274b86a18 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -32,9 +32,6 @@ export const { enableLazyPublicInstanceInFabric, } = dynamicFlags; -// These two can be removed -export const enableOwnerStacks = true; - // The rest of the flags are static for better dead code elimination. export const disableClientCache = true; export const disableCommentsAsDOMContainers = true; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 3a3874f6e2b22..9b878754736ce 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -41,7 +41,6 @@ export const enableLegacyFBSupport = false; export const enableLegacyHidden = false; export const enableNoCloningMemoCache = false; export const enableObjectFiber = false; -export const enableOwnerStacks = true; export const enablePersistedModeClonedFlag = false; export const enablePostpone = false; export const enableReactTestRendererWarning = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 3212b679c0279..f5deddae739be 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -91,7 +91,6 @@ export const enableReactTestRendererWarning = true; export const disableDefaultPropsExceptForClasses = true; export const enableObjectFiber = false; -export const enableOwnerStacks = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index f024f9ef0f972..4df92a5c90ef8 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -35,7 +35,6 @@ export const enableLegacyFBSupport = false; export const enableLegacyHidden = false; export const enableNoCloningMemoCache = false; export const enableObjectFiber = false; -export const enableOwnerStacks = true; export const enablePersistedModeClonedFlag = false; export const enablePostpone = false; export const enableProfilerCommitHooks = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 30ab2da252a57..b15f54484b029 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -71,7 +71,6 @@ export const disableDefaultPropsExceptForClasses = true; export const renameElementSymbol = false; export const enableObjectFiber = false; -export const enableOwnerStacks = true; export const enableShallowPropDiffing = false; export const enableSiblingPrerendering = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 126b9ee373bd2..185baf4626fa7 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -43,9 +43,6 @@ export const { // On WWW, __EXPERIMENTAL__ is used for a new modern build. // It's not used anywhere in production yet. -// Can remove these two -export const enableOwnerStacks = true; - export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__; diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js index cea90aebb14f6..008d2dc49aa49 100644 --- a/packages/shared/isValidElementType.js +++ b/packages/shared/isValidElementType.js @@ -36,7 +36,7 @@ import { const REACT_CLIENT_REFERENCE: symbol = Symbol.for('react.client.reference'); // This function is deprecated. Don't use. Only the renderer knows what a valid type is. -// TODO: Delete this when enableOwnerStacks ships. +// TODO: Delete this now that owner stacks shipped. export default function isValidElementType(type: mixed): boolean { if (typeof type === 'string' || typeof type === 'function') { return true; diff --git a/scripts/jest/setupTests.www.js b/scripts/jest/setupTests.www.js index 9ac7ffc6db002..b0b653bb78ff9 100644 --- a/scripts/jest/setupTests.www.js +++ b/scripts/jest/setupTests.www.js @@ -11,7 +11,6 @@ jest.mock('shared/ReactFeatureFlags', () => { // Flags that aren't currently used, but we still want to force variants to keep the // code live. actual.disableInputAttributeSyncing = __VARIANT__; - actual.enableOwnerStacks = __VARIANT__; // These are hardcoded to true for the next release, // but still run the tests against both variants until diff --git a/scripts/jest/setupTests.xplat.js b/scripts/jest/setupTests.xplat.js index 87fbf8bef8ac5..859a5065988d6 100644 --- a/scripts/jest/setupTests.xplat.js +++ b/scripts/jest/setupTests.xplat.js @@ -11,8 +11,6 @@ jest.mock('shared/ReactFeatureFlags', () => { 'shared/forks/ReactFeatureFlags.native-fb.js' ); - actual.enableOwnerStacks = __VARIANT__; - // Lots of tests use these, but we don't want to expose it to RN. // Ideally, tests for xplat wouldn't use react-dom, but many of our tests do. // Since the xplat tests run with the www entry points, some of these flags From e9252bcdccf7f8f691081e4d48ca47657bc723f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Tue, 4 Mar 2025 20:10:08 -0500 Subject: [PATCH 082/300] During a Swipe Gesture Render a Clone Offscreen and Animate it Onscreen (#32500) This is really the essence mechanism of the `useSwipeTransition` feature. We don't want to immediately switch to the destination state when starting a gesture. The effects remain mounted on the current state. We want the current state to be "live". This is important to for example allow a video to keeping playing while starting a swipe (think TikTok/Reels) and not stop until you've committed the action. The only thing that can be live is the "new" state. Therefore we treat the destination as the "old" state and perform a reverse animation from there. Ideally we could apply the old state to the DOM tree, take a snapshot and then revert it back in the mutation of `startViewTransition`. Unfortunately, the way `startViewTransition` was designed it always paints one frame of the "old" state which would lead this to cause a flicker. To work around this, we need to create a clone of any View Transition boundary that might be mutated and then render that offscreen. That way we can render the "current" state on screen and the "destination" state offscreen for the screenshots. Being mutated can be either due to React doing a DOM mutation or if a child boundary resizes that causes the parent to relayout. We don't have to do this for insertions or deletions since they only appear on one side. The worst case scenario is that we have to clone the whole root. That's what this first PR implements. We clone the container and if it's not absolutely positioned, we position it on top of the current one. If the container is `document` or `<html>` we instead clone the `<body>` tag since it's the only one we can insert a duplicate of. If the container is deep in the tree we clone just that even though technically we should probably clone the whole document in that case. We just keep the impact smaller. Ideally though we'd never hit this case. In fact, if we clone the document we issue a warning (always for now) since you probably should optimize this. In the future I intend to add optimizations when affected View Transition boundaries are absolutely positioned since they cannot possibly relayout the parent. This would be the ideal way to use this feature most efficiently but it still works without it. Since we render the "old" state outside the viewport, we need to then adjust the animation to put it back into the viewport. This is the trickiest part to get right while still preserving any customization of the View Transitions done using CSS. This current approach reapplies all the animations with adjusted keyframes. In the case of an "exit" the pseudo-element itself is positioned outside the viewport but since we can't programmatically update the style of the pseudo-element itself we instead adjust all the keyframes to put it back into the viewport. If there is no animation on the group we add one. In the case of an "update" the pseudo-element is positioned on the new state which is already inside the viewport. However, the auto-generated animation of the group has a starting keyframe that starts outside the viewport. In this case we need to adjust that keyframe. In the future I might explore a technique that inserts stylesheets instead of mutating the animations. It might be simpler. But whatever hacks work to maximize the compatibility is best. --- .../view-transition/src/components/Page.js | 11 +- packages/react-art/src/ReactFiberConfigART.js | 16 + .../src/client/ReactFiberConfigDOM.js | 496 +++++++++++++++++- .../src/ReactFiberConfigNative.js | 29 + .../src/createReactNoop.js | 22 + .../src/ReactFiberApplyGesture.js | 348 +++++++++++- .../src/ReactFiberCommitWork.js | 3 - .../src/ReactFiberConfigWithNoMutation.js | 4 + .../react-reconciler/src/ReactFiberRoot.js | 1 + .../src/ReactFiberWorkLoop.js | 8 + .../src/ReactInternalTypes.js | 2 + .../src/forks/ReactFiberConfig.custom.js | 6 + .../src/ReactFiberConfigTestHost.js | 49 ++ scripts/error-codes/codes.json | 3 +- 14 files changed, 981 insertions(+), 17 deletions(-) diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index d12ab56203807..d7acc93c0cb19 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -2,6 +2,8 @@ import React, { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity, unstable_useSwipeTransition as useSwipeTransition, + useEffect, + useState, } from 'react'; import SwipeRecognizer from './SwipeRecognizer'; @@ -53,6 +55,13 @@ export default function Page({url, navigate}) { navigate(show ? '/?a' : '/?b'); } + const [counter, setCounter] = useState(0); + + useEffect(() => { + const timer = setInterval(() => setCounter(c => c + 1), 1000); + return () => clearInterval(timer); + }, []); + const exclamation = ( <ViewTransition name="exclamation" onShare={onTransition}> <span>!</span> @@ -76,7 +85,7 @@ export default function Page({url, navigate}) { 'navigation-back': transitions['slide-right'], 'navigation-forward': transitions['slide-left'], }}> - <h1>{!show ? 'A' : 'B'}</h1> + <h1>{!show ? 'A' + counter : 'B' + counter}</h1> </ViewTransition> {show ? ( <div> diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index dc746b0e17d88..1ca506d022f4e 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -302,6 +302,10 @@ export function createInstance(type, props, internalInstanceHandle) { return instance; } +export function cloneMutableInstance(instance, keepChildren) { + return instance; +} + export function createTextInstance( text, rootContainerInstance, @@ -310,6 +314,10 @@ export function createTextInstance( return text; } +export function cloneMutableTextInstance(textInstance) { + return textInstance; +} + export function finalizeInitialChildren(domElement, type, props) { return false; } @@ -475,6 +483,14 @@ export function restoreRootViewTransitionName(rootContainer) { // Noop } +export function cloneRootViewTransitionContainer(rootContainer) { + throw new Error('Not implemented.'); +} + +export function removeRootViewTransitionClone(rootContainer, clone) { + throw new Error('Not implemented.'); +} + export type InstanceMeasurement = null; export function measureInstance(instance) { diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 2b288efe46c3f..ba79b08c47f7c 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -544,6 +544,13 @@ export function createInstance( return domElement; } +export function cloneMutableInstance( + instance: Instance, + keepChildren: boolean, +): Instance { + return instance.cloneNode(keepChildren); +} + export function appendInitialChild( parentInstance: Instance, child: Instance | TextInstance, @@ -609,6 +616,12 @@ export function createTextInstance( return textNode; } +export function cloneMutableTextInstance( + textInstance: TextInstance, +): TextInstance { + return textInstance.cloneNode(false); +} + let currentPopstateTransitionEvent: Event | null = null; export function shouldAttemptEagerTransition(): boolean { const event = window.event; @@ -1207,6 +1220,113 @@ export function cancelRootViewTransitionName(rootContainer: Container): void { } export function restoreRootViewTransitionName(rootContainer: Container): void { + let containerInstance: Instance; + if (rootContainer.nodeType === DOCUMENT_NODE) { + containerInstance = (rootContainer: any).body; + } else if (rootContainer.nodeName === 'HTML') { + containerInstance = (rootContainer.ownerDocument.body: any); + } else { + // If the container is not the whole document, then we ideally should probably + // clone the whole document outside of the React too. + containerInstance = (rootContainer: any); + } + // $FlowFixMe[prop-missing] + if (containerInstance.style.viewTransitionName === 'root') { + // If we moved the root view transition name to the container in a gesture + // we need to restore it now. + containerInstance.style.viewTransitionName = ''; + } + const documentElement: null | HTMLElement = + containerInstance.ownerDocument.documentElement; + if ( + documentElement !== null && + // $FlowFixMe[prop-missing] + documentElement.style.viewTransitionName === 'none' + ) { + // $FlowFixMe[prop-missing] + documentElement.style.viewTransitionName = ''; + } +} + +function getComputedTransform(style: CSSStyleDeclaration): string { + // Gets the merged transform of all the short hands. + const computedStyle: any = style; + let transform: string = computedStyle.transform; + if (transform === 'none') { + transform = ''; + } + const scale: string = computedStyle.scale; + if (scale !== 'none' && scale !== '') { + const parts = scale.split(' '); + transform = + (parts.length === 3 ? 'scale3d' : 'scale') + + '(' + + parts.join(', ') + + ') ' + + transform; + } + const rotate: string = computedStyle.rotate; + if (rotate !== 'none' && rotate !== '') { + const parts = rotate.split(' '); + if (parts.length === 1) { + transform = 'rotate(' + parts[0] + ') ' + transform; + } else if (parts.length === 2) { + transform = + 'rotate' + parts[0].toUpperCase() + '(' + parts[1] + ') ' + transform; + } else { + transform = 'rotate3d(' + parts.join(', ') + ') ' + transform; + } + } + const translate: string = computedStyle.translate; + if (translate !== 'none' && translate !== '') { + const parts = translate.split(' '); + transform = + (parts.length === 3 ? 'translate3d' : 'translate') + + '(' + + parts.join(', ') + + ') ' + + transform; + } + return transform; +} + +function moveOutOfViewport( + originalStyle: CSSStyleDeclaration, + element: HTMLElement, +): void { + // Apply a transform that safely puts the whole element outside the viewport + // while still letting it paint its "old" state to a snapshot. + const transform = getComputedTransform(originalStyle); + // Clear the long form properties. + // $FlowFixMe + element.style.translate = 'none'; + // $FlowFixMe + element.style.scale = 'none'; + // $FlowFixMe + element.style.rotate = 'none'; + // Apply a translate to move it way out of the viewport. This is applied first + // so that it is in the coordinate space of the parent and not after applying + // other transforms. That's why we need to merge the long form properties. + // TODO: Ideally we'd adjust for the parent's rotate/scale. Otherwise when + // we move back the ::view-transition-group we might overshoot or undershoot. + element.style.transform = 'translate(-20000px, -20000px) ' + transform; +} + +function moveOldFrameIntoViewport(keyframe: any): void { + // In the resulting View Transition Animation, the first frame will be offset. + const computedTransform: ?string = keyframe.transform; + if (computedTransform != null) { + let transform = computedTransform === 'none' ? '' : computedTransform; + transform = 'translate(20000px, 20000px) ' + transform; + keyframe.transform = transform; + } +} + +export function cloneRootViewTransitionContainer( + rootContainer: Container, +): Instance { + // This implies that we're not going to animate the root document but instead + // the clone so we first clear the name of the root container. const documentElement: null | HTMLElement = rootContainer.nodeType === DOCUMENT_NODE ? (rootContainer: any).documentElement @@ -1214,11 +1334,138 @@ export function restoreRootViewTransitionName(rootContainer: Container): void { if ( documentElement !== null && // $FlowFixMe[prop-missing] - documentElement.style.viewTransitionName === 'none' + documentElement.style.viewTransitionName === '' ) { // $FlowFixMe[prop-missing] - documentElement.style.viewTransitionName = ''; + documentElement.style.viewTransitionName = 'none'; + } + + let containerInstance: HTMLElement; + if (rootContainer.nodeType === DOCUMENT_NODE) { + containerInstance = (rootContainer: any).body; + } else if (rootContainer.nodeName === 'HTML') { + containerInstance = (rootContainer.ownerDocument.body: any); + } else { + // If the container is not the whole document, then we ideally should probably + // clone the whole document outside of the React too. + containerInstance = (rootContainer: any); + } + + const containerParent = containerInstance.parentNode; + if (containerParent === null) { + throw new Error('Cannot use a useSwipeTransition() in a detached root.'); } + + const clone: HTMLElement = containerInstance.cloneNode(false); + + const computedStyle = getComputedStyle(containerInstance); + + if ( + computedStyle.position === 'absolute' || + computedStyle.position === 'fixed' + ) { + // If the style is already absolute, we don't have to do anything because it'll appear + // in the same place. + } else { + // Otherwise we need to absolutely position the clone in the same location as the original. + let positionedAncestor: HTMLElement = containerParent; + while ( + positionedAncestor.parentNode != null && + positionedAncestor.parentNode.nodeType !== DOCUMENT_NODE + ) { + if (getComputedStyle(positionedAncestor).position !== 'static') { + break; + } + // $FlowFixMe: This is refined. + positionedAncestor = positionedAncestor.parentNode; + } + + const positionedAncestorStyle: any = positionedAncestor.style; + const containerInstanceStyle: any = containerInstance.style; + // Clear the transform while we're measuring since it affects the bounding client rect. + const prevAncestorTranslate = positionedAncestorStyle.translate; + const prevAncestorScale = positionedAncestorStyle.scale; + const prevAncestorRotate = positionedAncestorStyle.rotate; + const prevAncestorTransform = positionedAncestorStyle.transform; + const prevTranslate = containerInstanceStyle.translate; + const prevScale = containerInstanceStyle.scale; + const prevRotate = containerInstanceStyle.rotate; + const prevTransform = containerInstanceStyle.transform; + positionedAncestorStyle.translate = 'none'; + positionedAncestorStyle.scale = 'none'; + positionedAncestorStyle.rotate = 'none'; + positionedAncestorStyle.transform = 'none'; + containerInstanceStyle.translate = 'none'; + containerInstanceStyle.scale = 'none'; + containerInstanceStyle.rotate = 'none'; + containerInstanceStyle.transform = 'none'; + + const ancestorRect = positionedAncestor.getBoundingClientRect(); + const rect = containerInstance.getBoundingClientRect(); + + const cloneStyle = clone.style; + cloneStyle.position = 'absolute'; + cloneStyle.top = rect.top - ancestorRect.top + 'px'; + cloneStyle.left = rect.left - ancestorRect.left + 'px'; + cloneStyle.width = rect.width + 'px'; + cloneStyle.height = rect.height + 'px'; + cloneStyle.margin = '0px'; + cloneStyle.boxSizing = 'border-box'; + + positionedAncestorStyle.translate = prevAncestorTranslate; + positionedAncestorStyle.scale = prevAncestorScale; + positionedAncestorStyle.rotate = prevAncestorRotate; + positionedAncestorStyle.transform = prevAncestorTransform; + containerInstanceStyle.translate = prevTranslate; + containerInstanceStyle.scale = prevScale; + containerInstanceStyle.rotate = prevRotate; + containerInstanceStyle.transform = prevTransform; + } + + // For this transition the container will act as the root. Nothing outside of it should + // be affected anyway. This lets us transition from the cloned container to the original. + // $FlowFixMe[prop-missing] + clone.style.viewTransitionName = 'root'; + + // Move out of the viewport so that it's still painted for the snapshot but is not visible + // for the frame where the snapshot happens. + moveOutOfViewport(computedStyle, clone); + + // Insert the clone after the root container as a sibling. This may inject a body + // as the next sibling of an existing body. document.body will still point to the + // first one and any id selectors will still find the first one. That's why it's + // important that it's after the existing node. + containerInstance.parentNode.insertBefore( + clone, + containerInstance.nextSibling, + ); + + return clone; +} + +export function removeRootViewTransitionClone( + rootContainer: Container, + clone: Instance, +): void { + let containerInstance: Instance; + if (rootContainer.nodeType === DOCUMENT_NODE) { + containerInstance = (rootContainer: any).body; + } else if (rootContainer.nodeName === 'HTML') { + containerInstance = (rootContainer.ownerDocument.body: any); + } else { + // If the container is not the whole document, then we ideally should probably + // clone the whole document outside of the React too. + containerInstance = (rootContainer: any); + } + const containerParent = containerInstance.parentNode; + if (containerParent === null) { + throw new Error('Cannot use a useSwipeTransition() in a detached root.'); + } + // We assume that the clone is still within the same parent. + containerParent.removeChild(clone); + + // Now the root is on the containerInstance itself until we call restoreRootViewTransitionName. + containerInstance.style.viewTransitionName = 'root'; } export type InstanceMeasurement = { @@ -1417,8 +1664,127 @@ export type RunningGestureTransition = { ... }; +function mergeTranslate(translateA: ?string, translateB: ?string): string { + if (!translateA || translateA === 'none') { + return translateB || ''; + } + if (!translateB || translateB === 'none') { + return translateA || ''; + } + const partsA = translateA.split(' '); + const partsB = translateB.split(' '); + let i; + let result = ''; + for (i = 0; i < partsA.length && i < partsB.length; i++) { + if (i > 0) { + result += ' '; + } + result += 'calc(' + partsA[i] + ' + ' + partsB[i] + ')'; + } + for (; i < partsA.length; i++) { + result += ' ' + partsA[i]; + } + for (; i < partsB.length; i++) { + result += ' ' + partsB[i]; + } + return result; +} + +function animateGesture( + keyframes: any, + targetElement: Element, + pseudoElement: string, + timeline: AnimationTimeline, + rangeStart: number, + rangeEnd: number, + moveFirstFrameIntoViewport: boolean, + moveAllFramesIntoViewport: boolean, +) { + for (let i = 0; i < keyframes.length; i++) { + const keyframe = keyframes[i]; + // Delete any easing since we always apply linear easing to gestures. + delete keyframe.easing; + delete keyframe.computedOffset; + // Chrome returns "auto" for width/height which is not a valid value to + // animate to. Similarly, transform: "none" is actually lack of transform. + if (keyframe.width === 'auto') { + delete keyframe.width; + } + if (keyframe.height === 'auto') { + delete keyframe.height; + } + if (keyframe.transform === 'none') { + delete keyframe.transform; + } + if (moveAllFramesIntoViewport) { + if (keyframe.transform == null) { + // If a transform is not explicitly specified to override the auto + // generated one on the pseudo element, then we need to adjust it to + // put it back into the viewport. We don't know the offset relative to + // the screen so instead we use the translate prop to do a relative + // adjustment. + // TODO: If the "transform" was manually overridden on the pseudo + // element itself and no longer the auto generated one, then we shouldn't + // adjust it. I'm not sure how to detect this. + if (keyframe.translate == null || keyframe.translate === '') { + // TODO: If there's a CSS rule targeting translate on the pseudo element + // already we need to merge it. + const elementTranslate: ?string = (getComputedStyle( + targetElement, + pseudoElement, + ): any).translate; + keyframe.translate = mergeTranslate( + elementTranslate, + '20000px 20000px', + ); + } else { + keyframe.translate = mergeTranslate( + keyframe.translate, + '20000px 20000px', + ); + } + } + } + } + if (moveFirstFrameIntoViewport) { + // If this is the generated animation that does a FLIP matrix translation + // from the old position, we need to adjust it from the out of viewport + // position. If this is going from old to new it only applies to first + // keyframe. Otherwise it applies to every keyframe. + moveOldFrameIntoViewport(keyframes[0]); + } + const reverse = rangeStart > rangeEnd; + const anim = targetElement.animate(keyframes, { + pseudoElement: pseudoElement, + // Set the timeline to the current gesture timeline to drive the updates. + timeline: timeline, + // We reset all easing functions to linear so that it feels like you + // have direct impact on the transition and to avoid double bouncing + // from scroll bouncing. + easing: 'linear', + // We fill in both direction for overscroll. + fill: 'both', + // Range start needs to be higher than range end. If it goes in reverse + // we reverse the whole animation below. + rangeStart: (reverse ? rangeEnd : rangeStart) + '%', + rangeEnd: (reverse ? rangeStart : rangeEnd) + '%', + }); + if (!reverse) { + // We play all gestures in reverse, except if we're in reverse direction + // in which case we need to play it in reverse of the reverse. + anim.reverse(); + // In Safari, there's a bug where the starting position isn't immediately + // picked up from the ScrollTimeline for one frame. + // $FlowFixMe[cannot-resolve-name] + anim.currentTime = CSS.percent(100); + } +} + export function startGestureTransition( rootContainer: Container, + timeline: GestureTimeline, + rangeStart: number, + rangeEnd: number, transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, @@ -1435,26 +1801,136 @@ export function startGestureTransition( }); // $FlowFixMe[prop-missing] ownerDocument.__reactViewTransition = transition; - let blockingAnim = null; - const readyCallback = () => { + const readyCallback = (x: any) => { + const documentElement: Element = (ownerDocument.documentElement: any); + // Loop through all View Transition Animations. + const animations = documentElement.getAnimations({subtree: true}); + // First do a pass to collect all known group and new items so we can look + // up if they exist later. + const foundGroups: Set<string> = new Set(); + const foundNews: Set<string> = new Set(); + for (let i = 0; i < animations.length; i++) { + // $FlowFixMe + const pseudoElement: ?string = animations[i].effect.pseudoElement; + if (pseudoElement == null) { + } else if (pseudoElement.startsWith('::view-transition-group')) { + foundGroups.add(pseudoElement.slice(23)); + } else if (pseudoElement.startsWith('::view-transition-new')) { + // TODO: This is not really a sufficient detection because if the new + // pseudo element might exist but have animations disabled on it. + foundNews.add(pseudoElement.slice(21)); + } + } + for (let i = 0; i < animations.length; i++) { + const anim = animations[i]; + const effect: KeyframeEffect = (anim.effect: any); + // $FlowFixMe + const pseudoElement: ?string = effect.pseudoElement; + if ( + pseudoElement != null && + pseudoElement.startsWith('::view-transition') + ) { + // Ideally we could mutate the existing animation but unfortunately + // the mutable APIs seem less tested and therefore are lacking or buggy. + // Therefore we create a new animation instead. + anim.cancel(); + let isGeneratedGroupAnim = false; + let isExitGroupAnim = false; + if (pseudoElement.startsWith('::view-transition-group')) { + const groupName = pseudoElement.slice(23); + if (foundNews.has(groupName)) { + // If this has both "new" and "old" state we expect this to be an auto-generated + // animation that started outside the viewport. We need to adjust this first frame + // to be inside the viewport. + // $FlowFixMe[prop-missing] + const animationName: ?string = anim.animationName; + isGeneratedGroupAnim = + animationName != null && + // $FlowFixMe[prop-missing] + animationName.startsWith('-ua-view-transition-group-anim-'); + } else { + // If this has only an "old" state then the pseudo element will be outside + // the viewport. If any keyframes don't override "transform" we need to + // adjust them. + isExitGroupAnim = true; + } + // TODO: If this has only an old state and no new state, + } + animateGesture( + effect.getKeyframes(), + // $FlowFixMe: Always documentElement atm. + effect.target, + pseudoElement, + timeline, + rangeStart, + rangeEnd, + isGeneratedGroupAnim, + isExitGroupAnim, + ); + if (pseudoElement.startsWith('::view-transition-old')) { + const groupName = pseudoElement.slice(21); + if (!foundGroups.has(groupName) && !foundNews.has(groupName)) { + foundGroups.add(groupName); + // We haven't seen any group animation with this name. Since the old + // state was outside the viewport we need to put it back. Since we + // can't programmatically target the element itself, we use an + // animation to adjust it. + // This usually happens for exit animations where the element has + // the old position. + // If we also have a "new" state then we skip this because it means + // someone manually disabled the auto-generated animation. We need to + // treat the old state as having the position of the "new" state which + // will happen by default. + const pseudoElementName = '::view-transition-group' + groupName; + animateGesture( + [{}, {}], + // $FlowFixMe: Always documentElement atm. + effect.target, + pseudoElementName, + timeline, + rangeStart, + rangeEnd, + false, + true, // We let the helper apply the translate + ); + } + } + } + } // View Transitions with ScrollTimeline has a quirk where they end if the // ScrollTimeline ever reaches 100% but that doesn't mean we're done because // you can swipe back again. We can prevent this by adding a paused Animation // that never stops. This seems to keep all running Animations alive until // we explicitly abort (or something forces the View Transition to cancel). - const documentElement: Element = (ownerDocument.documentElement: any); - blockingAnim = documentElement.animate([{}, {}], { + const blockingAnim = documentElement.animate([{}, {}], { pseudoElement: '::view-transition', duration: 1, }); blockingAnim.pause(); animateCallback(); }; - transition.ready.then(readyCallback, readyCallback); + // In Chrome, "new" animations are not ready in the ready callback. We have to wait + // until requestAnimationFrame before we can observe them through getAnimations(). + // However, in Safari, that would cause a flicker because we're applying them late. + // TODO: Think of a feature detection for this instead. + const readyForAnimations = + navigator.userAgent.indexOf('Chrome') !== -1 + ? () => requestAnimationFrame(readyCallback) + : readyCallback; + transition.ready.then(readyForAnimations, readyCallback); transition.finished.then(() => { - if (blockingAnim !== null) { - // In Safari, we need to manually clear this or it'll block future transitions. - blockingAnim.cancel(); + // In Safari, we need to manually cancel all manually start animations + // or it'll block future transitions. + const documentElement: Element = (ownerDocument.documentElement: any); + const animations = documentElement.getAnimations({subtree: true}); + for (let i = 0; i < animations.length; i++) { + const anim = animations[i]; + const effect: KeyframeEffect = (anim.effect: any); + // $FlowFixMe + const pseudo: ?string = effect.pseudoElement; + if (pseudo != null && pseudo.startsWith('::view-transition')) { + anim.cancel(); + } } // $FlowFixMe[prop-missing] if (ownerDocument.__reactViewTransition === transition) { diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 4a9064c82a83b..5f1b5583a18d9 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -165,6 +165,13 @@ export function createInstance( return ((component: any): Instance); } +export function cloneMutableInstance( + instance: Instance, + keepChildren: boolean, +): Instance { + throw new Error('Not yet implemented.'); +} + export function createTextInstance( text: string, rootContainerInstance: Container, @@ -189,6 +196,12 @@ export function createTextInstance( return tag; } +export function cloneMutableTextInstance( + textInstance: TextInstance, +): TextInstance { + throw new Error('Not yet implemented.'); +} + export function finalizeInitialChildren( parentInstance: Instance, type: string, @@ -558,6 +571,19 @@ export function restoreRootViewTransitionName(rootContainer: Container): void { // Not yet implemented } +export function cloneRootViewTransitionContainer( + rootContainer: Container, +): Instance { + throw new Error('Not implemented.'); +} + +export function removeRootViewTransitionClone( + rootContainer: Container, + clone: Instance, +): void { + throw new Error('Not implemented.'); +} + export type InstanceMeasurement = null; export function measureInstance(instance: Instance): InstanceMeasurement { @@ -601,6 +627,9 @@ export type RunningGestureTransition = null; export function startGestureTransition( rootContainer: Container, + timeline: GestureTimeline, + rangeStart: number, + rangeEnd: number, transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index e392ad7b0c04f..4978dccf2a03c 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -453,6 +453,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return inst; }, + cloneMutableInstance(instance: Instance, keepChildren: boolean): Instance { + throw new Error('Not yet implemented.'); + }, + appendInitialChild( parentInstance: Instance, child: Instance | TextInstance, @@ -504,6 +508,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return inst; }, + cloneMutableTextInstance(textInstance: TextInstance): TextInstance { + throw new Error('Not yet implemented.'); + }, + scheduleTimeout: setTimeout, cancelTimeout: clearTimeout, noTimeout: -1, @@ -761,6 +769,17 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { restoreRootViewTransitionName(rootContainer: Container): void {}, + cloneRootViewTransitionContainer(rootContainer: Container): Instance { + throw new Error('Not yet implemented.'); + }, + + removeRootViewTransitionClone( + rootContainer: Container, + clone: Instance, + ): void { + throw new Error('Not implemented.'); + }, + measureInstance(instance: Instance): InstanceMeasurement { return null; }, @@ -796,6 +815,9 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { startGestureTransition( rootContainer: Container, + timeline: GestureTimeline, + rangeStart: number, + rangeEnd: number, transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js index 46af2ca4309fa..6847a2426c5ac 100644 --- a/packages/react-reconciler/src/ReactFiberApplyGesture.js +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -9,10 +9,327 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes'; +import type {Instance, TextInstance} from './ReactFiberConfig'; + +import type {OffscreenState} from './ReactFiberActivityComponent'; + import { + cloneMutableInstance, + cloneMutableTextInstance, + cloneRootViewTransitionContainer, + removeRootViewTransitionClone, cancelRootViewTransitionName, restoreRootViewTransitionName, + appendChild, + commitUpdate, + commitTextUpdate, + resetTextContent, + supportsResources, + supportsSingletons, + unhideInstance, + unhideTextInstance, } from './ReactFiberConfig'; +import { + popMutationContext, + pushMutationContext, + viewTransitionMutationContext, +} from './ReactFiberMutationTracking'; +import { + MutationMask, + Update, + ContentReset, + NoFlags, + Visibility, +} from './ReactFiberFlags'; +import { + HostComponent, + HostHoistable, + HostSingleton, + HostText, + HostPortal, + OffscreenComponent, + ViewTransitionComponent, +} from './ReactWorkTags'; + +let didWarnForRootClone = false; + +function detectMutationOrInsertClones(finishedWork: Fiber): boolean { + return true; +} + +let unhideHostChildren = false; + +function recursivelyInsertClonesFromExistingTree( + parentFiber: Fiber, + hostParentClone: Instance, +): void { + let child = parentFiber.child; + while (child !== null) { + switch (child.tag) { + case HostComponent: { + const instance: Instance = child.stateNode; + // If we have no mutations in this subtree, we just need to make a deep clone. + const clone: Instance = cloneMutableInstance(instance, true); + appendChild(hostParentClone, clone); + // TODO: We may need to transfer some DOM state such as scroll position + // for the deep clones. + // TODO: If there's a manual view-transition-name inside the clone we + // should ideally remove it from the original and then restore it in mutation + // phase. Otherwise it leads to duplicate names. + if (unhideHostChildren) { + unhideInstance(clone, child.memoizedProps); + } + break; + } + case HostText: { + const textInstance: TextInstance = child.stateNode; + if (textInstance === null) { + throw new Error( + 'This should have a text node initialized. This error is likely ' + + 'caused by a bug in React. Please file an issue.', + ); + } + const clone = cloneMutableTextInstance(textInstance); + appendChild(hostParentClone, clone); + if (unhideHostChildren) { + unhideTextInstance(clone, child.memoizedProps); + } + break; + } + case HostPortal: { + // TODO: Consider what should happen to Portals. For now we exclude them. + break; + } + case OffscreenComponent: { + const newState: OffscreenState | null = child.memoizedState; + const isHidden = newState !== null; + if (!isHidden) { + // Only insert clones if this tree is going to be visible. No need to + // clone invisible content. + // TODO: If this is visible but detached it should still be cloned. + // Since there was no mutation to this node, it couldn't have changed + // visibility so we don't need to update unhideHostChildren here. + recursivelyInsertClonesFromExistingTree(child, hostParentClone); + } + break; + } + case ViewTransitionComponent: + const prevMutationContext = pushMutationContext(); + // TODO: If this was already cloned by a previous pass we can reuse those clones. + recursivelyInsertClonesFromExistingTree(child, hostParentClone); + // TODO: Do we need to track whether this should have a name applied? + // child.flags |= Update; + popMutationContext(prevMutationContext); + break; + default: { + recursivelyInsertClonesFromExistingTree(child, hostParentClone); + break; + } + } + child = child.sibling; + } +} + +function recursivelyInsertClones( + parentFiber: Fiber, + hostParentClone: Instance, +) { + const deletions = parentFiber.deletions; + if (deletions !== null) { + for (let i = 0; i < deletions.length; i++) { + // const childToDelete = deletions[i]; + // TODO + } + } + + if ( + parentFiber.alternate === null || + (parentFiber.subtreeFlags & MutationMask) !== NoFlags + ) { + // If we have mutations or if this is a newly inserted tree, clone as we go. + let child = parentFiber.child; + while (child !== null) { + insertDestinationClonesOfFiber(child, hostParentClone); + child = child.sibling; + } + } else { + // Once we reach a subtree with no more mutations we can bail out. + // However, we must still insert deep clones of the HostComponents. + recursivelyInsertClonesFromExistingTree(parentFiber, hostParentClone); + } +} + +function insertDestinationClonesOfFiber( + finishedWork: Fiber, + hostParentClone: Instance, +) { + const current = finishedWork.alternate; + const flags = finishedWork.flags; + // The effect flag should be checked *after* we refine the type of fiber, + // because the fiber tag is more specific. An exception is any flag related + // to reconciliation, because those can be set on all fiber types. + switch (finishedWork.tag) { + case HostHoistable: { + if (supportsResources) { + // TODO: Hoistables should get optimistically inserted and then removed. + recursivelyInsertClones(finishedWork, hostParentClone); + break; + } + // Fall through + } + case HostSingleton: { + if (supportsSingletons) { + recursivelyInsertClones(finishedWork, hostParentClone); + if (__DEV__) { + // We cannot apply mutations to Host Singletons since by definition + // they cannot be cloned. Therefore we warn in DEV if this commit + // had any effect. + if (flags & Update) { + if (current === null) { + console.error( + 'useSwipeTransition() caused something to render a new <%s>. ' + + 'This is not possible in the current implementation. ' + + "Make sure that the swipe doesn't mount any new <%s> elements.", + finishedWork.type, + finishedWork.type, + ); + } else { + const newProps = finishedWork.memoizedProps; + const oldProps = current.memoizedProps; + const instance = finishedWork.stateNode; + const type = finishedWork.type; + const prev = pushMutationContext(); + + try { + // Since we currently don't have a separate diffing algorithm for + // individual properties, the Update flag can be a false positive. + // We have to apply the new props first o detect any mutations and + // then revert them. + commitUpdate(instance, type, oldProps, newProps, finishedWork); + if (viewTransitionMutationContext) { + console.error( + 'useSwipeTransition() caused something to mutate <%s>. ' + + 'This is not possible in the current implementation. ' + + "Make sure that the swipe doesn't update any state which " + + 'causes <%s> to change.', + finishedWork.type, + finishedWork.type, + ); + } + // Revert + commitUpdate(instance, type, newProps, oldProps, finishedWork); + } finally { + popMutationContext(prev); + } + } + } + } + break; + } + // Fall through + } + case HostComponent: { + const instance: Instance = finishedWork.stateNode; + if (current === null) { + // For insertions we don't need to clone. It's already new state node. + // TODO: Do we need to visit it for ViewTransitions though? + appendChild(hostParentClone, instance); + } else { + let clone: Instance; + if (finishedWork.child === null) { + // This node is terminal. We still do a deep clone in case this has user + // inserted content, text content or dangerouslySetInnerHTML. + clone = cloneMutableInstance(instance, true); + if (finishedWork.flags & ContentReset) { + resetTextContent(clone); + } + } else { + // If we have children we'll clone them as we walk the tree so we just + // do a shallow clone here. + clone = cloneMutableInstance(instance, false); + } + + if (flags & Update) { + const newProps = finishedWork.memoizedProps; + const oldProps = current.memoizedProps; + const type = finishedWork.type; + // Apply the delta to the clone. + commitUpdate(clone, type, oldProps, newProps, finishedWork); + } + + if (unhideHostChildren) { + unhideHostChildren = false; + recursivelyInsertClones(finishedWork, clone); + appendChild(hostParentClone, clone); + unhideHostChildren = true; + unhideInstance(clone, finishedWork.memoizedProps); + } else { + recursivelyInsertClones(finishedWork, clone); + appendChild(hostParentClone, clone); + } + } + break; + } + case HostText: { + const textInstance: TextInstance = finishedWork.stateNode; + if (textInstance === null) { + throw new Error( + 'This should have a text node initialized. This error is likely ' + + 'caused by a bug in React. Please file an issue.', + ); + } + if (current === null) { + // For insertions we don't need to clone. It's already new state node. + appendChild(hostParentClone, textInstance); + } else { + const clone = cloneMutableTextInstance(textInstance); + if (flags & Update) { + const newText: string = finishedWork.memoizedProps; + const oldText: string = current.memoizedProps; + commitTextUpdate(clone, newText, oldText); + } + appendChild(hostParentClone, clone); + if (unhideHostChildren) { + unhideTextInstance(clone, finishedWork.memoizedProps); + } + } + break; + } + case HostPortal: { + // TODO: Consider what should happen to Portals. For now we exclude them. + break; + } + case OffscreenComponent: { + const newState: OffscreenState | null = finishedWork.memoizedState; + const isHidden = newState !== null; + if (!isHidden) { + // Only insert clones if this tree is going to be visible. No need to + // clone invisible content. + // TODO: If this is visible but detached it should still be cloned. + const prevUnhide = unhideHostChildren; + unhideHostChildren = prevUnhide || (flags & Visibility) !== NoFlags; + recursivelyInsertClones(finishedWork, hostParentClone); + unhideHostChildren = prevUnhide; + } + break; + } + case ViewTransitionComponent: + const prevMutationContext = pushMutationContext(); + // TODO: If this was already cloned by a previous pass we can reuse those clones. + recursivelyInsertClones(finishedWork, hostParentClone); + if (viewTransitionMutationContext) { + // Track that this boundary had a mutation and therefore needs to animate + // whether it resized or not. + finishedWork.flags |= Update; + } + popMutationContext(prevMutationContext); + break; + default: { + recursivelyInsertClones(finishedWork, hostParentClone); + break; + } + } +} // Clone View Transition boundaries that have any mutations or might have had their // layout affected by child insertions. @@ -20,7 +337,30 @@ export function insertDestinationClones( root: FiberRoot, finishedWork: Fiber, ): void { - // TODO + unhideHostChildren = false; + // We'll either not transition the root, or we'll transition the clone. Regardless + // we cancel the root view transition name. + const needsClone = detectMutationOrInsertClones(finishedWork); + if (needsClone) { + if (__DEV__) { + if (!didWarnForRootClone) { + didWarnForRootClone = true; + console.warn( + 'useSwipeTransition() caused something to mutate or relayout the root. ' + + 'This currently requires a clone of the whole document. Make sure to ' + + 'add a <ViewTransition> directly around an absolutely positioned DOM node ' + + 'to minimize the impact of any changes caused by the Swipe Transition.', + ); + } + } + // Clone the whole root + const rootClone = cloneRootViewTransitionContainer(root.containerInfo); + root.gestureClone = rootClone; + recursivelyInsertClones(finishedWork, rootClone); + } else { + root.gestureClone = null; + cancelRootViewTransitionName(root.containerInfo); + } } // Revert insertions and apply view transition names to the "new" (current) state. @@ -28,8 +368,12 @@ export function applyDepartureTransitions( root: FiberRoot, finishedWork: Fiber, ): void { + const rootClone = root.gestureClone; + if (rootClone !== null) { + root.gestureClone = null; + removeRootViewTransitionClone(root.containerInfo, rootClone); + } // TODO - cancelRootViewTransitionName(root.containerInfo); } // Revert transition names and start/adjust animations on the started View Transition. diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index f5d0987393046..b9ea97bf56d8c 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -247,7 +247,6 @@ import { restoreNestedViewTransitions, measureUpdateViewTransition, measureNestedViewTransitions, - resetShouldStartViewTransition, resetAppearingViewTransitions, trackAppearingViewTransition, viewTransitionCancelableChildren, @@ -290,8 +289,6 @@ export function commitBeforeMutationEffects( focusedInstanceHandle = prepareForCommit(root.containerInfo); shouldFireAfterActiveInstanceBlur = false; - resetShouldStartViewTransition(); - const isViewTransitionEligible = enableViewTransition && includesOnlyViewTransitionEligibleLanes(committedLanes); diff --git a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js index 430ad02abc1b1..64b67491aa8d5 100644 --- a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js +++ b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js @@ -20,6 +20,8 @@ function shim(...args: any): empty { // Mutation (when unsupported) export const supportsMutation = false; +export const cloneMutableInstance = shim; +export const cloneMutableTextInstance = shim; export const appendChild = shim; export const appendChildToContainer = shim; export const commitTextUpdate = shim; @@ -40,6 +42,8 @@ export const restoreViewTransitionName = shim; export const cancelViewTransitionName = shim; export const cancelRootViewTransitionName = shim; export const restoreRootViewTransitionName = shim; +export const cloneRootViewTransitionContainer = shim; +export const removeRootViewTransitionClone = shim; export type InstanceMeasurement = null; export const measureInstance = shim; export const wasInstanceInViewport = shim; diff --git a/packages/react-reconciler/src/ReactFiberRoot.js b/packages/react-reconciler/src/ReactFiberRoot.js index 41401bac2bd8f..1e1ac1cab220a 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.js +++ b/packages/react-reconciler/src/ReactFiberRoot.js @@ -101,6 +101,7 @@ function FiberRootNode( if (enableSwipeTransition) { this.pendingGestures = null; this.stoppingGestures = null; + this.gestureClone = null; } this.incompleteTransitions = new Map(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index bf611f73275a7..f8d4fa03a0dff 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -228,6 +228,7 @@ import { invokePassiveEffectUnmountInDEV, accumulateSuspenseyCommit, } from './ReactFiberCommitWork'; +import {resetShouldStartViewTransition} from './ReactFiberCommitViewTransitions'; import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions'; import { insertDestinationClones, @@ -3449,6 +3450,8 @@ function commitRoot( } } + resetShouldStartViewTransition(); + // The commit phase is broken into several sub-phases. We do a separate pass // of the effect list for each phase: all mutation effects come before all // layout effects, and so on. @@ -3900,6 +3903,11 @@ function commitGestureOnRoot( finishedGesture.running = startGestureTransition( root.containerInfo, + finishedGesture.provider, + finishedGesture.rangeCurrent, + finishedGesture.direction + ? finishedGesture.rangeNext + : finishedGesture.rangePrevious, pendingTransitionTypes, flushGestureMutations, flushGestureAnimations, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index be5e9a5c0da69..1452746089c2b 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -26,6 +26,7 @@ import type {Lane, Lanes, LaneMap} from './ReactFiberLane'; import type {RootTag} from './ReactRootTags'; import type { Container, + Instance, TimeoutHandle, NoTimeout, SuspenseInstance, @@ -286,6 +287,7 @@ type BaseFiberRootProperties = { // enableSwipeTransition only pendingGestures: null | ScheduledGesture, stoppingGestures: null | ScheduledGesture, + gestureClone: null | Instance, }; // The following attributes are only used by DevTools and are only present in DEV builds. diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 9569b4a896cee..e978249e185fe 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -56,10 +56,12 @@ export const getChildHostContext = $$$config.getChildHostContext; export const prepareForCommit = $$$config.prepareForCommit; export const resetAfterCommit = $$$config.resetAfterCommit; export const createInstance = $$$config.createInstance; +export const cloneMutableInstance = $$$config.cloneMutableInstance; export const appendInitialChild = $$$config.appendInitialChild; export const finalizeInitialChildren = $$$config.finalizeInitialChildren; export const shouldSetTextContent = $$$config.shouldSetTextContent; export const createTextInstance = $$$config.createTextInstance; +export const cloneMutableTextInstance = $$$config.cloneMutableTextInstance; export const scheduleTimeout = $$$config.scheduleTimeout; export const cancelTimeout = $$$config.cancelTimeout; export const noTimeout = $$$config.noTimeout; @@ -141,6 +143,10 @@ export const cancelRootViewTransitionName = $$$config.cancelRootViewTransitionName; export const restoreRootViewTransitionName = $$$config.restoreRootViewTransitionName; +export const cloneRootViewTransitionContainer = + $$$config.cloneRootViewTransitionContainer; +export const removeRootViewTransitionClone = + $$$config.removeRootViewTransitionClone; export const measureInstance = $$$config.measureInstance; export const wasInstanceInViewport = $$$config.wasInstanceInViewport; export const hasInstanceChanged = $$$config.hasInstanceChanged; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index 48939e9ff45ad..bb18e44e5580d 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -172,6 +172,21 @@ export function createInstance( }; } +export function cloneMutableInstance( + instance: Instance, + keepChildren: boolean, +): Instance { + return { + type: instance.type, + props: instance.props, + isHidden: instance.isHidden, + children: keepChildren ? instance.children : [], + internalInstanceHandle: null, + rootContainerInstance: instance.rootContainerInstance, + tag: 'INSTANCE', + }; +} + export function appendInitialChild( parentInstance: Instance, child: Instance | TextInstance, @@ -210,6 +225,16 @@ export function createTextInstance( }; } +export function cloneMutableTextInstance( + textInstance: TextInstance, +): TextInstance { + return { + text: textInstance.text, + isHidden: textInstance.isHidden, + tag: 'TEXT', + }; +} + let currentUpdatePriority: EventPriority = NoEventPriority; export function setCurrentUpdatePriority(newPriority: EventPriority): void { currentUpdatePriority = newPriority; @@ -337,6 +362,27 @@ export function restoreRootViewTransitionName(rootContainer: Container): void { // Noop } +export function cloneRootViewTransitionContainer( + rootContainer: Container, +): Instance { + return { + type: 'ROOT', + props: {}, + isHidden: false, + children: [], + internalInstanceHandle: null, + rootContainerInstance: rootContainer, + tag: 'INSTANCE', + }; +} + +export function removeRootViewTransitionClone( + rootContainer: Container, + clone: Instance, +): void { + // Noop since it was never inserted anywhere. +} + export type InstanceMeasurement = null; export function measureInstance(instance: Instance): InstanceMeasurement { @@ -379,6 +425,9 @@ export type RunningGestureTransition = null; export function startGestureTransition( rootContainer: Container, + timeline: GestureTimeline, + rangeStart: number, + rangeEnd: number, transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 0722eba56d30d..9db8a9cb892bc 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -536,5 +536,6 @@ "548": "Finished rendering the gesture lane but there were no pending gestures. React should not have started a render in this case. This is a bug in React.", "549": "Cannot start a gesture with a disconnected AnimationTimeline.", "550": "useSwipeTransition is not yet supported in react-art.", - "551": "useSwipeTransition is not yet supported in React Native." + "551": "useSwipeTransition is not yet supported in React Native.", + "552": "Cannot use a useSwipeTransition() in a detached root." } From e03ac20f942124bb3989b3bb58bb7b9bf91a7860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Wed, 5 Mar 2025 09:33:06 -0500 Subject: [PATCH 083/300] Set direction in animate call directly (#32523) Setting the animation's currentTime causes a quirk where the transition can end up off by a bit and the end state can be slightly off the end time. However, I realized that we don't have to because if we just set the direction in the `animate()` call directly the Safari bug goes away. --- .../src/client/ReactFiberConfigDOM.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index ba79b08c47f7c..47d6a4cb24806 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1754,7 +1754,7 @@ function animateGesture( moveOldFrameIntoViewport(keyframes[0]); } const reverse = rangeStart > rangeEnd; - const anim = targetElement.animate(keyframes, { + targetElement.animate(keyframes, { pseudoElement: pseudoElement, // Set the timeline to the current gesture timeline to drive the updates. timeline: timeline, @@ -1764,20 +1764,14 @@ function animateGesture( easing: 'linear', // We fill in both direction for overscroll. fill: 'both', + // We play all gestures in reverse, except if we're in reverse direction + // in which case we need to play it in reverse of the reverse. + direction: reverse ? 'normal' : 'reverse', // Range start needs to be higher than range end. If it goes in reverse // we reverse the whole animation below. rangeStart: (reverse ? rangeEnd : rangeStart) + '%', rangeEnd: (reverse ? rangeStart : rangeEnd) + '%', }); - if (!reverse) { - // We play all gestures in reverse, except if we're in reverse direction - // in which case we need to play it in reverse of the reverse. - anim.reverse(); - // In Safari, there's a bug where the starting position isn't immediately - // picked up from the ScrollTimeline for one frame. - // $FlowFixMe[cannot-resolve-name] - anim.currentTime = CSS.percent(100); - } } export function startGestureTransition( From 6b1ae49571b97b15177bda7c1e39a87b42331da9 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 5 Mar 2025 12:24:45 -0500 Subject: [PATCH 084/300] [ez] Remove unused netlify.toml (#32530) I don't think this is in use anymore --- netlify.toml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 netlify.toml diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index 9920f41194895..0000000000000 --- a/netlify.toml +++ /dev/null @@ -1,9 +0,0 @@ -[build] - base = "" - publish = "fixtures/dom/build" - command = "yarn build --type=UMD_DEV && cd fixtures/dom/ && yarn && yarn build" - -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 From aac177c48439ab294f72e8b5a85059daa3f8a5ee Mon Sep 17 00:00:00 2001 From: Keith Cirkel <keithamus@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:45:16 +0000 Subject: [PATCH 085/300] Support beforetoggle/toggle events for dialog (#32479) --- .../src/client/ReactDOMComponent.js | 2 ++ .../ReactDOMEventPropagation-test.js | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponent.js b/packages/react-dom-bindings/src/client/ReactDOMComponent.js index efdfc66ff21f9..48b4d9472fb5d 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMComponent.js +++ b/packages/react-dom-bindings/src/client/ReactDOMComponent.js @@ -1280,6 +1280,8 @@ export function setInitialProperties( return; } case 'dialog': { + listenToNonDelegatedEvent('beforetoggle', domElement); + listenToNonDelegatedEvent('toggle', domElement); listenToNonDelegatedEvent('cancel', domElement); listenToNonDelegatedEvent('close', domElement); break; diff --git a/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js b/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js index 1598329340073..ebd3f9a540115 100644 --- a/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js @@ -1302,6 +1302,38 @@ describe('ReactDOMEventListener', () => { }); }); + it('onBeforeToggle Dialog API', async () => { + await testEmulatedBubblingEvent({ + type: 'dialog', + reactEvent: 'onBeforeToggle', + reactEventType: 'beforetoggle', + nativeEvent: 'beforetoggle', + dispatch(node) { + const e = new Event('beforetoggle', { + bubbles: false, + cancelable: true, + }); + node.dispatchEvent(e); + }, + }); + }); + + it('onToggle Dialog API', async () => { + await testEmulatedBubblingEvent({ + type: 'dialog', + reactEvent: 'onToggle', + reactEventType: 'toggle', + nativeEvent: 'toggle', + dispatch(node) { + const e = new Event('toggle', { + bubbles: false, + cancelable: true, + }); + node.dispatchEvent(e); + }, + }); + }); + it('onVolumeChange', async () => { await testEmulatedBubblingEvent({ type: 'video', From e81fcfe3f201a8f626e892fb52ccbd0edba627cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Wed, 5 Mar 2025 22:16:56 -0500 Subject: [PATCH 086/300] [Flight] Expose registerServerReference from the client builds (#32534) This is used to register Server References that exist in the current environment but also exists in the server it might call into. Such as a remote server. If the value comes from the remote server in the first place then this is called automatically to ensure that you can pass a reference back to where it came from - even if the `serverModuleMap` option is used. This was already the case when `serverModuleMap` wasn't passed. This is how you can pass server references back to the server. However, when we added `serverModuleMap` that pass was skipped because we were getting real functions instead of proxies. For functions that wasn't yet passed from the remote server to the current server, we can register them eagerly just like we do for `import('/server').registerServerReference()`. You can now also do this with `import('/client').registerServerReference()`. We could make them shared so you only have to do this once but it might not be possible to pass to the remote server and the remote server might not even be the same RSC renderer. Therefore I split them. It's up to the compiler whether it should do that or not. It has to know that any function you might call might be able to receive it. This is currently global to a specific RSC renderer. --- .../react-client/src/ReactFlightClient.js | 21 +++++++++- .../src/ReactFlightReplyClient.js | 26 ++++++++---- .../src/client/ReactFlightDOMClientBrowser.js | 13 +++--- .../src/client/ReactFlightDOMClientNode.js | 2 + .../src/client/ReactFlightDOMClientBrowser.js | 2 + .../src/client/ReactFlightDOMClientEdge.js | 2 + .../src/client/ReactFlightDOMClientNode.js | 2 + .../src/client/ReactFlightDOMClientBrowser.js | 13 +++--- .../src/client/ReactFlightDOMClientEdge.js | 2 + .../src/client/ReactFlightDOMClientNode.js | 2 + .../src/__tests__/ReactFlightDOMEdge-test.js | 42 +++++++++++++++++++ .../__tests__/ReactFlightDOMReplyEdge-test.js | 29 ++++++++++++- .../src/client/ReactFlightDOMClientBrowser.js | 13 +++--- .../src/client/ReactFlightDOMClientEdge.js | 2 + .../src/client/ReactFlightDOMClientNode.js | 2 + .../src/ReactFlightReplyServer.js | 6 ++- 16 files changed, 141 insertions(+), 38 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 0eaf513a676dd..3234814952fad 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -64,7 +64,10 @@ import { rendererPackageName, } from './ReactFlightClientConfig'; -import {createBoundServerReference} from './ReactFlightReplyClient'; +import { + createBoundServerReference, + registerBoundServerReference, +} from './ReactFlightReplyClient'; import {readTemporaryReference} from './ReactFlightTemporaryReferences'; @@ -1096,7 +1099,14 @@ function loadServerReference<A: Iterable<any>, T>( let promise: null | Thenable<any> = preloadModule(serverReference); if (!promise) { if (!metaData.bound) { - return (requireModule(serverReference): any); + const resolvedValue = (requireModule(serverReference): any); + registerBoundServerReference( + resolvedValue, + metaData.id, + metaData.bound, + response._encodeFormAction, + ); + return resolvedValue; } else { promise = Promise.resolve(metaData.bound); } @@ -1128,6 +1138,13 @@ function loadServerReference<A: Iterable<any>, T>( resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs); } + registerBoundServerReference( + resolvedValue, + metaData.id, + metaData.bound, + response._encodeFormAction, + ); + parentObject[key] = resolvedValue; // If this is the root object for a model reference, where `handler.value` diff --git a/packages/react-client/src/ReactFlightReplyClient.js b/packages/react-client/src/ReactFlightReplyClient.js index 65d1129b53830..3fa37cd00c788 100644 --- a/packages/react-client/src/ReactFlightReplyClient.js +++ b/packages/react-client/src/ReactFlightReplyClient.js @@ -1125,11 +1125,12 @@ function createFakeServerFunction<A: Iterable<any>, T>( } } -function registerServerReference( - proxy: any, - reference: {id: ServerReferenceId, bound: null | Thenable<Array<any>>}, +export function registerBoundServerReference<T: Function>( + reference: T, + id: ServerReferenceId, + bound: null | Thenable<Array<any>>, encodeFormAction: void | EncodeFormActionCallback, -) { +): void { // Expose encoder for use by SSR, as well as a special bind that can be used to // keep server capabilities. if (usedWithSSR) { @@ -1147,13 +1148,22 @@ function registerServerReference( encodeFormAction, ); }; - Object.defineProperties((proxy: any), { + Object.defineProperties((reference: any), { $$FORM_ACTION: {value: $$FORM_ACTION}, $$IS_SIGNATURE_EQUAL: {value: isSignatureEqual}, bind: {value: bind}, }); } - knownServerReferences.set(proxy, reference); + knownServerReferences.set(reference, {id, bound}); +} + +export function registerServerReference<T: Function>( + reference: T, + id: ServerReferenceId, + encodeFormAction?: EncodeFormActionCallback, +): ServerReference<T> { + registerBoundServerReference(reference, id, null, encodeFormAction); + return reference; } // $FlowFixMe[method-unbinding] @@ -1258,7 +1268,7 @@ export function createBoundServerReference<A: Iterable<any>, T>( ); } } - registerServerReference(action, {id, bound}, encodeFormAction); + registerBoundServerReference(action, id, bound, encodeFormAction); return action; } @@ -1358,6 +1368,6 @@ export function createServerReference<A: Iterable<any>, T>( ); } } - registerServerReference(action, {id, bound: null}, encodeFormAction); + registerBoundServerReference(action, id, null, encodeFormAction); return action; } diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js index afabc291041bf..9ae47e3b5512d 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientBrowser.js @@ -25,9 +25,11 @@ import { injectIntoDevTools, } from 'react-client/src/ReactFlightClient'; -import { - processReply, +import {processReply} from 'react-client/src/ReactFlightReplyClient'; + +export { createServerReference, + registerServerReference, } from 'react-client/src/ReactFlightReplyClient'; import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; @@ -151,12 +153,7 @@ function encodeReply( }); } -export { - createFromFetch, - createFromReadableStream, - encodeReply, - createServerReference, -}; +export {createFromFetch, createFromReadableStream, encodeReply}; if (__DEV__) { injectIntoDevTools(); diff --git a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js index 2e1c5566423ca..75c569e5ac4bb 100644 --- a/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-esm/src/client/ReactFlightDOMClientNode.js @@ -26,6 +26,8 @@ import { import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + function noServerCall() { throw new Error( 'Server Functions cannot be called during initial render. ' + diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js index 7ea840b140790..3aca4a355dce7 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientBrowser.js @@ -26,6 +26,8 @@ import { createServerReference as createServerReferenceImpl, } from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js index 8783cbfc6a7f9..b1fbfed08f072 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientEdge.js @@ -25,6 +25,8 @@ import { createServerReference as createServerReferenceImpl, } from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; diff --git a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js index 8be06af9f2038..b12a3a3ff49d8 100644 --- a/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-parcel/src/client/ReactFlightDOMClientNode.js @@ -21,6 +21,8 @@ import { import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + function findSourceMapURL(filename: string, environmentName: string) { const devServer = parcelRequire.meta.devServer; if (devServer != null) { diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js index b6b55e4586e16..ee319beca18ef 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientBrowser.js @@ -25,9 +25,11 @@ import { injectIntoDevTools, } from 'react-client/src/ReactFlightClient'; -import { - processReply, +import {processReply} from 'react-client/src/ReactFlightReplyClient'; + +export { createServerReference, + registerServerReference, } from 'react-client/src/ReactFlightReplyClient'; import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; @@ -150,12 +152,7 @@ function encodeReply( }); } -export { - createFromFetch, - createFromReadableStream, - encodeReply, - createServerReference, -}; +export {createFromFetch, createFromReadableStream, encodeReply}; if (__DEV__) { injectIntoDevTools(); diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js index 509950bc65135..48cb0dd4db13c 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientEdge.js @@ -41,6 +41,8 @@ import { createServerReference as createServerReferenceImpl, } from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js index 2ee76fa3b41c9..919be523f8823 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightDOMClientNode.js @@ -38,6 +38,8 @@ import { import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + function noServerCall() { throw new Error( 'Server Functions cannot be called during initial render. ' + diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index ba1ae3b64a850..5d3af9d4116f9 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -301,6 +301,48 @@ describe('ReactFlightDOMEdge', () => { expect(result.boundMethod()).toBe('hi, there'); }); + it('should load a server reference on a consuming server and pass it back', async () => { + function greet(name) { + return 'hi, ' + name; + } + const ServerModule = serverExports({ + greet, + }); + + const stream = await serverAct(() => + ReactServerDOMServer.renderToReadableStream( + { + method: ServerModule.greet, + boundMethod: ServerModule.greet.bind(null, 'there'), + }, + webpackMap, + ), + ); + const response = ReactServerDOMClient.createFromReadableStream(stream, { + serverConsumerManifest: { + moduleMap: webpackMap, + serverModuleMap: webpackServerMap, + moduleLoading: webpackModuleLoading, + }, + }); + + const result = await response; + + expect(result.method).toBe(greet); + expect(result.boundMethod()).toBe('hi, there'); + + const body = await ReactServerDOMClient.encodeReply({ + method: result.method, + boundMethod: result.boundMethod, + }); + const replyResult = await ReactServerDOMServer.decodeReply( + body, + webpackServerMap, + ); + expect(replyResult.method).toBe(greet); + expect(replyResult.boundMethod()).toBe('hi, there'); + }); + it('should encode long string in a compact format', async () => { const testString = '"\n\t'.repeat(500) + '🙃'; const testString2 = 'hello'.repeat(400); diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReplyEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReplyEdge-test.js index 2effa9868e99b..7315e78c619d2 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReplyEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReplyEdge-test.js @@ -22,7 +22,7 @@ if (typeof File === 'undefined' || typeof FormData === 'undefined') { global.FormData = require('undici').FormData; } -// let serverExports; +let serverExports; let webpackServerMap; let ReactServerDOMServer; let ReactServerDOMClient; @@ -36,7 +36,7 @@ describe('ReactFlightDOMReplyEdge', () => { require('react-server-dom-webpack/server.edge'), ); const WebpackMock = require('./utils/WebpackMock'); - // serverExports = WebpackMock.serverExports; + serverExports = WebpackMock.serverExports; webpackServerMap = WebpackMock.webpackServerMap; ReactServerDOMServer = require('react-server-dom-webpack/server.edge'); jest.resetModules(); @@ -308,4 +308,29 @@ describe('ReactFlightDOMReplyEdge', () => { expect(await decoded.a).toBe('hello'); expect(Array.from(await decoded.b)).toEqual(Array.from(buffer)); }); + + it('can pass a registered server reference', async () => { + function greet(name) { + return 'hi, ' + name; + } + const ServerModule = serverExports({ + greet, + }); + + ReactServerDOMClient.registerServerReference( + ServerModule.greet, + ServerModule.greet.$$id, + ); + + const body = await ReactServerDOMClient.encodeReply({ + method: ServerModule.greet, + boundMethod: ServerModule.greet.bind(null, 'there'), + }); + const replyResult = await ReactServerDOMServer.decodeReply( + body, + webpackServerMap, + ); + expect(replyResult.method).toBe(greet); + expect(replyResult.boundMethod()).toBe('hi, there'); + }); }); diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js index b6b55e4586e16..ee319beca18ef 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientBrowser.js @@ -25,9 +25,11 @@ import { injectIntoDevTools, } from 'react-client/src/ReactFlightClient'; -import { - processReply, +import {processReply} from 'react-client/src/ReactFlightReplyClient'; + +export { createServerReference, + registerServerReference, } from 'react-client/src/ReactFlightReplyClient'; import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; @@ -150,12 +152,7 @@ function encodeReply( }); } -export { - createFromFetch, - createFromReadableStream, - encodeReply, - createServerReference, -}; +export {createFromFetch, createFromReadableStream, encodeReply}; if (__DEV__) { injectIntoDevTools(); diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js index 509950bc65135..48cb0dd4db13c 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientEdge.js @@ -41,6 +41,8 @@ import { createServerReference as createServerReferenceImpl, } from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences'; diff --git a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js index 22c8928432b74..4118ad046d99d 100644 --- a/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js +++ b/packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js @@ -39,6 +39,8 @@ import { import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient'; +export {registerServerReference} from 'react-client/src/ReactFlightReplyClient'; + function noServerCall() { throw new Error( 'Server Functions cannot be called during initial render. ' + diff --git a/packages/react-server/src/ReactFlightReplyServer.js b/packages/react-server/src/ReactFlightReplyServer.js index 4db7571bb66a6..7c94352f288dc 100644 --- a/packages/react-server/src/ReactFlightReplyServer.js +++ b/packages/react-server/src/ReactFlightReplyServer.js @@ -936,8 +936,10 @@ function parseModelString( // Server Reference const ref = value.slice(2); // TODO: Just encode this in the reference inline instead of as a model. - const metaData: {id: ServerReferenceId, bound: Thenable<Array<any>>} = - getOutlinedModel(response, ref, obj, key, createModel); + const metaData: { + id: ServerReferenceId, + bound: null | Thenable<Array<any>>, + } = getOutlinedModel(response, ref, obj, key, createModel); return loadServerReference( response, metaData.id, From 029e8bd618af23fbdd9efdac565ad81f7d4640d8 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" <sebastian.silbermann@vercel.com> Date: Thu, 6 Mar 2025 17:12:50 +0100 Subject: [PATCH 087/300] Add Owner Stack to attribute hydration mismatches (#32538) --- .../src/__tests__/ReactDOMFizzForm-test.js | 104 +++--- .../src/__tests__/ReactDOMFizzServer-test.js | 2 +- .../__tests__/ReactDOMHydrationDiff-test.js | 313 +++++++++++++++--- .../src/__tests__/ReactDOMRoot-test.js | 42 ++- .../ReactDOMSingletonComponents-test.js | 50 ++- .../src/__tests__/ReactRenderDocument-test.js | 5 +- .../ReactServerRenderingHydration-test.js | 113 +++---- .../src/ReactFiberHydrationContext.js | 43 ++- 8 files changed, 444 insertions(+), 228 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js index f64cdd8bb8e39..c7f52b1c68899 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js @@ -201,25 +201,24 @@ describe('ReactDOMFizzForm', () => { await act(async () => { ReactDOMClient.hydrateRoot(container, <App isClient={true} />); }); - assertConsoleErrorDev( - [ - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + - "This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n" + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n\n' + - 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' + - 'https://react.dev/link/hydration-mismatch\n\n' + - ' <App isClient={true}>\n' + - ' <form\n' + - '+ action="action"\n' + - '- action="function"\n' + - ' >\n', - ], - {withoutStack: true}, - ); + assertConsoleErrorDev([ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + + "This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n" + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n\n' + + 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' + + 'https://react.dev/link/hydration-mismatch\n\n' + + ' <App isClient={true}>\n' + + ' <form\n' + + '+ action="action"\n' + + '- action="function"\n' + + ' >\n' + + '\n in form (at **)' + + '\n in App (at **)', + ]); }); it('should ideally warn when passing a string during SSR and function during hydration', async () => { @@ -392,40 +391,39 @@ describe('ReactDOMFizzForm', () => { await act(async () => { root = ReactDOMClient.hydrateRoot(container, <App />); }); - assertConsoleErrorDev( - [ - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + - "This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n" + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n\n' + - 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' + - 'https://react.dev/link/hydration-mismatch\n\n' + - ' <App>\n' + - ' <form\n' + - ' action={function action}\n' + - ' ref={{current:null}}\n' + - '+ method="DELETE"\n' + - '- method={null}\n' + - ' >\n' + - ' <input\n' + - ' type="submit"\n' + - ' formAction={function action}\n' + - ' ref={{current:null}}\n' + - '+ formTarget="elsewhere"\n' + - '- formTarget={null}\n' + - ' >\n' + - ' <button\n' + - ' formAction={function action}\n' + - ' ref={{current:null}}\n' + - '+ formEncType="text/plain"\n' + - '- formEncType={null}\n' + - ' >\n', - ], - {withoutStack: true}, - ); + assertConsoleErrorDev([ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + + "This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n" + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n\n' + + 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' + + 'https://react.dev/link/hydration-mismatch\n\n' + + ' <App>\n' + + ' <form\n' + + ' action={function action}\n' + + ' ref={{current:null}}\n' + + '+ method="DELETE"\n' + + '- method={null}\n' + + ' >\n' + + ' <input\n' + + ' type="submit"\n' + + ' formAction={function action}\n' + + ' ref={{current:null}}\n' + + '+ formTarget="elsewhere"\n' + + '- formTarget={null}\n' + + ' >\n' + + ' <button\n' + + ' formAction={function action}\n' + + ' ref={{current:null}}\n' + + '+ formEncType="text/plain"\n' + + '- formEncType={null}\n' + + ' >\n' + + '\n in input (at **)' + + '\n in App (at **)', + ]); await act(async () => { root.render(<App isUpdate={true} />); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 5b44d9e02e855..7542582528568 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -10233,7 +10233,7 @@ describe('ReactDOMFizzServer', () => { '\n+ client' + '\n- server' + '\n' + - '\n in Suspense (at **)' + + '\n in meta (at **)' + '\n in ClientApp (at **)', ]); } diff --git a/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js b/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js index 2efcfd01d65f4..c445f458e5b12 100644 --- a/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js @@ -23,6 +23,7 @@ function errorHandler() { describe('ReactDOMServerHydration', () => { let container; + let ownerStacks; beforeEach(() => { jest.resetModules(); @@ -32,7 +33,15 @@ describe('ReactDOMServerHydration', () => { act = React.act; window.addEventListener('error', errorHandler); - console.error = jest.fn(); + ownerStacks = []; + console.error = jest.fn(() => { + const ownerStack = React.captureOwnerStack(); + if (typeof ownerStack === 'string') { + ownerStacks.push(ownerStack === '' ? ' <empty>' : ownerStack); + } else { + ownerStacks.push(' ' + String(ownerStack)); + } + }); container = document.createElement('div'); document.body.appendChild(container); }); @@ -44,15 +53,25 @@ describe('ReactDOMServerHydration', () => { }); function normalizeCodeLocInfo(str) { - return ( - typeof str === 'string' && - str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) { - return '\n in ' + name + ' (at **)'; - }) - ); + return typeof str === 'string' + ? str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) { + return '\n in ' + name + ' (at **)'; + }) + : str; } - function formatMessage(args) { + function formatMessage(args, index) { + const ownerStack = ownerStacks[index]; + + if (ownerStack === undefined) { + throw new Error( + 'Expected an owner stack for message ' + + index + + ':\n' + + util.format(...args), + ); + } + const [format, ...rest] = args; if (format instanceof Error) { if (format.cause instanceof Error) { @@ -61,13 +80,23 @@ describe('ReactDOMServerHydration', () => { format.message + ']\n Cause [' + format.cause.message + - ']' + ']\n Owner Stack:' + + normalizeCodeLocInfo(ownerStack) ); } - return 'Caught [' + format.message + ']'; + return ( + 'Caught [' + + format.message + + ']\n Owner Stack:' + + normalizeCodeLocInfo(ownerStack) + ); } rest[rest.length - 1] = normalizeCodeLocInfo(rest[rest.length - 1]); - return util.format(format, ...rest); + return ( + util.format(format, ...rest) + + '\n Owner Stack:' + + normalizeCodeLocInfo(ownerStack) + ); } function formatConsoleErrors() { @@ -115,7 +144,10 @@ describe('ReactDOMServerHydration', () => { <main className="child"> + client - server - ]", + ] + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); } else { @@ -138,7 +170,10 @@ describe('ReactDOMServerHydration', () => { <main className="child"> + client - server - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); } @@ -177,7 +212,10 @@ describe('ReactDOMServerHydration', () => { <div> + This markup contains an nbsp entity:   client text - This markup contains an nbsp entity:   server text - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); } else { @@ -199,7 +237,10 @@ describe('ReactDOMServerHydration', () => { <div> + This markup contains an nbsp entity:   client text - This markup contains an nbsp entity:   server text - ", + + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); } @@ -245,7 +286,10 @@ describe('ReactDOMServerHydration', () => { - __html: "<span>server</span>" }} > - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -286,7 +330,10 @@ describe('ReactDOMServerHydration', () => { + dir="ltr" - dir="rtl" > - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -327,7 +374,10 @@ describe('ReactDOMServerHydration', () => { + dir="ltr" - dir={null} > - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -368,7 +418,10 @@ describe('ReactDOMServerHydration', () => { + dir={null} - dir="rtl" > - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -409,7 +462,10 @@ describe('ReactDOMServerHydration', () => { + dir={null} - dir="rtl" > - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -449,7 +505,78 @@ describe('ReactDOMServerHydration', () => { + style={{opacity:1}} - style={{opacity:"0"}} > - ", + + Owner Stack: + in main (at **) + in Mismatch (at **)", + ] + `); + }); + + // @gate __DEV__ + it('picks the DFS-first Fiber as the error Owner', () => { + function LeftMismatch({isClient}) { + return <div className={isClient ? 'client' : 'server'} />; + } + + function LeftIndirection({isClient}) { + return <LeftMismatch isClient={isClient} />; + } + + function MiddleMismatch({isClient}) { + return <span className={isClient ? 'client' : 'server'} />; + } + + function RightMisMatch({isClient}) { + return <p className={isClient ? 'client' : 'server'} />; + } + + function App({isClient}) { + return ( + <> + <LeftIndirection isClient={isClient} /> + <MiddleMismatch isClient={isClient} /> + <RightMisMatch isClient={isClient} /> + </> + ); + } + expect(testMismatch(App)).toMatchInlineSnapshot(` + [ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + + <App isClient={true}> + <LeftIndirection isClient={true}> + <LeftMismatch isClient={true}> + <div + + className="client" + - className="server" + > + <MiddleMismatch isClient={true}> + <span + + className="client" + - className="server" + > + <RightMisMatch isClient={true}> + <p + + className="client" + - className="server" + > + + Owner Stack: + in div (at **) + in LeftMismatch (at **) + in LeftIndirection (at **) + in App (at **)", ] `); }); @@ -483,7 +610,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> + <main className="only"> - ]", + ] + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -518,7 +648,10 @@ describe('ReactDOMServerHydration', () => { + <header className="1"> - <main className="2"> ... - ]", + ] + Owner Stack: + in header (at **) + in Mismatch (at **)", ] `); }); @@ -554,7 +687,10 @@ describe('ReactDOMServerHydration', () => { + <main className="2"> - <footer className="3"> ... - ]", + ] + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -589,7 +725,10 @@ describe('ReactDOMServerHydration', () => { <header> <main> + <footer className="3"> - ]", + ] + Owner Stack: + in footer (at **) + in Mismatch (at **)", ] `); }); @@ -620,7 +759,10 @@ describe('ReactDOMServerHydration', () => { <div className="parent"> + only - - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); } else { @@ -642,7 +784,10 @@ describe('ReactDOMServerHydration', () => { <div className="parent"> + only - - ", + + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); } @@ -679,7 +824,10 @@ describe('ReactDOMServerHydration', () => { + second - <footer className="3"> ... - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -714,7 +862,10 @@ describe('ReactDOMServerHydration', () => { + first - <main className="2"> ... - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -749,7 +900,10 @@ describe('ReactDOMServerHydration', () => { <header> <main> + third - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -784,7 +938,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> - <main className="only"> - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -819,7 +976,10 @@ describe('ReactDOMServerHydration', () => { + <main className="2"> - <header className="1"> ... - ]", + ] + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -854,7 +1014,10 @@ describe('ReactDOMServerHydration', () => { <header> + <footer className="3"> - <main className="2"> - ]", + ] + Owner Stack: + in footer (at **) + in Mismatch (at **)", ] `); }); @@ -887,7 +1050,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> - <footer className="3"> - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -916,7 +1082,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> - only - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -951,7 +1120,10 @@ describe('ReactDOMServerHydration', () => { + <main className="2"> - first ... - ]", + ] + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -986,7 +1158,10 @@ describe('ReactDOMServerHydration', () => { <header> + <footer className="3"> - second - ]", + ] + Owner Stack: + in footer (at **) + in Mismatch (at **)", ] `); }); @@ -1019,7 +1194,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> - third - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -1062,7 +1240,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> + <Suspense fallback={<p>}> - ]", + ] + Owner Stack: + in Suspense (at **) + in Mismatch (at **)", ] `); }); @@ -1097,7 +1278,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> - <Suspense> - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -1134,7 +1318,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> + <Suspense fallback={<p>}> - ]", + ] + Owner Stack: + in Suspense (at **) + in Mismatch (at **)", ] `); }); @@ -1175,7 +1362,10 @@ describe('ReactDOMServerHydration', () => { <Mismatch isClient={true}> <div className="parent"> - <Suspense> - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -1214,7 +1404,10 @@ describe('ReactDOMServerHydration', () => { + <main className="second"> - <footer className="3"> ... - ]", + ] + Owner Stack: + in main (at **) + in Mismatch (at **)", ] `); }); @@ -1252,7 +1445,10 @@ describe('ReactDOMServerHydration', () => { <header> + <footer className="3"> - <main className="second"> - ]", + ] + Owner Stack: + in footer (at **) + in Mismatch (at **)", ] `); }); @@ -1280,7 +1476,8 @@ describe('ReactDOMServerHydration', () => { [ "Caught [Switched to client rendering because the server rendering aborted due to: - The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]", + The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server] + Owner Stack: null", ] `); }); @@ -1308,7 +1505,8 @@ describe('ReactDOMServerHydration', () => { [ "Caught [Switched to client rendering because the server rendering aborted due to: - The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]", + The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server] + Owner Stack: null", ] `); }); @@ -1348,7 +1546,10 @@ describe('ReactDOMServerHydration', () => { <div className="parent"> + <header className="1"> ... - ]", + ] + Owner Stack: + in header (at **) + in Mismatch (at **)", ] `); }); @@ -1387,7 +1588,10 @@ describe('ReactDOMServerHydration', () => { - <header className="1"> - <main className="2"> - <footer className="3"> - ]", + ] + Owner Stack: + in div (at **) + in Mismatch (at **)", ] `); }); @@ -1451,7 +1655,12 @@ describe('ReactDOMServerHydration', () => { <header> <main> + <footer className="3"> - ]", + ] + Owner Stack: + in footer (at **) + in Panel (at **) + in ProfileSettings (at **) + in Mismatch (at **)", ] `); }); @@ -1508,7 +1717,11 @@ describe('ReactDOMServerHydration', () => { <ProfileSettings> <div className="parent"> - <footer className="3"> - ]", + ] + Owner Stack: + in div (at **) + in ProfileSettings (at **) + in Mismatch (at **)", ] `); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js index c447430f20ae5..25503d3a3c6a5 100644 --- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js @@ -164,28 +164,26 @@ describe('ReactDOMRoot', () => { </div>, ); await waitForAll([]); - assertConsoleErrorDev( - [ - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + - "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + - '\n' + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n' + - '\n' + - 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' + - '\n' + - 'https://react.dev/link/hydration-mismatch\n' + - '\n' + - ' <div>\n' + - ' <span\n' + - '- className="extra"\n' + - ' >\n', - ], - {withoutStack: true}, - ); + assertConsoleErrorDev([ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + + "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + + '\n' + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n' + + '\n' + + 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' + + '\n' + + 'https://react.dev/link/hydration-mismatch\n' + + '\n' + + ' <div>\n' + + ' <span\n' + + '- className="extra"\n' + + ' >\n' + + '\n in span (at **)', + ]); }); it('clears existing children', async () => { diff --git a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js index b47014dee9831..84db05bc779db 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js @@ -547,32 +547,30 @@ describe('ReactDOM HostSingleton', () => { ); expect(hydrationErrors).toEqual([]); await waitForAll([]); - assertConsoleErrorDev( - [ - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + - "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + - '\n' + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed ' + - 'which messes with the HTML before React loaded.\n' + - '\n' + - 'https://react.dev/link/hydration-mismatch\n' + - '\n' + - ' <html\n' + - '+ data-client-foo="foo"\n' + - '- data-client-foo={null}\n' + - ' >\n' + - ' <head>\n' + - ' <body\n' + - '+ data-client-baz="baz"\n' + - '- data-client-baz={null}\n' + - ' >\n', - ], - {withoutStack: true}, - ); + assertConsoleErrorDev([ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + + "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + + '\n' + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed ' + + 'which messes with the HTML before React loaded.\n' + + '\n' + + 'https://react.dev/link/hydration-mismatch\n' + + '\n' + + ' <html\n' + + '+ data-client-foo="foo"\n' + + '- data-client-foo={null}\n' + + ' >\n' + + ' <head>\n' + + ' <body\n' + + '+ data-client-baz="baz"\n' + + '- data-client-baz={null}\n' + + ' >\n' + + '\n in body (at **)', + ]); expect(persistentElements).toEqual([ document.documentElement, document.head, diff --git a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js index 09ac92aad969e..8395d2afde632 100644 --- a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js +++ b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js @@ -311,9 +311,10 @@ describe('rendering React components at document', () => { '+ Hello world\n' + '- Goodbye world\n' + '+ Hello world\n' + - '- Goodbye world\n', + '- Goodbye world\n' + + '\n in body (at **)' + + '\n in Component (at **)', ], - {withoutStack: true}, ); assertLog( diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js index a23e709047a01..e8435bd1ce8f3 100644 --- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js @@ -163,9 +163,10 @@ describe('ReactDOMServerHydration', () => { ' <TestComponent name="y" ref={function ref}>\n' + ' <span ref={{current:null}} onClick={function}>\n' + '+ y\n' + - '- x\n', + '- x\n' + + '\n in span (at **)' + + '\n in TestComponent (at **)', ], - {withoutStack: true}, ); expect(mountCount).toEqual(4); expect(element.innerHTML.length > 0).toBe(true); @@ -269,9 +270,9 @@ describe('ReactDOMServerHydration', () => { '\n' + ' <button autoFocus={false} onFocus={function mockConstructor}>\n' + '+ client\n' + - '- server\n', + '- server\n' + + '\n in button (at **)', ], - {withoutStack: true}, ); expect(onFocusBeforeHydration).not.toHaveBeenCalled(); @@ -294,31 +295,29 @@ describe('ReactDOMServerHydration', () => { />, ); }); - assertConsoleErrorDev( - [ - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + - "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + - '\n' + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' + - 'installed which messes with the HTML before React loaded.\n' + - '\n' + - 'https://react.dev/link/hydration-mismatch\n' + - '\n' + - ' <div\n style={{\n+ textDecoration: "none"\n' + - '+ color: "white"\n' + - '- color: "black"\n' + - '+ height: "10px"\n' + - '- height: "10px"\n' + - '- text-decoration: "none"\n' + - ' }}\n' + - ' >\n', - ], - {withoutStack: true}, - ); + assertConsoleErrorDev([ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + + "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + + '\n' + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' + + 'installed which messes with the HTML before React loaded.\n' + + '\n' + + 'https://react.dev/link/hydration-mismatch\n' + + '\n' + + ' <div\n style={{\n+ textDecoration: "none"\n' + + '+ color: "white"\n' + + '- color: "black"\n' + + '+ height: "10px"\n' + + '- height: "10px"\n' + + '- text-decoration: "none"\n' + + ' }}\n' + + ' >\n' + + '\n in div (at **)', + ]); }); it('should not warn when the style property differs on whitespace or order in IE', async () => { @@ -362,33 +361,31 @@ describe('ReactDOMServerHydration', () => { />, ); }); - assertConsoleErrorDev( - [ - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + - "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + - '\n' + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' + - 'installed which messes with the HTML before React loaded.\n' + - '\n' + - 'https://react.dev/link/hydration-mismatch\n' + - '\n' + - ' <div\n' + - ' style={{\n' + - '+ textDecoration: "none"\n' + - '+ color: "black"\n' + - '- color: "black"\n' + - '+ height: "10px"\n' + - '- height: "10px"\n' + - '- text-decoration: "none"\n' + - ' }}\n' + - ' >\n', - ], - {withoutStack: true}, - ); + assertConsoleErrorDev([ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. " + + "This won't be patched up. This can happen if a SSR-ed Client Component used:\n" + + '\n' + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' + + 'installed which messes with the HTML before React loaded.\n' + + '\n' + + 'https://react.dev/link/hydration-mismatch\n' + + '\n' + + ' <div\n' + + ' style={{\n' + + '+ textDecoration: "none"\n' + + '+ color: "black"\n' + + '- color: "black"\n' + + '+ height: "10px"\n' + + '- height: "10px"\n' + + '- text-decoration: "none"\n' + + ' }}\n' + + ' >\n' + + '\n in div (at **)', + ]); }); it('should throw rendering portals on the server', () => { @@ -652,9 +649,9 @@ describe('ReactDOMServerHydration', () => { ' <div dangerouslySetInnerHTML={undefined}>\n' + ' <p>\n' + '+ client\n' + - '- server\n', + '- server\n' + + '\n in p (at **)', ], - {withoutStack: true}, ); if (favorSafetyOverHydrationPerf) { diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.js b/packages/react-reconciler/src/ReactFiberHydrationContext.js index 23f13bbcadbf8..f6589b7445e86 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.js @@ -67,6 +67,7 @@ import { import {queueRecoverableErrors} from './ReactFiberWorkLoop'; import {getRootHostContainer, getHostContext} from './ReactFiberHostContext'; import {describeDiff} from './ReactFiberHydrationDiffs'; +import {runWithFiberInDEV} from './ReactCurrentFiber'; // The deepest Fiber on the stack involved in a hydration context. // This may have been an insertion or a hydration. @@ -749,22 +750,32 @@ export function emitPendingHydrationWarnings() { if (diffRoot !== null) { hydrationDiffRootDEV = null; const diff = describeDiff(diffRoot); - console.error( - "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. " + - 'This can happen if a SSR-ed Client Component used:\n' + - '\n' + - "- A server/client branch `if (typeof window !== 'undefined')`.\n" + - "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + - "- Date formatting in a user's locale which doesn't match the server.\n" + - '- External changing data without sending a snapshot of it along with the HTML.\n' + - '- Invalid HTML tag nesting.\n' + - '\n' + - 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' + - '\n' + - '%s%s', - 'https://react.dev/link/hydration-mismatch', - diff, - ); + + // Just pick the DFS-first leaf as the owner. + // Should be good enough since most warnings only have a single error. + let diffOwner: HydrationDiffNode = diffRoot; + while (diffOwner.children.length > 0) { + diffOwner = diffOwner.children[0]; + } + + runWithFiberInDEV(diffOwner.fiber, () => { + console.error( + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. " + + 'This can happen if a SSR-ed Client Component used:\n' + + '\n' + + "- A server/client branch `if (typeof window !== 'undefined')`.\n" + + "- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" + + "- Date formatting in a user's locale which doesn't match the server.\n" + + '- External changing data without sending a snapshot of it along with the HTML.\n' + + '- Invalid HTML tag nesting.\n' + + '\n' + + 'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' + + '\n' + + '%s%s', + 'https://react.dev/link/hydration-mismatch', + diff, + ); + }); } } } From 9e9b54d7f6c67b452c969abb98f4f7637ed72f56 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:06:36 -0500 Subject: [PATCH 088/300] [compiler] Make CompilerError compatible with reflection (#32539) --- .../babel-plugin-react-compiler/src/CompilerError.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index 5ea6f9862815f..3a3010dd2f62b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -188,6 +188,7 @@ export class CompilerError extends Error { constructor(...args: Array<any>) { super(...args); this.name = 'ReactCompilerError'; + this.details = []; } override get message(): string { @@ -197,7 +198,10 @@ export class CompilerError extends Error { override set message(_message: string) {} override toString(): string { - return this.details.map(detail => detail.toString()).join('\n\n'); + if (Array.isArray(this.details)) { + return this.details.map(detail => detail.toString()).join('\n\n'); + } + return this.name; } push(options: CompilerErrorDetailOptions): CompilerErrorDetail { From 562f17efab5c39b461ec100898c9a26b54c931e4 Mon Sep 17 00:00:00 2001 From: Mathias Stang <mathias.stang@gmail.com> Date: Thu, 6 Mar 2025 19:58:39 +0100 Subject: [PATCH 089/300] docs(eslint-plugin-react-hooks): add changelog for 5.1.0 & 5.2.0 (#32536) <!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. Before submitting a pull request, please make sure the following is done: 1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`. 2. Run `yarn` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`. 6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect". 7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`). 8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files. 9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`). 10. If you haven't already, complete the CLA. Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html --> ## Summary <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> Adds changelog entries for the last two minor releases of `eslint-plugin-react-hooks`. Fixes #31717. I chose to not include #31208 (838258144652ab2ef0cbe54d03e9bdd454348d48) and #32115 (fd2d2799840d9066a752bb32bbbb07c93f64a891) in the changelog as they only changed internals that do not affect consumers of the plugin, and it doesn't seem like the changelog previously included such changes. Changes are sorted by importance (rather than by commit date), with the most important changes first. ## How did you test this change? <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. How exactly did you verify that your PR solves the issue you wanted to solve? If you leave this empty, your PR will very likely be closed. --> Docs only, nothing to test. --- packages/eslint-plugin-react-hooks/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/eslint-plugin-react-hooks/CHANGELOG.md b/packages/eslint-plugin-react-hooks/CHANGELOG.md index f894ecc136696..b1e552e434f63 100644 --- a/packages/eslint-plugin-react-hooks/CHANGELOG.md +++ b/packages/eslint-plugin-react-hooks/CHANGELOG.md @@ -1,3 +1,15 @@ +## 5.2.0 + +- Support flat config ([@michaelfaith](https://github.com/michaelfaith) in [#30774](https://github.com/facebook/react/pull/30774)) +- Convert the plugin to TypeScript and provide package type declarations ([@michaelfaith](https://github.com/michaelfaith) in [#32279](https://github.com/facebook/react/pull/32279), [#32283](https://github.com/facebook/react/pull/32283), [#32240](https://github.com/facebook/react/pull/32240), [#32400](https://github.com/facebook/react/pull/32400) and [@poteto](https://github.com/poteto) in [#32420](https://github.com/facebook/react/pull/32420)) +- Fix false positive error in components with `do`/`while` loops ([@tyxla](https://github.com/tyxla) in [#31720](https://github.com/facebook/react/pull/31720)) +- Detect issues in class properties ([@mjesun](https://github.com/mjesun) & [@ecraig12345](https://github.com/ecraig12345) in [#31823](https://github.com/facebook/react/pull/31823)) + +## 5.1.0 + +- Add support for `do`/`while` loops ([@tyxla](https://github.com/tyxla) in [#28714](https://github.com/facebook/react/pull/28714)) +- Fix error when callback argument is an identifier with an `as` expression ([@mskelton](https://github.com/mskelton) in [#31119](https://github.com/facebook/react/pull/31119)) + ## 5.0.0 * **New Violations:** Component names now need to start with an uppercase letter instead of a non-lowercase letter. This means `_Button` or `_component` are no longer valid. ([@kassens](https://github.com/kassens)) in [#25162](https://github.com/facebook/react/pull/25162) From f9d78089c6ec8dce3a11cdf135d6d27b7a8dc1c5 Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Thu, 6 Mar 2025 14:00:12 -0500 Subject: [PATCH 090/300] [flags] make enableComponentPerformanceTrack dynamic (#32359) --- packages/shared/forks/ReactFeatureFlags.www-dynamic.js | 1 + packages/shared/forks/ReactFeatureFlags.www.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index eba6c45aba5bf..3fe3b7a0a3e29 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -39,6 +39,7 @@ export const enableUseEffectCRUDOverload = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = false; export const enableViewTransition = __VARIANT__; +export const enableComponentPerformanceTrack = __VARIANT__; export const enableScrollEndPolyfill = __VARIANT__; // TODO: These flags are hard-coded to the default values used in open source. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 185baf4626fa7..07e7e1f51aa17 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -37,6 +37,7 @@ export const { transitionLaneExpirationMs, enableFastAddPropertiesInDiffing, enableViewTransition, + enableComponentPerformanceTrack, enableScrollEndPolyfill, } = dynamicFeatureFlags; @@ -63,8 +64,6 @@ export const enableThrottledScheduling = false; export const enableHydrationLaneScheduling = true; -export const enableComponentPerformanceTrack = false; - // Logs additional User Timing API marks for use with an experimental profiling tool. export const enableSchedulingProfiler: boolean = __PROFILE__ && dynamicFeatureFlags.enableSchedulingProfiler; From cc680065c33739cc4c8cd2e8a67312b0c16a6ccc Mon Sep 17 00:00:00 2001 From: Nick Lefever <lenaic.lefever@gmail.com> Date: Fri, 7 Mar 2025 18:03:59 +0100 Subject: [PATCH 091/300] Fix asserts caused by OffscreenComponent rendering in React Native with passChildrenWhenCloningPersistedNodes (#32528) <!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. Before submitting a pull request, please make sure the following is done: 1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`. 2. Run `yarn` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`. 6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect". 7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`). 8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files. 9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`). 10. If you haven't already, complete the CLA. Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html --> ## Summary <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> This PR fixes asserts when `passChildrenWhenCloningPersistedNodes` is enabled for React Native and OffscreenComponent child rendering unhides host components. Discussions around possible fixes for the asserts seen in React Native suggested changing the way we handle hiding/unhiding host components by updating the fiber state with the hidden host component instead of submitting a hidden clone Fabric and keeping the original as the current fiber. Implementing this fix would require holding onto the original styling of the hidden host component. The reconciler updates the styling by adding `display: none` to hide the contents. If the original host component was already hidden, the renderer would lose that information and remove the styling when showing the contents again. To reduce the changes required to make `passChildrenWhenCloningPersistedNodes` work, this PR falls back to the original cloning method when OffscreenComponents are part of the children needed to be added back. This effectively resolve the asserts triggered by the feature in RN and improves overall performance. ## How did you test this change? <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. How exactly did you verify that your PR solves the issue you wanted to solve? If you leave this empty, your PR will very likely be closed. --> This fix was tested by enabling `passChildrenWhenCloningPersistedNodes` in an app built with React Native that had a repro for triggering the asserts. The asserts do not occur anymore when using the changes in this PR. --------- Co-authored-by: Nick <lefever@meta.com> --- .../src/ReactFiberCompleteWork.js | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 89012e78cf652..27e4b68134b08 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -341,7 +341,12 @@ function appendAllChildrenToContainer( workInProgress: Fiber, needsVisibilityToggle: boolean, isHidden: boolean, -) { +): boolean { + // Host components that have their visibility toggled by an OffscreenComponent + // do not support passChildrenWhenCloningPersistedNodes. To inform the callee + // about their presence, we track and return if they were added to the + // child set. + let hasOffscreenComponentChild = false; if (supportsPersistence) { // We only have the top Fiber that was created but we need recurse down its // children to find all the terminal nodes. @@ -386,6 +391,8 @@ function appendAllChildrenToContainer( /* needsVisibilityToggle */ _needsVisibilityToggle, /* isHidden */ true, ); + + hasOffscreenComponentChild = true; } else if (node.child !== null) { node.child.return = node; node = node.child; @@ -393,13 +400,13 @@ function appendAllChildrenToContainer( } node = (node: Fiber); if (node === workInProgress) { - return; + return hasOffscreenComponentChild; } // $FlowFixMe[incompatible-use] found when upgrading Flow while (node.sibling === null) { // $FlowFixMe[incompatible-use] found when upgrading Flow if (node.return === null || node.return === workInProgress) { - return; + return hasOffscreenComponentChild; } node = node.return; } @@ -408,6 +415,8 @@ function appendAllChildrenToContainer( node = node.sibling; } } + + return hasOffscreenComponentChild; } function updateHostContainer(current: null | Fiber, workInProgress: Fiber) { @@ -468,11 +477,12 @@ function updateHostComponent( const currentHostContext = getHostContext(); let newChildSet = null; + let hasOffscreenComponentChild = false; if (requiresClone && passChildrenWhenCloningPersistedNodes) { markCloned(workInProgress); newChildSet = createContainerChildSet(); // If children might have changed, we have to add them all to the set. - appendAllChildrenToContainer( + hasOffscreenComponentChild = appendAllChildrenToContainer( newChildSet, workInProgress, /* needsVisibilityToggle */ false, @@ -486,7 +496,7 @@ function updateHostComponent( oldProps, newProps, !requiresClone, - newChildSet, + !hasOffscreenComponentChild ? newChildSet : undefined, ); if (newInstance === currentInstance) { // No changes, just reuse the existing instance. @@ -513,7 +523,10 @@ function updateHostComponent( // Otherwise parents won't know that there are new children to propagate upwards. markUpdate(workInProgress); } - } else if (!passChildrenWhenCloningPersistedNodes) { + } else if ( + !passChildrenWhenCloningPersistedNodes || + hasOffscreenComponentChild + ) { // If children have changed, we have to add them all to the set. appendAllChildren( newInstance, From 00aa0043c7e32e1c822402edadde6f05535d2075 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:41:55 -0500 Subject: [PATCH 092/300] [compiler] Migrate compiler packages to tsup (#32550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently in the `compiler` workspace, we invoke esbuild directly to build most packages (with the exception of `snap`). This has been mostly fine, but does not allow us to do things like generate type declaration files. I would like #32416 to be able to consume the merged eslint-plugin-react-compiler from source rather than via npm, and one of the things that has come up from my exploration in that stack using the compiler from source is that babel-plugin-react-compiler is missing type declarations. This is primarily because React's build process uses rollup + rollup-plugin-typescript, which runs tsc. So the merged plugin needs to typecheck properly in order to build. An alternative might be to migrate to something like babel with rollup instead to simply strip types rather than typecheck before building. The minor downside of that approach is that we would need to manually maintain a d.ts file for eslint-plugin-react-hooks. For now I would like to see if this PR helps us make progress rather than go for the slightly worse alternative. [`tsup`](https://github.com/egoist/tsup) is esbuild based so build performance is comparable. It is slower when generating d.ts files, but it's still much faster than rollup which we used prior to esbuild. For now, I have turned off `dts` by default, and it is only passed when publishing on npm. If you want to also generate d.ts files you can run `yarn build --dts`. ``` # BEFORE: build all compiler packages (esbuild) $ time yarn build ✨ Done in 15.61s. yarn build 13.82s user 1.54s system 96% cpu 15.842 total # --- # AFTER: build all compiler packages (tsup) $ time yarn build ✨ Done in 12.39s. yarn build 12.58s user 1.68s system 106% cpu 13.350 total # --- # AFTER: build all compiler packages and type declarations (tsup) $ time yarn build --dts ✨ Done in 30.69s. yarn build 43.57s user 3.20s system 150% cpu 31.061 total ``` I still need to test if this unblocks #32416 but this stack can be landed independently though as we could probably just release type declarations on npm. No one should be using the compiler directly, but if they really wanted to, lack of type declarations would not stop them (cf React secret internals). Note that I still kept esbuild as we still use it directly for forgive. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32550). * #32551 * __->__ #32550 --- compiler/apps/playground/next-env.d.ts | 2 +- compiler/apps/playground/playwright.config.js | 2 +- compiler/package.json | 1 + .../babel-plugin-react-compiler/package.json | 4 +- .../scripts/build.js | 61 ---- .../src/Entrypoint/Reanimated.ts | 3 + .../babel-plugin-react-compiler/tsconfig.json | 3 - .../tsup.config.ts | 29 ++ .../eslint-plugin-react-compiler/package.json | 6 +- .../scripts/build.js | 67 ---- .../eslint-plugin-react-compiler/src/index.ts | 33 +- .../tsconfig.json | 3 - .../tsup.config.ts | 35 ++ .../packages/make-read-only-util/package.json | 4 +- .../make-read-only-util/scripts/build.js | 61 ---- .../make-read-only-util/tsup.config.ts | 29 ++ .../react-compiler-healthcheck/package.json | 4 +- .../scripts/build.js | 72 ---- .../react-compiler-healthcheck/tsup.config.ts | 40 +++ .../react-compiler-runtime/package.json | 4 +- .../react-compiler-runtime/tsup.config.ts | 30 ++ .../scripts/release/shared/build-packages.js | 2 +- compiler/yarn.lock | 325 +++++++++++++++++- 23 files changed, 521 insertions(+), 299 deletions(-) delete mode 100755 compiler/packages/babel-plugin-react-compiler/scripts/build.js create mode 100644 compiler/packages/babel-plugin-react-compiler/tsup.config.ts delete mode 100755 compiler/packages/eslint-plugin-react-compiler/scripts/build.js create mode 100644 compiler/packages/eslint-plugin-react-compiler/tsup.config.ts delete mode 100755 compiler/packages/make-read-only-util/scripts/build.js create mode 100644 compiler/packages/make-read-only-util/tsup.config.ts delete mode 100755 compiler/packages/react-compiler-healthcheck/scripts/build.js create mode 100644 compiler/packages/react-compiler-healthcheck/tsup.config.ts create mode 100644 compiler/packages/react-compiler-runtime/tsup.config.ts diff --git a/compiler/apps/playground/next-env.d.ts b/compiler/apps/playground/next-env.d.ts index 1b3be0840f3f6..40c3d68096c27 100644 --- a/compiler/apps/playground/next-env.d.ts +++ b/compiler/apps/playground/next-env.d.ts @@ -2,4 +2,4 @@ /// <reference types="next/image-types/global" /> // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/compiler/apps/playground/playwright.config.js b/compiler/apps/playground/playwright.config.js index ca243742f5636..2ef29293d412b 100644 --- a/compiler/apps/playground/playwright.config.js +++ b/compiler/apps/playground/playwright.config.js @@ -23,7 +23,7 @@ export default defineConfig({ // Test directory testDir: path.join(__dirname, '__tests__/e2e'), // If a test fails, retry it additional 2 times - retries: 2, + retries: 3, // Artifacts folder where screenshots, videos, and traces are stored. outputDir: 'test-results/', // Note: we only use text snapshots, so its safe to omit the host environment name diff --git a/compiler/package.json b/compiler/package.json index f1696b9b2a5c5..3d6a0f9c8605e 100644 --- a/compiler/package.json +++ b/compiler/package.json @@ -37,6 +37,7 @@ "prettier-plugin-hermes-parser": "^0.26.0", "prompt-promise": "^1.0.3", "rimraf": "^5.0.10", + "tsup": "^8.4.0", "typescript": "^5.4.3", "wait-on": "^7.2.0", "yargs": "^17.7.2" diff --git a/compiler/packages/babel-plugin-react-compiler/package.json b/compiler/packages/babel-plugin-react-compiler/package.json index b681c6a6c792c..bc49988a6a8e7 100644 --- a/compiler/packages/babel-plugin-react-compiler/package.json +++ b/compiler/packages/babel-plugin-react-compiler/package.json @@ -9,7 +9,7 @@ "!*.tsbuildinfo" ], "scripts": { - "build": "rimraf dist && scripts/build.js", + "build": "rimraf dist && tsup", "test": "./scripts/link-react-compiler-runtime.sh && yarn snap:ci", "jest": "yarn build && ts-node node_modules/.bin/jest", "snap": "node ../snap/dist/main.js", @@ -17,7 +17,7 @@ "snap:ci": "yarn snap:build && yarn snap", "ts:analyze-trace": "scripts/ts-analyze-trace.sh", "lint": "yarn eslint src", - "watch": "scripts/build.js --watch" + "watch": "yarn build --watch" }, "dependencies": { "@babel/types": "^7.19.0" diff --git a/compiler/packages/babel-plugin-react-compiler/scripts/build.js b/compiler/packages/babel-plugin-react-compiler/scripts/build.js deleted file mode 100755 index 5c451f11f99f9..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/scripts/build.js +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env node - -/** - * 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. - */ - -const esbuild = require('esbuild'); -const yargs = require('yargs'); -const path = require('path'); - -const argv = yargs(process.argv.slice(2)) - .options('w', { - alias: 'watch', - default: false, - type: 'boolean', - }) - .parse(); - -const config = { - entryPoints: [path.join(__dirname, '../src/index.ts')], - outfile: path.join(__dirname, '../dist/index.js'), - bundle: true, - external: ['@babel/types'], - format: 'cjs', - platform: 'node', - banner: { - js: `/** - * 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. - * - * @lightSyntaxTransform - * @noflow - * @nolint - * @preventMunge - * @preserve-invariant-messages - */ - -"use no memo";`, - }, -}; - -async function main() { - if (argv.w) { - const ctx = await esbuild.context(config); - await ctx.watch(); - console.log('watching for changes...'); - } else { - await esbuild.build({ - sourcemap: true, - minify: false, - ...config, - }); - } -} - -main(); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Reanimated.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Reanimated.ts index c18a10c0516ba..e946d7aab88c0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Reanimated.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Reanimated.ts @@ -3,6 +3,9 @@ import {hasOwnProperty} from '../Utils/utils'; import {PluginOptions} from './Options'; function hasModule(name: string): boolean { + if (typeof require === 'undefined') { + return false; + } try { return !!require.resolve(name); } catch (error: any) { diff --git a/compiler/packages/babel-plugin-react-compiler/tsconfig.json b/compiler/packages/babel-plugin-react-compiler/tsconfig.json index 568b1f44ea632..a0f0f60e566ad 100644 --- a/compiler/packages/babel-plugin-react-compiler/tsconfig.json +++ b/compiler/packages/babel-plugin-react-compiler/tsconfig.json @@ -5,8 +5,6 @@ "moduleResolution": "Bundler", "rootDir": "src", "outDir": "dist", - // https://github.com/microsoft/TypeScript/issues/30925 - "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", "jsx": "react-jsxdev", // weaken strictness from preset "importsNotUsedAsValues": "remove", @@ -16,7 +14,6 @@ "target": "ES2015", // ideally turn off only during dev, or on a per-file basis "noUnusedLocals": false, - "composite": true, "removeComments": true }, "exclude": [ diff --git a/compiler/packages/babel-plugin-react-compiler/tsup.config.ts b/compiler/packages/babel-plugin-react-compiler/tsup.config.ts new file mode 100644 index 0000000000000..3b255a6a7e957 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/tsup.config.ts @@ -0,0 +1,29 @@ +import {defineConfig} from 'tsup'; + +export default defineConfig({ + entry: ['./src/index.ts'], + outDir: './dist', + external: ['@babel/types'], + splitting: false, + sourcemap: false, + dts: false, + bundle: true, + format: 'cjs', + platform: 'node', + banner: { + js: `/** + * 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. + * + * @lightSyntaxTransform + * @noflow + * @nolint + * @preventMunge + * @preserve-invariant-messages + */ + +"use no memo";`, + }, +}); diff --git a/compiler/packages/eslint-plugin-react-compiler/package.json b/compiler/packages/eslint-plugin-react-compiler/package.json index 1bf80ce963c27..6a3be2b9ebfc9 100644 --- a/compiler/packages/eslint-plugin-react-compiler/package.json +++ b/compiler/packages/eslint-plugin-react-compiler/package.json @@ -4,9 +4,9 @@ "description": "ESLint plugin to display errors found by the React compiler.", "main": "dist/index.js", "scripts": { - "build": "rimraf dist && scripts/build.js", - "test": "tsc && jest", - "watch": "scripts/build.js --watch" + "build": "rimraf dist && tsup", + "test": "jest", + "watch": "yarn build --watch" }, "files": [ "dist" diff --git a/compiler/packages/eslint-plugin-react-compiler/scripts/build.js b/compiler/packages/eslint-plugin-react-compiler/scripts/build.js deleted file mode 100755 index d201f38b89570..0000000000000 --- a/compiler/packages/eslint-plugin-react-compiler/scripts/build.js +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env node - -/** - * 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. - */ - -const esbuild = require('esbuild'); -const yargs = require('yargs'); -const path = require('path'); - -const argv = yargs(process.argv.slice(2)) - .options('w', { - alias: 'watch', - default: false, - type: 'boolean', - }) - .parse(); - -const config = { - entryPoints: [path.join(__dirname, '../src/index.ts')], - outfile: path.join(__dirname, '../dist/index.js'), - bundle: true, - external: [ - '@babel/core', - '@babel/plugin-proposal-private-methods', - 'hermes-parser', - 'zod', - 'zod-validation-error', - ], - format: 'cjs', - platform: 'node', - banner: { - js: `/** - * 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. - * - * @lightSyntaxTransform - * @noflow - * @nolint - * @preventMunge - * @preserve-invariant-messages - */ - -"use no memo";`, - }, -}; - -async function main() { - if (argv.w) { - const ctx = await esbuild.context(config); - await ctx.watch(); - console.log('watching for changes...'); - } else { - await esbuild.build({ - sourcemap: true, - minify: false, - ...config, - }); - } -} - -main(); diff --git a/compiler/packages/eslint-plugin-react-compiler/src/index.ts b/compiler/packages/eslint-plugin-react-compiler/src/index.ts index 103cdbbbd3245..0dd3679d560e0 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/index.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/index.ts @@ -7,22 +7,27 @@ import ReactCompilerRule from './rules/ReactCompilerRule'; -module.exports = { - rules: { - 'react-compiler': ReactCompilerRule, - }, - configs: { - recommended: { - plugins: { - 'react-compiler': { - rules: { - 'react-compiler': ReactCompilerRule, - }, +const meta = { + name: 'eslint-plugin-react-compiler', +}; + +const rules = { + 'react-compiler': ReactCompilerRule, +}; + +const configs = { + recommended: { + plugins: { + 'react-compiler': { + rules: { + 'react-compiler': ReactCompilerRule, }, }, - rules: { - 'react-compiler/react-compiler': 'error', - }, + }, + rules: { + 'react-compiler/react-compiler': 'error', }, }, }; + +export {configs, rules, meta}; diff --git a/compiler/packages/eslint-plugin-react-compiler/tsconfig.json b/compiler/packages/eslint-plugin-react-compiler/tsconfig.json index f702a7e21f0da..bbc891773f1fe 100644 --- a/compiler/packages/eslint-plugin-react-compiler/tsconfig.json +++ b/compiler/packages/eslint-plugin-react-compiler/tsconfig.json @@ -6,9 +6,6 @@ "rootDir": "../", "noEmit": true, "jsx": "react-jsxdev", - "paths": { - "*": ["./src/types/*"] - }, // weaken strictness from preset "importsNotUsedAsValues": "remove", diff --git a/compiler/packages/eslint-plugin-react-compiler/tsup.config.ts b/compiler/packages/eslint-plugin-react-compiler/tsup.config.ts new file mode 100644 index 0000000000000..ac13d33bba5b3 --- /dev/null +++ b/compiler/packages/eslint-plugin-react-compiler/tsup.config.ts @@ -0,0 +1,35 @@ +import {defineConfig} from 'tsup'; + +export default defineConfig({ + entry: ['./src/index.ts'], + outDir: './dist', + external: [ + '@babel/core', + '@babel/plugin-proposal-private-methods', + 'hermes-parser', + 'zod', + 'zod-validation-error', + ], + splitting: false, + sourcemap: false, + dts: false, + bundle: true, + format: 'cjs', + platform: 'node', + banner: { + js: `/** + * 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. + * + * @lightSyntaxTransform + * @noflow + * @nolint + * @preventMunge + * @preserve-invariant-messages + */ + +"use no memo";`, + }, +}); diff --git a/compiler/packages/make-read-only-util/package.json b/compiler/packages/make-read-only-util/package.json index 59293c1771c0b..20a4643cd5299 100644 --- a/compiler/packages/make-read-only-util/package.json +++ b/compiler/packages/make-read-only-util/package.json @@ -6,9 +6,9 @@ "src" ], "scripts": { - "build": "rimraf dist && scripts/build.js", + "build": "rimraf dist && tsup", "test": "jest src", - "watch": "scripts/build.js --watch" + "watch": "yarn build --watch" }, "dependencies": { "invariant": "^2.2.4", diff --git a/compiler/packages/make-read-only-util/scripts/build.js b/compiler/packages/make-read-only-util/scripts/build.js deleted file mode 100755 index 91301c3b6a0f2..0000000000000 --- a/compiler/packages/make-read-only-util/scripts/build.js +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env node - -/** - * 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. - */ - -const esbuild = require('esbuild'); -const yargs = require('yargs'); -const path = require('path'); - -const argv = yargs(process.argv.slice(2)) - .options('w', { - alias: 'watch', - default: false, - type: 'boolean', - }) - .parse(); - -const config = { - entryPoints: [path.join(__dirname, '../src/makeReadOnly.ts')], - outfile: path.join(__dirname, '../dist/index.js'), - bundle: true, - format: 'esm', - platform: 'browser', - target: 'es6', - banner: { - js: `/** - * 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. - * - * @lightSyntaxTransform - * @noflow - * @nolint - * @preventMunge - * @preserve-invariant-messages - */ - -"use no memo";`, - }, -}; - -async function main() { - if (argv.w) { - const ctx = await esbuild.context(config); - await ctx.watch(); - console.log('watching for changes...'); - } else { - await esbuild.build({ - sourcemap: true, - minify: false, - ...config, - }); - } -} - -main(); diff --git a/compiler/packages/make-read-only-util/tsup.config.ts b/compiler/packages/make-read-only-util/tsup.config.ts new file mode 100644 index 0000000000000..cb65a61aaa319 --- /dev/null +++ b/compiler/packages/make-read-only-util/tsup.config.ts @@ -0,0 +1,29 @@ +import {defineConfig} from 'tsup'; + +export default defineConfig({ + entry: ['./src/makeReadOnly.ts'], + outDir: './dist', + splitting: false, + sourcemap: true, + dts: false, + bundle: true, + format: 'cjs', + platform: 'browser', + target: 'es2015', + banner: { + js: `/** + * 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. + * + * @lightSyntaxTransform + * @noflow + * @nolint + * @preventMunge + * @preserve-invariant-messages + */ + +"use no memo";`, + }, +}); diff --git a/compiler/packages/react-compiler-healthcheck/package.json b/compiler/packages/react-compiler-healthcheck/package.json index 44a52151bbbb4..2f6d625eb837d 100644 --- a/compiler/packages/react-compiler-healthcheck/package.json +++ b/compiler/packages/react-compiler-healthcheck/package.json @@ -6,9 +6,9 @@ "react-compiler-healthcheck": "dist/index.js" }, "scripts": { - "build": "rimraf dist && scripts/build.js", + "build": "rimraf dist && tsup", "test": "echo 'no tests'", - "watch": "scripts/build.js --watch" + "watch": "yarn build --watch" }, "dependencies": { "@babel/core": "^7.24.4", diff --git a/compiler/packages/react-compiler-healthcheck/scripts/build.js b/compiler/packages/react-compiler-healthcheck/scripts/build.js deleted file mode 100755 index e7b36fd1cd008..0000000000000 --- a/compiler/packages/react-compiler-healthcheck/scripts/build.js +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env node - -/** - * 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. - */ - -const esbuild = require('esbuild'); -const yargs = require('yargs'); -const path = require('path'); - -const argv = yargs(process.argv.slice(2)) - .options('w', { - alias: 'watch', - default: false, - type: 'boolean', - }) - .parse(); - -const config = { - entryPoints: [path.join(__dirname, '../src/index.ts')], - outfile: path.join(__dirname, '../dist/index.js'), - bundle: true, - external: [ - '@babel/core', - '@babel/parser', - 'chalk', - 'fast-glob', - 'ora', - 'yargs', - 'zod', - 'zod-validation-error', - ], - format: 'cjs', - platform: 'node', - banner: { - js: `#!/usr/bin/env node - -/** - * 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. - * - * @lightSyntaxTransform - * @noflow - * @nolint - * @preventMunge - * @preserve-invariant-messages - */ - -"use no memo";`, - }, -}; - -async function main() { - if (argv.w) { - const ctx = await esbuild.context(config); - await ctx.watch(); - console.log('watching for changes...'); - } else { - await esbuild.build({ - sourcemap: true, - minify: false, - ...config, - }); - } -} - -main(); diff --git a/compiler/packages/react-compiler-healthcheck/tsup.config.ts b/compiler/packages/react-compiler-healthcheck/tsup.config.ts new file mode 100644 index 0000000000000..9fe1e493dbf18 --- /dev/null +++ b/compiler/packages/react-compiler-healthcheck/tsup.config.ts @@ -0,0 +1,40 @@ +import {defineConfig} from 'tsup'; + +export default defineConfig({ + entry: ['./src/index.ts'], + outDir: './dist', + external: [ + '@babel/core', + '@babel/parser', + 'chalk', + 'fast-glob', + 'ora', + 'yargs', + 'zod', + 'zod-validation-error', + ], + splitting: false, + sourcemap: false, + dts: false, + bundle: true, + format: 'cjs', + platform: 'node', + banner: { + js: `#!/usr/bin/env node + +/** + * 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. + * + * @lightSyntaxTransform + * @noflow + * @nolint + * @preventMunge + * @preserve-invariant-messages + */ + +"use no memo";`, + }, +}); diff --git a/compiler/packages/react-compiler-runtime/package.json b/compiler/packages/react-compiler-runtime/package.json index c8bbc47345870..60a192b0a7ca8 100644 --- a/compiler/packages/react-compiler-runtime/package.json +++ b/compiler/packages/react-compiler-runtime/package.json @@ -13,9 +13,9 @@ "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" }, "scripts": { - "build": "rimraf dist && scripts/build.js", + "build": "rimraf dist && tsup", "test": "echo 'no tests'", - "watch": "scripts/build.js --watch" + "watch": "yarn build --watch" }, "repository": { "type": "git", diff --git a/compiler/packages/react-compiler-runtime/tsup.config.ts b/compiler/packages/react-compiler-runtime/tsup.config.ts new file mode 100644 index 0000000000000..6ebe42e089820 --- /dev/null +++ b/compiler/packages/react-compiler-runtime/tsup.config.ts @@ -0,0 +1,30 @@ +import {defineConfig} from 'tsup'; + +export default defineConfig({ + entry: ['./src/index.ts'], + outDir: './dist', + external: ['react'], + splitting: false, + sourcemap: true, + dts: false, + bundle: true, + format: 'cjs', + platform: 'browser', + target: 'es2015', + banner: { + js: `/** + * 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. + * + * @lightSyntaxTransform + * @noflow + * @nolint + * @preventMunge + * @preserve-invariant-messages + */ + +"use no memo";`, + }, +}); diff --git a/compiler/scripts/release/shared/build-packages.js b/compiler/scripts/release/shared/build-packages.js index 4152f61271104..21c88e3aae11e 100644 --- a/compiler/scripts/release/shared/build-packages.js +++ b/compiler/scripts/release/shared/build-packages.js @@ -4,7 +4,7 @@ const {execHelper} = require('./utils'); async function buildPackages(pkgNames) { const spinner = ora(`Building packages`).info(); for (const pkgName of pkgNames) { - const command = `NODE_ENV=production yarn workspace ${pkgName} run build`; + const command = `NODE_ENV=production yarn workspace ${pkgName} run build --dts`; spinner.start(`Running: ${command}\n`); try { await execHelper(command); diff --git a/compiler/yarn.lock b/compiler/yarn.lock index 9c25b35b34b81..de5cd4d228b46 100644 --- a/compiler/yarn.lock +++ b/compiler/yarn.lock @@ -2617,6 +2617,15 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -2719,6 +2728,101 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@rollup/rollup-android-arm-eabi@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz#661a45a4709c70e59e596ec78daa9cb8b8d27604" + integrity sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA== + +"@rollup/rollup-android-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz#128fe8dd510d880cf98b4cb6c7add326815a0c4b" + integrity sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg== + +"@rollup/rollup-darwin-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz#363467bc49fd0b1e17075798ac8e9ad1e1e29535" + integrity sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ== + +"@rollup/rollup-darwin-x64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz#c2fe3d85fffe47f0ed0f076b3563ada22c8af19c" + integrity sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q== + +"@rollup/rollup-freebsd-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz#d95bd8f6eaaf829781144fc8bd2d5d71d9f6a9f5" + integrity sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw== + +"@rollup/rollup-freebsd-x64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz#c3576c6011656e4966ded29f051edec636b44564" + integrity sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz#48c87d0dee4f8dc9591a416717f91b4a89d77e3d" + integrity sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg== + +"@rollup/rollup-linux-arm-musleabihf@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz#f4c4e7c03a7767f2e5aa9d0c5cfbf5c0f59f2d41" + integrity sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA== + +"@rollup/rollup-linux-arm64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz#1015c9d07a99005025d13b8622b7600029d0b52f" + integrity sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw== + +"@rollup/rollup-linux-arm64-musl@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz#8f895eb5577748fc75af21beae32439626e0a14c" + integrity sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz#c9cd5dbbdc6b3ca4dbeeb0337498cf31949004a0" + integrity sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz#7ebb5b4441faa17843a210f7d0583a20c93b40e4" + integrity sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA== + +"@rollup/rollup-linux-riscv64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz#10f5d7349fbd2fe78f9e36ecc90aab3154435c8d" + integrity sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg== + +"@rollup/rollup-linux-s390x-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz#196347d2fa20593ab09d0b7e2589fb69bdf742c6" + integrity sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ== + +"@rollup/rollup-linux-x64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz#7193cbd8d128212b8acda37e01b39d9e96259ef8" + integrity sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A== + +"@rollup/rollup-linux-x64-musl@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz#29a6867278ca0420b891574cfab98ecad70c59d1" + integrity sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA== + +"@rollup/rollup-win32-arm64-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz#89427dcac0c8e3a6d32b13a03a296a275d0de9a9" + integrity sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q== + +"@rollup/rollup-win32-ia32-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz#ecb9711ba2b6d2bf6ee51265abe057ab90913deb" + integrity sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w== + +"@rollup/rollup-win32-x64-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz#1973871850856ae72bc678aeb066ab952330e923" + integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw== + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -2888,7 +2992,7 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/estree@^1.0.6": +"@types/estree@1.0.6", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -3479,6 +3583,11 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -3864,6 +3973,13 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +bundle-require@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-5.1.0.tgz#8db66f41950da3d77af1ef3322f4c3e04009faee" + integrity sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA== + dependencies: + load-tsconfig "^0.2.3" + c8@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/c8/-/c8-9.1.0.tgz#0e57ba3ab9e5960ab1d650b4a86f71e53cb68112" @@ -3881,6 +3997,11 @@ c8@^9.1.0: yargs "^17.7.2" yargs-parser "^21.1.1" +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -3961,6 +4082,13 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chokidar@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + ci-info@^3.2.0: version "3.4.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.4.0.tgz#b28484fd436cbc267900364f096c9dc185efb251" @@ -4108,6 +4236,11 @@ commander@^2.9.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -4133,6 +4266,11 @@ concurrently@^7.4.0: tree-kill "^1.2.2" yargs "^17.3.1" +consola@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.0.tgz#4cfc9348fd85ed16a17940b3032765e31061ab88" + integrity sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA== + convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -4241,7 +4379,7 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, d dependencies: ms "2.1.2" -debug@^4.3.5: +debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -4870,6 +5008,11 @@ fbt@^1.0.2: dependencies: invariant "^2.2.4" +fdir@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" + integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== + fecha@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" @@ -6716,6 +6859,11 @@ joi@^17.11.0: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6924,11 +7072,21 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" +lilconfig@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + 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== +load-tsconfig@^0.2.3: + version "0.2.5" + resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" + integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -6966,6 +7124,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + lodash@^4.17.10, lodash@^4.17.13, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -7224,6 +7387,15 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + native-or-another@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/native-or-another/-/native-or-another-2.0.0.tgz#17a567f92beea9cd71acff96a7681a735eca3bff" @@ -7332,7 +7504,7 @@ nwsapi@^2.2.4: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== -object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -7564,7 +7736,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picocolors@^1.1.0: +picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -7574,11 +7746,21 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -7598,6 +7780,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +postcss-load-config@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" + integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== + dependencies: + lilconfig "^3.1.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -7791,6 +7980,11 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -7947,6 +8141,34 @@ rimraf@5.0.10, rimraf@6.0.1, rimraf@^3.0.0, rimraf@^3.0.2, rimraf@^5.0.10: dependencies: glob "^10.3.7" +rollup@^4.34.8: + version "4.34.9" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.9.tgz#e1eb397856476778aeb6ac2ac3d09b2ce177a558" + integrity sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.34.9" + "@rollup/rollup-android-arm64" "4.34.9" + "@rollup/rollup-darwin-arm64" "4.34.9" + "@rollup/rollup-darwin-x64" "4.34.9" + "@rollup/rollup-freebsd-arm64" "4.34.9" + "@rollup/rollup-freebsd-x64" "4.34.9" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.9" + "@rollup/rollup-linux-arm-musleabihf" "4.34.9" + "@rollup/rollup-linux-arm64-gnu" "4.34.9" + "@rollup/rollup-linux-arm64-musl" "4.34.9" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.9" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.9" + "@rollup/rollup-linux-riscv64-gnu" "4.34.9" + "@rollup/rollup-linux-s390x-gnu" "4.34.9" + "@rollup/rollup-linux-x64-gnu" "4.34.9" + "@rollup/rollup-linux-x64-musl" "4.34.9" + "@rollup/rollup-win32-arm64-msvc" "4.34.9" + "@rollup/rollup-win32-ia32-msvc" "4.34.9" + "@rollup/rollup-win32-x64-msvc" "4.34.9" + fsevents "~2.3.2" + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -8123,6 +8345,13 @@ source-map-support@^0.5.16: buffer-from "^1.0.0" source-map "^0.6.0" +source-map@0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -8256,6 +8485,19 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +sucrase@^3.35.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -8332,6 +8574,33 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.11: + version "0.2.12" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5" + integrity sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww== + dependencies: + fdir "^6.4.3" + picomatch "^4.0.2" + tmp@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" @@ -8364,6 +8633,13 @@ tough-cookie@^4.0.0, tough-cookie@^4.1.2: universalify "^0.2.0" url-parse "^1.5.3" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -8403,6 +8679,11 @@ ts-api-utils@^1.3.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + ts-jest@^28.0.7: version "28.0.8" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.8.tgz#cd204b8e7a2f78da32cf6c95c9a6165c5b99cc73" @@ -8455,6 +8736,28 @@ tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== +tsup@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/tsup/-/tsup-8.4.0.tgz#2fdf537e7abc8f1ccbbbfe4228f16831457d4395" + integrity sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ== + dependencies: + bundle-require "^5.1.0" + cac "^6.7.14" + chokidar "^4.0.3" + consola "^3.4.0" + debug "^4.4.0" + esbuild "^0.25.0" + joycon "^3.1.1" + picocolors "^1.1.1" + postcss-load-config "^6.0.1" + resolve-from "^5.0.0" + rollup "^4.34.8" + source-map "0.8.0-beta.0" + sucrase "^3.35.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.11" + tree-kill "^1.2.2" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -8641,6 +8944,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -8674,6 +8982,15 @@ whatwg-url@^12.0.0, whatwg-url@^12.0.1: tr46 "^4.1.1" webidl-conversions "^7.0.0" +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" From d331ba041142a801f8e2101408221732b0ee7f88 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:43:38 -0500 Subject: [PATCH 093/300] [ci] Fix incorrect condition (#32551) Fixes an incorrect condition for running tests in the compiler workspace. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32551). * __->__ #32551 * #32550 --- .github/workflows/compiler_typescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index 65fb789eccf17..d3b9517c8ef6e 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -96,4 +96,4 @@ jobs: - run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive' - run: yarn workspace ${{ matrix.workspace_name }} test - if: runner.os != 'Linux' && matrix.workspace_name != 'react-forgive' + if: matrix.workspace_name != 'react-forgive' From 50ab2dde940bf0027773a944da005277b3d5598a Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Mon, 10 Mar 2025 15:24:09 -0400 Subject: [PATCH 094/300] Make renameElementSymbol dynamic for native fb (#32566) Use variant to begin rolling this out internally. --- packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js | 1 + packages/shared/forks/ReactFeatureFlags.native-fb.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 6825b2b608b9b..7711c842691cb 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -28,3 +28,4 @@ export const enableSiblingPrerendering = __VARIANT__; export const enableUseEffectCRUDOverload = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = __VARIANT__; +export const renameElementSymbol = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index f996274b86a18..08c5781345ff7 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -30,6 +30,7 @@ export const { enableSiblingPrerendering, enableFastAddPropertiesInDiffing, enableLazyPublicInstanceInFabric, + renameElementSymbol, } = dynamicFlags; // The rest of the flags are static for better dead code elimination. @@ -73,7 +74,6 @@ export const enableTrustedTypesIntegration = false; export const enableUpdaterTracking = __PROFILE__; export const enableUseEffectEventHook = false; export const favorSafetyOverHydrationPerf = true; -export const renameElementSymbol = false; export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; From a8c2bbdabf561a53b7908c2b341815e64840e8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Mon, 10 Mar 2025 15:27:37 -0400 Subject: [PATCH 095/300] Cancel finished view transitions Animations manually in fire-and-forget too (#32545) Otherwise these can survive into the next View Transition and cause havoc to that transition. This was appearing as a flash in Safari in the fixture when going from A->B. This triggers a View Transition and at the same time the scroll position updates in an effect. That fires a scroll event which starts a gesture. This shouldn't really happen and the SwipeRecognizer should ideally ignore those but it's good to surface edge cases. That gesture is blocked on the View Transition finishing and then immediately after it starts a gesture View Transition. That gesture then picked up the former Animation from the previous transition which caused issues. This PR fixes that flash. --- .../src/client/ReactFiberConfigDOM.js | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 47d6a4cb24806..f4778617ea69b 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1550,6 +1550,25 @@ export function hasInstanceAffectedParent( return oldRect.height !== newRect.height || oldRect.width !== newRect.width; } +function cancelAllViewTransitionAnimations(scope: Element) { + // In Safari, we need to manually cancel all manually start animations + // or it'll block or interfer with future transitions. + const animations = scope.getAnimations({subtree: true}); + for (let i = 0; i < animations.length; i++) { + const anim = animations[i]; + const effect: KeyframeEffect = (anim.effect: any); + // $FlowFixMe + const pseudo: ?string = effect.pseudoElement; + if ( + pseudo != null && + pseudo.startsWith('::view-transition') && + effect.target === scope + ) { + anim.cancel(); + } + } +} + // How long to wait for new fonts to load before just committing anyway. // This freezes the screen. It needs to be short enough that it doesn't cause too much of // an issue when it's a new load and slow, yet long enough that you have a chance to load @@ -1640,6 +1659,7 @@ export function startViewTransition( } transition.ready.then(spawnedWorkCallback, spawnedWorkCallback); transition.finished.then(() => { + cancelAllViewTransitionAnimations((ownerDocument.documentElement: any)); // $FlowFixMe[prop-missing] if (ownerDocument.__reactViewTransition === transition) { // $FlowFixMe[prop-missing] @@ -1817,12 +1837,16 @@ export function startGestureTransition( } for (let i = 0; i < animations.length; i++) { const anim = animations[i]; + if (anim.playState !== 'running') { + continue; + } const effect: KeyframeEffect = (anim.effect: any); // $FlowFixMe const pseudoElement: ?string = effect.pseudoElement; if ( pseudoElement != null && - pseudoElement.startsWith('::view-transition') + pseudoElement.startsWith('::view-transition') && + effect.target === documentElement ) { // Ideally we could mutate the existing animation but unfortunately // the mutable APIs seem less tested and therefore are lacking or buggy. @@ -1913,19 +1937,7 @@ export function startGestureTransition( : readyCallback; transition.ready.then(readyForAnimations, readyCallback); transition.finished.then(() => { - // In Safari, we need to manually cancel all manually start animations - // or it'll block future transitions. - const documentElement: Element = (ownerDocument.documentElement: any); - const animations = documentElement.getAnimations({subtree: true}); - for (let i = 0; i < animations.length; i++) { - const anim = animations[i]; - const effect: KeyframeEffect = (anim.effect: any); - // $FlowFixMe - const pseudo: ?string = effect.pseudoElement; - if (pseudo != null && pseudo.startsWith('::view-transition')) { - anim.cancel(); - } - } + cancelAllViewTransitionAnimations((ownerDocument.documentElement: any)); // $FlowFixMe[prop-missing] if (ownerDocument.__reactViewTransition === transition) { // $FlowFixMe[prop-missing] From 696950aa69e3f2ef0d720c82705e02b532904d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Mon, 10 Mar 2025 15:27:46 -0400 Subject: [PATCH 096/300] Enable moveBefore in experimental releases (#32549) Enabling feature detection of early DOM features in a framework is reckless. I'm not judging other frameworks (but also a little bit). Because if you do something like `if (moveBefore) moveBefore(a, b) else insertBefore(a, b)` like we do and then the implementation has to change there are still too many websites out there that it becomes impossible to change it. It would break the web. It would instead have to change to a different name. That's what happened with `contains` -> `includes`. Counter to popular belief it didn't have anything to do with patching prototypes. Therefore, ideally frameworks shouldn't start rely on it until there's two implementations so that there's time for feedback. That's why we didn't immediately enable this even in experimental. However, at this point there's probably enough feature detection and it has shipped long enough in Chrome that it's unlikely to be able to change at this point. We can enable it now. For now just in `@experimental` to see if we can flush out issues with it before bringing it to stable. --- packages/shared/ReactFeatureFlags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 5588004bdf278..17b037cc043e8 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -193,7 +193,7 @@ export const disableLegacyContext = true; export const disableLegacyContextForFunctionComponents = true; // Enable the moveBefore() alternative to insertBefore(). This preserves states of moves. -export const enableMoveBefore = false; +export const enableMoveBefore = __EXPERIMENTAL__; // Disabled caching behavior of `react/cache` in client runtimes. export const disableClientCache = true; From 99e1024051f2e6b2d2849b966e2f4354aef2a1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Mon, 10 Mar 2025 18:12:43 -0400 Subject: [PATCH 097/300] Check if a child is a new child before calling moveBefore (#32567) This fixes a critical issue with moveBefore. I was told that the disconnected -> connected case was going to be relaxed and not be an error but apparently that is not the case. This means that we can't use this for initial insertions. Only moves. Unfortunately React's internals doesn't distinguish these cases. This adds a hack that checks each nodes but this is pretty bad for performance. We should only call this in one or the other case. Given that we still need feature detection. Both of which means that these calls are no longer inlined and this extra code. I wonder if it's even worth it given that you can't even rely on it working anyway since not all browsers have it. Kind of don't want to ship this until all browsers have it. Even then we'd ideally refactor React to use separate code paths for initial insertion vs moves. Which leads to some unfortunate code duplication. --- .../src/client/ReactFiberConfigDOM.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index f4778617ea69b..bbbc836f962b3 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -808,7 +808,7 @@ export function appendChild( parentInstance: Instance, child: Instance | TextInstance, ): void { - if (supportsMoveBefore) { + if (supportsMoveBefore && child.parentNode !== null) { // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore. parentInstance.moveBefore(child, null); } else { @@ -828,7 +828,7 @@ export function appendChildToContainer( container.nodeType === COMMENT_NODE ) { parentNode = (container.parentNode: any); - if (supportsMoveBefore) { + if (supportsMoveBefore && child.parentNode !== null) { // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore. parentNode.moveBefore(child, container); } else { @@ -840,7 +840,7 @@ export function appendChildToContainer( } else { parentNode = (container: any); } - if (supportsMoveBefore) { + if (supportsMoveBefore && child.parentNode !== null) { // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore. parentNode.moveBefore(child, null); } else { @@ -870,7 +870,7 @@ export function insertBefore( child: Instance | TextInstance, beforeChild: Instance | TextInstance | SuspenseInstance, ): void { - if (supportsMoveBefore) { + if (supportsMoveBefore && child.parentNode !== null) { // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore. parentInstance.moveBefore(child, beforeChild); } else { @@ -896,7 +896,7 @@ export function insertInContainerBefore( } else { parentNode = (container: any); } - if (supportsMoveBefore) { + if (supportsMoveBefore && child.parentNode !== null) { // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore. parentNode.moveBefore(child, beforeChild); } else { From 0ca3deebcf20d2514771a568e1be08801da5cf85 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Tue, 11 Mar 2025 10:41:48 -0400 Subject: [PATCH 098/300] [rcr] Fix incorrect output platform (#32569) Accidentally copypasted the wrong esbuild config. --- .../react-compiler-runtime/scripts/build.js | 67 ------------------- .../react-compiler-runtime/tsup.config.ts | 2 +- 2 files changed, 1 insertion(+), 68 deletions(-) delete mode 100755 compiler/packages/react-compiler-runtime/scripts/build.js diff --git a/compiler/packages/react-compiler-runtime/scripts/build.js b/compiler/packages/react-compiler-runtime/scripts/build.js deleted file mode 100755 index d1c7dd5456e13..0000000000000 --- a/compiler/packages/react-compiler-runtime/scripts/build.js +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env node - -/** - * 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. - */ - -const esbuild = require('esbuild'); -const yargs = require('yargs'); -const path = require('path'); -const {Generator} = require('npm-dts'); - -const argv = yargs(process.argv.slice(2)) - .options('w', { - alias: 'watch', - default: false, - type: 'boolean', - }) - .parse(); - -const config = { - entryPoints: [path.join(__dirname, '../src/index.ts')], - outfile: path.join(__dirname, '../dist/index.js'), - bundle: true, - external: ['react'], - format: 'cjs', - platform: 'node', - target: 'es6', - banner: { - js: `/** - * 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. - * - * @lightSyntaxTransform - * @noflow - * @nolint - * @preventMunge - * @preserve-invariant-messages - */ - -"use no memo";`, - }, -}; - -async function main() { - if (argv.w) { - const ctx = await esbuild.context(config); - await ctx.watch(); - console.log('watching for changes...'); - } else { - await esbuild.build({ - sourcemap: true, - minify: false, - ...config, - }); - await new Generator({ - entry: 'src/index.ts', - output: 'dist/index.d.ts', - }).generate(); - } -} - -main(); diff --git a/compiler/packages/react-compiler-runtime/tsup.config.ts b/compiler/packages/react-compiler-runtime/tsup.config.ts index 6ebe42e089820..ebc8df6f14e67 100644 --- a/compiler/packages/react-compiler-runtime/tsup.config.ts +++ b/compiler/packages/react-compiler-runtime/tsup.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ dts: false, bundle: true, format: 'cjs', - platform: 'browser', + platform: 'node', target: 'es2015', banner: { js: `/** From 2398554c6034e6d0992fcaa1c2e95f1757cab53e Mon Sep 17 00:00:00 2001 From: Hendrik Liebau <mail@hendrik-liebau.de> Date: Tue, 11 Mar 2025 22:15:38 +0100 Subject: [PATCH 099/300] [Flight]: Client-side `registerServerReference` must not break `.bind()` (#32565) --- .../src/ReactFlightReplyClient.js | 148 ++++++++++-------- .../src/__tests__/ReactFlightDOMEdge-test.js | 7 + 2 files changed, 94 insertions(+), 61 deletions(-) diff --git a/packages/react-client/src/ReactFlightReplyClient.js b/packages/react-client/src/ReactFlightReplyClient.js index 3fa37cd00c788..e3c4ec20ba1ab 100644 --- a/packages/react-client/src/ReactFlightReplyClient.js +++ b/packages/react-client/src/ReactFlightReplyClient.js @@ -63,10 +63,14 @@ export type EncodeFormActionCallback = <A>( export type ServerReferenceId = any; -const knownServerReferences: WeakMap< - Function, - {id: ServerReferenceId, bound: null | Thenable<Array<any>>}, -> = new WeakMap(); +type ServerReferenceClosure = { + id: ServerReferenceId, + originalBind: Function, + bound: null | Thenable<Array<any>>, +}; + +const knownServerReferences: WeakMap<Function, ServerReferenceClosure> = + new WeakMap(); // Serializable values export type ReactServerValue = @@ -760,16 +764,17 @@ export function processReply( } if (typeof value === 'function') { - const metaData = knownServerReferences.get(value); - if (metaData !== undefined) { - const metaDataJSON = JSON.stringify(metaData, resolveToJSON); + const referenceClosure = knownServerReferences.get(value); + if (referenceClosure !== undefined) { + const {id, bound} = referenceClosure; + const referenceClosureJSON = JSON.stringify({id, bound}, resolveToJSON); if (formData === null) { // Upgrade to use FormData to allow us to stream this value. formData = new FormData(); } // The reference to this function came from the same client so we can pass it back. const refId = nextPartId++; - formData.set(formFieldPrefix + refId, metaDataJSON); + formData.set(formFieldPrefix + refId, referenceClosureJSON); return serializeServerReferenceID(refId); } if (temporaryReferences !== undefined && key.indexOf(':') === -1) { @@ -864,7 +869,7 @@ export function processReply( } const boundCache: WeakMap< - {id: ServerReferenceId, bound: null | Thenable<Array<any>>}, + ServerReferenceClosure, Thenable<FormData>, > = new WeakMap(); @@ -905,8 +910,8 @@ function defaultEncodeFormAction( this: any => Promise<any>, identifierPrefix: string, ): ReactCustomFormAction { - const reference = knownServerReferences.get(this); - if (!reference) { + const referenceClosure = knownServerReferences.get(this); + if (!referenceClosure) { throw new Error( 'Tried to encode a Server Action from a different instance than the encoder is from. ' + 'This is a bug in React.', @@ -914,12 +919,13 @@ function defaultEncodeFormAction( } let data: null | FormData = null; let name; - const boundPromise = reference.bound; + const boundPromise = referenceClosure.bound; if (boundPromise !== null) { - let thenable = boundCache.get(reference); + let thenable = boundCache.get(referenceClosure); if (!thenable) { - thenable = encodeFormData(reference); - boundCache.set(reference, thenable); + const {id, bound} = referenceClosure; + thenable = encodeFormData({id, bound}); + boundCache.set(referenceClosure, thenable); } if (thenable.status === 'rejected') { throw thenable.reason; @@ -941,7 +947,7 @@ function defaultEncodeFormAction( name = '$ACTION_REF_' + identifierPrefix; } else { // This is the simple case so we can just encode the ID. - name = '$ACTION_ID_' + reference.id; + name = '$ACTION_ID_' + referenceClosure.id; } return { name: name, @@ -952,22 +958,22 @@ function defaultEncodeFormAction( } function customEncodeFormAction( - proxy: any => Promise<any>, + reference: any => Promise<any>, identifierPrefix: string, encodeFormAction: EncodeFormActionCallback, ): ReactCustomFormAction { - const reference = knownServerReferences.get(proxy); - if (!reference) { + const referenceClosure = knownServerReferences.get(reference); + if (!referenceClosure) { throw new Error( 'Tried to encode a Server Action from a different instance than the encoder is from. ' + 'This is a bug in React.', ); } - let boundPromise: Promise<Array<any>> = (reference.bound: any); + let boundPromise: Promise<Array<any>> = (referenceClosure.bound: any); if (boundPromise === null) { boundPromise = Promise.resolve([]); } - return encodeFormAction(reference.id, boundPromise); + return encodeFormAction(referenceClosure.id, boundPromise); } function isSignatureEqual( @@ -975,19 +981,19 @@ function isSignatureEqual( referenceId: ServerReferenceId, numberOfBoundArgs: number, ): boolean { - const reference = knownServerReferences.get(this); - if (!reference) { + const referenceClosure = knownServerReferences.get(this); + if (!referenceClosure) { throw new Error( 'Tried to encode a Server Action from a different instance than the encoder is from. ' + 'This is a bug in React.', ); } - if (reference.id !== referenceId) { + if (referenceClosure.id !== referenceId) { // These are different functions. return false; } // Now check if the number of bound arguments is the same. - const boundPromise = reference.bound; + const boundPromise = referenceClosure.bound; if (boundPromise === null) { // No bound arguments. return numberOfBoundArgs === 0; @@ -1131,10 +1137,20 @@ export function registerBoundServerReference<T: Function>( bound: null | Thenable<Array<any>>, encodeFormAction: void | EncodeFormActionCallback, ): void { + if (knownServerReferences.has(reference)) { + return; + } + + knownServerReferences.set(reference, { + id, + originalBind: reference.bind, + bound, + }); + // Expose encoder for use by SSR, as well as a special bind that can be used to // keep server capabilities. if (usedWithSSR) { - // Only expose this in builds that would actually use it. Not needed on the client. + // Only expose this in builds that would actually use it. Not needed in the browser. const $$FORM_ACTION = encodeFormAction === undefined ? defaultEncodeFormAction @@ -1154,7 +1170,6 @@ export function registerBoundServerReference<T: Function>( bind: {value: bind}, }); } - knownServerReferences.set(reference, {id, bound}); } export function registerServerReference<T: Function>( @@ -1171,43 +1186,54 @@ const FunctionBind = Function.prototype.bind; // $FlowFixMe[method-unbinding] const ArraySlice = Array.prototype.slice; function bind(this: Function): Function { - // $FlowFixMe[unsupported-syntax] - // $FlowFixMe[prop-missing] - const newFn = FunctionBind.apply(this, arguments); - const reference = knownServerReferences.get(this); - if (reference) { - if (__DEV__) { - const thisBind = arguments[0]; - if (thisBind != null) { - // This doesn't warn in browser environments since it's not instrumented outside - // usedWithSSR. This makes this an SSR only warning which we don't generally do. - // TODO: Consider a DEV only instrumentation in the browser. - console.error( - 'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().', - ); - } - } - const args = ArraySlice.call(arguments, 1); - let boundPromise = null; - if (reference.bound !== null) { - boundPromise = Promise.resolve((reference.bound: any)).then(boundArgs => - boundArgs.concat(args), + const referenceClosure = knownServerReferences.get(this); + + if (!referenceClosure) { + // $FlowFixMe[prop-missing] + return FunctionBind.apply(this, arguments); + } + + const newFn = referenceClosure.originalBind.apply(this, arguments); + + if (__DEV__) { + const thisBind = arguments[0]; + if (thisBind != null) { + // This doesn't warn in browser environments since it's not instrumented outside + // usedWithSSR. This makes this an SSR only warning which we don't generally do. + // TODO: Consider a DEV only instrumentation in the browser. + console.error( + 'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().', ); - } else { - boundPromise = Promise.resolve(args); - } - // Expose encoder for use by SSR, as well as a special bind that can be used to - // keep server capabilities. - if (usedWithSSR) { - // Only expose this in builds that would actually use it. Not needed on the client. - Object.defineProperties((newFn: any), { - $$FORM_ACTION: {value: this.$$FORM_ACTION}, - $$IS_SIGNATURE_EQUAL: {value: isSignatureEqual}, - bind: {value: bind}, - }); } - knownServerReferences.set(newFn, {id: reference.id, bound: boundPromise}); } + + const args = ArraySlice.call(arguments, 1); + let boundPromise = null; + if (referenceClosure.bound !== null) { + boundPromise = Promise.resolve((referenceClosure.bound: any)).then( + boundArgs => boundArgs.concat(args), + ); + } else { + boundPromise = Promise.resolve(args); + } + + knownServerReferences.set(newFn, { + id: referenceClosure.id, + originalBind: newFn.bind, + bound: boundPromise, + }); + + // Expose encoder for use by SSR, as well as a special bind that can be used to + // keep server capabilities. + if (usedWithSSR) { + // Only expose this in builds that would actually use it. Not needed on the client. + Object.defineProperties((newFn: any), { + $$FORM_ACTION: {value: this.$$FORM_ACTION}, + $$IS_SIGNATURE_EQUAL: {value: isSignatureEqual}, + bind: {value: bind}, + }); + } + return newFn; } diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 5d3af9d4116f9..30b8e171457bc 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -309,6 +309,13 @@ describe('ReactFlightDOMEdge', () => { greet, }); + // Registering the server reference also with the client must not break + // subsequent `.bind` calls. + ReactServerDOMClient.registerServerReference( + ServerModule.greet, + ServerModule.greet.$$id, + ); + const stream = await serverAct(() => ReactServerDOMServer.renderToReadableStream( { From ca8f91f6f6b1b31023eee06c1e2a827ee178b68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Tue, 11 Mar 2025 23:02:45 -0400 Subject: [PATCH 100/300] Log errors from startViewTransition to onRecoverableError (#32540) We customize the messages only in DEV to keep it small in prod. We skip some messages that are not really errors but more like information. --- .../src/client/ReactFiberConfigDOM.js | 111 ++++++++++++++---- .../src/ReactFiberConfigNative.js | 2 + .../src/createReactNoop.js | 2 + .../src/ReactFiberWorkLoop.js | 12 ++ .../src/ReactFiberConfigTestHost.js | 2 + 5 files changed, 109 insertions(+), 20 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index bbbc836f962b3..b20055d4f9d8d 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1576,6 +1576,69 @@ function cancelAllViewTransitionAnimations(scope: Element) { // either cached the font or preloaded it earlier. const SUSPENSEY_FONT_TIMEOUT = 500; +function customizeViewTransitionError(error: Object): mixed { + if (typeof error === 'object' && error !== null) { + switch (error.name) { + case 'TimeoutError': { + // We assume that the only reason a Timeout can happen is because the Navigation + // promise. We expect any other work to either be fast or have a timeout (fonts). + if (__DEV__) { + // eslint-disable-next-line react-internal/prod-error-codes + return new Error( + 'A ViewTransition timed out because a Navigation stalled. ' + + 'This can happen if a Navigation is blocked on React itself. ' + + "Such as if it's resolved inside useEffect. " + + 'This can be solved by moving the resolution to useLayoutEffect.', + {cause: error}, + ); + } + break; + } + case 'AbortError': { + if (__DEV__) { + // eslint-disable-next-line react-internal/prod-error-codes + return new Error( + 'A ViewTransition was aborted early. This might be because you have ' + + 'other View Transition libraries on the page and only one can run at ' + + "a time. To avoid this, use only React's built-in <ViewTransition> " + + 'to coordinate.', + {cause: error}, + ); + } + break; + } + case 'InvalidStateError': { + if ( + error.message === + 'View transition was skipped because document visibility state is hidden.' || + error.message === + 'Skipping view transition because document visibility state has become hidden.' || + error.message === + 'Skipping view transition because viewport size changed.' + ) { + // Skip logging this. This is not considered an error. + return null; + } + if (__DEV__) { + if ( + error.message === 'Transition was aborted because of invalid state' + ) { + // Chrome doesn't include the reason in the message but logs it in the console.. + // Redirect the user to look there. + // eslint-disable-next-line react-internal/prod-error-codes + return new Error( + 'A ViewTransition could not start. See the console for more details.', + {cause: error}, + ); + } + } + break; + } + } + } + return error; +} + export function startViewTransition( rootContainer: Container, transitionTypes: null | TransitionTypes, @@ -1584,6 +1647,7 @@ export function startViewTransition( afterMutationCallback: () => void, spawnedWorkCallback: () => void, passiveCallback: () => mixed, + errorCallback: mixed => void, ): boolean { const ownerDocument: Document = rootContainer.nodeType === DOCUMENT_NODE @@ -1641,24 +1705,19 @@ export function startViewTransition( }); // $FlowFixMe[prop-missing] ownerDocument.__reactViewTransition = transition; - if (__DEV__) { - transition.ready.then(undefined, (reason: mixed) => { - if ( - typeof reason === 'object' && - reason !== null && - reason.name === 'TimeoutError' - ) { - console.error( - 'A ViewTransition timed out because a Navigation stalled. ' + - 'This can happen if a Navigation is blocked on React itself. ' + - "Such as if it's resolved inside useEffect. " + - 'This can be solved by moving the resolution to useLayoutEffect.', - ); + const handleError = (error: mixed) => { + try { + error = customizeViewTransitionError(error); + if (error !== null) { + errorCallback(error); } - }); - } - transition.ready.then(spawnedWorkCallback, spawnedWorkCallback); - transition.finished.then(() => { + } finally { + // Continue the reset of the work. + spawnedWorkCallback(); + } + }; + transition.ready.then(spawnedWorkCallback, handleError); + transition.finished.finally(() => { cancelAllViewTransitionAnimations((ownerDocument.documentElement: any)); // $FlowFixMe[prop-missing] if (ownerDocument.__reactViewTransition === transition) { @@ -1802,6 +1861,7 @@ export function startGestureTransition( transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, + errorCallback: mixed => void, ): null | RunningGestureTransition { const ownerDocument: Document = rootContainer.nodeType === DOCUMENT_NODE @@ -1815,7 +1875,7 @@ export function startGestureTransition( }); // $FlowFixMe[prop-missing] ownerDocument.__reactViewTransition = transition; - const readyCallback = (x: any) => { + const readyCallback = () => { const documentElement: Element = (ownerDocument.documentElement: any); // Loop through all View Transition Animations. const animations = documentElement.getAnimations({subtree: true}); @@ -1935,8 +1995,19 @@ export function startGestureTransition( navigator.userAgent.indexOf('Chrome') !== -1 ? () => requestAnimationFrame(readyCallback) : readyCallback; - transition.ready.then(readyForAnimations, readyCallback); - transition.finished.then(() => { + const handleError = (error: mixed) => { + try { + error = customizeViewTransitionError(error); + if (error !== null) { + errorCallback(error); + } + } finally { + // Continue the reset of the work. + readyCallback(); + } + }; + transition.ready.then(readyForAnimations, handleError); + transition.finished.finally(() => { cancelAllViewTransitionAnimations((ownerDocument.documentElement: any)); // $FlowFixMe[prop-missing] if (ownerDocument.__reactViewTransition === transition) { diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 5f1b5583a18d9..0155232f0c23e 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -619,6 +619,7 @@ export function startViewTransition( afterMutationCallback: () => void, spawnedWorkCallback: () => void, passiveCallback: () => mixed, + errorCallback: mixed => void, ): boolean { return false; } @@ -633,6 +634,7 @@ export function startGestureTransition( transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, + errorCallback: mixed => void, ): RunningGestureTransition { mutationCallback(); animateCallback(); diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 4978dccf2a03c..c157c0119aa97 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -809,6 +809,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { afterMutationCallback: () => void, layoutCallback: () => void, passiveCallback: () => mixed, + errorCallback: mixed => void, ): boolean { return false; }, @@ -821,6 +822,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, + errorCallback: mixed => void, ): RunningGestureTransition { mutationCallback(); animateCallback(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index f8d4fa03a0dff..d7fe0893633fc 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -3511,6 +3511,7 @@ function commitRoot( flushAfterMutationEffects, flushSpawnedWork, flushPassiveEffects, + reportViewTransitionError, ); if (!startedViewTransition) { // Flush synchronously. @@ -3521,6 +3522,16 @@ function commitRoot( } } +function reportViewTransitionError(error: mixed) { + // Report errors that happens while preparing a View Transition. + if (pendingEffectsStatus === NO_PENDING_EFFECTS) { + return; + } + const root = pendingEffectsRoot; + const onRecoverableError = root.onRecoverableError; + onRecoverableError(error, makeErrorInfo(null)); +} + function flushAfterMutationEffects(): void { if (pendingEffectsStatus !== PENDING_AFTER_MUTATION_PHASE) { return; @@ -3911,6 +3922,7 @@ function commitGestureOnRoot( pendingTransitionTypes, flushGestureMutations, flushGestureAnimations, + reportViewTransitionError, ); } diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index bb18e44e5580d..3804fe22ceba4 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -417,6 +417,7 @@ export function startViewTransition( afterMutationCallback: () => void, spawnedWorkCallback: () => void, passiveCallback: () => mixed, + errorCallback: mixed => void, ): boolean { return false; } @@ -431,6 +432,7 @@ export function startGestureTransition( transitionTypes: null | TransitionTypes, mutationCallback: () => void, animateCallback: () => void, + errorCallback: mixed => void, ): RunningGestureTransition { mutationCallback(); animateCallback(); From 6aa8254bb7353fe3096289edc669cf168e9fd190 Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Wed, 12 Mar 2025 10:32:11 -0400 Subject: [PATCH 101/300] Add ref to Fragment (#32465) *This API is experimental and subject to change or removal.* This PR is an alternative to https://github.com/facebook/react/pull/32421 based on feedback: https://github.com/facebook/react/pull/32421#pullrequestreview-2625382015 . The difference here is that we traverse from the Fragment's fiber at operation time instead of keeping a set of children on the `FragmentInstance`. We still need to handle newly added or removed child nodes to apply event listeners and observers, so we treat those updates as effects. **Fragment Refs** This PR extends React's Fragment component to accept a `ref` prop. The Fragment's ref will attach to a custom host instance, which will provide an Element-like API for working with the Fragment's host parent and host children. Here I've implemented `addEventListener`, `removeEventListener`, and `focus` to get started but we'll be iterating on this by adding additional APIs in future PRs. This sets up the mechanism to attach refs and perform operations on children. The FragmentInstance is implemented in `react-dom` here but is planned for Fabric as well. The API works by targeting the first level of host children and proxying Element-like APIs to allow developers to manage groups of elements or elements that cannot be easily accessed such as from a third-party library or deep in a tree of Functional Component wrappers. ```javascript import {Fragment, useRef} from 'react'; const fragmentRef = useRef(null); <Fragment ref={fragmentRef}> <div id="A" /> <Wrapper> <div id="B"> <div id="C" /> </div> </Wrapper> <div id="D" /> </Fragment> ``` In this case, calling `fragmentRef.current.addEventListener()` would apply an event listener to `A`, `B`, and `D`. `C` is skipped because it is nested under the first level of Host Component. If another Host Component was appended as a sibling to `A`, `B`, or `D`, the event listener would be applied to that element as well and any other APIs would also affect the newly added child. This is an implementation of the basic feature as a starting point for feedback and further iteration. --- packages/react-art/src/ReactFiberConfigART.js | 21 + .../src/client/ReactFiberConfigDOM.js | 230 +++++++ .../__tests__/ReactDOMFragmentRefs-test.js | 620 ++++++++++++++++++ .../src/ReactFiberConfigFabric.js | 29 + .../src/ReactFiberConfigNative.js | 29 + .../src/createReactNoop.js | 12 + .../react-reconciler/src/ReactChildFiber.js | 50 +- .../src/ReactFiberBeginWork.js | 4 + .../src/ReactFiberCommitEffects.js | 18 +- .../src/ReactFiberCommitHostEffects.js | 151 ++++- .../src/ReactFiberCommitWork.js | 59 +- .../src/forks/ReactFiberConfig.custom.js | 8 + .../src/ReactFiberConfigTestHost.js | 29 + .../ReactElementValidator-test.internal.js | 10 +- .../ReactJSXElementValidator-test.js | 24 +- packages/shared/ReactFeatureFlags.js | 3 +- .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 2 + .../forks/ReactFeatureFlags.test-renderer.js | 2 + ...actFeatureFlags.test-renderer.native-fb.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 2 + .../forks/ReactFeatureFlags.www-dynamic.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 1 + 23 files changed, 1258 insertions(+), 49 deletions(-) create mode 100644 packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index 1ca506d022f4e..5b8788453f6af 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -318,6 +318,27 @@ export function cloneMutableTextInstance(textInstance) { return textInstance; } +export type FragmentInstanceType = null; + +export function createFragmentInstance(fiber): null { + return null; +} + +export function updateFragmentInstanceFiber(fiber, instance): void { + // Noop +} + +export function commitNewChildToFragmentInstance( + child, + fragmentInstance, +): void { + // Noop +} + +export function deleteChildFromFragmentInstance(child, fragmentInstance): void { + // Noop +} + export function finalizeInitialChildren(domElement, type, props) { return false; } diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index b20055d4f9d8d..4b42c032d1be9 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -34,6 +34,7 @@ import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostCo import hasOwnProperty from 'shared/hasOwnProperty'; import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion'; import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import {OffscreenComponent} from 'react-reconciler/src/ReactWorkTags'; export { setCurrentUpdatePriority, @@ -2159,6 +2160,235 @@ export function subscribeToGestureDirection( } } +type EventListenerOptionsOrUseCapture = + | boolean + | { + capture?: boolean, + once?: boolean, + passive?: boolean, + signal?: AbortSignal, + ... + }; + +type StoredEventListener = { + type: string, + listener: EventListener, + optionsOrUseCapture: void | EventListenerOptionsOrUseCapture, +}; + +export type FragmentInstanceType = { + _fragmentFiber: Fiber, + _eventListeners: null | Array<StoredEventListener>, + addEventListener( + type: string, + listener: EventListener, + optionsOrUseCapture?: EventListenerOptionsOrUseCapture, + ): void, + removeEventListener( + type: string, + listener: EventListener, + optionsOrUseCapture?: EventListenerOptionsOrUseCapture, + ): void, + focus(): void, +}; + +function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) { + this._fragmentFiber = fragmentFiber; + this._eventListeners = null; +} +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.addEventListener = function ( + this: FragmentInstanceType, + type: string, + listener: EventListener, + optionsOrUseCapture?: EventListenerOptionsOrUseCapture, +): void { + if (this._eventListeners === null) { + this._eventListeners = []; + } + + const listeners = this._eventListeners; + // Element.addEventListener will only apply uniquely new event listeners by default. Since we + // need to collect the listeners to apply to appended children, we track them ourselves and use + // custom equality check for the options. + const isNewEventListener = + indexOfEventListener(listeners, type, listener, optionsOrUseCapture) === -1; + if (isNewEventListener) { + listeners.push({type, listener, optionsOrUseCapture}); + traverseFragmentInstanceChildren( + this, + this._fragmentFiber.child, + addEventListenerToChild, + type, + listener, + optionsOrUseCapture, + ); + } + this._eventListeners = listeners; +}; +function addEventListenerToChild( + child: Instance, + type: string, + listener: EventListener, + optionsOrUseCapture?: EventListenerOptionsOrUseCapture, +): boolean { + child.addEventListener(type, listener, optionsOrUseCapture); + return false; +} +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.removeEventListener = function ( + this: FragmentInstanceType, + type: string, + listener: EventListener, + optionsOrUseCapture?: EventListenerOptionsOrUseCapture, +): void { + const listeners = this._eventListeners; + if (listeners === null) { + return; + } + if (typeof listeners !== 'undefined' && listeners.length > 0) { + traverseFragmentInstanceChildren( + this, + this._fragmentFiber.child, + removeEventListenerFromChild, + type, + listener, + optionsOrUseCapture, + ); + const index = indexOfEventListener( + listeners, + type, + listener, + optionsOrUseCapture, + ); + if (this._eventListeners !== null) { + this._eventListeners.splice(index, 1); + } + } +}; +function removeEventListenerFromChild( + child: Instance, + type: string, + listener: EventListener, + optionsOrUseCapture?: EventListenerOptionsOrUseCapture, +): boolean { + child.removeEventListener(type, listener, optionsOrUseCapture); + return false; +} +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.focus = function (this: FragmentInstanceType) { + traverseFragmentInstanceChildren( + this, + this._fragmentFiber.child, + setFocusIfFocusable, + ); +}; + +function traverseFragmentInstanceChildren<A, B, C>( + fragmentInstance: FragmentInstanceType, + child: Fiber | null, + fn: (Instance, A, B, C) => boolean, + a: A, + b: B, + c: C, +): void { + while (child !== null) { + if (child.tag === HostComponent) { + if (fn(child.stateNode, a, b, c)) { + return; + } + } else if ( + child.tag === OffscreenComponent && + child.memoizedState !== null + ) { + // Skip hidden subtrees + } else { + traverseFragmentInstanceChildren( + fragmentInstance, + child.child, + fn, + a, + b, + c, + ); + } + child = child.sibling; + } +} + +function normalizeListenerOptions( + opts: ?EventListenerOptionsOrUseCapture, +): string { + if (opts == null) { + return '0'; + } + + if (typeof opts === 'boolean') { + return `c=${opts ? '1' : '0'}`; + } + + return `c=${opts.capture ? '1' : '0'}&o=${opts.once ? '1' : '0'}&p=${opts.passive ? '1' : '0'}`; +} + +function indexOfEventListener( + eventListeners: Array<StoredEventListener>, + type: string, + listener: EventListener, + optionsOrUseCapture: void | EventListenerOptionsOrUseCapture, +): number { + for (let i = 0; i < eventListeners.length; i++) { + const item = eventListeners[i]; + if ( + item.type === type && + item.listener === listener && + normalizeListenerOptions(item.optionsOrUseCapture) === + normalizeListenerOptions(optionsOrUseCapture) + ) { + return i; + } + } + return -1; +} + +export function createFragmentInstance( + fragmentFiber: Fiber, +): FragmentInstanceType { + return new (FragmentInstance: any)(fragmentFiber); +} + +export function updateFragmentInstanceFiber( + fragmentFiber: Fiber, + instance: FragmentInstanceType, +): void { + instance._fragmentFiber = fragmentFiber; +} + +export function commitNewChildToFragmentInstance( + childElement: Instance, + fragmentInstance: FragmentInstanceType, +): void { + const eventListeners = fragmentInstance._eventListeners; + if (eventListeners !== null) { + for (let i = 0; i < eventListeners.length; i++) { + const {type, listener, optionsOrUseCapture} = eventListeners[i]; + childElement.addEventListener(type, listener, optionsOrUseCapture); + } + } +} + +export function deleteChildFromFragmentInstance( + childElement: Instance, + fragmentInstance: FragmentInstanceType, +): void { + const eventListeners = fragmentInstance._eventListeners; + if (eventListeners !== null) { + for (let i = 0; i < eventListeners.length; i++) { + const {type, listener, optionsOrUseCapture} = eventListeners[i]; + childElement.removeEventListener(type, listener, optionsOrUseCapture); + } + } +} + export function clearContainer(container: Container): void { const nodeType = container.nodeType; if (nodeType === DOCUMENT_NODE) { diff --git a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js new file mode 100644 index 0000000000000..727fd3014201f --- /dev/null +++ b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js @@ -0,0 +1,620 @@ +/** + * 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. + * + * @emails reactcore + */ + +'use strict'; + +let React; +let ReactDOMClient; +let act; +let container; +let Fragment; +let Activity; + +describe('FragmentRefs', () => { + beforeEach(() => { + jest.resetModules(); + React = require('react'); + Fragment = React.Fragment; + Activity = React.unstable_Activity; + ReactDOMClient = require('react-dom/client'); + act = require('internal-test-utils').act; + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + }); + + // @gate enableFragmentRefs + it('attaches a ref to Fragment', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + await act(() => + root.render( + <div id="parent"> + <Fragment ref={fragmentRef}> + <div id="child">Hi</div> + </Fragment> + </div>, + ), + ); + expect(container.innerHTML).toEqual( + '<div id="parent"><div id="child">Hi</div></div>', + ); + + expect(fragmentRef.current).not.toBe(null); + }); + + // @gate enableFragmentRefs + it('accepts a ref callback', async () => { + let fragmentRef; + const root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render( + <Fragment ref={ref => (fragmentRef = ref)}> + <div id="child">Hi</div> + </Fragment>, + ); + }); + + expect(fragmentRef._fragmentFiber).toBeTruthy(); + }); + + // @gate enableFragmentRefs + it('is available in effects', async () => { + function Test() { + const fragmentRef = React.useRef(null); + React.useLayoutEffect(() => { + expect(fragmentRef.current).not.toBe(null); + }); + React.useEffect(() => { + expect(fragmentRef.current).not.toBe(null); + }); + return ( + <Fragment ref={fragmentRef}> + <div /> + </Fragment> + ); + } + + const root = ReactDOMClient.createRoot(container); + await act(() => root.render(<Test />)); + }); + + describe('focus()', () => { + // @gate enableFragmentRefs + it('focuses the first focusable child', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + <div> + <Fragment ref={fragmentRef}> + <div id="child-a" /> + <style>{`#child-c {}`}</style> + <a id="child-b" href="/"> + B + </a> + <a id="child-c" href="/"> + C + </a> + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Test />); + }); + + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-b'); + document.activeElement.blur(); + }); + + // @gate enableFragmentRefs + it('preserves document order when adding and removing children', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test({showA, showB}) { + return ( + <Fragment ref={fragmentRef}> + {showA && <a href="/" id="child-a" />} + {showB && <a href="/" id="child-b" />} + </Fragment> + ); + } + + // Render with A as the first focusable child + await act(() => { + root.render(<Test showA={true} showB={false} />); + }); + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-a'); + document.activeElement.blur(); + // A is still the first focusable child, but B is also tracked + await act(() => { + root.render(<Test showA={true} showB={true} />); + }); + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-a'); + document.activeElement.blur(); + + // B is now the first focusable child + await act(() => { + root.render(<Test showA={false} showB={true} />); + }); + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-b'); + document.activeElement.blur(); + }); + }); + + describe('event listeners', () => { + // @gate enableFragmentRefs + it('adds and removes event listeners from children', async () => { + const parentRef = React.createRef(); + const fragmentRef = React.createRef(); + const childARef = React.createRef(); + const childBRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + let logs = []; + + function handleFragmentRefClicks() { + logs.push('fragmentRef'); + } + + function Test() { + React.useEffect(() => { + fragmentRef.current.addEventListener( + 'click', + handleFragmentRefClicks, + ); + + return () => { + fragmentRef.current.removeEventListener( + 'click', + handleFragmentRefClicks, + ); + }; + }, []); + return ( + <div ref={parentRef}> + <Fragment ref={fragmentRef}> + <>Text</> + <div ref={childARef}>A</div> + <> + <div ref={childBRef}>B</div> + </> + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Test />); + }); + + childARef.current.addEventListener('click', () => { + logs.push('A'); + }); + + childBRef.current.addEventListener('click', () => { + logs.push('B'); + }); + + // Clicking on the parent should not trigger any listeners + parentRef.current.click(); + expect(logs).toEqual([]); + + // Clicking a child triggers its own listeners and the Fragment's + childARef.current.click(); + expect(logs).toEqual(['fragmentRef', 'A']); + + logs = []; + + childBRef.current.click(); + expect(logs).toEqual(['fragmentRef', 'B']); + + logs = []; + + fragmentRef.current.removeEventListener('click', handleFragmentRefClicks); + + childARef.current.click(); + expect(logs).toEqual(['A']); + + logs = []; + + childBRef.current.click(); + expect(logs).toEqual(['B']); + }); + + // @gate enableFragmentRefs + it('adds and removes event listeners from children with multiple fragments', async () => { + const fragmentRef = React.createRef(); + const nestedFragmentRef = React.createRef(); + const nestedFragmentRef2 = React.createRef(); + const childARef = React.createRef(); + const childBRef = React.createRef(); + const childCRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render( + <div> + <Fragment ref={fragmentRef}> + <div ref={childARef}>A</div> + <div> + <Fragment ref={nestedFragmentRef}> + <div ref={childBRef}>B</div> + </Fragment> + </div> + <Fragment ref={nestedFragmentRef2}> + <div ref={childCRef}>C</div> + </Fragment> + </Fragment> + </div>, + ); + }); + + let logs = []; + + function handleFragmentRefClicks() { + logs.push('fragmentRef'); + } + + function handleNestedFragmentRefClicks() { + logs.push('nestedFragmentRef'); + } + + function handleNestedFragmentRef2Clicks() { + logs.push('nestedFragmentRef2'); + } + + fragmentRef.current.addEventListener('click', handleFragmentRefClicks); + nestedFragmentRef.current.addEventListener( + 'click', + handleNestedFragmentRefClicks, + ); + nestedFragmentRef2.current.addEventListener( + 'click', + handleNestedFragmentRef2Clicks, + ); + + childBRef.current.click(); + // Event bubbles to the parent fragment + expect(logs).toEqual(['nestedFragmentRef', 'fragmentRef']); + + logs = []; + + childARef.current.click(); + expect(logs).toEqual(['fragmentRef']); + + logs = []; + childCRef.current.click(); + expect(logs).toEqual(['fragmentRef', 'nestedFragmentRef2']); + + logs = []; + + fragmentRef.current.removeEventListener('click', handleFragmentRefClicks); + nestedFragmentRef.current.removeEventListener( + 'click', + handleNestedFragmentRefClicks, + ); + childCRef.current.click(); + expect(logs).toEqual(['nestedFragmentRef2']); + }); + + // @gate enableFragmentRefs + it('adds an event listener to a newly added child', async () => { + const fragmentRef = React.createRef(); + const childRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + let showChild; + + function Component() { + const [shouldShowChild, setShouldShowChild] = React.useState(false); + showChild = () => { + setShouldShowChild(true); + }; + + return ( + <div> + <Fragment ref={fragmentRef}> + <div id="a">A</div> + {shouldShowChild && ( + <div ref={childRef} id="b"> + B + </div> + )} + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Component />); + }); + + expect(fragmentRef.current).not.toBe(null); + expect(childRef.current).toBe(null); + + let hasClicked = false; + fragmentRef.current.addEventListener('click', () => { + hasClicked = true; + }); + + await act(() => { + showChild(); + }); + expect(childRef.current).not.toBe(null); + + childRef.current.click(); + expect(hasClicked).toBe(true); + }); + + // @gate enableFragmentRefs + it('applies event listeners to host children nested within non-host children', async () => { + const fragmentRef = React.createRef(); + const childRef = React.createRef(); + const nestedChildRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Wrapper({children}) { + return children; + } + + await act(() => { + root.render( + <div> + <Fragment ref={fragmentRef}> + <div ref={childRef}>Host A</div> + <Wrapper> + <Wrapper> + <Wrapper> + <div ref={nestedChildRef}>Host B</div> + </Wrapper> + </Wrapper> + </Wrapper> + </Fragment> + </div>, + ); + }); + const logs = []; + fragmentRef.current.addEventListener('click', e => { + logs.push(e.target.textContent); + }); + + expect(logs).toEqual([]); + childRef.current.click(); + expect(logs).toEqual(['Host A']); + nestedChildRef.current.click(); + expect(logs).toEqual(['Host A', 'Host B']); + }); + + // @gate enableFragmentRefs + it('allows adding and cleaning up listeners in effects', async () => { + const root = ReactDOMClient.createRoot(container); + + let logs = []; + function logClick(e) { + logs.push(e.currentTarget.id); + } + + let rerender; + let removeEventListeners; + + function Test() { + const fragmentRef = React.useRef(null); + // eslint-disable-next-line no-unused-vars + const [_, setState] = React.useState(0); + rerender = () => { + setState(p => p + 1); + }; + removeEventListeners = () => { + fragmentRef.current.removeEventListener('click', logClick); + }; + React.useEffect(() => { + fragmentRef.current.addEventListener('click', logClick); + + return removeEventListeners; + }); + + return ( + <Fragment ref={fragmentRef}> + <div id="child-a" /> + </Fragment> + ); + } + + // The event listener was applied + await act(() => root.render(<Test />)); + expect(logs).toEqual([]); + document.querySelector('#child-a').click(); + expect(logs).toEqual(['child-a']); + + // The event listener can be removed and re-added + logs = []; + await act(rerender); + document.querySelector('#child-a').click(); + expect(logs).toEqual(['child-a']); + }); + + // @gate enableFragmentRefs + it('does not apply removed event listeners to new children', async () => { + const root = ReactDOMClient.createRoot(container); + const fragmentRef = React.createRef(null); + function Test() { + return ( + <Fragment ref={fragmentRef}> + <div id="child-a" /> + </Fragment> + ); + } + + let logs = []; + function logClick(e) { + logs.push(e.currentTarget.id); + } + await act(() => { + root.render(<Test />); + }); + fragmentRef.current.addEventListener('click', logClick); + const childA = document.querySelector('#child-a'); + childA.click(); + expect(logs).toEqual(['child-a']); + + logs = []; + fragmentRef.current.removeEventListener('click', logClick); + childA.click(); + expect(logs).toEqual([]); + }); + + describe('with activity', () => { + // @gate enableFragmentRefs && enableActivity + it('does not apply event listeners to hidden trees', async () => { + const parentRef = React.createRef(); + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + <div ref={parentRef}> + <Fragment ref={fragmentRef}> + <div>Child 1</div> + <Activity mode="hidden"> + <div>Child 2</div> + </Activity> + <div>Child 3</div> + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Test />); + }); + + const logs = []; + fragmentRef.current.addEventListener('click', e => { + logs.push(e.target.textContent); + }); + + const [child1, child2, child3] = parentRef.current.children; + child1.click(); + child2.click(); + child3.click(); + expect(logs).toEqual(['Child 1', 'Child 3']); + }); + + // @gate enableFragmentRefs && enableActivity + it('applies event listeners to visible trees', async () => { + const parentRef = React.createRef(); + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + <div ref={parentRef}> + <Fragment ref={fragmentRef}> + <div>Child 1</div> + <Activity mode="visible"> + <div>Child 2</div> + </Activity> + <div>Child 3</div> + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Test />); + }); + + const logs = []; + fragmentRef.current.addEventListener('click', e => { + logs.push(e.target.textContent); + }); + + const [child1, child2, child3] = parentRef.current.children; + child1.click(); + child2.click(); + child3.click(); + expect(logs).toEqual(['Child 1', 'Child 2', 'Child 3']); + }); + + // @gate enableFragmentRefs && enableActivity + it('handles Activity modes switching', async () => { + const fragmentRef = React.createRef(); + const fragmentRef2 = React.createRef(); + const parentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test({mode}) { + return ( + <div id="parent" ref={parentRef}> + <Fragment ref={fragmentRef}> + <Activity mode={mode}> + <div id="child1">Child</div> + <Fragment ref={fragmentRef2}> + <div id="child2">Child 2</div> + </Fragment> + </Activity> + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Test mode="visible" />); + }); + + let logs = []; + fragmentRef.current.addEventListener('click', () => { + logs.push('clicked 1'); + }); + fragmentRef2.current.addEventListener('click', () => { + logs.push('clicked 2'); + }); + parentRef.current.lastChild.click(); + expect(logs).toEqual(['clicked 1', 'clicked 2']); + + logs = []; + await act(() => { + root.render(<Test mode="hidden" />); + }); + parentRef.current.firstChild.click(); + parentRef.current.lastChild.click(); + expect(logs).toEqual([]); + + logs = []; + await act(() => { + root.render(<Test mode="visible" />); + }); + parentRef.current.lastChild.click(); + // Event order is flipped here because the nested child re-registers first + expect(logs).toEqual(['clicked 2', 'clicked 1']); + }); + }); + }); +}); diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index 6db223e4b4dc5..c3b8ac4d35d14 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -591,6 +591,35 @@ export function waitForCommitToBeReady(): null { return null; } +export type FragmentInstanceType = null; + +export function createFragmentInstance( + fragmentFiber: Fiber, +): FragmentInstanceType { + return null; +} + +export function updateFragmentInstanceFiber( + fragmentFiber: Fiber, + instance: FragmentInstanceType, +): void { + // Noop +} + +export function commitNewChildToFragmentInstance( + child: PublicInstance, + fragmentInstance: FragmentInstanceType, +): void { + // Noop +} + +export function deleteChildFromFragmentInstance( + child: PublicInstance, + fragmentInstance: FragmentInstanceType, +): void { + // Noop +} + export const NotPendingTransition: TransitionStatus = null; export const HostTransitionContext: ReactContext<TransitionStatus> = { $$typeof: REACT_CONTEXT_TYPE, diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index 0155232f0c23e..c08e1f0f82474 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -202,6 +202,35 @@ export function cloneMutableTextInstance( throw new Error('Not yet implemented.'); } +export type FragmentInstanceType = null; + +export function createFragmentInstance( + fragmentFiber: Fiber, +): FragmentInstanceType { + return null; +} + +export function updateFragmentInstanceFiber( + fragmentFiber: Fiber, + instance: FragmentInstanceType, +): void { + // Noop +} + +export function commitNewChildToFragmentInstance( + child: PublicInstance, + fragmentInstance: FragmentInstanceType, +): void { + // Noop +} + +export function deleteChildFromFragmentInstance( + child: PublicInstance, + fragmentInstance: FragmentInstanceType, +): void { + // Noop +} + export function finalizeInitialChildren( parentInstance: Instance, type: string, diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index c157c0119aa97..2ddbea79289fa 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -512,6 +512,18 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { throw new Error('Not yet implemented.'); }, + createFragmentInstance(parentInstance) { + return null; + }, + + commitNewChildToFragmentInstance(child, fragmentInstance) { + // Noop + }, + + deleteChildFromFragmentInstance(child, fragmentInstance) { + // Noop + }, + scheduleTimeout: setTimeout, cancelTimeout: clearTimeout, noTimeout: -1, diff --git a/packages/react-reconciler/src/ReactChildFiber.js b/packages/react-reconciler/src/ReactChildFiber.js index 9ced8912b977b..6af8c1356f9ca 100644 --- a/packages/react-reconciler/src/ReactChildFiber.js +++ b/packages/react-reconciler/src/ReactChildFiber.js @@ -47,6 +47,7 @@ import isArray from 'shared/isArray'; import { enableAsyncIterableChildren, disableLegacyMode, + enableFragmentRefs, } from 'shared/ReactFeatureFlags'; import { @@ -214,10 +215,14 @@ function validateFragmentProps( const keys = Object.keys(element.props); for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (key !== 'children' && key !== 'key') { + if ( + key !== 'children' && + key !== 'key' && + (enableFragmentRefs ? key !== 'ref' : true) + ) { if (fiber === null) { - // For unkeyed root fragments there's no Fiber. We create a fake one just for - // error stack handling. + // For unkeyed root fragments without refs (enableFragmentRefs), + // there's no Fiber. We create a fake one just for error stack handling. fiber = createFiberFromElement(element, returnFiber.mode, 0); if (__DEV__) { fiber._debugInfo = currentDebugInfo; @@ -227,11 +232,19 @@ function validateFragmentProps( runWithFiberInDEV( fiber, erroredKey => { - console.error( - 'Invalid prop `%s` supplied to `React.Fragment`. ' + - 'React.Fragment can only have `key` and `children` props.', - erroredKey, - ); + if (enableFragmentRefs) { + console.error( + 'Invalid prop `%s` supplied to `React.Fragment`. ' + + 'React.Fragment can only have `key`, `ref`, and `children` props.', + erroredKey, + ); + } else { + console.error( + 'Invalid prop `%s` supplied to `React.Fragment`. ' + + 'React.Fragment can only have `key` and `children` props.', + erroredKey, + ); + } }, key, ); @@ -517,6 +530,9 @@ function createChildReconciler( lanes, element.key, ); + if (enableFragmentRefs) { + coerceRef(updated, element); + } validateFragmentProps(element, updated, returnFiber); return updated; } @@ -1619,6 +1635,9 @@ function createChildReconciler( if (child.tag === Fragment) { deleteRemainingChildren(returnFiber, child.sibling); const existing = useFiber(child, element.props.children); + if (enableFragmentRefs) { + coerceRef(existing, element); + } existing.return = returnFiber; if (__DEV__) { existing._debugOwner = element._owner; @@ -1670,6 +1689,9 @@ function createChildReconciler( lanes, element.key, ); + if (enableFragmentRefs) { + coerceRef(created, element); + } created.return = returnFiber; if (__DEV__) { // We treat the parent as the owner for stack purposes. @@ -1742,17 +1764,19 @@ function createChildReconciler( // not as a fragment. Nested arrays on the other hand will be treated as // fragment nodes. Recursion happens at the normal flow. - // Handle top level unkeyed fragments as if they were arrays. - // This leads to an ambiguity between <>{[...]}</> and <>...</>. + // Handle top level unkeyed fragments without refs (enableFragmentRefs) + // as if they were arrays. This leads to an ambiguity between <>{[...]}</> and <>...</>. // We treat the ambiguous cases above the same. // We don't use recursion here because a fragment inside a fragment // is no longer considered "top level" for these purposes. - const isUnkeyedTopLevelFragment = + const isUnkeyedUnrefedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && - newChild.key === null; - if (isUnkeyedTopLevelFragment) { + newChild.key === null && + (enableFragmentRefs ? newChild.props.ref === undefined : true); + + if (isUnkeyedUnrefedTopLevelFragment) { validateFragmentProps(newChild, null, returnFiber); newChild = newChild.props.children; } diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 9c88a5de870cf..b37bd4ea3f44f 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -116,6 +116,7 @@ import { disableDefaultPropsExceptForClasses, enableHydrationLaneScheduling, enableViewTransition, + enableFragmentRefs, } from 'shared/ReactFeatureFlags'; import isArray from 'shared/isArray'; import shallowEqual from 'shared/shallowEqual'; @@ -987,6 +988,9 @@ function updateFragment( renderLanes: Lanes, ) { const nextChildren = workInProgress.pendingProps; + if (enableFragmentRefs) { + markRef(current, workInProgress); + } reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } diff --git a/packages/react-reconciler/src/ReactFiberCommitEffects.js b/packages/react-reconciler/src/ReactFiberCommitEffects.js index 05cf17a34273e..a0f6b54780a2d 100644 --- a/packages/react-reconciler/src/ReactFiberCommitEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitEffects.js @@ -11,6 +11,7 @@ import type {Fiber} from './ReactInternalTypes'; import type {UpdateQueue} from './ReactFiberClassUpdateQueue'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks'; import type {HookFlags} from './ReactHookEffectTags'; +import type {FragmentInstanceType} from './ReactFiberConfig'; import { getViewTransitionName, type ViewTransitionState, @@ -24,9 +25,11 @@ import { enableSchedulingProfiler, enableUseEffectCRUDOverload, enableViewTransition, + enableFragmentRefs, } from 'shared/ReactFeatureFlags'; import { ClassComponent, + Fragment, HostComponent, HostHoistable, HostSingleton, @@ -48,6 +51,7 @@ import { import { getPublicInstance, createViewTransitionInstance, + createFragmentInstance, } from './ReactFiberConfig'; import { captureCommitPhaseError, @@ -877,7 +881,7 @@ function commitAttachRef(finishedWork: Fiber) { case HostComponent: instanceToUse = getPublicInstance(finishedWork.stateNode); break; - case ViewTransitionComponent: + case ViewTransitionComponent: { if (enableViewTransition) { const instance: ViewTransitionState = finishedWork.stateNode; const props: ViewTransitionProps = finishedWork.memoizedProps; @@ -888,6 +892,18 @@ function commitAttachRef(finishedWork: Fiber) { instanceToUse = instance.ref; break; } + instanceToUse = finishedWork.stateNode; + break; + } + case Fragment: + if (enableFragmentRefs) { + const instance: null | FragmentInstanceType = finishedWork.stateNode; + if (instance === null) { + finishedWork.stateNode = createFragmentInstance(finishedWork); + } + instanceToUse = finishedWork.stateNode; + break; + } // Fallthrough default: instanceToUse = finishedWork.stateNode; diff --git a/packages/react-reconciler/src/ReactFiberCommitHostEffects.js b/packages/react-reconciler/src/ReactFiberCommitHostEffects.js index c104c2a8464b5..7dad8b330d7e2 100644 --- a/packages/react-reconciler/src/ReactFiberCommitHostEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitHostEffects.js @@ -13,6 +13,7 @@ import type { SuspenseInstance, Container, ChildSet, + FragmentInstanceType, } from './ReactFiberConfig'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; @@ -24,6 +25,7 @@ import { HostText, HostPortal, DehydratedFragment, + Fragment, } from './ReactWorkTags'; import {ContentReset, Placement} from './ReactFiberFlags'; import { @@ -50,11 +52,14 @@ import { acquireSingletonInstance, releaseSingletonInstance, isSingletonScope, + commitNewChildToFragmentInstance, + deleteChildFromFragmentInstance, } from './ReactFiberConfig'; import {captureCommitPhaseError} from './ReactFiberWorkLoop'; import {trackHostMutation} from './ReactFiberMutationTracking'; import {runWithFiberInDEV} from './ReactCurrentFiber'; +import {enableFragmentRefs} from 'shared/ReactFeatureFlags'; export function commitHostMount(finishedWork: Fiber) { const type = finishedWork.type; @@ -199,19 +204,46 @@ export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) { } } -function getHostParentFiber(fiber: Fiber): Fiber { +export function commitNewChildToFragmentInstances( + fiber: Fiber, + parentFragmentInstances: Array<FragmentInstanceType>, +): void { + for (let i = 0; i < parentFragmentInstances.length; i++) { + const fragmentInstance = parentFragmentInstances[i]; + commitNewChildToFragmentInstance(fiber.stateNode, fragmentInstance); + } +} + +export function commitFragmentInstanceInsertionEffects(fiber: Fiber): void { let parent = fiber.return; while (parent !== null) { + if (isFragmentInstanceParent(parent)) { + const fragmentInstance: FragmentInstanceType = parent.stateNode; + commitNewChildToFragmentInstance(fiber.stateNode, fragmentInstance); + } + if (isHostParent(parent)) { - return parent; + return; } + parent = parent.return; } +} - throw new Error( - 'Expected to find a host parent. This error is likely caused by a bug ' + - 'in React. Please file an issue.', - ); +export function commitFragmentInstanceDeletionEffects(fiber: Fiber): void { + let parent = fiber.return; + while (parent !== null) { + if (isFragmentInstanceParent(parent)) { + const fragmentInstance: FragmentInstanceType = parent.stateNode; + deleteChildFromFragmentInstance(fiber.stateNode, fragmentInstance); + } + + if (isHostParent(parent)) { + return; + } + + parent = parent.return; + } } function isHostParent(fiber: Fiber): boolean { @@ -226,6 +258,10 @@ function isHostParent(fiber: Fiber): boolean { ); } +function isFragmentInstanceParent(fiber: Fiber): boolean { + return fiber && fiber.tag === Fragment && fiber.stateNode !== null; +} + function getHostSibling(fiber: Fiber): ?Instance { // We're going to search forward into the tree until we find a sibling host // node. Unfortunately, if multiple insertions are done in a row we have to @@ -288,6 +324,7 @@ function insertOrAppendPlacementNodeIntoContainer( node: Fiber, before: ?Instance, parent: Container, + parentFragmentInstances: null | Array<FragmentInstanceType>, ): void { const {tag} = node; const isHost = tag === HostComponent || tag === HostText; @@ -298,6 +335,16 @@ function insertOrAppendPlacementNodeIntoContainer( } else { appendChildToContainer(parent, stateNode); } + // TODO: Enable HostText for RN + if ( + enableFragmentRefs && + tag === HostComponent && + // Only run fragment insertion effects for initial insertions + node.alternate === null && + parentFragmentInstances !== null + ) { + commitNewChildToFragmentInstances(node, parentFragmentInstances); + } trackHostMutation(); return; } else if (tag === HostPortal) { @@ -319,10 +366,20 @@ function insertOrAppendPlacementNodeIntoContainer( const child = node.child; if (child !== null) { - insertOrAppendPlacementNodeIntoContainer(child, before, parent); + insertOrAppendPlacementNodeIntoContainer( + child, + before, + parent, + parentFragmentInstances, + ); let sibling = child.sibling; while (sibling !== null) { - insertOrAppendPlacementNodeIntoContainer(sibling, before, parent); + insertOrAppendPlacementNodeIntoContainer( + sibling, + before, + parent, + parentFragmentInstances, + ); sibling = sibling.sibling; } } @@ -332,6 +389,7 @@ function insertOrAppendPlacementNode( node: Fiber, before: ?Instance, parent: Instance, + parentFragmentInstances: null | Array<FragmentInstanceType>, ): void { const {tag} = node; const isHost = tag === HostComponent || tag === HostText; @@ -342,6 +400,16 @@ function insertOrAppendPlacementNode( } else { appendChild(parent, stateNode); } + // TODO: Enable HostText for RN + if ( + enableFragmentRefs && + tag === HostComponent && + // Only run fragment insertion effects for initial insertions + node.alternate === null && + parentFragmentInstances !== null + ) { + commitNewChildToFragmentInstances(node, parentFragmentInstances); + } trackHostMutation(); return; } else if (tag === HostPortal) { @@ -362,10 +430,15 @@ function insertOrAppendPlacementNode( const child = node.child; if (child !== null) { - insertOrAppendPlacementNode(child, before, parent); + insertOrAppendPlacementNode(child, before, parent, parentFragmentInstances); let sibling = child.sibling; while (sibling !== null) { - insertOrAppendPlacementNode(sibling, before, parent); + insertOrAppendPlacementNode( + sibling, + before, + parent, + parentFragmentInstances, + ); sibling = sibling.sibling; } } @@ -377,40 +450,78 @@ function commitPlacement(finishedWork: Fiber): void { } // Recursively insert all host nodes into the parent. - const parentFiber = getHostParentFiber(finishedWork); + let hostParentFiber; + let parentFragmentInstances = null; + let parentFiber = finishedWork.return; + while (parentFiber !== null) { + if (enableFragmentRefs && isFragmentInstanceParent(parentFiber)) { + const fragmentInstance: FragmentInstanceType = parentFiber.stateNode; + if (parentFragmentInstances === null) { + parentFragmentInstances = [fragmentInstance]; + } else { + parentFragmentInstances.push(fragmentInstance); + } + } + if (isHostParent(parentFiber)) { + hostParentFiber = parentFiber; + break; + } + parentFiber = parentFiber.return; + } + if (hostParentFiber == null) { + throw new Error( + 'Expected to find a host parent. This error is likely caused by a bug ' + + 'in React. Please file an issue.', + ); + } - switch (parentFiber.tag) { + switch (hostParentFiber.tag) { case HostSingleton: { if (supportsSingletons) { - const parent: Instance = parentFiber.stateNode; + const parent: Instance = hostParentFiber.stateNode; const before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its // children to find all the terminal nodes. - insertOrAppendPlacementNode(finishedWork, before, parent); + insertOrAppendPlacementNode( + finishedWork, + before, + parent, + parentFragmentInstances, + ); break; } // Fall through } case HostComponent: { - const parent: Instance = parentFiber.stateNode; - if (parentFiber.flags & ContentReset) { + const parent: Instance = hostParentFiber.stateNode; + if (hostParentFiber.flags & ContentReset) { // Reset the text content of the parent before doing any insertions resetTextContent(parent); // Clear ContentReset from the effect tag - parentFiber.flags &= ~ContentReset; + hostParentFiber.flags &= ~ContentReset; } const before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its // children to find all the terminal nodes. - insertOrAppendPlacementNode(finishedWork, before, parent); + insertOrAppendPlacementNode( + finishedWork, + before, + parent, + parentFragmentInstances, + ); break; } case HostRoot: case HostPortal: { - const parent: Container = parentFiber.stateNode.containerInfo; + const parent: Container = hostParentFiber.stateNode.containerInfo; const before = getHostSibling(finishedWork); - insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent); + insertOrAppendPlacementNodeIntoContainer( + finishedWork, + before, + parent, + parentFragmentInstances, + ); break; } default: diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index b9ea97bf56d8c..8b82e6a7e713c 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -61,6 +61,7 @@ import { disableLegacyMode, enableComponentPerformanceTrack, enableViewTransition, + enableFragmentRefs, } from 'shared/ReactFeatureFlags'; import { FunctionComponent, @@ -85,6 +86,7 @@ import { CacheComponent, TracingMarkerComponent, ViewTransitionComponent, + Fragment, } from './ReactWorkTags'; import { NoFlags, @@ -164,6 +166,7 @@ import { cancelRootViewTransitionName, restoreRootViewTransitionName, isSingletonScope, + updateFragmentInstanceFiber, } from './ReactFiberConfig'; import { captureCommitPhaseError, @@ -235,6 +238,8 @@ import { commitHostRemoveChild, commitHostSingletonAcquisition, commitHostSingletonRelease, + commitFragmentInstanceDeletionEffects, + commitFragmentInstanceInsertionEffects, } from './ReactFiberCommitHostEffects'; import { commitEnterViewTransitions, @@ -767,8 +772,15 @@ function commitLayoutEffectOnFiber( } break; } - // Fallthrough + break; } + case Fragment: + if (enableFragmentRefs) { + if (flags & Ref) { + safelyAttachRef(finishedWork, finishedWork.return); + } + } + // Fallthrough default: { recursivelyTraverseLayoutEffects( finishedRoot, @@ -1353,6 +1365,9 @@ function commitDeletionEffectsOnFiber( if (!offscreenSubtreeWasHidden) { safelyDetachRef(deletedFiber, nearestMountedAncestor); } + if (enableFragmentRefs && deletedFiber.tag === HostComponent) { + commitFragmentInstanceDeletionEffects(deletedFiber); + } // Intentional fallthrough to next branch } case HostText: { @@ -1563,6 +1578,14 @@ function commitDeletionEffectsOnFiber( } break; } + case Fragment: { + if (enableFragmentRefs) { + if (!offscreenSubtreeWasHidden) { + safelyDetachRef(deletedFiber, nearestMountedAncestor); + } + } + // Fallthrough + } default: { recursivelyTraverseDeletionEffects( finishedRoot, @@ -1947,6 +1970,7 @@ function commitMutationEffectsOnFiber( } case HostComponent: { recursivelyTraverseMutationEffects(root, finishedWork, lanes); + commitReconciliationEffects(finishedWork, lanes); if (flags & Ref) { @@ -2270,7 +2294,7 @@ function commitMutationEffectsOnFiber( } break; } - case ViewTransitionComponent: + case ViewTransitionComponent: { if (enableViewTransition) { if (flags & Ref) { if (!offscreenSubtreeWasHidden && current !== null) { @@ -2298,7 +2322,8 @@ function commitMutationEffectsOnFiber( popMutationContext(prevMutationContext); break; } - // Fallthrough + break; + } case ScopeComponent: { if (enableScopeAPI) { recursivelyTraverseMutationEffects(root, finishedWork, lanes); @@ -2321,6 +2346,13 @@ function commitMutationEffectsOnFiber( } break; } + case Fragment: + if (enableFragmentRefs) { + if (current && current.stateNode !== null) { + updateFragmentInstanceFiber(finishedWork, current.stateNode); + } + } + // Fallthrough default: { recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork, lanes); @@ -2638,6 +2670,10 @@ export function disappearLayoutEffects(finishedWork: Fiber) { // TODO (Offscreen) Check: flags & RefStatic safelyDetachRef(finishedWork, finishedWork.return); + if (enableFragmentRefs && finishedWork.tag === HostComponent) { + commitFragmentInstanceDeletionEffects(finishedWork); + } + recursivelyTraverseDisappearLayoutEffects(finishedWork); break; } @@ -2658,6 +2694,13 @@ export function disappearLayoutEffects(finishedWork: Fiber) { if (enableViewTransition) { safelyDetachRef(finishedWork, finishedWork.return); } + recursivelyTraverseDisappearLayoutEffects(finishedWork); + break; + } + case Fragment: { + if (enableFragmentRefs) { + safelyDetachRef(finishedWork, finishedWork.return); + } // Fallthrough } default: { @@ -2765,6 +2808,10 @@ export function reappearLayoutEffects( } case HostHoistable: case HostComponent: { + // TODO: Enable HostText for RN + if (enableFragmentRefs && finishedWork.tag === HostComponent) { + commitFragmentInstanceInsertionEffects(finishedWork); + } recursivelyTraverseReappearLayoutEffects( finishedRoot, finishedWork, @@ -2857,6 +2904,12 @@ export function reappearLayoutEffects( safelyAttachRef(finishedWork, finishedWork.return); break; } + break; + } + case Fragment: { + if (enableFragmentRefs) { + safelyAttachRef(finishedWork, finishedWork.return); + } // Fallthrough } default: { diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index e978249e185fe..2be7d18b87caf 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -45,6 +45,7 @@ export type ViewTransitionInstance = null | {name: string, ...}; export opaque type InstanceMeasurement = mixed; export type EventResponder = any; export type GestureTimeline = any; +export type FragmentInstanceType = null; export const rendererVersion = $$$config.rendererVersion; export const rendererPackageName = $$$config.rendererPackageName; @@ -160,6 +161,13 @@ export const subscribeToGestureDirection = export const createViewTransitionInstance = $$$config.createViewTransitionInstance; export const clearContainer = $$$config.clearContainer; +export const createFragmentInstance = $$$config.createFragmentInstance; +export const updateFragmentInstanceFiber = + $$$config.updateFragmentInstanceFiber; +export const commitNewChildToFragmentInstance = + $$$config.commitNewChildToFragmentInstance; +export const deleteChildFromFragmentInstance = + $$$config.deleteChildFromFragmentInstance; // ------------------- // Persistence diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index 3804fe22ceba4..d9a45550fa4b2 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -449,6 +449,35 @@ export function createViewTransitionInstance( return null; } +export type FragmentInstanceType = null; + +export function createFragmentInstance( + fragmentFiber: Object, +): FragmentInstanceType { + return null; +} + +export function updateFragmentInstanceFiber( + fragmentFiber: Object, + instance: FragmentInstanceType, +): void { + // Noop +} + +export function commitNewChildToFragmentInstance( + child: Instance, + fragmentInstance: FragmentInstanceType, +): void { + // noop +} + +export function deleteChildFromFragmentInstance( + child: Instance, + fragmentInstance: FragmentInstanceType, +): void { + // Noop +} + export function getInstanceFromNode(mockNode: Object): Object | null { const instance = nodeToInstanceMap.get(mockNode); if (instance !== undefined) { diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js index bbb06411b03e2..f1ee7026346ec 100644 --- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js +++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js @@ -427,9 +427,13 @@ describe('ReactElementValidator', () => { const root = ReactDOMClient.createRoot(document.createElement('div')); await act(() => root.render(React.createElement(Foo))); assertConsoleErrorDev([ - 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' + - 'can only have `key` and `children` props.\n' + - ' in Foo (at **)', + gate('enableFragmentRefs') + ? 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' + + 'can only have `key`, `ref`, and `children` props.\n' + + ' in Foo (at **)' + : 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' + + 'can only have `key` and `children` props.\n' + + ' in Foo (at **)', ]); }); diff --git a/packages/react/src/__tests__/ReactJSXElementValidator-test.js b/packages/react/src/__tests__/ReactJSXElementValidator-test.js index 041191e2ab5fd..41ee720478ae1 100644 --- a/packages/react/src/__tests__/ReactJSXElementValidator-test.js +++ b/packages/react/src/__tests__/ReactJSXElementValidator-test.js @@ -221,9 +221,13 @@ describe('ReactJSXElementValidator', () => { root.render(<Foo />); }); assertConsoleErrorDev([ - 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' + - 'can only have `key` and `children` props.\n' + - ' in Foo (at **)', + gate('enableFragmentRefs') + ? 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' + + 'can only have `key`, `ref`, and `children` props.\n' + + ' in Foo (at **)' + : 'Invalid prop `a` supplied to `React.Fragment`. React.Fragment ' + + 'can only have `key` and `children` props.\n' + + ' in Foo (at **)', ]); }); @@ -246,11 +250,15 @@ describe('ReactJSXElementValidator', () => { await act(() => { root.render(<Foo />); }); - assertConsoleErrorDev([ - 'Invalid prop `ref` supplied to `React.Fragment`.' + - ' React.Fragment can only have `key` and `children` props.\n' + - ' in Foo (at **)', - ]); + assertConsoleErrorDev( + gate('enableFragmentRefs') + ? [] + : [ + 'Invalid prop `ref` supplied to `React.Fragment`.' + + ' React.Fragment can only have `key` and `children` props.\n' + + ' in Foo (at **)', + ], + ); }); it('does not warn for fragments of multiple elements without keys', async () => { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 17b037cc043e8..6bed7187dd07e 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -160,9 +160,10 @@ export const enableInfiniteRenderLoopDetection = false; export const enableUseEffectCRUDOverload = false; export const enableFastAddPropertiesInDiffing = true; - export const enableLazyPublicInstanceInFabric = false; +export const enableFragmentRefs = false; + // ----------------------------------------------------------------------------- // Ready for next major. // diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 08c5781345ff7..b9e9bae96cb7d 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -83,6 +83,7 @@ export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableScrollEndPolyfill = true; +export const enableFragmentRefs = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 9b878754736ce..baeef0b56483d 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -76,6 +76,8 @@ export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableFragmentRefs = false; + // Profiling Only export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index f5deddae739be..1e3eedc9e2255 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -76,6 +76,8 @@ export const enableFastAddPropertiesInDiffing = true; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableFragmentRefs = false; + // TODO: This must be in sync with the main ReactFeatureFlags file because // the Test Renderer's value must be the same as the one used by the // react package. diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 4df92a5c90ef8..f2fa119800fa8 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -71,6 +71,7 @@ export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableFragmentRefs = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index b15f54484b029..2342b959f99f8 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -87,5 +87,7 @@ export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableFragmentRefs = false; + // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 3fe3b7a0a3e29..0116e160b643e 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -41,6 +41,7 @@ export const enableLazyPublicInstanceInFabric = false; export const enableViewTransition = __VARIANT__; export const enableComponentPerformanceTrack = __VARIANT__; export const enableScrollEndPolyfill = __VARIANT__; +export const enableFragmentRefs = __VARIANT__; // TODO: These flags are hard-coded to the default values used in open source. // Update the tests so that they pass in either mode, then set these diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 07e7e1f51aa17..0584af1f81826 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -39,6 +39,7 @@ export const { enableViewTransition, enableComponentPerformanceTrack, enableScrollEndPolyfill, + enableFragmentRefs, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build. From 75c979847f1c6dd954860f17b4dc181ad7c2891e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Wed, 12 Mar 2025 14:20:35 -0400 Subject: [PATCH 102/300] Ignore AbortError for gestures (#32579) Follow up to #32540. We do allow gestures to be cancelled early (we call skipTransition) if the gesture stops before it has even started. This happens in the fixture when we auto-scroll. --- .../src/client/ReactFiberConfigDOM.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 4b42c032d1be9..cdaaec4b3eeba 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1577,7 +1577,10 @@ function cancelAllViewTransitionAnimations(scope: Element) { // either cached the font or preloaded it earlier. const SUSPENSEY_FONT_TIMEOUT = 500; -function customizeViewTransitionError(error: Object): mixed { +function customizeViewTransitionError( + error: Object, + ignoreAbort: boolean, +): mixed { if (typeof error === 'object' && error !== null) { switch (error.name) { case 'TimeoutError': { @@ -1596,6 +1599,9 @@ function customizeViewTransitionError(error: Object): mixed { break; } case 'AbortError': { + if (ignoreAbort) { + return null; + } if (__DEV__) { // eslint-disable-next-line react-internal/prod-error-codes return new Error( @@ -1708,7 +1714,7 @@ export function startViewTransition( ownerDocument.__reactViewTransition = transition; const handleError = (error: mixed) => { try { - error = customizeViewTransitionError(error); + error = customizeViewTransitionError(error, false); if (error !== null) { errorCallback(error); } @@ -1998,7 +2004,7 @@ export function startGestureTransition( : readyCallback; const handleError = (error: mixed) => { try { - error = customizeViewTransitionError(error); + error = customizeViewTransitionError(error, true); if (error !== null) { errorCallback(error); } From 3456b6634abb4a8a2ef500397b94606ce4b6dda7 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:08:55 -0400 Subject: [PATCH 103/300] [compiler] Repro for object spread and Array.from with mutable iterators (#32520) See newly added test fixtures. Repros fixed in later prs of this stack --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32520). * #32522 * #32521 * __->__ #32520 --- ...ug-array-spread-mutable-iterator.expect.md | 87 +++++++++++++++ .../bug-array-spread-mutable-iterator.js | 32 ++++++ ...todo-granular-iterator-semantics.expect.md | 96 +++++++++++++++++ .../todo-granular-iterator-semantics.js | 36 +++++++ .../todo-type-inference-array-from.expect.md | 102 ++++++++++++++++++ .../todo-type-inference-array-from.js | 36 +++++++ .../packages/snap/src/SproutTodoFilter.ts | 1 + 7 files changed, 390 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md new file mode 100644 index 0000000000000..70f41f40477ae --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md @@ -0,0 +1,87 @@ + +## Input + +```javascript +/** + * TODO: object spreads should have conditionally mutate semantics + * Found differences in evaluator results + * Non-forget (expected): + * (kind: ok) [3,1,5,4] + * [3,1,5,4] + * [4,1,5,4] + * Forget: + * (kind: ok) [3,1,5,4] + * [3,1,5,4] + * [4] + */ + +function useBar({arg}) { + 'use memo'; + + /** + * Note that mutableIterator is mutated by the later object spread. Therefore, + * `s.values()` should be memoized within the same block as the object spread. + * In terms of compiler internals, they should have the same reactive scope. + */ + const s = new Set([1, 5, 4]); + const mutableIterator = s.values(); + + return [arg, ...mutableIterator]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; /** + * TODO: object spreads should have conditionally mutate semantics + * Found differences in evaluator results + * Non-forget (expected): + * (kind: ok) [3,1,5,4] + * [3,1,5,4] + * [4,1,5,4] + * Forget: + * (kind: ok) [3,1,5,4] + * [3,1,5,4] + * [4] + */ + +function useBar(t0) { + "use memo"; + const $ = _c(3); + const { arg } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const s = new Set([1, 5, 4]); + t1 = s.values(); + $[0] = t1; + } else { + t1 = $[0]; + } + const mutableIterator = t1; + let t2; + if ($[1] !== arg) { + t2 = [arg, ...mutableIterator]; + $[1] = arg; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{ arg: 3 }], + sequentialRenders: [{ arg: 3 }, { arg: 3 }, { arg: 4 }], +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js new file mode 100644 index 0000000000000..c83a9e53e6acc --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js @@ -0,0 +1,32 @@ +/** + * TODO: object spreads should have conditionally mutate semantics + * Found differences in evaluator results + * Non-forget (expected): + * (kind: ok) [3,1,5,4] + * [3,1,5,4] + * [4,1,5,4] + * Forget: + * (kind: ok) [3,1,5,4] + * [3,1,5,4] + * [4] + */ + +function useBar({arg}) { + 'use memo'; + + /** + * Note that mutableIterator is mutated by the later object spread. Therefore, + * `s.values()` should be memoized within the same block as the object spread. + * In terms of compiler internals, they should have the same reactive scope. + */ + const s = new Set([1, 5, 4]); + const mutableIterator = s.values(); + + return [arg, ...mutableIterator]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md new file mode 100644 index 0000000000000..1ba01dc5bf4df --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md @@ -0,0 +1,96 @@ + +## Input + +```javascript +import {useIdentity, ValidateMemoization} from 'shared-runtime'; + +/** + * TODO fixture for granular iterator semantics: + * 1. ConditionallyMutate the iterator itself, depending on whether the iterator + * is a mutable iterator. + * 2. Capture effect on elements within the iterator. + */ +function Validate({x, input}) { + 'use no memo'; + return ( + <> + <ValidateMemoization inputs={[]} output={x[0]} onlyCheckCompiled={true} /> + <ValidateMemoization + inputs={[input]} + output={x[1]} + onlyCheckCompiled={true} + /> + </> + ); +} +function useFoo(input) { + 'use memo'; + /** + * TODO: We should be able to memoize {} separately from `x`. + */ + const x = Array.from([{}]); + useIdentity(); + x.push([input]); + return <Validate x={x} input={input} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [1], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, ValidateMemoization } from "shared-runtime"; + +/** + * TODO fixture for granular iterator semantics: + * 1. ConditionallyMutate the iterator itself, depending on whether the iterator + * is a mutable iterator. + * 2. Capture effect on elements within the iterator. + */ +function Validate({ x, input }) { + "use no memo"; + return ( + <> + <ValidateMemoization inputs={[]} output={x[0]} onlyCheckCompiled={true} /> + <ValidateMemoization + inputs={[input]} + output={x[1]} + onlyCheckCompiled={true} + /> + </> + ); +} +function useFoo(input) { + "use memo"; + const $ = _c(3); + + const x = Array.from([{}]); + useIdentity(); + x.push([input]); + let t0; + if ($[0] !== input || $[1] !== x) { + t0 = <Validate x={x} input={input} />; + $[0] = input; + $[1] = x; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [1], +}; + +``` + +### Eval output +(kind: ok) <div>{"inputs":[],"output":{}}</div><div>{"inputs":[1],"output":[1]}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js new file mode 100644 index 0000000000000..27d861692cfa4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js @@ -0,0 +1,36 @@ +import {useIdentity, ValidateMemoization} from 'shared-runtime'; + +/** + * TODO fixture for granular iterator semantics: + * 1. ConditionallyMutate the iterator itself, depending on whether the iterator + * is a mutable iterator. + * 2. Capture effect on elements within the iterator. + */ +function Validate({x, input}) { + 'use no memo'; + return ( + <> + <ValidateMemoization inputs={[]} output={x[0]} onlyCheckCompiled={true} /> + <ValidateMemoization + inputs={[input]} + output={x[1]} + onlyCheckCompiled={true} + /> + </> + ); +} +function useFoo(input) { + 'use memo'; + /** + * TODO: We should be able to memoize {} separately from `x`. + */ + const x = Array.from([{}]); + useIdentity(); + x.push([input]); + return <Validate x={x} input={input} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [1], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md new file mode 100644 index 0000000000000..6061464afce78 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md @@ -0,0 +1,102 @@ + +## Input + +```javascript +import {useIdentity, ValidateMemoization} from 'shared-runtime'; + +/** + * Fixture to assert that we can infer the type and effects of an array created + * with `Array.from`. + */ +function Validate({x, val1, val2}) { + 'use no memo'; + return ( + <> + <ValidateMemoization + inputs={[val1]} + output={x[0]} + onlyCheckCompiled={true} + /> + <ValidateMemoization + inputs={[val2]} + output={x[1]} + onlyCheckCompiled={true} + /> + </> + ); +} +function useFoo({val1, val2}) { + 'use memo'; + const x = Array.from([]); + useIdentity(); + x.push([val1]); + x.push([val2]); + return <Validate x={x} val1={val1} val2={val2} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{val1: 1, val2: 2}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, ValidateMemoization } from "shared-runtime"; + +/** + * Fixture to assert that we can infer the type and effects of an array created + * with `Array.from`. + */ +function Validate({ x, val1, val2 }) { + "use no memo"; + return ( + <> + <ValidateMemoization + inputs={[val1]} + output={x[0]} + onlyCheckCompiled={true} + /> + + <ValidateMemoization + inputs={[val2]} + output={x[1]} + onlyCheckCompiled={true} + /> + </> + ); +} +function useFoo(t0) { + "use memo"; + const $ = _c(4); + const { val1, val2 } = t0; + + const x = Array.from([]); + useIdentity(); + x.push([val1]); + x.push([val2]); + let t1; + if ($[0] !== val1 || $[1] !== val2 || $[2] !== x) { + t1 = <Validate x={x} val1={val1} val2={val2} />; + $[0] = val1; + $[1] = val2; + $[2] = x; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ val1: 1, val2: 2 }], +}; + +``` + +### Eval output +(kind: ok) <div>{"inputs":[1],"output":[1]}</div><div>{"inputs":[2],"output":[2]}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js new file mode 100644 index 0000000000000..d1a80a4ea7090 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js @@ -0,0 +1,36 @@ +import {useIdentity, ValidateMemoization} from 'shared-runtime'; + +/** + * Fixture to assert that we can infer the type and effects of an array created + * with `Array.from`. + */ +function Validate({x, val1, val2}) { + 'use no memo'; + return ( + <> + <ValidateMemoization + inputs={[val1]} + output={x[0]} + onlyCheckCompiled={true} + /> + <ValidateMemoization + inputs={[val2]} + output={x[1]} + onlyCheckCompiled={true} + /> + </> + ); +} +function useFoo({val1, val2}) { + 'use memo'; + const x = Array.from([]); + useIdentity(); + x.push([val1]); + x.push([val2]); + return <Validate x={x} val1={val1} val2={val2} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{val1: 1, val2: 2}], +}; diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index 0817bbf89c582..bbed77c35a90d 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -462,6 +462,7 @@ const skipFilter = new Set([ // bugs 'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr', + 'bug-array-spread-mutable-iterator', `bug-capturing-func-maybealias-captured-mutate`, 'bug-aliased-capture-aliased-mutate', 'bug-aliased-capture-mutate', From 4ab827b869dfb89f34df1e38beae0d521b960f23 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 17:02:10 -0400 Subject: [PATCH 104/300] [compiler] Dedupe @babel/types (#32581) Extracting portions of #32416 for easier review. This PR dedupes @babel/types to resolve to 7.26.3, for compatibility in the root workspace where eslint-plugin-react-hooks resides. I also needed to update @babel/preset-typescript in snap. The compiler changes in HIR and ReactiveScopes were needed due to types changing. Notably, Babel [added support for optional chaining assignment](https://github.com/babel/babel/pull/15751) (currently [Stage 1](https://github.com/tc39/proposal-optional-chaining-assignment)), so in the latest versions of @babel/types, AssignmentExpression.left can now also be of t.OptionalMemberExpression. Given that this is in Stage 1, the compiler probably shouldn't support this syntax, so this PR updates HIR to bailout with a TODO if there is a non LVal on the lhs of an Assignment Expression. There was also a small superficial SourceLocation change needed in `InferReactiveScopeVariables` as Babel 8 changes were [accidentally released in 7](https://github.com/babel/babel/issues/10746#issuecomment-2699146670). It doesn't affect our analysis so it seems fine to just update with the new properties. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32581). * #32582 * __->__ #32581 Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- compiler/package.json | 5 +- .../babel-plugin-react-compiler/package.json | 2 +- .../src/HIR/BuildHIR.ts | 37 ++- .../src/HIR/FindContextIdentifiers.ts | 16 +- .../InferReactiveScopeVariables.ts | 6 +- .../babel-plugin-react-compiler/src/index.ts | 2 + .../eslint-plugin-react-compiler/package.json | 2 +- compiler/packages/snap/package.json | 2 +- compiler/yarn.lock | 226 +++++++++++------- 9 files changed, 194 insertions(+), 104 deletions(-) diff --git a/compiler/package.json b/compiler/package.json index 3d6a0f9c8605e..282f707d4e1eb 100644 --- a/compiler/package.json +++ b/compiler/package.json @@ -26,6 +26,7 @@ "react-is": "0.0.0-experimental-4beb1fd8-20241118" }, "devDependencies": { + "@babel/types": "^7.26.0", "@tsconfig/strictest": "^2.0.5", "concurrently": "^7.4.0", "esbuild": "^0.25.0", @@ -37,13 +38,15 @@ "prettier-plugin-hermes-parser": "^0.26.0", "prompt-promise": "^1.0.3", "rimraf": "^5.0.10", + "to-fast-properties": "^2.0.0", "tsup": "^8.4.0", "typescript": "^5.4.3", "wait-on": "^7.2.0", "yargs": "^17.7.2" }, "resolutions": { - "rimraf": "5.0.10" + "rimraf": "5.0.10", + "@babel/types": "7.26.3" }, "packageManager": "yarn@1.22.22" } diff --git a/compiler/packages/babel-plugin-react-compiler/package.json b/compiler/packages/babel-plugin-react-compiler/package.json index bc49988a6a8e7..7544903f8f4d4 100644 --- a/compiler/packages/babel-plugin-react-compiler/package.json +++ b/compiler/packages/babel-plugin-react-compiler/package.json @@ -20,7 +20,7 @@ "watch": "yarn build --watch" }, "dependencies": { - "@babel/types": "^7.19.0" + "@babel/types": "^7.26.0" }, "devDependencies": { "@babel/core": "^7.2.0", diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index c60bbd0e1ba3d..8a7dd10c245a2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -1909,16 +1909,31 @@ function lowerExpression( if (operator === '=') { const left = expr.get('left'); - return lowerAssignment( - builder, - left.node.loc ?? GeneratedSource, - InstructionKind.Reassign, - left, - lowerExpressionToTemporary(builder, expr.get('right')), - left.isArrayPattern() || left.isObjectPattern() - ? 'Destructure' - : 'Assignment', - ); + if (left.isLVal()) { + return lowerAssignment( + builder, + left.node.loc ?? GeneratedSource, + InstructionKind.Reassign, + left, + lowerExpressionToTemporary(builder, expr.get('right')), + left.isArrayPattern() || left.isObjectPattern() + ? 'Destructure' + : 'Assignment', + ); + } else { + /** + * OptionalMemberExpressions as the left side of an AssignmentExpression are Stage 1 and + * not supported by React Compiler yet. + */ + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Unsupported syntax on the left side of an AssignmentExpression`, + description: `Expected an LVal, got: ${left.type}`, + severity: ErrorSeverity.Todo, + loc: left.node.loc ?? null, + suggestions: null, + }); + return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc}; + } } const operators: { @@ -2091,7 +2106,7 @@ function lowerExpression( propName = namePath.node.name; if (propName.indexOf(':') !== -1) { builder.errors.push({ - reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name \`${name}\``, + reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name \`${propName}\``, severity: ErrorSeverity.Todo, loc: namePath.node.loc ?? null, suggestions: null, diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/FindContextIdentifiers.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/FindContextIdentifiers.ts index a4d23022b81b7..ecc534eb729ec 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/FindContextIdentifiers.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/FindContextIdentifiers.ts @@ -63,8 +63,20 @@ export function findContextIdentifiers( state: FindContextIdentifierState, ): void { const left = path.get('left'); - const currentFn = state.currentFn.at(-1) ?? null; - handleAssignment(currentFn, state.identifiers, left); + if (left.isLVal()) { + const currentFn = state.currentFn.at(-1) ?? null; + handleAssignment(currentFn, state.identifiers, left); + } else { + /** + * OptionalMemberExpressions as the left side of an AssignmentExpression are Stage 1 and + * not supported by React Compiler yet. + */ + CompilerError.throwTodo({ + reason: `Unsupported syntax on the left side of an AssignmentExpression`, + description: `Expected an LVal, got: ${left.type}`, + loc: left.node.loc ?? null, + }); + } }, UpdateExpression( path: NodePath<t.UpdateExpression>, diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts index 1f104d8592fab..0c1fd759bd5b8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -178,11 +178,15 @@ function mergeLocation(l: SourceLocation, r: SourceLocation): SourceLocation { return l; } else { return { + filename: l.filename, + identifierName: l.identifierName, start: { + index: Math.min(l.start.index, r.start.index), line: Math.min(l.start.line, r.start.line), column: Math.min(l.start.column, r.start.column), }, end: { + index: Math.max(l.end.index, r.end.index), line: Math.max(l.end.line, r.end.line), column: Math.max(l.end.column, r.end.column), }, @@ -202,7 +206,7 @@ export function inRange( return id >= range.start && id < range.end; } -function mayAllocate(env: Environment, instruction: Instruction): boolean { +function mayAllocate(_env: Environment, instruction: Instruction): boolean { const {value} = instruction; switch (value.kind) { case 'Destructure': { diff --git a/compiler/packages/babel-plugin-react-compiler/src/index.ts b/compiler/packages/babel-plugin-react-compiler/src/index.ts index 188c244d9ef2c..fa330f5582fc1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/index.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/index.ts @@ -22,6 +22,8 @@ export { findDirectiveEnablingMemoization, findDirectiveDisablingMemoization, type CompilerPipelineValue, + type Logger, + type LoggerEvent, type PluginOptions, } from './Entrypoint'; export { diff --git a/compiler/packages/eslint-plugin-react-compiler/package.json b/compiler/packages/eslint-plugin-react-compiler/package.json index 6a3be2b9ebfc9..2dd191f033dcf 100644 --- a/compiler/packages/eslint-plugin-react-compiler/package.json +++ b/compiler/packages/eslint-plugin-react-compiler/package.json @@ -22,7 +22,7 @@ "devDependencies": { "@babel/preset-env": "^7.22.4", "@babel/preset-typescript": "^7.18.6", - "@babel/types": "^7.19.0", + "@babel/types": "^7.26.0", "@types/eslint": "^8.56.12", "@types/node": "^20.2.5", "babel-jest": "^29.0.3", diff --git a/compiler/packages/snap/package.json b/compiler/packages/snap/package.json index 280e9c6840dce..ab6c7846ad61a 100644 --- a/compiler/packages/snap/package.json +++ b/compiler/packages/snap/package.json @@ -23,7 +23,7 @@ "@babel/code-frame": "^7.22.5", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/preset-flow": "^7.7.4", - "@babel/preset-typescript": "^7.18.6", + "@babel/preset-typescript": "^7.26.0", "@parcel/watcher": "^2.1.0", "@testing-library/react": "^13.4.0", "babel-plugin-idx": "^3.0.3", diff --git a/compiler/yarn.lock b/compiler/yarn.lock index de5cd4d228b46..065905110bf19 100644 --- a/compiler/yarn.lock +++ b/compiler/yarn.lock @@ -126,6 +126,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -140,6 +151,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz#c9b83d1ba74e163e023f008a3d3204588a7ceb60" @@ -212,6 +230,19 @@ "@babel/helper-split-export-declaration" "^7.18.6" semver "^6.3.0" +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70" @@ -308,6 +339,14 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -395,6 +434,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" @@ -415,6 +461,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== +"@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -457,6 +508,15 @@ "@babel/helper-member-expression-to-functions" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -492,6 +552,14 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -513,31 +581,6 @@ dependencies: "@babel/types" "^7.24.7" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-string-parser@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" - integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - -"@babel/helper-string-parser@^7.24.8": - version "7.24.8" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" - integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== - "@babel/helper-string-parser@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" @@ -682,6 +725,13 @@ dependencies: "@babel/types" "^7.26.3" +"@babel/parser@^7.26.10", "@babel/parser@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + "@babel/parser@^7.7.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" @@ -827,6 +877,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -890,6 +947,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-syntax-typescript@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" @@ -1120,6 +1184,14 @@ "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-simple-access" "^7.21.5" +"@babel/plugin-transform-modules-commonjs@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== + dependencies: + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-modules-commonjs@^7.8.3": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" @@ -1339,6 +1411,17 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-typescript" "^7.18.6" +"@babel/plugin-transform-typescript@^7.25.9": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz#2e9caa870aa102f50d7125240d9dbf91334b0950" + integrity sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-syntax-typescript" "^7.25.9" + "@babel/plugin-transform-unicode-escapes@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" @@ -1497,6 +1580,17 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-typescript" "^7.18.6" +"@babel/preset-typescript@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-typescript" "^7.25.9" + "@babel/register@^7.0.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" @@ -1581,6 +1675,15 @@ "@babel/parser" "^7.25.9" "@babel/types" "^7.25.9" +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@babel/template@^7.3.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" @@ -1618,69 +1721,20 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" - integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== +"@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.10", "@babel/types@^7.19.0", "@babel/types@^7.2.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" - integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== - dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.2.2", "@babel/types@^7.20.2", "@babel/types@^7.21.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" - integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.4.4": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" - integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== - dependencies: - "@babel/helper-string-parser" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" - integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - to-fast-properties "^2.0.0" - -"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.6", "@babel/types@^7.7.4": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" - integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== - dependencies: - "@babel/helper-string-parser" "^7.24.8" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" -"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": +"@babel/types@7.26.3", "@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.22.5", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.10", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== From e0e98d956074f95975a5851a8668fb48f13cb325 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 17:47:12 -0400 Subject: [PATCH 105/300] [playground] Update various deps (#32583) Updates various deps to align with the rest of the codebase. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32583). * #32584 * __->__ #32583 --- compiler/apps/playground/next-env.d.ts | 2 +- compiler/apps/playground/package.json | 16 +- compiler/apps/playground/yarn.lock | 300 ++++++++++++++++++------- 3 files changed, 220 insertions(+), 98 deletions(-) diff --git a/compiler/apps/playground/next-env.d.ts b/compiler/apps/playground/next-env.d.ts index 40c3d68096c27..1b3be0840f3f6 100644 --- a/compiler/apps/playground/next-env.d.ts +++ b/compiler/apps/playground/next-env.d.ts @@ -2,4 +2,4 @@ /// <reference types="next/image-types/global" /> // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/compiler/apps/playground/package.json b/compiler/apps/playground/package.json index b8360ae06f5a7..152ce0ea83c36 100644 --- a/compiler/apps/playground/package.json +++ b/compiler/apps/playground/package.json @@ -22,9 +22,9 @@ "@babel/plugin-transform-block-scoping": "^7.18.9", "@babel/plugin-transform-modules-commonjs": "^7.18.9", "@babel/preset-react": "^7.18.9", - "@babel/preset-typescript": "^7.18.9", + "@babel/preset-typescript": "^7.26.0", "@babel/traverse": "^7.18.9", - "@babel/types": "7.18.9", + "@babel/types": "7.26.3", "@heroicons/react": "^1.0.6", "@monaco-editor/react": "^4.4.6", "@playwright/test": "^1.42.1", @@ -39,13 +39,13 @@ "prettier": "^3.3.3", "pretty-format": "^29.3.1", "re-resizable": "^6.9.16", - "react": "19.0.0-rc-77b637d6-20241016", - "react-dom": "19.0.0-rc-77b637d6-20241016" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { "@types/node": "18.11.9", - "@types/react": "npm:types-react@19.0.0-rc.1", - "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "autoprefixer": "^10.4.13", "clsx": "^1.2.1", "concurrently": "^7.4.0", @@ -55,9 +55,5 @@ "postcss": "^8.4.31", "tailwindcss": "^3.2.4", "wait-on": "^7.2.0" - }, - "resolutions": { - "@types/react": "npm:types-react@19.0.0-rc.1", - "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1" } } diff --git a/compiler/apps/playground/yarn.lock b/compiler/apps/playground/yarn.lock index ea0487b01c901..ff74a59aa2dbc 100644 --- a/compiler/apps/playground/yarn.lock +++ b/compiler/apps/playground/yarn.lock @@ -23,6 +23,15 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.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== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/compat-data@^7.25.2": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb" @@ -59,6 +68,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz#5373c7bc8366b12a033b4be1ac13a206c6656aab" @@ -66,6 +86,13 @@ dependencies: "@babel/types" "^7.24.7" +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-compilation-targets@^7.25.2": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c" @@ -77,26 +104,26 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.25.0": - version "7.25.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz#57eaf1af38be4224a9d9dd01ddde05b741f50e14" - integrity sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.24.7" - "@babel/helper-member-expression-to-functions" "^7.24.8" - "@babel/helper-optimise-call-expression" "^7.24.7" - "@babel/helper-replace-supers" "^7.25.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" - "@babel/traverse" "^7.25.4" +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" semver "^6.3.1" -"@babel/helper-member-expression-to-functions@^7.24.8": - version "7.24.8" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz#6155e079c913357d24a4c20480db7c712a5c3fb6" - integrity sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA== +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== dependencies: - "@babel/traverse" "^7.24.8" - "@babel/types" "^7.24.8" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" "@babel/helper-module-imports@^7.24.7": version "7.24.7" @@ -106,6 +133,14 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-transforms@^7.24.8", "@babel/helper-module-transforms@^7.25.2": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6" @@ -116,26 +151,40 @@ "@babel/helper-validator-identifier" "^7.24.7" "@babel/traverse" "^7.25.2" -"@babel/helper-optimise-call-expression@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz#8b0a0456c92f6b323d27cfd00d1d664e76692a0f" - integrity sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: - "@babel/types" "^7.24.7" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878" integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg== -"@babel/helper-replace-supers@^7.25.0": - version "7.25.0" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz#ff44deac1c9f619523fe2ca1fd650773792000a9" - integrity sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg== +"@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.24.8" - "@babel/helper-optimise-call-expression" "^7.24.7" - "@babel/traverse" "^7.25.0" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" "@babel/helper-simple-access@^7.24.7": version "7.24.7" @@ -145,29 +194,44 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-skip-transparent-expression-wrappers@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" - integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" "@babel/helper-string-parser@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.24.7": +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.24.7", "@babel/helper-validator-option@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + "@babel/helpers@^7.25.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.6.tgz#57ee60141829ba2e102f30711ffe3afab357cc60" @@ -193,6 +257,13 @@ dependencies: "@babel/types" "^7.25.6" +"@babel/parser@^7.26.10", "@babel/parser@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + "@babel/plugin-syntax-jsx@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" @@ -200,13 +271,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-syntax-typescript@^7.18.9", "@babel/plugin-syntax-typescript@^7.24.7": +"@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-typescript@^7.18.9": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz#04db9ce5a9043d9c635e75ae7969a2cd50ca97ff" integrity sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg== dependencies: "@babel/helper-plugin-utils" "^7.24.8" +"@babel/plugin-syntax-typescript@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-block-scoping@^7.18.9": version "7.25.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz#23a6ed92e6b006d26b1869b1c91d1b917c2ea2ac" @@ -214,7 +299,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.8" -"@babel/plugin-transform-modules-commonjs@^7.18.9", "@babel/plugin-transform-modules-commonjs@^7.24.7": +"@babel/plugin-transform-modules-commonjs@^7.18.9": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz#ab6421e564b717cb475d6fff70ae7f103536ea3c" integrity sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA== @@ -223,6 +308,14 @@ "@babel/helper-plugin-utils" "^7.24.8" "@babel/helper-simple-access" "^7.24.7" +"@babel/plugin-transform-modules-commonjs@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== + dependencies: + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-react-display-name@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz#9caff79836803bc666bcfe210aeb6626230c293b" @@ -256,16 +349,16 @@ "@babel/helper-annotate-as-pure" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-typescript@^7.24.7": - version "7.25.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz#237c5d10de6d493be31637c6b9fa30b6c5461add" - integrity sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A== +"@babel/plugin-transform-typescript@^7.25.9": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz#2e9caa870aa102f50d7125240d9dbf91334b0950" + integrity sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw== dependencies: - "@babel/helper-annotate-as-pure" "^7.24.7" - "@babel/helper-create-class-features-plugin" "^7.25.0" - "@babel/helper-plugin-utils" "^7.24.8" - "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" - "@babel/plugin-syntax-typescript" "^7.24.7" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-syntax-typescript" "^7.25.9" "@babel/preset-react@^7.18.9": version "7.24.7" @@ -279,16 +372,16 @@ "@babel/plugin-transform-react-jsx-development" "^7.24.7" "@babel/plugin-transform-react-pure-annotations" "^7.24.7" -"@babel/preset-typescript@^7.18.9": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz#66cd86ea8f8c014855671d5ea9a737139cbbfef1" - integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== +"@babel/preset-typescript@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-validator-option" "^7.24.7" - "@babel/plugin-syntax-jsx" "^7.24.7" - "@babel/plugin-transform-modules-commonjs" "^7.24.7" - "@babel/plugin-transform-typescript" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-typescript" "^7.25.9" "@babel/runtime@^7.21.0": version "7.25.6" @@ -306,7 +399,16 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.18.9", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.4": +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.18.9", "@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -319,15 +421,28 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" - integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" -"@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": +"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== @@ -336,6 +451,14 @@ "@babel/helper-validator-identifier" "^7.24.7" to-fast-properties "^2.0.0" +"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@emnapi/runtime@^1.2.0": version "1.3.1" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" @@ -733,17 +856,15 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== -"@types/react-dom@npm:types-react-dom@19.0.0-rc.1": - version "19.0.0-rc.1" - resolved "https://registry.yarnpkg.com/types-react-dom/-/types-react-dom-19.0.0-rc.1.tgz#1d544d02c5df2a82d87c2eff979afa2e21a8317a" - integrity sha512-VSLZJl8VXCD0fAWp7DUTFUDCcZ8DVXOQmjhJMD03odgeFmu14ZQJHCXeETm3BEAhJqfgJaFkLnGkQv88sRx0fQ== - dependencies: - "@types/react" "*" +"@types/react-dom@^19.0.0": + version "19.0.4" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.0.4.tgz#bedba97f9346bd4c0fe5d39e689713804ec9ac89" + integrity sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg== -"@types/react@*", "@types/react@npm:types-react@19.0.0-rc.1": - version "19.0.0-rc.1" - resolved "https://registry.yarnpkg.com/types-react/-/types-react-19.0.0-rc.1.tgz#576d1a702f6d0cc5b24813a293913e5cbfeaa647" - integrity sha512-RshndUfqTW6K3STLPis8BtAYCGOkMbtvYsi90gmVNDZBXUyUc5juf2PE9LfS/JmOlUIRO8cWTS/1MTnmhjDqyQ== +"@types/react@^19.0.0": + version "19.0.10" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.0.10.tgz#d0c66dafd862474190fe95ce11a68de69ed2b0eb" + integrity sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g== dependencies: csstype "^3.0.2" @@ -2468,6 +2589,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +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-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -3025,12 +3151,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.0.0-rc-77b637d6-20241016: - version "19.0.0-rc-77b637d6-20241016" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-77b637d6-20241016.tgz#71afcba4abbd81a73e85086029202423cf85355e" - integrity sha512-xp5LvY+O6uvg0fNbSMyMXe0kbgzw6qn0mbqrdXStm4LBpFeMswLZ+XSNr+eJ0HyIiWrCw0rrXaVdqOxc9wtdKA== +react-dom@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0.tgz#43446f1f01c65a4cd7f7588083e686a6726cfb57" + integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ== dependencies: - scheduler "0.25.0-rc-77b637d6-20241016" + scheduler "^0.25.0" react-is@^16.13.1: version "16.13.1" @@ -3042,10 +3168,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.0.0-rc-77b637d6-20241016: - version "19.0.0-rc-77b637d6-20241016" - resolved "https://registry.yarnpkg.com/react/-/react-19.0.0-rc-77b637d6-20241016.tgz#9e20f116d0195979f192537e00a0fa1687680319" - integrity sha512-9A+i+PGSH/P4MezU4w38K9cbJuy0pzsXoPjPWIv6TQGCFmc5qCzC+8yce8dzfSEF1KJgCF2CLc5qtq/ePfiVqg== +react@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/react/-/react-19.0.0.tgz#6e1969251b9f108870aa4bff37a0ce9ddfaaabdd" + integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ== read-cache@^1.0.0: version "1.0.0" @@ -3167,10 +3293,10 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" -scheduler@0.25.0-rc-77b637d6-20241016: - version "0.25.0-rc-77b637d6-20241016" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-77b637d6-20241016.tgz#ab8f8d1cccc9668946caaa1103acdcdb5c871122" - integrity sha512-R5NTrZXJaW4Dj2jHmad2MTehpFq4yUQOxRKDNV7clP1q4Pz6RtUIcofdPnGUWM0krlJAw8DHd/4jT41pFK4iEg== +scheduler@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0.tgz#336cd9768e8cceebf52d3c80e3dcf5de23e7e015" + integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== semver@^6.3.1: version "6.3.1" From 26bca0005c7ef82a733c23f08baa1e2b8d702ec9 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 17:47:24 -0400 Subject: [PATCH 106/300] [playground] Wait for Monaco to fully load (#32584) I'm not sure what exactly is causing the flakiness in the playground e2e tests but I suspect it's some kind of timing issue. Let's try waiting for Monaco to be fully initialized before running tests. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32584). * __->__ #32584 * #32583 --- compiler/apps/playground/__tests__/e2e/page.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/apps/playground/__tests__/e2e/page.spec.ts b/compiler/apps/playground/__tests__/e2e/page.spec.ts index 3ba082cf62972..e2700e006ac5f 100644 --- a/compiler/apps/playground/__tests__/e2e/page.spec.ts +++ b/compiler/apps/playground/__tests__/e2e/page.spec.ts @@ -105,6 +105,9 @@ function nonReactFn() { test('editor should open successfully', async ({page}) => { await page.goto(`/`, {waitUntil: 'networkidle'}); + await page.waitForFunction( + () => typeof window['MonacoEnvironment'] !== 'undefined', + ); await page.screenshot({ fullPage: true, path: 'test-results/00-fresh-page.png', @@ -120,6 +123,9 @@ test('editor should compile from hash successfully', async ({page}) => { }; const hash = encodeStore(store); await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); + await page.waitForFunction( + () => typeof window['MonacoEnvironment'] !== 'undefined', + ); // User input from hash compiles await page.screenshot({ @@ -143,6 +149,9 @@ test('reset button works', async ({page}) => { }; const hash = encodeStore(store); await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); + await page.waitForFunction( + () => typeof window['MonacoEnvironment'] !== 'undefined', + ); // Reset button works page.on('dialog', dialog => dialog.accept()); @@ -166,6 +175,9 @@ TEST_CASE_INPUTS.forEach((t, idx) => }; const hash = encodeStore(store); await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); + await page.waitForFunction( + () => typeof window['MonacoEnvironment'] !== 'undefined', + ); await page.screenshot({ fullPage: true, path: `test-results/03-0${idx}-${t.name}.png`, From 5135f98795d13aeea6f009b537a660c7afbe17ed Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Wed, 12 Mar 2025 17:49:44 -0400 Subject: [PATCH 107/300] Add DOM fixture page for Fragment Ref (#32527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a page to the DOM fixture to test Fragment Refs. The first test case is for `addEventListener`/`removeEventListener`. Setting `enableFragmentRefs` to `__EXPERIMENTAL__` and building is required to run the fixture. <img width="872" alt="Screenshot 2025-03-05 at 12 58 57 PM" src="https://github.com/user-attachments/assets/fee498b7-fd96-4178-9e82-c46d4cb55c9b" /> --- fixtures/dom/src/components/Header.js | 1 + .../fixtures/fragment-refs/index.js | 104 ++++++++++++++++++ fixtures/dom/src/style.css | 13 +++ 3 files changed, 118 insertions(+) create mode 100644 fixtures/dom/src/components/fixtures/fragment-refs/index.js diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js index fe9709af00318..ae24b3223f643 100644 --- a/fixtures/dom/src/components/Header.js +++ b/fixtures/dom/src/components/Header.js @@ -89,6 +89,7 @@ class Header extends React.Component { <option value="/selection-events">Selection Events</option> <option value="/suspense">Suspense</option> <option value="/form-state">Form State</option> + <option value="/fragment-refs">Fragment Refs</option> </select> </label> <label htmlFor="global_version"> diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/index.js b/fixtures/dom/src/components/fixtures/fragment-refs/index.js new file mode 100644 index 0000000000000..bd4468126e43b --- /dev/null +++ b/fixtures/dom/src/components/fixtures/fragment-refs/index.js @@ -0,0 +1,104 @@ +import Fixture from '../../Fixture'; +import FixtureSet from '../../FixtureSet'; +import TestCase from '../../TestCase'; + +const React = window.React; +const {Fragment, useEffect, useRef, useState} = React; + +function WrapperComponent(props) { + return props.children; +} + +function handler(e) { + const text = e.currentTarget.innerText; + alert('You clicked: ' + text); +} + +export default function FragmentRefsPage() { + const fragmentRef = useRef(null); + const [extraChildCount, setExtraChildCount] = useState(0); + + React.useEffect(() => { + fragmentRef.current.addEventListener('click', handler); + + const lastFragmentRefValue = fragmentRef.current; + return () => { + lastFragmentRefValue.removeEventListener('click', handler); + }; + }); + + return ( + <FixtureSet title="Fragment Refs"> + <TestCase title="Event registration"> + <TestCase.Steps> + <li>Click one of the children, observe the alert</li> + <li>Add a new child, click it, observe the alert</li> + <li>Remove the event listeners, click a child, observe no alert</li> + <li> + Add the event listeners back, click a child, observe the alert + </li> + </TestCase.Steps> + + <TestCase.ExpectedResult> + <p> + Fragment refs can manage event listeners on the first level of host + children. This page loads with an effect that sets up click event + hanndlers on each child card. Clicking on a card will show an alert + with the card's text. + </p> + <p> + New child nodes will also have event listeners applied. Removed + nodes will have their listeners cleaned up. + </p> + </TestCase.ExpectedResult> + + <Fixture> + <div className="control-box" id="control-box"> + <div>Target count: {extraChildCount + 3}</div> + <button + onClick={() => { + setExtraChildCount(prev => prev + 1); + }}> + Add Child + </button> + <button + onClick={() => { + fragmentRef.current.addEventListener('click', handler); + }}> + Add click event listeners + </button> + <button + onClick={() => { + fragmentRef.current.removeEventListener('click', handler); + }}> + Remove click event listeners + </button> + <div class="card-container"> + <Fragment ref={fragmentRef}> + <div className="card" id="child-a"> + Child A + </div> + <div className="card" id="child-b"> + Child B + </div> + <WrapperComponent> + <div className="card" id="child-c"> + Child C + </div> + {Array.from({length: extraChildCount}).map((_, index) => ( + <div + className="card" + id={'extra-child-' + index} + key={index}> + Extra Child {index} + </div> + ))} + </WrapperComponent> + </Fragment> + </div> + </div> + </Fixture> + </TestCase> + </FixtureSet> + ); +} diff --git a/fixtures/dom/src/style.css b/fixtures/dom/src/style.css index cca207772adf1..568d8b4b0d1b5 100644 --- a/fixtures/dom/src/style.css +++ b/fixtures/dom/src/style.css @@ -309,3 +309,16 @@ th { tbody tr:nth-child(even) { background: #f0f0f0; } + +.card-container { + display: flex; + flex-wrap: wrap; +} + +.card { + background: white; + border-radius: 2px; + border: 1px solid rgba(0, 0, 0, 0.24); + margin: 10px; + padding: 10px; +} From 5de83dcc0f34dc196664cb200088c6253cb2cd34 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:27:15 -0400 Subject: [PATCH 108/300] [playground] Use onMount to check if the editor is available (#32586) Playground test flakiness seems to be fixed but adding this as an extra precaution --- .../playground/__tests__/e2e/page.spec.ts | 23 +++++++++---------- .../playground/components/Editor/Input.tsx | 3 +++ compiler/apps/playground/package.json | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/apps/playground/__tests__/e2e/page.spec.ts b/compiler/apps/playground/__tests__/e2e/page.spec.ts index e2700e006ac5f..48bb0eed4eda4 100644 --- a/compiler/apps/playground/__tests__/e2e/page.spec.ts +++ b/compiler/apps/playground/__tests__/e2e/page.spec.ts @@ -9,6 +9,13 @@ import {expect, test} from '@playwright/test'; import {encodeStore, type Store} from '../../lib/stores'; import {format} from 'prettier'; +function isMonacoLoaded(): boolean { + return ( + typeof window['MonacoEnvironment'] !== 'undefined' && + window['__MONACO_LOADED__'] === true + ); +} + function formatPrint(data: Array<string>): Promise<string> { return format(data.join(''), {parser: 'babel'}); } @@ -105,9 +112,7 @@ function nonReactFn() { test('editor should open successfully', async ({page}) => { await page.goto(`/`, {waitUntil: 'networkidle'}); - await page.waitForFunction( - () => typeof window['MonacoEnvironment'] !== 'undefined', - ); + await page.waitForFunction(isMonacoLoaded); await page.screenshot({ fullPage: true, path: 'test-results/00-fresh-page.png', @@ -123,9 +128,7 @@ test('editor should compile from hash successfully', async ({page}) => { }; const hash = encodeStore(store); await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); - await page.waitForFunction( - () => typeof window['MonacoEnvironment'] !== 'undefined', - ); + await page.waitForFunction(isMonacoLoaded); // User input from hash compiles await page.screenshot({ @@ -149,9 +152,7 @@ test('reset button works', async ({page}) => { }; const hash = encodeStore(store); await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); - await page.waitForFunction( - () => typeof window['MonacoEnvironment'] !== 'undefined', - ); + await page.waitForFunction(isMonacoLoaded); // Reset button works page.on('dialog', dialog => dialog.accept()); @@ -175,9 +176,7 @@ TEST_CASE_INPUTS.forEach((t, idx) => }; const hash = encodeStore(store); await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); - await page.waitForFunction( - () => typeof window['MonacoEnvironment'] !== 'undefined', - ); + await page.waitForFunction(isMonacoLoaded); await page.screenshot({ fullPage: true, path: `test-results/03-0${idx}-${t.name}.png`, diff --git a/compiler/apps/playground/components/Editor/Input.tsx b/compiler/apps/playground/components/Editor/Input.tsx index 34df68787db79..2adfdb512f716 100644 --- a/compiler/apps/playground/components/Editor/Input.tsx +++ b/compiler/apps/playground/components/Editor/Input.tsx @@ -89,6 +89,9 @@ export default function Input({errors, language}: Props): JSX.Element { _: editor.IStandaloneCodeEditor, monaco: Monaco, ) => void = (_, monaco) => { + if (typeof window !== 'undefined') { + window['__MONACO_LOADED__'] = true; + } setMonaco(monaco); const tscOptions = { diff --git a/compiler/apps/playground/package.json b/compiler/apps/playground/package.json index 152ce0ea83c36..795e9525ce82c 100644 --- a/compiler/apps/playground/package.json +++ b/compiler/apps/playground/package.json @@ -12,7 +12,7 @@ "vercel-build": "yarn build", "start": "next start", "lint": "next lint", - "test": "playwright test" + "test": "playwright test --workers=4" }, "dependencies": { "@babel/core": "^7.18.9", From f695f95290aa3560a00e8a3b617205ac9e087e0e Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:07:39 -0400 Subject: [PATCH 109/300] Update babel configs used in jest (#32588) Extracting portions of #32416 for easier review. This PR updates our babel configs (only used in jest) to support classes. Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32588). * #32592 * #32591 * #32590 * #32589 * __->__ #32588 Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- babel.config-ts.js | 2 ++ babel.config.js | 3 +- package.json | 3 +- yarn.lock | 81 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/babel.config-ts.js b/babel.config-ts.js index 20b35ec013e0f..45efe2a870820 100644 --- a/babel.config-ts.js +++ b/babel.config-ts.js @@ -7,6 +7,8 @@ module.exports = { plugins: [ '@babel/plugin-syntax-jsx', '@babel/plugin-transform-flow-strip-types', + ['@babel/plugin-transform-class-properties', {loose: true}], + '@babel/plugin-transform-classes', ], presets: [ ['@babel/preset-env', {targets: {node: 'current'}}], diff --git a/babel.config.js b/babel.config.js index f8a28b20cc87d..3498e2aebe86f 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,7 +4,8 @@ module.exports = { plugins: [ '@babel/plugin-syntax-jsx', '@babel/plugin-transform-flow-strip-types', - ['@babel/plugin-proposal-class-properties', {loose: true}], + ['@babel/plugin-transform-class-properties', {loose: true}], + '@babel/plugin-transform-classes', 'syntax-trailing-function-commas', [ '@babel/plugin-proposal-object-rest-spread', diff --git a/package.json b/package.json index 156eba91a7a05..d30b4034e8ca8 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "@babel/plugin-transform-arrow-functions": "^7.10.4", "@babel/plugin-transform-block-scoped-functions": "^7.10.4", "@babel/plugin-transform-block-scoping": "^7.11.1", - "@babel/plugin-transform-classes": "^7.10.4", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-classes": "^7.25.9", "@babel/plugin-transform-computed-properties": "^7.10.4", "@babel/plugin-transform-destructuring": "^7.10.4", "@babel/plugin-transform-for-of": "^7.10.4", diff --git a/yarn.lock b/yarn.lock index 7bc936afae4bd..ed53f3284334f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -103,6 +103,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + "@babel/core@^7.0.0": version "7.20.12" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" @@ -358,6 +363,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" @@ -740,7 +756,7 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/helper-replace-supers@^7.26.5": +"@babel/helper-replace-supers@^7.25.9", "@babel/helper-replace-supers@^7.26.5": version "7.26.5" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== @@ -1469,6 +1485,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-class-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" + integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-classes@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" @@ -1498,6 +1522,18 @@ "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/traverse" "^7.25.9" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" @@ -5480,6 +5516,16 @@ browserslist@^4.21.3, browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.13" +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + browserslist@^4.8.3: version "4.8.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.5.tgz#691af4e327ac877b25e7a3f7ee869c4ef36cdea3" @@ -5758,6 +5804,11 @@ caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001587: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz#4342712750d35f71ebba9fcac65e2cf8870013c3" integrity sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw== +caniuse-lite@^1.0.30001688: + version "1.0.30001703" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718" + integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ== + capture-stack-trace@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" @@ -7310,6 +7361,11 @@ electron-to-chromium@^1.4.284, electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.758.tgz#f39e530cae2ca4329a0f0e1840629d8d1da73156" integrity sha512-/o9x6TCdrYZBMdGeTifAP3wlF/gVT+TtWJe3BSmtNh92Mw81U9hrYwW9OAGUh+sEOX/yz5e34sksqRruZbjYrw== +electron-to-chromium@^1.5.73: + version "1.5.115" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.115.tgz#193dd534948b3ea77e3d95dd38ca12cdc01c76a9" + integrity sha512-MN1nahVHAQMOz6dz6bNZ7apgqc9InZy7Ja4DBEVCTdeiUcegbyOYE9bi/f2Z/z6ZxLi0RxLpyJ3EGe+4h3w73A== + electron@^23.1.2: version "23.1.2" resolved "https://registry.yarnpkg.com/electron/-/electron-23.1.2.tgz#f03e361c94f8dd2407963b5461d19aadea335316" @@ -7537,6 +7593,11 @@ escalade@^3.1.1, escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-goat@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" @@ -12534,6 +12595,11 @@ node-releases@^2.0.14, node-releases@^2.0.8: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + node-rsa@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.0.5.tgz#854dc1b275729d69bc25883f83ca80705db9262e" @@ -13366,6 +13432,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -16544,6 +16615,14 @@ update-browserslist-db@^1.0.10, update-browserslist-db@^1.0.13: escalade "^3.1.2" picocolors "^1.0.0" +update-browserslist-db@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-notifier@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" From 0e2402eb20de53e08fd0a767554f93e5e53af873 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:07:51 -0400 Subject: [PATCH 110/300] Update eslint fixtures (#32589) Extracting portions of #32416 for easier review. This PR lightly updates the build scripts for the eslint fixtures. Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32589). * #32592 * #32591 * #32590 * __->__ #32589 * #32588 --------- Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- fixtures/eslint-v6/build.mjs | 5 +++-- fixtures/eslint-v7/build.mjs | 5 +++-- fixtures/eslint-v8/build.mjs | 5 +++-- fixtures/eslint-v9/build.mjs | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/fixtures/eslint-v6/build.mjs b/fixtures/eslint-v6/build.mjs index ebcea15190dae..e0dd355ba4391 100644 --- a/fixtures/eslint-v6/build.mjs +++ b/fixtures/eslint-v6/build.mjs @@ -1,12 +1,13 @@ #!/usr/bin/env node -import {exec} from 'node:child_process'; +import {execSync} from 'node:child_process'; import {dirname, resolve} from 'node:path'; import {fileURLToPath} from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -exec('yarn build -r stable eslint-plugin-react-hooks', { +execSync('yarn build -r stable eslint-plugin-react-hooks', { cwd: resolve(__dirname, '..', '..'), + stdio: 'inherit', }); diff --git a/fixtures/eslint-v7/build.mjs b/fixtures/eslint-v7/build.mjs index ebcea15190dae..e0dd355ba4391 100644 --- a/fixtures/eslint-v7/build.mjs +++ b/fixtures/eslint-v7/build.mjs @@ -1,12 +1,13 @@ #!/usr/bin/env node -import {exec} from 'node:child_process'; +import {execSync} from 'node:child_process'; import {dirname, resolve} from 'node:path'; import {fileURLToPath} from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -exec('yarn build -r stable eslint-plugin-react-hooks', { +execSync('yarn build -r stable eslint-plugin-react-hooks', { cwd: resolve(__dirname, '..', '..'), + stdio: 'inherit', }); diff --git a/fixtures/eslint-v8/build.mjs b/fixtures/eslint-v8/build.mjs index ebcea15190dae..e0dd355ba4391 100644 --- a/fixtures/eslint-v8/build.mjs +++ b/fixtures/eslint-v8/build.mjs @@ -1,12 +1,13 @@ #!/usr/bin/env node -import {exec} from 'node:child_process'; +import {execSync} from 'node:child_process'; import {dirname, resolve} from 'node:path'; import {fileURLToPath} from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -exec('yarn build -r stable eslint-plugin-react-hooks', { +execSync('yarn build -r stable eslint-plugin-react-hooks', { cwd: resolve(__dirname, '..', '..'), + stdio: 'inherit', }); diff --git a/fixtures/eslint-v9/build.mjs b/fixtures/eslint-v9/build.mjs index ebcea15190dae..e0dd355ba4391 100644 --- a/fixtures/eslint-v9/build.mjs +++ b/fixtures/eslint-v9/build.mjs @@ -1,12 +1,13 @@ #!/usr/bin/env node -import {exec} from 'node:child_process'; +import {execSync} from 'node:child_process'; import {dirname, resolve} from 'node:path'; import {fileURLToPath} from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -exec('yarn build -r stable eslint-plugin-react-hooks', { +execSync('yarn build -r stable eslint-plugin-react-hooks', { cwd: resolve(__dirname, '..', '..'), + stdio: 'inherit', }); From f31779a112f3b5c9e56d651ad59c2db0bc236981 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:12:22 -0400 Subject: [PATCH 111/300] [ez] Run Prettier on eslint-plugin-react-compiler/src/types (#32590) Extracting portions of #32416 for easier review. This PR contains small formatting fixes. Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32590). * #32592 * #32591 * __->__ #32590 * #32589 * #32588 --------- Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- .../src/types/hermes-eslint.d.ts | 4 ++-- .../src/types/hermes-parser.d.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-eslint.d.ts b/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-eslint.d.ts index 539daf7142051..280b1e1e00a21 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-eslint.d.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-eslint.d.ts @@ -6,7 +6,7 @@ */ // v0.17.1 -declare module "hermes-eslint" { +declare module 'hermes-eslint' { // https://fburl.com/2vikhmaa type ParseForESLintOptions = { /** @@ -37,7 +37,7 @@ declare module "hermes-eslint" { /** * The source type of the script. */ - sourceType: "script" | "module"; + sourceType: 'script' | 'module'; /** * Ignore <fbt /> JSX elements when adding references to the module-level `React` variable. diff --git a/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-parser.d.ts b/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-parser.d.ts index 40771d183c06b..b624caf19c548 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-parser.d.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/types/hermes-parser.d.ts @@ -6,14 +6,14 @@ */ // v0.17.1 -declare module "hermes-parser" { +declare module 'hermes-parser' { type HermesParserOptions = { allowReturnOutsideFunction?: boolean; babel?: boolean; - flow?: "all" | "detect"; + flow?: 'all' | 'detect'; enableExperimentalComponentSyntax?: boolean; sourceFilename?: string; - sourceType?: "module" | "script" | "unambiguous"; + sourceType?: 'module' | 'script' | 'unambiguous'; tokens?: boolean; }; export function parse(code: string, options: Partial<HermesParserOptions>); From 8646349aebb514eeef7e01614fb4aeb2f2c7a6c8 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:12:33 -0400 Subject: [PATCH 112/300] [rollup] Fix codeFrame is not a function (#32591) Extracting portions of #32416 for easier review. Fixes a small issue where `codeFrame` is not a function when a rollup error was encountered. Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32591). * #32592 * __->__ #32591 * #32590 * #32589 * #32588 --------- Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- scripts/rollup/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 874b5b139f37c..71bfb1c3384ca 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -22,7 +22,7 @@ const useForks = require('./plugins/use-forks-plugin'); const dynamicImports = require('./plugins/dynamic-imports'); const Packaging = require('./packaging'); const {asyncRimRaf} = require('./utils'); -const codeFrame = require('@babel/code-frame'); +const codeFrame = require('@babel/code-frame').default; const Wrappers = require('./wrappers'); const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL; From a8ab2bcb627ed7c57d2e116b2e13ad5451259c2b Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:12:45 -0400 Subject: [PATCH 113/300] [rollup] Add support for running prebuild commands (#32592) Extracting portions of #32416 for easier review. Adds a new `prebuild` option to allow for a prebuild command to be run prior to building the bundle. Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32592). * __->__ #32592 * #32591 * #32590 * #32589 * #32588 --------- Co-authored-by: michael faith <michaelfaith@users.noreply.github.com> --- scripts/rollup/build.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 71bfb1c3384ca..02aa167b6332c 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -12,6 +12,7 @@ const stripBanner = require('rollup-plugin-strip-banner'); const chalk = require('chalk'); const resolve = require('@rollup/plugin-node-resolve').nodeResolve; const fs = require('fs'); +const childProcess = require('child_process'); const argv = require('minimist')(process.argv.slice(2)); const Modules = require('./modules'); const Bundles = require('./bundles'); @@ -812,6 +813,11 @@ function handleRollupError(error) { } } +function runShellCommand(command) { + console.log(chalk.dim('Running: ') + chalk.cyan(command)); + childProcess.execSync(command, {stdio: 'inherit', shell: true}); +} + async function buildEverything() { if (!argv['unsafe-partial']) { await asyncRimRaf('build'); @@ -859,6 +865,9 @@ async function buildEverything() { // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (const [bundle, bundleType] of bundles) { + if (bundle.prebuild) { + runShellCommand(bundle.prebuild); + } await createBundle(bundle, bundleType); } From 5ccfcd17ffa0adf9e7f5ba7fbf48e6bf6a4eb67e Mon Sep 17 00:00:00 2001 From: michael faith <michaelfaith@users.noreply.github.com> Date: Wed, 12 Mar 2025 20:43:06 -0500 Subject: [PATCH 114/300] feat(eslint-plugin-react-hooks): merge rule from eslint-plugin-react-compiler into `react-hooks` plugin (#32416) This change merges the `react-compiler` rule from `eslint-plugin-react-compiler` into the `eslint-plugin-react-hooks` plugin. In order to do the move in a way that keeps commit history with the moved files, but also no remove them from their origin until a future cleanup change can be done, I did the `git mv` first, and then recreated the files that were moved in their original places, as a separate commit. Unfortunately GH shows the moved files as new instead of the ones that are truly new. But in the IDE and `git blame`, commit history is intact with the moved files. Since this change adds new dependencies, and one of those dependencies has a higher `engines` declaration for `node` than what the plugin currently has, this is technically a breaking change and will have to go out as part of a major release. ### Related Changes - https://github.com/facebook/react/pull/32458 --------- Co-authored-by: Lauren Tan <poteto@users.noreply.github.com> --- babel.config-react-compiler.js | 19 + compiler/apps/playground/yarn.lock | 21 +- .../babel-plugin-react-compiler/tsconfig.json | 3 +- .../tsup.config.ts | 5 +- compiler/yarn.lock | 42 +- fixtures/eslint-v6/yarn.lock | 330 +++- fixtures/eslint-v7/yarn.lock | 340 +++- fixtures/eslint-v8/yarn.lock | 353 +++- fixtures/eslint-v9/yarn.lock | 353 +++- package.json | 11 +- .../__tests__/ReactCompilerRule-test.ts | 290 +++ .../ReactCompilerRuleTypescript-test.ts | 78 + .../eslint-plugin-react-hooks/package.json | 13 +- .../eslint-plugin-react-hooks/src/index.ts | 9 +- .../src/rules/ReactCompiler.ts | 346 ++++ .../src/types/hermes-eslint.d.ts | 58 + .../src/types/hermes-parser.d.ts | 20 + .../eslint-plugin-react-hooks/tsconfig.json | 30 +- .../ReactFreshBabelPlugin-test.js.snap | 170 +- .../transform-error-messages.js.snap | 14 +- scripts/jest/config.base.js | 6 + scripts/react-compiler/build-compiler.sh | 15 + scripts/react-compiler/link-compiler.sh | 17 + scripts/rollup/build.js | 29 +- scripts/rollup/bundles.js | 1 + yarn.lock | 1616 +++++++++++++---- 26 files changed, 3607 insertions(+), 582 deletions(-) create mode 100644 babel.config-react-compiler.js create mode 100644 packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts create mode 100644 packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts create mode 100644 packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts create mode 100644 packages/eslint-plugin-react-hooks/src/types/hermes-eslint.d.ts create mode 100644 packages/eslint-plugin-react-hooks/src/types/hermes-parser.d.ts create mode 100755 scripts/react-compiler/build-compiler.sh create mode 100755 scripts/react-compiler/link-compiler.sh diff --git a/babel.config-react-compiler.js b/babel.config-react-compiler.js new file mode 100644 index 0000000000000..11fb8c292e390 --- /dev/null +++ b/babel.config-react-compiler.js @@ -0,0 +1,19 @@ +'use strict'; + +/** + * HACK: @poteto React Compiler inlines Zod in its build artifact. Zod spreads values passed to .map + * which causes issues in @babel/plugin-transform-spread in loose mode, as it will result in + * {undefined: undefined} which fails to parse. + * + * [@babel/plugin-transform-block-scoping', {throwIfClosureRequired: true}] also causes issues with + * the built version of the compiler. The minimal set of plugins needed for this file is reexported + * from babel.config-ts. + * + * I will remove this hack later when we move eslint-plugin-react-hooks into the compiler directory. + **/ + +const baseConfig = require('./babel.config-ts'); + +module.exports = { + plugins: baseConfig.plugins, +}; diff --git a/compiler/apps/playground/yarn.lock b/compiler/apps/playground/yarn.lock index ff74a59aa2dbc..714a710ed5fe0 100644 --- a/compiler/apps/playground/yarn.lock +++ b/compiler/apps/playground/yarn.lock @@ -202,11 +202,6 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helper-string-parser@^7.24.8": - version "7.24.8" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" - integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== - "@babel/helper-string-parser@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" @@ -434,7 +429,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@7.26.3": +"@babel/types@7.26.3", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== @@ -442,15 +437,6 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": - version "7.25.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" - integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== - dependencies: - "@babel/helper-string-parser" "^7.24.8" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" - "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9": version "7.26.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" @@ -3643,11 +3629,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" diff --git a/compiler/packages/babel-plugin-react-compiler/tsconfig.json b/compiler/packages/babel-plugin-react-compiler/tsconfig.json index a0f0f60e566ad..4e4614aef434b 100644 --- a/compiler/packages/babel-plugin-react-compiler/tsconfig.json +++ b/compiler/packages/babel-plugin-react-compiler/tsconfig.json @@ -14,7 +14,8 @@ "target": "ES2015", // ideally turn off only during dev, or on a per-file basis "noUnusedLocals": false, - "removeComments": true + "removeComments": true, + "declaration": true, }, "exclude": [ "node_modules", diff --git a/compiler/packages/babel-plugin-react-compiler/tsup.config.ts b/compiler/packages/babel-plugin-react-compiler/tsup.config.ts index 3b255a6a7e957..12c04ec8a2480 100644 --- a/compiler/packages/babel-plugin-react-compiler/tsup.config.ts +++ b/compiler/packages/babel-plugin-react-compiler/tsup.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ bundle: true, format: 'cjs', platform: 'node', + target: 'es2015', banner: { js: `/** * Copyright (c) Meta Platforms, Inc. and affiliates. @@ -22,8 +23,6 @@ export default defineConfig({ * @nolint * @preventMunge * @preserve-invariant-messages - */ - -"use no memo";`, + */`, }, }); diff --git a/compiler/yarn.lock b/compiler/yarn.lock index 065905110bf19..16029bfc04379 100644 --- a/compiler/yarn.lock +++ b/compiler/yarn.lock @@ -279,31 +279,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== - dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/helper-function-name@^7.7.4": +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.7.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== @@ -701,11 +677,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== -"@babel/parser@^7.22.5": - version "7.22.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" - integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== - "@babel/parser@^7.24.4": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" @@ -1648,15 +1619,6 @@ "@babel/parser" "^7.21.9" "@babel/types" "^7.21.5" -"@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== - dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" - "@babel/template@^7.24.7": version "7.25.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" @@ -1734,7 +1696,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@7.26.3", "@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.22.5", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.10", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4": +"@babel/types@7.26.3", "@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.22.5", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.10", "@babel/types@^7.26.3", "@babel/types@^7.26.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== diff --git a/fixtures/eslint-v6/yarn.lock b/fixtures/eslint-v6/yarn.lock index 5af9623e877f3..e8e670f47a095 100644 --- a/fixtures/eslint-v6/yarn.lock +++ b/fixtures/eslint-v6/yarn.lock @@ -2,7 +2,15 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.0.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== @@ -11,11 +19,228 @@ js-tokens "^4.0.0" picocolors "^1.0.0" +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + +"@babel/core@^7.24.4": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384" + integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + +"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + +"@babel/plugin-transform-private-methods@^7.24.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + acorn-jsx@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -92,11 +317,26 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + 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.30001703" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718" + integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ== + chalk@^2.1.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -160,6 +400,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +convert-source-map@^2.0.0: + version "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== + cross-spawn@^6.0.5: version "6.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" @@ -171,7 +416,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -debug@^4.0.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.3.1: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -190,6 +435,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +electron-to-chromium@^1.5.73: + version "1.5.114" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3" + integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -200,6 +450,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -377,6 +632,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + glob-parent@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -396,6 +656,11 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^12.1.0: version "12.4.0" resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" @@ -413,6 +678,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== + +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -510,6 +787,11 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +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-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -520,6 +802,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +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== + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -533,6 +820,13 @@ lodash@^4.17.14, lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -577,6 +871,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -625,7 +924,7 @@ path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== -picocolors@^1.0.0: +picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -692,7 +991,7 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.1.2: +semver@^6.1.2, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -828,6 +1127,14 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +update-browserslist-db@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -863,3 +1170,18 @@ write@1.0.3: integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== dependencies: mkdirp "^0.5.1" + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +zod-validation-error@^3.0.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + +zod@^3.22.4: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== diff --git a/fixtures/eslint-v7/yarn.lock b/fixtures/eslint-v7/yarn.lock index 4a191de947f37..d2323b7c0c163 100644 --- a/fixtures/eslint-v7/yarn.lock +++ b/fixtures/eslint-v7/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -9,11 +17,160 @@ dependencies: "@babel/highlight" "^7.10.4" +"@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== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + +"@babel/core@^7.24.4": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384" + integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + "@babel/highlight@^7.10.4": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.9.tgz#8141ce68fc73757946f983b343f1231f4691acc6" @@ -24,6 +181,51 @@ js-tokens "^4.0.0" picocolors "^1.0.0" +"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + +"@babel/plugin-transform-private-methods@^7.24.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -53,6 +255,38 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -132,11 +366,26 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + 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.30001703" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718" + integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ== + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -183,6 +432,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +convert-source-map@^2.0.0: + version "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== + cross-spawn@^7.0.2: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -192,7 +446,7 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.0.1, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -211,6 +465,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +electron-to-chromium@^1.5.73: + version "1.5.114" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3" + integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -224,6 +483,11 @@ enquirer@^2.3.5: ansi-colors "^4.1.1" strip-ansi "^6.0.1" +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -403,6 +667,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -422,6 +691,11 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^13.6.0, globals@^13.9.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" @@ -439,6 +713,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== + +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -505,6 +791,11 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +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-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -525,6 +816,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +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== + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -550,6 +846,13 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -567,6 +870,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -603,7 +911,7 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -picocolors@^1.0.0: +picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -645,6 +953,11 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + semver@^7.2.1: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" @@ -739,6 +1052,14 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +update-browserslist-db@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -767,3 +1088,18 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +zod-validation-error@^3.0.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + +zod@^3.22.4: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== diff --git a/fixtures/eslint-v8/yarn.lock b/fixtures/eslint-v8/yarn.lock index d5a947b6347d7..b2261f0e73711 100644 --- a/fixtures/eslint-v8/yarn.lock +++ b/fixtures/eslint-v8/yarn.lock @@ -2,6 +2,213 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@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== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + +"@babel/core@^7.24.4": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384" + integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + +"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + +"@babel/plugin-transform-private-methods@^7.24.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@eslint-community/eslint-utils@^4.2.0": version "4.4.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" @@ -53,6 +260,38 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -129,11 +368,26 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + 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.30001703" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718" + integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ== + chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -159,6 +413,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +convert-source-map@^2.0.0: + version "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== + cross-spawn@^7.0.2: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -168,7 +427,7 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.3.1, debug@^4.3.2: +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -187,6 +446,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +electron-to-chromium@^1.5.73: + version "1.5.114" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3" + integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA== + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -342,6 +611,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -361,6 +635,11 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^13.19.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" @@ -378,6 +657,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== + +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -431,6 +722,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +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" @@ -438,6 +734,11 @@ js-yaml@^4.1.0: 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-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -453,6 +754,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +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== + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -480,6 +786,13 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -504,6 +817,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -559,6 +877,11 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -598,6 +921,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -646,6 +974,14 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +update-browserslist-db@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -670,7 +1006,22 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod-validation-error@^3.0.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + +zod@^3.22.4: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== diff --git a/fixtures/eslint-v9/yarn.lock b/fixtures/eslint-v9/yarn.lock index a473b3a4ce165..630bf074a30d0 100644 --- a/fixtures/eslint-v9/yarn.lock +++ b/fixtures/eslint-v9/yarn.lock @@ -2,6 +2,213 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@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== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + +"@babel/core@^7.24.4": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384" + integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + +"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + +"@babel/plugin-transform-private-methods@^7.24.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@eslint-community/eslint-utils@^4.2.0": version "4.4.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" @@ -91,6 +298,38 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161" integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -146,11 +385,26 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + 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.30001703" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718" + integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ== + chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -176,6 +430,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +convert-source-map@^2.0.0: + version "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== + cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -185,7 +444,7 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.3.1, debug@^4.3.2: +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -197,6 +456,16 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +electron-to-chromium@^1.5.73: + version "1.5.114" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3" + integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA== + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -340,6 +609,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -347,6 +621,11 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" @@ -357,6 +636,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== + +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -397,6 +688,11 @@ jiti@^2.4.2: resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== +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" @@ -404,6 +700,11 @@ js-yaml@^4.1.0: 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-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -419,6 +720,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +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== + keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -446,6 +752,13 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -463,6 +776,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -506,6 +824,11 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -521,6 +844,11 @@ resolve-from@^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" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -552,6 +880,14 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +update-browserslist-db@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -571,7 +907,22 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod-validation-error@^3.0.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + +zod@^3.22.4: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== diff --git a/package.json b/package.json index d30b4034e8ca8..2c7041f0fbc83 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@babel/cli": "^7.10.5", "@babel/code-frame": "^7.10.4", "@babel/core": "^7.11.1", + "@babel/helper-define-map": "^7.18.6", "@babel/helper-module-imports": "^7.10.4", "@babel/parser": "^7.11.3", "@babel/plugin-external-helpers": "^7.10.4", @@ -34,6 +35,7 @@ "@babel/plugin-transform-shorthand-properties": "^7.10.4", "@babel/plugin-transform-spread": "^7.11.0", "@babel/plugin-transform-template-literals": "^7.10.5", + "@babel/preset-env": "^7.26.9", "@babel/preset-flow": "^7.10.4", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.26.0", @@ -43,6 +45,7 @@ "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-typescript": "^12.1.2", + "@types/invariant": "^2.2.35", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "abortcontroller-polyfill": "^1.7.5", @@ -75,8 +78,8 @@ "glob-stream": "^6.1.0", "google-closure-compiler": "^20230206.0.0", "gzip-size": "^5.1.1", - "hermes-eslint": "^0.22.0", - "hermes-parser": "^0.22.0", + "hermes-eslint": "^0.25.1", + "hermes-parser": "^0.25.1", "jest": "^29.4.2", "jest-cli": "^29.4.2", "jest-diff": "^29.4.2", @@ -104,6 +107,8 @@ "targz": "^1.0.1", "through2": "^3.0.1", "tmp": "^0.1.0", + "to-fast-properties": "^2.0.0", + "tsup": "^8.4.0", "typescript": "^5.4.3", "undici": "^5.28.4", "web-streams-polyfill": "^3.1.1", @@ -113,6 +118,7 @@ "testRegex": "/scripts/jest/dont-run-jest-directly\\.js$" }, "scripts": { + "prebuild": "yarn --cwd compiler install --frozen-lockfile && ./scripts/react-compiler/link-compiler.sh", "build": "node ./scripts/rollup/build-all-release-channels.js", "build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE", "build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV", @@ -124,6 +130,7 @@ "lint-build": "node ./scripts/rollup/validate/index.js", "extract-errors": "node scripts/error-codes/extract-errors.js", "postinstall": "node ./scripts/flow/createFlowConfigs.js", + "pretest": "./scripts/react-compiler/build-compiler.sh && ./scripts/react-compiler/link-compiler.sh", "test": "node ./scripts/jest/jest-cli.js", "test-stable": "node ./scripts/jest/jest-cli.js --release-channel=stable", "test-www": "node ./scripts/jest/jest-cli.js --release-channel=www-modern", diff --git a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts new file mode 100644 index 0000000000000..f0d14494b9671 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts @@ -0,0 +1,290 @@ +/** + * 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 {ErrorSeverity} from 'babel-plugin-react-compiler'; +import {RuleTester as ESLintTester} from 'eslint'; +import ReactCompilerRule from '../src/rules/ReactCompiler'; + +const ESLintTesterV8 = require('eslint-v8').RuleTester; + +/** + * A string template tag that removes padding from the left side of multi-line strings + * @param {Array} strings array of code strings (only one expected) + */ +function normalizeIndent(strings: TemplateStringsArray): string { + const codeLines = strings[0]?.split('\n') ?? []; + const leftPadding = codeLines[1]?.match(/\s+/)![0] ?? ''; + return codeLines.map(line => line.slice(leftPadding.length)).join('\n'); +} + +type CompilerTestCases = { + valid: ESLintTester.ValidTestCase[]; + invalid: ESLintTester.InvalidTestCase[]; +}; + +const tests: CompilerTestCases = { + valid: [ + { + name: 'Basic example', + code: normalizeIndent` + function foo(x, y) { + if (x) { + return foo(false, y); + } + return [y * 10]; + } + `, + }, + { + name: 'Violation with Flow suppression', + code: ` + // Valid since error already suppressed with flow. + function useHookWithHook() { + if (cond) { + // $FlowFixMe[react-rule-hook] + useConditionalHook(); + } + } + `, + }, + { + name: 'Basic example with component syntax', + code: normalizeIndent` + export default component HelloWorld( + text: string = 'Hello!', + onClick: () => void, + ) { + return <div onClick={onClick}>{text}</div>; + } + `, + }, + { + name: 'Unsupported syntax', + code: normalizeIndent` + function foo(x) { + var y = 1; + return y * x; + } + `, + }, + { + // OK because invariants are only meant for the compiler team's consumption + name: '[Invariant] Defined after use', + code: normalizeIndent` + function Component(props) { + let y = function () { + m(x); + }; + + let x = { a }; + m(x); + return y; + } + `, + }, + { + name: "Classes don't throw", + code: normalizeIndent` + class Foo { + #bar() {} + } + `, + }, + { + // Don't report the issue if Flow already has + name: '[InvalidInput] Ref access during render', + code: normalizeIndent` + function Component(props) { + const ref = useRef(null); + // $FlowFixMe[react-rule-unsafe-ref] + const value = ref.current; + return value; + } + `, + }, + ], + invalid: [ + { + name: '[InvalidInput] Ref access during render', + code: normalizeIndent` + function Component(props) { + const ref = useRef(null); + const value = ref.current; + return value; + } + `, + errors: [ + { + message: + 'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)', + }, + ], + }, + { + name: 'Reportable levels can be configured', + options: [{reportableLevels: new Set([ErrorSeverity.Todo])}], + code: normalizeIndent` + function Foo(x) { + var y = 1; + return <div>{y * x}</div>; + }`, + errors: [ + { + message: + '(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration', + }, + ], + }, + { + name: '[InvalidReact] ESlint suppression', + // Indentation is intentionally weird so it doesn't add extra whitespace + code: normalizeIndent` + function Component(props) { + // eslint-disable-next-line react-hooks/rules-of-hooks + return <div>{props.foo}</div>; + }`, + errors: [ + { + message: + 'React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior', + suggestions: [ + { + output: normalizeIndent` + function Component(props) { + + return <div>{props.foo}</div>; + }`, + }, + ], + }, + { + message: + "Definition for rule 'react-hooks/rules-of-hooks' was not found.", + }, + ], + }, + { + name: 'Multiple diagnostics are surfaced', + options: [ + { + reportableLevels: new Set([ + ErrorSeverity.Todo, + ErrorSeverity.InvalidReact, + ]), + }, + ], + code: normalizeIndent` + function Foo(x) { + var y = 1; + return <div>{y * x}</div>; + } + function Bar(props) { + props.a.b = 2; + return <div>{props.c}</div> + }`, + errors: [ + { + message: + '(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration', + }, + { + message: + 'Mutating component props or hook arguments is not allowed. Consider using a local variable instead', + }, + ], + }, + { + name: 'Test experimental/unstable report all bailouts mode', + options: [ + { + reportableLevels: new Set([ErrorSeverity.InvalidReact]), + __unstable_donotuse_reportAllBailouts: true, + }, + ], + code: normalizeIndent` + function Foo(x) { + var y = 1; + return <div>{y * x}</div>; + }`, + errors: [ + { + message: + '[ReactCompilerBailout] (BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration (@:3:2)', + }, + ], + }, + { + name: "'use no forget' does not disable eslint rule", + code: normalizeIndent` + let count = 0; + function Component() { + 'use no forget'; + count = count + 1; + return <div>Hello world {count}</div> + } + `, + errors: [ + { + message: + 'Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)', + }, + ], + }, + { + name: "Unused 'use no forget' directive is reported when no errors are present on components", + code: normalizeIndent` + function Component() { + 'use no forget'; + return <div>Hello world</div> + } + `, + errors: [ + { + message: "Unused 'use no forget' directive", + suggestions: [ + { + output: + // yuck + '\nfunction Component() {\n \n return <div>Hello world</div>\n}\n', + }, + ], + }, + ], + }, + { + name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks", + code: normalizeIndent` + function notacomponent() { + 'use no forget'; + return 1 + 1; + } + `, + errors: [ + { + message: "Unused 'use no forget' directive", + suggestions: [ + { + output: + // yuck + '\nfunction notacomponent() {\n \n return 1 + 1;\n}\n', + }, + ], + }, + ], + }, + ], +}; + +const eslintTester = new ESLintTesterV8({ + parser: require.resolve('hermes-eslint'), + parserOptions: { + ecmaVersion: 2015, + sourceType: 'module', + enableExperimentalComponentSyntax: true, + }, +}); +eslintTester.run('react-compiler - eslint: v8', ReactCompilerRule, tests); diff --git a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts new file mode 100644 index 0000000000000..2efe6c7a38419 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRuleTypescript-test.ts @@ -0,0 +1,78 @@ +/** + * 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 {RuleTester} from 'eslint'; +import ReactCompilerRule from '../src/rules/ReactCompiler'; + +const ESLintTesterV8 = require('eslint-v8').RuleTester; + +/** + * A string template tag that removes padding from the left side of multi-line strings + * @param {Array} strings array of code strings (only one expected) + */ +function normalizeIndent(strings: TemplateStringsArray): string { + const codeLines = strings[0]?.split('\n') ?? []; + const leftPadding = codeLines[1]?.match(/\s+/)![0] ?? ''; + return codeLines.map(line => line.slice(leftPadding.length)).join('\n'); +} + +type CompilerTestCases = { + valid: RuleTester.ValidTestCase[]; + invalid: RuleTester.InvalidTestCase[]; +}; + +const tests: CompilerTestCases = { + valid: [ + { + name: 'Basic example', + filename: 'test.tsx', + code: normalizeIndent` + function Button(props) { + return null; + } + `, + }, + { + name: 'Repro for hooks as normal values', + filename: 'test.tsx', + code: normalizeIndent` + function Button(props) { + const scrollview = React.useRef<ScrollView>(null); + return <Button thing={scrollview} />; + } + `, + }, + ], + invalid: [ + { + name: 'Mutating useState value', + filename: 'test.tsx', + code: ` + import { useState } from 'react'; + function Component(props) { + // typescript syntax that hermes-parser doesn't understand yet + const x: \`foo\${1}\` = 'foo1'; + const [state, setState] = useState({a: 0}); + state.a = 1; + return <div>{props.foo}</div>; + } + `, + errors: [ + { + message: + "Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead", + line: 7, + }, + ], + }, + ], +}; + +const eslintTester = new ESLintTesterV8({ + parser: require.resolve('@typescript-eslint/parser-v5'), +}); +eslintTester.run('react-compiler - eslint: v8', ReactCompilerRule, tests); diff --git a/packages/eslint-plugin-react-hooks/package.json b/packages/eslint-plugin-react-hooks/package.json index 48cb7ae452a8d..25215d71e530f 100644 --- a/packages/eslint-plugin-react-hooks/package.json +++ b/packages/eslint-plugin-react-hooks/package.json @@ -21,7 +21,8 @@ "react" ], "scripts": { - "test": "jest", + "build:compiler": "cd ../../compiler && yarn workspace babel-plugin-react-compiler build", + "test": "yarn build:compiler && jest", "typecheck": "tsc --noEmit" }, "license": "MIT", @@ -37,9 +38,18 @@ "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" }, + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/plugin-transform-private-methods": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, "devDependencies": { "@babel/eslint-parser": "^7.11.4", "@babel/preset-typescript": "^7.26.0", + "@babel/types": "^7.19.0", "@tsconfig/strictest": "^2.0.5", "@typescript-eslint/parser-v2": "npm:@typescript-eslint/parser@^2.26.0", "@typescript-eslint/parser-v3": "npm:@typescript-eslint/parser@^3.10.0", @@ -51,6 +61,7 @@ "@types/node": "^20.2.5", "babel-eslint": "^10.0.3", "eslint-v7": "npm:eslint@^7.7.0", + "eslint-v8": "npm:eslint@^8.57.1", "eslint-v9": "npm:eslint@^9.0.0", "jest": "^29.5.0", "typescript": "^5.4.3" diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts index 61ac62912ddc2..26a2e2b2c4276 100644 --- a/packages/eslint-plugin-react-hooks/src/index.ts +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -4,14 +4,17 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import RulesOfHooks from './rules/RulesOfHooks'; -import ExhaustiveDeps from './rules/ExhaustiveDeps'; import type {ESLint, Linter, Rule} from 'eslint'; +import ExhaustiveDeps from './rules/ExhaustiveDeps'; +import ReactCompiler from './rules/ReactCompiler'; +import RulesOfHooks from './rules/RulesOfHooks'; + // All rules const rules = { - 'rules-of-hooks': RulesOfHooks, 'exhaustive-deps': ExhaustiveDeps, + 'react-compiler': ReactCompiler, + 'rules-of-hooks': RulesOfHooks, } satisfies Record<string, Rule.RuleModule>; // Config rules diff --git a/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts b/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts new file mode 100644 index 0000000000000..c2f9d3a103438 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/src/rules/ReactCompiler.ts @@ -0,0 +1,346 @@ +/** + * 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. + */ +/* eslint-disable no-for-of-loops/no-for-of-loops */ + +import {transformFromAstSync} from '@babel/core'; +// @ts-expect-error: no types available +import PluginProposalPrivateMethods from '@babel/plugin-transform-private-methods'; +import type {SourceLocation as BabelSourceLocation} from '@babel/types'; +import BabelPluginReactCompiler, { + type CompilerErrorDetailOptions, + CompilerSuggestionOperation, + ErrorSeverity, + parsePluginOptions, + validateEnvironmentConfig, + OPT_OUT_DIRECTIVES, + type Logger, + type LoggerEvent, + type PluginOptions, +} from 'babel-plugin-react-compiler'; +import type {Rule} from 'eslint'; +import {Statement} from 'estree'; +import * as HermesParser from 'hermes-parser'; + +type CompilerErrorDetailWithLoc = Omit<CompilerErrorDetailOptions, 'loc'> & { + loc: BabelSourceLocation; +}; + +function assertExhaustive(_: never, errorMsg: string): never { + throw new Error(errorMsg); +} + +const DEFAULT_REPORTABLE_LEVELS = new Set([ + ErrorSeverity.InvalidReact, + ErrorSeverity.InvalidJS, +]); +let reportableLevels = DEFAULT_REPORTABLE_LEVELS; + +function isReportableDiagnostic( + detail: CompilerErrorDetailOptions, +): detail is CompilerErrorDetailWithLoc { + return ( + reportableLevels.has(detail.severity) && + detail.loc != null && + typeof detail.loc !== 'symbol' + ); +} + +function makeSuggestions( + detail: CompilerErrorDetailOptions, +): Array<Rule.SuggestionReportDescriptor> { + const suggest: Array<Rule.SuggestionReportDescriptor> = []; + if (Array.isArray(detail.suggestions)) { + for (const suggestion of detail.suggestions) { + switch (suggestion.op) { + case CompilerSuggestionOperation.InsertBefore: + suggest.push({ + desc: suggestion.description, + fix(fixer) { + return fixer.insertTextBeforeRange( + suggestion.range, + suggestion.text, + ); + }, + }); + break; + case CompilerSuggestionOperation.InsertAfter: + suggest.push({ + desc: suggestion.description, + fix(fixer) { + return fixer.insertTextAfterRange( + suggestion.range, + suggestion.text, + ); + }, + }); + break; + case CompilerSuggestionOperation.Replace: + suggest.push({ + desc: suggestion.description, + fix(fixer) { + return fixer.replaceTextRange(suggestion.range, suggestion.text); + }, + }); + break; + case CompilerSuggestionOperation.Remove: + suggest.push({ + desc: suggestion.description, + fix(fixer) { + return fixer.removeRange(suggestion.range); + }, + }); + break; + default: + assertExhaustive(suggestion, 'Unhandled suggestion operation'); + } + } + } + return suggest; +} + +const COMPILER_OPTIONS: Partial<PluginOptions> = { + noEmit: true, + panicThreshold: 'none', + // Don't emit errors on Flow suppressions--Flow already gave a signal + flowSuppressions: false, +}; + +const rule: Rule.RuleModule = { + meta: { + type: 'problem', + docs: { + description: 'Surfaces diagnostics from React Forget', + recommended: true, + }, + fixable: 'code', + hasSuggestions: true, + // validation is done at runtime with zod + schema: [{type: 'object', additionalProperties: true}], + }, + create(context: Rule.RuleContext) { + // Compat with older versions of eslint + const sourceCode = context.sourceCode ?? context.getSourceCode(); + const filename = context.filename ?? context.getFilename(); + const userOpts = context.options[0] ?? {}; + if ( + userOpts.reportableLevels != null && + userOpts.reportableLevels instanceof Set + ) { + reportableLevels = userOpts.reportableLevels; + } else { + reportableLevels = DEFAULT_REPORTABLE_LEVELS; + } + /** + * Experimental setting to report all compilation bailouts on the compilation + * unit (e.g. function or hook) instead of the offensive line. + * Intended to be used when a codebase is 100% reliant on the compiler for + * memoization (i.e. deleted all manual memo) and needs compilation success + * signals for perf debugging. + */ + let __unstable_donotuse_reportAllBailouts: boolean = false; + if ( + userOpts.__unstable_donotuse_reportAllBailouts != null && + typeof userOpts.__unstable_donotuse_reportAllBailouts === 'boolean' + ) { + __unstable_donotuse_reportAllBailouts = + userOpts.__unstable_donotuse_reportAllBailouts; + } + + let shouldReportUnusedOptOutDirective = true; + const options: PluginOptions = { + ...parsePluginOptions(userOpts), + ...COMPILER_OPTIONS, + }; + const userLogger: Logger | null = options.logger; + options.logger = { + logEvent: (eventFilename, event): void => { + userLogger?.logEvent(eventFilename, event); + if (event.kind === 'CompileError') { + shouldReportUnusedOptOutDirective = false; + const detail = event.detail; + const suggest = makeSuggestions(detail); + if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) { + const locStr = + detail.loc != null && typeof detail.loc !== 'symbol' + ? ` (@:${detail.loc.start.line}:${detail.loc.start.column})` + : ''; + /** + * Report bailouts with a smaller span (just the first line). + * Compiler bailout lints only serve to flag that a react function + * has not been optimized by the compiler for codebases which depend + * on compiler memo heavily for perf. These lints are also often not + * actionable. + */ + let endLoc; + if (event.fnLoc.end.line === event.fnLoc.start.line) { + endLoc = event.fnLoc.end; + } else { + endLoc = { + line: event.fnLoc.start.line, + // Babel loc line numbers are 1-indexed + column: + sourceCode.text.split(/\r?\n|\r|\n/g)[ + event.fnLoc.start.line - 1 + ]?.length ?? 0, + }; + } + const firstLineLoc = { + start: event.fnLoc.start, + end: endLoc, + }; + context.report({ + message: `[ReactCompilerBailout] ${detail.reason}${locStr}`, + loc: firstLineLoc, + suggest, + }); + } + + if (!isReportableDiagnostic(detail)) { + return; + } + if ( + hasFlowSuppression(detail.loc, 'react-rule-hook') || + hasFlowSuppression(detail.loc, 'react-rule-unsafe-ref') + ) { + // If Flow already caught this error, we don't need to report it again. + return; + } + const loc = + detail.loc == null || typeof detail.loc === 'symbol' + ? event.fnLoc + : detail.loc; + if (loc != null) { + context.report({ + message: detail.reason, + loc, + suggest, + }); + } + } + }, + }; + + try { + options.environment = validateEnvironmentConfig( + options.environment ?? {}, + ); + } catch (err: unknown) { + options.logger?.logEvent('', err as LoggerEvent); + } + + function hasFlowSuppression( + nodeLoc: BabelSourceLocation, + suppression: string, + ): boolean { + const comments = sourceCode.getAllComments(); + const flowSuppressionRegex = new RegExp( + '\\$FlowFixMe\\[' + suppression + '\\]', + ); + for (const commentNode of comments) { + if ( + flowSuppressionRegex.test(commentNode.value) && + commentNode.loc!.end.line === nodeLoc.start.line - 1 + ) { + return true; + } + } + return false; + } + + let babelAST; + if (filename.endsWith('.tsx') || filename.endsWith('.ts')) { + try { + const {parse: babelParse} = require('@babel/parser'); + babelAST = babelParse(sourceCode.text, { + filename, + sourceType: 'unambiguous', + plugins: ['typescript', 'jsx'], + }); + } catch { + /* empty */ + } + } else { + try { + babelAST = HermesParser.parse(sourceCode.text, { + babel: true, + enableExperimentalComponentSyntax: true, + sourceFilename: filename, + sourceType: 'module', + }); + } catch { + /* empty */ + } + } + + if (babelAST != null) { + try { + transformFromAstSync(babelAST, sourceCode.text, { + filename, + highlightCode: false, + retainLines: true, + plugins: [ + [PluginProposalPrivateMethods, {loose: true}], + [BabelPluginReactCompiler, options], + ], + sourceType: 'module', + configFile: false, + babelrc: false, + }); + } catch (err) { + /* errors handled by injected logger */ + } + } + + function reportUnusedOptOutDirective(stmt: Statement) { + if ( + stmt.type === 'ExpressionStatement' && + stmt.expression.type === 'Literal' && + typeof stmt.expression.value === 'string' && + OPT_OUT_DIRECTIVES.has(stmt.expression.value) && + stmt.loc != null + ) { + context.report({ + message: `Unused '${stmt.expression.value}' directive`, + loc: stmt.loc, + suggest: [ + { + desc: 'Remove the directive', + fix(fixer) { + return fixer.remove(stmt); + }, + }, + ], + }); + } + } + if (shouldReportUnusedOptOutDirective) { + return { + FunctionDeclaration(fnDecl) { + for (const stmt of fnDecl.body.body) { + reportUnusedOptOutDirective(stmt); + } + }, + ArrowFunctionExpression(fnExpr) { + if (fnExpr.body.type === 'BlockStatement') { + for (const stmt of fnExpr.body.body) { + reportUnusedOptOutDirective(stmt); + } + } + }, + FunctionExpression(fnExpr) { + for (const stmt of fnExpr.body.body) { + reportUnusedOptOutDirective(stmt); + } + }, + }; + } else { + return {}; + } + }, +}; + +export default rule; diff --git a/packages/eslint-plugin-react-hooks/src/types/hermes-eslint.d.ts b/packages/eslint-plugin-react-hooks/src/types/hermes-eslint.d.ts new file mode 100644 index 0000000000000..280b1e1e00a21 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/src/types/hermes-eslint.d.ts @@ -0,0 +1,58 @@ +/** + * 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. + */ + +// v0.17.1 +declare module 'hermes-eslint' { + // https://fburl.com/2vikhmaa + type ParseForESLintOptions = { + /** + * Whether the whole script is executed under node.js environment. + * When enabled, the scope manager adds a function scope immediately following the global scope. + * Defaults to `false`. + */ + globalReturn: boolean; + + /** + * The identifier that's used for JSX Element creation (after transpilation). + * This should not be a member expression - just the root identifier (i.e. use "React" instead of "React.createElement"). + * + * To use the new global JSX transform function, you can explicitly set this to `null`. + * + * Defaults to `"React"`. + */ + jsxPragma: string | null; + + /** + * The identifier that's used for JSX fragment elements (after transpilation). + * If `null`, assumes transpilation will always use a member on `jsxFactory` (i.e. React.Fragment). + * This should not be a member expression - just the root identifier (i.e. use "h" instead of "h.Fragment"). + * Defaults to `null`. + */ + jsxFragmentName: string | null; + + /** + * The source type of the script. + */ + sourceType: 'script' | 'module'; + + /** + * Ignore <fbt /> JSX elements when adding references to the module-level `React` variable. + * FBT is JSX that's transformed to non-JSX and thus references differently + * + * https://facebook.github.io/fbt/ + */ + fbt: boolean; + + /** + * Support experimental component syntax + * + * Defaults to `true`. + */ + enableExperimentalComponentSyntax?: boolean; + }; + export function parse(code: string, options?: Partial<ParseForESLintOptions>); +} diff --git a/packages/eslint-plugin-react-hooks/src/types/hermes-parser.d.ts b/packages/eslint-plugin-react-hooks/src/types/hermes-parser.d.ts new file mode 100644 index 0000000000000..b624caf19c548 --- /dev/null +++ b/packages/eslint-plugin-react-hooks/src/types/hermes-parser.d.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +// v0.17.1 +declare module 'hermes-parser' { + type HermesParserOptions = { + allowReturnOutsideFunction?: boolean; + babel?: boolean; + flow?: 'all' | 'detect'; + enableExperimentalComponentSyntax?: boolean; + sourceFilename?: string; + sourceType?: 'module' | 'script' | 'unambiguous'; + tokens?: boolean; + }; + export function parse(code: string, options: Partial<HermesParserOptions>); +} diff --git a/packages/eslint-plugin-react-hooks/tsconfig.json b/packages/eslint-plugin-react-hooks/tsconfig.json index 068ca0315a7b5..c5d8847f1ec4a 100644 --- a/packages/eslint-plugin-react-hooks/tsconfig.json +++ b/packages/eslint-plugin-react-hooks/tsconfig.json @@ -2,14 +2,32 @@ "extends": "@tsconfig/strictest/tsconfig.json", "compilerOptions": { "module": "ES2015", - "target": "ES5", + "target": "ES2015", "moduleResolution": "Bundler", - "lib": ["ES2020"], - "rootDir": ".", + "lib": ["ES2020", "dom"], "sourceMap": false, "types": ["estree-jsx", "node"], - "downlevelIteration": true + "downlevelIteration": true, + "paths": { + "babel-plugin-react-compiler": ["../../compiler/packages/babel-plugin-react-compiler/src"] + }, + "jsx": "react-jsxdev", + "rootDir": "../..", + "baseUrl": ".", + "typeRoots": [ + "../../node_modules/@types" + ], + "checkJs": false, + "allowJs": false, + + // weaken strictness from preset + "importsNotUsedAsValues": "remove", + "noUncheckedIndexedAccess": false, + "noUnusedParameters": false, + "useUnknownInCatchVariables": true, + // ideally turn off only during dev, or on a per-file basis + "noUnusedLocals": false, + "removeComments": true, }, - "exclude": ["node_modules"], - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "__tests__/**/*.ts"] } diff --git a/packages/react-refresh/src/__tests__/__snapshots__/ReactFreshBabelPlugin-test.js.snap b/packages/react-refresh/src/__tests__/__snapshots__/ReactFreshBabelPlugin-test.js.snap index 7b18834ae495f..4f0979100339c 100644 --- a/packages/react-refresh/src/__tests__/__snapshots__/ReactFreshBabelPlugin-test.js.snap +++ b/packages/react-refresh/src/__tests__/__snapshots__/ReactFreshBabelPlugin-test.js.snap @@ -2,62 +2,43 @@ exports[`ReactFreshBabelPlugin can handle implicit arrow returns 1`] = ` var _s = $RefreshSig$(), - _s2 = $RefreshSig$(), - _s3 = $RefreshSig$(), - _s4 = $RefreshSig$(), - _s5 = $RefreshSig$(), - _s6 = $RefreshSig$(); - + _s2 = $RefreshSig$(), + _s3 = $RefreshSig$(), + _s4 = $RefreshSig$(), + _s5 = $RefreshSig$(), + _s6 = $RefreshSig$(); export default _s(() => { _s(); - return useContext(X); }, "useContext{}"); export const Foo = () => { _s2(); - return useContext(X); }; - _s2(Foo, "useContext{}"); - _c = Foo; module.exports = _s3(() => { _s3(); - return useContext(X); }, "useContext{}"); - const Bar = () => { _s4(); - return useContext(X); }; - _s4(Bar, "useContext{}"); - _c2 = Bar; - const Baz = _s5(memo(_c3 = _s5(() => { _s5(); - return useContext(X); }, "useContext{}")), "useContext{}"); - _c4 = Baz; - const Qux = () => { _s6(); - return 0, useContext(X); }; - _s6(Qux, "useContext{}"); - _c5 = Qux; - var _c, _c2, _c3, _c4, _c5; - $RefreshReg$(_c, "Foo"); $RefreshReg$(_c2, "Bar"); $RefreshReg$(_c3, "Baz$memo"); @@ -67,7 +48,6 @@ $RefreshReg$(_c5, "Qux"); exports[`ReactFreshBabelPlugin does not consider require-like methods to be HOCs 1`] = ` const A = require('A'); - const B = foo ? require('X') : require('Y'); const C = requireCond(gk, 'C'); const D = import('D'); @@ -80,19 +60,15 @@ export default function App() { </div>; } _c = App; - var _c; - $RefreshReg$(_c, "App"); `; exports[`ReactFreshBabelPlugin does not get tripped by IIFEs 1`] = ` while (item) { var _s = $RefreshSig$(); - _s(item => { _s(); - useFoo(); }, "useFoo{}", true)(item); } @@ -100,31 +76,23 @@ while (item) { exports[`ReactFreshBabelPlugin generates signatures for function declarations calling hooks 1`] = ` var _s = $RefreshSig$(); - export default function App() { _s(); - const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1>{foo}</h1>; } - _s(App, "useState{[foo, setFoo](0)}\\nuseEffect{}"); - _c = App; - var _c; - $RefreshReg$(_c, "App"); `; exports[`ReactFreshBabelPlugin generates signatures for function expressions calling hooks 1`] = ` var _s = $RefreshSig$(), - _s2 = $RefreshSig$(); - + _s2 = $RefreshSig$(); export const A = _s(React.memo(_c2 = _s(React.forwardRef(_c = _s((props, ref) => { _s(); - const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1 ref={ref}>{foo}</h1>; @@ -132,29 +100,22 @@ export const A = _s(React.memo(_c2 = _s(React.forwardRef(_c = _s((props, ref) => _c3 = A; export const B = _s2(React.memo(_c5 = _s2(React.forwardRef(_c4 = _s2(function (props, ref) { _s2(); - const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1 ref={ref}>{foo}</h1>; }, "useState{[foo, setFoo](0)}\\nuseEffect{}")), "useState{[foo, setFoo](0)}\\nuseEffect{}")), "useState{[foo, setFoo](0)}\\nuseEffect{}"); _c6 = B; - function hoc() { var _s3 = $RefreshSig$(); - return _s3(function Inner() { _s3(); - const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1 ref={ref}>{foo}</h1>; }, "useState{[foo, setFoo](0)}\\nuseEffect{}"); } - export let C = hoc(); - var _c, _c2, _c3, _c4, _c5, _c6; - $RefreshReg$(_c, "A$React.memo$React.forwardRef"); $RefreshReg$(_c2, "A$React.memo"); $RefreshReg$(_c3, "A"); @@ -165,38 +126,28 @@ $RefreshReg$(_c6, "B"); exports[`ReactFreshBabelPlugin generates valid signature for exotic ways to call Hooks 1`] = ` var _s2 = $RefreshSig$(); - import FancyHook from 'fancy'; export default function App() { _s2(); - var _s = $RefreshSig$(); - function useFancyState() { _s(); - const [foo, setFoo] = React.useState(0); useFancyEffect(); return foo; } - _s(useFancyState, "useState{[foo, setFoo](0)}\\nuseFancyEffect{}", true); - const bar = useFancyState(); const baz = FancyHook.useThing(); React.useState(); useThePlatform(); return <h1>{bar}{baz}</h1>; } - _s2(App, "useFancyState{bar}\\nuseThing{baz}\\nuseState{}\\nuseThePlatform{}", true, function () { return [FancyHook.useThing]; }); - _c = App; - var _c; - $RefreshReg$(_c, "App"); `; @@ -204,21 +155,16 @@ exports[`ReactFreshBabelPlugin ignores HOC definitions 1`] = ` let connect = () => { function Comp() { const handleClick = () => {}; - return <h1 onClick={handleClick}>Hi</h1>; } - return Comp; }; - function withRouter() { return function Child() { const handleClick = () => {}; - return <h1 onClick={handleClick}>Hi</h1>; }; } - ; `; @@ -226,15 +172,12 @@ exports[`ReactFreshBabelPlugin ignores complex definitions 1`] = ` let A = foo ? () => { return <h1>Hi</h1>; } : null; - const B = function Foo() { return <h1>Hi</h1>; }(); - let C = () => () => { return <h1>Hi</h1>; }; - let D = bar && (() => { return <h1>Hi</h1>; }); @@ -244,11 +187,9 @@ exports[`ReactFreshBabelPlugin ignores higher-order functions that are not HOCs const throttledAlert = throttle(function () { alert('Hi'); }); - const TooComplex = function () { return hello; }(() => {}); - if (cond) { const Foo = thing(() => {}); } @@ -258,44 +199,32 @@ exports[`ReactFreshBabelPlugin ignores unnamed function declarations 1`] = `expo exports[`ReactFreshBabelPlugin includes custom hooks into the signatures 1`] = ` var _s = $RefreshSig$(), - _s2 = $RefreshSig$(), - _s3 = $RefreshSig$(); - + _s2 = $RefreshSig$(), + _s3 = $RefreshSig$(); function useFancyState() { _s(); - const [foo, setFoo] = React.useState(0); useFancyEffect(); return foo; } - _s(useFancyState, "useState{[foo, setFoo](0)}\\nuseFancyEffect{}", false, function () { return [useFancyEffect]; }); - const useFancyEffect = () => { _s2(); - React.useEffect(() => {}); }; - _s2(useFancyEffect, "useEffect{}"); - export default function App() { _s3(); - const bar = useFancyState(); return <h1>{bar}</h1>; } - _s3(App, "useFancyState{bar}", false, function () { return [useFancyState]; }); - _c = App; - var _c; - $RefreshReg$(_c, "App"); `; @@ -306,26 +235,18 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = App; - var _hooks = require("./hooks"); - var _s = $RefreshSig$(); - function App() { _s(); - const bar = (0, _hooks.useFancyState)(); return <h1>{bar}</h1>; } - _s(App, "useFancyState{bar}", false, function () { return [_hooks.useFancyState]; }); - _c = App; - var _c; - $RefreshReg$(_c, "App"); `; @@ -339,16 +260,13 @@ exports[`ReactFreshBabelPlugin registers capitalized identifiers in HOC calls 1` function Foo() { return <h1>Hi</h1>; } - _c = Foo; export default _c2 = hoc(Foo); export const A = hoc(Foo); _c3 = A; const B = hoc(Foo); _c4 = B; - var _c, _c2, _c3, _c4; - $RefreshReg$(_c, "Foo"); $RefreshReg$(_c2, "%default%"); $RefreshReg$(_c3, "A"); @@ -375,21 +293,17 @@ const FunnyFactory = funny.factory\`\`; let Alias1 = A; let Alias2 = A.Foo; const Dict = {}; - function Foo() { return <div><A /><B /><StyledFactory1 /><StyledFactory2 /><StyledFactory3 /><Alias1 /><Alias2 /><Header /><Dict.X /></div>; } - _c5 = Foo; -const B = hoc(A); // This is currently registered as a false positive: - +const B = hoc(A); +// This is currently registered as a false positive: _c6 = B; -const NotAComponent = wow(A); // We could avoid it but it also doesn't hurt. - +const NotAComponent = wow(A); +// We could avoid it but it also doesn't hurt. _c7 = NotAComponent; - var _c, _c2, _c3, _c4, _c5, _c6, _c7; - $RefreshReg$(_c, "Header"); $RefreshReg$(_c2, "StyledFactory1"); $RefreshReg$(_c3, "StyledFactory2"); @@ -419,22 +333,18 @@ const FunnyFactory = funny.factory\`\`; let Alias1 = A; let Alias2 = A.Foo; const Dict = {}; - function Foo() { return [React.createElement(A), React.createElement(B), React.createElement(StyledFactory1), React.createElement(StyledFactory2), React.createElement(StyledFactory3), React.createElement(Alias1), React.createElement(Alias2), jsx(Header), React.createElement(Dict.X)]; } - _c5 = Foo; React.createContext(Store); -const B = hoc(A); // This is currently registered as a false positive: - +const B = hoc(A); +// This is currently registered as a false positive: _c6 = B; -const NotAComponent = wow(A); // We could avoid it but it also doesn't hurt. - +const NotAComponent = wow(A); +// We could avoid it but it also doesn't hurt. _c7 = NotAComponent; - var _c, _c2, _c3, _c4, _c5, _c6, _c7; - $RefreshReg$(_c, "Header"); $RefreshReg$(_c2, "StyledFactory1"); $RefreshReg$(_c3, "StyledFactory2"); @@ -456,9 +366,7 @@ _c5 = B; export default _c8 = React.memo(_c7 = forwardRef(_c6 = (props, ref) => { return <h1>Foo</h1>; })); - var _c, _c2, _c3, _c4, _c5, _c6, _c7, _c8; - $RefreshReg$(_c, "A$forwardRef"); $RefreshReg$(_c2, "A"); $RefreshReg$(_c3, "B$memo$React.forwardRef"); @@ -473,9 +381,7 @@ exports[`ReactFreshBabelPlugin registers likely HOCs with inline functions 2`] = export default _c3 = React.memo(_c2 = forwardRef(_c = function (props, ref) { return <h1>Foo</h1>; })); - var _c, _c2, _c3; - $RefreshReg$(_c, "%default%$React.memo$forwardRef"); $RefreshReg$(_c2, "%default%$React.memo"); $RefreshReg$(_c3, "%default%"); @@ -485,9 +391,7 @@ exports[`ReactFreshBabelPlugin registers likely HOCs with inline functions 3`] = export default _c3 = React.memo(_c2 = forwardRef(_c = function Named(props, ref) { return <h1>Foo</h1>; })); - var _c, _c2, _c3; - $RefreshReg$(_c, "%default%$React.memo$forwardRef"); $RefreshReg$(_c2, "%default%$React.memo"); $RefreshReg$(_c3, "%default%"); @@ -496,7 +400,6 @@ $RefreshReg$(_c3, "%default%"); exports[`ReactFreshBabelPlugin registers top-level exported function declarations 1`] = ` export function Hello() { function handleClick() {} - return <h1 onClick={handleClick}>Hi</h1>; } _c = Hello; @@ -504,19 +407,15 @@ export default function Bar() { return <Hello />; } _c2 = Bar; - function Baz() { return <h1>OK</h1>; } - _c3 = Baz; const NotAComp = 'hi'; export { Baz, NotAComp }; export function sum() {} export const Bad = 42; - var _c, _c2, _c3; - $RefreshReg$(_c, "Hello"); $RefreshReg$(_c2, "Bar"); $RefreshReg$(_c3, "Baz"); @@ -525,7 +424,6 @@ $RefreshReg$(_c3, "Baz"); exports[`ReactFreshBabelPlugin registers top-level exported named arrow functions 1`] = ` export const Hello = () => { function handleClick() {} - return <h1 onClick={handleClick}>Hi</h1>; }; _c = Hello; @@ -536,9 +434,7 @@ export default (() => { // You should name your components. return <Hello />; }); - var _c, _c2; - $RefreshReg$(_c, "Hello"); $RefreshReg$(_c2, "Bar"); `; @@ -546,20 +442,14 @@ $RefreshReg$(_c2, "Bar"); exports[`ReactFreshBabelPlugin registers top-level function declarations 1`] = ` function Hello() { function handleClick() {} - return <h1 onClick={handleClick}>Hi</h1>; } - _c = Hello; - function Bar() { return <Hello />; } - _c2 = Bar; - var _c, _c2; - $RefreshReg$(_c, "Hello"); $RefreshReg$(_c2, "Bar"); `; @@ -567,26 +457,17 @@ $RefreshReg$(_c2, "Bar"); exports[`ReactFreshBabelPlugin registers top-level variable declarations with arrow functions 1`] = ` let Hello = () => { const handleClick = () => {}; - return <h1 onClick={handleClick}>Hi</h1>; }; - _c = Hello; - const Bar = () => { return <Hello />; }; - _c2 = Bar; - var Baz = () => <div />; - _c3 = Baz; - var sum = () => {}; - var _c, _c2, _c3; - $RefreshReg$(_c, "Hello"); $RefreshReg$(_c2, "Bar"); $RefreshReg$(_c3, "Baz"); @@ -595,25 +476,17 @@ $RefreshReg$(_c3, "Baz"); exports[`ReactFreshBabelPlugin registers top-level variable declarations with function expressions 1`] = ` let Hello = function () { function handleClick() {} - return <h1 onClick={handleClick}>Hi</h1>; }; - _c = Hello; - const Bar = function Baz() { return <Hello />; }; - _c2 = Bar; - function sum() {} - let Baz = 10; var Qux; - var _c, _c2; - $RefreshReg$(_c, "Hello"); $RefreshReg$(_c2, "Bar"); `; @@ -637,9 +510,7 @@ namespace Foo { export const E = () => {}; } } - var _c, _c2, _c3, _c4; - $RefreshReg$(_c, "Foo$Bar$A"); $RefreshReg$(_c2, "Foo$Bar$B"); $RefreshReg$(_c3, "Foo$C"); @@ -648,21 +519,15 @@ $RefreshReg$(_c4, "Foo$D"); exports[`ReactFreshBabelPlugin uses custom identifiers for $RefreshReg$ and $RefreshSig$ 1`] = ` var _s = import.meta.refreshSig(); - export default function Bar() { _s(); - useContext(X); return <Foo />; } - _s(Bar, "useContext{}"); - _c = Bar; ; - var _c; - import.meta.refreshReg(_c, "Bar"); `; @@ -670,11 +535,8 @@ exports[`ReactFreshBabelPlugin uses original function declaration if it get reas function Hello() { return <h1>Hi</h1>; } - _c = Hello; Hello = connect(Hello); - var _c; - $RefreshReg$(_c, "Hello"); `; diff --git a/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap b/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap index a531f008bebab..95987c0cd1570 100644 --- a/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap +++ b/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap @@ -1,12 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`error transform handles deeply nested expressions 1`] = ` -"var val = (a, (b, // eslint-disable-next-line react-internal/prod-error-codes +"var val = (a, (b, +// eslint-disable-next-line react-internal/prod-error-codes new Error('foo')));" `; exports[`error transform handles deeply nested expressions 2`] = ` -"var val = (a, ( // eslint-disable-next-line react-internal/prod-error-codes +"var val = (a, ( +// eslint-disable-next-line react-internal/prod-error-codes b, new Error('foo')));" `; @@ -17,10 +19,11 @@ Error(_formatProdErrorMessage(231, listener, type));" exports[`error transform handles ignoring errors that are comment-excluded inside ternary expressions 1`] = ` "/*! FIXME (minify-errors-in-prod): Unminified error message in production build!*/ - /*! <expected-error-format>"bar"</expected-error-format>*/ -var val = someBool ? //eslint-disable-next-line react-internal/prod-error-codes -new Error('foo') : someOtherBool ? new Error('bar') : //eslint-disable-next-line react-internal/prod-error-codes +var val = someBool ? +//eslint-disable-next-line react-internal/prod-error-codes +new Error('foo') : someOtherBool ? new Error('bar') : +//eslint-disable-next-line react-internal/prod-error-codes new Error('baz');" `; @@ -36,7 +39,6 @@ NotAnError();" exports[`error transform should output FIXME for errors that don't have a matching error code 1`] = ` "/*! FIXME (minify-errors-in-prod): Unminified error message in production build!*/ - /*! <expected-error-format>"This is not a real error message."</expected-error-format>*/ Error('This is not a real error message.');" `; diff --git a/scripts/jest/config.base.js b/scripts/jest/config.base.js index 262c6ab18a144..15401cbba012e 100644 --- a/scripts/jest/config.base.js +++ b/scripts/jest/config.base.js @@ -7,6 +7,12 @@ module.exports = { '<rootDir>/scripts/bench/', ], transform: { + '^.+babel-plugin-react-compiler/dist/index.js$': [ + 'babel-jest', + { + configFile: require.resolve('../../babel.config-react-compiler.js'), + }, + ], '^.+\\.ts$': [ 'babel-jest', {configFile: require.resolve('../../babel.config-ts.js')}, diff --git a/scripts/react-compiler/build-compiler.sh b/scripts/react-compiler/build-compiler.sh new file mode 100755 index 0000000000000..1c65fdcaa66c9 --- /dev/null +++ b/scripts/react-compiler/build-compiler.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# 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. + +set -eo pipefail + +if [[ "$REACT_CLASS_EQUIVALENCE_TEST" == "true" ]]; then + exit 0 +fi + +echo "Building babel-plugin-react-compiler..." +yarn --cwd compiler install --frozen-lockfile +yarn --cwd compiler workspace babel-plugin-react-compiler build --dts diff --git a/scripts/react-compiler/link-compiler.sh b/scripts/react-compiler/link-compiler.sh new file mode 100755 index 0000000000000..47bb84be94d9f --- /dev/null +++ b/scripts/react-compiler/link-compiler.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# 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. + +set -eo pipefail + +if [[ "$REACT_CLASS_EQUIVALENCE_TEST" == "true" ]]; then + exit 0 +fi + +HERE=$(pwd) + +cd compiler/packages/babel-plugin-react-compiler && yarn --silent link && cd $HERE + +yarn --silent link babel-plugin-react-compiler diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 02aa167b6332c..1c3010d902f81 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -25,6 +25,8 @@ const Packaging = require('./packaging'); const {asyncRimRaf} = require('./utils'); const codeFrame = require('@babel/code-frame').default; const Wrappers = require('./wrappers'); +const commonjs = require('@rollup/plugin-commonjs'); +const {getBabelOutputPlugin} = require('@rollup/plugin-babel'); const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL; @@ -392,6 +394,7 @@ function getPlugins( }; }, }, + bundle.tsconfig != null ? commonjs() : false, // Shim any modules that need forking in this environment. useForks(forks), // Ensure we don't try to bundle any fbjs modules. @@ -415,13 +418,23 @@ function getPlugins( bundle ) ), - // Remove 'use strict' from individual source files. - { - name: "remove 'use strict'", - transform(source) { - return source.replace(/['"]use strict["']/g, ''); - }, - }, + // For Meta internal requirements this package needs to be built targeting ES5. + bundle.name === 'eslint-plugin-react-hooks' + ? getBabelOutputPlugin({ + presets: ['@babel/preset-env'], + }) + : false, + // Remove 'use strict' from individual source files. We skip eslint-plugin-react-hooks because + // it bundles compiler-type code that may examine "use strict" used outside of a directive + // context, e.g. as a StringLiteral. + bundle.name !== 'eslint-plugin-react-hooks' + ? { + name: "remove 'use strict'", + transform(source) { + return source.replace(/['"]use strict["']/g, ''); + }, + } + : false, // Turn __DEV__ and process.env checks into constants. replace({ preventAssignment: true, @@ -490,7 +503,7 @@ function getPlugins( // takes care of it. renaming: false, }), - needsMinifiedByClosure && + (needsMinifiedByClosure || bundle.name === 'eslint-plugin-react-hooks') && // Add the whitespace back prettier({ parser: 'flow', diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 0a378c4f31bf7..824a1bbd52fc2 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -1197,6 +1197,7 @@ const bundles = [ wrapWithModuleBoundaries: false, externals: [], tsconfig: './packages/eslint-plugin-react-hooks/tsconfig.json', + prebuild: `mkdir -p ./compiler/packages/babel-plugin-react-compiler/dist && echo "module.exports = require('../src/index.ts');" > ./compiler/packages/babel-plugin-react-compiler/dist/index.js`, }, /******* React Fresh *******/ diff --git a/yarn.lock b/yarn.lock index ed53f3284334f..fe05ccf257b8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -93,17 +93,12 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/compat-data@^7.16.0": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" - integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== - "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.23.5": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== -"@babel/compat-data@^7.26.5": +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": version "7.26.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== @@ -129,29 +124,7 @@ json5 "^2.2.2" semver "^6.3.0" -"@babel/core@^7.11.1": - version "7.11.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" - integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.0" - "@babel/helper-module-transforms" "^7.11.0" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.1" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.11.0" - "@babel/types" "^7.11.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": +"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.8", "@babel/core@^7.23.9", "@babel/core@^7.24.4": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.5.tgz#15ab5b98e101972d171aeef92ac70d8d6718f06a" integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== @@ -172,27 +145,6 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/core@^7.14.8": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" - integrity sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.0" - "@babel/helper-compilation-targets" "^7.16.0" - "@babel/helper-module-transforms" "^7.16.0" - "@babel/helpers" "^7.16.0" - "@babel/parser" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.0" - "@babel/types" "^7.16.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - "@babel/eslint-parser@^7.11.4": version "7.11.4" resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.4.tgz#f79bac69088097a8418f5c67fc462c89a72c2f48" @@ -220,15 +172,6 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" - integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew== - dependencies: - "@babel/types" "^7.16.0" - jsesc "^2.5.1" - source-map "^0.5.0" - "@babel/generator@^7.20.7", "@babel/generator@^7.24.5", "@babel/generator@^7.7.2": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3" @@ -239,6 +182,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" +"@babel/generator@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7" + integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== + dependencies: + "@babel/parser" "^7.26.10" + "@babel/types" "^7.26.10" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/generator@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" @@ -331,16 +285,6 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-compilation-targets@^7.16.0": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" - integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== - dependencies: - "@babel/compat-data" "^7.16.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.17.5" - semver "^6.3.0" - "@babel/helper-compilation-targets@^7.18.9": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" @@ -363,7 +307,7 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-compilation-targets@^7.25.9": +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": version "7.26.5" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== @@ -434,14 +378,33 @@ "@babel/helper-regex" "^7.10.4" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" - integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0" + integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong== dependencies: - "@babel/helper-function-name" "^7.10.4" - "@babel/types" "^7.10.5" - lodash "^4.17.19" + "@babel/helper-annotate-as-pure" "^7.25.9" + regexpu-core "^6.2.0" + semver "^6.3.1" + +"@babel/helper-define-map@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.18.6.tgz#8dca645a768d0a5007b0bb90078c1d623e99e614" + integrity sha512-XSOjXUDG7KODvtURN1p29hGHa4RFgqBQELuBowUOBt3alf2Ny/oNFJygS4yCXwM0vMoqLDjE1O7wSmocUmQ3Kg== + dependencies: + "@babel/helper-function-name" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-define-polyfill-provider@^0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" + integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" "@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" @@ -474,6 +437,14 @@ "@babel/template" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-function-name@^7.18.6": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" + integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== + dependencies: + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-function-name@^7.18.9": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" @@ -482,7 +453,7 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" -"@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.23.0": +"@babel/helper-function-name@^7.19.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -527,20 +498,6 @@ dependencies: "@babel/types" "^7.10.4" -"@babel/helper-hoist-variables@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" - integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - "@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" @@ -606,7 +563,7 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== @@ -644,7 +601,7 @@ "@babel/helper-split-export-declaration" "^7.24.5" "@babel/helper-validator-identifier" "^7.24.5" -"@babel/helper-module-transforms@^7.26.0": +"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== @@ -724,6 +681,15 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-remap-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" + integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-wrap-function" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/helper-replace-supers@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" @@ -844,27 +810,12 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-string-parser@^7.19.4", "@babel/helper-string-parser@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" - integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== - -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - "@babel/helper-string-parser@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== -"@babel/helper-validator-identifier@^7.10.4": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" - integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== - -"@babel/helper-validator-identifier@^7.14.0", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.24.5": +"@babel/helper-validator-identifier@^7.14.0", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.24.5": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== @@ -874,11 +825,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - "@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" @@ -914,23 +860,14 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helpers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" - integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== - dependencies: - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helpers@^7.16.0": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" - integrity sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w== +"@babel/helper-wrap-function@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" + integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== dependencies: - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.3" - "@babel/types" "^7.16.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" "@babel/helpers@^7.20.7", "@babel/helpers@^7.24.5": version "7.24.5" @@ -1005,12 +942,12 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.5": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.5": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== -"@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.11.3", "@babel/parser@^7.12.13": +"@babel/parser@^7.11.0", "@babel/parser@^7.11.3", "@babel/parser@^7.12.13": version "7.14.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.3.tgz#9b530eecb071fd0c93519df25c5ff9f14759f298" integrity sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ== @@ -1025,18 +962,64 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA== -"@babel/parser@^7.16.0", "@babel/parser@^7.16.3": +"@babel/parser@^7.16.0": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== -"@babel/parser@^7.26.9": +"@babel/parser@^7.24.4", "@babel/parser@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== dependencies: "@babel/types" "^7.26.9" +"@babel/parser@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749" + integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA== + dependencies: + "@babel/types" "^7.26.10" + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" + integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" + integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" + integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" + integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" + integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/plugin-external-helpers@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.10.4.tgz#40d38e8e48a1fa3766ab43496253266ca26783ce" @@ -1227,6 +1210,11 @@ "@babel/helper-create-class-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + "@babel/plugin-proposal-private-property-in-object@^7.14.5": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz#69e935b2c5c79d2488112d886f0c4e2790fee76f" @@ -1294,6 +1282,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-import-assertions@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-attributes@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -1434,6 +1436,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" @@ -1448,6 +1458,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-arrow-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-async-generator-functions@^7.26.8": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz#5e3991135e3b9c6eaaf5eff56d1ae5a11df45ff8" + integrity sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-remap-async-to-generator" "^7.25.9" + "@babel/traverse" "^7.26.8" + "@babel/plugin-transform-async-to-generator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" @@ -1457,6 +1483,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-remap-async-to-generator" "^7.10.4" +"@babel/plugin-transform-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" + integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-remap-async-to-generator" "^7.25.9" + "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" @@ -1471,6 +1506,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoped-functions@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz#3dc4405d31ad1cbe45293aa57205a6e3b009d53e" + integrity sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/plugin-transform-block-scoping@^7.0.0": version "7.20.14" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.14.tgz#2f5025f01713ba739daf737997308e0d29d1dd75" @@ -1485,6 +1527,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoping@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" + integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-class-properties@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" @@ -1493,36 +1542,15 @@ "@babel/helper-create-class-features-plugin" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-classes@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" - globals "^11.1.0" - -"@babel/plugin-transform-classes@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" - integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== +"@babel/plugin-transform-class-static-block@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" + integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-define-map" "^7.10.4" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - globals "^11.1.0" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-classes@^7.25.9": +"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.10.4", "@babel/plugin-transform-classes@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== @@ -1549,6 +1577,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-computed-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/template" "^7.25.9" + "@babel/plugin-transform-destructuring@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" @@ -1563,6 +1599,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-destructuring@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-dotall-regex@^7.10.4", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" @@ -1571,6 +1614,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-dotall-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" + integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-duplicate-keys@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" @@ -1578,6 +1629,28 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-duplicate-keys@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" + integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" + integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-dynamic-import@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" + integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-exponentiation-operator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" @@ -1586,6 +1659,20 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-exponentiation-operator@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" + integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-export-namespace-from@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" + integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-flow-strip-types@^7.0.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" @@ -1616,6 +1703,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-for-of@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz#27231f79d5170ef33b5111f07fe5cafeb2c96a56" + integrity sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-function-name@^7.0.0": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" @@ -1633,6 +1728,22 @@ "@babel/helper-function-name" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-function-name@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== + dependencies: + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-transform-json-strings@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" + integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-literals@^7.0.0": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" @@ -1647,6 +1758,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-logical-assignment-operators@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" + integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-member-expression-literals@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" @@ -1661,6 +1786,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-member-expression-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-modules-amd@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" @@ -1670,6 +1802,14 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-amd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" + integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== + dependencies: + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-modules-commonjs@^7.0.0": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" @@ -1699,7 +1839,7 @@ "@babel/helper-simple-access" "^7.16.0" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.25.9": +"@babel/plugin-transform-modules-commonjs@^7.25.9", "@babel/plugin-transform-modules-commonjs@^7.26.3": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== @@ -1717,6 +1857,16 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" + integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== + dependencies: + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/plugin-transform-modules-umd@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" @@ -1725,6 +1875,14 @@ "@babel/helper-module-transforms" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-modules-umd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" + integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== + dependencies: + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" @@ -1732,6 +1890,14 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.10.4" +"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" + integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-new-target@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" @@ -1739,6 +1905,36 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-new-target@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" + integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.26.6": + version "7.26.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz#fbf6b3c92cb509e7b319ee46e3da89c5bedd31fe" + integrity sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + +"@babel/plugin-transform-numeric-separator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" + integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-object-rest-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" + integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== + dependencies: + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" + "@babel/plugin-transform-object-super@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" @@ -1755,6 +1951,29 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-replace-supers" "^7.10.4" +"@babel/plugin-transform-object-super@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + +"@babel/plugin-transform-optional-catch-binding@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" + integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" + integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" @@ -1770,6 +1989,30 @@ "@babel/helper-get-function-arity" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-parameters@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-private-methods@^7.24.4", "@babel/plugin-transform-private-methods@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-private-property-in-object@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" + integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-property-literals@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" @@ -1784,6 +2027,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-property-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-react-display-name@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" @@ -1892,6 +2142,22 @@ dependencies: regenerator-transform "^0.14.2" +"@babel/plugin-transform-regenerator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" + integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-regexp-modifiers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" + integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-reserved-words@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" @@ -1899,6 +2165,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-reserved-words@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" + integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" @@ -1913,6 +2186,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-shorthand-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-spread@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" @@ -1929,6 +2209,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" +"@babel/plugin-transform-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-sticky-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" @@ -1937,6 +2225,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-regex" "^7.10.4" +"@babel/plugin-transform-sticky-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" + integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-template-literals@^7.0.0": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" @@ -1952,6 +2247,13 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-template-literals@^7.26.8": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz#966b15d153a991172a540a69ad5e1845ced990b5" + integrity sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/plugin-transform-typeof-symbol@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" @@ -1959,6 +2261,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typeof-symbol@^7.26.7": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz#d0e33acd9223744c1e857dbd6fa17bd0a3786937" + integrity sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/plugin-transform-typescript@^7.16.0": version "7.16.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.1.tgz#cc0670b2822b0338355bc1b3d2246a42b8166409" @@ -1986,6 +2295,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-escapes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" + integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-unicode-property-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" + integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-unicode-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" @@ -1994,6 +2318,22 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" + integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-unicode-sets-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" + integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/preset-env@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" @@ -2068,6 +2408,81 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/preset-env@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.9.tgz#2ec64e903d0efe743699f77a10bdf7955c2123c3" + integrity sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ== + dependencies: + "@babel/compat-data" "^7.26.8" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-import-assertions" "^7.26.0" + "@babel/plugin-syntax-import-attributes" "^7.26.0" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.25.9" + "@babel/plugin-transform-async-generator-functions" "^7.26.8" + "@babel/plugin-transform-async-to-generator" "^7.25.9" + "@babel/plugin-transform-block-scoped-functions" "^7.26.5" + "@babel/plugin-transform-block-scoping" "^7.25.9" + "@babel/plugin-transform-class-properties" "^7.25.9" + "@babel/plugin-transform-class-static-block" "^7.26.0" + "@babel/plugin-transform-classes" "^7.25.9" + "@babel/plugin-transform-computed-properties" "^7.25.9" + "@babel/plugin-transform-destructuring" "^7.25.9" + "@babel/plugin-transform-dotall-regex" "^7.25.9" + "@babel/plugin-transform-duplicate-keys" "^7.25.9" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-dynamic-import" "^7.25.9" + "@babel/plugin-transform-exponentiation-operator" "^7.26.3" + "@babel/plugin-transform-export-namespace-from" "^7.25.9" + "@babel/plugin-transform-for-of" "^7.26.9" + "@babel/plugin-transform-function-name" "^7.25.9" + "@babel/plugin-transform-json-strings" "^7.25.9" + "@babel/plugin-transform-literals" "^7.25.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" + "@babel/plugin-transform-member-expression-literals" "^7.25.9" + "@babel/plugin-transform-modules-amd" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.26.3" + "@babel/plugin-transform-modules-systemjs" "^7.25.9" + "@babel/plugin-transform-modules-umd" "^7.25.9" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-new-target" "^7.25.9" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.26.6" + "@babel/plugin-transform-numeric-separator" "^7.25.9" + "@babel/plugin-transform-object-rest-spread" "^7.25.9" + "@babel/plugin-transform-object-super" "^7.25.9" + "@babel/plugin-transform-optional-catch-binding" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" + "@babel/plugin-transform-private-methods" "^7.25.9" + "@babel/plugin-transform-private-property-in-object" "^7.25.9" + "@babel/plugin-transform-property-literals" "^7.25.9" + "@babel/plugin-transform-regenerator" "^7.25.9" + "@babel/plugin-transform-regexp-modifiers" "^7.26.0" + "@babel/plugin-transform-reserved-words" "^7.25.9" + "@babel/plugin-transform-shorthand-properties" "^7.25.9" + "@babel/plugin-transform-spread" "^7.25.9" + "@babel/plugin-transform-sticky-regex" "^7.25.9" + "@babel/plugin-transform-template-literals" "^7.26.8" + "@babel/plugin-transform-typeof-symbol" "^7.26.7" + "@babel/plugin-transform-unicode-escapes" "^7.25.9" + "@babel/plugin-transform-unicode-property-regex" "^7.25.9" + "@babel/plugin-transform-unicode-regex" "^7.25.9" + "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.11.0" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.40.0" + semver "^6.3.1" + "@babel/preset-flow@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.10.4.tgz#e0d9c72f8cb02d1633f6a5b7b16763aa2edf659f" @@ -2076,6 +2491,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-flow-strip-types" "^7.10.4" +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + "@babel/preset-modules@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" @@ -2191,7 +2615,7 @@ "@babel/parser" "^7.24.0" "@babel/types" "^7.24.0" -"@babel/template@^7.26.9": +"@babel/template@^7.24.7", "@babel/template@^7.25.9", "@babel/template@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== @@ -2254,54 +2678,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" - integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.0" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-hoist-variables" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/parser" "^7.16.3" - "@babel/types" "^7.16.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.20.12", "@babel/traverse@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8" - integrity sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA== - dependencies: - "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.24.5" - "@babel/parser" "^7.24.5" - "@babel/types" "^7.24.5" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/traverse@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": +"@babel/traverse@^7.16.0", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7", "@babel/traverse@^7.24.5", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== @@ -2314,59 +2691,20 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.7", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.3.3": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" - integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ== - dependencies: - "@babel/helper-string-parser" "^7.24.1" - "@babel/helper-validator-identifier" "^7.24.5" - to-fast-properties "^2.0.0" - -"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.13", "@babel/types@^7.4.4": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" - integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== - dependencies: - "@babel/helper-validator-identifier" "^7.14.0" - to-fast-properties "^2.0.0" - -"@babel/types@^7.12.5": - version "7.12.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96" - integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" - integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.20.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.4": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" - integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== +"@babel/traverse@^7.26.8": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380" + integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.10" + debug "^4.3.1" + globals "^11.1.0" -"@babel/types@^7.25.9", "@babel/types@^7.26.9": +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.13", "@babel/types@^7.12.5", "@babel/types@^7.16.0", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.25.9", "@babel/types@^7.26.9", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.8.3": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== @@ -2374,14 +2712,13 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@babel/types@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" - integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== +"@babel/types@^7.24.7", "@babel/types@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259" + integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ== dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" @@ -2439,6 +2776,131 @@ opn "5.3.0" react "^16.13.1" +"@esbuild/aix-ppc64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz#c33cf6bbee34975626b01b80451cbb72b4c6c91d" + integrity sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ== + +"@esbuild/android-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz#ea766015c7d2655164f22100d33d7f0308a28d6d" + integrity sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA== + +"@esbuild/android-arm@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.1.tgz#e84d2bf2fe2e6177a0facda3a575b2139fd3cb9c" + integrity sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q== + +"@esbuild/android-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.1.tgz#58337bee3bc6d78d10425e5500bd11370cfdfbed" + integrity sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw== + +"@esbuild/darwin-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz#a46805c1c585d451aa83be72500bd6e8495dd591" + integrity sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ== + +"@esbuild/darwin-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz#0643e003bb238c63fc93ddbee7d26a003be3cd98" + integrity sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA== + +"@esbuild/freebsd-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz#cff18da5469c09986b93e87979de5d6872fe8f8e" + integrity sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A== + +"@esbuild/freebsd-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz#362fc09c2de14987621c1878af19203c46365dde" + integrity sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww== + +"@esbuild/linux-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz#aa90d5b02efc97a271e124e6d1cea490634f7498" + integrity sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ== + +"@esbuild/linux-arm@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz#dfcefcbac60a20918b19569b4b657844d39db35a" + integrity sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ== + +"@esbuild/linux-ia32@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz#6f9527077ccb7953ed2af02e013d4bac69f13754" + integrity sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ== + +"@esbuild/linux-loong64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz#287d2412a5456e5860c2839d42a4b51284d1697c" + integrity sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg== + +"@esbuild/linux-mips64el@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz#530574b9e1bc5d20f7a4f44c5f045e26f3783d57" + integrity sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg== + +"@esbuild/linux-ppc64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz#5d7e6b283a0b321ea42c6bc0abeb9eb99c1f5589" + integrity sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg== + +"@esbuild/linux-riscv64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz#14fa0cd073c26b4ee2465d18cd1e18eea7859fa8" + integrity sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ== + +"@esbuild/linux-s390x@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz#e677b4b9d1b384098752266ccaa0d52a420dc1aa" + integrity sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ== + +"@esbuild/linux-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz#f1c796b78fff5ce393658313e8c58613198d9954" + integrity sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA== + +"@esbuild/netbsd-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz#0d280b7dfe3973f111b02d5fe9f3063b92796d29" + integrity sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g== + +"@esbuild/netbsd-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz#be663893931a4bb3f3a009c5cc24fa9681cc71c0" + integrity sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA== + +"@esbuild/openbsd-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz#d9021b884233673a05dc1cc26de0bf325d824217" + integrity sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg== + +"@esbuild/openbsd-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz#9f1dc1786ed2e2938c404b06bcc48be9a13250de" + integrity sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw== + +"@esbuild/sunos-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz#89aac24a4b4115959b3f790192cf130396696c27" + integrity sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg== + +"@esbuild/win32-arm64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz#354358647a6ea98ea6d243bf48bdd7a434999582" + integrity sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ== + +"@esbuild/win32-ia32@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz#8cea7340f2647eba951a041dc95651e3908cd4cb" + integrity sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A== + +"@esbuild/win32-x64@0.25.1": + version "0.25.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz#7d79922cb2d88f9048f06393dbf62d2e4accb584" + integrity sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -2506,6 +2968,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + "@eslint/js@9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.0.0.tgz#1a9e4b4c96d8c7886e0110ed310a0135144a1691" @@ -2569,6 +3036,15 @@ debug "^4.3.1" minimatch "^3.0.5" +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -2840,6 +3316,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -3303,6 +3788,101 @@ estree-walker "^2.0.2" picomatch "^4.0.2" +"@rollup/rollup-android-arm-eabi@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz#e1d7700735f7e8de561ef7d1fa0362082a180c43" + integrity sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ== + +"@rollup/rollup-android-arm64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz#fa6cdfb1fc9e2c8e227a7f35d524d8f7f90cf4db" + integrity sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA== + +"@rollup/rollup-darwin-arm64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz#6da5a1ddc4f11d4a7ae85ab443824cb6bf614e30" + integrity sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q== + +"@rollup/rollup-darwin-x64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz#25b74ce2d8d3f9ea8e119b01384d44a1c0a0d3ae" + integrity sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q== + +"@rollup/rollup-freebsd-arm64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz#be3d39e3441df5d6e187c83d158c60656c82e203" + integrity sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ== + +"@rollup/rollup-freebsd-x64@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz#cd932d3ec679711efd65ca25821fb318e25b7ce4" + integrity sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw== + +"@rollup/rollup-linux-arm-gnueabihf@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz#d300b74c6f805474225632f185daaeae760ac2bb" + integrity sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg== + +"@rollup/rollup-linux-arm-musleabihf@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz#2caac622380f314c41934ed1e68ceaf6cc380cc3" + integrity sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A== + +"@rollup/rollup-linux-arm64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz#1ec841650b038cc15c194c26326483fd7ebff3e3" + integrity sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A== + +"@rollup/rollup-linux-arm64-musl@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz#2fc70a446d986e27f6101ea74e81746987f69150" + integrity sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg== + +"@rollup/rollup-linux-loongarch64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz#561bd045cd9ce9e08c95f42e7a8688af8c93d764" + integrity sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g== + +"@rollup/rollup-linux-powerpc64le-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz#45d849a0b33813f33fe5eba9f99e0ff15ab5caad" + integrity sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA== + +"@rollup/rollup-linux-riscv64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz#78dde3e6fcf5b5733a97d0a67482d768aa1e83a5" + integrity sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g== + +"@rollup/rollup-linux-s390x-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz#2e34835020f9e03dfb411473a5c2a0e8a9c5037b" + integrity sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw== + +"@rollup/rollup-linux-x64-gnu@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz#4f9774beddc6f4274df57ac99862eb23040de461" + integrity sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA== + +"@rollup/rollup-linux-x64-musl@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz#dfcff2c1aed518b3d23ccffb49afb349d74fb608" + integrity sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg== + +"@rollup/rollup-win32-arm64-msvc@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz#b0b37e2d77041e3aa772f519291309abf4c03a84" + integrity sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg== + +"@rollup/rollup-win32-ia32-msvc@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz#5b5a40e44a743ddc0e06b8e1b3982f856dc9ce0a" + integrity sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw== + +"@rollup/rollup-win32-x64-msvc@4.35.0": + version "4.35.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz#05f25dbc9981bee1ae6e713daab10397044a46ca" + integrity sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw== + "@sinclair/typebox@^0.25.16": version "0.25.24" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" @@ -3492,7 +4072,7 @@ dependencies: "@types/estree" "*" -"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6": +"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -3541,6 +4121,11 @@ dependencies: "@types/node" "*" +"@types/invariant@^2.2.35": + version "2.2.37" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.37.tgz#1709741e534364d653c87dff22fc76fa94aa7bc0" + integrity sha512-IwpIMieE55oGWiXkQPSBY1nw1nFs6bsKXTFskNY8sdS17K24vyEBRQZEwlRS7ZmXCWnJcQtbxWzly+cODWGs2A== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" @@ -5050,6 +5635,30 @@ babel-plugin-minify-type-constructors@^0.4.3: dependencies: babel-helper-is-void-0 "^0.4.3" +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.12" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" + integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.6.3" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz#4e4e182f1bb37c7ba62e2af81d8dd09df31344f6" + integrity sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.3" + core-js-compat "^3.40.0" + +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8" + integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.3" + babel-plugin-syntax-trailing-function-commas@^6.5.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" @@ -5495,17 +6104,6 @@ browserslist@^4.14.5: node-releases "^2.0.8" update-browserslist-db "^1.0.10" -browserslist@^4.17.5: - version "4.18.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" - integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== - dependencies: - caniuse-lite "^1.0.30001280" - electron-to-chromium "^1.3.896" - escalade "^3.1.1" - node-releases "^2.0.1" - picocolors "^1.0.0" - browserslist@^4.21.3, browserslist@^4.22.2: version "4.23.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" @@ -5516,7 +6114,7 @@ browserslist@^4.21.3, browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.13" -browserslist@^4.24.0: +browserslist@^4.24.0, browserslist@^4.24.4: version "4.24.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== @@ -5620,6 +6218,13 @@ bundle-name@^3.0.0: dependencies: run-applescript "^5.0.0" +bundle-require@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-5.1.0.tgz#8db66f41950da3d77af1ef3322f4c3e04009faee" + integrity sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA== + dependencies: + load-tsconfig "^0.2.3" + bunyan@1.8.15: version "1.8.15" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" @@ -5640,6 +6245,11 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -5794,11 +6404,6 @@ caniuse-lite@^1.0.30001111: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001114.tgz#2e88119afb332ead5eaa330e332e951b1c4bfea9" integrity sha512-ml/zTsfNBM+T1+mjglWRPgVsu2L76GAaADKX5f4t0pbhttEp0WMawJsHDYlFkVZkoA+89uvBRrVrEE4oqenzXQ== -caniuse-lite@^1.0.30001280: - version "1.0.30001365" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001365.tgz" - integrity sha512-VDQZ8OtpuIPMBA4YYvZXECtXbddMCUFJk1qu8Mqxfm/SZJNSr1cy4IuLCOL7RJ/YASrvJcYg1Zh+UEUQ5m6z8Q== - caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001587: version "1.0.30001616" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz#4342712750d35f71ebba9fcac65e2cf8870013c3" @@ -5946,6 +6551,13 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chokidar@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + chownr@^1.0.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -6214,7 +6826,7 @@ commander@^2.18.0, commander@^2.20.0, commander@^2.6.0, commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.1: +commander@^4.0.0, commander@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -6357,6 +6969,11 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== +consola@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.0.tgz#4cfc9348fd85ed16a17940b3032765e31061ab88" + integrity sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA== + console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" @@ -6433,6 +7050,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +core-js-compat@^3.40.0: + version "3.41.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.41.0.tgz#4cdfce95f39a8f27759b667cf693d96e5dda3d17" + integrity sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A== + dependencies: + browserslist "^4.24.4" + core-js-compat@^3.6.2: version "3.6.4" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" @@ -6880,6 +7504,13 @@ debug@^4.3.4, debug@~4.3.1: dependencies: ms "2.1.2" +debug@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + decamelize@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-6.0.0.tgz#8cad4d916fde5c41a264a43d0ecc56fe3d31749e" @@ -7351,20 +7982,15 @@ electron-to-chromium@^1.3.523: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.538.tgz#15226638ee9db5d8e74f4c860cef6078d8e1e871" integrity sha512-rlyYXLlOoZkJuvY4AJXUpP7CHRVtwZz311HPVoEO1UHo/kqDCsP1pNas0A9paZuPEiYGdLwrjllF2hs69NEaTw== -electron-to-chromium@^1.3.896: - version "1.3.903" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.903.tgz#e2d3c3809f4ef05fdbe5cc88969dfc94b1bd15b9" - integrity sha512-+PnYAyniRRTkNq56cqYDLq9LyklZYk0hqoDy9GpcU11H5QjRmFZVDbxtgHUMK/YzdNTcn1XWP5gb+hFlSCr20g== - electron-to-chromium@^1.4.284, electron-to-chromium@^1.4.668: version "1.4.758" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.758.tgz#f39e530cae2ca4329a0f0e1840629d8d1da73156" integrity sha512-/o9x6TCdrYZBMdGeTifAP3wlF/gVT+TtWJe3BSmtNh92Mw81U9hrYwW9OAGUh+sEOX/yz5e34sksqRruZbjYrw== electron-to-chromium@^1.5.73: - version "1.5.115" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.115.tgz#193dd534948b3ea77e3d95dd38ca12cdc01c76a9" - integrity sha512-MN1nahVHAQMOz6dz6bNZ7apgqc9InZy7Ja4DBEVCTdeiUcegbyOYE9bi/f2Z/z6ZxLi0RxLpyJ3EGe+4h3w73A== + version "1.5.114" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3" + integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA== electron@^23.1.2: version "23.1.2" @@ -7583,6 +8209,37 @@ es6-error@4.1.1, es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +esbuild@^0.25.0: + version "0.25.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.1.tgz#a16b8d070b6ad4871935277bda6ccfe852e3fa2f" + integrity sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.1" + "@esbuild/android-arm" "0.25.1" + "@esbuild/android-arm64" "0.25.1" + "@esbuild/android-x64" "0.25.1" + "@esbuild/darwin-arm64" "0.25.1" + "@esbuild/darwin-x64" "0.25.1" + "@esbuild/freebsd-arm64" "0.25.1" + "@esbuild/freebsd-x64" "0.25.1" + "@esbuild/linux-arm" "0.25.1" + "@esbuild/linux-arm64" "0.25.1" + "@esbuild/linux-ia32" "0.25.1" + "@esbuild/linux-loong64" "0.25.1" + "@esbuild/linux-mips64el" "0.25.1" + "@esbuild/linux-ppc64" "0.25.1" + "@esbuild/linux-riscv64" "0.25.1" + "@esbuild/linux-s390x" "0.25.1" + "@esbuild/linux-x64" "0.25.1" + "@esbuild/netbsd-arm64" "0.25.1" + "@esbuild/netbsd-x64" "0.25.1" + "@esbuild/openbsd-arm64" "0.25.1" + "@esbuild/openbsd-x64" "0.25.1" + "@esbuild/sunos-x64" "0.25.1" + "@esbuild/win32-arm64" "0.25.1" + "@esbuild/win32-ia32" "0.25.1" + "@esbuild/win32-x64" "0.25.1" + escalade@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" @@ -7800,6 +8457,50 @@ eslint-utils@^2.0.0, eslint-utils@^2.1.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +"eslint-v8@npm:eslint@^8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + "eslint-v9@npm:eslint@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.0.0.tgz#6270548758e390343f78c8afd030566d86927d40" @@ -8462,6 +9163,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fdir@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" + integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== + fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" @@ -8914,11 +9620,6 @@ fx-runner@1.4.0: which "1.2.4" winreg "0.0.12" -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -9100,6 +9801,18 @@ glob@^10.2.5: minipass "^5.0.0 || ^6.0.2" path-scurry "^1.7.0" +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^6.0.1: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" @@ -9576,26 +10289,21 @@ has@^1.0.3: resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== -hasown@^2.0.0: +hasown@^2.0.0, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" -hermes-eslint@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/hermes-eslint/-/hermes-eslint-0.22.0.tgz#b4b9a58a546f9b2f33536a977bcea3f026057f67" - integrity sha512-WnD0xPY1Clvd4F68g2esS89C0NGeu/pn3sdqGXXdnlgr3jZtG5lugscRATS+0+mXOtZ6PTxSClVr2JL4BNor2Q== +hermes-eslint@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-eslint/-/hermes-eslint-0.25.1.tgz#e7d2d845256705d5e2d5cf69dc79032ac3921bb3" + integrity sha512-nPz9+oyejT1zsIwoJ2pWdUvLcN1i+tbaWCOD8PpNBYQtnHXaPXImZp/6zZHnm3bo/DoFcAgh8+SNcxLFxh7m/A== dependencies: esrecurse "^4.3.0" - hermes-estree "0.22.0" - hermes-parser "0.22.0" - -hermes-estree@0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.22.0.tgz#38559502b119f728901d2cfe2ef422f277802a1d" - integrity sha512-FLBt5X9OfA8BERUdc6aZS36Xz3rRuB0Y/mfocSADWEJfomc1xfene33GdyAmtTkKTBXTN/EgAy+rjTKkkZJHlw== + hermes-estree "0.25.1" + hermes-parser "0.25.1" hermes-estree@0.23.0: version "0.23.0" @@ -9607,12 +10315,10 @@ hermes-estree@0.23.1: resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.23.1.tgz#d0bac369a030188120ee7024926aabe5a9f84fdb" integrity sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg== -hermes-parser@0.22.0, hermes-parser@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.22.0.tgz#fc8e0e6c7bfa8db85b04c9f9544a102c4fcb4040" - integrity sha512-gn5RfZiEXCsIWsFGsKiykekktUoh0PdFWYocXsUdZIyWSckT6UIyPcyyUIPSR3kpnELWeK3n3ztAse7Mat6PSA== - dependencies: - hermes-estree "0.22.0" +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== hermes-parser@0.23.0: version "0.23.0" @@ -9628,6 +10334,13 @@ hermes-parser@0.23.1: dependencies: hermes-estree "0.23.1" +hermes-parser@0.25.1, hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -10256,6 +10969,13 @@ is-core-module@^2.13.0, is-core-module@^2.9.0: dependencies: hasown "^2.0.0" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -11290,6 +12010,11 @@ jose@5.4.1: resolved "https://registry.yarnpkg.com/jose/-/jose-5.4.1.tgz#b471ee3963920ba5452fd1b1398c8ba72a7b2fcf" integrity sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ== +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + jpeg-js@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" @@ -11378,6 +12103,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +jsesc@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -11676,6 +12406,11 @@ lighthouse-logger@^1.0.0: debug "^2.6.8" marky "^1.2.0" +lilconfig@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + 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" @@ -11697,6 +12432,11 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +load-tsconfig@^0.2.3: + version "0.2.5" + resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" + integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -11758,6 +12498,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -11833,6 +12578,11 @@ lodash.omitby@4.6.0: resolved "https://registry.yarnpkg.com/lodash.omitby/-/lodash.omitby-4.6.0.tgz#5c15ff4754ad555016b53c041311e8f079204791" integrity sha1-XBX/R1StVVAWtTwEExHo8HkgR5E= +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -12353,7 +13103,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1, ms@^2.1.2: +ms@2.1.3, ms@^2.1.1, ms@^2.1.2, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -12385,7 +13135,7 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -mz@2.7.0: +mz@2.7.0, mz@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== @@ -12585,11 +13335,6 @@ node-releases@^1.1.60: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== -node-releases@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== - node-releases@^2.0.14, node-releases@^2.0.8: version "2.0.14" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" @@ -13607,6 +14352,13 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +postcss-load-config@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" + integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== + dependencies: + lilconfig "^3.1.1" + postcss-modules-extract-imports@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a" @@ -14335,6 +15087,11 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -14374,6 +15131,13 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +regenerate-unicode-properties@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -14386,6 +15150,11 @@ regenerate@^1.2.1, regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" @@ -14413,6 +15182,13 @@ regenerator-transform@^0.14.2: dependencies: "@babel/runtime" "^7.8.4" +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -14447,6 +15223,18 @@ regexpu-core@^4.7.0: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" +regexpu-core@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.2.0" + regjsgen "^0.8.0" + regjsparser "^0.12.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + registry-auth-token@^3.0.1: version "3.4.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" @@ -14501,6 +15289,11 @@ regjsgen@^0.5.1: resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== +regjsgen@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== + regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" @@ -14508,6 +15301,13 @@ regjsparser@^0.1.4: dependencies: jsesc "~0.5.0" +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== + dependencies: + jsesc "~3.0.2" + regjsparser@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" @@ -14614,7 +15414,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0: version "1.14.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== @@ -14630,6 +15430,15 @@ resolve@^1.12.0, resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.14.2: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -14777,6 +15586,34 @@ rollup@^3.29.5: optionalDependencies: fsevents "~2.3.2" +rollup@^4.34.8: + version "4.35.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.35.0.tgz#76c95dba17a579df4c00c3955aed32aa5d4dc66d" + integrity sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.35.0" + "@rollup/rollup-android-arm64" "4.35.0" + "@rollup/rollup-darwin-arm64" "4.35.0" + "@rollup/rollup-darwin-x64" "4.35.0" + "@rollup/rollup-freebsd-arm64" "4.35.0" + "@rollup/rollup-freebsd-x64" "4.35.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.35.0" + "@rollup/rollup-linux-arm-musleabihf" "4.35.0" + "@rollup/rollup-linux-arm64-gnu" "4.35.0" + "@rollup/rollup-linux-arm64-musl" "4.35.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.35.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.35.0" + "@rollup/rollup-linux-riscv64-gnu" "4.35.0" + "@rollup/rollup-linux-s390x-gnu" "4.35.0" + "@rollup/rollup-linux-x64-gnu" "4.35.0" + "@rollup/rollup-linux-x64-musl" "4.35.0" + "@rollup/rollup-win32-arm64-msvc" "4.35.0" + "@rollup/rollup-win32-ia32-msvc" "4.35.0" + "@rollup/rollup-win32-x64-msvc" "4.35.0" + fsevents "~2.3.2" + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -14985,7 +15822,7 @@ semver-truncate@^1.1.2: dependencies: semver "^5.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -15417,6 +16254,13 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= +source-map@0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -15875,6 +16719,19 @@ style-loader@^1.2.1: loader-utils "^2.0.0" schema-utils "^2.6.6" +sucrase@^3.35.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + sumchecker@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" @@ -16163,6 +17020,19 @@ tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.11: + version "0.2.12" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5" + integrity sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww== + dependencies: + fdir "^6.4.3" + picomatch "^4.0.2" + titleize@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" @@ -16269,6 +17139,13 @@ tough-cookie@^4.1.2: universalify "^0.2.0" url-parse "^1.5.3" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + tr46@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" @@ -16286,6 +17163,11 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -16303,6 +17185,11 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + ts-node@8.9.1: version "8.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.9.1.tgz#2f857f46c47e91dcd28a14e052482eb14cfd65a5" @@ -16329,6 +17216,28 @@ tslib@^2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tsup@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/tsup/-/tsup-8.4.0.tgz#2fdf537e7abc8f1ccbbbfe4228f16831457d4395" + integrity sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ== + dependencies: + bundle-require "^5.1.0" + cac "^6.7.14" + chokidar "^4.0.3" + consola "^3.4.0" + debug "^4.4.0" + esbuild "^0.25.0" + joycon "^3.1.1" + picocolors "^1.1.1" + postcss-load-config "^6.0.1" + resolve-from "^5.0.0" + rollup "^4.34.8" + source-map "0.8.0-beta.0" + sucrase "^3.35.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.11" + tree-kill "^1.2.2" + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -16482,6 +17391,11 @@ unicode-canonical-property-names-ecmascript@^1.0.4: resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== + unicode-match-property-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" @@ -16490,16 +17404,34 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== + unicode-property-aliases-ecmascript@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -16969,6 +17901,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -17139,6 +18076,15 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + when@3.7.7: version "3.7.7" resolved "https://registry.yarnpkg.com/when/-/when-3.7.7.tgz#aba03fc3bb736d6c88b091d013d8a8e590d84718" @@ -17500,3 +18446,13 @@ zip-stream@^2.1.2: archiver-utils "^2.1.0" compress-commons "^2.1.1" readable-stream "^3.4.0" + +zod-validation-error@^3.0.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + +zod@^3.22.4: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== From 1b77c3d7b98f21ec707d3accb441c678dac2803f Mon Sep 17 00:00:00 2001 From: Mohhamad Hussain <smhnaqvi111@gmail.com> Date: Thu, 13 Mar 2025 19:15:26 +0330 Subject: [PATCH 115/300] Update DEVELOPMENT_GUIDE.md (#32281) fix: update CONTRIBUTING.md link path Updated the relative path to CONTRIBUTING.md from `../CONTRIBUTING.md` to `./../../CONTRIBUTING.md` to ensure the correct file is referenced. <!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. Before submitting a pull request, please make sure the following is done: 1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`. 2. Run `yarn` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`. 6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect". 7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`). 8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files. 9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`). 10. If you haven't already, complete the CLA. Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html --> ## Summary <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> ## How did you test this change? <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. How exactly did you verify that your PR solves the issue you wanted to solve? If you leave this empty, your PR will very likely be closed. --> --- compiler/docs/DEVELOPMENT_GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/docs/DEVELOPMENT_GUIDE.md b/compiler/docs/DEVELOPMENT_GUIDE.md index d4de8749b06d3..af3973fada860 100644 --- a/compiler/docs/DEVELOPMENT_GUIDE.md +++ b/compiler/docs/DEVELOPMENT_GUIDE.md @@ -1,6 +1,6 @@ # React Compiler Development Guide -Note: for general notes about contributing, see the [CONTRIBUTING.md](../CONTRIBUTING.md). +Note: for general notes about contributing, see the [CONTRIBUTING.md](../../CONTRIBUTING.md). ## Compiler Development From ef06b54f8d1d9818806e221974c8b64efbbfda0d Mon Sep 17 00:00:00 2001 From: Tyler Scott Williams <tyler@coolsoftware.dev> Date: Thu, 13 Mar 2025 11:46:26 -0400 Subject: [PATCH 116/300] fix: clarify which mobx libs are not compatible with compiler (#32570) ## Summary Right now, `react-compiler-healthcheck` flags `mobx` as a "known incompatible library". But it's not precisely *MobX* that's incompatible. It's the observer HOC that comes from `mobx-react` and `mobx-react-lite`. I've been working on [mst-use-observable](https://github.com/coolsoftwaretyler/mst-use-observable), which makes MobX-State-Tree compatible with the compiler. However, projects that use `mobx-state-tree` and `mst-use-observable` will still depend on `mobx` as a dependency. And there [have been efforts in the past to write a hook for observability](https://github.com/mobxjs/mobx/discussions/2566). So it's possible that MobX could become compatible, so long as authors access it with a hook, rather than the HOC. I would like to propose updating the health check to be a little more precise and flag the HOC dependencies, rather than MobX itself. Thanks in advance for your consideration! ## How did you test this change? `npx react-compiler-healthcheck` shouldn't flag on `mobx` in dependencies, but will for `mobx-react-lite` and `mobx-react`. Test suites, formatting, linting, all passed. --------- Co-authored-by: lauren <poteto@users.noreply.github.com> --- compiler/packages/react-compiler-healthcheck/src/config.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/packages/react-compiler-healthcheck/src/config.ts b/compiler/packages/react-compiler-healthcheck/src/config.ts index 8f25776f489cf..9655d2654b8b9 100644 --- a/compiler/packages/react-compiler-healthcheck/src/config.ts +++ b/compiler/packages/react-compiler-healthcheck/src/config.ts @@ -1,3 +1,7 @@ export const config = { - knownIncompatibleLibraries: ['mobx', '@risingstack/react-easy-state'], + knownIncompatibleLibraries: [ + 'mobx-react', + 'mobx-react-lite', + '@risingstack/react-easy-state', + ], }; From ed1264f07701e092ac1a8466611372613d1a0102 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:58:02 -0400 Subject: [PATCH 117/300] [compiler] Patch array and argument spread mutability (#32521) Array and argument spreads may mutate stateful iterables. Spread sites should have `ConditionallyMutate` effects (e.g. mutate if the ValueKind is mutable, otherwise read). See - [ecma spec (13.2.4.1 Runtime Semantics: ArrayAccumulation. SpreadElement : ... AssignmentExpression)](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-arrayaccumulation). - [ecma spec 13.3.8.1 Runtime Semantics: ArgumentListEvaluation](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-argumentlistevaluation) Note that - Object and JSX Attribute spreads do not evaluate iterables (srcs [mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#description), [ecma](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-propertydefinitionevaluation)) - An ideal mutability inference system could model known collections (i.e. Arrays or Sets) as a "mutated collection of non-mutable objects" (see `todo-granular-iterator-semantics`), but this is not what we do today. As such, an array / argument spread will always extend the range of built-in arrays, sets, etc - Due to HIR limitations, call expressions with argument spreads may cause unnecessary bailouts and/or scope merging when we know the call itself has `freeze`, `capture`, or `read` semantics (e.g. `useHook(...mutableValue)`) We can deal with this by rewriting these call instructions to (1) create an intermediate array to consume the iterator and (2) capture and spread the array at the callsite --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32521). * #32596 * #32595 * #32594 * #32593 * #32522 * __->__ #32521 --- .../src/Inference/InferReferenceEffects.ts | 113 ++++++++++++------ .../array-spread-later-mutated.expect.md | 62 ++++++++++ .../compiler/array-spread-later-mutated.js | 20 ++++ ...> array-spread-mutable-iterator.expect.md} | 30 +++-- ...or.js => array-spread-mutable-iterator.js} | 0 ...spread-argument-mutable-iterator.expect.md | 42 +++++++ .../call-spread-argument-mutable-iterator.js | 13 ++ ...ok-call-spreads-mutable-iterator.expect.md | 33 +++++ ...todo-hook-call-spreads-mutable-iterator.js | 12 ++ ...wer-property-load-into-temporary.expect.md | 19 +-- ...alls-lower-property-load-into-temporary.js | 5 +- .../packages/snap/src/SproutTodoFilter.ts | 1 - 12 files changed, 283 insertions(+), 67 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{bug-array-spread-mutable-iterator.expect.md => array-spread-mutable-iterator.expect.md} (82%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{bug-array-spread-mutable-iterator.js => array-spread-mutable-iterator.js} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index e6883250705d4..bfa0825408355 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -872,11 +872,33 @@ function inferBlock( reason: new Set([ValueReason.Other]), context: new Set(), }; + + for (const element of instrValue.elements) { + if (element.kind === 'Spread') { + state.referenceAndRecordEffects( + freezeActions, + element.place, + isArrayType(element.place.identifier) + ? Effect.Capture + : Effect.ConditionallyMutate, + ValueReason.Other, + ); + } else if (element.kind === 'Identifier') { + state.referenceAndRecordEffects( + freezeActions, + element, + Effect.Capture, + ValueReason.Other, + ); + } else { + let _: 'Hole' = element.kind; + } + } + state.initialize(instrValue, valueKind); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = Effect.Store; continuation = { - kind: 'initialize', - valueKind, - effect: {kind: Effect.Capture, reason: ValueReason.Other}, - lvalueEffect: Effect.Store, + kind: 'funeffects', }; break; } @@ -1241,21 +1263,12 @@ function inferBlock( for (let i = 0; i < instrValue.args.length; i++) { const arg = instrValue.args[i]; const place = arg.kind === 'Identifier' ? arg : arg.place; - if (effects !== null) { - state.referenceAndRecordEffects( - freezeActions, - place, - effects[i], - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - place, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } + state.referenceAndRecordEffects( + freezeActions, + place, + getArgumentEffect(effects != null ? effects[i] : null, arg), + ValueReason.Other, + ); hasCaptureArgument ||= place.effect === Effect.Capture; } if (signature !== null) { @@ -1307,7 +1320,10 @@ function inferBlock( signature !== null ? { kind: signature.returnValueKind, - reason: new Set([ValueReason.Other]), + reason: new Set([ + signature.returnValueReason ?? + ValueReason.KnownReturnSignature, + ]), context: new Set(), } : { @@ -1356,25 +1372,16 @@ function inferBlock( for (let i = 0; i < instrValue.args.length; i++) { const arg = instrValue.args[i]; const place = arg.kind === 'Identifier' ? arg : arg.place; - if (effects !== null) { - /* - * If effects are inferred for an argument, we should fail invalid - * mutating effects - */ - state.referenceAndRecordEffects( - freezeActions, - place, - effects[i], - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - place, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } + /* + * If effects are inferred for an argument, we should fail invalid + * mutating effects + */ + state.referenceAndRecordEffects( + freezeActions, + place, + getArgumentEffect(effects != null ? effects[i] : null, arg), + ValueReason.Other, + ); hasCaptureArgument ||= place.effect === Effect.Capture; } if (signature !== null) { @@ -2049,3 +2056,31 @@ function areArgumentsImmutableAndNonMutating( } return true; } + +function getArgumentEffect( + signatureEffect: Effect | null, + arg: Place | SpreadPattern, +): Effect { + if (signatureEffect != null) { + if (arg.kind === 'Identifier') { + return signatureEffect; + } else if ( + signatureEffect === Effect.Mutate || + signatureEffect === Effect.ConditionallyMutate + ) { + return signatureEffect; + } else { + // see call-spread-argument-mutable-iterator test fixture + if (signatureEffect === Effect.Freeze) { + CompilerError.throwTodo({ + reason: 'Support spread syntax for hook arguments', + loc: arg.place.loc, + }); + } + // effects[i] is Effect.Capture | Effect.Read | Effect.Store + return Effect.ConditionallyMutate; + } + } else { + return Effect.ConditionallyMutate; + } +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md new file mode 100644 index 0000000000000..a317e22faf92e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +function useBar({arg}) { + /** + * Note that mutableIterator is mutated by the later object spread. Therefore, + * `s.values()` should be memoized within the same block as the object spread. + * In terms of compiler internals, they should have the same reactive scope. + */ + const obj = {}; + const s = new Set([obj, 5, 4]); + const mutableIterator = s.values(); + const arr = [...mutableIterator]; + + obj.x = arg; + return arr; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function useBar(t0) { + const $ = _c(2); + const { arg } = t0; + let arr; + if ($[0] !== arg) { + const obj = {}; + const s = new Set([obj, 5, 4]); + const mutableIterator = s.values(); + arr = [...mutableIterator]; + + obj.x = arg; + $[0] = arg; + $[1] = arr; + } else { + arr = $[1]; + } + return arr; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{ arg: 3 }], + sequentialRenders: [{ arg: 3 }, { arg: 3 }, { arg: 4 }], +}; + +``` + +### Eval output +(kind: ok) [{"x":3},5,4] +[{"x":3},5,4] +[{"x":4},5,4] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js new file mode 100644 index 0000000000000..036ce2ddfc681 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js @@ -0,0 +1,20 @@ +function useBar({arg}) { + /** + * Note that mutableIterator is mutated by the later object spread. Therefore, + * `s.values()` should be memoized within the same block as the object spread. + * In terms of compiler internals, they should have the same reactive scope. + */ + const obj = {}; + const s = new Set([obj, 5, 4]); + const mutableIterator = s.values(); + const arr = [...mutableIterator]; + + obj.x = arg; + return arr; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.expect.md similarity index 82% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.expect.md index 70f41f40477ae..25499af1b0202 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.expect.md @@ -55,26 +55,20 @@ import { c as _c } from "react/compiler-runtime"; /** function useBar(t0) { "use memo"; - const $ = _c(3); + const $ = _c(2); const { arg } = t0; let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + if ($[0] !== arg) { const s = new Set([1, 5, 4]); - t1 = s.values(); - $[0] = t1; - } else { - t1 = $[0]; - } - const mutableIterator = t1; - let t2; - if ($[1] !== arg) { - t2 = [arg, ...mutableIterator]; - $[1] = arg; - $[2] = t2; + const mutableIterator = s.values(); + + t1 = [arg, ...mutableIterator]; + $[0] = arg; + $[1] = t1; } else { - t2 = $[2]; + t1 = $[1]; } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { @@ -84,4 +78,8 @@ export const FIXTURE_ENTRYPOINT = { }; ``` - \ No newline at end of file + +### Eval output +(kind: ok) [3,1,5,4] +[3,1,5,4] +[4,1,5,4] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md new file mode 100644 index 0000000000000..74fb57b6bc2fe --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +import {useIdentity} from 'shared-runtime'; + +function useFoo() { + const it = new Set([1, 2]).values(); + useIdentity(); + return Math.max(...it); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +## Code + +```javascript +import { useIdentity } from "shared-runtime"; + +function useFoo() { + const it = new Set([1, 2]).values(); + useIdentity(); + return Math.max(...it); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +### Eval output +(kind: ok) 2 +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js new file mode 100644 index 0000000000000..1b30f0a46f7c7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js @@ -0,0 +1,13 @@ +import {useIdentity} from 'shared-runtime'; + +function useFoo() { + const it = new Set([1, 2]).values(); + useIdentity(); + return Math.max(...it); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md new file mode 100644 index 0000000000000..5d0af122f2146 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +import {useIdentity} from 'shared-runtime'; + +function Component() { + const items = makeArray(0, 1, 2, null, 4, false, 6); + return useIdentity(...items.values()); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + sequentialRenders: [{}, {}], +}; + +``` + + +## Error + +``` + 3 | function Component() { + 4 | const items = makeArray(0, 1, 2, null, 4, false, 6); +> 5 | return useIdentity(...items.values()); + | ^^^^^^^^^^^^^^ Todo: Support spread syntax for hook arguments (5:5) + 6 | } + 7 | + 8 | export const FIXTURE_ENTRYPOINT = { +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js new file mode 100644 index 0000000000000..982fd83dc8a20 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js @@ -0,0 +1,12 @@ +import {useIdentity} from 'shared-runtime'; + +function Component() { + const items = makeArray(0, 1, 2, null, 4, false, 6); + return useIdentity(...items.values()); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + sequentialRenders: [{}, {}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md index 877c15e883ffa..56bf9e3d62148 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md @@ -4,9 +4,10 @@ ```javascript import {makeArray} from 'shared-runtime'; -function Component(props) { +const other = [0, 1]; +function Component({}) { const items = makeArray(0, 1, 2, null, 4, false, 6); - const max = Math.max(...items.filter(Boolean)); + const max = Math.max(2, items.push(5), ...other); return max; } @@ -21,13 +22,13 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` - 3 | function Component(props) { - 4 | const items = makeArray(0, 1, 2, null, 4, false, 6); -> 5 | const max = Math.max(...items.filter(Boolean)); - | ^^^^^^^^ Invariant: [Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. Got a `Identifier` (5:5) - 6 | return max; - 7 | } - 8 | + 4 | function Component({}) { + 5 | const items = makeArray(0, 1, 2, null, 4, false, 6); +> 6 | const max = Math.max(2, items.push(5), ...other); + | ^^^^^^^^ Invariant: [Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. Got a `Identifier` (6:6) + 7 | return max; + 8 | } + 9 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js index 475cf383cf1e4..b2883c3303831 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js @@ -1,8 +1,9 @@ import {makeArray} from 'shared-runtime'; -function Component(props) { +const other = [0, 1]; +function Component({}) { const items = makeArray(0, 1, 2, null, 4, false, 6); - const max = Math.max(...items.filter(Boolean)); + const max = Math.max(2, items.push(5), ...other); return max; } diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index bbed77c35a90d..0817bbf89c582 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -462,7 +462,6 @@ const skipFilter = new Set([ // bugs 'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr', - 'bug-array-spread-mutable-iterator', `bug-capturing-func-maybealias-captured-mutate`, 'bug-aliased-capture-aliased-mutate', 'bug-aliased-capture-mutate', From 38a76009202e161cb023a92c33052b9dac7daf14 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:58:17 -0400 Subject: [PATCH 118/300] [compiler][optim] Add shape for Array.from (#32522) (see title) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32522). * #32596 * #32595 * #32594 * #32593 * __->__ #32522 * #32521 --- .../src/HIR/Globals.ts | 18 +++- .../array-from-arg1-captures-arg0.expect.md | 91 +++++++++++++++++++ .../compiler/array-from-arg1-captures-arg0.js | 26 ++++++ .../array-from-captures-arg0.expect.md | 88 ++++++++++++++++++ .../compiler/array-from-captures-arg0.js | 26 ++++++ .../array-from-maybemutates-arg0.expect.md | 64 +++++++++++++ .../compiler/array-from-maybemutates-arg0.js | 14 +++ ...todo-granular-iterator-semantics.expect.md | 24 +++-- ...md => type-inference-array-from.expect.md} | 48 ++++++++-- ...y-from.js => type-inference-array-from.js} | 6 ++ 10 files changed, 385 insertions(+), 20 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{todo-type-inference-array-from.expect.md => type-inference-array-from.expect.md} (71%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{todo-type-inference-array-from.js => type-inference-array-from.js} (87%) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts index 3fe2e938ce57b..df8196c1d7a0d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts @@ -119,8 +119,8 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ ], /* * https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.from - * Array.from(arrayLike, optionalFn, optionalThis) not added because - * the Effect of `arrayLike` is polymorphic i.e. + * Array.from(arrayLike, optionalFn, optionalThis) + * Note that the Effect of `arrayLike` is polymorphic i.e. * - Effect.read if * - it does not have an @iterator property and is array-like * (i.e. has a length property) @@ -128,6 +128,20 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ * - Effect.mutate if it is a self-mutative iterator (e.g. a generator * function) */ + [ + 'from', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [ + Effect.ConditionallyMutate, + Effect.ConditionallyMutate, + Effect.ConditionallyMutate, + ], + restParam: Effect.Read, + returnType: {kind: 'Object', shapeId: BuiltInArrayId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + }), + ], [ 'of', // Array.of(element0, ..., elementN) diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md new file mode 100644 index 0000000000000..8892c8d484bfb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md @@ -0,0 +1,91 @@ + +## Input + +```javascript +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism). + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, (x, idx) => ({...x, id: idx})); + return <Stringify>{derived.at(-1)}</Stringify>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, Stringify } from "shared-runtime"; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism). + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component(t0) { + const $ = _c(4); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = Array.from(arr, _temp); + let t1; + if ($[0] !== derived) { + t1 = derived.at(-1); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = <Stringify>{t1}</Stringify>; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} +function _temp(x, idx) { + return { ...x, id: idx }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok) <div>{"children":{"value":5,"id":2}}</div> +<div>{"children":{"value":6,"id":2}}</div> +<div>{"children":{"value":6,"id":2}}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js new file mode 100644 index 0000000000000..f2b364bc60eef --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js @@ -0,0 +1,26 @@ +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism). + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, (x, idx) => ({...x, id: idx})); + return <Stringify>{derived.at(-1)}</Stringify>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md new file mode 100644 index 0000000000000..66d0b4258462c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md @@ -0,0 +1,88 @@ + +## Input + +```javascript +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism) + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr); + return <Stringify>{derived.at(-1)}</Stringify>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, Stringify } from "shared-runtime"; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism) + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component(t0) { + const $ = _c(4); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = Array.from(arr); + let t1; + if ($[0] !== derived) { + t1 = derived.at(-1); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = <Stringify>{t1}</Stringify>; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok) <div>{"children":{"value":5}}</div> +<div>{"children":{"value":6}}</div> +<div>{"children":{"value":6}}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js new file mode 100644 index 0000000000000..c9b09c384de20 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js @@ -0,0 +1,26 @@ +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism) + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr); + return <Stringify>{derived.at(-1)}</Stringify>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md new file mode 100644 index 0000000000000..586124280a3dd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md @@ -0,0 +1,64 @@ + +## Input + +```javascript +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, mutateAndReturn); + return <Stringify>{derived.at(-1)}</Stringify>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime"; + +function Component(t0) { + const $ = _c(4); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = Array.from(arr, mutateAndReturn); + let t1; + if ($[0] !== derived) { + t1 = derived.at(-1); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = <Stringify>{t1}</Stringify>; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok) <div>{"children":{"value":5,"wat0":"joe"}}</div> +<div>{"children":{"value":6,"wat0":"joe"}}</div> +<div>{"children":{"value":6,"wat0":"joe"}}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js new file mode 100644 index 0000000000000..edb4e37125843 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js @@ -0,0 +1,14 @@ +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, mutateAndReturn); + return <Stringify>{derived.at(-1)}</Stringify>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md index 1ba01dc5bf4df..ea3f1d4f38715 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md @@ -68,21 +68,29 @@ function Validate({ x, input }) { } function useFoo(input) { "use memo"; - const $ = _c(3); + const $ = _c(5); const x = Array.from([{}]); useIdentity(); - x.push([input]); let t0; - if ($[0] !== input || $[1] !== x) { - t0 = <Validate x={x} input={input} />; + if ($[0] !== input) { + t0 = [input]; $[0] = input; - $[1] = x; - $[2] = t0; + $[1] = t0; + } else { + t0 = $[1]; + } + x.push(t0); + let t1; + if ($[2] !== input || $[3] !== x) { + t1 = <Validate x={x} input={input} />; + $[2] = input; + $[3] = x; + $[4] = t1; } else { - t0 = $[2]; + t1 = $[4]; } - return t0; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md similarity index 71% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md index 6061464afce78..5209fd953ec88 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md @@ -37,6 +37,12 @@ function useFoo({val1, val2}) { export const FIXTURE_ENTRYPOINT = { fn: useFoo, params: [{val1: 1, val2: 2}], + params: [ + {val1: 1, val2: 2}, + {val1: 1, val2: 2}, + {val1: 1, val2: 3}, + {val1: 4, val2: 2}, + ], }; ``` @@ -71,29 +77,51 @@ function Validate({ x, val1, val2 }) { } function useFoo(t0) { "use memo"; - const $ = _c(4); + const $ = _c(8); const { val1, val2 } = t0; const x = Array.from([]); useIdentity(); - x.push([val1]); - x.push([val2]); let t1; - if ($[0] !== val1 || $[1] !== val2 || $[2] !== x) { - t1 = <Validate x={x} val1={val1} val2={val2} />; + if ($[0] !== val1) { + t1 = [val1]; $[0] = val1; - $[1] = val2; - $[2] = x; - $[3] = t1; + $[1] = t1; + } else { + t1 = $[1]; + } + x.push(t1); + let t2; + if ($[2] !== val2) { + t2 = [val2]; + $[2] = val2; + $[3] = t2; + } else { + t2 = $[3]; + } + x.push(t2); + let t3; + if ($[4] !== val1 || $[5] !== val2 || $[6] !== x) { + t3 = <Validate x={x} val1={val1} val2={val2} />; + $[4] = val1; + $[5] = val2; + $[6] = x; + $[7] = t3; } else { - t1 = $[3]; + t3 = $[7]; } - return t1; + return t3; } export const FIXTURE_ENTRYPOINT = { fn: useFoo, params: [{ val1: 1, val2: 2 }], + params: [ + { val1: 1, val2: 2 }, + { val1: 1, val2: 2 }, + { val1: 1, val2: 3 }, + { val1: 4, val2: 2 }, + ], }; ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.js similarity index 87% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.js index d1a80a4ea7090..dfd4e0e0f19af 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.js @@ -33,4 +33,10 @@ function useFoo({val1, val2}) { export const FIXTURE_ENTRYPOINT = { fn: useFoo, params: [{val1: 1, val2: 2}], + params: [ + {val1: 1, val2: 2}, + {val1: 1, val2: 2}, + {val1: 1, val2: 3}, + {val1: 4, val2: 2}, + ], }; From eb53139ee50fe53c85e8ad51b21ad0968c1f782d Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:58:40 -0400 Subject: [PATCH 119/300] [compiler][optim] infer mixedReadOnly for numeric and computed properties (#32593) Expand type inference to infer mixedReadOnly types for numeric and computed property accesses. ```js function Component({idx}) const data = useFragment(...) // we want to type `posts` correctly as Array const posts = data.viewers[idx].posts.slice(0, 5); // ... } ``` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32593). * #32596 * #32595 * #32594 * __->__ #32593 * #32522 * #32521 --- .../src/HIR/Environment.ts | 43 ++++++++++--- .../src/HIR/Types.ts | 10 ++- .../src/TypeInference/InferTypes.ts | 47 ++++++++++---- .../relay-transitive-mixeddata.expect.md | 61 +++++++++++++++++++ .../compiler/relay-transitive-mixeddata.js | 21 +++++++ 5 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index b61243f0424ad..7dc9e044bfb7d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -1126,9 +1126,32 @@ export class Environment { ); } + getFallthroughPropertyType( + receiver: Type, + _property: Type, + ): BuiltInType | PolyType | null { + let shapeId = null; + if (receiver.kind === 'Object' || receiver.kind === 'Function') { + shapeId = receiver.shapeId; + } + + if (shapeId !== null) { + const shape = this.#shapes.get(shapeId); + + CompilerError.invariant(shape !== undefined, { + reason: `[HIR] Forget internal error: cannot resolve shape ${shapeId}`, + description: null, + loc: null, + suggestions: null, + }); + return shape.properties.get('*') ?? null; + } + return null; + } + getPropertyType( receiver: Type, - property: string, + property: string | number, ): BuiltInType | PolyType | null { let shapeId = null; if (receiver.kind === 'Object' || receiver.kind === 'Function') { @@ -1146,17 +1169,19 @@ export class Environment { loc: null, suggestions: null, }); - let value = - shape.properties.get(property) ?? shape.properties.get('*') ?? null; - if (value === null && isHookName(property)) { - value = this.#getCustomHookType(); + if (typeof property === 'string') { + return ( + shape.properties.get(property) ?? + shape.properties.get('*') ?? + (isHookName(property) ? this.#getCustomHookType() : null) + ); + } else { + return shape.properties.get('*') ?? null; } - return value; - } else if (isHookName(property)) { + } else if (typeof property === 'string' && isHookName(property)) { return this.#getCustomHookType(); - } else { - return null; } + return null; } getFunctionSignature(type: FunctionType): FunctionSignature | null { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts index a67b580a6728b..1de81919c391d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts @@ -60,7 +60,15 @@ export type PropType = { kind: 'Property'; objectType: Type; objectName: string; - propertyName: PropertyLiteral; + propertyName: + | { + kind: 'literal'; + value: PropertyLiteral; + } + | { + kind: 'computed'; + value: Type; + }; }; export type ObjectMethod = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 3054a83c76138..02e4e60e4b840 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -307,11 +307,26 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.object.identifier.type, objectName: getName(names, value.object.identifier.id), - propertyName: value.property, + propertyName: { + kind: 'literal', + value: value.property, + }, }); break; } + case 'ComputedLoad': { + yield equation(left, { + kind: 'Property', + objectType: value.object.identifier.type, + objectName: getName(names, value.object.identifier.id), + propertyName: { + kind: 'computed', + value: value.property.identifier.type, + }, + }); + break; + } case 'MethodCall': { const returnType = makeType(); yield equation(value.property.identifier.type, { @@ -336,7 +351,10 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.value.identifier.type, objectName: getName(names, value.value.identifier.id), - propertyName: makePropertyLiteral(propertyName), + propertyName: { + kind: 'literal', + value: makePropertyLiteral(propertyName), + }, }); } else { break; @@ -353,7 +371,10 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.value.identifier.type, objectName: getName(names, value.value.identifier.id), - propertyName: makePropertyLiteral(property.key.name), + propertyName: { + kind: 'literal', + value: makePropertyLiteral(property.key.name), + }, }); } } @@ -410,7 +431,6 @@ function* generateInstructionTypes( case 'RegExpLiteral': case 'MetaProperty': case 'ComputedStore': - case 'ComputedLoad': case 'Await': case 'GetIterator': case 'IteratorNext': @@ -454,12 +474,13 @@ class Unifier { return; } const objectType = this.get(tB.objectType); - let propertyType; - if (typeof tB.propertyName === 'number') { - propertyType = null; - } else { - propertyType = this.env.getPropertyType(objectType, tB.propertyName); - } + const propertyType = + tB.propertyName.kind === 'literal' + ? this.env.getPropertyType(objectType, tB.propertyName.value) + : this.env.getFallthroughPropertyType( + objectType, + tB.propertyName.value, + ); if (propertyType !== null) { this.unify(tA, propertyType); } @@ -677,7 +698,11 @@ class Unifier { const RefLikeNameRE = /^(?:[a-zA-Z$_][a-zA-Z$_0-9]*)Ref$|^ref$/; function isRefLikeName(t: PropType): boolean { - return RefLikeNameRE.test(t.objectName) && t.propertyName === 'current'; + return ( + t.propertyName.kind === 'literal' && + RefLikeNameRE.test(t.objectName) && + t.propertyName.value === 'current' + ); } function tryUnionTypes(ty1: Type, ty2: Type): Type | null { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md new file mode 100644 index 0000000000000..5caf2e8e0da08 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md @@ -0,0 +1,61 @@ + +## Input + +```javascript +import {useFragment} from 'shared-runtime'; + +/** + * React compiler should infer that the returned value is a primitive and avoid + * memoizing it. + */ +function useRelayData({query, idx}) { + 'use memo'; + const data = useFragment('', query); + return data.a[idx].toString(); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useRelayData, + params: [{query: '', idx: 0}], + sequentialRenders: [ + {query: '', idx: 0}, + {query: '', idx: 0}, + {query: '', idx: 1}, + ], +}; + +``` + +## Code + +```javascript +import { useFragment } from "shared-runtime"; + +/** + * React compiler should infer that the returned value is a primitive and avoid + * memoizing it. + */ +function useRelayData(t0) { + "use memo"; + const { query, idx } = t0; + + const data = useFragment("", query); + return data.a[idx].toString(); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useRelayData, + params: [{ query: "", idx: 0 }], + sequentialRenders: [ + { query: "", idx: 0 }, + { query: "", idx: 0 }, + { query: "", idx: 1 }, + ], +}; + +``` + +### Eval output +(kind: ok) "1" +"1" +"2" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js new file mode 100644 index 0000000000000..78708f30c71c6 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js @@ -0,0 +1,21 @@ +import {useFragment} from 'shared-runtime'; + +/** + * React compiler should infer that the returned value is a primitive and avoid + * memoizing it. + */ +function useRelayData({query, idx}) { + 'use memo'; + const data = useFragment('', query); + return data.a[idx].toString(); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useRelayData, + params: [{query: '', idx: 0}], + sequentialRenders: [ + {query: '', idx: 0}, + {query: '', idx: 0}, + {query: '', idx: 1}, + ], +}; From 89a46a57df3a3cc309fff937794341ab215e01e9 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:59:50 -0400 Subject: [PATCH 120/300] [compiler][optim] more shapes for mixedreadonly (#32594) - Add `at`, `indexOf`, and `includes` - Optimize MixedReadOnly which is currently only used by hook return values. Hook return values are typed as Frozen, this change propagates that to return values of aliasing function calls (such as `at`). One potential issue is that developers may pass `enableAssumeHooksFollowRulesOfReact:false` and set `transitiveMixedData`, expecting their transitive mixed data to be mutable. This is a bit of an edge case and already doesn't have clear semantics. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32594). * #32596 * #32595 * __->__ #32594 * #32593 * #32522 * #32521 --- .../src/HIR/ObjectShape.ts | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts index f51768c586b72..22ae261867b3a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts @@ -535,6 +535,30 @@ addObject(BUILTIN_SHAPES, BuiltInRefValueId, [ ['*', {kind: 'Object', shapeId: BuiltInRefValueId}], ]); +/** + * MixedReadOnly = + * | primitive + * | simple objects (Record<string, MixedReadOnly>) + * | Array<MixedReadOnly> + * + * APIs such as Relay — but also Flux and other data stores — often return a + * union of types with some interesting properties in terms of analysis. + * + * Given this constraint, if data came from Relay, then we should be able to + * infer things like `data.items.map(): Array`. That may seem like a leap at + * first but remember, we assume you're not patching builtins. Thus the only way + * data.items.map can exist and be a function, given the above set of data types + * and builtin JS methods, is if `data.items` was an Array, and `data.items.map` + * is therefore calling Array.prototype.map. Then we know that function returns + * an Array as well. This relies on the fact that map() is being called, so if + * data.items was some other type it would error at runtime - so it's sound. + * + * Note that this shape is currently only used for hook return values, which + * means that it's safe to type aliasing method-call return kinds as `Frozen`. + * + * Also note that all newly created arrays from method-calls (e.g. `.map`) + * have the appropriate mutable `BuiltInArray` shape + */ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [ [ 'toString', @@ -546,6 +570,36 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [ returnValueKind: ValueKind.Primitive, }), ], + [ + 'indexOf', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'includes', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'at', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Frozen, + }), + ], [ 'map', addFunction(BUILTIN_SHAPES, [], { @@ -642,9 +696,9 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [ addFunction(BUILTIN_SHAPES, [], { positionalParams: [], restParam: Effect.ConditionallyMutate, - returnType: {kind: 'Poly'}, + returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId}, calleeEffect: Effect.ConditionallyMutate, - returnValueKind: ValueKind.Mutable, + returnValueKind: ValueKind.Frozen, noAlias: true, mutableOnlyIfOperandsAreMutable: true, }), From 1c79cb82ab8d3bd1f099115704f28df1097beb46 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:06:48 -0400 Subject: [PATCH 121/300] [compiler][ez] Move compiler gating tests (#32595) Move all gating tests to `gating/` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32595). * #32596 * __->__ #32595 * #32594 * #32593 * #32522 * #32521 --- .../src/HIR/Environment.ts | 2 +- .../arrow-function-expr-gating-test.expect.md | 35 ---------- .../arrow-function-expr-gating-test.js | 4 -- ...codegen-emit-imports-same-source.expect.md | 2 +- .../codegen-instrument-forget-test.expect.md | 4 +- ...gating-test-export-function-and-default.js | 14 ---- .../arrow-function-expr-gating-test.expect.md | 50 +++++++++++++++ .../gating/arrow-function-expr-gating-test.js | 10 +++ ...en-instrument-forget-gating-test.expect.md | 64 +++++++++++++++---- .../codegen-instrument-forget-gating-test.js | 15 ++++- ...component-syntax-ref-gating.flow.expect.md | 0 .../error.component-syntax-ref-gating.flow.js | 0 .../error.gating-hoisting.expect.md | 0 .../{ => gating}/error.gating-hoisting.js | 0 .../error.gating-use-before-decl.expect.md | 0 .../error.gating-use-before-decl.js | 0 ...ccess-function-name-in-component.expect.md | 0 ...ating-access-function-name-in-component.js | 0 ...ng-preserves-function-properties.expect.md | 0 .../gating-preserves-function-properties.tsx | 0 ...ing-test-export-default-function.expect.md | 14 +++- .../gating-test-export-default-function.js | 5 ++ ...test-export-function-and-default.expect.md | 63 ++++++++++++++---- ...gating-test-export-function-and-default.js | 26 ++++++++ .../gating-test-export-function.expect.md | 14 +++- .../gating-test-export-function.js | 5 ++ .../{ => gating}/gating-test.expect.md | 14 +++- .../compiler/{ => gating}/gating-test.js | 5 ++ ...with-hoisted-type-reference.flow.expect.md | 14 +++- ...gating-with-hoisted-type-reference.flow.js | 5 ++ ...ion-expression-React-memo-gating.expect.md | 5 +- ...r-function-expression-React-memo-gating.js | 0 ...expr-export-default-gating-test.expect.md} | 21 +++--- ...i-arrow-expr-export-default-gating-test.js | 11 ++++ ...ti-arrow-expr-export-gating-test.expect.md | 41 ++++++++---- .../multi-arrow-expr-export-gating-test.js | 16 +++++ .../multi-arrow-expr-gating-test.expect.md} | 49 +++++++++----- .../gating/multi-arrow-expr-gating-test.js | 18 ++++++ ...mport-without-compiled-functions.expect.md | 0 ...ating-import-without-compiled-functions.js | 0 ...i-arrow-expr-export-default-gating-test.js | 9 --- .../multi-arrow-expr-export-gating-test.js | 9 --- .../compiler/multi-arrow-expr-gating-test.js | 11 ---- .../packages/snap/src/SproutTodoFilter.ts | 13 +--- 44 files changed, 402 insertions(+), 166 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/codegen-instrument-forget-gating-test.expect.md (57%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/codegen-instrument-forget-gating-test.js (52%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.component-syntax-ref-gating.flow.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.component-syntax-ref-gating.flow.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-hoisting.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-hoisting.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-use-before-decl.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-use-before-decl.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-access-function-name-in-component.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-access-function-name-in-component.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-preserves-function-properties.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-preserves-function-properties.tsx (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-default-function.expect.md (86%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-default-function.js (76%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-function-and-default.expect.md (51%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-function.expect.md (86%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-function.js (77%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test.expect.md (86%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test.js (75%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-with-hoisted-type-reference.flow.expect.md (79%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-with-hoisted-type-reference.flow.js (69%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/infer-function-expression-React-memo-gating.expect.md (80%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/infer-function-expression-React-memo-gating.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{multi-arrow-expr-gating-test.expect.md => gating/multi-arrow-expr-export-default-gating-test.expect.md} (69%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/multi-arrow-expr-export-gating-test.expect.md (57%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{multi-arrow-expr-export-default-gating-test.expect.md => gating/multi-arrow-expr-gating-test.expect.md} (50%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/repro-no-gating-import-without-compiled-functions.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/repro-no-gating-import-without-compiled-functions.js (100%) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 7dc9e044bfb7d..785240653e0d3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -663,7 +663,7 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = { source: 'react-compiler-runtime', importSpecifierName: 'shouldInstrument', }, - globalGating: '__DEV__', + globalGating: 'DEV', }, enableEmitHookGuards: { source: 'react-compiler-runtime', diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md deleted file mode 100644 index 25feb6fd64d1b..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md +++ /dev/null @@ -1,35 +0,0 @@ - -## Input - -```javascript -// @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; - -export default ErrorView; - -``` - -## Code - -```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating -const ErrorView = isForgetEnabled_Fixtures() - ? (error, _retry) => { - const $ = _c(2); - let t0; - if ($[0] !== error) { - t0 = <MessageBox error={error} />; - $[0] = error; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; - } - : (error, _retry) => <MessageBox error={error}></MessageBox>; - -export default ErrorView; - -``` - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js deleted file mode 100644 index 3debc725be2bd..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js +++ /dev/null @@ -1,4 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; - -export default ErrorView; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md index 9a59b36cc0f10..161b42dc6d7d3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md @@ -21,7 +21,7 @@ import { import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @enableEmitInstrumentForget function useFoo(props) { - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("useFoo", "/codegen-emit-imports-same-source.ts"); const $ = _c(2); let t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md index b5da853b6e5c7..319de18794f2d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md @@ -28,7 +28,7 @@ import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget function Bar(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Bar", "/codegen-instrument-forget-test.ts"); const $ = _c(2); let t0; @@ -48,7 +48,7 @@ function NoForget(props) { function Foo(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Foo", "/codegen-instrument-forget-test.ts"); const $ = _c(2); let t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js deleted file mode 100644 index abd227e247eaf..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js +++ /dev/null @@ -1,14 +0,0 @@ -// @gating @compilationMode(annotation) -export default function Bar(props) { - 'use forget'; - return <div>{props.bar}</div>; -} - -function NoForget(props) { - return <Bar>{props.noForget}</Bar>; -} - -export function Foo(props) { - 'use forget'; - return <Foo>{props.bar}</Foo>; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md new file mode 100644 index 0000000000000..659e2c9ff9e48 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @gating +import {Stringify} from 'shared-runtime'; +const ErrorView = ({error, _retry}) => <Stringify error={error}></Stringify>; + +export default ErrorView; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('ErrorView'), + params: [{}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; +const ErrorView = isForgetEnabled_Fixtures() + ? (t0) => { + const $ = _c(2); + const { error } = t0; + let t1; + if ($[0] !== error) { + t1 = <Stringify error={error} />; + $[0] = error; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + : ({ error, _retry }) => <Stringify error={error}></Stringify>; + +export default ErrorView; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("ErrorView"), + params: [{}], +}; + +``` + +### Eval output +(kind: ok) <div>{}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js new file mode 100644 index 0000000000000..4c6ab77596ec1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js @@ -0,0 +1,10 @@ +// @gating +import {Stringify} from 'shared-runtime'; +const ErrorView = ({error, _retry}) => <Stringify error={error}></Stringify>; + +export default ErrorView; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('ErrorView'), + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md similarity index 57% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md index fc9247344d56c..fe85e38e106a6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md @@ -15,9 +15,22 @@ function NoForget(props) { function Foo(props) { 'use forget'; - return <Foo>{props.bar}</Foo>; + if (props.bar < 0) { + return props.children; + } + return ( + <Foo bar={props.bar - 1}> + <NoForget /> + </Foo> + ); } +global.DEV = true; +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{bar: 2}], +}; + ``` ## Code @@ -29,7 +42,7 @@ import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Bar", "/codegen-instrument-forget-gating-test.ts"); const $ = _c(2); let t0; @@ -53,23 +66,50 @@ function NoForget(props) { const Foo = isForgetEnabled_Fixtures() ? function Foo(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Foo", "/codegen-instrument-forget-gating-test.ts"); - const $ = _c(2); - let t0; - if ($[0] !== props.bar) { - t0 = <Foo>{props.bar}</Foo>; - $[0] = props.bar; + const $ = _c(3); + if (props.bar < 0) { + return props.children; + } + + const t0 = props.bar - 1; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = <NoForget />; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== t0) { + t2 = <Foo bar={t0}>{t1}</Foo>; $[1] = t0; + $[2] = t2; } else { - t0 = $[1]; + t2 = $[2]; } - return t0; + return t2; } : function Foo(props) { "use forget"; - return <Foo>{props.bar}</Foo>; + if (props.bar < 0) { + return props.children; + } + return ( + <Foo bar={props.bar - 1}> + <NoForget /> + </Foo> + ); }; +global.DEV = true; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Foo"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div></div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.js similarity index 52% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.js index dffb8ce795357..6730630ac3885 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.js @@ -11,5 +11,18 @@ function NoForget(props) { function Foo(props) { 'use forget'; - return <Foo>{props.bar}</Foo>; + if (props.bar < 0) { + return props.children; + } + return ( + <Foo bar={props.bar - 1}> + <NoForget /> + </Foo> + ); } + +global.DEV = true; +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.tsx similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.tsx rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md index 92830938b5927..4e4a35c37557a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md @@ -17,6 +17,11 @@ function Foo(props) { return <Foo>{props.bar}</Foo>; } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -66,5 +71,12 @@ const Foo = isForgetEnabled_Fixtures() return <Foo>{props.bar}</Foo>; }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div>2</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.js similarity index 76% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.js index 5ac847953d58f..1ec9a902b2448 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.js @@ -12,3 +12,8 @@ function Foo(props) { 'use forget'; return <Foo>{props.bar}</Foo>; } + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md similarity index 51% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md index ea3d320a3ec42..95c629aa11f95 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md @@ -12,11 +12,23 @@ function NoForget(props) { return <Bar>{props.noForget}</Bar>; } -export function Foo(props) { +function Foo(props) { 'use forget'; - return <Foo>{props.bar}</Foo>; + if (props.bar < 0) { + return props.children; + } + return ( + <Foo bar={props.bar - 1}> + <NoForget /> + </Foo> + ); } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -47,25 +59,50 @@ export default Bar; function NoForget(props) { return <Bar>{props.noForget}</Bar>; } - -export const Foo = isForgetEnabled_Fixtures() +const Foo = isForgetEnabled_Fixtures() ? function Foo(props) { "use forget"; - const $ = _c(2); - let t0; - if ($[0] !== props.bar) { - t0 = <Foo>{props.bar}</Foo>; - $[0] = props.bar; + const $ = _c(3); + if (props.bar < 0) { + return props.children; + } + + const t0 = props.bar - 1; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = <NoForget />; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== t0) { + t2 = <Foo bar={t0}>{t1}</Foo>; $[1] = t0; + $[2] = t2; } else { - t0 = $[1]; + t2 = $[2]; } - return t0; + return t2; } : function Foo(props) { "use forget"; - return <Foo>{props.bar}</Foo>; + if (props.bar < 0) { + return props.children; + } + return ( + <Foo bar={props.bar - 1}> + <NoForget /> + </Foo> + ); }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div>2</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js new file mode 100644 index 0000000000000..5b7f7c81b74c0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js @@ -0,0 +1,26 @@ +// @gating @compilationMode(annotation) +export default function Bar(props) { + 'use forget'; + return <div>{props.bar}</div>; +} + +function NoForget(props) { + return <Bar>{props.noForget}</Bar>; +} + +function Foo(props) { + 'use forget'; + if (props.bar < 0) { + return props.children; + } + return ( + <Foo bar={props.bar - 1}> + <NoForget /> + </Foo> + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md index caaf1b4472207..ab31d2939d44d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md @@ -17,6 +17,11 @@ export function Foo(props) { return <Foo>{props.bar}</Foo>; } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -66,5 +71,12 @@ export const Foo = isForgetEnabled_Fixtures() return <Foo>{props.bar}</Foo>; }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div>2</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.js similarity index 77% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.js index 45ce5cbdb56a4..5bcf9ac67d764 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.js @@ -12,3 +12,8 @@ export function Foo(props) { 'use forget'; return <Foo>{props.bar}</Foo>; } + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md index 5166dab4184bd..e68e4840cf526 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md @@ -17,6 +17,11 @@ function Foo(props) { return <Foo>{props.bar}</Foo>; } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -65,5 +70,12 @@ const Foo = isForgetEnabled_Fixtures() return <Foo>{props.bar}</Foo>; }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div>2</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.js similarity index 75% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.js index bf031d8b1562c..3bb92654d97d0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.js @@ -12,3 +12,8 @@ function Foo(props) { 'use forget'; return <Foo>{props.bar}</Foo>; } + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md similarity index 79% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md index e87606372da08..26c6e510d1479 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md @@ -13,6 +13,11 @@ component Component(value: string) { export default memo<Props>(Component); +export const FIXTURE_ENTRYPOINT = { + fn: eval('Component'), + params: [{value: 'foo'}], +}; + ``` ## Code @@ -43,5 +48,12 @@ const Component = isForgetEnabled_Fixtures() export default memo<Props>(Component); +export const FIXTURE_ENTRYPOINT = { + fn: eval("Component"), + params: [{ value: "foo" }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div>foo</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.js similarity index 69% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.js index 86433ec50d346..1fe640b6934a2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.js @@ -8,3 +8,8 @@ component Component(value: string) { } export default memo<Props>(Component); + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Component'), + params: [{value: 'foo'}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md similarity index 80% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md index 6524c9d20288d..4931b87b01765 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md @@ -35,4 +35,7 @@ export default React.forwardRef( ); ``` - \ No newline at end of file + +### Eval output +(kind: exception) Fixture not implemented +logs: ['forwardRef render functions accept exactly two parameters: props and ref. %s','Did you forget to use the ref parameter?'] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md similarity index 69% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md index 92d3aa72093e1..b3dc5011f9ca5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md @@ -3,17 +3,17 @@ ```javascript // @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; +import {Stringify} from 'shared-runtime'; -const Renderer = props => ( +const ErrorView = (error, _retry) => <Stringify error={error}></Stringify>; + +export default props => ( <Foo> <Bar></Bar> <ErrorView></ErrorView> </Foo> ); -export default Renderer; - ``` ## Code @@ -21,12 +21,14 @@ export default Renderer; ```javascript import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; + const ErrorView = isForgetEnabled_Fixtures() ? (error, _retry) => { const $ = _c(2); let t0; if ($[0] !== error) { - t0 = <MessageBox error={error} />; + t0 = <Stringify error={error} />; $[0] = error; $[1] = t0; } else { @@ -34,9 +36,9 @@ const ErrorView = isForgetEnabled_Fixtures() } return t0; } - : (error, _retry) => <MessageBox error={error}></MessageBox>; + : (error, _retry) => <Stringify error={error}></Stringify>; -const Renderer = isForgetEnabled_Fixtures() +export default isForgetEnabled_Fixtures() ? (props) => { const $ = _c(1); let t0; @@ -59,7 +61,8 @@ const Renderer = isForgetEnabled_Fixtures() <ErrorView></ErrorView> </Foo> ); -export default Renderer; ``` - \ No newline at end of file + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js new file mode 100644 index 0000000000000..e856a290f7f41 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js @@ -0,0 +1,11 @@ +// @gating +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => <Stringify error={error}></Stringify>; + +export default props => ( + <Foo> + <Bar></Bar> + <ErrorView></ErrorView> + </Foo> +); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md similarity index 57% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md index 61d87a11cd39a..ea2a1c154129d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md @@ -3,15 +3,22 @@ ```javascript // @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => <Stringify error={error}></Stringify>; export const Renderer = props => ( - <Foo> - <Bar></Bar> + <div> + <span></span> <ErrorView></ErrorView> - </Foo> + </div> ); +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; + ``` ## Code @@ -19,12 +26,14 @@ export const Renderer = props => ( ```javascript import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; + const ErrorView = isForgetEnabled_Fixtures() ? (error, _retry) => { const $ = _c(2); let t0; if ($[0] !== error) { - t0 = <MessageBox error={error} />; + t0 = <Stringify error={error} />; $[0] = error; $[1] = t0; } else { @@ -32,7 +41,7 @@ const ErrorView = isForgetEnabled_Fixtures() } return t0; } - : (error, _retry) => <MessageBox error={error}></MessageBox>; + : (error, _retry) => <Stringify error={error}></Stringify>; export const Renderer = isForgetEnabled_Fixtures() ? (props) => { @@ -40,10 +49,10 @@ export const Renderer = isForgetEnabled_Fixtures() let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = ( - <Foo> - <Bar /> + <div> + <span /> <ErrorView /> - </Foo> + </div> ); $[0] = t0; } else { @@ -52,11 +61,17 @@ export const Renderer = isForgetEnabled_Fixtures() return t0; } : (props) => ( - <Foo> - <Bar></Bar> + <div> + <span></span> <ErrorView></ErrorView> - </Foo> + </div> ); +export const FIXTURE_ENTRYPOINT = { + fn: eval("Renderer"), + params: [{}], +}; ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div><span></span><div>{"error":{}}</div></div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js new file mode 100644 index 0000000000000..19152d6839340 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js @@ -0,0 +1,16 @@ +// @gating +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => <Stringify error={error}></Stringify>; + +export const Renderer = props => ( + <div> + <span></span> + <ErrorView></ErrorView> + </div> +); + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md similarity index 50% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md index a9a90eaa21e70..cc5195edb2224 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md @@ -3,15 +3,24 @@ ```javascript // @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; +import {Stringify} from 'shared-runtime'; -export default Renderer = props => ( - <Foo> - <Bar></Bar> +const ErrorView = (error, _retry) => <Stringify error={error}></Stringify>; + +const Renderer = props => ( + <div> + <span></span> <ErrorView></ErrorView> - </Foo> + </div> ); +export default Renderer; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; + ``` ## Code @@ -19,12 +28,14 @@ export default Renderer = props => ( ```javascript import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; + const ErrorView = isForgetEnabled_Fixtures() ? (error, _retry) => { const $ = _c(2); let t0; if ($[0] !== error) { - t0 = <MessageBox error={error} />; + t0 = <Stringify error={error} />; $[0] = error; $[1] = t0; } else { @@ -32,18 +43,18 @@ const ErrorView = isForgetEnabled_Fixtures() } return t0; } - : (error, _retry) => <MessageBox error={error}></MessageBox>; + : (error, _retry) => <Stringify error={error}></Stringify>; -export default Renderer = isForgetEnabled_Fixtures() +const Renderer = isForgetEnabled_Fixtures() ? (props) => { const $ = _c(1); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = ( - <Foo> - <Bar /> + <div> + <span /> <ErrorView /> - </Foo> + </div> ); $[0] = t0; } else { @@ -52,11 +63,19 @@ export default Renderer = isForgetEnabled_Fixtures() return t0; } : (props) => ( - <Foo> - <Bar></Bar> + <div> + <span></span> <ErrorView></ErrorView> - </Foo> + </div> ); +export default Renderer; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("Renderer"), + params: [{}], +}; ``` - \ No newline at end of file + +### Eval output +(kind: ok) <div><span></span><div>{"error":{}}</div></div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js new file mode 100644 index 0000000000000..ae4fd8dc94cf5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js @@ -0,0 +1,18 @@ +// @gating +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => <Stringify error={error}></Stringify>; + +const Renderer = props => ( + <div> + <span></span> + <ErrorView></ErrorView> + </div> +); + +export default Renderer; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js deleted file mode 100644 index 0c07f55b10104..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js +++ /dev/null @@ -1,9 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; - -export default Renderer = props => ( - <Foo> - <Bar></Bar> - <ErrorView></ErrorView> - </Foo> -); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js deleted file mode 100644 index ac71868f33fa0..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js +++ /dev/null @@ -1,9 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; - -export const Renderer = props => ( - <Foo> - <Bar></Bar> - <ErrorView></ErrorView> - </Foo> -); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js deleted file mode 100644 index 72109d6f81676..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js +++ /dev/null @@ -1,11 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>; - -const Renderer = props => ( - <Foo> - <Bar></Bar> - <ErrorView></ErrorView> - </Foo> -); - -export default Renderer; diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index 0817bbf89c582..7c487869d333d 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -222,7 +222,6 @@ const skipFilter = new Set([ 'array-at-mutate-after-capture', 'array-join', 'array-push-effect', - 'arrow-function-expr-gating-test', 'assignment-in-nested-if', 'await-side-effecting-promise', 'await', @@ -335,16 +334,10 @@ const skipFilter = new Set([ 'babel-existing-react-import', 'babel-existing-react-kitchensink-import', 'call', - 'codegen-instrument-forget-gating-test', 'codegen-instrument-forget-test', 'conditional-on-mutable', 'constructor', 'frozen-after-alias', - 'gating-test-export-default-function', - 'gating-test-export-function-and-default', - 'gating-test-export-function', - 'gating-test', - 'gating-with-hoisted-type-reference.flow', 'hook-call', 'hooks-freeze-arguments', 'hooks-freeze-possibly-mutable-arguments', @@ -352,8 +345,6 @@ const skipFilter = new Set([ 'independent', 'interdependent-across-if', 'interdependent', - 'multi-arrow-expr-export-gating-test', - 'multi-arrow-expr-gating-test', 'mutable-liverange-loop', 'sequence-expression', 'ssa-call-jsx-2', @@ -361,7 +352,6 @@ const skipFilter = new Set([ 'ssa-newexpression', 'ssa-shadowing', 'template-literal', - 'multi-arrow-expr-export-default-gating-test', // works, but appears differently when printing // due to optional function argument @@ -407,7 +397,6 @@ const skipFilter = new Set([ 'infer-functions-hook-with-hook-call', 'infer-functions-hook-with-jsx', 'infer-function-expression-component', - 'infer-function-expression-React-memo-gating', 'infer-skip-components-without-hooks-or-jsx', 'class-component-with-render-helper', 'fbt/fbtparam-with-jsx-element-content', @@ -422,7 +411,7 @@ const skipFilter = new Set([ 'transitive-freeze-function-expressions', // nothing to compile/run - 'repro-no-gating-import-without-compiled-functions', + 'gating/repro-no-gating-import-without-compiled-functions', // TODOs 'rules-of-hooks/todo.bail.rules-of-hooks-279ac76f53af', From f457d0b4c6dd70c10acb9c93c7d01c80d8e23b92 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:10:22 -0400 Subject: [PATCH 122/300] [compiler][ez] Only fail gating hoisting check for referenced identifiers (#32596) Reduce false positive bailouts by using the same `isReferencedIdentifier` logic that the compiler also uses for determining context variables and a function's own hoisted declarations. Details: Previously, we counted every babel identifier as a reference. This is problematic because babel counts most string symbols as an identifier. ```js print(x); // x is an identifier as expected obj.x // x is.. also an identifier here {x: 2} // x is also an identifier here ``` This PR adds a check for `isReferencedIdentifier`. Note that only non-lval references pass this check. This should be fine as we don't need to hoist function declarations before writes to the same lvalue (which should error in strict mode anyways) ```js print(x); // isReferencedIdentifier(x) -> true obj.x // isReferencedIdentifier(x) -> false {x: 2} // isReferencedIdentifier(x) -> false x = 2 // isReferencedIdentifier(x) -> false ``` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32596). * __->__ #32596 * #32595 * #32594 * #32593 * #32522 * #32521 --- .../src/Entrypoint/Program.ts | 2 +- ...nreferenced-identifier-collision.expect.md | 60 +++++++++++++++++++ ...ting-nonreferenced-identifier-collision.js | 16 +++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 787d9e7047efe..d69dee527dba3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -1143,7 +1143,7 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel( * A null scope means there's no function scope, which means we're at the * top level scope. */ - if (scope === null) { + if (scope === null && id.isReferencedIdentifier()) { errors.pushErrorDetail( new CompilerErrorDetail({ reason: `Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting`, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md new file mode 100644 index 0000000000000..a8f2a8dc58b21 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md @@ -0,0 +1,60 @@ + +## Input + +```javascript +// @gating +import {identity, useHook as useRenamed} from 'shared-runtime'; +const _ = { + useHook: () => {}, +}; +identity(_.useHook); + +function useHook() { + useRenamed(); + return <div>hello world!</div>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { identity, useHook as useRenamed } from "shared-runtime"; +const _ = { + useHook: isForgetEnabled_Fixtures() ? () => {} : () => {}, +}; +identity(_.useHook); +const useHook = isForgetEnabled_Fixtures() + ? function useHook() { + const $ = _c(1); + useRenamed(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <div>hello world!</div>; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : function useHook() { + useRenamed(); + return <div>hello world!</div>; + }; + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{}], +}; + +``` + +### Eval output +(kind: ok) <div>hello world!</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.js new file mode 100644 index 0000000000000..f5d557978b2d0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.js @@ -0,0 +1,16 @@ +// @gating +import {identity, useHook as useRenamed} from 'shared-runtime'; +const _ = { + useHook: () => {}, +}; +identity(_.useHook); + +function useHook() { + useRenamed(); + return <div>hello world!</div>; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{}], +}; From 0df46f01a90f16e771a80cf13dadce3aca091b95 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:54:39 -0400 Subject: [PATCH 123/300] [ci] Update eslint-plugin-react-hooks output location for Meta builds (#32601) Updates where this file is output so we can sync it independently to another directory. --- .github/workflows/runtime_commit_artifacts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 4315256affe45..93230d293486b 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -107,9 +107,9 @@ jobs: mkdir ./compiled/facebook-www/__test_utils__ mv build/__test_utils__/ReactAllWarnings.js ./compiled/facebook-www/__test_utils__/ReactAllWarnings.js - # Move eslint-plugin-react-hooks into facebook-www + # Move eslint-plugin-react-hooks into eslint-plugin-react-hooks mv build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \ - ./compiled/facebook-www/eslint-plugin-react-hooks.js + ./compiled/eslint-plugin-react-hooks/index.js # Move unstable_server-external-runtime.js into facebook-www mv build/oss-experimental/react-dom/unstable_server-external-runtime.js \ From 77987e5ee370dd40c53a6f026c8026065cc3178c Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 17:46:46 -0400 Subject: [PATCH 124/300] [ci] mkdir before mv (#32602) Missed this earlier. --- .github/workflows/runtime_commit_artifacts.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 93230d293486b..b136c60bb714b 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -108,6 +108,7 @@ jobs: mv build/__test_utils__/ReactAllWarnings.js ./compiled/facebook-www/__test_utils__/ReactAllWarnings.js # Move eslint-plugin-react-hooks into eslint-plugin-react-hooks + mkdir ./compiled/eslint-plugin-react-hooks mv build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \ ./compiled/eslint-plugin-react-hooks/index.js From 93b61fc4ecb34abec2b55c206f34ed22dd340b71 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 19:08:38 -0400 Subject: [PATCH 125/300] [compiler][ez] Stop bailing out early for hoisted gated functions (#32597) Some code movement for the next PR --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32597). * #32598 * __->__ #32597 --- .../src/Entrypoint/Gating.ts | 91 +++++++++++-------- .../src/Entrypoint/Options.ts | 9 ++ .../src/Entrypoint/Program.ts | 70 +++++++------- 3 files changed, 93 insertions(+), 77 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts index 3edd673a5ed29..263f76395f0fa 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts @@ -8,6 +8,7 @@ import {NodePath} from '@babel/core'; import * as t from '@babel/types'; import {PluginOptions} from './Options'; +import {CompilerError} from '../CompilerError'; export function insertGatedFunctionDeclaration( fnPath: NodePath< @@ -18,47 +19,59 @@ export function insertGatedFunctionDeclaration( | t.ArrowFunctionExpression | t.FunctionExpression, gating: NonNullable<PluginOptions['gating']>, + referencedBeforeDeclaration: boolean, ): void { - const gatingExpression = t.conditionalExpression( - t.callExpression(t.identifier(gating.importSpecifierName), []), - buildFunctionExpression(compiled), - buildFunctionExpression(fnPath.node), - ); - - /* - * Convert function declarations to named variables *unless* this is an - * `export default function ...` since `export default const ...` is - * not supported. For that case we fall through to replacing w the raw - * conditional expression - */ - if ( - fnPath.parentPath.node.type !== 'ExportDefaultDeclaration' && - fnPath.node.type === 'FunctionDeclaration' && - fnPath.node.id != null - ) { - fnPath.replaceWith( - t.variableDeclaration('const', [ - t.variableDeclarator(fnPath.node.id, gatingExpression), - ]), - ); - } else if ( - fnPath.parentPath.node.type === 'ExportDefaultDeclaration' && - fnPath.node.type !== 'ArrowFunctionExpression' && - fnPath.node.id != null - ) { - fnPath.insertAfter( - t.exportDefaultDeclaration(t.identifier(fnPath.node.id.name)), - ); - fnPath.parentPath.replaceWith( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(fnPath.node.id.name), - gatingExpression, - ), - ]), - ); + if (referencedBeforeDeclaration) { + const identifier = + fnPath.node.type === 'FunctionDeclaration' ? fnPath.node.id : null; + CompilerError.invariant(false, { + reason: `Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting`, + description: `Rewrite the reference to ${identifier?.name ?? 'this function'} to not rely on hoisting to fix this issue`, + loc: identifier?.loc ?? null, + suggestions: null, + }); } else { - fnPath.replaceWith(gatingExpression); + const gatingExpression = t.conditionalExpression( + t.callExpression(t.identifier(gating.importSpecifierName), []), + buildFunctionExpression(compiled), + buildFunctionExpression(fnPath.node), + ); + + /* + * Convert function declarations to named variables *unless* this is an + * `export default function ...` since `export default const ...` is + * not supported. For that case we fall through to replacing w the raw + * conditional expression + */ + if ( + fnPath.parentPath.node.type !== 'ExportDefaultDeclaration' && + fnPath.node.type === 'FunctionDeclaration' && + fnPath.node.id != null + ) { + fnPath.replaceWith( + t.variableDeclaration('const', [ + t.variableDeclarator(fnPath.node.id, gatingExpression), + ]), + ); + } else if ( + fnPath.parentPath.node.type === 'ExportDefaultDeclaration' && + fnPath.node.type !== 'ArrowFunctionExpression' && + fnPath.node.id != null + ) { + fnPath.insertAfter( + t.exportDefaultDeclaration(t.identifier(fnPath.node.id.name)), + ); + fnPath.parentPath.replaceWith( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(fnPath.node.id.name), + gatingExpression, + ), + ]), + ); + } else { + fnPath.replaceWith(gatingExpression); + } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index 781abd05f35da..e0c670c5641cd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -12,6 +12,7 @@ import { EnvironmentConfig, ExternalFunction, parseEnvironmentConfig, + tryParseExternalFunction, } from '../HIR/Environment'; import {hasOwnProperty} from '../Utils/utils'; import {fromZodError} from 'zod-validation-error'; @@ -271,6 +272,14 @@ export function parsePluginOptions(obj: unknown): PluginOptions { parsedOptions[key] = parseTargetConfig(value); break; } + case 'gating': { + if (value == null) { + parsedOptions[key] = null; + } else { + parsedOptions[key] = tryParseExternalFunction(value); + } + break; + } default: { parsedOptions[key] = value; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index d69dee527dba3..34c1af955b3cc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -17,7 +17,6 @@ import { ExternalFunction, ReactFunctionType, MINIMAL_RETRY_CONFIG, - tryParseExternalFunction, } from '../HIR/Environment'; import {CodegenFunction} from '../ReactiveScopes'; import {isComponentDeclaration} from '../Utils/ComponentDeclaration'; @@ -541,30 +540,26 @@ export function compileProgram( if (moduleScopeOptOutDirectives.length > 0) { return; } - + let gating: null | { + gatingFn: ExternalFunction; + referencedBeforeDeclared: Set<CompileResult>; + } = null; if (pass.opts.gating != null) { - const error = checkFunctionReferencedBeforeDeclarationAtTopLevel( - program, - compiledFns.map(result => { - return result.originalFn; - }), - ); - if (error) { - handleError(error, pass, null); - return; - } + gating = { + gatingFn: pass.opts.gating, + referencedBeforeDeclared: + getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns), + }; } const hasLoweredContextAccess = compiledFns.some( c => c.compiledFn.hasLoweredContextAccess, ); const externalFunctions: Array<ExternalFunction> = []; - let gating: null | ExternalFunction = null; try { // TODO: check for duplicate import specifiers - if (pass.opts.gating != null) { - gating = tryParseExternalFunction(pass.opts.gating); - externalFunctions.push(gating); + if (gating != null) { + externalFunctions.push(gating.gatingFn); } const lowerContextAccess = environment.lowerContextAccess; @@ -613,7 +608,12 @@ export function compileProgram( const transformedFn = createNewFunctionNode(originalFn, compiledFn); if (gating != null && kind === 'original') { - insertGatedFunctionDeclaration(originalFn, transformedFn, gating); + insertGatedFunctionDeclaration( + originalFn, + transformedFn, + gating.gatingFn, + gating.referencedBeforeDeclared.has(result), + ); } else { originalFn.replaceWith(transformedFn); } @@ -1093,20 +1093,23 @@ function getFunctionName( } } -function checkFunctionReferencedBeforeDeclarationAtTopLevel( +function getFunctionReferencedBeforeDeclarationAtTopLevel( program: NodePath<t.Program>, - fns: Array<BabelFn>, -): CompilerError | null { - const fnIds = new Set( + fns: Array<CompileResult>, +): Set<CompileResult> { + const fnNames = new Map<string, {id: t.Identifier; fn: CompileResult}>( fns - .map(fn => getFunctionName(fn)) + .map<[NodePath<t.Expression> | null, CompileResult]>(fn => [ + getFunctionName(fn.originalFn), + fn, + ]) .filter( - (name): name is NodePath<t.Identifier> => !!name && name.isIdentifier(), + (entry): entry is [NodePath<t.Identifier>, CompileResult] => + !!entry[0] && entry[0].isIdentifier(), ) - .map(name => name.node), + .map(entry => [entry[0].node.name, {id: entry[0].node, fn: entry[1]}]), ); - const fnNames = new Map([...fnIds].map(id => [id.name, id])); - const errors = new CompilerError(); + const referencedBeforeDeclaration = new Set<CompileResult>(); program.traverse({ TypeAnnotation(path) { @@ -1132,8 +1135,7 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel( * We've reached the declaration, hoisting is no longer possible, stop * checking for this component name. */ - if (fnIds.has(id.node)) { - fnIds.delete(id.node); + if (id.node === fn.id) { fnNames.delete(id.node.name); return; } @@ -1144,20 +1146,12 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel( * top level scope. */ if (scope === null && id.isReferencedIdentifier()) { - errors.pushErrorDetail( - new CompilerErrorDetail({ - reason: `Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting`, - description: `Rewrite the reference to ${fn.name} to not rely on hoisting to fix this issue`, - loc: fn.loc ?? null, - suggestions: null, - severity: ErrorSeverity.Invariant, - }), - ); + referencedBeforeDeclaration.add(fn.fn); } }, }); - return errors.details.length > 0 ? errors : null; + return referencedBeforeDeclaration; } function getReactCompilerRuntimeModule(opts: PluginOptions): string { From d92e5713be2dc78f467c31fce4a1e5c84a74e4e6 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 19:31:49 -0400 Subject: [PATCH 126/300] [compiler] Avoid bailouts when inserting gating (#32598) This change fixes a coverage hole in rolling out with `gating`. Prior to this PR, configuring `gating` causes React Compiler to bail out of optimizing some functions. This means that it's not entirely safe to cutover from `gating` enabled for all users (i.e. rolled out 100%) to removing the `gating` config altogether, as new functions may be opted into compilation when they stop bailing out due to gating-specific logic. This is technically slightly slower due to the additional function indirection. An alternative approach is to recommend running a codemod to insert `use no memo`s on currently-bailing out functions before removing the`gating` config. --- Tested [internally]( https://fburl.com/diff/q982ovua) by enabling on a page that previously had a few hundred bailouts due to gating + hoisted function declarations and (1) clicking around locally and (2) running a bunch of e2e tests --- .../src/Entrypoint/Gating.ts | 125 ++++++++++++++++-- ...component-syntax-ref-gating.flow.expect.md | 62 +++++++++ .../component-syntax-ref-gating.flow.js | 12 ++ ...component-syntax-ref-gating.flow.expect.md | 24 ---- .../error.component-syntax-ref-gating.flow.js | 4 - .../gating/error.gating-hoisting.expect.md | 26 ---- .../compiler/gating/error.gating-hoisting.js | 5 - .../error.gating-use-before-decl.expect.md | 24 ---- .../gating/error.gating-use-before-decl.js | 5 - .../gating-use-before-decl-ref.expect.md | 61 +++++++++ .../gating/gating-use-before-decl-ref.js | 13 ++ .../gating/gating-use-before-decl.expect.md | 64 +++++++++ .../compiler/gating/gating-use-before-decl.js | 14 ++ .../gating/invalid-fnexpr-reference.expect.md | 59 +++++++++ .../gating/invalid-fnexpr-reference.js | 15 +++ .../reassigned-fnexpr-variable.expect.md | 86 ++++++++++++ .../gating/reassigned-fnexpr-variable.js | 23 ++++ 17 files changed, 526 insertions(+), 96 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts index 263f76395f0fa..16ef7986ef16a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts @@ -10,6 +10,117 @@ import * as t from '@babel/types'; import {PluginOptions} from './Options'; import {CompilerError} from '../CompilerError'; +/** + * Gating rewrite for function declarations which are referenced before their + * declaration site. + * + * ```js + * // original + * export default React.memo(Foo); + * function Foo() { ... } + * + * // React compiler optimized + gated + * import {gating} from 'myGating'; + * export default React.memo(Foo); + * const gating_result = gating(); <- inserted + * function Foo_optimized() {} <- inserted + * function Foo_unoptimized() {} <- renamed from Foo + * function Foo() { <- inserted function, which can be hoisted by JS engines + * if (gating_result) return Foo_optimized(); + * else return Foo_unoptimized(); + * } + * ``` + */ +function insertAdditionalFunctionDeclaration( + fnPath: NodePath<t.FunctionDeclaration>, + compiled: t.FunctionDeclaration, + gating: NonNullable<PluginOptions['gating']>, +): void { + const originalFnName = fnPath.node.id; + const originalFnParams = fnPath.node.params; + const compiledParams = fnPath.node.params; + /** + * Note that other than `export default function() {}`, all other function + * declarations must have a binding identifier. Since default exports cannot + * be referenced, it's safe to assume that all function declarations passed + * here will have an identifier. + * https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#sec-function-definitions + */ + CompilerError.invariant(originalFnName != null && compiled.id != null, { + reason: + 'Expected function declarations that are referenced elsewhere to have a named identifier', + loc: fnPath.node.loc ?? null, + }); + CompilerError.invariant(originalFnParams.length === compiledParams.length, { + reason: + 'Expected React Compiler optimized function declarations to have the same number of parameters as source', + loc: fnPath.node.loc ?? null, + }); + + const gatingCondition = fnPath.scope.generateUidIdentifier( + `${gating.importSpecifierName}_result`, + ); + const unoptimizedFnName = fnPath.scope.generateUidIdentifier( + `${originalFnName.name}_unoptimized`, + ); + const optimizedFnName = fnPath.scope.generateUidIdentifier( + `${originalFnName.name}_optimized`, + ); + /** + * Step 1: rename existing functions + */ + compiled.id.name = optimizedFnName.name; + fnPath.get('id').replaceInline(unoptimizedFnName); + + /** + * Step 2: insert new function declaration + */ + const newParams: Array<t.Identifier | t.RestElement> = []; + const genNewArgs: Array<() => t.Identifier | t.SpreadElement> = []; + for (let i = 0; i < originalFnParams.length; i++) { + const argName = `arg${i}`; + if (originalFnParams[i].type === 'RestElement') { + newParams.push(t.restElement(t.identifier(argName))); + genNewArgs.push(() => t.spreadElement(t.identifier(argName))); + } else { + newParams.push(t.identifier(argName)); + genNewArgs.push(() => t.identifier(argName)); + } + } + // insertAfter called in reverse order of how nodes should appear in program + fnPath.insertAfter( + t.functionDeclaration( + originalFnName, + newParams, + t.blockStatement([ + t.ifStatement( + gatingCondition, + t.returnStatement( + t.callExpression( + compiled.id, + genNewArgs.map(fn => fn()), + ), + ), + t.returnStatement( + t.callExpression( + unoptimizedFnName, + genNewArgs.map(fn => fn()), + ), + ), + ), + ]), + ), + ); + fnPath.insertBefore( + t.variableDeclaration('const', [ + t.variableDeclarator( + gatingCondition, + t.callExpression(t.identifier(gating.importSpecifierName), []), + ), + ]), + ); + fnPath.insertBefore(compiled); +} export function insertGatedFunctionDeclaration( fnPath: NodePath< t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression @@ -21,15 +132,13 @@ export function insertGatedFunctionDeclaration( gating: NonNullable<PluginOptions['gating']>, referencedBeforeDeclaration: boolean, ): void { - if (referencedBeforeDeclaration) { - const identifier = - fnPath.node.type === 'FunctionDeclaration' ? fnPath.node.id : null; - CompilerError.invariant(false, { - reason: `Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting`, - description: `Rewrite the reference to ${identifier?.name ?? 'this function'} to not rely on hoisting to fix this issue`, - loc: identifier?.loc ?? null, - suggestions: null, + if (referencedBeforeDeclaration && fnPath.isFunctionDeclaration()) { + CompilerError.invariant(compiled.type === 'FunctionDeclaration', { + reason: 'Expected compiled node type to match input type', + description: `Got ${compiled.type} but expected FunctionDeclaration`, + loc: fnPath.node.loc ?? null, }); + insertAdditionalFunctionDeclaration(fnPath, compiled, gating); } else { const gatingExpression = t.conditionalExpression( t.callExpression(t.identifier(gating.importSpecifierName), []), diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md new file mode 100644 index 0000000000000..25215580c81f0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +// @flow @gating +import {Stringify} from 'shared-runtime'; +import * as React from 'react'; + +component Foo(ref: React.RefSetter<Controls>) { + return <Stringify ref={ref} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{ref: React.createRef()}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; +import { Stringify } from "shared-runtime"; +import * as React from "react"; + +const Foo = React.forwardRef(Foo_withRef); +const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { + const $ = _c(2); + let t0; + if ($[0] !== ref) { + t0 = <Stringify ref={ref} />; + $[0] = ref; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +function _Foo_withRef_unoptimized( + _$$empty_props_placeholder$$: $ReadOnly<{}>, + ref: React.RefSetter<Controls>, +): React.Node { + return <Stringify ref={ref} />; +} +function Foo_withRef(arg0, arg1) { + if (_isForgetEnabled_Fixtures_result) + return _Foo_withRef_optimized(arg0, arg1); + else return _Foo_withRef_unoptimized(arg0, arg1); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval("(...args) => React.createElement(Foo, args)"), + params: [{ ref: React.createRef() }], +}; + +``` + +### Eval output +(kind: ok) <div>{"ref":null}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js new file mode 100644 index 0000000000000..02bed83cb21e7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js @@ -0,0 +1,12 @@ +// @flow @gating +import {Stringify} from 'shared-runtime'; +import * as React from 'react'; + +component Foo(ref: React.RefSetter<Controls>) { + return <Stringify ref={ref} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{ref: React.createRef()}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md deleted file mode 100644 index 3f1576b022887..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md +++ /dev/null @@ -1,24 +0,0 @@ - -## Input - -```javascript -// @flow @gating -component Foo(ref: React.RefSetter<Controls>) { - return <Bar ref={ref} />; -} - -``` - - -## Error - -``` - 1 | // @flow @gating -> 2 | component Foo(ref: React.RefSetter<Controls>) { - | ^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo_withRef to not rely on hoisting to fix this issue (2:2) - 3 | return <Bar ref={ref} />; - 4 | } - 5 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js deleted file mode 100644 index 7cb271dd06c5e..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js +++ /dev/null @@ -1,4 +0,0 @@ -// @flow @gating -component Foo(ref: React.RefSetter<Controls>) { - return <Bar ref={ref} />; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md deleted file mode 100644 index 00084b56f308f..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md +++ /dev/null @@ -1,26 +0,0 @@ - -## Input - -```javascript -// @gating -const Foo = React.forwardRef(Foo_withRef); -function Foo_withRef(props, ref) { - return <Bar ref={ref} {...props}></Bar>; -} - -``` - - -## Error - -``` - 1 | // @gating - 2 | const Foo = React.forwardRef(Foo_withRef); -> 3 | function Foo_withRef(props, ref) { - | ^^^^^^^^^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo_withRef to not rely on hoisting to fix this issue (3:3) - 4 | return <Bar ref={ref} {...props}></Bar>; - 5 | } - 6 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js deleted file mode 100644 index f0a2879c0982b..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js +++ /dev/null @@ -1,5 +0,0 @@ -// @gating -const Foo = React.forwardRef(Foo_withRef); -function Foo_withRef(props, ref) { - return <Bar ref={ref} {...props}></Bar>; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md deleted file mode 100644 index a1b5a61c210dd..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md +++ /dev/null @@ -1,24 +0,0 @@ - -## Input - -```javascript -// @gating -import {memo} from 'react'; - -export default memo(Foo); -function Foo() {} - -``` - - -## Error - -``` - 3 | - 4 | export default memo(Foo); -> 5 | function Foo() {} - | ^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo to not rely on hoisting to fix this issue (5:5) - 6 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js deleted file mode 100644 index 7f778176e28ba..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js +++ /dev/null @@ -1,5 +0,0 @@ -// @gating -import {memo} from 'react'; - -export default memo(Foo); -function Foo() {} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md new file mode 100644 index 0000000000000..d09c98dffd917 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md @@ -0,0 +1,61 @@ + +## Input + +```javascript +// @gating +import {createRef, forwardRef} from 'react'; +import {Stringify} from 'shared-runtime'; + +const Foo = forwardRef(Foo_withRef); +function Foo_withRef(props, ref) { + return <Stringify ref={ref} {...props} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{prop1: 1, prop2: 2, ref: createRef()}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { createRef, forwardRef } from "react"; +import { Stringify } from "shared-runtime"; + +const Foo = forwardRef(Foo_withRef); +const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function _Foo_withRef_optimized(props, ref) { + const $ = _c(3); + let t0; + if ($[0] !== props || $[1] !== ref) { + t0 = <Stringify ref={ref} {...props} />; + $[0] = props; + $[1] = ref; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} +function _Foo_withRef_unoptimized(props, ref) { + return <Stringify ref={ref} {...props} />; +} +function Foo_withRef(arg0, arg1) { + if (_isForgetEnabled_Fixtures_result) + return _Foo_withRef_optimized(arg0, arg1); + else return _Foo_withRef_unoptimized(arg0, arg1); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval("(...args) => React.createElement(Foo, args)"), + params: [{ prop1: 1, prop2: 2, ref: createRef() }], +}; + +``` + +### Eval output +(kind: ok) <div>{"0":{"prop1":1,"prop2":2,"ref":{"current":null}},"ref":"[[ cyclic ref *3 ]]"}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js new file mode 100644 index 0000000000000..a382497d99227 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js @@ -0,0 +1,13 @@ +// @gating +import {createRef, forwardRef} from 'react'; +import {Stringify} from 'shared-runtime'; + +const Foo = forwardRef(Foo_withRef); +function Foo_withRef(props, ref) { + return <Stringify ref={ref} {...props} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{prop1: 1, prop2: 2, ref: createRef()}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md new file mode 100644 index 0000000000000..0bbfc96756778 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md @@ -0,0 +1,64 @@ + +## Input + +```javascript +// @gating +import {memo} from 'react'; +import {Stringify} from 'shared-runtime'; + +export default memo(Foo); +function Foo({prop1, prop2}) { + 'use memo'; + return <Stringify prop1={prop1} prop2={prop2} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{prop1: 1, prop2: 2}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { memo } from "react"; +import { Stringify } from "shared-runtime"; + +export default memo(Foo); +const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function _Foo_optimized(t0) { + "use memo"; + const $ = _c(3); + const { prop1, prop2 } = t0; + let t1; + if ($[0] !== prop1 || $[1] !== prop2) { + t1 = <Stringify prop1={prop1} prop2={prop2} />; + $[0] = prop1; + $[1] = prop2; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} +function _Foo_unoptimized({ prop1, prop2 }) { + "use memo"; + return <Stringify prop1={prop1} prop2={prop2} />; +} +function Foo(arg0) { + if (_isForgetEnabled_Fixtures_result) return _Foo_optimized(arg0); + else return _Foo_unoptimized(arg0); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval("Foo"), + params: [{ prop1: 1, prop2: 2 }], +}; + +``` + +### Eval output +(kind: ok) <div>{"prop1":1,"prop2":2}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js new file mode 100644 index 0000000000000..d51a7fcac0a4d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js @@ -0,0 +1,14 @@ +// @gating +import {memo} from 'react'; +import {Stringify} from 'shared-runtime'; + +export default memo(Foo); +function Foo({prop1, prop2}) { + 'use memo'; + return <Stringify prop1={prop1} prop2={prop2} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{prop1: 1, prop2: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md new file mode 100644 index 0000000000000..47b58453ca035 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +// @gating +import * as React from 'react'; + +let Foo; +const MemoFoo = React.memo(Foo); +Foo = () => <div>hello world!</div>; + +/** + * Evaluate this fixture module to assert that compiler + original have the same + * runtime error message. + */ +export const FIXTURE_ENTRYPOINT = { + fn: () => {}, + params: [], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import * as React from "react"; + +let Foo; +const MemoFoo = React.memo(Foo); +Foo = isForgetEnabled_Fixtures() + ? () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <div>hello world!</div>; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : () => <div>hello world!</div>; + +/** + * Evaluate this fixture module to assert that compiler + original have the same + * runtime error message. + */ +export const FIXTURE_ENTRYPOINT = { + fn: isForgetEnabled_Fixtures() ? () => {} : () => {}, + params: [], +}; + +``` + +### Eval output +(kind: ok) +logs: ['memo: The first argument must be a component. Instead received: %s','undefined'] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js new file mode 100644 index 0000000000000..2bdeb76f5898b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js @@ -0,0 +1,15 @@ +// @gating +import * as React from 'react'; + +let Foo; +const MemoFoo = React.memo(Foo); +Foo = () => <div>hello world!</div>; + +/** + * Evaluate this fixture module to assert that compiler + original have the same + * runtime error message. + */ +export const FIXTURE_ENTRYPOINT = { + fn: () => {}, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md new file mode 100644 index 0000000000000..5f18d98491ff7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md @@ -0,0 +1,86 @@ + +## Input + +```javascript +// @gating +import * as React from 'react'; + +/** + * Test that the correct `Foo` is printed + */ +let Foo = () => <div>hello world 1!</div>; +const MemoOne = React.memo(Foo); +Foo = () => <div>hello world 2!</div>; +const MemoTwo = React.memo(Foo); + +export const FIXTURE_ENTRYPOINT = { + fn: () => { + 'use no memo'; + return ( + <> + <MemoOne /> + <MemoTwo /> + </> + ); + }, + params: [], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import * as React from "react"; + +/** + * Test that the correct `Foo` is printed + */ +let Foo = isForgetEnabled_Fixtures() + ? () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <div>hello world 1!</div>; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : () => <div>hello world 1!</div>; +const MemoOne = React.memo(Foo); +Foo = isForgetEnabled_Fixtures() + ? () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <div>hello world 2!</div>; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : () => <div>hello world 2!</div>; +const MemoTwo = React.memo(Foo); + +export const FIXTURE_ENTRYPOINT = { + fn: () => { + "use no memo"; + return ( + <> + <MemoOne /> + <MemoTwo /> + </> + ); + }, + params: [], +}; + +``` + +### Eval output +(kind: ok) <div>hello world 1!</div><div>hello world 2!</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js new file mode 100644 index 0000000000000..f2275da7a2c01 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js @@ -0,0 +1,23 @@ +// @gating +import * as React from 'react'; + +/** + * Test that the correct `Foo` is printed + */ +let Foo = () => <div>hello world 1!</div>; +const MemoOne = React.memo(Foo); +Foo = () => <div>hello world 2!</div>; +const MemoTwo = React.memo(Foo); + +export const FIXTURE_ENTRYPOINT = { + fn: () => { + 'use no memo'; + return ( + <> + <MemoOne /> + <MemoTwo /> + </> + ); + }, + params: [], +}; From 7939d92fcc95ad5ee719c38272eaef14a3750fc0 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 13 Mar 2025 19:54:54 -0400 Subject: [PATCH 127/300] [compiler] clean up retry pipeline: `fireRetry` flag -> compileMode (#32511) Removes `EnvironmentConfig.enableMinimalTransformsForRetry` in favor of `run` parameters. This is a minimal difference but lets us explicitly opt out certain compiler passes based on mode parameters, instead of environment configurations Retry flags don't really make sense to have in `EnvironmentConfig` anyways as the config is user-facing API, while retrying is a compiler implementation detail. (per @josephsavona's feedback https://github.com/facebook/react/pull/32164#issuecomment-2608616479) > Re the "hacky" framing of this in the PR title: I think this is fine. I can see having something like a compilation or output mode that we use when running the pipeline. Rather than changing environment settings when we re-run, various passes could take effect based on the combination of the mode + env flags. The modes might be: > > * Full: transform, validate, memoize. This is the default today. > * Transform: Along the lines of the backup mode in this PR. Only applies transforms that do not require following the rules of React, like `fire()`. > * Validate: This could be used for ESLint. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32511). * #32512 * __->__ #32511 --- .../scripts/jest/makeTransform.ts | 1 + .../src/Entrypoint/Pipeline.ts | 73 ++++++++++++------- .../src/Entrypoint/Program.ts | 40 +++++----- .../src/HIR/Environment.ts | 22 +++--- .../src/Inference/InferFunctionEffects.ts | 19 +++-- .../src/Inference/InferReferenceEffects.ts | 11 +-- .../bailout-capitalized-fn-call.expect.md | 4 +- .../bailout-capitalized-fn-call.js | 2 +- .../bailout-eslint-suppressions.expect.md | 4 +- .../bailout-eslint-suppressions.js | 2 +- .../bailout-validate-preserve-memo.expect.md | 4 +- .../bailout-validate-preserve-memo.js | 2 +- .../bailout-validate-prop-write.expect.md | 4 +- .../bailout-validate-prop-write.js | 2 +- ...lout-validate-ref-current-access.expect.md | 2 +- .../bailout-validate-ref-current-access.js | 2 +- ...ailout-validate-conditional-hook.expect.md | 4 +- .../bailout-validate-conditional-hook.js | 2 +- 18 files changed, 111 insertions(+), 89 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts b/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts index 3ebd28f849ac5..99291c2984321 100644 --- a/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts +++ b/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts @@ -181,6 +181,7 @@ function ReactForgetFunctionTransform() { fn, forgetOptions, 'Other', + 'all_features', '_c', null, null, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 0953289c1950e..994aa8f18c114 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -24,6 +24,7 @@ import { pruneUnusedLabelsHIR, } from '../HIR'; import { + CompilerMode, Environment, EnvironmentConfig, ReactFunctionType, @@ -100,6 +101,7 @@ import {outlineJSX} from '../Optimization/OutlineJsx'; import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls'; import {transformFire} from '../Transform'; import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender'; +import {CompilerError} from '..'; export type CompilerPipelineValue = | {kind: 'ast'; name: string; value: CodegenFunction} @@ -113,6 +115,7 @@ function run( >, config: EnvironmentConfig, fnType: ReactFunctionType, + mode: CompilerMode, useMemoCacheIdentifier: string, logger: Logger | null, filename: string | null, @@ -122,6 +125,7 @@ function run( const env = new Environment( func.scope, fnType, + mode, config, contextIdentifiers, logger, @@ -160,10 +164,10 @@ function runWithEnvironment( validateUseMemo(hir); if ( + env.isInferredMemoEnabled && !env.config.enablePreserveExistingManualUseMemo && !env.config.disableMemoizationForDebugging && - !env.config.enableChangeDetectionForDebugging && - !env.config.enableMinimalTransformsForRetry + !env.config.enableChangeDetectionForDebugging ) { dropManualMemoization(hir); log({kind: 'hir', name: 'DropManualMemoization', value: hir}); @@ -196,8 +200,13 @@ function runWithEnvironment( inferTypes(hir); log({kind: 'hir', name: 'InferTypes', value: hir}); - if (env.config.validateHooksUsage) { - validateHooksUsage(hir); + if (env.isInferredMemoEnabled) { + if (env.config.validateHooksUsage) { + validateHooksUsage(hir); + } + if (env.config.validateNoCapitalizedCalls) { + validateNoCapitalizedCalls(hir); + } } if (env.config.enableFire) { @@ -205,10 +214,6 @@ function runWithEnvironment( log({kind: 'hir', name: 'TransformFire', value: hir}); } - if (env.config.validateNoCapitalizedCalls) { - validateNoCapitalizedCalls(hir); - } - if (env.config.lowerContextAccess) { lowerContextAccess(hir, env.config.lowerContextAccess); } @@ -219,7 +224,12 @@ function runWithEnvironment( analyseFunctions(hir); log({kind: 'hir', name: 'AnalyseFunctions', value: hir}); - inferReferenceEffects(hir); + const fnEffectErrors = inferReferenceEffects(hir); + if (env.isInferredMemoEnabled) { + if (fnEffectErrors.length > 0) { + CompilerError.throw(fnEffectErrors[0]); + } + } log({kind: 'hir', name: 'InferReferenceEffects', value: hir}); validateLocalsNotReassignedAfterRender(hir); @@ -239,28 +249,30 @@ function runWithEnvironment( inferMutableRanges(hir); log({kind: 'hir', name: 'InferMutableRanges', value: hir}); - if (env.config.assertValidMutableRanges) { - assertValidMutableRanges(hir); - } + if (env.isInferredMemoEnabled) { + if (env.config.assertValidMutableRanges) { + assertValidMutableRanges(hir); + } - if (env.config.validateRefAccessDuringRender) { - validateNoRefAccessInRender(hir); - } + if (env.config.validateRefAccessDuringRender) { + validateNoRefAccessInRender(hir); + } - if (env.config.validateNoSetStateInRender) { - validateNoSetStateInRender(hir); - } + if (env.config.validateNoSetStateInRender) { + validateNoSetStateInRender(hir); + } - if (env.config.validateNoSetStateInPassiveEffects) { - validateNoSetStateInPassiveEffects(hir); - } + if (env.config.validateNoSetStateInPassiveEffects) { + validateNoSetStateInPassiveEffects(hir); + } - if (env.config.validateNoJSXInTryStatements) { - validateNoJSXInTryStatement(hir); - } + if (env.config.validateNoJSXInTryStatements) { + validateNoJSXInTryStatement(hir); + } - if (env.config.validateNoImpureFunctionsInRender) { - validateNoImpureFunctionsInRender(hir); + if (env.config.validateNoImpureFunctionsInRender) { + validateNoImpureFunctionsInRender(hir); + } } inferReactivePlaces(hir); @@ -280,7 +292,12 @@ function runWithEnvironment( value: hir, }); - if (!env.config.enableMinimalTransformsForRetry) { + if (env.isInferredMemoEnabled) { + /** + * Only create reactive scopes (which directly map to generated memo blocks) + * if inferred memoization is enabled. This makes all later passes which + * transform reactive-scope labeled instructions no-ops. + */ inferReactiveScopeVariables(hir); log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir}); } @@ -529,6 +546,7 @@ export function compileFn( >, config: EnvironmentConfig, fnType: ReactFunctionType, + mode: CompilerMode, useMemoCacheIdentifier: string, logger: Logger | null, filename: string | null, @@ -538,6 +556,7 @@ export function compileFn( func, config, fnType, + mode, useMemoCacheIdentifier, logger, filename, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 34c1af955b3cc..0865b50f84116 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -16,7 +16,6 @@ import { EnvironmentConfig, ExternalFunction, ReactFunctionType, - MINIMAL_RETRY_CONFIG, } from '../HIR/Environment'; import {CodegenFunction} from '../ReactiveScopes'; import {isComponentDeclaration} from '../Utils/ComponentDeclaration'; @@ -407,6 +406,7 @@ export function compileProgram( fn, environment, fnType, + 'all_features', useMemoCacheIdentifier.name, pass.opts.logger, pass.filename, @@ -417,18 +417,29 @@ export function compileProgram( compileResult = {kind: 'error', error: err}; } } - // If non-memoization features are enabled, retry regardless of error kind - if (compileResult.kind === 'error' && environment.enableFire) { + + if (compileResult.kind === 'error') { + /** + * If an opt out directive is present, log only instead of throwing and don't mark as + * containing a critical error. + */ + if (optOutDirectives.length > 0) { + logError(compileResult.error, pass, fn.node.loc ?? null); + } else { + handleError(compileResult.error, pass, fn.node.loc ?? null); + } + // If non-memoization features are enabled, retry regardless of error kind + if (!environment.enableFire) { + return null; + } try { compileResult = { kind: 'compile', compiledFn: compileFn( fn, - { - ...environment, - ...MINIMAL_RETRY_CONFIG, - }, + environment, fnType, + 'no_inferred_memo', useMemoCacheIdentifier.name, pass.opts.logger, pass.filename, @@ -436,20 +447,9 @@ export function compileProgram( ), }; } catch (err) { - compileResult = {kind: 'error', error: err}; - } - } - if (compileResult.kind === 'error') { - /** - * If an opt out directive is present, log only instead of throwing and don't mark as - * containing a critical error. - */ - if (optOutDirectives.length > 0) { - logError(compileResult.error, pass, fn.node.loc ?? null); - } else { - handleError(compileResult.error, pass, fn.node.loc ?? null); + // TODO: we might want to log error here, but this will also result in duplicate logging + return null; } - return null; } pass.opts.logger?.logEvent(pass.filename, { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 785240653e0d3..4fce273b7a2de 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -96,6 +96,8 @@ export const MacroSchema = z.union([ z.tuple([z.string(), z.array(MacroMethodSchema)]), ]); +export type CompilerMode = 'all_features' | 'no_inferred_memo'; + export type Macro = z.infer<typeof MacroSchema>; export type MacroMethod = z.infer<typeof MacroMethodSchema>; @@ -550,8 +552,6 @@ const EnvironmentConfigSchema = z.object({ */ disableMemoizationForDebugging: z.boolean().default(false), - enableMinimalTransformsForRetry: z.boolean().default(false), - /** * When true, rather using memoized values, the compiler will always re-compute * values, and then use a heuristic to compare the memoized value to the newly @@ -626,17 +626,6 @@ const EnvironmentConfigSchema = z.object({ export type EnvironmentConfig = z.infer<typeof EnvironmentConfigSchema>; -export const MINIMAL_RETRY_CONFIG: PartialEnvironmentConfig = { - validateHooksUsage: false, - validateRefAccessDuringRender: false, - validateNoSetStateInRender: false, - validateNoSetStateInPassiveEffects: false, - validateNoJSXInTryStatements: false, - validateMemoizedEffectDependencies: false, - validateNoCapitalizedCalls: null, - validateBlocklistedImports: null, - enableMinimalTransformsForRetry: true, -}; /** * For test fixtures and playground only. * @@ -851,6 +840,7 @@ export class Environment { code: string | null; config: EnvironmentConfig; fnType: ReactFunctionType; + compilerMode: CompilerMode; useMemoCacheIdentifier: string; hasLoweredContextAccess: boolean; hasFireRewrite: boolean; @@ -861,6 +851,7 @@ export class Environment { constructor( scope: BabelScope, fnType: ReactFunctionType, + compilerMode: CompilerMode, config: EnvironmentConfig, contextIdentifiers: Set<t.Identifier>, logger: Logger | null, @@ -870,6 +861,7 @@ export class Environment { ) { this.#scope = scope; this.fnType = fnType; + this.compilerMode = compilerMode; this.config = config; this.filename = filename; this.code = code; @@ -924,6 +916,10 @@ export class Environment { this.#hoistedIdentifiers = new Set(); } + get isInferredMemoEnabled(): boolean { + return this.compilerMode !== 'no_inferred_memo'; + } + get nextIdentifierId(): IdentifierId { return makeIdentifierId(this.#nextIdentifer++); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts index fe209924e4e08..a58ae440219b9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts @@ -5,7 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, ErrorSeverity, ValueKind} from '..'; +import { + CompilerError, + CompilerErrorDetailOptions, + ErrorSeverity, + ValueKind, +} from '..'; import { AbstractValue, BasicBlock, @@ -290,21 +295,21 @@ export function inferTerminalFunctionEffects( return functionEffects; } -export function raiseFunctionEffectErrors( +export function transformFunctionEffectErrors( functionEffects: Array<FunctionEffect>, -): void { - functionEffects.forEach(eff => { +): Array<CompilerErrorDetailOptions> { + return functionEffects.map(eff => { switch (eff.kind) { case 'ReactMutation': case 'GlobalMutation': { - CompilerError.throw(eff.error); + return eff.error; } case 'ContextMutation': { - CompilerError.throw({ + return { severity: ErrorSeverity.Invariant, reason: `Unexpected ContextMutation in top-level function effects`, loc: eff.loc, - }); + }; } default: assertExhaustive( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index bfa0825408355..ae71da64b4191 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError} from '../CompilerError'; +import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError'; import {Environment} from '../HIR'; import { AbstractValue, @@ -49,7 +49,7 @@ import {assertExhaustive} from '../Utils/utils'; import { inferTerminalFunctionEffects, inferInstructionFunctionEffects, - raiseFunctionEffectErrors, + transformFunctionEffectErrors, } from './InferFunctionEffects'; const UndefinedValue: InstructionValue = { @@ -103,7 +103,7 @@ const UndefinedValue: InstructionValue = { export default function inferReferenceEffects( fn: HIRFunction, options: {isFunctionExpression: boolean} = {isFunctionExpression: false}, -): void { +): Array<CompilerErrorDetailOptions> { /* * Initial state contains function params * TODO: include module declarations here as well @@ -241,8 +241,9 @@ export default function inferReferenceEffects( if (options.isFunctionExpression) { fn.effects = functionEffects; - } else if (!fn.env.config.enableMinimalTransformsForRetry) { - raiseFunctionEffectErrors(functionEffects); + return []; + } else { + return transformFunctionEffectErrors(functionEffects); } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.expect.md index f7f413dedf906..77bdd312751e3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoCapitalizedCalls @enableFire +// @validateNoCapitalizedCalls @enableFire @panicThreshold(none) import {fire} from 'react'; const CapitalizedCall = require('shared-runtime').sum; @@ -24,7 +24,7 @@ function Component({prop1, bar}) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; // @validateNoCapitalizedCalls @enableFire +import { useFire } from "react/compiler-runtime"; // @validateNoCapitalizedCalls @enableFire @panicThreshold(none) import { fire } from "react"; const CapitalizedCall = require("shared-runtime").sum; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.js index b872fd8670e8e..679945eaab54f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-capitalized-fn-call.js @@ -1,4 +1,4 @@ -// @validateNoCapitalizedCalls @enableFire +// @validateNoCapitalizedCalls @enableFire @panicThreshold(none) import {fire} from 'react'; const CapitalizedCall = require('shared-runtime').sum; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.expect.md index ad7f0ab467b00..30bd6d42e50a9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @enableFire +// @enableFire @panicThreshold(none) import {useRef} from 'react'; function Component({props, bar}) { @@ -26,7 +26,7 @@ function Component({props, bar}) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; // @enableFire +import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) import { useRef } from "react"; function Component(t0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.js index 7145fef03b1d4..5312e5707cc1f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-eslint-suppressions.js @@ -1,4 +1,4 @@ -// @enableFire +// @enableFire @panicThreshold(none) import {useRef} from 'react'; function Component({props, bar}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.expect.md index 9f8db6265f064..6477a011264bd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validatePreserveExistingMemoizationGuarantees @enableFire +// @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold(none) import {fire} from 'react'; import {sum} from 'shared-runtime'; @@ -24,7 +24,7 @@ function Component({prop1, bar}) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableFire +import { useFire } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold(none) import { fire } from "react"; import { sum } from "shared-runtime"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.js index b7c01e40ac2a5..c3bb8b4216438 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-preserve-memo.js @@ -1,4 +1,4 @@ -// @validatePreserveExistingMemoizationGuarantees @enableFire +// @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold(none) import {fire} from 'react'; import {sum} from 'shared-runtime'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.expect.md index 74d9f679c4ffd..e6ce051f10aad 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @enableFire +// @enableFire @panicThreshold(none) import {fire} from 'react'; function Component({prop1}) { @@ -20,7 +20,7 @@ function Component({prop1}) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; // @enableFire +import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) import { fire } from "react"; function Component(t0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.js index 4fac46f78e81c..b0cfd4fe39fc8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-prop-write.js @@ -1,4 +1,4 @@ -// @enableFire +// @enableFire @panicThreshold(none) import {fire} from 'react'; function Component({prop1}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.expect.md index af0fc7b5a2f26..79f5a2986d119 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @flow @enableFire +// @flow @enableFire @panicThreshold(none) import {fire} from 'react'; import {print} from 'shared-runtime'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.js index 96ccf0b161e93..f649fe5902362 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/bailout-validate-ref-current-access.js @@ -1,4 +1,4 @@ -// @flow @enableFire +// @flow @enableFire @panicThreshold(none) import {fire} from 'react'; import {print} from 'shared-runtime'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.expect.md index c20f89d3af8d2..dde2b692f40f7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @enableFire +// @enableFire @panicThreshold(none) import {fire, useEffect} from 'react'; import {Stringify} from 'shared-runtime'; @@ -29,7 +29,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; // @enableFire +import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) import { fire, useEffect } from "react"; import { Stringify } from "shared-runtime"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.js index 0a29fd852c030..fa8c034bfb289 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-validate-conditional-hook.js @@ -1,4 +1,4 @@ -// @enableFire +// @enableFire @panicThreshold(none) import {fire, useEffect} from 'react'; import {Stringify} from 'shared-runtime'; From 67338703aa52d662998733e58671dc9fe1edae47 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 20:31:04 -0400 Subject: [PATCH 128/300] [ci] Update yarn and node_modules cache key (#32603) Now that the compiler lint rule is merged into eslint-plugin-react-hooks, we also need to update our caches so compiler dependencies are also cached. This should fix the CI walltime regression we are now seeing. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32603). * #32604 * __->__ #32603 --- .github/workflows/runtime_build_and_test.yml | 80 ++++++++++++------- .../workflows/runtime_eslint_plugin_e2e.yml | 8 +- .github/workflows/runtime_prereleases.yml | 4 +- .../runtime_releases_from_npm_manual.yml | 4 +- .github/workflows/shared_lint.yml | 8 +- package.json | 2 +- scripts/react-compiler/build-compiler.sh | 1 - 7 files changed, 66 insertions(+), 41 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index d53e352fbb266..272411b302e63 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -50,13 +50,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -74,16 +74,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - run: | yarn generate-inline-fizz-runtime git diff --quiet || (echo "There was a change to the Fizz runtime. Run `yarn generate-inline-fizz-runtime` and check in the result." && false) @@ -100,16 +102,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - run: yarn flags # ----- TESTS ----- @@ -153,16 +157,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }} # ----- BUILD ----- @@ -183,7 +189,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - uses: actions/setup-java@v4 with: distribution: temurin @@ -193,10 +199,12 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci env: CI: github @@ -261,16 +269,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -293,16 +303,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -340,16 +352,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -375,16 +389,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -407,13 +423,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: 'fixtures/dom/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('fixtures/dom/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -447,7 +463,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' # Fixture copies some built packages from the workroot after install. # That means dependencies of the built packages are not installed. # We need to install dependencies of the workroot to fulfill all dependency constraints @@ -456,10 +472,12 @@ jobs: id: node_modules with: path: "**/node_modules" - key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -512,16 +530,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -563,16 +583,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -600,13 +622,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -626,6 +648,8 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build for PR uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index f8878548c0597..07bcf16c2354a 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -35,16 +35,18 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: - path: "node_modules" - key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: "**/node_modules" + key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Build plugin working-directory: fixtures/eslint-v${{ matrix.eslint_major }} run: node build.mjs diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index e52bed6bb16bf..7536519ba3aed 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -34,13 +34,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index c0b3867e03da0..e0a9630277d27 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -66,13 +66,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index 4b077eff65ab5..a492a320d41c9 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -24,7 +24,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 with: @@ -44,7 +44,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 with: @@ -64,7 +64,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 with: @@ -84,7 +84,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: '**/yarn.lock' - name: Restore cached node_modules uses: actions/cache@v4 with: diff --git a/package.json b/package.json index 2c7041f0fbc83..ca0b62e84672d 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "testRegex": "/scripts/jest/dont-run-jest-directly\\.js$" }, "scripts": { - "prebuild": "yarn --cwd compiler install --frozen-lockfile && ./scripts/react-compiler/link-compiler.sh", + "prebuild": "./scripts/react-compiler/link-compiler.sh", "build": "node ./scripts/rollup/build-all-release-channels.js", "build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE", "build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV", diff --git a/scripts/react-compiler/build-compiler.sh b/scripts/react-compiler/build-compiler.sh index 1c65fdcaa66c9..f73258646266d 100755 --- a/scripts/react-compiler/build-compiler.sh +++ b/scripts/react-compiler/build-compiler.sh @@ -11,5 +11,4 @@ if [[ "$REACT_CLASS_EQUIVALENCE_TEST" == "true" ]]; then fi echo "Building babel-plugin-react-compiler..." -yarn --cwd compiler install --frozen-lockfile yarn --cwd compiler workspace babel-plugin-react-compiler build --dts From ef1103d3e92168803000e12d9a27cb7440e5e1c8 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 20:59:27 -0400 Subject: [PATCH 129/300] [ci] Fix node_modules cache glob (#32604) Seems like the stringified cache path can cause some directories not to be cached, trying an alternative format --- .github/workflows/compiler_playground.yml | 5 +- .github/workflows/compiler_prereleases.yml | 5 +- .github/workflows/compiler_typescript.yml | 15 ++-- .../workflows/devtools_regression_tests.yml | 20 +++--- .github/workflows/runtime_build_and_test.yml | 70 +++++++++++-------- .../workflows/runtime_commit_artifacts.yml | 5 +- .../workflows/runtime_eslint_plugin_e2e.yml | 5 +- .github/workflows/runtime_prereleases.yml | 5 +- .../runtime_releases_from_npm_manual.yml | 5 +- .github/workflows/shared_lint.yml | 20 +++--- 10 files changed, 93 insertions(+), 62 deletions(-) diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index d3d2420ee21d8..21f18e4a95257 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -36,8 +36,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: npx playwright install --with-deps chromium - run: CI=true yarn test diff --git a/.github/workflows/compiler_prereleases.yml b/.github/workflows/compiler_prereleases.yml index 4f4954dd952b4..9b6744158bad7 100644 --- a/.github/workflows/compiler_prereleases.yml +++ b/.github/workflows/compiler_prereleases.yml @@ -46,8 +46,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - name: Publish packages to npm run: | diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index d3b9517c8ef6e..133b7c969b0a2 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -46,8 +46,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler lint @@ -66,8 +67,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler jest @@ -90,8 +92,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive' diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 4babfeefb0f2c..d76c2795a7683 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -29,8 +29,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -63,8 +64,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -120,8 +122,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 @@ -154,8 +157,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 272411b302e63..2036ca74fba8b 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -55,8 +55,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -79,8 +80,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -107,8 +109,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -162,8 +165,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -198,8 +202,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -274,8 +279,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -308,8 +314,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -357,8 +364,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -394,8 +402,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -428,8 +437,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('fixtures/dom/yarn.lock') }} + path: | + **/node_modules + key: fixtures_dom-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('fixtures/dom/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -471,8 +481,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: fixtures_flight-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -535,8 +546,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -588,8 +600,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -627,8 +640,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index b136c60bb714b..c195d0e4e7756 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -73,8 +73,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index 07bcf16c2354a..2cbfa8d109f5b 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -40,8 +40,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-eslint_e2e-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 7536519ba3aed..124e94530bd9b 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -39,8 +39,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index e0a9630277d27..d39e0d9364087 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -71,8 +71,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index a492a320d41c9..1a4dae9cf2c11 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -28,8 +28,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -48,8 +49,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -68,8 +70,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -88,8 +91,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile From e9d80d939ee0f6b1d6146eb11917c433a7791d17 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 21:52:46 -0400 Subject: [PATCH 130/300] Revert "[ci] Fix node_modules cache glob (#32604)" (#32606) This reverts commit ef1103d3e92168803000e12d9a27cb7440e5e1c8. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32606). * #32609 * #32608 * #32607 * __->__ #32606 --- .github/workflows/compiler_playground.yml | 5 +- .github/workflows/compiler_prereleases.yml | 5 +- .github/workflows/compiler_typescript.yml | 15 ++-- .../workflows/devtools_regression_tests.yml | 20 +++--- .github/workflows/runtime_build_and_test.yml | 70 ++++++++----------- .../workflows/runtime_commit_artifacts.yml | 5 +- .../workflows/runtime_eslint_plugin_e2e.yml | 5 +- .github/workflows/runtime_prereleases.yml | 5 +- .../runtime_releases_from_npm_manual.yml | 5 +- .github/workflows/shared_lint.yml | 20 +++--- 10 files changed, 62 insertions(+), 93 deletions(-) diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index 21f18e4a95257..d3d2420ee21d8 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -36,9 +36,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: "**/node_modules" + key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: npx playwright install --with-deps chromium - run: CI=true yarn test diff --git a/.github/workflows/compiler_prereleases.yml b/.github/workflows/compiler_prereleases.yml index 9b6744158bad7..4f4954dd952b4 100644 --- a/.github/workflows/compiler_prereleases.yml +++ b/.github/workflows/compiler_prereleases.yml @@ -46,9 +46,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: "**/node_modules" + key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - name: Publish packages to npm run: | diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index 133b7c969b0a2..d3b9517c8ef6e 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -46,9 +46,8 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: | - **/node_modules - key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: "**/node_modules" + key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler lint @@ -67,9 +66,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: "**/node_modules" + key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler jest @@ -92,9 +90,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: compiler-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: "**/node_modules" + key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive' diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index d76c2795a7683..4babfeefb0f2c 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -29,9 +29,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: "**/node_modules" + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -64,9 +63,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -122,9 +120,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 @@ -157,9 +154,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 2036ca74fba8b..272411b302e63 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -55,9 +55,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -80,9 +79,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -109,9 +107,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -165,9 +162,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -202,9 +198,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -279,9 +274,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -314,9 +308,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -364,9 +357,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -402,9 +394,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -437,9 +428,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: fixtures_dom-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('fixtures/dom/yarn.lock') }} + path: "**/node_modules" + key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('fixtures/dom/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -481,9 +471,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: fixtures_flight-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -546,9 +535,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -600,9 +588,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -640,9 +627,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index c195d0e4e7756..b136c60bb714b 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -73,9 +73,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: "**/node_modules" + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index 2cbfa8d109f5b..07bcf16c2354a 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -40,9 +40,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-eslint_e2e-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 124e94530bd9b..7536519ba3aed 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -39,9 +39,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index d39e0d9364087..e0a9630277d27 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -71,9 +71,8 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: | - **/node_modules - key: runtime-release-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index 1a4dae9cf2c11..a492a320d41c9 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -28,9 +28,8 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: | - **/node_modules - key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -49,9 +48,8 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: | - **/node_modules - key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -70,9 +68,8 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: | - **/node_modules - key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -91,9 +88,8 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: | - **/node_modules - key: shared-lint-node_modules-v2-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "**/node_modules" + key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile From 5200721e5c93a59f8af0403d61abe513cb7d32fe Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 22:14:00 -0400 Subject: [PATCH 131/300] Revert "[ci] Update yarn and node_modules cache key (#32603)" (#32607) This reverts commit 67338703aa52d662998733e58671dc9fe1edae47. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32607). * #32609 * #32608 * __->__ #32607 --- .github/workflows/runtime_build_and_test.yml | 80 +++++++------------ .../workflows/runtime_eslint_plugin_e2e.yml | 8 +- .github/workflows/runtime_prereleases.yml | 4 +- .../runtime_releases_from_npm_manual.yml | 4 +- .github/workflows/shared_lint.yml | 8 +- package.json | 2 +- scripts/react-compiler/build-compiler.sh | 1 + 7 files changed, 41 insertions(+), 66 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 272411b302e63..d53e352fbb266 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -50,13 +50,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -74,18 +74,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - run: | yarn generate-inline-fizz-runtime git diff --quiet || (echo "There was a change to the Fizz runtime. Run `yarn generate-inline-fizz-runtime` and check in the result." && false) @@ -102,18 +100,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - run: yarn flags # ----- TESTS ----- @@ -157,18 +153,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }} # ----- BUILD ----- @@ -189,7 +183,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - uses: actions/setup-java@v4 with: distribution: temurin @@ -199,12 +193,10 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci env: CI: github @@ -269,18 +261,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -303,18 +293,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -352,18 +340,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -389,18 +375,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -423,13 +407,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: 'fixtures/dom/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('fixtures/dom/yarn.lock') }} + key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -463,7 +447,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock # Fixture copies some built packages from the workroot after install. # That means dependencies of the built packages are not installed. # We need to install dependencies of the workroot to fulfill all dependency constraints @@ -472,12 +456,10 @@ jobs: id: node_modules with: path: "**/node_modules" - key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -530,18 +512,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -583,18 +563,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -622,13 +600,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -648,8 +626,6 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Restore archived build for PR uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index 07bcf16c2354a..f8878548c0597 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -35,18 +35,16 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: "node_modules" + key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler - name: Build plugin working-directory: fixtures/eslint-v${{ matrix.eslint_major }} run: node build.mjs diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 7536519ba3aed..e52bed6bb16bf 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -34,13 +34,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index e0a9630277d27..c0b3867e03da0 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -66,13 +66,13 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index a492a320d41c9..4b077eff65ab5 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -24,7 +24,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 with: @@ -44,7 +44,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 with: @@ -64,7 +64,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 with: @@ -84,7 +84,7 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: '**/yarn.lock' + cache-dependency-path: yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 with: diff --git a/package.json b/package.json index ca0b62e84672d..2c7041f0fbc83 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "testRegex": "/scripts/jest/dont-run-jest-directly\\.js$" }, "scripts": { - "prebuild": "./scripts/react-compiler/link-compiler.sh", + "prebuild": "yarn --cwd compiler install --frozen-lockfile && ./scripts/react-compiler/link-compiler.sh", "build": "node ./scripts/rollup/build-all-release-channels.js", "build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE", "build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV", diff --git a/scripts/react-compiler/build-compiler.sh b/scripts/react-compiler/build-compiler.sh index f73258646266d..1c65fdcaa66c9 100755 --- a/scripts/react-compiler/build-compiler.sh +++ b/scripts/react-compiler/build-compiler.sh @@ -11,4 +11,5 @@ if [[ "$REACT_CLASS_EQUIVALENCE_TEST" == "true" ]]; then fi echo "Building babel-plugin-react-compiler..." +yarn --cwd compiler install --frozen-lockfile yarn --cwd compiler workspace babel-plugin-react-compiler build --dts From 4eba294c69422ade02f2bb4d6a0255755b406b32 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 22:21:35 -0400 Subject: [PATCH 132/300] [ci] Cache runtime and compiler only for test runs (#32608) We only need the compiler built for `yarn test` in the root directory. Rather than always cache both for every step, let's just do it where it's needed explicitly. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32608). * #32609 * __->__ #32608 --- .github/workflows/runtime_build_and_test.yml | 46 ++++++++++++------- .../workflows/runtime_eslint_plugin_e2e.yml | 8 +++- package.json | 2 +- scripts/react-compiler/build-compiler.sh | 1 - 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index d53e352fbb266..44cafd8a0902d 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -56,7 +56,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -80,7 +80,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -106,7 +106,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -153,16 +153,20 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: | + yarn.lock + compiler/yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-and-compiler-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }} # ----- BUILD ----- @@ -183,7 +187,9 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: | + yarn.lock + compiler/yarn.lock - uses: actions/setup-java@v4 with: distribution: temurin @@ -193,10 +199,12 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-and-compiler-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci env: CI: github @@ -261,16 +269,20 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: | + yarn.lock + compiler/yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-and-compiler-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -299,7 +311,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -346,7 +358,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -381,7 +393,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -413,7 +425,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: fixtures_dom-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_dom-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -456,7 +468,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: fixtures_flight-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_flight-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -518,7 +530,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -569,7 +581,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -606,7 +618,7 @@ jobs: id: node_modules with: path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index f8878548c0597..ade779e743451 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -35,16 +35,20 @@ jobs: with: node-version-file: '.nvmrc' cache: yarn - cache-dependency-path: yarn.lock + cache-dependency-path: | + yarn.lock + compiler/yarn.lock - name: Restore cached node_modules uses: actions/cache@v4 id: node_modules with: path: "node_modules" - key: runtime-eslint_e2e-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-and-compiler-eslint_e2e-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + - run: yarn install --frozen-lockfile + working-directory: compiler - name: Build plugin working-directory: fixtures/eslint-v${{ matrix.eslint_major }} run: node build.mjs diff --git a/package.json b/package.json index 2c7041f0fbc83..ca0b62e84672d 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "testRegex": "/scripts/jest/dont-run-jest-directly\\.js$" }, "scripts": { - "prebuild": "yarn --cwd compiler install --frozen-lockfile && ./scripts/react-compiler/link-compiler.sh", + "prebuild": "./scripts/react-compiler/link-compiler.sh", "build": "node ./scripts/rollup/build-all-release-channels.js", "build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE", "build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV", diff --git a/scripts/react-compiler/build-compiler.sh b/scripts/react-compiler/build-compiler.sh index 1c65fdcaa66c9..f73258646266d 100755 --- a/scripts/react-compiler/build-compiler.sh +++ b/scripts/react-compiler/build-compiler.sh @@ -11,5 +11,4 @@ if [[ "$REACT_CLASS_EQUIVALENCE_TEST" == "true" ]]; then fi echo "Building babel-plugin-react-compiler..." -yarn --cwd compiler install --frozen-lockfile yarn --cwd compiler workspace babel-plugin-react-compiler build --dts From f3c956006a90dc68210bd3e19497d10fb9b028d3 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 13 Mar 2025 22:29:48 -0400 Subject: [PATCH 133/300] [ci] Update node_modules cache path (#32609) Alternative of #32604. Bust all old caches since I reverted some other changes to the keys. --- .github/workflows/compiler_playground.yml | 5 +- .github/workflows/compiler_prereleases.yml | 5 +- .github/workflows/compiler_typescript.yml | 15 ++-- .../workflows/devtools_regression_tests.yml | 20 +++--- .github/workflows/runtime_build_and_test.yml | 70 +++++++++++-------- .../workflows/runtime_commit_artifacts.yml | 5 +- .../workflows/runtime_eslint_plugin_e2e.yml | 5 +- .github/workflows/runtime_prereleases.yml | 5 +- .../runtime_releases_from_npm_manual.yml | 5 +- .github/workflows/shared_lint.yml | 20 +++--- 10 files changed, 93 insertions(+), 62 deletions(-) diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index d3d2420ee21d8..e53158bf60e79 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -36,8 +36,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: npx playwright install --with-deps chromium - run: CI=true yarn test diff --git a/.github/workflows/compiler_prereleases.yml b/.github/workflows/compiler_prereleases.yml index 4f4954dd952b4..8baede7ac2a89 100644 --- a/.github/workflows/compiler_prereleases.yml +++ b/.github/workflows/compiler_prereleases.yml @@ -46,8 +46,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - name: Publish packages to npm run: | diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index d3b9517c8ef6e..dbde1b9962206 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -46,8 +46,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler lint @@ -66,8 +67,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler jest @@ -90,8 +92,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: compiler-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + path: | + **/node_modules + key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive' diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 4babfeefb0f2c..9399b80f0e250 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -29,8 +29,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -63,8 +64,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -120,8 +122,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 @@ -154,8 +157,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 44cafd8a0902d..1cb04920e89df 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -55,8 +55,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -79,8 +80,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -105,8 +107,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -160,8 +163,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-and-compiler-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + path: | + **/node_modules + key: runtime-and-compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -198,8 +202,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-and-compiler-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + path: | + **/node_modules + key: runtime-and-compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -276,8 +281,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-and-compiler-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + path: | + **/node_modules + key: runtime-and-compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -310,8 +316,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -357,8 +364,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -392,8 +400,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -424,8 +433,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: fixtures_dom-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: fixtures_dom-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -467,8 +477,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: fixtures_flight-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: fixtures_flight-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -529,8 +540,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -580,8 +592,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -617,8 +630,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + path: | + **/node_modules + key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index b136c60bb714b..9e60606210f39 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -73,8 +73,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index ade779e743451..8b8222721cfaa 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -42,8 +42,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "node_modules" - key: runtime-and-compiler-eslint_e2e-node_modules-v3-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + path: | + **/node_modules + key: runtime-and-compiler-eslint_e2e-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index e52bed6bb16bf..293e5f2ce9fa6 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -39,8 +39,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index c0b3867e03da0..c4675bfb7fd4a 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -71,8 +71,9 @@ jobs: uses: actions/cache@v4 id: node_modules with: - path: "**/node_modules" - key: runtime-release-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + path: | + **/node_modules + key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index 4b077eff65ab5..08ade6cb7faaa 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -28,8 +28,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -48,8 +49,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -68,8 +70,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -88,8 +91,9 @@ jobs: - name: Restore cached node_modules uses: actions/cache@v4 with: - path: "**/node_modules" - key: shared-lint-node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + path: | + **/node_modules + key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile From 5398b7115847e87c0053aa719728d8dd1a635ccd Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:44:49 -0400 Subject: [PATCH 134/300] [compiler] detect and throw on untransformed required features (#32512) Traverse program after running compiler transform to find untransformed references to compiler features (e.g. `inferEffectDeps`, `fire`). Hard error to fail the babel pipeline when the compiler fails to transform these features to give predictable runtime semantics. Untransformed calls to functions like `fire` will throw at runtime anyways, so let's fail the build to catch these earlier. Note that with this fails the build *regardless of panicThreshold* --- .../src/Babel/BabelPlugin.ts | 10 +- .../src/Entrypoint/Program.ts | 23 +- .../ValidateNoUntransformedReferences.ts | 275 ++++++++++++++++++ .../src/HIR/Environment.ts | 1 + ...e-in-non-react-fn-default-import.expect.md | 26 ++ ...callsite-in-non-react-fn-default-import.js | 6 + .../error.callsite-in-non-react-fn.expect.md | 26 ++ .../error.callsite-in-non-react-fn.js | 6 + .../error.non-inlined-effect-fn.expect.md | 41 +++ .../error.non-inlined-effect-fn.js | 21 ++ ...mport-default-property-useEffect.expect.md | 27 ++ ...todo-import-default-property-useEffect.js} | 2 +- .../bailout-retry/error.todo-syntax.expect.md | 51 ++++ .../bailout-retry/error.todo-syntax.js | 18 ++ .../bailout-retry/error.use-no-memo.expect.md | 27 ++ .../bailout-retry/error.use-no-memo.js | 7 + .../infer-effect-dependencies.expect.md | 26 +- .../infer-effect-dependencies.js | 7 - ...mport-default-property-useEffect.expect.md | 44 --- .../error.todo-infer-deps-on-retry.expect.md | 42 +++ .../error.todo-infer-deps-on-retry.js | 17 ++ .../bailout-retry/error.todo-syntax.expect.md | 20 +- .../bailout-retry/error.todo-syntax.js | 2 +- ...ror.untransformed-fire-reference.expect.md | 23 ++ .../error.untransformed-fire-reference.js | 4 + .../bailout-retry/error.use-no-memo.expect.md | 42 +++ ...do-use-no-memo.js => error.use-no-memo.js} | 7 +- ...-fire-todo-syntax-shouldnt-throw.expect.md | 95 ++++++ .../no-fire-todo-syntax-shouldnt-throw.js | 35 +++ .../bailout-retry/todo-use-no-memo.expect.md | 47 --- .../__tests__/ReactCompilerRule-test.ts | 32 ++ 31 files changed, 864 insertions(+), 146 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/{todo.import-default-property-useEffect.js => bailout-retry/error.todo-import-default-property-useEffect.js} (73%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/{todo-use-no-memo.js => error.use-no-memo.js} (51%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts index aa49bda22b27e..ff9817380fada 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts @@ -11,6 +11,7 @@ import { injectReanimatedFlag, pipelineUsesReanimatedPlugin, } from '../Entrypoint/Reanimated'; +import validateNoUntransformedReferences from '../Entrypoint/ValidateNoUntransformedReferences'; const ENABLE_REACT_COMPILER_TIMINGS = process.env['ENABLE_REACT_COMPILER_TIMINGS'] === '1'; @@ -61,12 +62,19 @@ export default function BabelPluginReactCompiler( }, }; } - compileProgram(prog, { + const result = compileProgram(prog, { opts, filename: pass.filename ?? null, comments: pass.file.ast.comments ?? [], code: pass.file.code, }); + validateNoUntransformedReferences( + prog, + pass.filename ?? null, + opts.logger, + opts.environment, + result?.retryErrors ?? [], + ); if (ENABLE_REACT_COMPILER_TIMINGS === true) { performance.mark(`${filename}:end`, { detail: 'BabelPlugin:Program:end', diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 0865b50f84116..72fa101260df5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -271,6 +271,9 @@ function isFilePartOfSources( return false; } +type CompileProgramResult = { + retryErrors: Array<{fn: BabelFn; error: CompilerError}>; +}; /** * `compileProgram` is directly invoked by the react-compiler babel plugin, so * exceptions thrown by this function will fail the babel build. @@ -285,16 +288,16 @@ function isFilePartOfSources( export function compileProgram( program: NodePath<t.Program>, pass: CompilerPass, -): void { +): CompileProgramResult | null { if (shouldSkipCompilation(program, pass)) { - return; + return null; } const environment = pass.opts.environment; const restrictedImportsErr = validateRestrictedImports(program, environment); if (restrictedImportsErr) { handleError(restrictedImportsErr, pass, null); - return; + return null; } const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c'); @@ -365,7 +368,7 @@ export function compileProgram( filename: pass.filename ?? null, }, ); - + const retryErrors: Array<{fn: BabelFn; error: CompilerError}> = []; const processFn = ( fn: BabelFn, fnType: ReactFunctionType, @@ -429,7 +432,9 @@ export function compileProgram( handleError(compileResult.error, pass, fn.node.loc ?? null); } // If non-memoization features are enabled, retry regardless of error kind - if (!environment.enableFire) { + if ( + !(environment.enableFire || environment.inferEffectDependencies != null) + ) { return null; } try { @@ -448,6 +453,9 @@ export function compileProgram( }; } catch (err) { // TODO: we might want to log error here, but this will also result in duplicate logging + if (err instanceof CompilerError) { + retryErrors.push({fn, error: err}); + } return null; } } @@ -538,7 +546,7 @@ export function compileProgram( program.node.directives, ); if (moduleScopeOptOutDirectives.length > 0) { - return; + return null; } let gating: null | { gatingFn: ExternalFunction; @@ -596,7 +604,7 @@ export function compileProgram( } } catch (err) { handleError(err, pass, null); - return; + return null; } /* @@ -638,6 +646,7 @@ export function compileProgram( } addImportsToProgram(program, externalFunctions); } + return {retryErrors}; } function shouldSkipCompilation( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts new file mode 100644 index 0000000000000..07ab3b2b6a172 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/ValidateNoUntransformedReferences.ts @@ -0,0 +1,275 @@ +import {NodePath} from '@babel/core'; +import * as t from '@babel/types'; + +import { + CompilerError, + CompilerErrorDetailOptions, + EnvironmentConfig, + ErrorSeverity, + Logger, +} from '..'; +import {getOrInsertWith} from '../Utils/utils'; +import {Environment} from '../HIR'; +import {DEFAULT_EXPORT} from '../HIR/Environment'; + +function throwInvalidReact( + options: Omit<CompilerErrorDetailOptions, 'severity'>, + {logger, filename}: TraversalState, +): never { + const detail: CompilerErrorDetailOptions = { + ...options, + severity: ErrorSeverity.InvalidReact, + }; + logger?.logEvent(filename, { + kind: 'CompileError', + fnLoc: null, + detail, + }); + CompilerError.throw(detail); +} +function assertValidEffectImportReference( + numArgs: number, + paths: Array<NodePath<t.Node>>, + context: TraversalState, +): void { + for (const path of paths) { + const parent = path.parentPath; + if (parent != null && parent.isCallExpression()) { + const args = parent.get('arguments'); + /** + * Only error on untransformed references of the form `useMyEffect(...)` + * or `moduleNamespace.useMyEffect(...)`, with matching argument counts. + * TODO: do we also want a mode to also hard error on non-call references? + */ + if (args.length === numArgs) { + const maybeErrorDiagnostic = matchCompilerDiagnostic( + path, + context.transformErrors, + ); + /** + * Note that we cannot easily check the type of the first argument here, + * as it may have already been transformed by the compiler (and not + * memoized). + */ + throwInvalidReact( + { + reason: + '[InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. ' + + 'This will break your build! ' + + 'To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.', + description: maybeErrorDiagnostic + ? `(Bailout reason: ${maybeErrorDiagnostic})` + : null, + loc: parent.node.loc ?? null, + }, + context, + ); + } + } + } +} + +function assertValidFireImportReference( + paths: Array<NodePath<t.Node>>, + context: TraversalState, +): void { + if (paths.length > 0) { + const maybeErrorDiagnostic = matchCompilerDiagnostic( + paths[0], + context.transformErrors, + ); + throwInvalidReact( + { + reason: + '[Fire] Untransformed reference to compiler-required feature. ' + + 'Either remove this `fire` call or ensure it is successfully transformed by the compiler', + description: maybeErrorDiagnostic + ? `(Bailout reason: ${maybeErrorDiagnostic})` + : null, + loc: paths[0].node.loc ?? null, + }, + context, + ); + } +} +export default function validateNoUntransformedReferences( + path: NodePath<t.Program>, + filename: string | null, + logger: Logger | null, + env: EnvironmentConfig, + transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>, +): void { + const moduleLoadChecks = new Map< + string, + Map<string, CheckInvalidReferenceFn> + >(); + if (env.enableFire) { + /** + * Error on any untransformed references to `fire` (e.g. including non-call + * expressions) + */ + for (const module of Environment.knownReactModules) { + const react = getOrInsertWith(moduleLoadChecks, module, () => new Map()); + react.set('fire', assertValidFireImportReference); + } + } + if (env.inferEffectDependencies) { + for (const { + function: {source, importSpecifierName}, + numRequiredArgs, + } of env.inferEffectDependencies) { + const module = getOrInsertWith(moduleLoadChecks, source, () => new Map()); + module.set( + importSpecifierName, + assertValidEffectImportReference.bind(null, numRequiredArgs), + ); + } + } + if (moduleLoadChecks.size > 0) { + transformProgram(path, moduleLoadChecks, filename, logger, transformErrors); + } +} + +type TraversalState = { + shouldInvalidateScopes: boolean; + program: NodePath<t.Program>; + logger: Logger | null; + filename: string | null; + transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>; +}; +type CheckInvalidReferenceFn = ( + paths: Array<NodePath<t.Node>>, + context: TraversalState, +) => void; + +function validateImportSpecifier( + specifier: NodePath<t.ImportSpecifier>, + importSpecifierChecks: Map<string, CheckInvalidReferenceFn>, + state: TraversalState, +): void { + const imported = specifier.get('imported'); + const specifierName: string = + imported.node.type === 'Identifier' + ? imported.node.name + : imported.node.value; + const checkFn = importSpecifierChecks.get(specifierName); + if (checkFn == null) { + return; + } + if (state.shouldInvalidateScopes) { + state.shouldInvalidateScopes = false; + state.program.scope.crawl(); + } + + const local = specifier.get('local'); + const binding = local.scope.getBinding(local.node.name); + CompilerError.invariant(binding != null, { + reason: 'Expected binding to be found for import specifier', + loc: local.node.loc ?? null, + }); + checkFn(binding.referencePaths, state); +} + +function validateNamespacedImport( + specifier: NodePath<t.ImportNamespaceSpecifier | t.ImportDefaultSpecifier>, + importSpecifierChecks: Map<string, CheckInvalidReferenceFn>, + state: TraversalState, +): void { + if (state.shouldInvalidateScopes) { + state.shouldInvalidateScopes = false; + state.program.scope.crawl(); + } + const local = specifier.get('local'); + const binding = local.scope.getBinding(local.node.name); + const defaultCheckFn = importSpecifierChecks.get(DEFAULT_EXPORT); + + CompilerError.invariant(binding != null, { + reason: 'Expected binding to be found for import specifier', + loc: local.node.loc ?? null, + }); + const filteredReferences = new Map< + CheckInvalidReferenceFn, + Array<NodePath<t.Node>> + >(); + for (const reference of binding.referencePaths) { + if (defaultCheckFn != null) { + getOrInsertWith(filteredReferences, defaultCheckFn, () => []).push( + reference, + ); + } + const parent = reference.parentPath; + if ( + parent != null && + parent.isMemberExpression() && + parent.get('object') === reference + ) { + if (parent.node.computed || parent.node.property.type !== 'Identifier') { + continue; + } + const checkFn = importSpecifierChecks.get(parent.node.property.name); + if (checkFn != null) { + getOrInsertWith(filteredReferences, checkFn, () => []).push(parent); + } + } + } + + for (const [checkFn, references] of filteredReferences) { + checkFn(references, state); + } +} +function transformProgram( + path: NodePath<t.Program>, + + moduleLoadChecks: Map<string, Map<string, CheckInvalidReferenceFn>>, + filename: string | null, + logger: Logger | null, + transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>, +): void { + const traversalState: TraversalState = { + shouldInvalidateScopes: true, + program: path, + filename, + logger, + transformErrors, + }; + path.traverse({ + ImportDeclaration(path: NodePath<t.ImportDeclaration>) { + const importSpecifierChecks = moduleLoadChecks.get( + path.node.source.value, + ); + if (importSpecifierChecks == null) { + return; + } + const specifiers = path.get('specifiers'); + for (const specifier of specifiers) { + if (specifier.isImportSpecifier()) { + validateImportSpecifier( + specifier, + importSpecifierChecks, + traversalState, + ); + } else { + validateNamespacedImport( + specifier as NodePath< + t.ImportNamespaceSpecifier | t.ImportDefaultSpecifier + >, + importSpecifierChecks, + traversalState, + ); + } + } + }, + }); +} + +function matchCompilerDiagnostic( + badReference: NodePath<t.Node>, + transformErrors: Array<{fn: NodePath<t.Node>; error: CompilerError}>, +): string | null { + for (const {fn, error} of transformErrors) { + if (fn.isAncestor(badReference)) { + return error.toString(); + } + } + return null; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 4fce273b7a2de..1f64452e4bacf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -1121,6 +1121,7 @@ export class Environment { moduleName.toLowerCase() === 'react-dom' ); } + static knownReactModules: ReadonlyArray<string> = ['react', 'react-dom']; getFallthroughPropertyType( receiver: Type, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md new file mode 100644 index 0000000000000..cd977ae834b8a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none) +import useMyEffect from 'useEffectWrapper'; + +function nonReactFn(arg) { + useMyEffect(() => [1, 2, arg]); +} + +``` + + +## Error + +``` + 3 | + 4 | function nonReactFn(arg) { +> 5 | useMyEffect(() => [1, 2, arg]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5) + 6 | } + 7 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js new file mode 100644 index 0000000000000..8061b44a3dc2f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn-default-import.js @@ -0,0 +1,6 @@ +// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none) +import useMyEffect from 'useEffectWrapper'; + +function nonReactFn(arg) { + useMyEffect(() => [1, 2, arg]); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md new file mode 100644 index 0000000000000..e36e9b3c0e681 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.expect.md @@ -0,0 +1,26 @@ + +## Input + +```javascript +// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none) +import {useEffect} from 'react'; + +function nonReactFn(arg) { + useEffect(() => [1, 2, arg]); +} + +``` + + +## Error + +``` + 3 | + 4 | function nonReactFn(arg) { +> 5 | useEffect(() => [1, 2, arg]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (5:5) + 6 | } + 7 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js new file mode 100644 index 0000000000000..813c3b6c3dccb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.callsite-in-non-react-fn.js @@ -0,0 +1,6 @@ +// @inferEffectDependencies @compilationMode(infer) @panicThreshold(none) +import {useEffect} from 'react'; + +function nonReactFn(arg) { + useEffect(() => [1, 2, arg]); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md new file mode 100644 index 0000000000000..ee17fb048e7e7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @inferEffectDependencies @panicThreshold(none) +import {useEffect} from 'react'; + +/** + * Error on non-inlined effect functions: + * 1. From the effect hook callee's perspective, it only makes sense + * to either + * (a) never hard error (i.e. failing to infer deps is acceptable) or + * (b) always hard error, + * regardless of whether the callback function is an inline fn. + * 2. (Technical detail) it's harder to support detecting cases in which + * function (pre-Forget transform) was inline but becomes memoized + */ +function Component({foo}) { + function f() { + console.log(foo); + } + + // No inferred dep array, the argument is not a lambda + useEffect(f); +} + +``` + + +## Error + +``` + 18 | + 19 | // No inferred dep array, the argument is not a lambda +> 20 | useEffect(f); + | ^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (20:20) + 21 | } + 22 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js new file mode 100644 index 0000000000000..a011e3bf144e1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.non-inlined-effect-fn.js @@ -0,0 +1,21 @@ +// @inferEffectDependencies @panicThreshold(none) +import {useEffect} from 'react'; + +/** + * Error on non-inlined effect functions: + * 1. From the effect hook callee's perspective, it only makes sense + * to either + * (a) never hard error (i.e. failing to infer deps is acceptable) or + * (b) always hard error, + * regardless of whether the callback function is an inline fn. + * 2. (Technical detail) it's harder to support detecting cases in which + * function (pre-Forget transform) was inline but becomes memoized + */ +function Component({foo}) { + function f() { + console.log(foo); + } + + // No inferred dep array, the argument is not a lambda + useEffect(f); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md new file mode 100644 index 0000000000000..71ecefeeea50f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @inferEffectDependencies @panicThreshold(none) +import React from 'react'; + +function NonReactiveDepInEffect() { + const obj = makeObject_Primitives(); + React.useEffect(() => print(obj)); +} + +``` + + +## Error + +``` + 4 | function NonReactiveDepInEffect() { + 5 | const obj = makeObject_Primitives(); +> 6 | React.useEffect(() => print(obj)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) + 7 | } + 8 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js similarity index 73% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js index 0dbae754ecf76..237535fe5b4e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-import-default-property-useEffect.js @@ -1,4 +1,4 @@ -// @inferEffectDependencies +// @inferEffectDependencies @panicThreshold(none) import React from 'react'; function NonReactiveDepInEffect() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md new file mode 100644 index 0000000000000..575fd2101f77f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md @@ -0,0 +1,51 @@ + +## Input + +```javascript +// @inferEffectDependencies @panicThreshold(none) +import {useSpecialEffect} from 'shared-runtime'; + +/** + * Note that a react compiler-based transform still has limitations on JS syntax. + * We should surface these as actionable lint / build errors to devs. + */ +function Component({prop1}) { + 'use memo'; + useSpecialEffect(() => { + try { + console.log(prop1); + } finally { + console.log('exiting'); + } + }, [prop1]); + return <div>{prop1}</div>; +} + +``` + + +## Error + +``` + 8 | function Component({prop1}) { + 9 | 'use memo'; +> 10 | useSpecialEffect(() => { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +> 11 | try { + | ^^^^^^^^^ +> 12 | console.log(prop1); + | ^^^^^^^^^ +> 13 | } finally { + | ^^^^^^^^^ +> 14 | console.log('exiting'); + | ^^^^^^^^^ +> 15 | } + | ^^^^^^^^^ +> 16 | }, [prop1]); + | ^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (10:16) + 17 | return <div>{prop1}</div>; + 18 | } + 19 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js new file mode 100644 index 0000000000000..d3c83c25620f8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.js @@ -0,0 +1,18 @@ +// @inferEffectDependencies @panicThreshold(none) +import {useSpecialEffect} from 'shared-runtime'; + +/** + * Note that a react compiler-based transform still has limitations on JS syntax. + * We should surface these as actionable lint / build errors to devs. + */ +function Component({prop1}) { + 'use memo'; + useSpecialEffect(() => { + try { + console.log(prop1); + } finally { + console.log('exiting'); + } + }, [prop1]); + return <div>{prop1}</div>; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md new file mode 100644 index 0000000000000..52b99a393e102 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @inferEffectDependencies @panicThreshold(none) +import {useEffect} from 'react'; + +function Component({propVal}) { + 'use no memo'; + useEffect(() => [propVal]); +} + +``` + + +## Error + +``` + 4 | function Component({propVal}) { + 5 | 'use no memo'; +> 6 | useEffect(() => [propVal]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics. (6:6) + 7 | } + 8 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js new file mode 100644 index 0000000000000..53eeda50501ed --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.use-no-memo.js @@ -0,0 +1,7 @@ +// @inferEffectDependencies @panicThreshold(none) +import {useEffect} from 'react'; + +function Component({propVal}) { + 'use no memo'; + useEffect(() => [propVal]); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md index 89da346a72c50..d0745f9bd37ba 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.expect.md @@ -34,13 +34,6 @@ function Component({foo, bar}) { console.log(bar.qux); }); - function f() { - console.log(foo); - } - - // No inferred dep array, the argument is not a lambda - useEffect(f); - useEffectWrapper(() => { console.log(foo); }); @@ -58,7 +51,7 @@ import useEffectWrapper from "useEffectWrapper"; const moduleNonReactive = 0; function Component(t0) { - const $ = _c(14); + const $ = _c(12); const { foo, bar } = t0; const ref = useRef(0); @@ -119,7 +112,7 @@ function Component(t0) { useEffect(t4, [bar.baz, bar.qux]); let t5; if ($[10] !== foo) { - t5 = function f() { + t5 = () => { console.log(foo); }; $[10] = foo; @@ -127,20 +120,7 @@ function Component(t0) { } else { t5 = $[11]; } - const f = t5; - - useEffect(f); - let t6; - if ($[12] !== foo) { - t6 = () => { - console.log(foo); - }; - $[12] = foo; - $[13] = t6; - } else { - t6 = $[13]; - } - useEffectWrapper(t6, [foo]); + useEffectWrapper(t5, [foo]); } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.js index efdd4164789b4..2bad5ee1cc920 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/infer-effect-dependencies.js @@ -30,13 +30,6 @@ function Component({foo, bar}) { console.log(bar.qux); }); - function f() { - console.log(foo); - } - - // No inferred dep array, the argument is not a lambda - useEffect(f); - useEffectWrapper(() => { console.log(foo); }); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md deleted file mode 100644 index a5a576c83067a..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md +++ /dev/null @@ -1,44 +0,0 @@ - -## Input - -```javascript -// @inferEffectDependencies -import React from 'react'; - -function NonReactiveDepInEffect() { - const obj = makeObject_Primitives(); - React.useEffect(() => print(obj)); -} - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies -import React from "react"; - -function NonReactiveDepInEffect() { - const $ = _c(2); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = makeObject_Primitives(); - $[0] = t0; - } else { - t0 = $[0]; - } - const obj = t0; - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => print(obj); - $[1] = t1; - } else { - t1 = $[1]; - } - React.useEffect(t1); -} - -``` - -### Eval output -(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.expect.md new file mode 100644 index 0000000000000..0384d3335ca57 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +// @inferEffectDependencies @panicThreshold(none) +import {useRef} from 'react'; +import {useSpecialEffect} from 'shared-runtime'; + +/** + * The retry pipeline disables memoization features, which means we need to + * provide an alternate implementation of effect dependencies which does not + * rely on memoization. + */ +function useFoo({cond}) { + const ref = useRef(); + const derived = cond ? ref.current : makeObject(); + useSpecialEffect(() => { + log(derived); + }, [derived]); + return ref; +} + +``` + + +## Error + +``` + 11 | const ref = useRef(); + 12 | const derived = cond ? ref.current : makeObject(); +> 13 | useSpecialEffect(() => { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +> 14 | log(derived); + | ^^^^^^^^^^^^^^^^^ +> 15 | }, [derived]); + | ^^^^^^^^^^^^^^^^ InvalidReact: [InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.. (Bailout reason: Invariant: Expected function expression scope to exist (13:15)) (13:15) + 16 | return ref; + 17 | } + 18 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.js new file mode 100644 index 0000000000000..f3a57bb912547 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-infer-deps-on-retry.js @@ -0,0 +1,17 @@ +// @inferEffectDependencies @panicThreshold(none) +import {useRef} from 'react'; +import {useSpecialEffect} from 'shared-runtime'; + +/** + * The retry pipeline disables memoization features, which means we need to + * provide an alternate implementation of effect dependencies which does not + * rely on memoization. + */ +function useFoo({cond}) { + const ref = useRef(); + const derived = cond ? ref.current : makeObject(); + useSpecialEffect(() => { + log(derived); + }, [derived]); + return ref; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md index 86836d86b6092..c5d7456d653c2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @enableFire +// @enableFire @panicThreshold(none) import {fire} from 'react'; /** @@ -29,21 +29,13 @@ function Component({prop1}) { ## Error ``` - 9 | function Component({prop1}) { - 10 | const foo = () => { -> 11 | try { - | ^^^^^ -> 12 | console.log(prop1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -> 13 | } finally { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -> 14 | console.log('jbrown215'); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -> 15 | } - | ^^^^^^ Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15) 16 | }; 17 | useEffect(() => { - 18 | fire(foo()); +> 18 | fire(foo()); + | ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler. (Bailout reason: Todo: (BuildHIR::lowerStatement) Handle TryStatement without a catch clause (11:15)) (18:18) + 19 | }); + 20 | } + 21 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.js index ec50ed84c5780..b1ee459177509 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.todo-syntax.js @@ -1,4 +1,4 @@ -// @enableFire +// @enableFire @panicThreshold(none) import {fire} from 'react'; /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md new file mode 100644 index 0000000000000..ddcc86ff00efe --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.expect.md @@ -0,0 +1,23 @@ + +## Input + +```javascript +// @enableFire @panicThreshold(none) +import {fire} from 'react'; + +console.log(fire == null); + +``` + + +## Error + +``` + 2 | import {fire} from 'react'; + 3 | +> 4 | console.log(fire == null); + | ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler (4:4) + 5 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.js new file mode 100644 index 0000000000000..25a60a97167dd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.untransformed-fire-reference.js @@ -0,0 +1,4 @@ +// @enableFire @panicThreshold(none) +import {fire} from 'react'; + +console.log(fire == null); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md new file mode 100644 index 0000000000000..84a27b43b8950 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +// @enableFire @panicThreshold(none) +import {fire} from 'react'; + +/** + * TODO: we should eventually distinguish between `use no memo` and `use no + * compiler` directives. The former should be used to *only* disable memoization + * features. + */ +function Component({props, bar}) { + 'use no memo'; + const foo = () => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + fire(foo()); + fire(bar()); + }); + + return null; +} + +``` + + +## Error + +``` + 13 | }; + 14 | useEffect(() => { +> 15 | fire(foo(props)); + | ^^^^ InvalidReact: [Fire] Untransformed reference to compiler-required feature. Either remove this `fire` call or ensure it is successfully transformed by the compiler (15:15) + 16 | fire(foo()); + 17 | fire(bar()); + 18 | }); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.js similarity index 51% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.js index 2587e24ee1170..92e1ff211a62c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/error.use-no-memo.js @@ -1,6 +1,11 @@ -// @enableFire +// @enableFire @panicThreshold(none) import {fire} from 'react'; +/** + * TODO: we should eventually distinguish between `use no memo` and `use no + * compiler` directives. The former should be used to *only* disable memoization + * features. + */ function Component({props, bar}) { 'use no memo'; const foo = () => { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md new file mode 100644 index 0000000000000..fecc28bb00158 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md @@ -0,0 +1,95 @@ + +## Input + +```javascript +// @enableFire @panicThreshold(none) +import {fire} from 'react'; + +/** + * Compilation of this file should succeed. + */ +function NonFireComponent({prop1}) { + /** + * This component bails out but does not use fire + */ + const foo = () => { + try { + console.log(prop1); + } finally { + console.log('jbrown215'); + } + }; + useEffect(() => { + foo(); + }); +} + +function FireComponent(props) { + /** + * This component uses fire and compiles successfully + */ + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return null; +} + +``` + +## Code + +```javascript +import { useFire } from "react/compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) +import { fire } from "react"; + +/** + * Compilation of this file should succeed. + */ +function NonFireComponent({ prop1 }) { + /** + * This component bails out but does not use fire + */ + const foo = () => { + try { + console.log(prop1); + } finally { + console.log("jbrown215"); + } + }; + useEffect(() => { + foo(); + }); +} + +function FireComponent(props) { + const $ = _c(3); + + const foo = _temp; + const t0 = useFire(foo); + let t1; + if ($[0] !== props || $[1] !== t0) { + t1 = () => { + t0(props); + }; + $[0] = props; + $[1] = t0; + $[2] = t1; + } else { + t1 = $[2]; + } + useEffect(t1); + return null; +} +function _temp(props_0) { + console.log(props_0); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.js new file mode 100644 index 0000000000000..899fa33376512 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.js @@ -0,0 +1,35 @@ +// @enableFire @panicThreshold(none) +import {fire} from 'react'; + +/** + * Compilation of this file should succeed. + */ +function NonFireComponent({prop1}) { + /** + * This component bails out but does not use fire + */ + const foo = () => { + try { + console.log(prop1); + } finally { + console.log('jbrown215'); + } + }; + useEffect(() => { + foo(); + }); +} + +function FireComponent(props) { + /** + * This component uses fire and compiles successfully + */ + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return null; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.expect.md deleted file mode 100644 index 907501228b10b..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/todo-use-no-memo.expect.md +++ /dev/null @@ -1,47 +0,0 @@ - -## Input - -```javascript -// @enableFire -import {fire} from 'react'; - -function Component({props, bar}) { - 'use no memo'; - const foo = () => { - console.log(props); - }; - useEffect(() => { - fire(foo(props)); - fire(foo()); - fire(bar()); - }); - - return null; -} - -``` - -## Code - -```javascript -// @enableFire -import { fire } from "react"; - -function Component({ props, bar }) { - "use no memo"; - const foo = () => { - console.log(props); - }; - useEffect(() => { - fire(foo(props)); - fire(foo()); - fire(bar()); - }); - - return null; -} - -``` - -### Eval output -(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts index 71be6b6622eb5..ea5c30d5b27f0 100644 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts @@ -274,6 +274,38 @@ const tests: CompilerTestCases = { }, ], }, + { + name: 'Pipeline errors are reported', + code: normalizeIndent` + import useMyEffect from 'useMyEffect'; + function Component({a}) { + 'use no memo'; + useMyEffect(() => console.log(a.b)); + return <div>Hello world</div>; + } + `, + options: [ + { + environment: { + inferEffectDependencies: [ + { + function: { + source: 'useMyEffect', + importSpecifierName: 'default', + }, + numRequiredArgs: 1, + }, + ], + }, + }, + ], + errors: [ + { + message: + '[InferEffectDependencies] React Compiler is unable to infer dependencies of this effect. This will break your build! To resolve, either pass your own dependency array or fix reported compiler bailout diagnostics.', + }, + ], + }, ], }; From 1b6e3dd985c0cfaa4f15fae6fda26f7b57bd25da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 13:16:20 -0400 Subject: [PATCH 135/300] Merge restoreEnterViewTransitions and restoreExitViewTransitions (#32585) This is the exact same code in both cases. It's just general clean up. By unifying them it becomes less confusing to reuse these helpers in the Apply Gesture path where the naming is reversed. --- .../src/ReactFiberCommitViewTransitions.js | 35 +++++-------------- .../src/ReactFiberCommitWork.js | 9 +++-- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 22b9d11ac67d2..5dcf1498ed18a 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -514,37 +514,20 @@ function restorePairedViewTransitions(parent: Fiber): void { } } -export function restoreEnterViewTransitions(placement: Fiber): void { - if (placement.tag === ViewTransitionComponent) { - const instance: ViewTransitionState = placement.stateNode; - instance.paired = null; - restoreViewTransitionOnHostInstances(placement.child, false); - restorePairedViewTransitions(placement); - } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - let child = placement.child; - while (child !== null) { - restoreEnterViewTransitions(child); - child = child.sibling; - } - } else { - restorePairedViewTransitions(placement); - } -} - -export function restoreExitViewTransitions(deletion: Fiber): void { - if (deletion.tag === ViewTransitionComponent) { - const instance: ViewTransitionState = deletion.stateNode; +export function restoreEnterOrExitViewTransitions(fiber: Fiber): void { + if (fiber.tag === ViewTransitionComponent) { + const instance: ViewTransitionState = fiber.stateNode; instance.paired = null; - restoreViewTransitionOnHostInstances(deletion.child, false); - restorePairedViewTransitions(deletion); - } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - let child = deletion.child; + restoreViewTransitionOnHostInstances(fiber.child, false); + restorePairedViewTransitions(fiber); + } else if ((fiber.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + let child = fiber.child; while (child !== null) { - restoreExitViewTransitions(child); + restoreEnterOrExitViewTransitions(child); child = child.sibling; } } else { - restorePairedViewTransitions(deletion); + restorePairedViewTransitions(fiber); } } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 8b82e6a7e713c..bcf7fc188ee46 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -246,8 +246,7 @@ import { commitExitViewTransitions, commitBeforeUpdateViewTransition, commitNestedViewTransitions, - restoreEnterViewTransitions, - restoreExitViewTransitions, + restoreEnterOrExitViewTransitions, restoreUpdateViewTransition, restoreNestedViewTransitions, measureUpdateViewTransition, @@ -3228,7 +3227,7 @@ function commitPassiveMountOnFiber( // This was a new mount. This means we could've triggered an enter animation on // the content. Restore the view transitions if there were any assigned in the // snapshot phase. - restoreEnterViewTransitions(finishedWork); + restoreEnterOrExitViewTransitions(finishedWork); } // When updating this function, also update reconnectPassiveEffects, which does @@ -3529,7 +3528,7 @@ function commitPassiveMountOnFiber( // Content is now hidden but wasn't before. This means we could've // triggered an exit animation on the content. Restore the view // transitions if there were any assigned in the snapshot phase. - restoreExitViewTransitions(current); + restoreEnterOrExitViewTransitions(current); } if (instance._visibility & OffscreenPassiveEffectsConnected) { // The effects are currently connected. Update them. @@ -3576,7 +3575,7 @@ function commitPassiveMountOnFiber( // Content is now visible but wasn't before. This means we could've // triggered an enter animation on the content. Restore the view // transitions if there were any assigned in the snapshot phase. - restoreEnterViewTransitions(finishedWork); + restoreEnterOrExitViewTransitions(finishedWork); } if (instance._visibility & OffscreenPassiveEffectsConnected) { // The effects are currently connected. Update them. From 3e956805e899bff7aea7b19c56e6adaf362cdc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 13:16:30 -0400 Subject: [PATCH 136/300] Gate flushGestureMutations and flushGestureAnimations (#32605) Normally these are gated by the whole commitGestureOnRoot path but in the case of an early commit these phases may need to be invoked. Earlier. Those paths weren't gated which I noticed when I started adding code to them. --- packages/react-reconciler/src/ReactFiberWorkLoop.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index d7fe0893633fc..380fa20c3d1c1 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -3927,6 +3927,9 @@ function commitGestureOnRoot( } function flushGestureMutations(): void { + if (!enableSwipeTransition) { + return; + } if (pendingEffectsStatus !== PENDING_GESTURE_MUTATION_PHASE) { return; } @@ -3953,6 +3956,9 @@ function flushGestureMutations(): void { } function flushGestureAnimations(): void { + if (!enableSwipeTransition) { + return; + } // If we get canceled before we start we might not have applied // mutations yet. We need to apply them first. flushGestureMutations(); From 6daef4e7c8eea99c1b7eb7e451e028c6d7358321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 13:16:42 -0400 Subject: [PATCH 137/300] Make xViewTransitionToHostInstances helpers reusable (#32611) This prepares from being able to reuse some this in ApplyGesture. These all start with resetting a counter but it's tricky to have to remember to do this and tricky to do from the outside of this module. So we make an exported helper that does the resetting. Ideally it gets inlined. We also stop passing "current" to measureViewTransitionHostInstances. Same thing for cancelViewTransitionHostInstances. This doesn't make sense for "nested" which has not updated and so might not have an alternate. Instead we pass in the old and new name if they might be different. --- .../src/ReactFiberCommitViewTransitions.js | 163 +++++++++++------- .../src/ReactFiberCommitWork.js | 14 +- 2 files changed, 110 insertions(+), 67 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 5dcf1498ed18a..09f749bd22a3a 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -71,15 +71,40 @@ export let viewTransitionCancelableChildren: null | Array< Instance | string | Props, > = null; // tupled array where each entry is [instance: Instance, oldName: string, props: Props] -export function setViewTransitionCancelableChildren( - children: null | Array<Instance | string | Props>, +export function pushViewTransitionCancelableScope(): null | Array< + Instance | string | Props, +> { + const prevChildren = viewTransitionCancelableChildren; + viewTransitionCancelableChildren = null; + return prevChildren; +} + +export function popViewTransitionCancelableScope( + prevChildren: null | Array<Instance | string | Props>, ): void { - viewTransitionCancelableChildren = children; + viewTransitionCancelableChildren = prevChildren; } let viewTransitionHostInstanceIdx = 0; -function applyViewTransitionToHostInstances( +export function applyViewTransitionToHostInstances( + child: null | Fiber, + name: string, + className: ?string, + collectMeasurements: null | Array<InstanceMeasurement>, + stopAtNestedViewTransitions: boolean, +): boolean { + viewTransitionHostInstanceIdx = 0; + return applyViewTransitionToHostInstancesRecursive( + child, + name, + className, + collectMeasurements, + stopAtNestedViewTransitions, + ); +} + +function applyViewTransitionToHostInstancesRecursive( child: null | Fiber, name: string, className: ?string, @@ -128,7 +153,7 @@ function applyViewTransitionToHostInstances( // inner most one is the one that handles the update. } else { if ( - applyViewTransitionToHostInstances( + applyViewTransitionToHostInstancesRecursive( child.child, name, className, @@ -207,7 +232,6 @@ function commitAppearingPairViewTransitions(placement: Fiber): void { if (className !== 'none') { // We found a new appearing view transition with the same name as this deletion. // We'll transition between them. - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( child.child, name, @@ -242,7 +266,6 @@ export function commitEnterViewTransitions(placement: Fiber): void { state.paired ? props.share : props.enter, ); if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( placement.child, name, @@ -310,7 +333,6 @@ function commitDeletedPairViewTransitions(deletion: Fiber): void { ); if (className !== 'none') { // We found a new appearing view transition with the same name as this deletion. - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( child.child, name, @@ -361,7 +383,6 @@ export function commitExitViewTransitions(deletion: Fiber): void { pair !== undefined ? props.share : props.exit, ); if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( deletion.child, name, @@ -449,7 +470,6 @@ export function commitBeforeUpdateViewTransition( return; } } - viewTransitionHostInstanceIdx = 0; applyViewTransitionToHostInstances( current.child, oldName, @@ -472,7 +492,6 @@ export function commitNestedViewTransitions(changedParent: Fiber): void { props.layout, ); if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; applyViewTransitionToHostInstances( child.child, name, @@ -553,9 +572,22 @@ export function restoreNestedViewTransitions(changedParent: Fiber): void { } } -function cancelViewTransitionHostInstances( - currentViewTransition: Fiber, +export function cancelViewTransitionHostInstances( child: null | Fiber, + oldName: string, + stopAtNestedViewTransitions: boolean, +): void { + viewTransitionHostInstanceIdx = 0; + cancelViewTransitionHostInstancesRecursive( + child, + oldName, + stopAtNestedViewTransitions, + ); +} + +function cancelViewTransitionHostInstancesRecursive( + child: null | Fiber, + oldName: string, stopAtNestedViewTransitions: boolean, ): void { if (!supportsMutation) { @@ -564,10 +596,6 @@ function cancelViewTransitionHostInstances( while (child !== null) { if (child.tag === HostComponent) { const instance: Instance = child.stateNode; - const oldName = getViewTransitionName( - currentViewTransition.memoizedProps, - currentViewTransition.stateNode, - ); if (viewTransitionCancelableChildren === null) { viewTransitionCancelableChildren = []; } @@ -589,9 +617,9 @@ function cancelViewTransitionHostInstances( // Skip any nested view transitions for updates since in that case the // inner most one is the one that handles the update. } else { - cancelViewTransitionHostInstances( - currentViewTransition, + cancelViewTransitionHostInstancesRecursive( child.child, + oldName, stopAtNestedViewTransitions, ); } @@ -599,11 +627,32 @@ function cancelViewTransitionHostInstances( } } -function measureViewTransitionHostInstances( - currentViewTransition: Fiber, +export function measureViewTransitionHostInstances( parentViewTransition: Fiber, child: null | Fiber, - name: string, + newName: string, + oldName: string, + className: ?string, + previousMeasurements: null | Array<InstanceMeasurement>, + stopAtNestedViewTransitions: boolean, +): boolean { + viewTransitionHostInstanceIdx = 0; + return measureViewTransitionHostInstancesRecursive( + parentViewTransition, + child, + newName, + oldName, + className, + previousMeasurements, + stopAtNestedViewTransitions, + ); +} + +function measureViewTransitionHostInstancesRecursive( + parentViewTransition: Fiber, + child: null | Fiber, + newName: string, + oldName: string, className: ?string, previousMeasurements: null | Array<InstanceMeasurement>, stopAtNestedViewTransitions: boolean, @@ -654,10 +703,10 @@ function measureViewTransitionHostInstances( applyViewTransitionName( instance, viewTransitionHostInstanceIdx === 0 - ? name + ? newName : // If we have multiple Host Instances below, we add a suffix to the name to give // each one a unique name. - name + '_' + viewTransitionHostInstanceIdx, + newName + '_' + viewTransitionHostInstanceIdx, className, ); } @@ -667,10 +716,6 @@ function measureViewTransitionHostInstances( // animating it. However, in the current model this only works if the parent also // doesn't animate. So we have to queue these and wait until we complete the parent // to cancel them. - const oldName = getViewTransitionName( - currentViewTransition.memoizedProps, - currentViewTransition.stateNode, - ); if (viewTransitionCancelableChildren === null) { viewTransitionCancelableChildren = []; } @@ -696,11 +741,11 @@ function measureViewTransitionHostInstances( parentViewTransition.flags |= child.flags & AffectedParentLayout; } else { if ( - measureViewTransitionHostInstances( - currentViewTransition, + measureViewTransitionHostInstancesRecursive( parentViewTransition, child.child, - name, + newName, + oldName, className, previousMeasurements, stopAtNestedViewTransitions, @@ -719,6 +764,11 @@ export function measureUpdateViewTransition( finishedWork: Fiber, ): boolean { const props: ViewTransitionProps = finishedWork.memoizedProps; + const newName = getViewTransitionName(props, finishedWork.stateNode); + const oldName = getViewTransitionName( + current.memoizedProps, + current.stateNode, + ); const updateClassName: ?string = getViewTransitionClassName( props.className, props.update, @@ -745,24 +795,21 @@ export function measureUpdateViewTransition( if (layoutClassName === 'none') { // If we did not update, then all changes are considered a layout. We'll // attempt to cancel. - viewTransitionHostInstanceIdx = 0; - cancelViewTransitionHostInstances(current, finishedWork.child, true); + cancelViewTransitionHostInstances(finishedWork.child, oldName, true); return false; } // We didn't update but we might still apply layout so we measure each // instance to see if it moved or resized. className = layoutClassName; } - const name = getViewTransitionName(props, finishedWork.stateNode); // If nothing changed due to a mutation, or children changing size // and the measurements end up unchanged, we should restore it to not animate. - viewTransitionHostInstanceIdx = 0; const previousMeasurements = current.memoizedState; const inViewport = measureViewTransitionHostInstances( - current, finishedWork, finishedWork.child, - name, + newName, + oldName, className, previousMeasurements, true, @@ -782,29 +829,25 @@ export function measureNestedViewTransitions(changedParent: Fiber): void { let child = changedParent.child; while (child !== null) { if (child.tag === ViewTransitionComponent) { - const current = child.alternate; - if (current !== null) { - const props: ViewTransitionProps = child.memoizedProps; - const name = getViewTransitionName(props, child.stateNode); - const className: ?string = getViewTransitionClassName( - props.className, - props.layout, - ); - viewTransitionHostInstanceIdx = 0; - const inViewport = measureViewTransitionHostInstances( - current, - child, - child.child, - name, - className, - child.memoizedState, - false, - ); - if ((child.flags & Update) === NoFlags || !inViewport) { - // Nothing changed. - } else { - scheduleViewTransitionEvent(child, props.onLayout); - } + const props: ViewTransitionProps = child.memoizedProps; + const name = getViewTransitionName(props, child.stateNode); + const className: ?string = getViewTransitionClassName( + props.className, + props.layout, + ); + const inViewport = measureViewTransitionHostInstances( + child, + child.child, + name, + name, // Since this is unchanged, new and old name is the same. + className, + child.memoizedState, + false, + ); + if ((child.flags & Update) === NoFlags || !inViewport) { + // Nothing changed. + } else { + scheduleViewTransitionEvent(child, props.onLayout); } } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { measureNestedViewTransitions(child); diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index bcf7fc188ee46..2663c1e087542 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -254,7 +254,8 @@ import { resetAppearingViewTransitions, trackAppearingViewTransition, viewTransitionCancelableChildren, - setViewTransitionCancelableChildren, + pushViewTransitionCancelableScope, + popViewTransitionCancelableScope, } from './ReactFiberCommitViewTransitions'; import { viewTransitionMutationContext, @@ -2474,14 +2475,14 @@ function commitAfterMutationEffectsOnFiber( switch (finishedWork.tag) { case HostRoot: { viewTransitionContextChanged = false; - setViewTransitionCancelableChildren(null); + pushViewTransitionCancelableScope(); recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); if (!viewTransitionContextChanged) { // If we didn't leak any resizing out to the root, we don't have to transition // the root itself. This means that we can now safely cancel any cancellations // that bubbled all the way up. const cancelableChildren = viewTransitionCancelableChildren; - setViewTransitionCancelableChildren(null); + popViewTransitionCancelableScope(null); if (cancelableChildren !== null) { for (let i = 0; i < cancelableChildren.length; i += 3) { cancelViewTransitionName( @@ -2532,9 +2533,8 @@ function commitAfterMutationEffectsOnFiber( const wasMutated = (finishedWork.flags & Update) !== NoFlags; const prevContextChanged = viewTransitionContextChanged; - const prevCancelableChildren = viewTransitionCancelableChildren; + const prevCancelableChildren = pushViewTransitionCancelableScope(); viewTransitionContextChanged = false; - setViewTransitionCancelableChildren(null); recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); if (viewTransitionContextChanged) { @@ -2557,7 +2557,7 @@ function commitAfterMutationEffectsOnFiber( prevCancelableChildren, viewTransitionCancelableChildren, ); - setViewTransitionCancelableChildren(prevCancelableChildren); + popViewTransitionCancelableScope(prevCancelableChildren); } // TODO: If this doesn't end up canceled, because a parent animates, // then we should probably issue an event since this instance is part of it. @@ -2571,7 +2571,7 @@ function commitAfterMutationEffectsOnFiber( ); // If this boundary did update, we cannot cancel its children so those are dropped. - setViewTransitionCancelableChildren(prevCancelableChildren); + popViewTransitionCancelableScope(prevCancelableChildren); } if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) { From c4a3b92e098cf1896939758e7419cbdb0e2f0cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 13:20:17 -0400 Subject: [PATCH 138/300] Add more phases to the ReactFiberApplyGesture (#32578) Stacked on #32585 and #32605. This adds more loops for the phases of "Apply Gesture". It doesn't implement the interesting bit yet like adding view-transition-names and measurements. I'll do that in a separate PR to keep reviewing easier. The three phases of this approach is roughly: - Clone and apply names to the "old" state. - Inside startViewTransition: Apply names to the "new" state. Measure both the "old" and "new" state to know whether to cancel some of them. Delete the clones which will include all the "old" names. - After startViewTransition: Restore "new" names back to no view-transition-name. Since we don't have any other Effects in these phases we have a bit more flexibility and we can avoid extra phases that traverse the tree. I've tried to avoid any additional passes. An interesting consequence of this approach is that we could measure both the "old" and "new" state before `startViewTransition`. This would be more efficient because we wouldn't need to take View Transition snapshots of parts of the tree that won't actually animate. However, that would require an extra pass and force layout earlier. It would also have different semantics from the fire-and-forget View Transitions because we could optimize better which can be visible. It would also not account for any late mutations. So I decided to instead let the layout be computed by painting as usual and then measure both "old" and "new" inside the startViewTransition instead. Then canceling anything that doesn't animate to keep it consistent. Unfortunately, though there's not a lot of code sharing possible in these phases because the strategy is so different with the cloning and because the animation is performed in reverse. The "finishedWork" Fiber represents the "old" state and the "current" Fiber represents the "new" state. The most complicated phase is the cloning. I actually ended up having to make a very different pattern from the other phases and CommitWork in general. Because we have to clone as we go and also do other things like apply names and finding pairs, it has more phases. I ended up with an approach that uses three different loops. The outer one for updated trees, one for inserted trees that don't need cloning (doesn't include reappearing offscreen) and one for not updated trees that still need cloning. Inside each loop it can also be in different phases which I track with the `visitPhase` enum - this pattern is kind of new. Additionally, we need to measure the cloned nodes after we've applied mutations to them and we have to wait until the whole tree is inserted. We don't have a reference to these DOM elements in the Fiber tree since that still refers to the original ones. We need to store the cloned elements somewhere. So I added a temporary field on the ViewTransitionState to keep track of any clones owned by that ViewTransition. When we deep clone an unchanged subtree we don't have DOM element instances. It wouldn't be quite safe to try to find them from the tree structure. So we need to avoid the deep clones if we might need DOM elements. Therefore we keep traversing in the case where we need to find nested ViewTransition boundaries that are either potentially affected by layout or a "pair". For the other two phases the pattern there's a lot of code duplication since it's slightly different from the commit ones but they at least follow the same pattern. For the restore phase I was actually able to reuse most of the code. I don't love how much code this is. --- packages/react-reconciler/src/ReactFiber.js | 1 + .../src/ReactFiberApplyGesture.js | 831 +++++++++++++++--- .../src/ReactFiberViewTransitionComponent.js | 3 +- 3 files changed, 722 insertions(+), 113 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 70cb3ed9de023..f6011a3ba155c 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -878,6 +878,7 @@ export function createFiberFromViewTransition( const instance: ViewTransitionState = { autoName: null, paired: null, + clones: null, ref: null, }; fiber.stateNode = instance; diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js index 6847a2426c5ac..a83c52c57cad8 100644 --- a/packages/react-reconciler/src/ReactFiberApplyGesture.js +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -13,6 +13,11 @@ import type {Instance, TextInstance} from './ReactFiberConfig'; import type {OffscreenState} from './ReactFiberActivityComponent'; +import type { + ViewTransitionState, + ViewTransitionProps, +} from './ReactFiberViewTransitionComponent'; + import { cloneMutableInstance, cloneMutableTextInstance, @@ -40,6 +45,8 @@ import { ContentReset, NoFlags, Visibility, + ViewTransitionNamedStatic, + ViewTransitionStatic, } from './ReactFiberFlags'; import { HostComponent, @@ -50,6 +57,10 @@ import { OffscreenComponent, ViewTransitionComponent, } from './ReactWorkTags'; +import { + restoreEnterOrExitViewTransitions, + restoreNestedViewTransitions, +} from './ReactFiberCommitViewTransitions'; let didWarnForRootClone = false; @@ -57,26 +68,284 @@ function detectMutationOrInsertClones(finishedWork: Fiber): boolean { return true; } -let unhideHostChildren = false; +const CLONE_UPDATE = 0; // Mutations in this subtree or potentially affected by layout. +const CLONE_EXIT = 1; // Inside a reappearing offscreen before the next ViewTransition or HostComponent. +const CLONE_UNHIDE = 2; // Inside a reappearing offscreen before the next HostComponent. +const CLONE_APPEARING_PAIR = 3; // Like UNHIDE but we're already inside the first Host Component only finding pairs. +const CLONE_UNCHANGED = 4; // Nothing in this tree was changed but we're still walking to clone it. +const INSERT_EXIT = 5; // Inside a newly mounted tree before the next ViewTransition or HostComponent. +const INSERT_APPEND = 6; // Inside a newly mounted tree before the next HostComponent. +const INSERT_APPEARING_PAIR = 7; // Inside a newly mounted tree only finding pairs. +type VisitPhase = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; + +function trackDeletedPairViewTransitions(deletion: Fiber): void { + if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { + // This has no named view transitions in its subtree. + return; + } + let child = deletion.child; + while (child !== null) { + if (child.tag === OffscreenComponent && child.memoizedState === null) { + // This tree was already hidden so we skip it. + } else { + if ( + child.tag === ViewTransitionComponent && + (child.flags & ViewTransitionNamedStatic) !== NoFlags + ) { + const props: ViewTransitionProps = child.memoizedProps; + const name = props.name; + if (name != null && name !== 'auto') { + // TODO: Find a pair + } + } + trackDeletedPairViewTransitions(child); + } + child = child.sibling; + } +} + +function trackEnterViewTransitions(deletion: Fiber): void { + if (deletion.tag === ViewTransitionComponent) { + const props: ViewTransitionProps = deletion.memoizedProps; + const name = props.name; + if (name != null && name !== 'auto') { + // TODO: Find a pair + } + // Look for more pairs deeper in the tree. + trackDeletedPairViewTransitions(deletion); + } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + let child = deletion.child; + while (child !== null) { + trackEnterViewTransitions(child); + child = child.sibling; + } + } else { + trackDeletedPairViewTransitions(deletion); + } +} + +function recursivelyInsertNew( + parentFiber: Fiber, + hostParentClone: Instance, + parentViewTransition: null | ViewTransitionState, + visitPhase: VisitPhase, +): void { + if ( + visitPhase === INSERT_APPEARING_PAIR && + parentViewTransition === null && + (parentFiber.subtreeFlags & ViewTransitionNamedStatic) === NoFlags + ) { + // We're just searching for pairs but we have reached the end. + return; + } + let child = parentFiber.child; + while (child !== null) { + recursivelyInsertNewFiber( + child, + hostParentClone, + parentViewTransition, + visitPhase, + ); + child = child.sibling; + } +} + +function recursivelyInsertNewFiber( + finishedWork: Fiber, + hostParentClone: Instance, + parentViewTransition: null | ViewTransitionState, + visitPhase: VisitPhase, +): void { + switch (finishedWork.tag) { + case HostHoistable: { + if (supportsResources) { + // TODO: Hoistables should get optimistically inserted and then removed. + recursivelyInsertNew( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); + break; + } + // Fall through + } + case HostSingleton: { + if (supportsSingletons) { + recursivelyInsertNew( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); + + if (__DEV__) { + // We cannot apply mutations to Host Singletons since by definition + // they cannot be cloned. Therefore we warn in DEV if this commit + // had any effect. + if (finishedWork.flags & Update) { + console.error( + 'useSwipeTransition() caused something to render a new <%s>. ' + + 'This is not possible in the current implementation. ' + + "Make sure that the swipe doesn't mount any new <%s> elements.", + finishedWork.type, + finishedWork.type, + ); + } + } + break; + } + // Fall through + } + case HostComponent: { + const instance: Instance = finishedWork.stateNode; + // For insertions we don't need to clone. It's already new state node. + if (visitPhase !== INSERT_APPEARING_PAIR) { + appendChild(hostParentClone, instance); + recursivelyInsertNew( + finishedWork, + instance, + null, + INSERT_APPEARING_PAIR, + ); + } else { + recursivelyInsertNew(finishedWork, instance, null, visitPhase); + } + if (parentViewTransition !== null) { + if (parentViewTransition.clones === null) { + parentViewTransition.clones = [instance]; + } else { + parentViewTransition.clones.push(instance); + } + } + break; + } + case HostText: { + const textInstance: TextInstance = finishedWork.stateNode; + if (textInstance === null) { + throw new Error( + 'This should have a text node initialized. This error is likely ' + + 'caused by a bug in React. Please file an issue.', + ); + } + // For insertions we don't need to clone. It's already new state node. + if (visitPhase !== INSERT_APPEARING_PAIR) { + appendChild(hostParentClone, textInstance); + } + break; + } + case HostPortal: { + // TODO: Consider what should happen to Portals. For now we exclude them. + break; + } + case OffscreenComponent: { + const newState: OffscreenState | null = finishedWork.memoizedState; + const isHidden = newState !== null; + if (!isHidden) { + // Only insert nodes if this tree is going to be visible. No need to + // insert invisible content. + // Since there was no mutation to this node, it couldn't have changed + // visibility so we don't need to update visitPhase here. + recursivelyInsertNew( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); + } + break; + } + case ViewTransitionComponent: + const prevMutationContext = pushMutationContext(); + const viewTransitionState: ViewTransitionState = finishedWork.stateNode; + // TODO: If this was already cloned by a previous pass we can reuse those clones. + viewTransitionState.clones = null; + let nextPhase; + if (visitPhase === INSERT_EXIT) { + // This was an Enter of a ViewTransition. We now move onto inserting the inner + // HostComponents and finding inner pairs. + nextPhase = INSERT_APPEND; + // TODO: Mark the name and find a pair. + } else { + nextPhase = visitPhase; + } + recursivelyInsertNew( + finishedWork, + hostParentClone, + viewTransitionState, + nextPhase, + ); + popMutationContext(prevMutationContext); + break; + default: { + recursivelyInsertNew( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); + break; + } + } +} function recursivelyInsertClonesFromExistingTree( parentFiber: Fiber, hostParentClone: Instance, + parentViewTransition: null | ViewTransitionState, + visitPhase: VisitPhase, ): void { let child = parentFiber.child; while (child !== null) { switch (child.tag) { case HostComponent: { const instance: Instance = child.stateNode; - // If we have no mutations in this subtree, we just need to make a deep clone. - const clone: Instance = cloneMutableInstance(instance, true); + let nextPhase: VisitPhase; + switch (visitPhase) { + case CLONE_EXIT: + case CLONE_UNHIDE: + case CLONE_APPEARING_PAIR: + // If this was an unhide, we need to keep going if there are any named + // pairs in this subtree, since they might need to be marked. + nextPhase = + (child.subtreeFlags & ViewTransitionNamedStatic) !== NoFlags + ? CLONE_APPEARING_PAIR + : CLONE_UNCHANGED; + break; + default: + // We've found any "layout" View Transitions at this point so we can bail. + nextPhase = CLONE_UNCHANGED; + } + let clone: Instance; + if (nextPhase !== CLONE_UNCHANGED) { + // We might need a handle on these clones, so we need to do a shallow clone + // and keep going. + clone = cloneMutableInstance(instance, false); + recursivelyInsertClonesFromExistingTree( + child, + clone, + null, + nextPhase, + ); + } else { + // If we have no mutations in this subtree, and we don't need a handle on the + // clones, then we can do a deep clone instead and bailout. + clone = cloneMutableInstance(instance, true); + // TODO: We may need to transfer some DOM state such as scroll position + // for the deep clones. + // TODO: If there's a manual view-transition-name inside the clone we + // should ideally remove it from the original and then restore it in mutation + // phase. Otherwise it leads to duplicate names. + } appendChild(hostParentClone, clone); - // TODO: We may need to transfer some DOM state such as scroll position - // for the deep clones. - // TODO: If there's a manual view-transition-name inside the clone we - // should ideally remove it from the original and then restore it in mutation - // phase. Otherwise it leads to duplicate names. - if (unhideHostChildren) { + if (parentViewTransition !== null) { + if (parentViewTransition.clones === null) { + parentViewTransition.clones = [clone]; + } else { + parentViewTransition.clones.push(clone); + } + } + if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { unhideInstance(clone, child.memoizedProps); } break; @@ -91,7 +360,7 @@ function recursivelyInsertClonesFromExistingTree( } const clone = cloneMutableTextInstance(textInstance); appendChild(hostParentClone, clone); - if (unhideHostChildren) { + if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { unhideTextInstance(clone, child.memoizedProps); } break; @@ -108,21 +377,52 @@ function recursivelyInsertClonesFromExistingTree( // clone invisible content. // TODO: If this is visible but detached it should still be cloned. // Since there was no mutation to this node, it couldn't have changed - // visibility so we don't need to update unhideHostChildren here. - recursivelyInsertClonesFromExistingTree(child, hostParentClone); + // visibility so we don't need to update visitPhase here. + recursivelyInsertClonesFromExistingTree( + child, + hostParentClone, + parentViewTransition, + visitPhase, + ); } break; } case ViewTransitionComponent: const prevMutationContext = pushMutationContext(); + const viewTransitionState: ViewTransitionState = child.stateNode; // TODO: If this was already cloned by a previous pass we can reuse those clones. - recursivelyInsertClonesFromExistingTree(child, hostParentClone); - // TODO: Do we need to track whether this should have a name applied? + viewTransitionState.clones = null; + let nextPhase; + if (visitPhase === CLONE_EXIT) { + // This was an Enter of a ViewTransition. We now move onto unhiding the inner + // HostComponents and finding inner pairs. + nextPhase = CLONE_UNHIDE; + // TODO: Mark the name and find a pair. + } else if (visitPhase === CLONE_UPDATE) { + // If the tree had no mutations and we've found the top most ViewTransition + // then this is the one we might apply the "layout" state too if it has changed + // position. After we've found its HostComponents we can bail out. + nextPhase = CLONE_UNCHANGED; + } else { + nextPhase = visitPhase; + } + recursivelyInsertClonesFromExistingTree( + child, + hostParentClone, + viewTransitionState, + nextPhase, + ); + // TODO: Only the first level should track if this was s // child.flags |= Update; popMutationContext(prevMutationContext); break; default: { - recursivelyInsertClonesFromExistingTree(child, hostParentClone); + recursivelyInsertClonesFromExistingTree( + child, + hostParentClone, + parentViewTransition, + visitPhase, + ); break; } } @@ -133,12 +433,14 @@ function recursivelyInsertClonesFromExistingTree( function recursivelyInsertClones( parentFiber: Fiber, hostParentClone: Instance, + parentViewTransition: null | ViewTransitionState, + visitPhase: VisitPhase, ) { const deletions = parentFiber.deletions; if (deletions !== null) { for (let i = 0; i < deletions.length; i++) { - // const childToDelete = deletions[i]; - // TODO + const childToDelete = deletions[i]; + trackEnterViewTransitions(childToDelete); } } @@ -149,21 +451,45 @@ function recursivelyInsertClones( // If we have mutations or if this is a newly inserted tree, clone as we go. let child = parentFiber.child; while (child !== null) { - insertDestinationClonesOfFiber(child, hostParentClone); + insertDestinationClonesOfFiber( + child, + hostParentClone, + parentViewTransition, + visitPhase, + ); child = child.sibling; } } else { // Once we reach a subtree with no more mutations we can bail out. // However, we must still insert deep clones of the HostComponents. - recursivelyInsertClonesFromExistingTree(parentFiber, hostParentClone); + recursivelyInsertClonesFromExistingTree( + parentFiber, + hostParentClone, + parentViewTransition, + visitPhase, + ); } } function insertDestinationClonesOfFiber( finishedWork: Fiber, hostParentClone: Instance, + parentViewTransition: null | ViewTransitionState, + visitPhase: VisitPhase, ) { const current = finishedWork.alternate; + if (current === null) { + // This is a newly mounted subtree. Insert any HostComponents and trigger + // Enter transitions. + recursivelyInsertNewFiber( + finishedWork, + hostParentClone, + parentViewTransition, + INSERT_EXIT, + ); + return; + } + const flags = finishedWork.flags; // The effect flag should be checked *after* we refine the type of fiber, // because the fiber tag is more specific. An exception is any flag related @@ -172,55 +498,55 @@ function insertDestinationClonesOfFiber( case HostHoistable: { if (supportsResources) { // TODO: Hoistables should get optimistically inserted and then removed. - recursivelyInsertClones(finishedWork, hostParentClone); + recursivelyInsertClones( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); break; } // Fall through } case HostSingleton: { if (supportsSingletons) { - recursivelyInsertClones(finishedWork, hostParentClone); + recursivelyInsertClones( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); if (__DEV__) { // We cannot apply mutations to Host Singletons since by definition // they cannot be cloned. Therefore we warn in DEV if this commit // had any effect. if (flags & Update) { - if (current === null) { - console.error( - 'useSwipeTransition() caused something to render a new <%s>. ' + - 'This is not possible in the current implementation. ' + - "Make sure that the swipe doesn't mount any new <%s> elements.", - finishedWork.type, - finishedWork.type, - ); - } else { - const newProps = finishedWork.memoizedProps; - const oldProps = current.memoizedProps; - const instance = finishedWork.stateNode; - const type = finishedWork.type; - const prev = pushMutationContext(); - - try { - // Since we currently don't have a separate diffing algorithm for - // individual properties, the Update flag can be a false positive. - // We have to apply the new props first o detect any mutations and - // then revert them. - commitUpdate(instance, type, oldProps, newProps, finishedWork); - if (viewTransitionMutationContext) { - console.error( - 'useSwipeTransition() caused something to mutate <%s>. ' + - 'This is not possible in the current implementation. ' + - "Make sure that the swipe doesn't update any state which " + - 'causes <%s> to change.', - finishedWork.type, - finishedWork.type, - ); - } - // Revert - commitUpdate(instance, type, newProps, oldProps, finishedWork); - } finally { - popMutationContext(prev); + const newProps = finishedWork.memoizedProps; + const oldProps = current.memoizedProps; + const instance = finishedWork.stateNode; + const type = finishedWork.type; + const prev = pushMutationContext(); + + try { + // Since we currently don't have a separate diffing algorithm for + // individual properties, the Update flag can be a false positive. + // We have to apply the new props first o detect any mutations and + // then revert them. + commitUpdate(instance, type, oldProps, newProps, finishedWork); + if (viewTransitionMutationContext) { + console.error( + 'useSwipeTransition() caused something to mutate <%s>. ' + + 'This is not possible in the current implementation. ' + + "Make sure that the swipe doesn't update any state which " + + 'causes <%s> to change.', + finishedWork.type, + finishedWork.type, + ); } + // Revert + commitUpdate(instance, type, newProps, oldProps, finishedWork); + } finally { + popMutationContext(prev); } } } @@ -230,42 +556,46 @@ function insertDestinationClonesOfFiber( } case HostComponent: { const instance: Instance = finishedWork.stateNode; - if (current === null) { - // For insertions we don't need to clone. It's already new state node. - // TODO: Do we need to visit it for ViewTransitions though? - appendChild(hostParentClone, instance); - } else { - let clone: Instance; - if (finishedWork.child === null) { - // This node is terminal. We still do a deep clone in case this has user - // inserted content, text content or dangerouslySetInnerHTML. - clone = cloneMutableInstance(instance, true); - if (finishedWork.flags & ContentReset) { - resetTextContent(clone); - } - } else { - // If we have children we'll clone them as we walk the tree so we just - // do a shallow clone here. - clone = cloneMutableInstance(instance, false); + let clone: Instance; + if (finishedWork.child === null) { + // This node is terminal. We still do a deep clone in case this has user + // inserted content, text content or dangerouslySetInnerHTML. + clone = cloneMutableInstance(instance, true); + if (finishedWork.flags & ContentReset) { + resetTextContent(clone); } + } else { + // If we have children we'll clone them as we walk the tree so we just + // do a shallow clone here. + clone = cloneMutableInstance(instance, false); + } - if (flags & Update) { - const newProps = finishedWork.memoizedProps; - const oldProps = current.memoizedProps; - const type = finishedWork.type; - // Apply the delta to the clone. - commitUpdate(clone, type, oldProps, newProps, finishedWork); - } + if (flags & Update) { + const newProps = finishedWork.memoizedProps; + const oldProps = current.memoizedProps; + const type = finishedWork.type; + // Apply the delta to the clone. + commitUpdate(clone, type, oldProps, newProps, finishedWork); + } - if (unhideHostChildren) { - unhideHostChildren = false; - recursivelyInsertClones(finishedWork, clone); - appendChild(hostParentClone, clone); - unhideHostChildren = true; - unhideInstance(clone, finishedWork.memoizedProps); + if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { + recursivelyInsertClones( + finishedWork, + clone, + null, + CLONE_APPEARING_PAIR, + ); + appendChild(hostParentClone, clone); + unhideInstance(clone, finishedWork.memoizedProps); + } else { + recursivelyInsertClones(finishedWork, clone, null, visitPhase); + appendChild(hostParentClone, clone); + } + if (parentViewTransition !== null) { + if (parentViewTransition.clones === null) { + parentViewTransition.clones = [clone]; } else { - recursivelyInsertClones(finishedWork, clone); - appendChild(hostParentClone, clone); + parentViewTransition.clones.push(clone); } } break; @@ -278,20 +608,15 @@ function insertDestinationClonesOfFiber( 'caused by a bug in React. Please file an issue.', ); } - if (current === null) { - // For insertions we don't need to clone. It's already new state node. - appendChild(hostParentClone, textInstance); - } else { - const clone = cloneMutableTextInstance(textInstance); - if (flags & Update) { - const newText: string = finishedWork.memoizedProps; - const oldText: string = current.memoizedProps; - commitTextUpdate(clone, newText, oldText); - } - appendChild(hostParentClone, clone); - if (unhideHostChildren) { - unhideTextInstance(clone, finishedWork.memoizedProps); - } + const clone = cloneMutableTextInstance(textInstance); + if (flags & Update) { + const newText: string = finishedWork.memoizedProps; + const oldText: string = current.memoizedProps; + commitTextUpdate(clone, newText, oldText); + } + appendChild(hostParentClone, clone); + if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { + unhideTextInstance(clone, finishedWork.memoizedProps); } break; } @@ -306,17 +631,45 @@ function insertDestinationClonesOfFiber( // Only insert clones if this tree is going to be visible. No need to // clone invisible content. // TODO: If this is visible but detached it should still be cloned. - const prevUnhide = unhideHostChildren; - unhideHostChildren = prevUnhide || (flags & Visibility) !== NoFlags; - recursivelyInsertClones(finishedWork, hostParentClone); - unhideHostChildren = prevUnhide; + let nextPhase; + if (visitPhase === CLONE_UPDATE && (flags & Visibility) !== NoFlags) { + // This is the root of an appear. We need to trigger Enter transitions. + nextPhase = CLONE_EXIT; + } else { + nextPhase = visitPhase; + } + recursivelyInsertClones( + finishedWork, + hostParentClone, + parentViewTransition, + nextPhase, + ); + } else if (current !== null && current.memoizedState === null) { + // Was previously mounted as visible but is now hidden. + trackEnterViewTransitions(current); } break; } case ViewTransitionComponent: const prevMutationContext = pushMutationContext(); + const viewTransitionState: ViewTransitionState = finishedWork.stateNode; // TODO: If this was already cloned by a previous pass we can reuse those clones. - recursivelyInsertClones(finishedWork, hostParentClone); + viewTransitionState.clones = null; + let nextPhase; + if (visitPhase === CLONE_EXIT) { + // This was an Enter of a ViewTransition. We now move onto unhiding the inner + // HostComponents and finding inner pairs. + nextPhase = CLONE_UNHIDE; + // TODO: Mark the name and find a pair. + } else { + nextPhase = visitPhase; + } + recursivelyInsertClones( + finishedWork, + hostParentClone, + viewTransitionState, + nextPhase, + ); if (viewTransitionMutationContext) { // Track that this boundary had a mutation and therefore needs to animate // whether it resized or not. @@ -325,7 +678,12 @@ function insertDestinationClonesOfFiber( popMutationContext(prevMutationContext); break; default: { - recursivelyInsertClones(finishedWork, hostParentClone); + recursivelyInsertClones( + finishedWork, + hostParentClone, + parentViewTransition, + visitPhase, + ); break; } } @@ -337,7 +695,6 @@ export function insertDestinationClones( root: FiberRoot, finishedWork: Fiber, ): void { - unhideHostChildren = false; // We'll either not transition the root, or we'll transition the clone. Regardless // we cancel the root view transition name. const needsClone = detectMutationOrInsertClones(finishedWork); @@ -356,24 +713,274 @@ export function insertDestinationClones( // Clone the whole root const rootClone = cloneRootViewTransitionContainer(root.containerInfo); root.gestureClone = rootClone; - recursivelyInsertClones(finishedWork, rootClone); + recursivelyInsertClones(finishedWork, rootClone, null, CLONE_UPDATE); } else { root.gestureClone = null; cancelRootViewTransitionName(root.containerInfo); } } +function applyDeletedPairViewTransitions(deletion: Fiber): void { + if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { + // This has no named view transitions in its subtree. + return; + } + let child = deletion.child; + while (child !== null) { + if (child.tag === OffscreenComponent && child.memoizedState === null) { + // This tree was already hidden so we skip it. + } else { + if ( + child.tag === ViewTransitionComponent && + (child.flags & ViewTransitionNamedStatic) !== NoFlags + ) { + const props: ViewTransitionProps = child.memoizedProps; + const name = props.name; + if (name != null && name !== 'auto') { + // TODO: Find a pair + } + } + applyDeletedPairViewTransitions(child); + } + child = child.sibling; + } +} + +function applyEnterViewTransitions(deletion: Fiber): void { + if (deletion.tag === ViewTransitionComponent) { + const props: ViewTransitionProps = deletion.memoizedProps; + const name = props.name; + if (name != null && name !== 'auto') { + // TODO: Find a pair + } + // Look for more pairs deeper in the tree. + applyDeletedPairViewTransitions(deletion); + } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + // TODO: Check if this is a hidden Offscreen or a Portal. + let child = deletion.child; + while (child !== null) { + applyEnterViewTransitions(child); + child = child.sibling; + } + } else { + applyDeletedPairViewTransitions(deletion); + } +} + +function measureExitViewTransitions(placement: Fiber): void { + if (placement.tag === ViewTransitionComponent) { + // const state: ViewTransitionState = placement.stateNode; + const props: ViewTransitionProps = placement.memoizedProps; + const name = props.name; + if (name != null && name !== 'auto') { + // TODO: Find a pair + } + } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + // TODO: Check if this is a hidden Offscreen or a Portal. + let child = placement.child; + while (child !== null) { + measureExitViewTransitions(child); + child = child.sibling; + } + } else { + // We don't need to find pairs here because we would've already found and + // measured the pairs inside the deletion phase. + } +} + +function measureNestedViewTransitions(changedParent: Fiber): void { + let child = changedParent.child; + while (child !== null) { + if (child.tag === ViewTransitionComponent) { + const current = child.alternate; + if (current !== null) { + // const props: ViewTransitionProps = child.memoizedProps; + // const name = getViewTransitionName(props, child.stateNode); + // TODO: Measure both the old and new state and see if they're different. + } + } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { + // TODO: Check if this is a hidden Offscreen or a Portal. + measureNestedViewTransitions(child); + } + child = child.sibling; + } +} + +function measureUpdateViewTransition( + current: Fiber, + finishedWork: Fiber, +): void { + // TODO +} + +function recursivelyApplyViewTransitions(parentFiber: Fiber) { + const deletions = parentFiber.deletions; + if (deletions !== null) { + for (let i = 0; i < deletions.length; i++) { + const childToDelete = deletions[i]; + applyEnterViewTransitions(childToDelete); + } + } + + if ( + parentFiber.alternate === null || + (parentFiber.subtreeFlags & MutationMask) !== NoFlags + ) { + // If we have mutations or if this is a newly inserted tree, clone as we go. + let child = parentFiber.child; + while (child !== null) { + applyViewTransitionsOnFiber(child); + child = child.sibling; + } + } else { + // Nothing has changed in this subtree, but the parent may have still affected + // its size and position. We need to measure the old and new state to see if + // we should animate its size and position. + measureNestedViewTransitions(parentFiber); + } +} + +function applyViewTransitionsOnFiber(finishedWork: Fiber) { + const current = finishedWork.alternate; + if (current === null) { + measureExitViewTransitions(finishedWork); + return; + } + + const flags = finishedWork.flags; + // The effect flag should be checked *after* we refine the type of fiber, + // because the fiber tag is more specific. An exception is any flag related + // to reconciliation, because those can be set on all fiber types. + switch (finishedWork.tag) { + case HostComponent: { + // const instance: Instance = finishedWork.stateNode; + // TODO: Apply name and measure. + recursivelyApplyViewTransitions(finishedWork); + break; + } + case HostText: { + break; + } + case HostPortal: { + // TODO: Consider what should happen to Portals. For now we exclude them. + break; + } + case OffscreenComponent: { + if (flags & Visibility) { + const newState: OffscreenState | null = finishedWork.memoizedState; + const isHidden = newState !== null; + if (!isHidden) { + measureExitViewTransitions(finishedWork); + } else if (current !== null && current.memoizedState === null) { + // Was previously mounted as visible but is now hidden. + applyEnterViewTransitions(current); + } + } + break; + } + case ViewTransitionComponent: + measureUpdateViewTransition(current, finishedWork); + const viewTransitionState: ViewTransitionState = finishedWork.stateNode; + viewTransitionState.clones = null; // Reset + recursivelyApplyViewTransitions(finishedWork); + break; + default: { + recursivelyApplyViewTransitions(finishedWork); + break; + } + } +} + // Revert insertions and apply view transition names to the "new" (current) state. export function applyDepartureTransitions( root: FiberRoot, finishedWork: Fiber, ): void { + // First measure and apply view-transition-names to the "new" states. + recursivelyApplyViewTransitions(finishedWork); + // Then remove the clones. const rootClone = root.gestureClone; if (rootClone !== null) { root.gestureClone = null; removeRootViewTransitionClone(root.containerInfo, rootClone); } - // TODO +} + +function recursivelyRestoreViewTransitions(parentFiber: Fiber) { + const deletions = parentFiber.deletions; + if (deletions !== null) { + for (let i = 0; i < deletions.length; i++) { + const childToDelete = deletions[i]; + restoreEnterOrExitViewTransitions(childToDelete); + } + } + + if ( + parentFiber.alternate === null || + (parentFiber.subtreeFlags & MutationMask) !== NoFlags + ) { + // If we have mutations or if this is a newly inserted tree, clone as we go. + let child = parentFiber.child; + while (child !== null) { + restoreViewTransitionsOnFiber(child); + child = child.sibling; + } + } else { + // Nothing has changed in this subtree, but the parent may have still affected + // its size and position. We need to measure the old and new state to see if + // we should animate its size and position. + restoreNestedViewTransitions(parentFiber); + } +} + +function restoreViewTransitionsOnFiber(finishedWork: Fiber) { + const current = finishedWork.alternate; + if (current === null) { + restoreEnterOrExitViewTransitions(finishedWork); + return; + } + + const flags = finishedWork.flags; + // The effect flag should be checked *after* we refine the type of fiber, + // because the fiber tag is more specific. An exception is any flag related + // to reconciliation, because those can be set on all fiber types. + switch (finishedWork.tag) { + case HostComponent: { + // const instance: Instance = finishedWork.stateNode; + // TODO: Restore the name. + recursivelyRestoreViewTransitions(finishedWork); + break; + } + case HostText: { + break; + } + case HostPortal: { + // TODO: Consider what should happen to Portals. For now we exclude them. + break; + } + case OffscreenComponent: { + if (flags & Visibility) { + const newState: OffscreenState | null = finishedWork.memoizedState; + const isHidden = newState !== null; + if (!isHidden) { + restoreEnterOrExitViewTransitions(finishedWork); + } else if (current !== null && current.memoizedState === null) { + // Was previously mounted as visible but is now hidden. + restoreEnterOrExitViewTransitions(current); + } + } + break; + } + case ViewTransitionComponent: + const viewTransitionState: ViewTransitionState = finishedWork.stateNode; + viewTransitionState.clones = null; // Reset + recursivelyRestoreViewTransitions(finishedWork); + break; + default: { + recursivelyRestoreViewTransitions(finishedWork); + break; + } + } } // Revert transition names and start/adjust animations on the started View Transition. @@ -381,6 +988,6 @@ export function startGestureAnimations( root: FiberRoot, finishedWork: Fiber, ): void { - // TODO + restoreViewTransitionsOnFiber(finishedWork); restoreRootViewTransitionName(root.containerInfo); } diff --git a/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js b/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js index 16b5ef304974e..ecacf2a439944 100644 --- a/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js +++ b/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js @@ -9,7 +9,7 @@ import type {ReactNodeList} from 'shared/ReactTypes'; import type {FiberRoot} from './ReactInternalTypes'; -import type {ViewTransitionInstance} from './ReactFiberConfig'; +import type {ViewTransitionInstance, Instance} from './ReactFiberConfig'; import { getWorkInProgressRoot, @@ -45,6 +45,7 @@ export type ViewTransitionProps = { export type ViewTransitionState = { autoName: null | string, // the view-transition-name to use when an explicit one is not specified paired: null | ViewTransitionState, // a temporary state during the commit phase if we have paired this with another instance + clones: null | Array<Instance>, // a temporary state during the apply gesture phase if we cloned this boundary ref: null | ViewTransitionInstance, // the current ref instance. This can change through the lifetime of the instance. }; From 2e385738a4dfd1554c146f6313b1530a09b5ce78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 14:15:39 -0400 Subject: [PATCH 139/300] Find Pairs and Apply View Transition Names to the Clones in the "old" Phase (#32599) Stacked on #32578. We need to apply view-transition-names to the clones that we create in the "old" phase for the ViewTransition boundaries that should activate. Finding pairs is a little trickier than in ReactFiberCommitViewTransitions. Normally we collect all name "insertions" in the `accumulateSuspenseyCommit` phase before we even commit. Then in the snapshot do we visit all "deletions" and since we already collected all the insertions we know immediately if the deletion had a pair and should therefore get a "name" assigned to activate the boundary. For ReactFiberApplyGesture we need to assign names to "insertions" since it's in reverse but we don't already have a map of deletions. Therefore we need to first visit all deletions. Instead of doing that in a completely separate pass, we instead visit deletions in the same pass to find pairs. Since this is in the same pass we might visit insertions before deletions or vice versa depending on document order. However, we can deal with this by applying the name to the insertion when we find the deletion if we've already made the clones at that point. Applying names to pure exits, updates or nested (relayout) is a bit more straight-forward. --- .../src/ReactFiberApplyGesture.js | 262 +++++++++++++++++- .../src/ReactFiberCommitViewTransitions.js | 13 +- 2 files changed, 263 insertions(+), 12 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js index a83c52c57cad8..918a36d66151f 100644 --- a/packages/react-reconciler/src/ReactFiberApplyGesture.js +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -25,6 +25,7 @@ import { removeRootViewTransitionClone, cancelRootViewTransitionName, restoreRootViewTransitionName, + applyViewTransitionName, appendChild, commitUpdate, commitTextUpdate, @@ -60,7 +61,12 @@ import { import { restoreEnterOrExitViewTransitions, restoreNestedViewTransitions, + appearingViewTransitions, } from './ReactFiberCommitViewTransitions'; +import { + getViewTransitionName, + getViewTransitionClassName, +} from './ReactFiberViewTransitionComponent'; let didWarnForRootClone = false; @@ -78,7 +84,37 @@ const INSERT_APPEND = 6; // Inside a newly mounted tree before the next HostComp const INSERT_APPEARING_PAIR = 7; // Inside a newly mounted tree only finding pairs. type VisitPhase = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; +function applyViewTransitionToClones( + name: string, + className: ?string, + clones: Array<Instance>, +): void { + // This gets called when we have found a pair, but after the clone in created. The clone is + // created by the insertion side. If the insertion side if found before the deletion side + // then this is called by the deletion. If the deletion is visited first then this is called + // later by the insertion when the clone has been created. + for (let i = 0; i < clones.length; i++) { + applyViewTransitionName( + clones[i], + i === 0 + ? name + : // If we have multiple Host Instances below, we add a suffix to the name to give + // each one a unique name. + name + '_' + i, + className, + ); + } +} + function trackDeletedPairViewTransitions(deletion: Fiber): void { + if ( + appearingViewTransitions === null || + appearingViewTransitions.size === 0 + ) { + // We've found all. + return; + } + const pairs = appearingViewTransitions; if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { // This has no named view transitions in its subtree. return; @@ -95,7 +131,40 @@ function trackDeletedPairViewTransitions(deletion: Fiber): void { const props: ViewTransitionProps = child.memoizedProps; const name = props.name; if (name != null && name !== 'auto') { - // TODO: Find a pair + const pair = pairs.get(name); + if (pair !== undefined) { + // Delete the entry so that we know when we've found all of them + // and can stop searching (size reaches zero). + pairs.delete(name); + const className: ?string = getViewTransitionClassName( + props.className, + props.share, + ); + if (className !== 'none') { + // TODO: Since the deleted instance already has layout we could + // check if it's in the viewport and if not skip the pairing. + // It would currently cause layout thrash though so if we did that + // we need to avoid inserting the root of the cloned trees until + // the end. + + // The "old" instance is actually the one we're inserting. + const oldInstance: ViewTransitionState = pair; + // The "new" instance is the already mounted one we're deleting. + const newInstance: ViewTransitionState = child.stateNode; + oldInstance.paired = newInstance; + newInstance.paired = oldInstance; + const clones = oldInstance.clones; + if (clones !== null) { + // If we have clones that means that we've already visited this + // ViewTransition boundary before and we can now apply the name + // to those clones. Otherwise, we have to wait until we clone it. + applyViewTransitionToClones(name, className, clones); + } + } + if (pairs.size === 0) { + break; + } + } } } trackDeletedPairViewTransitions(child); @@ -107,9 +176,41 @@ function trackDeletedPairViewTransitions(deletion: Fiber): void { function trackEnterViewTransitions(deletion: Fiber): void { if (deletion.tag === ViewTransitionComponent) { const props: ViewTransitionProps = deletion.memoizedProps; - const name = props.name; - if (name != null && name !== 'auto') { - // TODO: Find a pair + const name = getViewTransitionName(props, deletion.stateNode); + const pair = + appearingViewTransitions !== null + ? appearingViewTransitions.get(name) + : undefined; + const className: ?string = getViewTransitionClassName( + props.className, + pair !== undefined ? props.share : props.enter, + ); + if (className !== 'none') { + if (pair !== undefined) { + // TODO: Since the deleted instance already has layout we could + // check if it's in the viewport and if not skip the pairing. + // It would currently cause layout thrash though so if we did that + // we need to avoid inserting the root of the cloned trees until + // the end. + + // Delete the entry so that we know when we've found all of them + // and can stop searching (size reaches zero). + // $FlowFixMe[incompatible-use]: Refined by the pair. + appearingViewTransitions.delete(name); + // The "old" instance is actually the one we're inserting. + const oldInstance: ViewTransitionState = pair; + // The "new" instance is the already mounted one we're deleting. + const newInstance: ViewTransitionState = deletion.stateNode; + oldInstance.paired = newInstance; + newInstance.paired = oldInstance; + const clones = oldInstance.clones; + if (clones !== null) { + // If we have clones that means that we've already visited this + // ViewTransition boundary before and we can now apply the name + // to those clones. Otherwise, we have to wait until we clone it. + applyViewTransitionToClones(name, className, clones); + } + } } // Look for more pairs deeper in the tree. trackDeletedPairViewTransitions(deletion); @@ -124,6 +225,122 @@ function trackEnterViewTransitions(deletion: Fiber): void { } } +function applyAppearingPairViewTransition(child: Fiber): void { + // Normally these helpers do recursive calls but since insertion/offscreen is forked + // we call this helper from those loops instead. This must be called only on + // ViewTransitionComponent that has already had their clones filled. + if ((child.flags & ViewTransitionNamedStatic) !== NoFlags) { + const state: ViewTransitionState = child.stateNode; + // If this is not yet paired, it doesn't mean that we won't pair it later when + // we find the deletion side. If that's the case then we'll add the names to + // the clones then. + if (state.paired) { + const props: ViewTransitionProps = child.memoizedProps; + if (props.name == null || props.name === 'auto') { + throw new Error( + 'Found a pair with an auto name. This is a bug in React.', + ); + } + const name = props.name; + // Note that this class name that doesn't actually really matter because the + // "new" side will be the one that wins in practice. + const className: ?string = getViewTransitionClassName( + props.className, + props.share, + ); + if (className !== 'none') { + const clones = state.clones; + // If there are no clones at this point, that should mean that there are no + // HostComponent children in this ViewTransition. + if (clones !== null) { + applyViewTransitionToClones(name, className, clones); + } + } + } + } +} + +function applyExitViewTransition(placement: Fiber): void { + // Normally these helpers do recursive calls but since insertion/offscreen is forked + // we call this helper from those loops instead. This must be called only on + // ViewTransitionComponent that has already had their clones filled. + const state: ViewTransitionState = placement.stateNode; + const props: ViewTransitionProps = placement.memoizedProps; + const name = getViewTransitionName(props, state); + const className: ?string = getViewTransitionClassName( + props.className, + // Note that just because we don't have a pair yet doesn't mean we won't find one + // later. However, that doesn't matter because if we do the class name that wins + // is the one applied by the "new" side anyway. + state.paired ? props.share : props.exit, + ); + if (className !== 'none') { + // TODO: Ideally we could determine if this exit is in the viewport and + // exclude it otherwise but that would require waiting until we insert + // and layout the clones first. Currently wait until the view transition + // starts before reading the layout. + const clones = state.clones; + // If there are no clones at this point, that should mean that there are no + // HostComponent children in this ViewTransition. + if (clones !== null) { + applyViewTransitionToClones(name, className, clones); + } + } +} + +function applyNestedViewTransition(child: Fiber): void { + const state: ViewTransitionState = child.stateNode; + const props: ViewTransitionProps = child.memoizedProps; + const name = getViewTransitionName(props, state); + const className: ?string = getViewTransitionClassName( + props.className, + props.layout, + ); + if (className !== 'none') { + const clones = state.clones; + // If there are no clones at this point, that should mean that there are no + // HostComponent children in this ViewTransition. + if (clones !== null) { + applyViewTransitionToClones(name, className, clones); + } + } +} + +function applyUpdateViewTransition(current: Fiber, finishedWork: Fiber): void { + const state: ViewTransitionState = finishedWork.stateNode; + // Updates can have conflicting names and classNames. + // Since we're doing a reverse animation the "new" state is actually the current + // and the "old" state is the finishedWork. + const newProps: ViewTransitionProps = current.memoizedProps; + const oldProps: ViewTransitionProps = finishedWork.memoizedProps; + const oldName = getViewTransitionName(oldProps, state); + // This className applies only if there are fewer child DOM nodes than + // before or if this update should've been cancelled but we ended up with + // a parent animating so we need to animate the child too. Otherwise + // the "new" state wins. Since "new" normally wins, that's usually what + // we would use. However, since this animation is going in reverse we actually + // want the props from "current" since that's the class that would've won if + // it was the normal direction. To preserve the same effect in either direction. + let className: ?string = getViewTransitionClassName( + newProps.className, + newProps.update, + ); + if (className === 'none') { + className = getViewTransitionClassName(newProps.className, newProps.layout); + if (className === 'none') { + // If both update and layout are both "none" then we don't have to + // apply a name. Since we won't animate this boundary. + return; + } + } + const clones = state.clones; + // If there are no clones at this point, that should mean that there are no + // HostComponent children in this ViewTransition. + if (clones !== null) { + applyViewTransitionToClones(oldName, className, clones); + } +} + function recursivelyInsertNew( parentFiber: Fiber, hostParentClone: Instance, @@ -265,7 +482,6 @@ function recursivelyInsertNewFiber( // This was an Enter of a ViewTransition. We now move onto inserting the inner // HostComponents and finding inner pairs. nextPhase = INSERT_APPEND; - // TODO: Mark the name and find a pair. } else { nextPhase = visitPhase; } @@ -275,6 +491,16 @@ function recursivelyInsertNewFiber( viewTransitionState, nextPhase, ); + // After we've inserted the new nodes into the "clones" set we can apply share + // or exit transitions to them. + if (visitPhase === INSERT_EXIT) { + applyExitViewTransition(finishedWork); + } else if ( + visitPhase === INSERT_APPEARING_PAIR || + visitPhase === INSERT_APPEND + ) { + applyAppearingPairViewTransition(finishedWork); + } popMutationContext(prevMutationContext); break; default: { @@ -412,8 +638,18 @@ function recursivelyInsertClonesFromExistingTree( viewTransitionState, nextPhase, ); - // TODO: Only the first level should track if this was s - // child.flags |= Update; + // After we've collected the cloned instances, we can apply exit or share transitions + // to them. + if (visitPhase === CLONE_EXIT) { + applyExitViewTransition(child); + } else if ( + visitPhase === CLONE_APPEARING_PAIR || + visitPhase === CLONE_UNHIDE + ) { + applyAppearingPairViewTransition(child); + } else if (visitPhase === CLONE_UPDATE) { + applyNestedViewTransition(child); + } popMutationContext(prevMutationContext); break; default: { @@ -675,6 +911,18 @@ function insertDestinationClonesOfFiber( // whether it resized or not. finishedWork.flags |= Update; } + // After we've collected the cloned instances, we can apply exit or share transitions + // to them. + if (visitPhase === CLONE_EXIT) { + applyExitViewTransition(finishedWork); + } else if ( + visitPhase === CLONE_APPEARING_PAIR || + visitPhase === CLONE_UNHIDE + ) { + applyAppearingPairViewTransition(finishedWork); + } else if (visitPhase === CLONE_UPDATE) { + applyUpdateViewTransition(current, finishedWork); + } popMutationContext(prevMutationContext); break; default: { diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 09f749bd22a3a..dc3074a8ecd95 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -49,7 +49,8 @@ export function resetShouldStartViewTransition(): void { // This tracks named ViewTransition components found in the accumulateSuspenseyCommit // phase that might need to find deleted pairs in the beforeMutation phase. -let appearingViewTransitions: Map<string, ViewTransitionState> | null = null; +export let appearingViewTransitions: Map<string, ViewTransitionState> | null = + null; export function resetAppearingViewTransitions(): void { appearingViewTransitions = null; @@ -347,9 +348,10 @@ function commitDeletedPairViewTransitions(deletion: Fiber): void { restoreViewTransitionOnHostInstances(child.child, false); } else { // We'll transition between them. - const oldinstance: ViewTransitionState = child.stateNode; + const oldInstance: ViewTransitionState = child.stateNode; const newInstance: ViewTransitionState = pair; - newInstance.paired = oldinstance; + newInstance.paired = oldInstance; + oldInstance.paired = newInstance; // Note: If the other side ends up outside the viewport, we'll still run this. // Therefore it's possible for onShare to be called with only an old snapshot. scheduleViewTransitionEvent(child, props.onShare); @@ -398,9 +400,10 @@ export function commitExitViewTransitions(deletion: Fiber): void { } else if (pair !== undefined) { // We found a new appearing view transition with the same name as this deletion. // We'll transition between them instead of running the normal exit. - const oldinstance: ViewTransitionState = deletion.stateNode; + const oldInstance: ViewTransitionState = deletion.stateNode; const newInstance: ViewTransitionState = pair; - newInstance.paired = oldinstance; + newInstance.paired = oldInstance; + oldInstance.paired = newInstance; // Delete the entry so that we know when we've found all of them // and can stop searching (size reaches zero). // $FlowFixMe[incompatible-use]: Refined by the pair. From 2c560374d6276c51cfeeff7af39628058c155a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 14:26:55 -0400 Subject: [PATCH 140/300] Measure and apply names for the "new" phase (#32612) Stacked on #32599 and #32611. This is able to reuse the code from CommitViewTransitions for "enter", "shared" and "layout". The difference is that for "enter"/"shared" in the "new" phase we pass in the deletions. For "layout" of nested boundaries we just need to measure the clones at the same time we measure the original nodes since we haven't measured them in a previous phase in the current approach. With these updates, things move around more like expected in the fixture because we're now applying the appropriate pairs to trigger individual animations instead of just the full document cross-fade. The "update" phase is a little more complicated and is coming soon. --- .../src/ReactFiberApplyGesture.js | 73 ++----------------- .../src/ReactFiberCommitViewTransitions.js | 44 +++++++++-- .../src/ReactFiberCommitWork.js | 6 +- 3 files changed, 44 insertions(+), 79 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js index 918a36d66151f..c9b1d7688f580 100644 --- a/packages/react-reconciler/src/ReactFiberApplyGesture.js +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -62,6 +62,8 @@ import { restoreEnterOrExitViewTransitions, restoreNestedViewTransitions, appearingViewTransitions, + commitEnterViewTransitions, + measureNestedViewTransitions, } from './ReactFiberCommitViewTransitions'; import { getViewTransitionName, @@ -968,53 +970,6 @@ export function insertDestinationClones( } } -function applyDeletedPairViewTransitions(deletion: Fiber): void { - if ((deletion.subtreeFlags & ViewTransitionNamedStatic) === NoFlags) { - // This has no named view transitions in its subtree. - return; - } - let child = deletion.child; - while (child !== null) { - if (child.tag === OffscreenComponent && child.memoizedState === null) { - // This tree was already hidden so we skip it. - } else { - if ( - child.tag === ViewTransitionComponent && - (child.flags & ViewTransitionNamedStatic) !== NoFlags - ) { - const props: ViewTransitionProps = child.memoizedProps; - const name = props.name; - if (name != null && name !== 'auto') { - // TODO: Find a pair - } - } - applyDeletedPairViewTransitions(child); - } - child = child.sibling; - } -} - -function applyEnterViewTransitions(deletion: Fiber): void { - if (deletion.tag === ViewTransitionComponent) { - const props: ViewTransitionProps = deletion.memoizedProps; - const name = props.name; - if (name != null && name !== 'auto') { - // TODO: Find a pair - } - // Look for more pairs deeper in the tree. - applyDeletedPairViewTransitions(deletion); - } else if ((deletion.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - // TODO: Check if this is a hidden Offscreen or a Portal. - let child = deletion.child; - while (child !== null) { - applyEnterViewTransitions(child); - child = child.sibling; - } - } else { - applyDeletedPairViewTransitions(deletion); - } -} - function measureExitViewTransitions(placement: Fiber): void { if (placement.tag === ViewTransitionComponent) { // const state: ViewTransitionState = placement.stateNode; @@ -1036,24 +991,6 @@ function measureExitViewTransitions(placement: Fiber): void { } } -function measureNestedViewTransitions(changedParent: Fiber): void { - let child = changedParent.child; - while (child !== null) { - if (child.tag === ViewTransitionComponent) { - const current = child.alternate; - if (current !== null) { - // const props: ViewTransitionProps = child.memoizedProps; - // const name = getViewTransitionName(props, child.stateNode); - // TODO: Measure both the old and new state and see if they're different. - } - } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - // TODO: Check if this is a hidden Offscreen or a Portal. - measureNestedViewTransitions(child); - } - child = child.sibling; - } -} - function measureUpdateViewTransition( current: Fiber, finishedWork: Fiber, @@ -1066,7 +1003,7 @@ function recursivelyApplyViewTransitions(parentFiber: Fiber) { if (deletions !== null) { for (let i = 0; i < deletions.length; i++) { const childToDelete = deletions[i]; - applyEnterViewTransitions(childToDelete); + commitEnterViewTransitions(childToDelete, true); } } @@ -1084,7 +1021,7 @@ function recursivelyApplyViewTransitions(parentFiber: Fiber) { // Nothing has changed in this subtree, but the parent may have still affected // its size and position. We need to measure the old and new state to see if // we should animate its size and position. - measureNestedViewTransitions(parentFiber); + measureNestedViewTransitions(parentFiber, true); } } @@ -1121,7 +1058,7 @@ function applyViewTransitionsOnFiber(finishedWork: Fiber) { measureExitViewTransitions(finishedWork); } else if (current !== null && current.memoizedState === null) { // Was previously mounted as visible but is now hidden. - applyEnterViewTransitions(current); + commitEnterViewTransitions(current, true); } } break; diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index dc3074a8ecd95..80e190181086b 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -257,7 +257,10 @@ function commitAppearingPairViewTransitions(placement: Fiber): void { } } -export function commitEnterViewTransitions(placement: Fiber): void { +export function commitEnterViewTransitions( + placement: Fiber, + gesture: boolean, +): void { if (placement.tag === ViewTransitionComponent) { const state: ViewTransitionState = placement.stateNode; const props: ViewTransitionProps = placement.memoizedProps; @@ -284,7 +287,11 @@ export function commitEnterViewTransitions(placement: Fiber): void { commitAppearingPairViewTransitions(placement); if (!state.paired) { - scheduleViewTransitionEvent(placement, props.onEnter); + if (gesture) { + // TODO: Schedule gesture events. + } else { + scheduleViewTransitionEvent(placement, props.onEnter); + } } } } else { @@ -293,7 +300,7 @@ export function commitEnterViewTransitions(placement: Fiber): void { } else if ((placement.subtreeFlags & ViewTransitionStatic) !== NoFlags) { let child = placement.child; while (child !== null) { - commitEnterViewTransitions(child); + commitEnterViewTransitions(child, gesture); child = child.sibling; } } else { @@ -703,6 +710,8 @@ function measureViewTransitionHostInstancesRecursive( } if ((parentViewTransition.flags & Update) !== NoFlags) { // We might update this node so we need to apply its new name for the new state. + // Additionally in the ApplyGesture case we also need to do this because the clone + // will have the name but this one won't. applyViewTransitionName( instance, viewTransitionHostInstanceIdx === 0 @@ -828,32 +837,51 @@ export function measureUpdateViewTransition( return inViewport; } -export function measureNestedViewTransitions(changedParent: Fiber): void { +export function measureNestedViewTransitions( + changedParent: Fiber, + gesture: boolean, +): void { let child = changedParent.child; while (child !== null) { if (child.tag === ViewTransitionComponent) { const props: ViewTransitionProps = child.memoizedProps; - const name = getViewTransitionName(props, child.stateNode); + const state: ViewTransitionState = child.stateNode; + const name = getViewTransitionName(props, state); const className: ?string = getViewTransitionClassName( props.className, props.layout, ); + let previousMeasurements: null | Array<InstanceMeasurement>; + if (gesture) { + const clones = state.clones; + if (clones === null) { + previousMeasurements = null; + } else { + previousMeasurements = clones.map(measureInstance); + } + } else { + previousMeasurements = child.memoizedState; + } const inViewport = measureViewTransitionHostInstances( child, child.child, name, name, // Since this is unchanged, new and old name is the same. className, - child.memoizedState, + previousMeasurements, false, ); if ((child.flags & Update) === NoFlags || !inViewport) { // Nothing changed. } else { - scheduleViewTransitionEvent(child, props.onLayout); + if (gesture) { + // TODO: Schedule gesture events. + } else { + scheduleViewTransitionEvent(child, props.onLayout); + } } } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { - measureNestedViewTransitions(child); + measureNestedViewTransitions(child, gesture); } child = child.sibling; } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 2663c1e087542..99a03b4ad104f 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -2450,7 +2450,7 @@ function recursivelyTraverseAfterMutationEffects( // Nothing has changed in this subtree, but the parent may have still affected // its size and position. We need to measure this and if not, restore it to // not animate. - measureNestedViewTransitions(parentFiber); + measureNestedViewTransitions(parentFiber, false); } } @@ -2468,7 +2468,7 @@ function commitAfterMutationEffectsOnFiber( // we can just bail after we're done with the first one. // The first ViewTransition inside a newly mounted tree runs an enter transition // but other nested ones don't unless they have a named pair. - commitEnterViewTransitions(finishedWork); + commitEnterViewTransitions(finishedWork, false); return; } @@ -2512,7 +2512,7 @@ function commitAfterMutationEffectsOnFiber( // The Offscreen tree is visible. const wasHidden = current.memoizedState !== null; if (wasHidden) { - commitEnterViewTransitions(finishedWork); + commitEnterViewTransitions(finishedWork, false); // If it was previous hidden then the children are treated as enter // not updates so we don't need to visit these children. } else { From 6b5d9fd3166eb58b469fb23f7b96972b184c0218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 17:38:35 -0400 Subject: [PATCH 141/300] Move traverseFragmentInstanceChildren to internal ReactFiberTreeReflection (#32613) This is a nit but a Config should not have to know anything about the internals of Fibers. Ideally it shouldn't even access them but we have some cases where we need pointers back in like for this fragment. The way we've typically abstracted this is using the `ReactFiberTreeReflection` helper that's in the `react-reconciler`. Such as in the event system. https://github.com/facebook/react/blob/f3c956006a90dc68210bd3e19497d10fb9b028d3/packages/react-dom-bindings/src/events/ReactDOMEventListener.js#L22-L26 We sometimes cheat but we really should clean this up such that a `Fiber` is actually an opaque type to the Configs and it can never dot into it without using a helper. So this just moves `traverseFragmentInstanceChildren` to ReactFiberTreeReflection so that the ConfigDOM doesn't ever dot into its fields itself. It just passes the Fiber through back into the react-reconciler. I had to add a wrapper to read the `.child` to avoid that being assumed too. I also noticed that FragmentInstanceType is not actually passed through so that argument is unnecessary. --- .../src/client/ReactFiberConfigDOM.js | 51 +++---------------- .../src/ReactFiberTreeReflection.js | 37 +++++++++++++- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index cdaaec4b3eeba..1e9ee657e0ca8 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -34,7 +34,6 @@ import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostCo import hasOwnProperty from 'shared/hasOwnProperty'; import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion'; import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; -import {OffscreenComponent} from 'react-reconciler/src/ReactWorkTags'; export { setCurrentUpdatePriority, @@ -54,6 +53,8 @@ import { markNodeAsHoistable, isOwnedInstance, } from './ReactDOMComponentTree'; +import {traverseFragmentInstance} from 'react-reconciler/src/ReactFiberTreeReflection'; + export {detachDeletedInstance}; import {hasRole} from './DOMAccessibilityRoles'; import { @@ -2221,9 +2222,8 @@ FragmentInstance.prototype.addEventListener = function ( indexOfEventListener(listeners, type, listener, optionsOrUseCapture) === -1; if (isNewEventListener) { listeners.push({type, listener, optionsOrUseCapture}); - traverseFragmentInstanceChildren( - this, - this._fragmentFiber.child, + traverseFragmentInstance( + this._fragmentFiber, addEventListenerToChild, type, listener, @@ -2253,9 +2253,8 @@ FragmentInstance.prototype.removeEventListener = function ( return; } if (typeof listeners !== 'undefined' && listeners.length > 0) { - traverseFragmentInstanceChildren( - this, - this._fragmentFiber.child, + traverseFragmentInstance( + this._fragmentFiber, removeEventListenerFromChild, type, listener, @@ -2283,45 +2282,9 @@ function removeEventListenerFromChild( } // $FlowFixMe[prop-missing] FragmentInstance.prototype.focus = function (this: FragmentInstanceType) { - traverseFragmentInstanceChildren( - this, - this._fragmentFiber.child, - setFocusIfFocusable, - ); + traverseFragmentInstance(this._fragmentFiber, setFocusIfFocusable); }; -function traverseFragmentInstanceChildren<A, B, C>( - fragmentInstance: FragmentInstanceType, - child: Fiber | null, - fn: (Instance, A, B, C) => boolean, - a: A, - b: B, - c: C, -): void { - while (child !== null) { - if (child.tag === HostComponent) { - if (fn(child.stateNode, a, b, c)) { - return; - } - } else if ( - child.tag === OffscreenComponent && - child.memoizedState !== null - ) { - // Skip hidden subtrees - } else { - traverseFragmentInstanceChildren( - fragmentInstance, - child.child, - fn, - a, - b, - c, - ); - } - child = child.sibling; - } -} - function normalizeListenerOptions( opts: ?EventListenerOptionsOrUseCapture, ): string { diff --git a/packages/react-reconciler/src/ReactFiberTreeReflection.js b/packages/react-reconciler/src/ReactFiberTreeReflection.js index 5dce60a559fd8..14ce8405d7b99 100644 --- a/packages/react-reconciler/src/ReactFiberTreeReflection.js +++ b/packages/react-reconciler/src/ReactFiberTreeReflection.js @@ -8,7 +8,7 @@ */ import type {Fiber} from './ReactInternalTypes'; -import type {Container, SuspenseInstance} from './ReactFiberConfig'; +import type {Container, SuspenseInstance, Instance} from './ReactFiberConfig'; import type {SuspenseState} from './ReactFiberSuspenseComponent'; import { @@ -19,6 +19,7 @@ import { HostPortal, HostText, SuspenseComponent, + OffscreenComponent, } from './ReactWorkTags'; import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; @@ -317,3 +318,37 @@ export function doesFiberContain( } return false; } + +export function traverseFragmentInstance<A, B, C>( + fragmentFiber: Fiber, + fn: (Instance, A, B, C) => boolean, + a: A, + b: B, + c: C, +): void { + return traverseFragmentInstanceChildren(fragmentFiber.child, fn, a, b, c); +} + +function traverseFragmentInstanceChildren<A, B, C>( + child: Fiber | null, + fn: (Instance, A, B, C) => boolean, + a: A, + b: B, + c: C, +): void { + while (child !== null) { + if (child.tag === HostComponent) { + if (fn(child.stateNode, a, b, c)) { + return; + } + } else if ( + child.tag === OffscreenComponent && + child.memoizedState !== null + ) { + // Skip hidden subtrees + } else { + traverseFragmentInstanceChildren(child.child, fn, a, b, c); + } + child = child.sibling; + } +} From 17d274dc127400b31379a1b26c5be53599c36aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 14 Mar 2025 17:38:53 -0400 Subject: [PATCH 142/300] Remove Mutation Check Around commit/measureUpdateViewTransition (#32617) There's two ways to find updated View Transitions. One is the "commit/measureNestedViewTransitions" pass which is used to find things in unchanged subtrees. This can only lead to the relayout case since there's can't possibly be any mutations in the subtree. This is only triggered when none of the direct siblings have any mutations at all. The other case is "commit/measureUpdateViewTransition" which is for a ViewTransition that itself has mutations scheduled inside of it which leads to the "update" case. However, there's a case between these two cases. When a direct sibling has a mutation but there's also a ViewTransition exactly at the same level. In that case we can't bail out on the whole set of children so we won't trigger the "nested" case. Previously we also didn't trigger the "commit/measureUpdateViewTransition" case because we first checked if that had any mutations inside of it at all. This leads to neither case picking up this boundary. We could check if the ViewTransition itself has any mutations inside and if not trigger the nested path. There's a simpler way though. Because `commit/measureUpdateViewTransition` is actually just optimistic. The flags are pessimistic and we don't know for sure if there will actually be a mutation until we've traversed the tree. It can sometimes lead to the "relayout" case. So we can just use that same path, knowing that it'll just lead to the layout pass. Therefore it's safe to just remove this check. --- .../src/ReactFiberCommitWork.js | 108 ++++++++---------- .../react-reconciler/src/ReactFiberFlags.js | 4 +- 2 files changed, 49 insertions(+), 63 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 99a03b4ad104f..3e2a5153dd147 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -100,7 +100,7 @@ import { Hydrating, Passive, BeforeMutationMask, - BeforeMutationTransitionMask, + BeforeAndAfterMutationTransitionMask, MutationMask, LayoutMask, PassiveMask, @@ -311,7 +311,7 @@ function commitBeforeMutationEffects_begin(isViewTransitionEligible: boolean) { // If this commit is eligible for a View Transition we look into all mutated subtrees. // TODO: We could optimize this by marking these with the Snapshot subtree flag in the render phase. const subtreeMask = isViewTransitionEligible - ? BeforeMutationTransitionMask + ? BeforeAndAfterMutationTransitionMask : BeforeMutationMask; while (nextEffect !== null) { const fiber = nextEffect; @@ -487,16 +487,8 @@ function commitBeforeMutationEffectsOnFiber( if (current === null) { // This is a new mount. We should have handled this as part of the // Placement effect or it is deeper inside a entering transition. - } else if ( - (finishedWork.subtreeFlags & - (Placement | - Update | - ChildDeletion | - ContentReset | - Visibility)) !== - NoFlags - ) { - // Something mutated within this subtree. This might need to cause + } else { + // Something may have mutated within this subtree. This might need to cause // a cross-fade of this parent. We first assign old names to the // previous tree in the before mutation phase in case we need to. // TODO: This walks the tree that we might continue walking anyway. @@ -2440,7 +2432,7 @@ function recursivelyTraverseAfterMutationEffects( lanes: Lanes, ) { // We need to visit the same nodes that we visited in the before mutation phase. - if (parentFiber.subtreeFlags & BeforeMutationTransitionMask) { + if (parentFiber.subtreeFlags & BeforeAndAfterMutationTransitionMask) { let child = parentFiber.child; while (child !== null) { commitAfterMutationEffectsOnFiber(child, root, lanes); @@ -2525,63 +2517,57 @@ function commitAfterMutationEffectsOnFiber( break; } case ViewTransitionComponent: { - if ( - (finishedWork.subtreeFlags & - (Placement | Update | ChildDeletion | ContentReset | Visibility)) !== - NoFlags - ) { - const wasMutated = (finishedWork.flags & Update) !== NoFlags; + const wasMutated = (finishedWork.flags & Update) !== NoFlags; - const prevContextChanged = viewTransitionContextChanged; - const prevCancelableChildren = pushViewTransitionCancelableScope(); - viewTransitionContextChanged = false; - recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); + const prevContextChanged = viewTransitionContextChanged; + const prevCancelableChildren = pushViewTransitionCancelableScope(); + viewTransitionContextChanged = false; + recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); - if (viewTransitionContextChanged) { - finishedWork.flags |= Update; - } + if (viewTransitionContextChanged) { + finishedWork.flags |= Update; + } - const inViewport = measureUpdateViewTransition(current, finishedWork); + const inViewport = measureUpdateViewTransition(current, finishedWork); - if ((finishedWork.flags & Update) === NoFlags || !inViewport) { - // If this boundary didn't update, then we may be able to cancel its children. - // We bubble them up to the parent set to be determined later if we can cancel. - // Similarly, if old and new state was outside the viewport, we can skip it - // even if it did update. - if (prevCancelableChildren === null) { - // Bubbling up this whole set to the parent. - } else { - // Merge with parent set. - // $FlowFixMe[method-unbinding] - prevCancelableChildren.push.apply( - prevCancelableChildren, - viewTransitionCancelableChildren, - ); - popViewTransitionCancelableScope(prevCancelableChildren); - } - // TODO: If this doesn't end up canceled, because a parent animates, - // then we should probably issue an event since this instance is part of it. + if ((finishedWork.flags & Update) === NoFlags || !inViewport) { + // If this boundary didn't update, then we may be able to cancel its children. + // We bubble them up to the parent set to be determined later if we can cancel. + // Similarly, if old and new state was outside the viewport, we can skip it + // even if it did update. + if (prevCancelableChildren === null) { + // Bubbling up this whole set to the parent. } else { - const props: ViewTransitionProps = finishedWork.memoizedProps; - scheduleViewTransitionEvent( - finishedWork, - wasMutated || viewTransitionContextChanged - ? props.onUpdate - : props.onLayout, + // Merge with parent set. + // $FlowFixMe[method-unbinding] + prevCancelableChildren.push.apply( + prevCancelableChildren, + viewTransitionCancelableChildren, ); - - // If this boundary did update, we cannot cancel its children so those are dropped. popViewTransitionCancelableScope(prevCancelableChildren); } + // TODO: If this doesn't end up canceled, because a parent animates, + // then we should probably issue an event since this instance is part of it. + } else { + const props: ViewTransitionProps = finishedWork.memoizedProps; + scheduleViewTransitionEvent( + finishedWork, + wasMutated || viewTransitionContextChanged + ? props.onUpdate + : props.onLayout, + ); - if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) { - // This boundary changed size in a way that may have caused its parent to - // relayout. We need to bubble this information up to the parent. - viewTransitionContextChanged = true; - } else { - // Otherwise, we restore it to whatever the parent had found so far. - viewTransitionContextChanged = prevContextChanged; - } + // If this boundary did update, we cannot cancel its children so those are dropped. + popViewTransitionCancelableScope(prevCancelableChildren); + } + + if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) { + // This boundary changed size in a way that may have caused its parent to + // relayout. We need to bubble this information up to the parent. + viewTransitionContextChanged = true; + } else { + // Otherwise, we restore it to whatever the parent had found so far. + viewTransitionContextChanged = prevContextChanged; } break; } diff --git a/packages/react-reconciler/src/ReactFiberFlags.js b/packages/react-reconciler/src/ReactFiberFlags.js index b6b2efd1996a8..8b26c66859363 100644 --- a/packages/react-reconciler/src/ReactFiberFlags.js +++ b/packages/react-reconciler/src/ReactFiberFlags.js @@ -108,8 +108,8 @@ export const BeforeMutationMask: number = // For View Transition support we use the snapshot phase to scan the tree for potentially // affected ViewTransition components. -export const BeforeMutationTransitionMask: number = - Snapshot | Update | Placement | ChildDeletion | Visibility; +export const BeforeAndAfterMutationTransitionMask: number = + Snapshot | Update | Placement | ChildDeletion | Visibility | ContentReset; export const MutationMask = Placement | From 99563e91736e9de473c4865d5cb6fd9eb1a26bcb Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Sat, 15 Mar 2025 15:21:57 -0400 Subject: [PATCH 143/300] Partially revert #32588 (#32621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/facebook/react/pull/32588 changed the babel config impacting local tests, and I'm not able to run test: <img width="1354" alt="Screenshot 2025-03-15 at 2 37 00 PM" src="https://github.com/user-attachments/assets/2d4afe39-6ab6-4c83-87a9-ceb0ee5f8df5" /> This PR reverts those changes until we can re-land with a fix. --- babel.config.js | 3 +-- package.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/babel.config.js b/babel.config.js index 3498e2aebe86f..f8a28b20cc87d 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,8 +4,7 @@ module.exports = { plugins: [ '@babel/plugin-syntax-jsx', '@babel/plugin-transform-flow-strip-types', - ['@babel/plugin-transform-class-properties', {loose: true}], - '@babel/plugin-transform-classes', + ['@babel/plugin-proposal-class-properties', {loose: true}], 'syntax-trailing-function-commas', [ '@babel/plugin-proposal-object-rest-spread', diff --git a/package.json b/package.json index ca0b62e84672d..0fcb952cc6f6d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@babel/plugin-transform-block-scoped-functions": "^7.10.4", "@babel/plugin-transform-block-scoping": "^7.11.1", "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-classes": "^7.10.4", "@babel/plugin-transform-computed-properties": "^7.10.4", "@babel/plugin-transform-destructuring": "^7.10.4", "@babel/plugin-transform-for-of": "^7.10.4", From 1a191701fe5000098d23328b2ea9d70457fea1f8 Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Mon, 17 Mar 2025 09:17:00 -0400 Subject: [PATCH 144/300] [refactor] Add element type for Activity (#32499) This PR separates Activity to it's own element type separate from Offscreen. The goal is to allow us to add Activity element boundary semantics during hydration similar to Suspense semantics, without impacting the Offscreen behavior in suspended children. --- .../__tests__/storeComponentFilters-test.js | 166 ++++++++++ .../fiber/DevToolsFiberComponentStack.js | 7 + .../src/backend/fiber/renderer.js | 12 + .../src/backend/types.js | 1 + .../views/Settings/ComponentsSettings.js | 6 + .../src/frontend/types.js | 4 +- packages/react-reconciler/src/ReactFiber.js | 15 + .../src/ReactFiberBeginWork.js | 44 +++ .../src/ReactFiberCompleteWork.js | 2 + .../src/ReactFiberComponentStack.js | 8 +- .../react-reconciler/src/ReactWorkTags.js | 4 +- .../src/__tests__/ReactErrorStacks-test.js | 312 +++++++++++++----- .../src/getComponentNameFromFiber.js | 3 + packages/react-server/src/ReactFizzServer.js | 4 +- packages/react/src/ReactClient.js | 4 +- packages/shared/ReactSymbols.js | 1 + packages/shared/getComponentNameFromType.js | 4 +- packages/shared/isValidElementType.js | 2 + 18 files changed, 504 insertions(+), 95 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js b/packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js index 26cd51383297f..5f00af92bf5ae 100644 --- a/packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js +++ b/packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js @@ -135,6 +135,172 @@ describe('Store component filters', () => { }); // @reactVersion >= 16.0 + it('should filter Suspense', async () => { + const Suspense = React.Suspense; + await actAsync(async () => + render( + <React.Fragment> + <Suspense> + <div>Visible</div> + </Suspense> + <Suspense> + <div>Hidden</div> + </Suspense> + </React.Fragment>, + ), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <Suspense> + <div> + ▾ <Suspense> + <div> + `); + + await actAsync( + async () => + (store.componentFilters = [ + utils.createElementTypeFilter(Types.ElementTypeActivity), + ]), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <Suspense> + <div> + ▾ <Suspense> + <div> + `); + + await actAsync( + async () => + (store.componentFilters = [ + utils.createElementTypeFilter(Types.ElementTypeActivity, false), + ]), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <Suspense> + <div> + ▾ <Suspense> + <div> + `); + }); + + it('should filter Activity', async () => { + const Activity = React.unstable_Activity; + + if (Activity != null) { + await actAsync(async () => + render( + <React.Fragment> + <Activity mode="visible"> + <div>Visible</div> + </Activity> + <Activity mode="hidden"> + <div>Hidden</div> + </Activity> + </React.Fragment>, + ), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <Activity> + <div> + ▾ <Activity> + <div> + `); + + await actAsync( + async () => + (store.componentFilters = [ + utils.createElementTypeFilter(Types.ElementTypeActivity), + ]), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + <div> + <div> + `); + + await actAsync( + async () => + (store.componentFilters = [ + utils.createElementTypeFilter(Types.ElementTypeActivity, false), + ]), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <Activity> + <div> + ▾ <Activity> + <div> + `); + } + }); + + it('should filter ViewTransition', async () => { + const ViewTransition = React.unstable_ViewTransition; + + if (ViewTransition != null) { + await actAsync(async () => + render( + <React.Fragment> + <ViewTransition> + <div>Visible</div> + </ViewTransition> + <ViewTransition> + <div>Hidden</div> + </ViewTransition> + </React.Fragment>, + ), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <ViewTransition> + <div> + ▾ <ViewTransition> + <div> + `); + + await actAsync( + async () => + (store.componentFilters = [ + utils.createElementTypeFilter(Types.ElementTypeActivity), + ]), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <ViewTransition> + <div> + ▾ <ViewTransition> + <div> + `); + + await actAsync( + async () => + (store.componentFilters = [ + utils.createElementTypeFilter(Types.ElementTypeActivity, false), + ]), + ); + + expect(store).toMatchInlineSnapshot(` + [root] + ▾ <ViewTransition> + <div> + ▾ <ViewTransition> + <div> + `); + } + }); + it('should ignore invalid ElementTypeRoot filter', async () => { const Component = () => <div>Hi</div>; diff --git a/packages/react-devtools-shared/src/backend/fiber/DevToolsFiberComponentStack.js b/packages/react-devtools-shared/src/backend/fiber/DevToolsFiberComponentStack.js index 46e479905afbb..4f63f562e0b51 100644 --- a/packages/react-devtools-shared/src/backend/fiber/DevToolsFiberComponentStack.js +++ b/packages/react-devtools-shared/src/backend/fiber/DevToolsFiberComponentStack.js @@ -44,6 +44,7 @@ export function describeFiber( ForwardRef, ClassComponent, ViewTransitionComponent, + ActivityComponent, } = workTagMap; switch (workInProgress.tag) { @@ -60,6 +61,8 @@ export function describeFiber( return describeBuiltInComponentFrame('SuspenseList'); case ViewTransitionComponent: return describeBuiltInComponentFrame('ViewTransition'); + case ActivityComponent: + return describeBuiltInComponentFrame('Activity'); case FunctionComponent: case IndeterminateComponent: case SimpleMemoComponent: @@ -154,6 +157,7 @@ export function getOwnerStackByFiberInDev( SuspenseComponent, SuspenseListComponent, ViewTransitionComponent, + ActivityComponent, } = workTagMap; try { let info = ''; @@ -184,6 +188,9 @@ export function getOwnerStackByFiberInDev( case ViewTransitionComponent: info += describeBuiltInComponentFrame('ViewTransition'); break; + case ActivityComponent: + info += describeBuiltInComponentFrame('Activity'); + break; } let owner: void | null | Fiber | ReactComponentInfo = workInProgress; diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index ef8bac0099a2b..d06a47e9d3d08 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -28,6 +28,7 @@ import { ElementTypeSuspenseList, ElementTypeTracingMarker, ElementTypeViewTransition, + ElementTypeActivity, ElementTypeVirtual, StrictMode, } from 'react-devtools-shared/src/frontend/types'; @@ -385,6 +386,7 @@ export function getInternalReactConstants(version: string): { YieldComponent: -1, // Removed Throw: 29, ViewTransitionComponent: 30, // Experimental + ActivityComponent: 31, }; } else if (gte(version, '17.0.0-alpha')) { ReactTypeOfWork = { @@ -421,6 +423,7 @@ export function getInternalReactConstants(version: string): { YieldComponent: -1, // Removed Throw: -1, // Doesn't exist yet ViewTransitionComponent: -1, // Doesn't exist yet + ActivityComponent: -1, // Doesn't exist yet }; } else if (gte(version, '16.6.0-beta.0')) { ReactTypeOfWork = { @@ -457,6 +460,7 @@ export function getInternalReactConstants(version: string): { YieldComponent: -1, // Removed Throw: -1, // Doesn't exist yet ViewTransitionComponent: -1, // Doesn't exist yet + ActivityComponent: -1, // Doesn't exist yet }; } else if (gte(version, '16.4.3-alpha')) { ReactTypeOfWork = { @@ -493,6 +497,7 @@ export function getInternalReactConstants(version: string): { YieldComponent: -1, // Removed Throw: -1, // Doesn't exist yet ViewTransitionComponent: -1, // Doesn't exist yet + ActivityComponent: -1, // Doesn't exist yet }; } else { ReactTypeOfWork = { @@ -529,6 +534,7 @@ export function getInternalReactConstants(version: string): { YieldComponent: 9, Throw: -1, // Doesn't exist yet ViewTransitionComponent: -1, // Doesn't exist yet + ActivityComponent: -1, // Doesn't exist yet }; } // ********************************************************** @@ -572,6 +578,7 @@ export function getInternalReactConstants(version: string): { TracingMarkerComponent, Throw, ViewTransitionComponent, + ActivityComponent, } = ReactTypeOfWork; function resolveFiberType(type: any): $FlowFixMe { @@ -622,6 +629,8 @@ export function getInternalReactConstants(version: string): { } switch (tag) { + case ActivityComponent: + return 'Activity'; case CacheComponent: return 'Cache'; case ClassComponent: @@ -892,6 +901,7 @@ export function attach( StrictModeBits, } = getInternalReactConstants(version); const { + ActivityComponent, CacheComponent, ClassComponent, ContextConsumer, @@ -1565,6 +1575,8 @@ export function attach( const {type, tag} = fiber; switch (tag) { + case ActivityComponent: + return ElementTypeActivity; case ClassComponent: case IncompleteClassComponent: return ElementTypeClass; diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 7b0d801026acc..56fb9a8ec315b 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -77,6 +77,7 @@ export type WorkTagMap = { YieldComponent: WorkTag, Throw: WorkTag, ViewTransitionComponent: WorkTag, + ActivityComponent: WorkTag, }; export type HostInstance = Object; diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js index a521c5bea908f..e9e4f07616ac3 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js @@ -472,6 +472,8 @@ export default function ComponentsSettings({ ((parseInt(currentTarget.value, 10): any): ElementType), ) }> + {/* TODO: currently only experimental, only list this if it's available */} + {/*<option value={ElementTypeActivity}>activity</option>*/} <option value={ElementTypeClass}>class</option> <option value={ElementTypeContext}>context</option> <option value={ElementTypeFunction}>function</option> @@ -485,6 +487,10 @@ export default function ComponentsSettings({ <option value={ElementTypeOtherOrUnknown}>other</option> <option value={ElementTypeProfiler}>profiler</option> <option value={ElementTypeSuspense}>suspense</option> + {/* TODO: currently only experimental, only list this if it's available */} + {/*<option value={ElementTypeViewTransition}>*/} + {/* view transition*/} + {/*</option>*/} </select> )} {(componentFilter.type === ComponentFilterLocation || diff --git a/packages/react-devtools-shared/src/frontend/types.js b/packages/react-devtools-shared/src/frontend/types.js index 302b168bd6eb0..827ba4dccb97e 100644 --- a/packages/react-devtools-shared/src/frontend/types.js +++ b/packages/react-devtools-shared/src/frontend/types.js @@ -50,6 +50,7 @@ export const ElementTypeSuspenseList = 13; export const ElementTypeTracingMarker = 14; export const ElementTypeVirtual = 15; export const ElementTypeViewTransition = 16; +export const ElementTypeActivity = 17; // Different types of elements displayed in the Elements tree. // These types may be used to visually distinguish types, @@ -68,7 +69,8 @@ export type ElementType = | 13 | 14 | 15 - | 16; + | 16 + | 17; // WARNING // The values below are referenced by ComponentFilters (which are saved via localStorage). diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index f6011a3ba155c..7b63135c05f29 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -71,6 +71,7 @@ import { TracingMarkerComponent, Throw, ViewTransitionComponent, + ActivityComponent, } from './ReactWorkTags'; import {OffscreenVisible} from './ReactFiberActivityComponent'; import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber'; @@ -107,6 +108,7 @@ import { REACT_TRACING_MARKER_TYPE, REACT_ELEMENT_TYPE, REACT_VIEW_TRANSITION_TYPE, + REACT_ACTIVITY_TYPE, } from 'shared/ReactSymbols'; import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent'; import { @@ -588,6 +590,8 @@ export function createFiberFromTypeAndProps( } } else { getTag: switch (type) { + case REACT_ACTIVITY_TYPE: + return createFiberFromActivity(pendingProps, mode, lanes, key); case REACT_FRAGMENT_TYPE: return createFiberFromFragment(pendingProps.children, mode, lanes, key); case REACT_STRICT_MODE_TYPE: @@ -865,6 +869,17 @@ export function createFiberFromOffscreen( fiber.stateNode = primaryChildInstance; return fiber; } +export function createFiberFromActivity( + pendingProps: OffscreenProps, + mode: TypeOfMode, + lanes: Lanes, + key: null | string, +): Fiber { + const fiber = createFiber(ActivityComponent, pendingProps, key, mode); + fiber.elementType = REACT_ACTIVITY_TYPE; + fiber.lanes = lanes; + return fiber; +} export function createFiberFromViewTransition( pendingProps: ViewTransitionProps, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index b37bd4ea3f44f..ac5923e85d26e 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -77,6 +77,7 @@ import { TracingMarkerComponent, Throw, ViewTransitionComponent, + ActivityComponent, } from './ReactWorkTags'; import { NoFlags, @@ -867,6 +868,46 @@ function deferHiddenOffscreenComponent( // fork the function. const updateLegacyHiddenComponent = updateOffscreenComponent; +function updateActivityComponent( + current: null | Fiber, + workInProgress: Fiber, + renderLanes: Lanes, +) { + const nextProps = workInProgress.pendingProps; + const nextChildren = nextProps.children; + const nextMode = nextProps.mode; + const mode = workInProgress.mode; + const offscreenChildProps: OffscreenProps = { + mode: nextMode, + children: nextChildren, + }; + + if (current === null) { + const primaryChildFragment = mountWorkInProgressOffscreenFiber( + offscreenChildProps, + mode, + renderLanes, + ); + primaryChildFragment.ref = workInProgress.ref; + workInProgress.child = primaryChildFragment; + primaryChildFragment.return = workInProgress; + + return primaryChildFragment; + } else { + const currentChild: Fiber = (current.child: any); + + const primaryChildFragment = updateWorkInProgressOffscreenFiber( + currentChild, + offscreenChildProps, + ); + + primaryChildFragment.ref = workInProgress.ref; + workInProgress.child = primaryChildFragment; + primaryChildFragment.return = workInProgress; + return primaryChildFragment; + } +} + function updateCacheComponent( current: Fiber | null, workInProgress: Fiber, @@ -4025,6 +4066,9 @@ function beginWork( } break; } + case ActivityComponent: { + return updateActivityComponent(current, workInProgress, renderLanes); + } case OffscreenComponent: { return updateOffscreenComponent(current, workInProgress, renderLanes); } diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 27e4b68134b08..ce112782757d3 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -76,6 +76,7 @@ import { TracingMarkerComponent, Throw, ViewTransitionComponent, + ActivityComponent, } from './ReactWorkTags'; import {NoMode, ConcurrentMode, ProfileMode} from './ReactTypeOfMode'; import { @@ -972,6 +973,7 @@ function completeWork( } // Fallthrough } + case ActivityComponent: case LazyComponent: case SimpleMemoComponent: case FunctionComponent: diff --git a/packages/react-reconciler/src/ReactFiberComponentStack.js b/packages/react-reconciler/src/ReactFiberComponentStack.js index f44dff91c7a10..edb84b7e6de69 100644 --- a/packages/react-reconciler/src/ReactFiberComponentStack.js +++ b/packages/react-reconciler/src/ReactFiberComponentStack.js @@ -24,6 +24,7 @@ import { ClassComponent, HostText, ViewTransitionComponent, + ActivityComponent, } from './ReactWorkTags'; import { describeBuiltInComponentFrame, @@ -53,6 +54,8 @@ function describeFiber(fiber: Fiber): string { return describeFunctionComponentFrame(fiber.type.render); case ClassComponent: return describeClassComponentFrame(fiber.type); + case ActivityComponent: + return describeBuiltInComponentFrame('Activity'); case ViewTransitionComponent: if (enableViewTransition) { return describeBuiltInComponentFrame('ViewTransition'); @@ -129,9 +132,12 @@ export function getOwnerStackByFiberInDev(workInProgress: Fiber): string { case SuspenseListComponent: info += describeBuiltInComponentFrame('SuspenseList'); break; + case ActivityComponent: + info += describeBuiltInComponentFrame('Activity'); + break; case ViewTransitionComponent: if (enableViewTransition) { - info += describeBuiltInComponentFrame('SuspenseList'); + info += describeBuiltInComponentFrame('ViewTransition'); break; } // Fallthrough diff --git a/packages/react-reconciler/src/ReactWorkTags.js b/packages/react-reconciler/src/ReactWorkTags.js index 5de62ebeb7569..4b01934be7781 100644 --- a/packages/react-reconciler/src/ReactWorkTags.js +++ b/packages/react-reconciler/src/ReactWorkTags.js @@ -38,7 +38,8 @@ export type WorkTag = | 27 | 28 | 29 - | 30; + | 30 + | 31; export const FunctionComponent = 0; export const ClassComponent = 1; @@ -69,3 +70,4 @@ export const HostSingleton = 27; export const IncompleteFunctionComponent = 28; export const Throw = 29; export const ViewTransitionComponent = 30; +export const ActivityComponent = 31; diff --git a/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js b/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js index b49f1d4aec5e3..1b8beba58b07e 100644 --- a/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js @@ -10,17 +10,72 @@ 'use strict'; let React; +let Suspense; +let Activity; +let ViewTransition; let ReactNoop; let waitForAll; describe('ReactFragment', () => { + let didCatchErrors = []; + let rootCaughtErrors = []; + let SomethingThatErrors; + let CatchingBoundary; + let onCaughtError; + beforeEach(function () { jest.resetModules(); React = require('react'); + Suspense = React.Suspense; + Activity = React.unstable_Activity; + ViewTransition = React.unstable_ViewTransition; ReactNoop = require('react-noop-renderer'); const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; + + didCatchErrors = []; + rootCaughtErrors = []; + + onCaughtError = function (error, errorInfo) { + rootCaughtErrors.push( + error.message, + normalizeCodeLocInfo(errorInfo.componentStack), + React.captureOwnerStack + ? normalizeCodeLocInfo(React.captureOwnerStack()) + : null, + ); + }; + + SomethingThatErrors = () => { + throw new Error('uh oh'); + }; + + // eslint-disable-next-line no-shadow + CatchingBoundary = class CatchingBoundary extends React.Component { + constructor() { + super(); + this.state = {}; + } + + static getDerivedStateFromError(error) { + return {errored: true}; + } + + componentDidCatch(err, errInfo) { + didCatchErrors.push( + err.message, + normalizeCodeLocInfo(errInfo.componentStack), + ); + } + + render() { + if (this.state.errored) { + return null; + } + return this.props.children; + } + }; }); function componentStack(components) { @@ -38,21 +93,7 @@ describe('ReactFragment', () => { ); } - it('retains component stacks when rethrowing an error', async () => { - function Foo() { - return ( - <RethrowingBoundary> - <Bar /> - </RethrowingBoundary> - ); - } - function Bar() { - return <SomethingThatErrors />; - } - function SomethingThatErrors() { - throw new Error('uh oh'); - } - + it('retains component and owner stacks when rethrowing an error', async () => { class RethrowingBoundary extends React.Component { static getDerivedStateFromError(error) { throw error; @@ -63,33 +104,36 @@ describe('ReactFragment', () => { } } - const errors = []; - class CatchingBoundary extends React.Component { - constructor() { - super(); - this.state = {}; - } - static getDerivedStateFromError(error) { - return {errored: true}; - } - componentDidCatch(err, errInfo) { - errors.push(err.message, normalizeCodeLocInfo(errInfo.componentStack)); - } - render() { - if (this.state.errored) { - return null; - } - return this.props.children; - } + function Foo() { + return ( + <RethrowingBoundary> + <Bar /> + </RethrowingBoundary> + ); + } + function Bar() { + return <SomethingThatErrors />; } - ReactNoop.render( + ReactNoop.createRoot({ + onCaughtError, + }).render( <CatchingBoundary> <Foo /> </CatchingBoundary>, ); await waitForAll([]); - expect(errors).toEqual([ + expect(didCatchErrors).toEqual([ + 'uh oh', + componentStack([ + 'SomethingThatErrors', + 'Bar', + 'RethrowingBoundary', + 'Foo', + 'CatchingBoundary', + ]), + ]); + expect(rootCaughtErrors).toEqual([ 'uh oh', componentStack([ 'SomethingThatErrors', @@ -98,77 +142,171 @@ describe('ReactFragment', () => { 'Foo', 'CatchingBoundary', ]), + __DEV__ ? componentStack(['Bar', 'Foo']) : null, ]); }); - it('retains owner stacks when rethrowing an error', async () => { - function Foo() { - return ( - <RethrowingBoundary> - <Bar /> - </RethrowingBoundary> - ); - } - function Bar() { - return <SomethingThatErrors />; - } - function SomethingThatErrors() { + it('includes built-in for Suspense', async () => { + ReactNoop.createRoot({ + onCaughtError, + }).render( + <CatchingBoundary> + <Suspense> + <SomethingThatErrors /> + </Suspense> + </CatchingBoundary>, + ); + await waitForAll([]); + expect(didCatchErrors).toEqual([ + 'uh oh', + componentStack(['SomethingThatErrors', 'Suspense', 'CatchingBoundary']), + ]); + expect(rootCaughtErrors).toEqual([ + 'uh oh', + componentStack(['SomethingThatErrors', 'Suspense', 'CatchingBoundary']), + __DEV__ ? componentStack(['SomethingThatErrors']) : null, + ]); + }); + + // @gate enableActivity + it('includes built-in for Activity', async () => { + ReactNoop.createRoot({ + onCaughtError, + }).render( + <CatchingBoundary> + <Activity> + <SomethingThatErrors /> + </Activity> + </CatchingBoundary>, + ); + await waitForAll([]); + expect(didCatchErrors).toEqual([ + 'uh oh', + componentStack(['SomethingThatErrors', 'Activity', 'CatchingBoundary']), + ]); + expect(rootCaughtErrors).toEqual([ + 'uh oh', + componentStack(['SomethingThatErrors', 'Activity', 'CatchingBoundary']), + __DEV__ ? componentStack(['SomethingThatErrors']) : null, + ]); + }); + + // @gate enableViewTransition + it('includes built-in for ViewTransition', async () => { + ReactNoop.createRoot({ + onCaughtError, + }).render( + <CatchingBoundary> + <ViewTransition> + <SomethingThatErrors /> + </ViewTransition> + </CatchingBoundary>, + ); + await waitForAll([]); + expect(didCatchErrors).toEqual([ + 'uh oh', + componentStack([ + 'SomethingThatErrors', + 'ViewTransition', + 'CatchingBoundary', + ]), + ]); + expect(rootCaughtErrors).toEqual([ + 'uh oh', + componentStack([ + 'SomethingThatErrors', + 'ViewTransition', + 'CatchingBoundary', + ]), + __DEV__ ? componentStack(['SomethingThatErrors']) : null, + ]); + }); + + it('includes built-in for Lazy', async () => { + // Lazy component throws + const LazyComponent = React.lazy(() => { throw new Error('uh oh'); - } + }); - class RethrowingBoundary extends React.Component { - static getDerivedStateFromError(error) { - throw error; - } + ReactNoop.createRoot({ + onCaughtError, + }).render( + <CatchingBoundary> + <LazyComponent /> + </CatchingBoundary>, + ); + await waitForAll([]); + expect(didCatchErrors).toEqual([ + 'uh oh', + componentStack(['Lazy', 'CatchingBoundary']), + ]); + expect(rootCaughtErrors).toEqual([ + 'uh oh', + componentStack(['Lazy', 'CatchingBoundary']), + __DEV__ ? '' : null, // No owner stack + ]); + }); - render() { - return this.props.children; - } - } + // @gate enableSuspenseList + it('includes built-in for SuspenseList', async () => { + const SuspenseList = React.unstable_SuspenseList; - const errors = []; - class CatchingBoundary extends React.Component { - constructor() { - super(); - this.state = {}; - } - static getDerivedStateFromError(error) { - return {errored: true}; - } - render() { - if (this.state.errored) { - return null; - } - return this.props.children; - } - } + ReactNoop.createRoot({ + onCaughtError, + }).render( + <CatchingBoundary> + <SuspenseList> + <SomethingThatErrors /> + </SuspenseList> + </CatchingBoundary>, + ); + await waitForAll([]); + expect(didCatchErrors).toEqual([ + 'uh oh', + componentStack([ + 'SomethingThatErrors', + 'SuspenseList', + 'CatchingBoundary', + ]), + ]); + expect(rootCaughtErrors).toEqual([ + 'uh oh', + componentStack([ + 'SomethingThatErrors', + 'SuspenseList', + 'CatchingBoundary', + ]), + __DEV__ ? componentStack(['SomethingThatErrors']) : null, + ]); + }); + it('does not include built-in for Fragment', async () => { ReactNoop.createRoot({ - onCaughtError(error, errorInfo) { - errors.push( - error.message, - normalizeCodeLocInfo(errorInfo.componentStack), - React.captureOwnerStack - ? normalizeCodeLocInfo(React.captureOwnerStack()) - : null, - ); - }, + onCaughtError, }).render( <CatchingBoundary> - <Foo /> + <> + <SomethingThatErrors /> + </> </CatchingBoundary>, ); await waitForAll([]); - expect(errors).toEqual([ + expect(didCatchErrors).toEqual([ 'uh oh', componentStack([ 'SomethingThatErrors', - 'Bar', - 'RethrowingBoundary', - 'Foo', + // No Fragment 'CatchingBoundary', ]), - __DEV__ ? componentStack(['Bar', 'Foo']) : null, + ]); + expect(rootCaughtErrors).toEqual([ + 'uh oh', + componentStack([ + 'SomethingThatErrors', + // No Fragment + 'CatchingBoundary', + ]), + __DEV__ ? componentStack(['SomethingThatErrors']) : null, ]); }); }); diff --git a/packages/react-reconciler/src/getComponentNameFromFiber.js b/packages/react-reconciler/src/getComponentNameFromFiber.js index 04ad072c2583f..670475cdec5a0 100644 --- a/packages/react-reconciler/src/getComponentNameFromFiber.js +++ b/packages/react-reconciler/src/getComponentNameFromFiber.js @@ -47,6 +47,7 @@ import { TracingMarkerComponent, Throw, ViewTransitionComponent, + ActivityComponent, } from 'react-reconciler/src/ReactWorkTags'; import getComponentNameFromType from 'shared/getComponentNameFromType'; import {REACT_STRICT_MODE_TYPE} from 'shared/ReactSymbols'; @@ -85,6 +86,8 @@ export function getComponentNameFromOwner( export default function getComponentNameFromFiber(fiber: Fiber): string | null { const {tag, type} = fiber; switch (tag) { + case ActivityComponent: + return 'Activity'; case CacheComponent: return 'Cache'; case ContextConsumer: diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index b87b8955f3654..6d4db474e355a 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -151,9 +151,9 @@ import { REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE, REACT_SCOPE_TYPE, - REACT_OFFSCREEN_TYPE, REACT_POSTPONE_TYPE, REACT_VIEW_TRANSITION_TYPE, + REACT_ACTIVITY_TYPE, } from 'shared/ReactSymbols'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import { @@ -2253,7 +2253,7 @@ function renderElement( task.keyPath = prevKeyPath; return; } - case REACT_OFFSCREEN_TYPE: { + case REACT_ACTIVITY_TYPE: { renderOffscreen(request, task, keyPath, props); return; } diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index 33175a9a77f35..b48a3cdb1a74e 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -15,7 +15,7 @@ import { REACT_SUSPENSE_TYPE, REACT_SUSPENSE_LIST_TYPE, REACT_LEGACY_HIDDEN_TYPE, - REACT_OFFSCREEN_TYPE, + REACT_ACTIVITY_TYPE, REACT_SCOPE_TYPE, REACT_TRACING_MARKER_TYPE, REACT_VIEW_TRANSITION_TYPE, @@ -116,7 +116,7 @@ export { useDeferredValue, REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList, REACT_LEGACY_HIDDEN_TYPE as unstable_LegacyHidden, - REACT_OFFSCREEN_TYPE as unstable_Activity, + REACT_ACTIVITY_TYPE as unstable_Activity, getCacheForType as unstable_getCacheForType, useCacheRefresh as unstable_useCacheRefresh, use, diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index fc5893585900f..1fd00cd76d8c1 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -34,6 +34,7 @@ export const REACT_MEMO_TYPE: symbol = Symbol.for('react.memo'); export const REACT_LAZY_TYPE: symbol = Symbol.for('react.lazy'); export const REACT_SCOPE_TYPE: symbol = Symbol.for('react.scope'); export const REACT_OFFSCREEN_TYPE: symbol = Symbol.for('react.offscreen'); +export const REACT_ACTIVITY_TYPE: symbol = Symbol.for('react.activity'); export const REACT_LEGACY_HIDDEN_TYPE: symbol = Symbol.for( 'react.legacy_hidden', ); diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 92726b8f6ec45..002652080ca3d 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -25,6 +25,7 @@ import { REACT_LAZY_TYPE, REACT_TRACING_MARKER_TYPE, REACT_VIEW_TRANSITION_TYPE, + REACT_ACTIVITY_TYPE, } from 'shared/ReactSymbols'; import { @@ -83,7 +84,8 @@ export default function getComponentNameFromType(type: mixed): string | null { return 'Suspense'; case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; - // Fall through + case REACT_ACTIVITY_TYPE: + return 'Activity'; case REACT_VIEW_TRANSITION_TYPE: if (enableViewTransition) { return 'ViewTransition'; diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js index 008d2dc49aa49..41370b47a08c6 100644 --- a/packages/shared/isValidElementType.js +++ b/packages/shared/isValidElementType.js @@ -24,6 +24,7 @@ import { REACT_OFFSCREEN_TYPE, REACT_TRACING_MARKER_TYPE, REACT_VIEW_TRANSITION_TYPE, + REACT_ACTIVITY_TYPE, } from 'shared/ReactSymbols'; import { enableScopeAPI, @@ -50,6 +51,7 @@ export default function isValidElementType(type: mixed): boolean { type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || (enableLegacyHidden && type === REACT_LEGACY_HIDDEN_TYPE) || + type === REACT_ACTIVITY_TYPE || type === REACT_OFFSCREEN_TYPE || (enableScopeAPI && type === REACT_SCOPE_TYPE) || (enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE) || From df319522758b7fdfed3ddfa517cc1cc298ef1602 Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Mon, 17 Mar 2025 09:37:07 -0400 Subject: [PATCH 145/300] Remove offscreen type (#32639) Based off https://github.com/facebook/react/pull/32499 This is no longer used. [Review commit](https://github.com/facebook/react/commit/88c297d12f8b2562be3982fba867f03a137551cb) --- packages/react-reconciler/src/ReactFiber.js | 4 ---- packages/shared/ReactSymbols.js | 1 - packages/shared/isValidElementType.js | 2 -- 3 files changed, 7 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 7b63135c05f29..7f5fbe59b8e90 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -103,7 +103,6 @@ import { REACT_MEMO_TYPE, REACT_LAZY_TYPE, REACT_SCOPE_TYPE, - REACT_OFFSCREEN_TYPE, REACT_LEGACY_HIDDEN_TYPE, REACT_TRACING_MARKER_TYPE, REACT_ELEMENT_TYPE, @@ -614,8 +613,6 @@ export function createFiberFromTypeAndProps( return createFiberFromSuspense(pendingProps, mode, lanes, key); case REACT_SUSPENSE_LIST_TYPE: return createFiberFromSuspenseList(pendingProps, mode, lanes, key); - case REACT_OFFSCREEN_TYPE: - return createFiberFromOffscreen(pendingProps, mode, lanes, key); case REACT_LEGACY_HIDDEN_TYPE: if (enableLegacyHidden) { return createFiberFromLegacyHidden(pendingProps, mode, lanes, key); @@ -854,7 +851,6 @@ export function createFiberFromOffscreen( key: null | string, ): Fiber { const fiber = createFiber(OffscreenComponent, pendingProps, key, mode); - fiber.elementType = REACT_OFFSCREEN_TYPE; fiber.lanes = lanes; const primaryChildInstance: OffscreenInstance = { _visibility: OffscreenVisible, diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 1fd00cd76d8c1..937c01cf75912 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -33,7 +33,6 @@ export const REACT_SUSPENSE_LIST_TYPE: symbol = Symbol.for( export const REACT_MEMO_TYPE: symbol = Symbol.for('react.memo'); export const REACT_LAZY_TYPE: symbol = Symbol.for('react.lazy'); export const REACT_SCOPE_TYPE: symbol = Symbol.for('react.scope'); -export const REACT_OFFSCREEN_TYPE: symbol = Symbol.for('react.offscreen'); export const REACT_ACTIVITY_TYPE: symbol = Symbol.for('react.activity'); export const REACT_LEGACY_HIDDEN_TYPE: symbol = Symbol.for( 'react.legacy_hidden', diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js index 41370b47a08c6..84e4fe802a930 100644 --- a/packages/shared/isValidElementType.js +++ b/packages/shared/isValidElementType.js @@ -21,7 +21,6 @@ import { REACT_LAZY_TYPE, REACT_SCOPE_TYPE, REACT_LEGACY_HIDDEN_TYPE, - REACT_OFFSCREEN_TYPE, REACT_TRACING_MARKER_TYPE, REACT_VIEW_TRANSITION_TYPE, REACT_ACTIVITY_TYPE, @@ -52,7 +51,6 @@ export default function isValidElementType(type: mixed): boolean { type === REACT_SUSPENSE_LIST_TYPE || (enableLegacyHidden && type === REACT_LEGACY_HIDDEN_TYPE) || type === REACT_ACTIVITY_TYPE || - type === REACT_OFFSCREEN_TYPE || (enableScopeAPI && type === REACT_SCOPE_TYPE) || (enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE) || (enableViewTransition && type === REACT_VIEW_TRANSITION_TYPE) From 8243f3f0631698e819c690710a7f18f767068981 Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Mon, 17 Mar 2025 11:23:28 -0400 Subject: [PATCH 146/300] [bug] Fix component name for Portal and add tests (#32640) Based off: https://github.com/facebook/react/pull/32499 While looking into `React.lazy` issues for built-ins, I noticed we already error for `lazy` with build-ins, but we don't have any tests for `getComponentNameFromType` using all the built-ins. This may be something we should handle, but for now we should at least have tests. Here's why: while writing tests, I noticed we check `type` instead of `$$typeof` for portals: https://github.com/facebook/react/blob/9cdf8a99edcfd94d7420835ea663edca04237527/packages/react-reconciler/src/ReactPortal.js#L25-L32 This PR adds tests for all the built-ins and fixes the portal bug. [Commit to review](https://github.com/facebook/react/pull/32640/commits/e068c167d48d4df01e79db8f13276bb46d7ab439) --- .../src/__tests__/ReactLazy-test.internal.js | 276 +++++++++++++++++- packages/shared/getComponentNameFromType.js | 4 +- 2 files changed, 277 insertions(+), 3 deletions(-) diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 5472c9c89fb6b..e8c2153cab94a 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -791,7 +791,7 @@ describe('ReactLazy', () => { ); }); - it('throws with a useful error when wrapping fragment with lazy()', async () => { + it('throws with a useful error when wrapping Fragment with lazy()', async () => { const BadLazy = lazy(() => fakeImport(React.Fragment)); const root = ReactTestRenderer.create( @@ -817,6 +817,280 @@ describe('ReactLazy', () => { ); }); + // @gate !fb + it('throws with a useful error when wrapping createPortal with lazy()', async () => { + const ReactDOM = require('react-dom'); + const container = document.createElement('div'); + const portal = ReactDOM.createPortal(<div />, container); + const BadLazy = lazy(() => fakeImport(portal)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(portal); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Portal. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping Profiler with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.Profiler)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.Profiler); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Profiler. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping StrictMode with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.StrictMode)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.StrictMode); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: StrictMode. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping Suspense with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.Suspense)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.Suspense); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Suspense. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping Context with lazy()', async () => { + const Context = React.createContext(null); + const BadLazy = lazy(() => fakeImport(Context)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(Context); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + gate('enableRenderableContext') + ? 'Element type is invalid. Received a promise that resolves to: Context.Provider. ' + + 'Lazy element type must resolve to a class or function.' + : 'Element type is invalid. Received a promise that resolves to: Context.Consumer. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableRenderableContext + it('throws with a useful error when wrapping Context.Consumer with lazy()', async () => { + const Context = React.createContext(null); + const BadLazy = lazy(() => fakeImport(Context.Consumer)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(Context.Consumer); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Context.Consumer. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableSuspenseList + it('throws with a useful error when wrapping SuspenseList with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_SuspenseList)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_SuspenseList); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: SuspenseList. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableViewTransition + it('throws with a useful error when wrapping ViewTransition with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_ViewTransition)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_ViewTransition); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: ViewTransition. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableActivity + it('throws with a useful error when wrapping Activity with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_Activity)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_Activity); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Activity. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableTransitionTracing + it('throws with a useful error when wrapping TracingMarker with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_TracingMarker)); + + const root = ReactTestRenderer.create( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_TracingMarker); + root.update( + <Suspense fallback={<Text text="Loading..." />}> + <BadLazy /> + </Suspense>, + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: TracingMarker. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + it('throws with a useful error when wrapping lazy() multiple times', async () => { const Lazy1 = lazy(() => fakeImport(Text)); const Lazy2 = lazy(() => fakeImport(Lazy1)); diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 002652080ca3d..da5cba301edc1 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -74,8 +74,6 @@ export default function getComponentNameFromType(type: mixed): string | null { switch (type) { case REACT_FRAGMENT_TYPE: return 'Fragment'; - case REACT_PORTAL_TYPE: - return 'Portal'; case REACT_PROFILER_TYPE: return 'Profiler'; case REACT_STRICT_MODE_TYPE: @@ -106,6 +104,8 @@ export default function getComponentNameFromType(type: mixed): string | null { } } switch (type.$$typeof) { + case REACT_PORTAL_TYPE: + return 'Portal'; case REACT_PROVIDER_TYPE: if (enableRenderableContext) { return null; From cd28a946d57695a025581c0ff851bde08ea6ca27 Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Mon, 17 Mar 2025 11:40:05 -0400 Subject: [PATCH 147/300] Add observer methods to fragment instances (#32619) This implements `observeUsing(observer)` and `unobserverUsing(observer)` on fragment instances. IntersectionObservers and ResizeObservers can be passed to observe each host child of the fragment. This is the equivalent to calling `observer.observe(child)` or `observer.unobserve(child)` for each child target. Just like the addEventListener, the observer is held on the fragment instance and applied to any newly mounted child. So you can do things like wrap a paginated list in a fragment and have each child automatically observed as they commit in. Unlike, the event listeners though, we don't `unobserve` when a child is removed. If a removed child is currently intersecting, the observer callback will be called when it is removed with an empty rect. This lets you track all the currently intersecting elements by setting state from the observer callback and either adding or removing them from your list depending on the intersecting state. If you want to track the removal of items offscreen, you'd have to maintain that state separately and append intersecting data to it in the observer callback. This is what the fixture example does. There could be more convenient ways of managing the state of multiple child intersections, but basic examples are able to be modeled with the simple implementation. Let's see how the usage goes as we integrate this with more advanced loggers and other features. For now you can only attach one observer to an instance. This could change based on usage but the fragments are composable and could be stacked as one way to apply multiple observers to the same elements. In practice, one pattern we expect to enable is more composable logging such as ```javascript function Feed({ items }) { return ( <ImpressionLogger> {items.map((item) => ( <FeedItem /> ))} </ImpressionLogger> ); } ``` where `ImpressionLogger` would set up the IntersectionObserver using a fragment ref with the required business logic and various components could layer it wherever the logging is needed. Currently most callsites use a hook form, which can require wiring up refs through the tree and merging refs for multiple loggers. --- .../fragment-refs/EventListenerCase.js | 96 +++++++++++ .../fragment-refs/IntersectionObserverCase.js | 153 ++++++++++++++++++ .../fragment-refs/ResizeObserverCase.js | 63 ++++++++ .../fixtures/fragment-refs/index.js | 100 +----------- fixtures/dom/src/style.css | 36 +++++ .../src/client/ReactFiberConfigDOM.js | 51 ++++++ .../__tests__/ReactDOMFragmentRefs-test.js | 114 +++++++++++++ .../__tests__/ReactDOMTestSelectors-test.js | 105 +++--------- .../src/__tests__/utils/IntersectionMocks.js | 77 +++++++++ 9 files changed, 615 insertions(+), 180 deletions(-) create mode 100644 fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js create mode 100644 fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js create mode 100644 fixtures/dom/src/components/fixtures/fragment-refs/ResizeObserverCase.js create mode 100644 packages/react-dom/src/__tests__/utils/IntersectionMocks.js diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js new file mode 100644 index 0000000000000..b3baf5a381d7a --- /dev/null +++ b/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js @@ -0,0 +1,96 @@ +import TestCase from '../../TestCase'; +import Fixture from '../../Fixture'; + +const React = window.React; +const {Fragment, useEffect, useRef, useState} = React; + +function WrapperComponent(props) { + return props.children; +} + +function handler(e) { + const text = e.currentTarget.innerText; + alert('You clicked: ' + text); +} + +export default function EventListenerCase() { + const fragmentRef = useRef(null); + const [extraChildCount, setExtraChildCount] = useState(0); + + useEffect(() => { + fragmentRef.current.addEventListener('click', handler); + + const lastFragmentRefValue = fragmentRef.current; + return () => { + lastFragmentRefValue.removeEventListener('click', handler); + }; + }); + + return ( + <TestCase title="Event Registration"> + <TestCase.Steps> + <li>Click one of the children, observe the alert</li> + <li>Add a new child, click it, observe the alert</li> + <li>Remove the event listeners, click a child, observe no alert</li> + <li>Add the event listeners back, click a child, observe the alert</li> + </TestCase.Steps> + + <TestCase.ExpectedResult> + <p> + Fragment refs can manage event listeners on the first level of host + children. This page loads with an effect that sets up click event + hanndlers on each child card. Clicking on a card will show an alert + with the card's text. + </p> + <p> + New child nodes will also have event listeners applied. Removed nodes + will have their listeners cleaned up. + </p> + </TestCase.ExpectedResult> + + <Fixture> + <div className="control-box"> + <div>Target count: {extraChildCount + 3}</div> + <button + onClick={() => { + setExtraChildCount(prev => prev + 1); + }}> + Add Child + </button> + <button + onClick={() => { + fragmentRef.current.addEventListener('click', handler); + }}> + Add click event listeners + </button> + <button + onClick={() => { + fragmentRef.current.removeEventListener('click', handler); + }}> + Remove click event listeners + </button> + <div class="card-container"> + <Fragment ref={fragmentRef}> + <div className="card" id="child-a"> + Child A + </div> + <div className="card" id="child-b"> + Child B + </div> + <WrapperComponent> + <div className="card" id="child-c"> + Child C + </div> + {Array.from({length: extraChildCount}).map((_, index) => ( + <div className="card" id={'extra-child-' + index} key={index}> + Extra Child {index} + </div> + ))} + </WrapperComponent> + </Fragment> + </div> + </div> + </Fixture> + </TestCase> + ); +} diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js new file mode 100644 index 0000000000000..e087215aee4df --- /dev/null +++ b/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js @@ -0,0 +1,153 @@ +import TestCase from '../../TestCase'; +import Fixture from '../../Fixture'; + +const React = window.React; +const {Fragment, useEffect, useRef, useState} = React; + +function WrapperComponent(props) { + return props.children; +} + +function ObservedChild({id}) { + return ( + <div id={id} className="observable-card"> + {id} + </div> + ); +} + +const initialItems = [ + ['A', false], + ['B', false], + ['C', false], +]; + +export default function IntersectionObserverCase() { + const fragmentRef = useRef(null); + const [items, setItems] = useState(initialItems); + const addedItems = items.slice(3); + const anyOnScreen = items.some(([, onScreen]) => onScreen); + const observerRef = useRef(null); + + useEffect(() => { + if (observerRef.current === null) { + observerRef.current = new IntersectionObserver( + entries => { + setItems(prev => { + const newItems = [...prev]; + entries.forEach(entry => { + const index = newItems.findIndex( + ([id]) => id === entry.target.id + ); + newItems[index] = [entry.target.id, entry.isIntersecting]; + }); + return newItems; + }); + }, + { + threshold: [0.5], + } + ); + } + fragmentRef.current.observeUsing(observerRef.current); + + const lastFragmentRefValue = fragmentRef.current; + return () => { + lastFragmentRefValue.unobserveUsing(observerRef.current); + observerRef.current = null; + }; + }, []); + + return ( + <TestCase title="Intersection Observer"> + <TestCase.Steps> + <li> + Scroll the children into view, observe the sidebar appears and shows + which children are in the viewport + </li> + <li> + Add a new child and observe that the Intersection Observer is applied + </li> + <li> + Click Unobserve and observe that the state of children in the viewport + is no longer updated + </li> + <li> + Click Observe and observe that the state of children in the viewport + is updated again + </li> + </TestCase.Steps> + + <TestCase.ExpectedResult> + <p> + Fragment refs manage Intersection Observers on the first level of host + children. This page loads with an effect that sets up an Inersection + Observer applied to each child card. + </p> + <p> + New child nodes will also have the observer applied. Removed nodes + will be unobserved. + </p> + </TestCase.ExpectedResult> + <Fixture> + <button + onClick={() => { + setItems(prev => [ + ...prev, + [`Extra child: ${prev.length + 1}`, false], + ]); + }}> + Add Child + </button> + <button + onClick={() => { + setItems(prev => { + if (prev.length === 3) { + return prev; + } + return prev.slice(0, prev.length - 1); + }); + }}> + Remove Child + </button> + <button + onClick={() => { + fragmentRef.current.observeUsing(observerRef.current); + }}> + Observe + </button> + <button + onClick={() => { + fragmentRef.current.unobserveUsing(observerRef.current); + setItems(prev => { + return prev.map(item => [item[0], false]); + }); + }}> + Unobserve + </button> + {anyOnScreen && ( + <div className="fixed-sidebar card-container"> + <p> + <strong>Children on screen:</strong> + </p> + {items.map(item => ( + <div className={`card ${item[1] ? 'onscreen' : null}`}> + {item[0]} + </div> + ))} + </div> + )} + <Fragment ref={fragmentRef}> + <ObservedChild id="A" /> + <WrapperComponent> + <ObservedChild id="B" /> + </WrapperComponent> + <ObservedChild id="C" /> + {addedItems.map((_, index) => ( + <ObservedChild id={`Extra child: ${index + 4}`} /> + ))} + </Fragment> + </Fixture> + </TestCase> + ); +} diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/ResizeObserverCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/ResizeObserverCase.js new file mode 100644 index 0000000000000..ecacd50921374 --- /dev/null +++ b/fixtures/dom/src/components/fixtures/fragment-refs/ResizeObserverCase.js @@ -0,0 +1,63 @@ +import TestCase from '../../TestCase'; +import Fixture from '../../Fixture'; + +const React = window.React; +const {Fragment, useEffect, useRef, useState} = React; + +export default function ResizeObserverCase() { + const fragmentRef = useRef(null); + const [width, setWidth] = useState([0, 0, 0]); + + useEffect(() => { + const resizeObserver = new window.ResizeObserver(entries => { + if (entries.length > 0) { + setWidth(prev => { + const newWidth = [...prev]; + entries.forEach(entry => { + const index = parseInt(entry.target.id, 10); + newWidth[index] = Math.round(entry.contentRect.width); + }); + return newWidth; + }); + } + }); + + fragmentRef.current.observeUsing(resizeObserver); + const lastFragmentRefValue = fragmentRef.current; + return () => { + lastFragmentRefValue.unobserveUsing(resizeObserver); + }; + }, []); + + return ( + <TestCase title="Resize Observer"> + <TestCase.Steps> + <li>Resize the viewport width until the children respond</li> + <li>See that the width data updates as they elements resize</li> + </TestCase.Steps> + <TestCase.ExpectedResult> + The Fragment Ref has a ResizeObserver attached which has a callback to + update the width state of each child node. + </TestCase.ExpectedResult> + <Fixture> + <Fragment ref={fragmentRef}> + <div className="card" id="0" style={{width: '100%'}}> + <p> + Width: <b>{width[0]}px</b> + </p> + </div> + <div className="card" id="1" style={{width: '80%'}}> + <p> + Width: <b>{width[1]}px</b> + </p> + </div> + <div className="card" id="2" style={{width: '50%'}}> + <p> + Width: <b>{width[2]}px</b> + </p> + </div> + </Fragment> + </Fixture> + </TestCase> + ); +} diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/index.js b/fixtures/dom/src/components/fixtures/fragment-refs/index.js index bd4468126e43b..03d95ec30a98d 100644 --- a/fixtures/dom/src/components/fixtures/fragment-refs/index.js +++ b/fixtures/dom/src/components/fixtures/fragment-refs/index.js @@ -1,104 +1,16 @@ -import Fixture from '../../Fixture'; import FixtureSet from '../../FixtureSet'; -import TestCase from '../../TestCase'; +import EventListenerCase from './EventListenerCase'; +import IntersectionObserverCase from './IntersectionObserverCase'; +import ResizeObserverCase from './ResizeObserverCase'; const React = window.React; -const {Fragment, useEffect, useRef, useState} = React; - -function WrapperComponent(props) { - return props.children; -} - -function handler(e) { - const text = e.currentTarget.innerText; - alert('You clicked: ' + text); -} export default function FragmentRefsPage() { - const fragmentRef = useRef(null); - const [extraChildCount, setExtraChildCount] = useState(0); - - React.useEffect(() => { - fragmentRef.current.addEventListener('click', handler); - - const lastFragmentRefValue = fragmentRef.current; - return () => { - lastFragmentRefValue.removeEventListener('click', handler); - }; - }); - return ( <FixtureSet title="Fragment Refs"> - <TestCase title="Event registration"> - <TestCase.Steps> - <li>Click one of the children, observe the alert</li> - <li>Add a new child, click it, observe the alert</li> - <li>Remove the event listeners, click a child, observe no alert</li> - <li> - Add the event listeners back, click a child, observe the alert - </li> - </TestCase.Steps> - - <TestCase.ExpectedResult> - <p> - Fragment refs can manage event listeners on the first level of host - children. This page loads with an effect that sets up click event - hanndlers on each child card. Clicking on a card will show an alert - with the card's text. - </p> - <p> - New child nodes will also have event listeners applied. Removed - nodes will have their listeners cleaned up. - </p> - </TestCase.ExpectedResult> - - <Fixture> - <div className="control-box" id="control-box"> - <div>Target count: {extraChildCount + 3}</div> - <button - onClick={() => { - setExtraChildCount(prev => prev + 1); - }}> - Add Child - </button> - <button - onClick={() => { - fragmentRef.current.addEventListener('click', handler); - }}> - Add click event listeners - </button> - <button - onClick={() => { - fragmentRef.current.removeEventListener('click', handler); - }}> - Remove click event listeners - </button> - <div class="card-container"> - <Fragment ref={fragmentRef}> - <div className="card" id="child-a"> - Child A - </div> - <div className="card" id="child-b"> - Child B - </div> - <WrapperComponent> - <div className="card" id="child-c"> - Child C - </div> - {Array.from({length: extraChildCount}).map((_, index) => ( - <div - className="card" - id={'extra-child-' + index} - key={index}> - Extra Child {index} - </div> - ))} - </WrapperComponent> - </Fragment> - </div> - </div> - </Fixture> - </TestCase> + <EventListenerCase /> + <IntersectionObserverCase /> + <ResizeObserverCase /> </FixtureSet> ); } diff --git a/fixtures/dom/src/style.css b/fixtures/dom/src/style.css index 568d8b4b0d1b5..30a9ed5fff972 100644 --- a/fixtures/dom/src/style.css +++ b/fixtures/dom/src/style.css @@ -322,3 +322,39 @@ tbody tr:nth-child(even) { margin: 10px; padding: 10px; } + +.observable-card { + height: 200px; + border: 1px solid black; + background: #e0e0e0; + padding: 20px; + font-size: 18px; + overflow: auto; + margin-bottom: 50px; + position: relative; +} + +.observable-card::after { + content: ""; + position: absolute; + top: 50%; + left: 0; + width: 100%; + border-top: 1px dotted red; +} + +.fixed-sidebar { + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 200px; + z-index: 1000; + background-color: gray; + display: flex; + flex-direction: column; +} + +.onscreen { + background-color: green; +} diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 1e9ee657e0ca8..2d34bc94005be 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -2186,6 +2186,7 @@ type StoredEventListener = { export type FragmentInstanceType = { _fragmentFiber: Fiber, _eventListeners: null | Array<StoredEventListener>, + _observers: null | Set<IntersectionObserver | ResizeObserver>, addEventListener( type: string, listener: EventListener, @@ -2197,11 +2198,14 @@ export type FragmentInstanceType = { optionsOrUseCapture?: EventListenerOptionsOrUseCapture, ): void, focus(): void, + observeUsing(observer: IntersectionObserver | ResizeObserver): void, + unobserveUsing(observer: IntersectionObserver | ResizeObserver): void, }; function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) { this._fragmentFiber = fragmentFiber; this._eventListeners = null; + this._observers = null; } // $FlowFixMe[prop-missing] FragmentInstance.prototype.addEventListener = function ( @@ -2284,6 +2288,48 @@ function removeEventListenerFromChild( FragmentInstance.prototype.focus = function (this: FragmentInstanceType) { traverseFragmentInstance(this._fragmentFiber, setFocusIfFocusable); }; +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.observeUsing = function ( + this: FragmentInstanceType, + observer: IntersectionObserver | ResizeObserver, +): void { + if (this._observers === null) { + this._observers = new Set(); + } + this._observers.add(observer); + traverseFragmentInstance(this._fragmentFiber, observeChild, observer); +}; +function observeChild( + child: Instance, + observer: IntersectionObserver | ResizeObserver, +) { + observer.observe(child); + return false; +} +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.unobserveUsing = function ( + this: FragmentInstanceType, + observer: IntersectionObserver | ResizeObserver, +): void { + if (this._observers === null || !this._observers.has(observer)) { + if (__DEV__) { + console.error( + 'You are calling unobserveUsing() with an observer that is not being observed with this fragment ' + + 'instance. First attach the observer with observeUsing()', + ); + } + } else { + this._observers.delete(observer); + traverseFragmentInstance(this._fragmentFiber, unobserveChild, observer); + } +}; +function unobserveChild( + child: Instance, + observer: IntersectionObserver | ResizeObserver, +) { + observer.unobserve(child); + return false; +} function normalizeListenerOptions( opts: ?EventListenerOptionsOrUseCapture, @@ -2343,6 +2389,11 @@ export function commitNewChildToFragmentInstance( childElement.addEventListener(type, listener, optionsOrUseCapture); } } + if (fragmentInstance._observers !== null) { + fragmentInstance._observers.forEach(observer => { + observer.observe(childElement); + }); + } } export function deleteChildFromFragmentInstance( diff --git a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js index 727fd3014201f..e1d8532b7ee99 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js @@ -15,6 +15,9 @@ let act; let container; let Fragment; let Activity; +let mockIntersectionObserver; +let simulateIntersection; +let assertConsoleErrorDev; describe('FragmentRefs', () => { beforeEach(() => { @@ -24,6 +27,12 @@ describe('FragmentRefs', () => { Activity = React.unstable_Activity; ReactDOMClient = require('react-dom/client'); act = require('internal-test-utils').act; + const IntersectionMocks = require('./utils/IntersectionMocks'); + mockIntersectionObserver = IntersectionMocks.mockIntersectionObserver; + simulateIntersection = IntersectionMocks.simulateIntersection; + assertConsoleErrorDev = + require('internal-test-utils').assertConsoleErrorDev; + container = document.createElement('div'); document.body.appendChild(container); }); @@ -617,4 +626,109 @@ describe('FragmentRefs', () => { }); }); }); + + describe('observers', () => { + beforeEach(() => { + mockIntersectionObserver(); + }); + + // @gate enableFragmentRefs + it('attaches intersection observers to children', async () => { + let logs = []; + const observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + logs.push(entry.target.id); + }); + }); + function Test({showB}) { + const fragmentRef = React.useRef(null); + React.useEffect(() => { + fragmentRef.current.observeUsing(observer); + const lastRefValue = fragmentRef.current; + return () => { + lastRefValue.unobserveUsing(observer); + }; + }, []); + return ( + <div id="parent"> + <React.Fragment ref={fragmentRef}> + <div id="childA">A</div> + {showB && <div id="childB">B</div>} + </React.Fragment> + </div> + ); + } + + function simulateAllChildrenIntersecting() { + const parent = container.firstChild; + if (parent) { + const children = Array.from(parent.children).map(child => { + return [child, {y: 0, x: 0, width: 1, height: 1}, 1]; + }); + simulateIntersection(...children); + } + } + + const root = ReactDOMClient.createRoot(container); + await act(() => root.render(<Test showB={false} />)); + simulateAllChildrenIntersecting(); + expect(logs).toEqual(['childA']); + + // Reveal child and expect it to be observed + logs = []; + await act(() => root.render(<Test showB={true} />)); + simulateAllChildrenIntersecting(); + expect(logs).toEqual(['childA', 'childB']); + + // Hide child and expect it to be unobserved + logs = []; + await act(() => root.render(<Test showB={false} />)); + simulateAllChildrenIntersecting(); + expect(logs).toEqual(['childA']); + + // Unmount component and expect all children to be unobserved + logs = []; + await act(() => root.render(null)); + simulateAllChildrenIntersecting(); + expect(logs).toEqual([]); + }); + + // @gate enableFragmentRefs + it('warns when unobserveUsing() is called with an observer that was not observed', async () => { + const fragmentRef = React.createRef(); + const observer = new IntersectionObserver(() => {}); + const observer2 = new IntersectionObserver(() => {}); + function Test() { + return ( + <React.Fragment ref={fragmentRef}> + <div /> + </React.Fragment> + ); + } + + const root = ReactDOMClient.createRoot(container); + await act(() => root.render(<Test />)); + + // Warning when there is no attached observer + fragmentRef.current.unobserveUsing(observer); + assertConsoleErrorDev( + [ + 'You are calling unobserveUsing() with an observer that is not being observed with this fragment ' + + 'instance. First attach the observer with observeUsing()', + ], + {withoutStack: true}, + ); + + // Warning when the attached observer does not match + fragmentRef.current.observeUsing(observer); + fragmentRef.current.unobserveUsing(observer2); + assertConsoleErrorDev( + [ + 'You are calling unobserveUsing() with an observer that is not being observed with this fragment ' + + 'instance. First attach the observer with observeUsing()', + ], + {withoutStack: true}, + ); + }); + }); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMTestSelectors-test.js b/packages/react-dom/src/__tests__/ReactDOMTestSelectors-test.js index 65bb49e06a1c8..8b6eb0d49d5e7 100644 --- a/packages/react-dom/src/__tests__/ReactDOMTestSelectors-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMTestSelectors-test.js @@ -23,6 +23,9 @@ describe('ReactDOMTestSelectors', () => { let focusWithin; let getFindAllNodesFailureDescription; let observeVisibleRects; + let mockIntersectionObserver; + let simulateIntersection; + let setBoundingClientRect; let container; @@ -51,6 +54,10 @@ describe('ReactDOMTestSelectors', () => { container = document.createElement('div'); document.body.appendChild(container); + const IntersectionMocks = require('./utils/IntersectionMocks'); + mockIntersectionObserver = IntersectionMocks.mockIntersectionObserver; + simulateIntersection = IntersectionMocks.simulateIntersection; + setBoundingClientRect = IntersectionMocks.setBoundingClientRect; }); afterEach(() => { @@ -608,21 +615,6 @@ No matching component was found for: }); describe('findBoundingRects', () => { - // Stub out getBoundingClientRect for the specified target. - // This API is required by the test selectors but it isn't implemented by jsdom. - function setBoundingClientRect(target, {x, y, width, height}) { - target.getBoundingClientRect = function () { - return { - width, - height, - left: x, - right: x + width, - top: y, - bottom: y + height, - }; - }; - } - // @gate www || experimental it('should return a single rect for a component that returns a single root host element', async () => { const ref = React.createRef(); @@ -1223,69 +1215,10 @@ No matching component was found for: }); describe('observeVisibleRects', () => { - // Stub out getBoundingClientRect for the specified target. - // This API is required by the test selectors but it isn't implemented by jsdom. - function setBoundingClientRect(target, {x, y, width, height}) { - target.getBoundingClientRect = function () { - return { - width, - height, - left: x, - right: x + width, - top: y, - bottom: y + height, - }; - }; - } - - function simulateIntersection(...entries) { - callback( - entries.map(([target, rect, ratio]) => ({ - boundingClientRect: { - top: rect.y, - left: rect.x, - width: rect.width, - height: rect.height, - }, - intersectionRatio: ratio, - target, - })), - ); - } - - let callback; - let observedTargets; + let observerMock; beforeEach(() => { - callback = null; - observedTargets = []; - - class IntersectionObserver { - constructor() { - callback = arguments[0]; - } - - disconnect() { - callback = null; - observedTargets.splice(0); - } - - observe(target) { - observedTargets.push(target); - } - - unobserve(target) { - const index = observedTargets.indexOf(target); - if (index >= 0) { - observedTargets.splice(index, 1); - } - } - } - - // This is a broken polyfill. - // It is only intended to provide bare minimum test coverage. - // More meaningful tests will require the use of fixtures. - window.IntersectionObserver = IntersectionObserver; + observerMock = mockIntersectionObserver(); }); // @gate www || experimental @@ -1317,8 +1250,8 @@ No matching component was found for: handleVisibilityChange, ); - expect(callback).not.toBeNull(); - expect(observedTargets).toHaveLength(1); + expect(observerMock.callback).not.toBeNull(); + expect(observerMock.observedTargets).toHaveLength(1); expect(handleVisibilityChange).not.toHaveBeenCalled(); // Simulate IntersectionObserver notification. @@ -1370,8 +1303,8 @@ No matching component was found for: handleVisibilityChange, ); - expect(callback).not.toBeNull(); - expect(observedTargets).toHaveLength(2); + expect(observerMock.callback).not.toBeNull(); + expect(observerMock.observedTargets).toHaveLength(2); expect(handleVisibilityChange).not.toHaveBeenCalled(); // Simulate IntersectionObserver notification. @@ -1437,12 +1370,12 @@ No matching component was found for: handleVisibilityChange, ); - expect(callback).not.toBeNull(); - expect(observedTargets).toHaveLength(1); + expect(observerMock.callback).not.toBeNull(); + expect(observerMock.observedTargets).toHaveLength(1); expect(handleVisibilityChange).not.toHaveBeenCalled(); disconnect(); - expect(callback).toBeNull(); + expect(observerMock.callback).toBeNull(); }); // This test reuires gating because it relies on the __DEV__ only commit hook to work. @@ -1570,9 +1503,9 @@ No matching component was found for: handleVisibilityChange, ); - expect(callback).not.toBeNull(); - expect(observedTargets).toHaveLength(1); - expect(observedTargets[0]).toBe(ref1.current); + expect(observerMock.callback).not.toBeNull(); + expect(observerMock.observedTargets).toHaveLength(1); + expect(observerMock.observedTargets[0]).toBe(ref1.current); }); }); }); diff --git a/packages/react-dom/src/__tests__/utils/IntersectionMocks.js b/packages/react-dom/src/__tests__/utils/IntersectionMocks.js new file mode 100644 index 0000000000000..a6a6b68caa0c7 --- /dev/null +++ b/packages/react-dom/src/__tests__/utils/IntersectionMocks.js @@ -0,0 +1,77 @@ +/** + * 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. + * + */ + +const intersectionObserverMock = {callback: null, observedTargets: []}; + +/** + * This is a broken polyfill. + * It is only intended to provide bare minimum test coverage. + * More meaningful tests will require the use of fixtures. + */ +export function mockIntersectionObserver() { + intersectionObserverMock.callback = null; + intersectionObserverMock.observedTargets = []; + + class IntersectionObserver { + constructor() { + intersectionObserverMock.callback = arguments[0]; + } + + disconnect() { + intersectionObserverMock.callback = null; + intersectionObserverMock.observedTargets.splice(0); + } + + observe(target) { + intersectionObserverMock.observedTargets.push(target); + } + + unobserve(target) { + const index = intersectionObserverMock.observedTargets.indexOf(target); + if (index >= 0) { + intersectionObserverMock.observedTargets.splice(index, 1); + } + } + } + + window.IntersectionObserver = IntersectionObserver; + + return intersectionObserverMock; +} + +export function simulateIntersection(...entries) { + intersectionObserverMock.callback( + entries.map(([target, rect, ratio]) => ({ + boundingClientRect: { + top: rect.y, + left: rect.x, + width: rect.width, + height: rect.height, + }, + intersectionRatio: ratio, + target, + })), + ); +} + +/** + * Stub out getBoundingClientRect for the specified target. + * This API is required by the test selectors but it isn't implemented by jsdom. + */ +export function setBoundingClientRect(target, {x, y, width, height}) { + target.getBoundingClientRect = function () { + return { + width, + height, + left: x, + right: x + width, + top: y, + bottom: y + height, + }; + }; +} From fbcda19a23da819889afdd7164b29c556fbcfc7a Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Mon, 17 Mar 2025 12:14:19 -0400 Subject: [PATCH 148/300] [devtools] add filters for internal builds (#32646) We don't have an experimental-only build of devtools, but we can at least add these filters to the internal build. A better way would be to use feature detection, but I'm not sure how and this isn't a very heavily used feautre. --- .../views/Settings/ComponentsSettings.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js index e9e4f07616ac3..4309b0e5fbe4d 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js @@ -42,6 +42,8 @@ import { ElementTypeOtherOrUnknown, ElementTypeProfiler, ElementTypeSuspense, + ElementTypeActivity, + ElementTypeViewTransition, } from 'react-devtools-shared/src/frontend/types'; import {getDefaultOpenInEditorURL} from 'react-devtools-shared/src/utils'; @@ -56,6 +58,7 @@ import type { RegExpComponentFilter, EnvironmentNameComponentFilter, } from 'react-devtools-shared/src/frontend/types'; +import {isInternalFacebookBuild} from 'react-devtools-feature-flags'; const vscodeFilepath = 'vscode://file/{path}:{line}'; @@ -472,8 +475,9 @@ export default function ComponentsSettings({ ((parseInt(currentTarget.value, 10): any): ElementType), ) }> - {/* TODO: currently only experimental, only list this if it's available */} - {/*<option value={ElementTypeActivity}>activity</option>*/} + {isInternalFacebookBuild && ( + <option value={ElementTypeActivity}>activity</option> + )} <option value={ElementTypeClass}>class</option> <option value={ElementTypeContext}>context</option> <option value={ElementTypeFunction}>function</option> @@ -487,10 +491,11 @@ export default function ComponentsSettings({ <option value={ElementTypeOtherOrUnknown}>other</option> <option value={ElementTypeProfiler}>profiler</option> <option value={ElementTypeSuspense}>suspense</option> - {/* TODO: currently only experimental, only list this if it's available */} - {/*<option value={ElementTypeViewTransition}>*/} - {/* view transition*/} - {/*</option>*/} + {isInternalFacebookBuild && ( + <option value={ElementTypeViewTransition}> + view transition + </option> + )} </select> )} {(componentFilter.type === ComponentFilterLocation || From 9320a0139df876509c8ebb6f6fd950a6690bd5d9 Mon Sep 17 00:00:00 2001 From: Andrew Clark <git@andrewclark.io> Date: Mon, 17 Mar 2025 12:46:27 -0400 Subject: [PATCH 149/300] Fix COMMIT_SHA when generating PR artifacts (#32647) Follow-up to #31850. We want to build using the original commit SHA, not the merge commit that GitHub Actions creates behind the scenes. We were already checking out the correct commit object, but the COMMIT_SHA artifact was still pointing to the merge commit. This should fix the sizebot links to point to working URLs, too. --- .github/workflows/runtime_build_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 1cb04920e89df..63e432b4dcd69 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -330,7 +330,7 @@ jobs: merge-multiple: true - name: Display structure of build run: ls -R build - - run: echo ${{ github.sha }} >> build/COMMIT_SHA + - run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA - name: Scrape warning messages run: | mkdir -p ./build/__test_utils__ @@ -664,7 +664,7 @@ jobs: node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js - name: Display structure of build for PR run: ls -R build - - run: echo ${{ github.sha }} >> build/COMMIT_SHA + - run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA - run: node ./scripts/tasks/danger - name: Archive sizebot results uses: actions/upload-artifact@v4 From 6c6699f3d2d15428575005a037b73bcb0b5bd0e4 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:13:02 -0400 Subject: [PATCH 150/300] [ci] Don't use third party action to push commits (#32648) In light of recent third party actions being compromised, let's just push the commit ourselves rather than use a third party action. We already detect if changes are needed, so the step will only run if so. I also added a `dry_run` option to the manual runs of this workflow for testing. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32648). * #32650 * #32649 * __->__ #32648 --- .../workflows/runtime_commit_artifacts.yml | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 9e60606210f39..6b1cad2245aa6 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -16,6 +16,11 @@ on: required: true default: false type: boolean + dry_run: + description: Perform a dry run (run everything except push) + required: true + default: false + type: boolean env: TZ: /usr/share/zoneinfo/America/Los_Angeles @@ -246,16 +251,16 @@ jobs: git status -u - name: Commit changes to branch if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true' - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: | - ${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }} + run: | + git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}" + git config --global user.name "${{ github.triggering_actor }}" - DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }}) - branch: builds/facebook-www - commit_user_name: ${{ github.triggering_actor }} - commit_user_email: ${{ format('{0}@users.noreply.github.com', github.triggering_actor) }} - create_branch: true + git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }} + + DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit" + - name: Push changes to branch + if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true') + run: git push commit_fbsource_artifacts: needs: download_artifacts @@ -413,13 +418,13 @@ jobs: git status - name: Commit changes to branch if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true' - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: | - ${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }} + run: | + git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}" + git config --global user.name "${{ github.triggering_actor }}" + + git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }} - DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }}) - branch: builds/facebook-fbsource - commit_user_name: ${{ github.triggering_actor }} - commit_user_email: ${{ format('{0}@users.noreply.github.com', github.triggering_actor) }} - create_branch: true + DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit" + - name: Push changes to branch + if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true') + run: git push From c37d89827e64d7ed34d9a2a3edce9cd00baafb3e Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:13:17 -0400 Subject: [PATCH 151/300] [ci] Pin Discord webhook action to specific commit sha (#32649) Pins the discord webhook action to `86dc739f3f165f16dadc5666051c367efa1692f4`, which is what the v6 tag points to. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32649). * #32650 * __->__ #32649 * #32648 --- .github/workflows/compiler_discord_notify.yml | 2 +- .github/workflows/runtime_discord_notify.yml | 2 +- .github/workflows/runtime_releases_from_npm_manual.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index f81f42b9cb7df..5b46d0f87601f 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Discord Webhook Action - uses: tsickert/discord-webhook@v6.0.0 + uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4 with: webhook-url: ${{ secrets.COMPILER_DISCORD_WEBHOOK_URL }} embed-author-name: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index e41b1c56405a7..f2be08f904ee2 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Discord Webhook Action - uses: tsickert/discord-webhook@v6.0.0 + uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4 with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} embed-author-name: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index c4675bfb7fd4a..0293696428f65 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Discord Webhook Action - uses: tsickert/discord-webhook@v6.0.0 + uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4 with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} embed-author-name: ${{ github.event.sender.login }} From ca02c4bb40325c3efbc60969e9199a0c379d2d6b Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:13:27 -0400 Subject: [PATCH 152/300] [ci][ez] use yarn --cwd (#32650) Run yarn install via `--cwd` instead of `working-directory` to make the labels clearer --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32650). * __->__ #32650 * #32649 * #32648 --- .github/workflows/devtools_regression_tests.yml | 3 +-- .github/workflows/runtime_build_and_test.yml | 15 +++++---------- .github/workflows/runtime_commit_artifacts.yml | 5 +---- .github/workflows/runtime_eslint_plugin_e2e.yml | 3 +-- .github/workflows/runtime_prereleases.yml | 3 +-- .../runtime_releases_from_npm_manual.yml | 3 +-- 6 files changed, 10 insertions(+), 22 deletions(-) diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 9399b80f0e250..844b82b82cdb9 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -35,8 +35,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: scripts/release + - run: yarn --cwd scripts/release install --frozen-lockfile - name: Download react-devtools artifacts for base revision run: | git fetch origin main diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 63e432b4dcd69..d685c848e21f4 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -169,8 +169,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler + - run: yarn --cwd compiler install --frozen-lockfile - run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }} # ----- BUILD ----- @@ -208,8 +207,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler + - run: yarn --cwd compiler install --frozen-lockfile - run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci env: CI: github @@ -287,8 +285,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler + - run: yarn --cwd compiler install --frozen-lockfile - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -438,8 +435,7 @@ jobs: key: fixtures_dom-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - - run: yarn install --frozen-lockfile - working-directory: fixtures/dom + - run: yarn --cwd fixtures/dom install --frozen-lockfile - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -635,8 +631,7 @@ jobs: key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - - run: yarn install --frozen-lockfile - working-directory: scripts/release + - run: yarn --cwd scripts/release install --frozen-lockfile - name: Download artifacts for base revision run: | GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }}) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 6b1cad2245aa6..5de51b2664e48 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -84,10 +84,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - name: yarn install (react) - - run: yarn install --frozen-lockfile - name: yarn install (scripts/release) - working-directory: scripts/release + - run: yarn --cwd scripts/release install --frozen-lockfile - name: Download artifacts for base revision run: | GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index 8b8222721cfaa..2deb958ea79cc 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -48,8 +48,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: compiler + - run: yarn --cwd compiler install --frozen-lockfile - name: Build plugin working-directory: fixtures/eslint-v${{ matrix.eslint_major }} run: node build.mjs diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 293e5f2ce9fa6..ce44246df9a75 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -45,8 +45,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: scripts/release + - run: yarn --cwd scripts/release install --frozen-lockfile - run: | scripts/release/prepare-release-from-ci.js --skipTests -r ${{ inputs.release_channel }} --commit=${{ inputs.commit_sha }} cp ./scripts/release/ci-npmrc ~/.npmrc diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index 0293696428f65..d04f202a7ea71 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -77,8 +77,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile - - run: yarn install --frozen-lockfile - working-directory: scripts/release + - run: yarn --cwd scripts/release install --frozen-lockfile - run: cp ./scripts/release/ci-npmrc ~/.npmrc - if: '${{ inputs.only_packages }}' name: 'Prepare ${{ inputs.only_packages }} from NPM' From 9fde224a53693101a4d15e038d6db37e7a3596ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Mon, 17 Mar 2025 16:17:01 -0400 Subject: [PATCH 153/300] Materialize the tree ID when ViewTransition name=auto consumes one (#32651) ViewTransition uses the `useId` algorithm to auto-assign names. This ensures that we could animate between SSR content and client content by ensuring that the names line up. However, I missed that we need to bump the id (materialize it) when we do that. This is what function components do if they use one or more `useId()`. This caused duplicate names when two ViewTransitions were nested without any siblings since they would share name. --- .../view-transition/src/components/Page.js | 18 ++++++++-- .../src/ReactFiberBeginWork.js | 3 ++ packages/react-server/src/ReactFizzServer.js | 33 ++++++++++++++++--- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index d7acc93c0cb19..19313a99e372d 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -4,6 +4,7 @@ import React, { unstable_useSwipeTransition as useSwipeTransition, useEffect, useState, + useId, } from 'react'; import SwipeRecognizer from './SwipeRecognizer'; @@ -39,6 +40,11 @@ function Component() { ); } +function Id() { + // This is just testing that Id inside a ViewTransition can hydrate correctly. + return <span id={useId()} />; +} + export default function Page({url, navigate}) { const [renderedUrl, startGesture] = useSwipeTransition('/?a', url, '/?b'); const show = renderedUrl === '/?b'; @@ -77,8 +83,12 @@ export default function Page({url, navigate}) { </button> <ViewTransition className="none"> <div> - <ViewTransition className={transitions['slide-on-nav']}> - <h1>{!show ? 'A' : 'B'}</h1> + <ViewTransition> + <div> + <ViewTransition className={transitions['slide-on-nav']}> + <h1>{!show ? 'A' : 'B'}</h1> + </ViewTransition> + </div> </ViewTransition> <ViewTransition className={{ @@ -102,7 +112,9 @@ export default function Page({url, navigate}) { {show ? <div>hello{exclamation}</div> : <section>Loading</section>} </ViewTransition> <p>scroll me</p> - <p></p> + <p> + <Id /> + </p> <p></p> <p></p> <p></p> diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index ac5923e85d26e..4636c08818d51 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -3298,6 +3298,9 @@ function updateViewTransition( // to client rendered content. If we don't end up using that we could just assign an incremeting // counter in the commit phase instead. assignViewTransitionAutoName(pendingProps, instance); + if (getIsHydrating()) { + pushMaterializedTreeId(workInProgress); + } } if (current !== null && current.memoizedProps.name !== pendingProps.name) { // If the name changes, we schedule a ref effect to create a new ref instance. diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 6d4db474e355a..caf2ac3f8a638 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -2211,6 +2211,34 @@ function renderOffscreen( } } +function renderViewTransition( + request: Request, + task: Task, + keyPath: KeyNode, + props: Object, +) { + const prevKeyPath = task.keyPath; + task.keyPath = keyPath; + if (props.name != null && props.name !== 'auto') { + renderNodeDestructive(request, task, props.children, -1); + } else { + // This will be auto-assigned a name which claims a "useId" slot. + // This component materialized an id. We treat this as its own level, with + // a single "child" slot. + const prevTreeContext = task.treeContext; + const totalChildren = 1; + const index = 0; + // Modify the id context. Because we'll need to reset this if something + // suspends or errors, we'll use the non-destructive render path. + task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index); + renderNode(request, task, props.children, -1); + // Like the other contexts, this does not need to be in a finally block + // because renderNode takes care of unwinding the stack. + task.treeContext = prevTreeContext; + } + task.keyPath = prevKeyPath; +} + function renderElement( request: Request, task: Task, @@ -2267,10 +2295,7 @@ function renderElement( } case REACT_VIEW_TRANSITION_TYPE: { if (enableViewTransition) { - const prevKeyPath = task.keyPath; - task.keyPath = keyPath; - renderNodeDestructive(request, task, props.children, -1); - task.keyPath = prevKeyPath; + renderViewTransition(request, task, keyPath, props); return; } // Fallthrough From 02372952e4f24fa02dcb9b32af26cb2472617cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Mon, 17 Mar 2025 17:26:00 -0400 Subject: [PATCH 154/300] Don't auto-start browser in SSR fixtures (#32652) I end up restarting these a lot and it's annoying to have it open another tab each time. The flight fixture already doesn't auto-start. --- fixtures/ssr/package.json | 2 +- fixtures/view-transition/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fixtures/ssr/package.json b/fixtures/ssr/package.json index 33ea7add74e89..1535c39fb3151 100644 --- a/fixtures/ssr/package.json +++ b/fixtures/ssr/package.json @@ -20,7 +20,7 @@ "prestart": "cp -r ../../build/oss-experimental/* ./node_modules/", "prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/", "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"", - "dev:client": "PORT=3001 react-scripts start", + "dev:client": "BROWSER=none PORT=3001 react-scripts start", "dev:server": "NODE_ENV=development node server", "start": "react-scripts build && NODE_ENV=production node server", "build": "react-scripts build", diff --git a/fixtures/view-transition/package.json b/fixtures/view-transition/package.json index c6226c6f78958..7b9af04096027 100644 --- a/fixtures/view-transition/package.json +++ b/fixtures/view-transition/package.json @@ -26,7 +26,7 @@ "prestart": "cp -r ../../build/oss-experimental/* ./node_modules/", "prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/", "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"", - "dev:client": "PORT=3001 react-scripts start", + "dev:client": "BROWSER=none PORT=3001 react-scripts start", "dev:server": "NODE_ENV=development node server", "start": "react-scripts build && NODE_ENV=production node server", "build": "react-scripts build", From 90b511ec7a9f2f3fd2b7f0039d8fc52c23f573a1 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV <dmytropostolov@gmail.com> Date: Tue, 18 Mar 2025 00:30:58 +0100 Subject: [PATCH 155/300] fix(react-compiler): implement NumericLiteral as ObjectPropertyKey (#31791) --- .../src/HIR/BuildHIR.ts | 5 ++ .../src/HIR/HIR.ts | 4 ++ .../src/HIR/PrintHIR.ts | 3 + .../ReactiveScopes/CodegenReactiveFunction.ts | 3 + ...c-literal-as-object-property-key.expect.md | 65 +++++++++++++++++++ .../numeric-literal-as-object-property-key.js | 18 +++++ 6 files changed, 98 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 8a7dd10c245a2..6f93ef2f3a3df 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -1455,6 +1455,11 @@ function lowerObjectPropertyKey( kind: 'identifier', name: key.node.name, }; + } else if (key.isNumericLiteral()) { + return { + kind: 'identifier', + name: String(key.node.value), + }; } builder.errors.push({ diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index 1ca00ef5877c4..5de4d9ba0c9eb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -703,6 +703,10 @@ export type ObjectPropertyKey = | { kind: 'computed'; name: Place; + } + | { + kind: 'number'; + name: number; }; export type ObjectProperty = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts index 19d3f707b9199..c8182c9e72a7c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts @@ -330,6 +330,9 @@ function printObjectPropertyKey(key: ObjectPropertyKey): string { case 'computed': { return `[${printPlace(key.name)}]`; } + case 'number': { + return String(key.name); + } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 6c89aea45ea45..f8a2bc4989c12 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -2429,6 +2429,9 @@ function codegenObjectPropertyKey( }); return expr; } + case 'number': { + return t.numericLiteral(key.name); + } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.expect.md new file mode 100644 index 0000000000000..32d2a5a259fdd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.expect.md @@ -0,0 +1,65 @@ + +## Input + +```javascript +function Test() { + const obj = { + 21: 'dimaMachina', + }; + // Destructuring assignment + const {21: myVar} = obj; + return ( + <div> + {obj[21]} + {myVar} + </div> + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Test, + params: [{}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function Test() { + const $ = _c(2); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { 21: "dimaMachina" }; + $[0] = t0; + } else { + t0 = $[0]; + } + const obj = t0; + + const { 21: myVar } = obj; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ( + <div> + {obj[21]} + {myVar} + </div> + ); + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Test, + params: [{}], +}; + +``` + +### Eval output +(kind: ok) <div>dimaMachinadimaMachina</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.js new file mode 100644 index 0000000000000..b385417bbdd3b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/numeric-literal-as-object-property-key.js @@ -0,0 +1,18 @@ +function Test() { + const obj = { + 21: 'dimaMachina', + }; + // Destructuring assignment + const {21: myVar} = obj; + return ( + <div> + {obj[21]} + {myVar} + </div> + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Test, + params: [{}], +}; From 3c3696d5548c8a67f2332fd78332b9366abaf2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Mon, 17 Mar 2025 21:38:13 -0400 Subject: [PATCH 156/300] Measure Updated ViewTransition Boundaries (#32653) This does the same thing for `measureUpdateViewTransition` that we did for `measureNestedViewTransitions` in https://github.com/facebook/react/pull/32612/commits/e3cbaffef05c7b476c07f7495e06788a9503e636. If a boundary hasn't mutated and didn't change in size, we mark it for cancellation. Otherwise we add names to it. The different from the CommitViewTransition path is that the "old" names are added to the clones so this is the first time the "new" names. Now we also cancel any boundaries that were unchanged. So now the root no longer animates. We still have to clone them. There are other optimizations that can avoid cloning but once we've done all the layouts we can still cancel the running animation and let them just be the regular content if they didn't change. Just like the regular fire-and-forget path. This also fixes the measurement so that we measure clones by adjusting their position back into the viewport. This actually surfaces a bug in Safari that was already in #32612. It turns out that the old names aren't picked up for some reason and so in Safari they looked more like a cross-fade than what #32612 was supposed to fix. However, now that bug is even more apparent because they actually just disappear in Safari. I'm not sure what that bug is but it's unrelated to this PR so will fix that separately. --- .../view-transition/src/components/Page.js | 4 +- packages/react-art/src/ReactFiberConfigART.js | 4 + .../src/client/ReactFiberConfigDOM.js | 30 +++- .../src/ReactFiberConfigNative.js | 4 + .../src/createReactNoop.js | 4 + .../src/ReactFiberApplyGesture.js | 136 ++++++++++++++---- .../src/ReactFiberCommitHostEffects.js | 1 + .../src/ReactFiberCommitViewTransitions.js | 51 +++++-- .../src/ReactFiberCommitWork.js | 18 +-- .../src/ReactFiberConfigWithNoMutation.js | 1 + .../src/forks/ReactFiberConfig.custom.js | 1 + .../src/ReactFiberConfigTestHost.js | 4 + 12 files changed, 197 insertions(+), 61 deletions(-) diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index 19313a99e372d..48288da25234f 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -77,9 +77,9 @@ export default function Page({url, navigate}) { <div> <button onClick={() => { - navigate(show ? '/?a' : '/?b'); + navigate(url === '/?b' ? '/?a' : '/?b'); }}> - {show ? 'A' : 'B'} + {url === '/?b' ? 'A' : 'B'} </button> <ViewTransition className="none"> <div> diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index 5b8788453f6af..a168975221f34 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -518,6 +518,10 @@ export function measureInstance(instance) { return null; } +export function measureClonedInstance(instance) { + return null; +} + export function wasInstanceInViewport(measurement): boolean { return true; } diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 2d34bc94005be..3615e06481629 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1477,10 +1477,12 @@ export type InstanceMeasurement = { view: boolean, // is in viewport bounds }; -export function measureInstance(instance: Instance): InstanceMeasurement { - const ownerWindow = instance.ownerDocument.defaultView; - const rect = instance.getBoundingClientRect(); - const computedStyle = getComputedStyle(instance); +function createMeasurement( + rect: ClientRect | DOMRect, + computedStyle: CSSStyleDeclaration, + element: Element, +): InstanceMeasurement { + const ownerWindow = element.ownerDocument.defaultView; return { rect: rect, abs: @@ -1508,6 +1510,26 @@ export function measureInstance(instance: Instance): InstanceMeasurement { }; } +export function measureInstance(instance: Instance): InstanceMeasurement { + const rect = instance.getBoundingClientRect(); + const computedStyle = getComputedStyle(instance); + return createMeasurement(rect, computedStyle, instance); +} + +export function measureClonedInstance(instance: Instance): InstanceMeasurement { + const measuredRect = instance.getBoundingClientRect(); + // Adjust the DOMRect based on the translate that put it outside the viewport. + // TODO: This might not be completely correct if the parent also has a transform. + const rect = new DOMRect( + measuredRect.x + 20000, + measuredRect.y + 20000, + measuredRect.width, + measuredRect.height, + ); + const computedStyle = getComputedStyle(instance); + return createMeasurement(rect, computedStyle, instance); +} + export function wasInstanceInViewport( measurement: InstanceMeasurement, ): boolean { diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index c08e1f0f82474..b15a6f9f76b02 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -620,6 +620,10 @@ export function measureInstance(instance: Instance): InstanceMeasurement { return null; } +export function measureClonedInstance(instance: Instance): InstanceMeasurement { + return null; +} + export function wasInstanceInViewport( measurement: InstanceMeasurement, ): boolean { diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 2ddbea79289fa..266f860fe6d32 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -796,6 +796,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return null; }, + measureClonedInstance(instance: Instance): InstanceMeasurement { + return null; + }, + wasInstanceInViewport(measurement: InstanceMeasurement): boolean { return true; }, diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js index c9b1d7688f580..ea4a0ce70ae5f 100644 --- a/packages/react-reconciler/src/ReactFiberApplyGesture.js +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -9,7 +9,7 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes'; -import type {Instance, TextInstance} from './ReactFiberConfig'; +import type {Instance, TextInstance, Props} from './ReactFiberConfig'; import type {OffscreenState} from './ReactFiberActivityComponent'; @@ -25,6 +25,7 @@ import { removeRootViewTransitionClone, cancelRootViewTransitionName, restoreRootViewTransitionName, + cancelViewTransitionName, applyViewTransitionName, appendChild, commitUpdate, @@ -39,6 +40,7 @@ import { popMutationContext, pushMutationContext, viewTransitionMutationContext, + trackHostMutation, } from './ReactFiberMutationTracking'; import { MutationMask, @@ -48,6 +50,7 @@ import { Visibility, ViewTransitionNamedStatic, ViewTransitionStatic, + AffectedParentLayout, } from './ReactFiberFlags'; import { HostComponent, @@ -61,9 +64,14 @@ import { import { restoreEnterOrExitViewTransitions, restoreNestedViewTransitions, + restoreUpdateViewTransitionForGesture, appearingViewTransitions, commitEnterViewTransitions, measureNestedViewTransitions, + measureUpdateViewTransition, + viewTransitionCancelableChildren, + pushViewTransitionCancelableScope, + popViewTransitionCancelableScope, } from './ReactFiberCommitViewTransitions'; import { getViewTransitionName, @@ -72,6 +80,10 @@ import { let didWarnForRootClone = false; +// Used during the apply phase to track whether a parent ViewTransition component +// might have been affected by any mutations / relayouts below. +let viewTransitionContextChanged: boolean = false; + function detectMutationOrInsertClones(finishedWork: Fiber): boolean { return true; } @@ -421,6 +433,7 @@ function recursivelyInsertNewFiber( // For insertions we don't need to clone. It's already new state node. if (visitPhase !== INSERT_APPEARING_PAIR) { appendChild(hostParentClone, instance); + trackHostMutation(); recursivelyInsertNew( finishedWork, instance, @@ -450,6 +463,7 @@ function recursivelyInsertNewFiber( // For insertions we don't need to clone. It's already new state node. if (visitPhase !== INSERT_APPEARING_PAIR) { appendChild(hostParentClone, textInstance); + trackHostMutation(); } break; } @@ -575,6 +589,7 @@ function recursivelyInsertClonesFromExistingTree( } if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { unhideInstance(clone, child.memoizedProps); + trackHostMutation(); } break; } @@ -590,6 +605,7 @@ function recursivelyInsertClonesFromExistingTree( appendChild(hostParentClone, clone); if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { unhideTextInstance(clone, child.memoizedProps); + trackHostMutation(); } break; } @@ -679,6 +695,10 @@ function recursivelyInsertClones( for (let i = 0; i < deletions.length; i++) { const childToDelete = deletions[i]; trackEnterViewTransitions(childToDelete); + // Normally we would only mark something as triggering a mutation if there was + // actually a HostInstance below here. If this tree didn't contain a HostInstances + // we shouldn't trigger a mutation even though a virtual component was deleted. + trackHostMutation(); } } @@ -801,6 +821,7 @@ function insertDestinationClonesOfFiber( clone = cloneMutableInstance(instance, true); if (finishedWork.flags & ContentReset) { resetTextContent(clone); + trackHostMutation(); } } else { // If we have children we'll clone them as we walk the tree so we just @@ -825,6 +846,7 @@ function insertDestinationClonesOfFiber( ); appendChild(hostParentClone, clone); unhideInstance(clone, finishedWork.memoizedProps); + trackHostMutation(); } else { recursivelyInsertClones(finishedWork, clone, null, visitPhase); appendChild(hostParentClone, clone); @@ -851,10 +873,12 @@ function insertDestinationClonesOfFiber( const newText: string = finishedWork.memoizedProps; const oldText: string = current.memoizedProps; commitTextUpdate(clone, newText, oldText); + trackHostMutation(); } appendChild(hostParentClone, clone); if (visitPhase === CLONE_EXIT || visitPhase === CLONE_UNHIDE) { unhideTextInstance(clone, finishedWork.memoizedProps); + trackHostMutation(); } break; } @@ -885,6 +909,10 @@ function insertDestinationClonesOfFiber( } else if (current !== null && current.memoizedState === null) { // Was previously mounted as visible but is now hidden. trackEnterViewTransitions(current); + // Normally we would only mark something as triggering a mutation if there was + // actually a HostInstance below here. If this tree didn't contain a HostInstances + // we shouldn't trigger a mutation even though a virtual component was hidden. + trackHostMutation(); } break; } @@ -991,13 +1019,6 @@ function measureExitViewTransitions(placement: Fiber): void { } } -function measureUpdateViewTransition( - current: Fiber, - finishedWork: Fiber, -): void { - // TODO -} - function recursivelyApplyViewTransitions(parentFiber: Fiber) { const deletions = parentFiber.deletions; if (deletions !== null) { @@ -1037,15 +1058,6 @@ function applyViewTransitionsOnFiber(finishedWork: Fiber) { // because the fiber tag is more specific. An exception is any flag related // to reconciliation, because those can be set on all fiber types. switch (finishedWork.tag) { - case HostComponent: { - // const instance: Instance = finishedWork.stateNode; - // TODO: Apply name and measure. - recursivelyApplyViewTransitions(finishedWork); - break; - } - case HostText: { - break; - } case HostPortal: { // TODO: Consider what should happen to Portals. For now we exclude them. break; @@ -1063,12 +1075,59 @@ function applyViewTransitionsOnFiber(finishedWork: Fiber) { } break; } - case ViewTransitionComponent: - measureUpdateViewTransition(current, finishedWork); + case ViewTransitionComponent: { + const prevContextChanged = viewTransitionContextChanged; + const prevCancelableChildren = pushViewTransitionCancelableScope(); + viewTransitionContextChanged = false; + recursivelyApplyViewTransitions(finishedWork); + + if (viewTransitionContextChanged) { + finishedWork.flags |= Update; + } + + const inViewport = measureUpdateViewTransition( + current, + finishedWork, + true, + ); + + if ((finishedWork.flags & Update) === NoFlags || !inViewport) { + // If this boundary didn't update, then we may be able to cancel its children. + // We bubble them up to the parent set to be determined later if we can cancel. + // Similarly, if old and new state was outside the viewport, we can skip it + // even if it did update. + if (prevCancelableChildren === null) { + // Bubbling up this whole set to the parent. + } else { + // Merge with parent set. + // $FlowFixMe[method-unbinding] + prevCancelableChildren.push.apply( + prevCancelableChildren, + viewTransitionCancelableChildren, + ); + popViewTransitionCancelableScope(prevCancelableChildren); + } + // TODO: If this doesn't end up canceled, because a parent animates, + // then we should probably issue an event since this instance is part of it. + } else { + // TODO: Schedule gesture events. + // If this boundary did update, we cannot cancel its children so those are dropped. + popViewTransitionCancelableScope(prevCancelableChildren); + } + + if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) { + // This boundary changed size in a way that may have caused its parent to + // relayout. We need to bubble this information up to the parent. + viewTransitionContextChanged = true; + } else { + // Otherwise, we restore it to whatever the parent had found so far. + viewTransitionContextChanged = prevContextChanged; + } + const viewTransitionState: ViewTransitionState = finishedWork.stateNode; viewTransitionState.clones = null; // Reset - recursivelyApplyViewTransitions(finishedWork); break; + } default: { recursivelyApplyViewTransitions(finishedWork); break; @@ -1082,13 +1141,38 @@ export function applyDepartureTransitions( finishedWork: Fiber, ): void { // First measure and apply view-transition-names to the "new" states. + viewTransitionContextChanged = false; + pushViewTransitionCancelableScope(); + recursivelyApplyViewTransitions(finishedWork); + // Then remove the clones. const rootClone = root.gestureClone; if (rootClone !== null) { root.gestureClone = null; removeRootViewTransitionClone(root.containerInfo, rootClone); } + + if (!viewTransitionContextChanged) { + // If we didn't leak any resizing out to the root, we don't have to transition + // the root itself. This means that we can now safely cancel any cancellations + // that bubbled all the way up. + const cancelableChildren = viewTransitionCancelableChildren; + if (cancelableChildren !== null) { + for (let i = 0; i < cancelableChildren.length; i += 3) { + cancelViewTransitionName( + ((cancelableChildren[i]: any): Instance), + ((cancelableChildren[i + 1]: any): string), + ((cancelableChildren[i + 2]: any): Props), + ); + } + } + // We also cancel the root itself. First we restore the name to the documentElement + // and then we cancel it. + restoreRootViewTransitionName(root.containerInfo); + cancelRootViewTransitionName(root.containerInfo); + } + popViewTransitionCancelableScope(null); } function recursivelyRestoreViewTransitions(parentFiber: Fiber) { @@ -1130,15 +1214,6 @@ function restoreViewTransitionsOnFiber(finishedWork: Fiber) { // because the fiber tag is more specific. An exception is any flag related // to reconciliation, because those can be set on all fiber types. switch (finishedWork.tag) { - case HostComponent: { - // const instance: Instance = finishedWork.stateNode; - // TODO: Restore the name. - recursivelyRestoreViewTransitions(finishedWork); - break; - } - case HostText: { - break; - } case HostPortal: { // TODO: Consider what should happen to Portals. For now we exclude them. break; @@ -1157,8 +1232,7 @@ function restoreViewTransitionsOnFiber(finishedWork: Fiber) { break; } case ViewTransitionComponent: - const viewTransitionState: ViewTransitionState = finishedWork.stateNode; - viewTransitionState.clones = null; // Reset + restoreUpdateViewTransitionForGesture(current, finishedWork); recursivelyRestoreViewTransitions(finishedWork); break; default: { diff --git a/packages/react-reconciler/src/ReactFiberCommitHostEffects.js b/packages/react-reconciler/src/ReactFiberCommitHostEffects.js index 7dad8b330d7e2..2ca49f677de1f 100644 --- a/packages/react-reconciler/src/ReactFiberCommitHostEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitHostEffects.js @@ -199,6 +199,7 @@ export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) { unhideTextInstance(instance, node.memoizedProps); } } + trackHostMutation(); } catch (error) { captureCommitPhaseError(node, node.return, error); } diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 80e190181086b..c8ec019da7f07 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -31,6 +31,7 @@ import { applyViewTransitionName, restoreViewTransitionName, measureInstance, + measureClonedInstance, hasInstanceChanged, hasInstanceAffectedParent, wasInstanceInViewport, @@ -564,16 +565,23 @@ export function restoreUpdateViewTransition( current: Fiber, finishedWork: Fiber, ): void { - finishedWork.memoizedState = null; restoreViewTransitionOnHostInstances(current.child, true); restoreViewTransitionOnHostInstances(finishedWork.child, true); } +export function restoreUpdateViewTransitionForGesture( + current: Fiber, + finishedWork: Fiber, +): void { + // For gestures we don't need to reset "finishedWork" because those would + // have all been clones that got deleted. + restoreViewTransitionOnHostInstances(current.child, true); +} + export function restoreNestedViewTransitions(changedParent: Fiber): void { let child = changedParent.child; while (child !== null) { if (child.tag === ViewTransitionComponent) { - child.memoizedState = null; restoreViewTransitionOnHostInstances(child.child, false); } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { restoreNestedViewTransitions(child); @@ -774,13 +782,16 @@ function measureViewTransitionHostInstancesRecursive( export function measureUpdateViewTransition( current: Fiber, finishedWork: Fiber, + gesture: boolean, ): boolean { - const props: ViewTransitionProps = finishedWork.memoizedProps; - const newName = getViewTransitionName(props, finishedWork.stateNode); - const oldName = getViewTransitionName( - current.memoizedProps, - current.stateNode, - ); + // If this was a gesture then which Fiber was used for the "old" vs "new" state is reversed. + // We still need to treat "finishedWork" as the Fiber that contains the flags for this commmit. + const oldFiber = gesture ? finishedWork : current; + const newFiber = gesture ? current : finishedWork; + const props: ViewTransitionProps = newFiber.memoizedProps; + const state: ViewTransitionState = newFiber.stateNode; + const newName = getViewTransitionName(props, state); + const oldName = getViewTransitionName(oldFiber.memoizedProps, state); const updateClassName: ?string = getViewTransitionClassName( props.className, props.update, @@ -807,7 +818,9 @@ export function measureUpdateViewTransition( if (layoutClassName === 'none') { // If we did not update, then all changes are considered a layout. We'll // attempt to cancel. - cancelViewTransitionHostInstances(finishedWork.child, oldName, true); + // This should use the Fiber that got names applied in the snapshot phase + // since those are the ones we're trying to cancel. + cancelViewTransitionHostInstances(oldFiber.child, oldName, true); return false; } // We didn't update but we might still apply layout so we measure each @@ -816,10 +829,21 @@ export function measureUpdateViewTransition( } // If nothing changed due to a mutation, or children changing size // and the measurements end up unchanged, we should restore it to not animate. - const previousMeasurements = current.memoizedState; + let previousMeasurements: null | Array<InstanceMeasurement>; + if (gesture) { + const clones = state.clones; + if (clones === null) { + previousMeasurements = null; + } else { + previousMeasurements = clones.map(measureClonedInstance); + } + } else { + previousMeasurements = oldFiber.memoizedState; + oldFiber.memoizedState = null; // Clear it. We won't need it anymore. + } const inViewport = measureViewTransitionHostInstances( - finishedWork, - finishedWork.child, + finishedWork, // This is always finishedWork since it's used to assign flags. + newFiber.child, // This either current or finishedWork depending on if was a gesture. newName, oldName, className, @@ -857,10 +881,11 @@ export function measureNestedViewTransitions( if (clones === null) { previousMeasurements = null; } else { - previousMeasurements = clones.map(measureInstance); + previousMeasurements = clones.map(measureClonedInstance); } } else { previousMeasurements = child.memoizedState; + child.memoizedState = null; // Clear it. We won't need it anymore. } const inViewport = measureViewTransitionHostInstances( child, diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 3e2a5153dd147..63bf7b7eb9b15 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -2474,7 +2474,6 @@ function commitAfterMutationEffectsOnFiber( // the root itself. This means that we can now safely cancel any cancellations // that bubbled all the way up. const cancelableChildren = viewTransitionCancelableChildren; - popViewTransitionCancelableScope(null); if (cancelableChildren !== null) { for (let i = 0; i < cancelableChildren.length; i += 3) { cancelViewTransitionName( @@ -2487,6 +2486,7 @@ function commitAfterMutationEffectsOnFiber( // We also cancel the root itself. cancelRootViewTransitionName(root.containerInfo); } + popViewTransitionCancelableScope(null); break; } case HostComponent: { @@ -2528,7 +2528,11 @@ function commitAfterMutationEffectsOnFiber( finishedWork.flags |= Update; } - const inViewport = measureUpdateViewTransition(current, finishedWork); + const inViewport = measureUpdateViewTransition( + current, + finishedWork, + false, + ); if ((finishedWork.flags & Update) === NoFlags || !inViewport) { // If this boundary didn't update, then we may be able to cancel its children. @@ -3618,15 +3622,7 @@ function commitPassiveMountOnFiber( if (current === null) { // This is a new mount. We should have handled this as part of the // Placement effect or it is deeper inside a entering transition. - } else if ( - (finishedWork.subtreeFlags & - (Placement | - Update | - ChildDeletion | - ContentReset | - Visibility)) !== - NoFlags - ) { + } else { // Something mutated within this subtree. This might have caused // something to cross-fade if we didn't already cancel it. // If not, restore it. diff --git a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js index 64b67491aa8d5..74e30da88c96e 100644 --- a/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js +++ b/packages/react-reconciler/src/ReactFiberConfigWithNoMutation.js @@ -46,6 +46,7 @@ export const cloneRootViewTransitionContainer = shim; export const removeRootViewTransitionClone = shim; export type InstanceMeasurement = null; export const measureInstance = shim; +export const measureClonedInstance = shim; export const wasInstanceInViewport = shim; export const hasInstanceChanged = shim; export const hasInstanceAffectedParent = shim; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 2be7d18b87caf..f22b6a580e7d9 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -149,6 +149,7 @@ export const cloneRootViewTransitionContainer = export const removeRootViewTransitionClone = $$$config.removeRootViewTransitionClone; export const measureInstance = $$$config.measureInstance; +export const measureClonedInstance = $$$config.measureClonedInstance; export const wasInstanceInViewport = $$$config.wasInstanceInViewport; export const hasInstanceChanged = $$$config.hasInstanceChanged; export const hasInstanceAffectedParent = $$$config.hasInstanceAffectedParent; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index d9a45550fa4b2..2df5f0157195c 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -389,6 +389,10 @@ export function measureInstance(instance: Instance): InstanceMeasurement { return null; } +export function measureClonedInstance(instance: Instance): InstanceMeasurement { + return null; +} + export function wasInstanceInViewport( measurement: InstanceMeasurement, ): boolean { From a35aaf704cca9a5db16f5b197e3ac17eb960b72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Tue, 18 Mar 2025 09:24:31 -0400 Subject: [PATCH 157/300] Update ViewTransition fixture to include bigger buttons/swipe (#32656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I made the button a bit bigger and moved the swipe recognizer around the whole screen. Typically these are used around the whole content without any affordances and not as a standalone scrubber. Ideally the swipe would be able to be inside the animating content but it can't yet due to [this Safari bug](https://bugs.webkit.org/show_bug.cgi?id=288795). Added back some paragraphs so that scrolling can be tested properly. It appears it's possible to get the swipe to be a bit misaligned if you scroll enough on iOS. <img width="437" alt="Screenshot 2025-03-17 at 10 27 42 PM" src="https://github.com/user-attachments/assets/589dc828-717e-420c-83dc-94ae6ad59791" /> --- .../view-transition/src/components/Page.css | 13 +- .../view-transition/src/components/Page.js | 135 +++++++++--------- 2 files changed, 81 insertions(+), 67 deletions(-) diff --git a/fixtures/view-transition/src/components/Page.css b/fixtures/view-transition/src/components/Page.css index 4bdf5644efadf..e7d876681d872 100644 --- a/fixtures/view-transition/src/components/Page.css +++ b/fixtures/view-transition/src/components/Page.css @@ -8,7 +8,16 @@ } .swipe-recognizer { - width: 200px; - border: 1px solid #333333; + width: 300px; + background: #eee; border-radius: 10px; + padding: 20px; } + +.button { + background: #000; + color: #fff; + border: 0px; + border-radius: 5px; + padding: 10px; +} \ No newline at end of file diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index 48288da25234f..ee4b95331f759 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -74,75 +74,80 @@ export default function Page({url, navigate}) { </ViewTransition> ); return ( - <div> - <button - onClick={() => { - navigate(url === '/?b' ? '/?a' : '/?b'); - }}> - {url === '/?b' ? 'A' : 'B'} - </button> - <ViewTransition className="none"> - <div> - <ViewTransition> - <div> - <ViewTransition className={transitions['slide-on-nav']}> - <h1>{!show ? 'A' : 'B'}</h1> - </ViewTransition> - </div> - </ViewTransition> - <ViewTransition - className={{ - 'navigation-back': transitions['slide-right'], - 'navigation-forward': transitions['slide-left'], - }}> - <h1>{!show ? 'A' + counter : 'B' + counter}</h1> - </ViewTransition> - {show ? ( - <div> - {a} - {b} - </div> - ) : ( - <div> - {b} - {a} - </div> - )} - <ViewTransition> - {show ? <div>hello{exclamation}</div> : <section>Loading</section>} - </ViewTransition> - <p>scroll me</p> - <p> - <Id /> - </p> - <p></p> - <p></p> - <p></p> - <p></p> - <p></p> - <div className="swipe-recognizer"> - <SwipeRecognizer - action={swipeAction} - gesture={startGesture} - direction={show ? 'left' : 'right'}> - Swipe me - </SwipeRecognizer> - </div> - <p></p> - <p></p> - {show ? null : ( + <div className="swipe-recognizer"> + <SwipeRecognizer + action={swipeAction} + gesture={startGesture} + direction={show ? 'left' : 'right'}> + <button + className="button" + onClick={() => { + navigate(url === '/?b' ? '/?a' : '/?b'); + }}> + {url === '/?b' ? 'Goto A' : 'Goto B'} + </button> + <ViewTransition className="none"> + <div> <ViewTransition> - <div>world{exclamation}</div> + <div> + <ViewTransition className={transitions['slide-on-nav']}> + <h1>{!show ? 'A' : 'B' + counter}</h1> + </ViewTransition> + </div> + </ViewTransition> + <ViewTransition + className={{ + 'navigation-back': transitions['slide-right'], + 'navigation-forward': transitions['slide-left'], + }}> + <h1>{!show ? 'A' + counter : 'B'}</h1> </ViewTransition> - )} - <Activity mode={show ? 'visible' : 'hidden'}> + {show ? ( + <div> + {a} + {b} + </div> + ) : ( + <div> + {b} + {a} + </div> + )} <ViewTransition> - <div>!!</div> + {show ? ( + <div>hello{exclamation}</div> + ) : ( + <section>Loading</section> + )} </ViewTransition> - </Activity> - {show ? <Component /> : null} - </div> - </ViewTransition> + <p> + <Id /> + </p> + {show ? null : ( + <ViewTransition> + <div>world{exclamation}</div> + </ViewTransition> + )} + <Activity mode={show ? 'visible' : 'hidden'}> + <ViewTransition> + <div>!!</div> + </ViewTransition> + </Activity> + <p>these</p> + <p>rows</p> + <p>exist</p> + <p>to</p> + <p>test</p> + <p>scrolling</p> + <p>content</p> + <p>out</p> + <p>of</p> + <p>the</p> + <p>viewport</p> + {show ? <Component /> : null} + </div> + </ViewTransition> + </SwipeRecognizer> </div> ); } From c69a5fc53a5135136668ca878f99b634d2374837 Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Tue, 18 Mar 2025 11:58:12 -0400 Subject: [PATCH 158/300] Add blur() and focusLast() to fragment instances (#32654) `focus()` was added in https://github.com/facebook/react/pull/32465. Here we add `focusLast()` and `blur()`. I also extended `focus` to take options. `focus` will focus the first focusable element. `focusLast` will focus the last focusable element. We could consider a `focusFirst` naming or even the `focusWithin` used by test selector APIs as well. `blur` will only have an effect if the current `document.activeElement` is one of the fragment children. --- .../fixtures/fragment-refs/FocusCase.js | 55 ++++ .../fixtures/fragment-refs/index.js | 2 + fixtures/dom/src/style.css | 4 + .../src/client/ReactFiberConfigDOM.js | 67 ++++- .../__tests__/ReactDOMFragmentRefs-test.js | 242 +++++++++++++----- .../src/ReactFiberTreeReflection.js | 2 +- 6 files changed, 300 insertions(+), 72 deletions(-) create mode 100644 fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js new file mode 100644 index 0000000000000..43850b1aa0773 --- /dev/null +++ b/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js @@ -0,0 +1,55 @@ +import TestCase from '../../TestCase'; +import Fixture from '../../Fixture'; + +const React = window.React; + +const {Fragment, useEffect, useRef, useState} = React; + +export default function FocusCase() { + const fragmentRef = useRef(null); + + return ( + <TestCase title="Focus Management"> + <TestCase.Steps> + <li>Click to focus the first child</li> + <li>Click to focus the last child</li> + <li>Click to blur any focus within the fragment</li> + </TestCase.Steps> + + <TestCase.ExpectedResult> + <p> + The focus method will focus the first focusable child within the + fragment, skipping any unfocusable children. + </p> + <p> + The focusLast method is the reverse, focusing the last focusable + child. + </p> + <p> + Blur will call blur on the document, only if one of the children + within the fragment is the active element. + </p> + </TestCase.ExpectedResult> + + <button onClick={() => fragmentRef.current.focus()}> + Focus first child + </button> + <button onClick={() => fragmentRef.current.focusLast()}> + Focus last child + </button> + <button onClick={() => fragmentRef.current.blur()}>Blur</button> + + <Fixture> + <div className="highlight-focused-children" style={{display: 'flex'}}> + <Fragment ref={fragmentRef}> + <div style={{outline: '1px solid black'}}>Unfocusable div</div> + <button>Button 1</button> + <button>Button 2</button> + <input type="text" placeholder="Input field" /> + <div style={{outline: '1px solid black'}}>Unfocusable div</div> + </Fragment> + </div> + </Fixture> + </TestCase> + ); +} diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/index.js b/fixtures/dom/src/components/fixtures/fragment-refs/index.js index 03d95ec30a98d..89316a35efe7f 100644 --- a/fixtures/dom/src/components/fixtures/fragment-refs/index.js +++ b/fixtures/dom/src/components/fixtures/fragment-refs/index.js @@ -2,6 +2,7 @@ import FixtureSet from '../../FixtureSet'; import EventListenerCase from './EventListenerCase'; import IntersectionObserverCase from './IntersectionObserverCase'; import ResizeObserverCase from './ResizeObserverCase'; +import FocusCase from './FocusCase'; const React = window.React; @@ -11,6 +12,7 @@ export default function FragmentRefsPage() { <EventListenerCase /> <IntersectionObserverCase /> <ResizeObserverCase /> + <FocusCase /> </FixtureSet> ); } diff --git a/fixtures/dom/src/style.css b/fixtures/dom/src/style.css index 30a9ed5fff972..0442b0606b5d8 100644 --- a/fixtures/dom/src/style.css +++ b/fixtures/dom/src/style.css @@ -358,3 +358,7 @@ tbody tr:nth-child(even) { .onscreen { background-color: green; } + +.highlight-focused-children *:focus { + outline: 2px solid green; +} diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 3615e06481629..1398f2c50f28f 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -2205,6 +2205,11 @@ type StoredEventListener = { optionsOrUseCapture: void | EventListenerOptionsOrUseCapture, }; +type FocusOptions = { + preventScroll?: boolean, + focusVisible?: boolean, +}; + export type FragmentInstanceType = { _fragmentFiber: Fiber, _eventListeners: null | Array<StoredEventListener>, @@ -2219,7 +2224,9 @@ export type FragmentInstanceType = { listener: EventListener, optionsOrUseCapture?: EventListenerOptionsOrUseCapture, ): void, - focus(): void, + focus(focusOptions?: FocusOptions): void, + focusLast(focusOptions?: FocusOptions): void, + blur(): void, observeUsing(observer: IntersectionObserver | ResizeObserver): void, unobserveUsing(observer: IntersectionObserver | ResizeObserver): void, }; @@ -2307,10 +2314,57 @@ function removeEventListenerFromChild( return false; } // $FlowFixMe[prop-missing] -FragmentInstance.prototype.focus = function (this: FragmentInstanceType) { - traverseFragmentInstance(this._fragmentFiber, setFocusIfFocusable); +FragmentInstance.prototype.focus = function ( + this: FragmentInstanceType, + focusOptions?: FocusOptions, +): void { + traverseFragmentInstance( + this._fragmentFiber, + setFocusIfFocusable, + focusOptions, + ); }; // $FlowFixMe[prop-missing] +FragmentInstance.prototype.focusLast = function ( + this: FragmentInstanceType, + focusOptions?: FocusOptions, +) { + const children: Array<Instance> = []; + traverseFragmentInstance(this._fragmentFiber, collectChildren, children); + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + if (setFocusIfFocusable(child, focusOptions)) { + break; + } + } +}; +function collectChildren( + child: Instance, + collection: Array<Instance>, +): boolean { + collection.push(child); + return false; +} +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void { + // TODO: When we have a parent element reference, we can skip traversal if the fragment's parent + // does not contain document.activeElement + traverseFragmentInstance( + this._fragmentFiber, + blurActiveElementWithinFragment, + ); +}; +function blurActiveElementWithinFragment(child: Instance): boolean { + // TODO: We can get the activeElement from the parent outside of the loop when we have a reference. + const ownerDocument = child.ownerDocument; + if (child === ownerDocument.activeElement) { + // $FlowFixMe[prop-missing] + child.blur(); + return true; + } + return false; +} +// $FlowFixMe[prop-missing] FragmentInstance.prototype.observeUsing = function ( this: FragmentInstanceType, observer: IntersectionObserver | ResizeObserver, @@ -3190,7 +3244,10 @@ export function isHiddenSubtree(fiber: Fiber): boolean { return fiber.tag === HostComponent && fiber.memoizedProps.hidden === true; } -export function setFocusIfFocusable(node: Instance): boolean { +export function setFocusIfFocusable( + node: Instance, + focusOptions?: FocusOptions, +): boolean { // The logic for determining if an element is focusable is kind of complex, // and since we want to actually change focus anyway- we can just skip it. // Instead we'll just listen for a "focus" event to verify that focus was set. @@ -3206,7 +3263,7 @@ export function setFocusIfFocusable(node: Instance): boolean { try { element.addEventListener('focus', handleFocus); // $FlowFixMe[method-unbinding] - (element.focus || HTMLElement.prototype.focus).call(element); + (element.focus || HTMLElement.prototype.focus).call(element, focusOptions); } finally { element.removeEventListener('focus', handleFocus); } diff --git a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js index e1d8532b7ee99..3ab50fa64b28e 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js @@ -19,6 +19,10 @@ let mockIntersectionObserver; let simulateIntersection; let assertConsoleErrorDev; +function Wrapper({children}) { + return children; +} + describe('FragmentRefs', () => { beforeEach(() => { jest.resetModules(); @@ -99,82 +103,192 @@ describe('FragmentRefs', () => { await act(() => root.render(<Test />)); }); - describe('focus()', () => { - // @gate enableFragmentRefs - it('focuses the first focusable child', async () => { - const fragmentRef = React.createRef(); - const root = ReactDOMClient.createRoot(container); + describe('focus methods', () => { + describe('focus()', () => { + // @gate enableFragmentRefs + it('focuses the first focusable child', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); - function Test() { - return ( - <div> + function Test() { + return ( + <div> + <Fragment ref={fragmentRef}> + <div id="child-a" /> + <style>{`#child-c {}`}</style> + <a id="child-b" href="/"> + B + </a> + <a id="child-c" href="/"> + C + </a> + </Fragment> + </div> + ); + } + + await act(() => { + root.render(<Test />); + }); + + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-b'); + document.activeElement.blur(); + }); + + // @gate enableFragmentRefs + it('preserves document order when adding and removing children', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test({showA, showB}) { + return ( <Fragment ref={fragmentRef}> - <div id="child-a" /> - <style>{`#child-c {}`}</style> - <a id="child-b" href="/"> - B - </a> - <a id="child-c" href="/"> - C - </a> + {showA && <a href="/" id="child-a" />} + {showB && <a href="/" id="child-b" />} </Fragment> - </div> - ); - } + ); + } - await act(() => { - root.render(<Test />); - }); + // Render with A as the first focusable child + await act(() => { + root.render(<Test showA={true} showB={false} />); + }); + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-a'); + document.activeElement.blur(); + // A is still the first focusable child, but B is also tracked + await act(() => { + root.render(<Test showA={true} showB={true} />); + }); + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-a'); + document.activeElement.blur(); - await act(() => { - fragmentRef.current.focus(); + // B is now the first focusable child + await act(() => { + root.render(<Test showA={false} showB={true} />); + }); + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-b'); + document.activeElement.blur(); }); - expect(document.activeElement.id).toEqual('child-b'); - document.activeElement.blur(); }); - // @gate enableFragmentRefs - it('preserves document order when adding and removing children', async () => { - const fragmentRef = React.createRef(); - const root = ReactDOMClient.createRoot(container); + describe('focusLast()', () => { + // @gate enableFragmentRefs + it('focuses the last focusable child', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); - function Test({showA, showB}) { - return ( - <Fragment ref={fragmentRef}> - {showA && <a href="/" id="child-a" />} - {showB && <a href="/" id="child-b" />} - </Fragment> - ); - } + function Test() { + return ( + <div> + <Fragment ref={fragmentRef}> + <a id="child-a" href="/"> + A + </a> + <a id="child-b" href="/"> + B + </a> + <Wrapper> + <a id="child-c" href="/"> + C + </a> + </Wrapper> + <div id="child-d" /> + <style id="child-e">{`#child-d {}`}</style> + </Fragment> + </div> + ); + } - // Render with A as the first focusable child - await act(() => { - root.render(<Test showA={true} showB={false} />); - }); - await act(() => { - fragmentRef.current.focus(); - }); - expect(document.activeElement.id).toEqual('child-a'); - document.activeElement.blur(); - // A is still the first focusable child, but B is also tracked - await act(() => { - root.render(<Test showA={true} showB={true} />); - }); - await act(() => { - fragmentRef.current.focus(); + await act(() => { + root.render(<Test />); + }); + + await act(() => { + fragmentRef.current.focusLast(); + }); + expect(document.activeElement.id).toEqual('child-c'); + document.activeElement.blur(); }); - expect(document.activeElement.id).toEqual('child-a'); - document.activeElement.blur(); + }); - // B is now the first focusable child - await act(() => { - root.render(<Test showA={false} showB={true} />); + describe('blur()', () => { + // @gate enableFragmentRefs + it('removes focus from an element inside of the Fragment', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + <Fragment ref={fragmentRef}> + <a id="child-a" href="/"> + A + </a> + </Fragment> + ); + } + + await act(() => { + root.render(<Test />); + }); + + await act(() => { + fragmentRef.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-a'); + + await act(() => { + fragmentRef.current.blur(); + }); + expect(document.activeElement).toEqual(document.body); }); - await act(() => { - fragmentRef.current.focus(); + + // @gate enableFragmentRefs + it('does not remove focus from elements outside of the Fragment', async () => { + const fragmentRefA = React.createRef(); + const fragmentRefB = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + <Fragment ref={fragmentRefA}> + <a id="child-a" href="/"> + A + </a> + <Fragment ref={fragmentRefB}> + <a id="child-b" href="/"> + B + </a> + </Fragment> + </Fragment> + ); + } + + await act(() => { + root.render(<Test />); + }); + + await act(() => { + fragmentRefA.current.focus(); + }); + expect(document.activeElement.id).toEqual('child-a'); + + await act(() => { + fragmentRefB.current.blur(); + }); + expect(document.activeElement.id).toEqual('child-a'); }); - expect(document.activeElement.id).toEqual('child-b'); - document.activeElement.blur(); }); }); @@ -389,10 +503,6 @@ describe('FragmentRefs', () => { const nestedChildRef = React.createRef(); const root = ReactDOMClient.createRoot(container); - function Wrapper({children}) { - return children; - } - await act(() => { root.render( <div> diff --git a/packages/react-reconciler/src/ReactFiberTreeReflection.js b/packages/react-reconciler/src/ReactFiberTreeReflection.js index 14ce8405d7b99..6d113377c35cf 100644 --- a/packages/react-reconciler/src/ReactFiberTreeReflection.js +++ b/packages/react-reconciler/src/ReactFiberTreeReflection.js @@ -326,7 +326,7 @@ export function traverseFragmentInstance<A, B, C>( b: B, c: C, ): void { - return traverseFragmentInstanceChildren(fragmentFiber.child, fn, a, b, c); + traverseFragmentInstanceChildren(fragmentFiber.child, fn, a, b, c); } function traverseFragmentInstanceChildren<A, B, C>( From 476f53879e80d4ee976ed036a0e8986126fa3117 Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Tue, 18 Mar 2025 13:54:26 -0400 Subject: [PATCH 159/300] Add getClientRects to fragment instances (#32660) Adds `getClientRects()` to fragment instances with a fixture test case. `Element.getClientRect` returns a collection of `DOMRect`s (see example of multiline span returning two `DOMRect` boxes). `fragmentInstance.getClientRects` here flattens those collections into an array of rects. --- fixtures/dom/src/components/Fixture.js | 4 + .../fragment-refs/EventListenerCase.js | 38 +++---- .../fixtures/fragment-refs/FocusCase.js | 17 +-- .../fragment-refs/GetClientRectsCase.js | 100 ++++++++++++++++++ .../fragment-refs/IntersectionObserverCase.js | 96 +++++++++-------- .../fixtures/fragment-refs/index.js | 2 + fixtures/dom/src/style.css | 10 +- .../src/client/ReactFiberConfigDOM.js | 14 +++ .../__tests__/ReactDOMFragmentRefs-test.js | 43 ++++++++ .../src/__tests__/utils/IntersectionMocks.js | 18 ++++ 10 files changed, 266 insertions(+), 76 deletions(-) create mode 100644 fixtures/dom/src/components/fixtures/fragment-refs/GetClientRectsCase.js diff --git a/fixtures/dom/src/components/Fixture.js b/fixtures/dom/src/components/Fixture.js index 3643a65c36751..7fa303a3f2f17 100644 --- a/fixtures/dom/src/components/Fixture.js +++ b/fixtures/dom/src/components/Fixture.js @@ -16,3 +16,7 @@ class Fixture extends React.Component { Fixture.propTypes = propTypes; export default Fixture; + +Fixture.Controls = function FixtureControls({children}) { + return <div className="test-fixture__controls">{children}</div>; +}; diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js index b3baf5a381d7a..125b67cf39a96 100644 --- a/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js +++ b/fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js @@ -49,7 +49,7 @@ export default function EventListenerCase() { </TestCase.ExpectedResult> <Fixture> - <div className="control-box"> + <Fixture.Controls> <div>Target count: {extraChildCount + 3}</div> <button onClick={() => { @@ -69,26 +69,26 @@ export default function EventListenerCase() { }}> Remove click event listeners </button> - <div class="card-container"> - <Fragment ref={fragmentRef}> - <div className="card" id="child-a"> - Child A + </Fixture.Controls> + <div className="card-container"> + <Fragment ref={fragmentRef}> + <div className="card" id="child-a"> + Child A + </div> + <div className="card" id="child-b"> + Child B + </div> + <WrapperComponent> + <div className="card" id="child-c"> + Child C </div> - <div className="card" id="child-b"> - Child B - </div> - <WrapperComponent> - <div className="card" id="child-c"> - Child C + {Array.from({length: extraChildCount}).map((_, index) => ( + <div className="card" id={'extra-child-' + index} key={index}> + Extra Child {index} </div> - {Array.from({length: extraChildCount}).map((_, index) => ( - <div className="card" id={'extra-child-' + index} key={index}> - Extra Child {index} - </div> - ))} - </WrapperComponent> - </Fragment> - </div> + ))} + </WrapperComponent> + </Fragment> </div> </Fixture> </TestCase> diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js index 43850b1aa0773..888107c9e07c7 100644 --- a/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js +++ b/fixtures/dom/src/components/fixtures/fragment-refs/FocusCase.js @@ -31,15 +31,16 @@ export default function FocusCase() { </p> </TestCase.ExpectedResult> - <button onClick={() => fragmentRef.current.focus()}> - Focus first child - </button> - <button onClick={() => fragmentRef.current.focusLast()}> - Focus last child - </button> - <button onClick={() => fragmentRef.current.blur()}>Blur</button> - <Fixture> + <Fixture.Controls> + <button onClick={() => fragmentRef.current.focus()}> + Focus first child + </button> + <button onClick={() => fragmentRef.current.focusLast()}> + Focus last child + </button> + <button onClick={() => fragmentRef.current.blur()}>Blur</button> + </Fixture.Controls> <div className="highlight-focused-children" style={{display: 'flex'}}> <Fragment ref={fragmentRef}> <div style={{outline: '1px solid black'}}>Unfocusable div</div> diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/GetClientRectsCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/GetClientRectsCase.js new file mode 100644 index 0000000000000..7b20a0a2e0d67 --- /dev/null +++ b/fixtures/dom/src/components/fixtures/fragment-refs/GetClientRectsCase.js @@ -0,0 +1,100 @@ +import TestCase from '../../TestCase'; +import Fixture from '../../Fixture'; + +const React = window.React; +const {Fragment, useEffect, useRef, useState} = React; + +export default function GetClientRectsCase() { + const fragmentRef = useRef(null); + const [rects, setRects] = useState([]); + const getRects = () => { + const rects = fragmentRef.current.getClientRects(); + setRects(rects); + }; + + return ( + <TestCase title="getClientRects"> + <TestCase.Steps> + <li> + Click the "Print Rects" button to get the client rects of the + elements. + </li> + </TestCase.Steps> + <TestCase.ExpectedResult> + Calling getClientRects on the fragment instance will return a list of a + DOMRectList for each child node. + </TestCase.ExpectedResult> + <Fixture> + <Fixture.Controls> + <button onClick={getRects}>Print Rects</button> + <div style={{display: 'flex'}}> + <div + style={{ + position: 'relative', + width: '30vw', + height: '30vh', + border: '1px solid black', + }}> + {rects.map(({x, y, width, height}, index) => { + const scale = 0.3; + + return ( + <div + key={index} + style={{ + position: 'absolute', + top: y * scale, + left: x * scale, + width: width * scale, + height: height * scale, + border: '1px solid red', + boxSizing: 'border-box', + }}></div> + ); + })} + </div> + <div> + {rects.map(({x, y, width, height}, index) => { + return ( + <div> + {index} :: {`{`}x: {x}, y: {y}, width: {width}, height:{' '} + {height} + {`}`} + </div> + ); + })} + </div> + </div> + </Fixture.Controls> + <Fragment ref={fragmentRef}> + <span + style={{ + width: '300px', + height: '250px', + backgroundColor: 'lightblue', + fontSize: 20, + border: '1px solid black', + marginBottom: '10px', + }}> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + </span> + <div + style={{ + width: '150px', + height: '100px', + backgroundColor: 'lightgreen', + border: '1px solid black', + }}></div> + <div + style={{ + width: '500px', + height: '50px', + backgroundColor: 'lightpink', + border: '1px solid black', + }}></div> + </Fragment> + </Fixture> + </TestCase> + ); +} diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js b/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js index e087215aee4df..057e94d09ac1c 100644 --- a/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js +++ b/fixtures/dom/src/components/fixtures/fragment-refs/IntersectionObserverCase.js @@ -90,53 +90,55 @@ export default function IntersectionObserverCase() { </p> </TestCase.ExpectedResult> <Fixture> - <button - onClick={() => { - setItems(prev => [ - ...prev, - [`Extra child: ${prev.length + 1}`, false], - ]); - }}> - Add Child - </button> - <button - onClick={() => { - setItems(prev => { - if (prev.length === 3) { - return prev; - } - return prev.slice(0, prev.length - 1); - }); - }}> - Remove Child - </button> - <button - onClick={() => { - fragmentRef.current.observeUsing(observerRef.current); - }}> - Observe - </button> - <button - onClick={() => { - fragmentRef.current.unobserveUsing(observerRef.current); - setItems(prev => { - return prev.map(item => [item[0], false]); - }); - }}> - Unobserve - </button> - {anyOnScreen && ( - <div className="fixed-sidebar card-container"> - <p> - <strong>Children on screen:</strong> - </p> - {items.map(item => ( - <div className={`card ${item[1] ? 'onscreen' : null}`}> - {item[0]} - </div> - ))} - </div> - )} + <Fixture.Controls> + <button + onClick={() => { + setItems(prev => [ + ...prev, + [`Extra child: ${prev.length + 1}`, false], + ]); + }}> + Add Child + </button> + <button + onClick={() => { + setItems(prev => { + if (prev.length === 3) { + return prev; + } + return prev.slice(0, prev.length - 1); + }); + }}> + Remove Child + </button> + <button + onClick={() => { + fragmentRef.current.observeUsing(observerRef.current); + }}> + Observe + </button> + <button + onClick={() => { + fragmentRef.current.unobserveUsing(observerRef.current); + setItems(prev => { + return prev.map(item => [item[0], false]); + }); + }}> + Unobserve + </button> + {anyOnScreen && ( + <div className="fixed-sidebar card-container"> + <p> + <strong>Children on screen:</strong> + </p> + {items.map(item => ( + <div className={`card ${item[1] ? 'onscreen' : null}`}> + {item[0]} + </div> + ))} + </div> + )} + </Fixture.Controls> <Fragment ref={fragmentRef}> <ObservedChild id="A" /> <WrapperComponent> diff --git a/fixtures/dom/src/components/fixtures/fragment-refs/index.js b/fixtures/dom/src/components/fixtures/fragment-refs/index.js index 89316a35efe7f..b84f273177d3a 100644 --- a/fixtures/dom/src/components/fixtures/fragment-refs/index.js +++ b/fixtures/dom/src/components/fixtures/fragment-refs/index.js @@ -3,6 +3,7 @@ import EventListenerCase from './EventListenerCase'; import IntersectionObserverCase from './IntersectionObserverCase'; import ResizeObserverCase from './ResizeObserverCase'; import FocusCase from './FocusCase'; +import GetClientRectsCase from './GetClientRectsCase'; const React = window.React; @@ -13,6 +14,7 @@ export default function FragmentRefsPage() { <IntersectionObserverCase /> <ResizeObserverCase /> <FocusCase /> + <GetClientRectsCase /> </FixtureSet> ); } diff --git a/fixtures/dom/src/style.css b/fixtures/dom/src/style.css index 0442b0606b5d8..66fda7afe0cac 100644 --- a/fixtures/dom/src/style.css +++ b/fixtures/dom/src/style.css @@ -224,7 +224,7 @@ p { } .test-case__body { - padding: 10px; + padding: 10px 10px 0 10px; } .test-case__desc { @@ -280,11 +280,17 @@ p { .test-fixture { padding: 20px; - margin: 0 -15px; /* opposite of .test-case padding */ + margin: 0 -10px; /* opposite of .test-case padding */ background-color: #f4f4f4; border-top: 1px solid #d9d9d9; } +.test-fixture__controls { + margin: -20px -20px 20px -20px; + padding: 20px; + border: 1px solid #444; +} + .field-group { overflow: hidden; } diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 1398f2c50f28f..6efcf65091c73 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -2229,6 +2229,7 @@ export type FragmentInstanceType = { blur(): void, observeUsing(observer: IntersectionObserver | ResizeObserver): void, unobserveUsing(observer: IntersectionObserver | ResizeObserver): void, + getClientRects(): Array<DOMRect>, }; function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) { @@ -2406,6 +2407,19 @@ function unobserveChild( observer.unobserve(child); return false; } +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.getClientRects = function ( + this: FragmentInstanceType, +): Array<DOMRect> { + const rects: Array<DOMRect> = []; + traverseFragmentInstance(this._fragmentFiber, collectClientRects, rects); + return rects; +}; +function collectClientRects(child: Instance, rects: Array<DOMRect>): boolean { + // $FlowFixMe[method-unbinding] + rects.push.apply(rects, child.getClientRects()); + return false; +} function normalizeListenerOptions( opts: ?EventListenerOptionsOrUseCapture, diff --git a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js index 3ab50fa64b28e..c2cfc99ee87b0 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js @@ -17,6 +17,7 @@ let Fragment; let Activity; let mockIntersectionObserver; let simulateIntersection; +let setClientRects; let assertConsoleErrorDev; function Wrapper({children}) { @@ -34,6 +35,7 @@ describe('FragmentRefs', () => { const IntersectionMocks = require('./utils/IntersectionMocks'); mockIntersectionObserver = IntersectionMocks.mockIntersectionObserver; simulateIntersection = IntersectionMocks.simulateIntersection; + setClientRects = IntersectionMocks.setClientRects; assertConsoleErrorDev = require('internal-test-utils').assertConsoleErrorDev; @@ -841,4 +843,45 @@ describe('FragmentRefs', () => { ); }); }); + + describe('getClientRects', () => { + // @gate enableFragmentRefs + it('returns the bounding client recs of all children', async () => { + const fragmentRef = React.createRef(); + const childARef = React.createRef(); + const childBRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( + <React.Fragment ref={fragmentRef}> + <div ref={childARef} /> + <div ref={childBRef} /> + </React.Fragment> + ); + } + + await act(() => root.render(<Test />)); + setClientRects(childARef.current, [ + { + x: 1, + y: 2, + width: 3, + height: 4, + }, + { + x: 5, + y: 6, + width: 7, + height: 8, + }, + ]); + setClientRects(childBRef.current, [{x: 9, y: 10, width: 11, height: 12}]); + const clientRects = fragmentRef.current.getClientRects(); + expect(clientRects.length).toBe(3); + expect(clientRects[0].left).toBe(1); + expect(clientRects[1].left).toBe(5); + expect(clientRects[2].left).toBe(9); + }); + }); }); diff --git a/packages/react-dom/src/__tests__/utils/IntersectionMocks.js b/packages/react-dom/src/__tests__/utils/IntersectionMocks.js index a6a6b68caa0c7..d89a683e8e938 100644 --- a/packages/react-dom/src/__tests__/utils/IntersectionMocks.js +++ b/packages/react-dom/src/__tests__/utils/IntersectionMocks.js @@ -75,3 +75,21 @@ export function setBoundingClientRect(target, {x, y, width, height}) { }; }; } + +/** + * Stub out getClientRects for the specified target. + */ +export function setClientRects(target, rects) { + target.getClientRects = function () { + return rects.map(({x, y, width, height}) => ({ + width, + height, + left: x, + right: x + width, + top: y, + bottom: y + height, + x, + y, + })); + }; +} From 86d5ac0882305c5bbff0fd7b40385e7d50d0d2b4 Mon Sep 17 00:00:00 2001 From: dan <dan.abramov@me.com> Date: Tue, 18 Mar 2025 19:05:56 +0000 Subject: [PATCH 160/300] Revert "Fix:- Improve HOC support and state preservation in React Refresh" (#32214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts facebook/react#30660 I don’t feel confident in the approach. This part of code is supposed to rely on the module bundler behaving as expected. _Maybe_ this is correct but I need to review it closer — it was intentionally _not_ implemented this way originally. I’ll try to take a closer look some time this week. We don’t have to merge this revert right now but just flagging that I don’t understand the thinking behind the new approach and don’t have confidence in it. --- .../react-refresh/src/ReactFreshRuntime.js | 68 +---- .../src/__tests__/ReactFresh-test.js | 236 ------------------ 2 files changed, 11 insertions(+), 293 deletions(-) diff --git a/packages/react-refresh/src/ReactFreshRuntime.js b/packages/react-refresh/src/ReactFreshRuntime.js index f7bce315ec033..43e8148d3de11 100644 --- a/packages/react-refresh/src/ReactFreshRuntime.js +++ b/packages/react-refresh/src/ReactFreshRuntime.js @@ -146,21 +146,6 @@ function canPreserveStateBetween(prevType: any, nextType: any) { if (isReactClass(prevType) || isReactClass(nextType)) { return false; } - - if (typeof prevType !== typeof nextType) { - return false; - } else if ( - typeof prevType === 'object' && - prevType !== null && - nextType !== null - ) { - if ( - getProperty(prevType, '$$typeof') !== getProperty(nextType, '$$typeof') - ) { - return false; - } - } - if (haveEqualSignatures(prevType, nextType)) { return true; } @@ -198,18 +183,6 @@ function getProperty(object: any, property: string) { } } -function registerRefreshUpdate( - update: RefreshUpdate, - family: Family, - shouldPreserveState: boolean, -) { - if (shouldPreserveState) { - update.updatedFamilies.add(family); - } else { - update.staleFamilies.add(family); - } -} - export function performReactRefresh(): RefreshUpdate | null { if (!__DEV__) { throw new Error( @@ -227,11 +200,6 @@ export function performReactRefresh(): RefreshUpdate | null { try { const staleFamilies = new Set<Family>(); const updatedFamilies = new Set<Family>(); - // TODO: rename these fields to something more meaningful. - const update: RefreshUpdate = { - updatedFamilies, // Families that will re-render preserving state - staleFamilies, // Families that will be remounted - }; const updates = pendingUpdates; pendingUpdates = []; @@ -243,34 +211,20 @@ export function performReactRefresh(): RefreshUpdate | null { updatedFamiliesByType.set(nextType, family); family.current = nextType; - const shouldPreserveState = canPreserveStateBetween(prevType, nextType); - - if (typeof prevType === 'object' && prevType !== null) { - const nextFamily = { - current: - getProperty(nextType, '$$typeof') === REACT_FORWARD_REF_TYPE - ? nextType.render - : getProperty(nextType, '$$typeof') === REACT_MEMO_TYPE - ? nextType.type - : nextType, - }; - switch (getProperty(prevType, '$$typeof')) { - case REACT_FORWARD_REF_TYPE: { - updatedFamiliesByType.set(prevType.render, nextFamily); - registerRefreshUpdate(update, nextFamily, shouldPreserveState); - break; - } - case REACT_MEMO_TYPE: - updatedFamiliesByType.set(prevType.type, nextFamily); - registerRefreshUpdate(update, nextFamily, shouldPreserveState); - break; - } - } - // Determine whether this should be a re-render or a re-mount. - registerRefreshUpdate(update, family, shouldPreserveState); + if (canPreserveStateBetween(prevType, nextType)) { + updatedFamilies.add(family); + } else { + staleFamilies.add(family); + } }); + // TODO: rename these fields to something more meaningful. + const update: RefreshUpdate = { + updatedFamilies, // Families that will re-render preserving state + staleFamilies, // Families that will be remounted + }; + helpersByRendererID.forEach(helpers => { // Even if there are no roots, set the handler on first update. // This ensures that if *new* roots are mounted, they'll use the resolve handler. diff --git a/packages/react-refresh/src/__tests__/ReactFresh-test.js b/packages/react-refresh/src/__tests__/ReactFresh-test.js index f5148f36040a6..6fb00a66a24b1 100644 --- a/packages/react-refresh/src/__tests__/ReactFresh-test.js +++ b/packages/react-refresh/src/__tests__/ReactFresh-test.js @@ -699,242 +699,6 @@ describe('ReactFresh', () => { } }); - it('can remount when change function to memo', async () => { - if (__DEV__) { - await act(async () => { - await render(() => { - function Test() { - return <p>hi test</p>; - } - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - // Check the initial render - const el = container.firstChild; - expect(el.textContent).toBe('hi test'); - - // Patch to change function to memo - await act(async () => { - await patch(() => { - function Test2() { - return <p>hi memo</p>; - } - const Test = React.memo(Test2); - $RefreshReg$(Test2, 'Test2'); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - // Check remount - expect(container.firstChild).not.toBe(el); - const nextEl = container.firstChild; - expect(nextEl.textContent).toBe('hi memo'); - - // Patch back to original function - await act(async () => { - await patch(() => { - function Test() { - return <p>hi test</p>; - } - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - // Check final remount - expect(container.firstChild).not.toBe(nextEl); - const newEl = container.firstChild; - expect(newEl.textContent).toBe('hi test'); - } - }); - - it('can remount when change memo to forwardRef', async () => { - if (__DEV__) { - await act(async () => { - await render(() => { - function Test2() { - return <p>hi memo</p>; - } - const Test = React.memo(Test2); - $RefreshReg$(Test2, 'Test2'); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - // Check the initial render - const el = container.firstChild; - expect(el.textContent).toBe('hi memo'); - - // Patch to change memo to forwardRef - await act(async () => { - await patch(() => { - function Test2() { - return <p>hi forwardRef</p>; - } - const Test = React.forwardRef(Test2); - $RefreshReg$(Test2, 'Test2'); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - // Check remount - expect(container.firstChild).not.toBe(el); - const nextEl = container.firstChild; - expect(nextEl.textContent).toBe('hi forwardRef'); - - // Patch back to memo - await act(async () => { - await patch(() => { - function Test2() { - return <p>hi memo</p>; - } - const Test = React.memo(Test2); - $RefreshReg$(Test2, 'Test2'); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - // Check final remount - expect(container.firstChild).not.toBe(nextEl); - const newEl = container.firstChild; - expect(newEl.textContent).toBe('hi memo'); - } - }); - - it('can remount when change function to forwardRef', async () => { - if (__DEV__) { - await act(async () => { - await render(() => { - function Test() { - return <p>hi test</p>; - } - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - // Check the initial render - const el = container.firstChild; - expect(el.textContent).toBe('hi test'); - - // Patch to change function to forwardRef - await act(async () => { - await patch(() => { - function Test2() { - return <p>hi forwardRef</p>; - } - const Test = React.forwardRef(Test2); - $RefreshReg$(Test2, 'Test2'); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - // Check remount - expect(container.firstChild).not.toBe(el); - const nextEl = container.firstChild; - expect(nextEl.textContent).toBe('hi forwardRef'); - - // Patch back to a new function - await act(async () => { - await patch(() => { - function Test() { - return <p>hi test1</p>; - } - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - // Check final remount - expect(container.firstChild).not.toBe(nextEl); - const newEl = container.firstChild; - expect(newEl.textContent).toBe('hi test1'); - } - }); - - it('resets state when switching between different component types', async () => { - if (__DEV__) { - await act(async () => { - await render(() => { - function Test() { - const [count, setCount] = React.useState(0); - return ( - <div onClick={() => setCount(c => c + 1)}>count: {count}</div> - ); - } - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - expect(container.firstChild.textContent).toBe('count: 0'); - await act(async () => { - container.firstChild.click(); - }); - expect(container.firstChild.textContent).toBe('count: 1'); - - await act(async () => { - await patch(() => { - function Test2() { - const [count, setCount] = React.useState(0); - return ( - <div onClick={() => setCount(c => c + 1)}>count: {count}</div> - ); - } - const Test = React.memo(Test2); - $RefreshReg$(Test2, 'Test2'); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - expect(container.firstChild.textContent).toBe('count: 0'); - await act(async () => { - container.firstChild.click(); - }); - expect(container.firstChild.textContent).toBe('count: 1'); - - await act(async () => { - await patch(() => { - const Test = React.forwardRef((props, ref) => { - const [count, setCount] = React.useState(0); - const handleClick = () => setCount(c => c + 1); - - // Ensure ref is extensible - const divRef = React.useRef(null); - React.useEffect(() => { - if (ref) { - if (typeof ref === 'function') { - ref(divRef.current); - } else if (Object.isExtensible(ref)) { - ref.current = divRef.current; - } - } - }, [ref]); - - return ( - <div ref={divRef} onClick={handleClick}> - count: {count} - </div> - ); - }); - $RefreshReg$(Test, 'Test'); - return Test; - }); - }); - - expect(container.firstChild.textContent).toBe('count: 0'); - await act(async () => { - container.firstChild.click(); - }); - expect(container.firstChild.textContent).toBe('count: 1'); - } - }); - it('can update simple memo function in isolation', async () => { if (__DEV__) { await render(() => { From 6584a6eec488a7a155fe2231874aecf178b07a9a Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Tue, 18 Mar 2025 18:00:08 -0400 Subject: [PATCH 161/300] [compiler] Hoist dependencies from functions more conservatively (#32616) Alternative to facebook/react#31584 which sets enableTreatFunctionDepsAsConditional:true` by default. This PR changes dependency hoisting to be more conservative while trying to preserve an optimal "happy path". We assume that a function "is likely called" if we observe the following in the react function body. - a direct callsite - passed directly as a jsx attribute or child - passed directly to a hook - a direct return A function is also "likely called" if it is directly called, passed to jsx / hooks, or returned from another function that "is likely called". Note that this approach marks the function definition site with its hoistable properties (not its use site). I tried implementing use-site hoisting semantics, but it felt both unpredictable (i.e. as a developer, I can't trust that callbacks are well memoized) and not helpful (type + null checks of a value are usually colocated with their use site) In this fixture (copied here for easy reference), it should be safe to use `a.value` and `b.value` as dependencies, even though these functions are conditionally called. ```js // inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx function Component({a, b}) { const logA = () => { console.log(a.value); }; const logB = () => { console.log(b.value); }; const hasLogged = useRef(false); const log = () => { if (!hasLogged.current) { logA(); logB(); hasLogged.current = true; } }; return <Stringify log={log} shouldInvokeFns={true} />; } ``` On the other hand, this means that we produce invalid output for code like manually implementing `Array.map` ```js // inner-function/nullable-objects/bug-invalid-array-map-manual.js function useFoo({arr1, arr2}) { const cb = e => arr2[0].value + e.value; const y = []; for (let i = 0; i < arr1.length; i++) { y.push(cb(arr1[i])); } return y; } ``` --- .../src/HIR/CollectHoistablePropertyLoads.ts | 193 +++++++++++++++--- ...nction-expression-prototype-call.expect.md | 4 +- ...map-named-callback-cross-context.expect.md | 133 ++++++++++++ .../array-map-named-callback-cross-context.js | 35 ++++ .../array-map-named-callback.expect.md | 108 ++++++++++ .../array-map-named-callback.js | 23 +++ ...rray-map-named-chained-callbacks.expect.md | 130 ++++++++++++ .../array-map-named-chained-callbacks.js | 26 +++ .../array-map-simple.expect.md | 111 ++++++++++ .../nullable-objects/array-map-simple.js | 25 +++ .../conditional-call-chain.expect.md | 112 ++++++++++ .../assume-invoked/conditional-call-chain.tsx | 29 +++ .../assume-invoked/conditional-call.expect.md | 85 ++++++++ .../assume-invoked/conditional-call.ts | 23 +++ .../conditionally-return-fn.expect.md | 87 ++++++++ .../assume-invoked/conditionally-return-fn.ts | 32 +++ .../assume-invoked/direct-call.expect.md | 74 +++++++ .../assume-invoked/direct-call.ts | 17 ++ ...nal-callsite-in-another-function.expect.md | 130 ++++++++++++ ...onditional-callsite-in-another-function.ts | 51 +++++ .../assume-invoked/hook-call.expect.md | 80 ++++++++ .../assume-invoked/hook-call.ts | 29 +++ .../assume-invoked/jsx-and-passed.expect.md | 80 ++++++++ .../assume-invoked/jsx-and-passed.ts | 17 ++ .../assume-invoked/jsx-function.expect.md | 75 +++++++ .../assume-invoked/jsx-function.tsx | 29 +++ .../assume-invoked/return-function.expect.md | 78 +++++++ .../assume-invoked/return-function.ts | 28 +++ .../use-memo-returned.expect.md | 82 ++++++++ .../assume-invoked/use-memo-returned.ts | 28 +++ .../bug-invalid-array-map-manual.expect.md | 68 ++++++ .../bug-invalid-array-map-manual.js | 18 ++ .../return-object-of-functions.expect.md | 57 ++++++ .../return-object-of-functions.js | 17 ++ ...function-uncond-access-local-var.expect.md | 4 +- ...nfer-object-method-uncond-access.expect.md | 4 +- ...l-dependency-on-context-variable.expect.md | 4 +- .../context-var-granular-dep.expect.md | 4 +- ...e-variables-nested-object-method.expect.md | 4 +- .../useEffect-nested-lambdas.expect.md | 25 +-- .../packages/snap/src/SproutTodoFilter.ts | 1 + 41 files changed, 2107 insertions(+), 53 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts index 7b352696860e8..cb6854d1b3674 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts @@ -13,11 +13,13 @@ import { BlockId, DependencyPathEntry, GeneratedSource, + getHookKind, HIRFunction, Identifier, IdentifierId, InstructionId, InstructionValue, + LoweredFunction, PropertyLiteral, ReactiveScopeDependency, ScopeId, @@ -112,6 +114,9 @@ export function collectHoistablePropertyLoads( hoistableFromOptionals, registry, nestedFnImmutableContext: null, + assumedInvokedFns: fn.env.config.enableTreatFunctionDepsAsConditional + ? new Set() + : getAssumedInvokedFunctions(fn), }); } @@ -127,6 +132,11 @@ type CollectHoistablePropertyLoadsContext = { * but are currently kept separate for readability. */ nestedFnImmutableContext: ReadonlySet<IdentifierId> | null; + /** + * Functions which are assumed to be eventually called (as opposed to ones which might + * not be called, e.g. the 0th argument of Array.map) + */ + assumedInvokedFns: ReadonlySet<LoweredFunction>; }; function collectHoistablePropertyLoadsImpl( fn: HIRFunction, @@ -338,7 +348,13 @@ function collectNonNullsInBlocks( context.registry.getOrCreateIdentifier(identifier), ); } - const nodes = new Map<BlockId, BlockInfo>(); + const nodes = new Map< + BlockId, + { + block: BasicBlock; + assumedNonNullObjects: Set<PropertyPathNode>; + } + >(); for (const [_, block] of fn.body.blocks) { const assumedNonNullObjects = new Set<PropertyPathNode>( knownNonNullIdentifiers, @@ -358,32 +374,30 @@ function collectNonNullsInBlocks( ) { assumedNonNullObjects.add(maybeNonNull); } - if ( - (instr.value.kind === 'FunctionExpression' || - instr.value.kind === 'ObjectMethod') && - !fn.env.config.enableTreatFunctionDepsAsConditional - ) { + if (instr.value.kind === 'FunctionExpression') { const innerFn = instr.value.loweredFunc; - const innerHoistableMap = collectHoistablePropertyLoadsImpl( - innerFn.func, - { - ...context, - nestedFnImmutableContext: - context.nestedFnImmutableContext ?? - new Set( - innerFn.func.context - .filter(place => - isImmutableAtInstr(place.identifier, instr.id, context), - ) - .map(place => place.identifier.id), - ), - }, - ); - const innerHoistables = assertNonNull( - innerHoistableMap.get(innerFn.func.body.entry), - ); - for (const entry of innerHoistables.assumedNonNullObjects) { - assumedNonNullObjects.add(entry); + if (context.assumedInvokedFns.has(innerFn)) { + const innerHoistableMap = collectHoistablePropertyLoadsImpl( + innerFn.func, + { + ...context, + nestedFnImmutableContext: + context.nestedFnImmutableContext ?? + new Set( + innerFn.func.context + .filter(place => + isImmutableAtInstr(place.identifier, instr.id, context), + ) + .map(place => place.identifier.id), + ), + }, + ); + const innerHoistables = assertNonNull( + innerHoistableMap.get(innerFn.func.body.entry), + ); + for (const entry of innerHoistables.assumedNonNullObjects) { + assumedNonNullObjects.add(entry); + } } } } @@ -591,3 +605,130 @@ function reduceMaybeOptionalChains( } } while (changed); } + +function getAssumedInvokedFunctions( + fn: HIRFunction, + temporaries: Map< + IdentifierId, + {fn: LoweredFunction; mayInvoke: Set<LoweredFunction>} + > = new Map(), +): ReadonlySet<LoweredFunction> { + const hoistableFunctions = new Set<LoweredFunction>(); + /** + * Step 1: Conservatively collect identifier to function expression mappings + */ + for (const block of fn.body.blocks.values()) { + for (const {lvalue, value} of block.instructions) { + /** + * Conservatively only match function expressions which can have guaranteed ssa. + * ObjectMethods and ObjectProperties do not. + */ + if (value.kind === 'FunctionExpression') { + temporaries.set(lvalue.identifier.id, { + fn: value.loweredFunc, + mayInvoke: new Set(), + }); + } else if (value.kind === 'StoreLocal') { + const lvalue = value.lvalue.place.identifier; + const maybeLoweredFunc = temporaries.get(value.value.identifier.id); + if (maybeLoweredFunc != null) { + temporaries.set(lvalue.id, maybeLoweredFunc); + } + } else if (value.kind === 'LoadLocal') { + const maybeLoweredFunc = temporaries.get(value.place.identifier.id); + if (maybeLoweredFunc != null) { + temporaries.set(lvalue.identifier.id, maybeLoweredFunc); + } + } + } + } + /** + * Step 2: Forward pass to do analysis of assumed function calls. Note that + * this is conservative and does not count indirect references through + * containers (e.g. `return {cb: () => {...}})`). + */ + for (const block of fn.body.blocks.values()) { + for (const {lvalue, value} of block.instructions) { + if (value.kind === 'CallExpression') { + const callee = value.callee; + const maybeHook = getHookKind(fn.env, callee.identifier); + const maybeLoweredFunc = temporaries.get(callee.identifier.id); + if (maybeLoweredFunc != null) { + // Direct calls + hoistableFunctions.add(maybeLoweredFunc.fn); + } else if (maybeHook != null) { + /** + * Assume arguments to all hooks are safe to invoke + */ + for (const arg of value.args) { + if (arg.kind === 'Identifier') { + const maybeLoweredFunc = temporaries.get(arg.identifier.id); + if (maybeLoweredFunc != null) { + hoistableFunctions.add(maybeLoweredFunc.fn); + } + } + } + } + } else if (value.kind === 'JsxExpression') { + /** + * Assume JSX attributes and children are safe to invoke + */ + for (const attr of value.props) { + if (attr.kind === 'JsxSpreadAttribute') { + continue; + } + const maybeLoweredFunc = temporaries.get(attr.place.identifier.id); + if (maybeLoweredFunc != null) { + hoistableFunctions.add(maybeLoweredFunc.fn); + } + } + for (const child of value.children ?? []) { + const maybeLoweredFunc = temporaries.get(child.identifier.id); + if (maybeLoweredFunc != null) { + hoistableFunctions.add(maybeLoweredFunc.fn); + } + } + } else if (value.kind === 'FunctionExpression') { + /** + * Recursively traverse into other function expressions which may invoke + * or pass already declared functions to react (e.g. as JSXAttributes). + * + * If lambda A calls lambda B, we assume lambda B is safe to invoke if + * lambda A is -- even if lambda B is conditionally called. (see + * `conditional-call-chain` fixture for example). + */ + const loweredFunc = value.loweredFunc.func; + const lambdasCalled = getAssumedInvokedFunctions( + loweredFunc, + temporaries, + ); + const maybeLoweredFunc = temporaries.get(lvalue.identifier.id); + if (maybeLoweredFunc != null) { + for (const called of lambdasCalled) { + maybeLoweredFunc.mayInvoke.add(called); + } + } + } + } + if (block.terminal.kind === 'return') { + /** + * Assume directly returned functions are safe to call + */ + const maybeLoweredFunc = temporaries.get( + block.terminal.value.identifier.id, + ); + if (maybeLoweredFunc != null) { + hoistableFunctions.add(maybeLoweredFunc.fn); + } + } + } + + for (const [_, {fn, mayInvoke}] of temporaries) { + if (hoistableFunctions.has(fn)) { + for (const called of mayInvoke) { + hoistableFunctions.add(called); + } + } + } + return hoistableFunctions; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md index 5666876f00158..2df5b908902d9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md @@ -23,11 +23,11 @@ import { c as _c } from "react/compiler-runtime"; function Component(props) { const $ = _c(4); let t0; - if ($[0] !== props.name) { + if ($[0] !== props) { t0 = function () { return <div>{props.name}</div>; }; - $[0] = props.name; + $[0] = props; $[1] = t0; } else { t0 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md new file mode 100644 index 0000000000000..c1a6dfb3eae11 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md @@ -0,0 +1,133 @@ + +## Input + +```javascript +import {Stringify} from 'shared-runtime'; + +/** + * Forked from array-map-simple.js + * + * Named lambdas (e.g. cb1) may be defined in the top scope of a function and + * used in a different lambda (getArrMap1). + * + * Here, we should try to determine if cb1 is actually called. In this case: + * - getArrMap1 is assumed to be called as it's passed to JSX + * - cb1 is not assumed to be called since it's only used as a call operand + */ +function useFoo({arr1, arr2}) { + const cb1 = e => arr1[0].value + e.value; + const getArrMap1 = () => arr1.map(cb1); + const cb2 = e => arr2[0].value + e.value; + const getArrMap2 = () => arr1.map(cb2); + return ( + <Stringify + getArrMap1={getArrMap1} + getArrMap2={getArrMap2} + shouldInvokeFns={true} + /> + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { Stringify } from "shared-runtime"; + +/** + * Forked from array-map-simple.js + * + * Named lambdas (e.g. cb1) may be defined in the top scope of a function and + * used in a different lambda (getArrMap1). + * + * Here, we should try to determine if cb1 is actually called. In this case: + * - getArrMap1 is assumed to be called as it's passed to JSX + * - cb1 is not assumed to be called since it's only used as a call operand + */ +function useFoo(t0) { + const $ = _c(13); + const { arr1, arr2 } = t0; + let t1; + if ($[0] !== arr1[0]) { + t1 = (e) => arr1[0].value + e.value; + $[0] = arr1[0]; + $[1] = t1; + } else { + t1 = $[1]; + } + const cb1 = t1; + let t2; + if ($[2] !== arr1 || $[3] !== cb1) { + t2 = () => arr1.map(cb1); + $[2] = arr1; + $[3] = cb1; + $[4] = t2; + } else { + t2 = $[4]; + } + const getArrMap1 = t2; + let t3; + if ($[5] !== arr2) { + t3 = (e_0) => arr2[0].value + e_0.value; + $[5] = arr2; + $[6] = t3; + } else { + t3 = $[6]; + } + const cb2 = t3; + let t4; + if ($[7] !== arr1 || $[8] !== cb2) { + t4 = () => arr1.map(cb2); + $[7] = arr1; + $[8] = cb2; + $[9] = t4; + } else { + t4 = $[9]; + } + const getArrMap2 = t4; + let t5; + if ($[10] !== getArrMap1 || $[11] !== getArrMap2) { + t5 = ( + <Stringify + getArrMap1={getArrMap1} + getArrMap2={getArrMap2} + shouldInvokeFns={true} + /> + ); + $[10] = getArrMap1; + $[11] = getArrMap2; + $[12] = t5; + } else { + t5 = $[12]; + } + return t5; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ arr1: [], arr2: [] }], + sequentialRenders: [ + { arr1: [], arr2: [] }, + { arr1: [], arr2: null }, + { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div> +<div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div> +<div>{"getArrMap1":{"kind":"Function","result":[2,3]},"getArrMap2":{"kind":"Function","result":[0,1]},"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.js new file mode 100644 index 0000000000000..e9056562262e8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.js @@ -0,0 +1,35 @@ +import {Stringify} from 'shared-runtime'; + +/** + * Forked from array-map-simple.js + * + * Named lambdas (e.g. cb1) may be defined in the top scope of a function and + * used in a different lambda (getArrMap1). + * + * Here, we should try to determine if cb1 is actually called. In this case: + * - getArrMap1 is assumed to be called as it's passed to JSX + * - cb1 is not assumed to be called since it's only used as a call operand + */ +function useFoo({arr1, arr2}) { + const cb1 = e => arr1[0].value + e.value; + const getArrMap1 = () => arr1.map(cb1); + const cb2 = e => arr2[0].value + e.value; + const getArrMap2 = () => arr1.map(cb2); + return ( + <Stringify + getArrMap1={getArrMap1} + getArrMap2={getArrMap2} + shouldInvokeFns={true} + /> + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.expect.md new file mode 100644 index 0000000000000..a741eb59f2a55 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.expect.md @@ -0,0 +1,108 @@ + +## Input + +```javascript +/** + * Forked from array-map-simple.js + * + * Whether lambdas are named or passed inline shouldn't affect whether we expect + * it to be called. + */ +function useFoo({arr1, arr2}) { + const cb1 = e => arr1[0].value + e.value; + const x = arr1.map(cb1); + const cb2 = e => arr2[0].value + e.value; + const y = arr1.map(cb2); + return [x, y]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; /** + * Forked from array-map-simple.js + * + * Whether lambdas are named or passed inline shouldn't affect whether we expect + * it to be called. + */ +function useFoo(t0) { + const $ = _c(13); + const { arr1, arr2 } = t0; + let t1; + if ($[0] !== arr1[0]) { + t1 = (e) => arr1[0].value + e.value; + $[0] = arr1[0]; + $[1] = t1; + } else { + t1 = $[1]; + } + const cb1 = t1; + let t2; + if ($[2] !== arr1 || $[3] !== cb1) { + t2 = arr1.map(cb1); + $[2] = arr1; + $[3] = cb1; + $[4] = t2; + } else { + t2 = $[4]; + } + const x = t2; + let t3; + if ($[5] !== arr2) { + t3 = (e_0) => arr2[0].value + e_0.value; + $[5] = arr2; + $[6] = t3; + } else { + t3 = $[6]; + } + const cb2 = t3; + let t4; + if ($[7] !== arr1 || $[8] !== cb2) { + t4 = arr1.map(cb2); + $[7] = arr1; + $[8] = cb2; + $[9] = t4; + } else { + t4 = $[9]; + } + const y = t4; + let t5; + if ($[10] !== x || $[11] !== y) { + t5 = [x, y]; + $[10] = x; + $[11] = y; + $[12] = t5; + } else { + t5 = $[12]; + } + return t5; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ arr1: [], arr2: [] }], + sequentialRenders: [ + { arr1: [], arr2: [] }, + { arr1: [], arr2: null }, + { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] }, + ], +}; + +``` + +### Eval output +(kind: ok) [[],[]] +[[],[]] +[[2,3],[0,1]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.js new file mode 100644 index 0000000000000..bf4f3ba66186a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback.js @@ -0,0 +1,23 @@ +/** + * Forked from array-map-simple.js + * + * Whether lambdas are named or passed inline shouldn't affect whether we expect + * it to be called. + */ +function useFoo({arr1, arr2}) { + const cb1 = e => arr1[0].value + e.value; + const x = arr1.map(cb1); + const cb2 = e => arr2[0].value + e.value; + const y = arr1.map(cb2); + return [x, y]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md new file mode 100644 index 0000000000000..9bf77fd1276c0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md @@ -0,0 +1,130 @@ + +## Input + +```javascript +/** + * Forked from array-map-simple.js + * + * Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be + * called (it's only passed to array.map). In this case, we should be + * conservative and assume that all named lambdas are conditionally called. + */ +function useFoo({arr1, arr2}) { + const getVal1 = () => arr1[0].value; + const cb1 = e => getVal1() + e.value; + const x = arr1.map(cb1); + const getVal2 = () => arr2[0].value; + const cb2 = e => getVal2() + e.value; + const y = arr1.map(cb2); + return [x, y]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; /** + * Forked from array-map-simple.js + * + * Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be + * called (it's only passed to array.map). In this case, we should be + * conservative and assume that all named lambdas are conditionally called. + */ +function useFoo(t0) { + const $ = _c(17); + const { arr1, arr2 } = t0; + let t1; + if ($[0] !== arr1[0]) { + t1 = () => arr1[0].value; + $[0] = arr1[0]; + $[1] = t1; + } else { + t1 = $[1]; + } + const getVal1 = t1; + let t2; + if ($[2] !== getVal1) { + t2 = (e) => getVal1() + e.value; + $[2] = getVal1; + $[3] = t2; + } else { + t2 = $[3]; + } + const cb1 = t2; + let t3; + if ($[4] !== arr1 || $[5] !== cb1) { + t3 = arr1.map(cb1); + $[4] = arr1; + $[5] = cb1; + $[6] = t3; + } else { + t3 = $[6]; + } + const x = t3; + let t4; + if ($[7] !== arr2) { + t4 = () => arr2[0].value; + $[7] = arr2; + $[8] = t4; + } else { + t4 = $[8]; + } + const getVal2 = t4; + let t5; + if ($[9] !== getVal2) { + t5 = (e_0) => getVal2() + e_0.value; + $[9] = getVal2; + $[10] = t5; + } else { + t5 = $[10]; + } + const cb2 = t5; + let t6; + if ($[11] !== arr1 || $[12] !== cb2) { + t6 = arr1.map(cb2); + $[11] = arr1; + $[12] = cb2; + $[13] = t6; + } else { + t6 = $[13]; + } + const y = t6; + let t7; + if ($[14] !== x || $[15] !== y) { + t7 = [x, y]; + $[14] = x; + $[15] = y; + $[16] = t7; + } else { + t7 = $[16]; + } + return t7; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ arr1: [], arr2: [] }], + sequentialRenders: [ + { arr1: [], arr2: [] }, + { arr1: [], arr2: null }, + { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] }, + ], +}; + +``` + +### Eval output +(kind: ok) [[],[]] +[[],[]] +[[2,3],[0,1]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.js new file mode 100644 index 0000000000000..598faba46dbf1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.js @@ -0,0 +1,26 @@ +/** + * Forked from array-map-simple.js + * + * Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be + * called (it's only passed to array.map). In this case, we should be + * conservative and assume that all named lambdas are conditionally called. + */ +function useFoo({arr1, arr2}) { + const getVal1 = () => arr1[0].value; + const cb1 = e => getVal1() + e.value; + const x = arr1.map(cb1); + const getVal2 = () => arr2[0].value; + const cb2 = e => getVal2() + e.value; + const y = arr1.map(cb2); + return [x, y]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.expect.md new file mode 100644 index 0000000000000..5eb97aa1bf01e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.expect.md @@ -0,0 +1,111 @@ + +## Input + +```javascript +/** + * Test that we're not hoisting property reads from lambdas that are created to + * pass to opaque functions, which often have maybe-invoke semantics. + * + * In this example, we shouldn't hoist `arr[0].value` out of the lambda. + * ```js + * e => arr[0].value + e.value <-- created to pass to map + * arr.map(<cb>) <-- argument only invoked if array is non-empty + * ``` + */ +function useFoo({arr1, arr2}) { + const x = arr1.map(e => arr1[0].value + e.value); + const y = arr1.map(e => arr2[0].value + e.value); + return [x, y]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; /** + * Test that we're not hoisting property reads from lambdas that are created to + * pass to opaque functions, which often have maybe-invoke semantics. + * + * In this example, we shouldn't hoist `arr[0].value` out of the lambda. + * ```js + * e => arr[0].value + e.value <-- created to pass to map + * arr.map(<cb>) <-- argument only invoked if array is non-empty + * ``` + */ +function useFoo(t0) { + const $ = _c(12); + const { arr1, arr2 } = t0; + let t1; + if ($[0] !== arr1) { + let t2; + if ($[2] !== arr1[0]) { + t2 = (e) => arr1[0].value + e.value; + $[2] = arr1[0]; + $[3] = t2; + } else { + t2 = $[3]; + } + t1 = arr1.map(t2); + $[0] = arr1; + $[1] = t1; + } else { + t1 = $[1]; + } + const x = t1; + let t2; + if ($[4] !== arr1 || $[5] !== arr2) { + let t3; + if ($[7] !== arr2) { + t3 = (e_0) => arr2[0].value + e_0.value; + $[7] = arr2; + $[8] = t3; + } else { + t3 = $[8]; + } + t2 = arr1.map(t3); + $[4] = arr1; + $[5] = arr2; + $[6] = t2; + } else { + t2 = $[6]; + } + const y = t2; + let t3; + if ($[9] !== x || $[10] !== y) { + t3 = [x, y]; + $[9] = x; + $[10] = y; + $[11] = t3; + } else { + t3 = $[11]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ arr1: [], arr2: [] }], + sequentialRenders: [ + { arr1: [], arr2: [] }, + { arr1: [], arr2: null }, + { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] }, + ], +}; + +``` + +### Eval output +(kind: ok) [[],[]] +[[],[]] +[[2,3],[0,1]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.js new file mode 100644 index 0000000000000..80748d6131bf0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-simple.js @@ -0,0 +1,25 @@ +/** + * Test that we're not hoisting property reads from lambdas that are created to + * pass to opaque functions, which often have maybe-invoke semantics. + * + * In this example, we shouldn't hoist `arr[0].value` out of the lambda. + * ```js + * e => arr[0].value + e.value <-- created to pass to map + * arr.map(<cb>) <-- argument only invoked if array is non-empty + * ``` + */ +function useFoo({arr1, arr2}) { + const x = arr1.map(e => arr1[0].value + e.value); + const y = arr1.map(e => arr2[0].value + e.value); + return [x, y]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md new file mode 100644 index 0000000000000..e0dc1eeb5f7c4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md @@ -0,0 +1,112 @@ + +## Input + +```javascript +import {useRef} from 'react'; +import {Stringify} from 'shared-runtime'; + +function Component({a, b}) { + const logA = () => { + console.log(a.value); + }; + const logB = () => { + console.log(b.value); + }; + const hasLogged = useRef(false); + const log = () => { + if (!hasLogged.current) { + logA(); + logB(); + hasLogged.current = true; + } + }; + return <Stringify log={log} shouldInvokeFns={true} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{a: {value: 1}, b: {value: 2}}], + sequentialRenders: [ + {a: {value: 1}, b: {value: 2}}, + {a: {value: 3}, b: {value: 4}}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useRef } from "react"; +import { Stringify } from "shared-runtime"; + +function Component(t0) { + const $ = _c(9); + const { a, b } = t0; + let t1; + if ($[0] !== a.value) { + t1 = () => { + console.log(a.value); + }; + $[0] = a.value; + $[1] = t1; + } else { + t1 = $[1]; + } + const logA = t1; + let t2; + if ($[2] !== b.value) { + t2 = () => { + console.log(b.value); + }; + $[2] = b.value; + $[3] = t2; + } else { + t2 = $[3]; + } + const logB = t2; + + const hasLogged = useRef(false); + let t3; + if ($[4] !== logA || $[5] !== logB) { + t3 = () => { + if (!hasLogged.current) { + logA(); + logB(); + hasLogged.current = true; + } + }; + $[4] = logA; + $[5] = logB; + $[6] = t3; + } else { + t3 = $[6]; + } + const log = t3; + let t4; + if ($[7] !== log) { + t4 = <Stringify log={log} shouldInvokeFns={true} />; + $[7] = log; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: { value: 1 }, b: { value: 2 } }], + sequentialRenders: [ + { a: { value: 1 }, b: { value: 2 } }, + { a: { value: 3 }, b: { value: 4 } }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"log":{"kind":"Function"},"shouldInvokeFns":true}</div> +<div>{"log":{"kind":"Function"},"shouldInvokeFns":true}</div> +logs: [1,2] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx new file mode 100644 index 0000000000000..746287fe6063c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx @@ -0,0 +1,29 @@ +import {useRef} from 'react'; +import {Stringify} from 'shared-runtime'; + +function Component({a, b}) { + const logA = () => { + console.log(a.value); + }; + const logB = () => { + console.log(b.value); + }; + const hasLogged = useRef(false); + const log = () => { + if (!hasLogged.current) { + logA(); + logB(); + hasLogged.current = true; + } + }; + return <Stringify log={log} shouldInvokeFns={true} />; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{a: {value: 1}, b: {value: 2}}], + sequentialRenders: [ + {a: {value: 1}, b: {value: 2}}, + {a: {value: 3}, b: {value: 4}}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md new file mode 100644 index 0000000000000..0080fd0468933 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md @@ -0,0 +1,85 @@ + +## Input + +```javascript +import {useState} from 'react'; +import {useIdentity} from 'shared-runtime'; + +/** + * Assume that conditionally called functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback({obj}: {obj: {value: number}}) { + const [state, setState] = useState(0); + const cb = () => { + if (obj.value !== 0) setState(obj.value); + }; + useIdentity(null); + if (state === 0) { + cb(); + } + return {cb}; +} +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}}], + sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useState } from "react"; +import { useIdentity } from "shared-runtime"; + +/** + * Assume that conditionally called functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback(t0) { + const $ = _c(4); + const { obj } = t0; + const [state, setState] = useState(0); + let t1; + if ($[0] !== obj.value) { + t1 = () => { + if (obj.value !== 0) { + setState(obj.value); + } + }; + $[0] = obj.value; + $[1] = t1; + } else { + t1 = $[1]; + } + const cb = t1; + + useIdentity(null); + if (state === 0) { + cb(); + } + let t2; + if ($[2] !== cb) { + t2 = { cb }; + $[2] = cb; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{ obj: { value: 1 } }], + sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }], +}; + +``` + +### Eval output +(kind: ok) {"cb":"[[ function params=0 ]]"} +{"cb":"[[ function params=0 ]]"} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts new file mode 100644 index 0000000000000..12d92b726f387 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts @@ -0,0 +1,23 @@ +import {useState} from 'react'; +import {useIdentity} from 'shared-runtime'; + +/** + * Assume that conditionally called functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback({obj}: {obj: {value: number}}) { + const [state, setState] = useState(0); + const cb = () => { + if (obj.value !== 0) setState(obj.value); + }; + useIdentity(null); + if (state === 0) { + cb(); + } + return {cb}; +} +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}}], + sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md new file mode 100644 index 0000000000000..77b62bc8c24b6 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md @@ -0,0 +1,87 @@ + +## Input + +```javascript +import {createHookWrapper} from 'shared-runtime'; + +/** + * Assume that conditionally returned functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback({ + obj, + shouldMakeCb, + setState, +}: { + obj: {value: number}; + shouldMakeCb: boolean; + setState: (newState: number) => void; +}) { + const cb = () => setState(obj.value); + if (shouldMakeCb) return cb; + else return null; +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, shouldMakeCb: true, setState}], + sequentialRenders: [ + {obj: {value: 1}, shouldMakeCb: true, setState}, + {obj: {value: 2}, shouldMakeCb: true, setState}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { createHookWrapper } from "shared-runtime"; + +/** + * Assume that conditionally returned functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback(t0) { + const $ = _c(3); + const { obj, shouldMakeCb, setState } = t0; + let t1; + if ($[0] !== obj.value || $[1] !== setState) { + t1 = () => setState(obj.value); + $[0] = obj.value; + $[1] = setState; + $[2] = t1; + } else { + t1 = $[2]; + } + const cb = t1; + if (shouldMakeCb) { + return cb; + } else { + return null; + } +} + +const setState = (arg: number) => { + "use no memo"; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{ obj: { value: 1 }, shouldMakeCb: true, setState }], + sequentialRenders: [ + { obj: { value: 1 }, shouldMakeCb: true, setState }, + { obj: { value: 2 }, shouldMakeCb: true, setState }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> +<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts new file mode 100644 index 0000000000000..08dde03b03793 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts @@ -0,0 +1,32 @@ +import {createHookWrapper} from 'shared-runtime'; + +/** + * Assume that conditionally returned functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback({ + obj, + shouldMakeCb, + setState, +}: { + obj: {value: number}; + shouldMakeCb: boolean; + setState: (newState: number) => void; +}) { + const cb = () => setState(obj.value); + if (shouldMakeCb) return cb; + else return null; +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, shouldMakeCb: true, setState}], + sequentialRenders: [ + {obj: {value: 1}, shouldMakeCb: true, setState}, + {obj: {value: 2}, shouldMakeCb: true, setState}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md new file mode 100644 index 0000000000000..2f31be1ffe151 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md @@ -0,0 +1,74 @@ + +## Input + +```javascript +import {useState} from 'react'; +import {useIdentity} from 'shared-runtime'; + +function useMakeCallback({obj}: {obj: {value: number}}) { + const [state, setState] = useState(0); + const cb = () => { + if (obj.value !== state) setState(obj.value); + }; + useIdentity(); + cb(); + return [cb]; +} +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}}], + sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useState } from "react"; +import { useIdentity } from "shared-runtime"; + +function useMakeCallback(t0) { + const $ = _c(5); + const { obj } = t0; + const [state, setState] = useState(0); + let t1; + if ($[0] !== obj.value || $[1] !== state) { + t1 = () => { + if (obj.value !== state) { + setState(obj.value); + } + }; + $[0] = obj.value; + $[1] = state; + $[2] = t1; + } else { + t1 = $[2]; + } + const cb = t1; + + useIdentity(); + cb(); + let t2; + if ($[3] !== cb) { + t2 = [cb]; + $[3] = cb; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{ obj: { value: 1 } }], + sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }], +}; + +``` + +### Eval output +(kind: ok) ["[[ function params=0 ]]"] +["[[ function params=0 ]]"] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts new file mode 100644 index 0000000000000..c2e82922978a0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts @@ -0,0 +1,17 @@ +import {useState} from 'react'; +import {useIdentity} from 'shared-runtime'; + +function useMakeCallback({obj}: {obj: {value: number}}) { + const [state, setState] = useState(0); + const cb = () => { + if (obj.value !== state) setState(obj.value); + }; + useIdentity(); + cb(); + return [cb]; +} +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}}], + sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md new file mode 100644 index 0000000000000..8301912b024c2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md @@ -0,0 +1,130 @@ + +## Input + +```javascript +import {createHookWrapper} from 'shared-runtime'; + +/** + * (Given that the returned lambda is assumed to be invoked, see + * return-function) + * + * If lambda A conditionally calls lambda B, optimistically assume that property + * loads from lambda B has the same hoistability of ones from lambda A. This + * helps optimize components / hooks that create and chain many helper + * functions. + * + * Type systems and code readability encourage developers to colocate length and + * null checks values in the same function as where values are used. i.e. + * developers are unlikely to write the following code. + * ```js + * function useFoo(obj, objNotNullAndHasElements) { + * // ... + * const get0th = () => obj.arr[0].value; + * return () => objNotNullAndHasElements ? get0th : undefined; + * } + * ``` + * + * In Meta code, this assumption helps reduce the number of memo dependency + * deopts. + */ +function useMakeCallback({ + obj, + cond, + setState, +}: { + obj: {value: number}; + cond: boolean; + setState: (newState: number) => void; +}) { + const cb = () => setState(obj.value); + // cb's property loads are assumed to be hoistable to the start of this lambda + return () => (cond ? cb() : undefined); +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, cond: true, setState}], + sequentialRenders: [ + {obj: {value: 1}, cond: true, setState}, + {obj: {value: 2}, cond: true, setState}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { createHookWrapper } from "shared-runtime"; + +/** + * (Given that the returned lambda is assumed to be invoked, see + * return-function) + * + * If lambda A conditionally calls lambda B, optimistically assume that property + * loads from lambda B has the same hoistability of ones from lambda A. This + * helps optimize components / hooks that create and chain many helper + * functions. + * + * Type systems and code readability encourage developers to colocate length and + * null checks values in the same function as where values are used. i.e. + * developers are unlikely to write the following code. + * ```js + * function useFoo(obj, objNotNullAndHasElements) { + * // ... + * const get0th = () => obj.arr[0].value; + * return () => objNotNullAndHasElements ? get0th : undefined; + * } + * ``` + * + * In Meta code, this assumption helps reduce the number of memo dependency + * deopts. + */ +function useMakeCallback(t0) { + const $ = _c(6); + const { obj, cond, setState } = t0; + let t1; + if ($[0] !== obj.value || $[1] !== setState) { + t1 = () => setState(obj.value); + $[0] = obj.value; + $[1] = setState; + $[2] = t1; + } else { + t1 = $[2]; + } + const cb = t1; + let t2; + if ($[3] !== cb || $[4] !== cond) { + t2 = () => (cond ? cb() : undefined); + $[3] = cb; + $[4] = cond; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} + +const setState = (arg: number) => { + "use no memo"; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{ obj: { value: 1 }, cond: true, setState }], + sequentialRenders: [ + { obj: { value: 1 }, cond: true, setState }, + { obj: { value: 2 }, cond: true, setState }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> +<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts new file mode 100644 index 0000000000000..b6283aa6a6b57 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts @@ -0,0 +1,51 @@ +import {createHookWrapper} from 'shared-runtime'; + +/** + * (Given that the returned lambda is assumed to be invoked, see + * return-function) + * + * If lambda A conditionally calls lambda B, optimistically assume that property + * loads from lambda B has the same hoistability of ones from lambda A. This + * helps optimize components / hooks that create and chain many helper + * functions. + * + * Type systems and code readability encourage developers to colocate length and + * null checks values in the same function as where values are used. i.e. + * developers are unlikely to write the following code. + * ```js + * function useFoo(obj, objNotNullAndHasElements) { + * // ... + * const get0th = () => obj.arr[0].value; + * return () => objNotNullAndHasElements ? get0th : undefined; + * } + * ``` + * + * In Meta code, this assumption helps reduce the number of memo dependency + * deopts. + */ +function useMakeCallback({ + obj, + cond, + setState, +}: { + obj: {value: number}; + cond: boolean; + setState: (newState: number) => void; +}) { + const cb = () => setState(obj.value); + // cb's property loads are assumed to be hoistable to the start of this lambda + return () => (cond ? cb() : undefined); +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, cond: true, setState}], + sequentialRenders: [ + {obj: {value: 1}, cond: true, setState}, + {obj: {value: 2}, cond: true, setState}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md new file mode 100644 index 0000000000000..ab8326a2286d5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md @@ -0,0 +1,80 @@ + +## Input + +```javascript +import {createHookWrapper, useIdentity} from 'shared-runtime'; + +/** + * Assume that functions passed hook arguments are invoked and that their + * property loads are hoistable. + */ +function useMakeCallback({ + obj, + setState, +}: { + obj: {value: number}; + setState: (newState: number) => void; +}) { + const cb = useIdentity(() => setState(obj.value)); + return cb; +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, setState}], + sequentialRenders: [ + {obj: {value: 1}, setState}, + {obj: {value: 2}, setState}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { createHookWrapper, useIdentity } from "shared-runtime"; + +/** + * Assume that functions passed hook arguments are invoked and that their + * property loads are hoistable. + */ +function useMakeCallback(t0) { + const $ = _c(3); + const { obj, setState } = t0; + let t1; + if ($[0] !== obj.value || $[1] !== setState) { + t1 = () => setState(obj.value); + $[0] = obj.value; + $[1] = setState; + $[2] = t1; + } else { + t1 = $[2]; + } + const cb = useIdentity(t1); + return cb; +} + +const setState = (arg: number) => { + "use no memo"; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{ obj: { value: 1 }, setState }], + sequentialRenders: [ + { obj: { value: 1 }, setState }, + { obj: { value: 2 }, setState }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> +<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts new file mode 100644 index 0000000000000..a1ab6e18c5a5d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts @@ -0,0 +1,29 @@ +import {createHookWrapper, useIdentity} from 'shared-runtime'; + +/** + * Assume that functions passed hook arguments are invoked and that their + * property loads are hoistable. + */ +function useMakeCallback({ + obj, + setState, +}: { + obj: {value: number}; + setState: (newState: number) => void; +}) { + const cb = useIdentity(() => setState(obj.value)); + return cb; +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, setState}], + sequentialRenders: [ + {obj: {value: 1}, setState}, + {obj: {value: 2}, setState}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md new file mode 100644 index 0000000000000..688901a4e2199 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md @@ -0,0 +1,80 @@ + +## Input + +```javascript +import {createHookWrapper} from 'shared-runtime'; + +function useFoo({arr1}) { + const cb1 = e => arr1[0].value + e.value; + const x = arr1.map(cb1); + return [x, cb1]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useFoo), + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { createHookWrapper } from "shared-runtime"; + +function useFoo(t0) { + const $ = _c(8); + const { arr1 } = t0; + let t1; + if ($[0] !== arr1[0]) { + t1 = (e) => arr1[0].value + e.value; + $[0] = arr1[0]; + $[1] = t1; + } else { + t1 = $[1]; + } + const cb1 = t1; + let t2; + if ($[2] !== arr1 || $[3] !== cb1) { + t2 = arr1.map(cb1); + $[2] = arr1; + $[3] = cb1; + $[4] = t2; + } else { + t2 = $[4]; + } + const x = t2; + let t3; + if ($[5] !== cb1 || $[6] !== x) { + t3 = [x, cb1]; + $[5] = cb1; + $[6] = x; + $[7] = t3; + } else { + t3 = $[7]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useFoo), + params: [{ arr1: [], arr2: [] }], + sequentialRenders: [ + { arr1: [], arr2: [] }, + { arr1: [], arr2: null }, + { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div> +<div>{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div> +<div>{"result":[[2,3],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts new file mode 100644 index 0000000000000..c08701022a3bc --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts @@ -0,0 +1,17 @@ +import {createHookWrapper} from 'shared-runtime'; + +function useFoo({arr1}) { + const cb1 = e => arr1[0].value + e.value; + const x = arr1.map(cb1); + return [x, cb1]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useFoo), + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md new file mode 100644 index 0000000000000..76228fc249119 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md @@ -0,0 +1,75 @@ + +## Input + +```javascript +// @flow +import {Stringify} from 'shared-runtime'; + +/** + * Assume that functions captured directly as jsx attributes are invoked and + * that their property loads are hoistable. + */ +function useMakeCallback({ + obj, + setState, +}: { + obj: {value: number}; + setState: (newState: number) => void; +}) { + return <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />; +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}, setState}], + sequentialRenders: [ + {obj: {value: 1}, setState}, + {obj: {value: 2}, setState}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { Stringify } from "shared-runtime"; + +function useMakeCallback(t0) { + const $ = _c(3); + const { obj, setState } = t0; + let t1; + if ($[0] !== obj.value || $[1] !== setState) { + t1 = <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />; + $[0] = obj.value; + $[1] = setState; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +const setState = (arg: number) => { + "use no memo"; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{ obj: { value: 1 }, setState }], + sequentialRenders: [ + { obj: { value: 1 }, setState }, + { obj: { value: 2 }, setState }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> +<div>{"cb":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx new file mode 100644 index 0000000000000..316a0a03fb683 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx @@ -0,0 +1,29 @@ +// @flow +import {Stringify} from 'shared-runtime'; + +/** + * Assume that functions captured directly as jsx attributes are invoked and + * that their property loads are hoistable. + */ +function useMakeCallback({ + obj, + setState, +}: { + obj: {value: number}; + setState: (newState: number) => void; +}) { + return <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />; +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}, setState}], + sequentialRenders: [ + {obj: {value: 1}, setState}, + {obj: {value: 2}, setState}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md new file mode 100644 index 0000000000000..31e317d07e992 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md @@ -0,0 +1,78 @@ + +## Input + +```javascript +import {createHookWrapper} from 'shared-runtime'; + +/** + * Assume that directly returned functions are invoked and that their property + * loads are hoistable. + */ +function useMakeCallback({ + obj, + setState, +}: { + obj: {value: number}; + setState: (newState: number) => void; +}) { + return () => setState(obj.value); +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, setState}], + sequentialRenders: [ + {obj: {value: 1}, setState}, + {obj: {value: 2}, setState}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { createHookWrapper } from "shared-runtime"; + +/** + * Assume that directly returned functions are invoked and that their property + * loads are hoistable. + */ +function useMakeCallback(t0) { + const $ = _c(3); + const { obj, setState } = t0; + let t1; + if ($[0] !== obj.value || $[1] !== setState) { + t1 = () => setState(obj.value); + $[0] = obj.value; + $[1] = setState; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +const setState = (arg: number) => { + "use no memo"; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{ obj: { value: 1 }, setState }], + sequentialRenders: [ + { obj: { value: 1 }, setState }, + { obj: { value: 2 }, setState }, + ], +}; + +``` + +### Eval output +(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div> +<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts new file mode 100644 index 0000000000000..f0e0ac77f01a2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts @@ -0,0 +1,28 @@ +import {createHookWrapper} from 'shared-runtime'; + +/** + * Assume that directly returned functions are invoked and that their property + * loads are hoistable. + */ +function useMakeCallback({ + obj, + setState, +}: { + obj: {value: number}; + setState: (newState: number) => void; +}) { + return () => setState(obj.value); +} + +const setState = (arg: number) => { + 'use no memo'; + return arg; +}; +export const FIXTURE_ENTRYPOINT = { + fn: createHookWrapper(useMakeCallback), + params: [{obj: {value: 1}, setState}], + sequentialRenders: [ + {obj: {value: 1}, setState}, + {obj: {value: 2}, setState}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md new file mode 100644 index 0000000000000..e750b8ab8415e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md @@ -0,0 +1,82 @@ + +## Input + +```javascript +import {useState, useMemo} from 'react'; +import {useIdentity} from 'shared-runtime'; + +/** + * Assume that conditionally called functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback({ + obj, + shouldSynchronizeState, +}: { + obj: {value: number}; + shouldSynchronizeState: boolean; +}) { + const [state, setState] = useState(0); + const cb = useMemo(() => { + return () => { + if (obj.value !== 0) setState(obj.value); + }; + }, [obj.value, shouldSynchronizeState]); + useIdentity(null); + return cb; +} +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}}], + sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useState, useMemo } from "react"; +import { useIdentity } from "shared-runtime"; + +/** + * Assume that conditionally called functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback(t0) { + const $ = _c(2); + const { obj, shouldSynchronizeState } = t0; + + const [, setState] = useState(0); + let t1; + let t2; + if ($[0] !== obj.value) { + t2 = () => { + if (obj.value !== 0) { + setState(obj.value); + } + }; + $[0] = obj.value; + $[1] = t2; + } else { + t2 = $[1]; + } + t1 = t2; + const cb = t1; + + useIdentity(null); + return cb; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{ obj: { value: 1 } }], + sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }], +}; + +``` + +### Eval output +(kind: ok) "[[ function params=0 ]]" +"[[ function params=0 ]]" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts new file mode 100644 index 0000000000000..6cb2e44a2b46a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts @@ -0,0 +1,28 @@ +import {useState, useMemo} from 'react'; +import {useIdentity} from 'shared-runtime'; + +/** + * Assume that conditionally called functions can be invoked and that their + * property loads are hoistable to the function declaration site. + */ +function useMakeCallback({ + obj, + shouldSynchronizeState, +}: { + obj: {value: number}; + shouldSynchronizeState: boolean; +}) { + const [state, setState] = useState(0); + const cb = useMemo(() => { + return () => { + if (obj.value !== 0) setState(obj.value); + }; + }, [obj.value, shouldSynchronizeState]); + useIdentity(null); + return cb; +} +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{obj: {value: 1}}], + sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md new file mode 100644 index 0000000000000..d00e71e14f6ca --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md @@ -0,0 +1,68 @@ + +## Input + +```javascript +function useFoo({arr1, arr2}) { + const cb = e => arr2[0].value + e.value; + const y = []; + for (let i = 0; i < arr1.length; i++) { + y.push(cb(arr1[i])); + } + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function useFoo(t0) { + const $ = _c(5); + const { arr1, arr2 } = t0; + let t1; + if ($[0] !== arr2[0].value) { + t1 = (e) => arr2[0].value + e.value; + $[0] = arr2[0].value; + $[1] = t1; + } else { + t1 = $[1]; + } + const cb = t1; + let y; + if ($[2] !== arr1 || $[3] !== cb) { + y = []; + for (let i = 0; i < arr1.length; i++) { + y.push(cb(arr1[i])); + } + $[2] = arr1; + $[3] = cb; + $[4] = y; + } else { + y = $[4]; + } + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ arr1: [], arr2: [] }], + sequentialRenders: [ + { arr1: [], arr2: [] }, + { arr1: [], arr2: null }, + { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] }, + ], +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js new file mode 100644 index 0000000000000..eed75613061ab --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js @@ -0,0 +1,18 @@ +function useFoo({arr1, arr2}) { + const cb = e => arr2[0].value + e.value; + const y = []; + for (let i = 0; i < arr1.length; i++) { + y.push(cb(arr1[i])); + } + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{arr1: [], arr2: []}], + sequentialRenders: [ + {arr1: [], arr2: []}, + {arr1: [], arr2: null}, + {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md new file mode 100644 index 0000000000000..5ccf5b5edc9d1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +/** + * Assume that only directly returned functions or JSX attributes are invoked. + * Conservatively estimate that functions wrapped in objects or other containers + * might never be called (and therefore their property loads are not hoistable). + */ +function useMakeCallback({arr}) { + return { + getElement0: () => arr[0].value, + getElement1: () => arr[1].value, + }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{arr: [1, 2]}], + sequentialRenders: [{arr: [1, 2]}, {arr: []}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; /** + * Assume that only directly returned functions or JSX attributes are invoked. + * Conservatively estimate that functions wrapped in objects or other containers + * might never be called (and therefore their property loads are not hoistable). + */ +function useMakeCallback(t0) { + const $ = _c(2); + const { arr } = t0; + let t1; + if ($[0] !== arr) { + t1 = { getElement0: () => arr[0].value, getElement1: () => arr[1].value }; + $[0] = arr; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{ arr: [1, 2] }], + sequentialRenders: [{ arr: [1, 2] }, { arr: [] }], +}; + +``` + +### Eval output +(kind: ok) {"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"} +{"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js new file mode 100644 index 0000000000000..6aface49f808a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js @@ -0,0 +1,17 @@ +/** + * Assume that only directly returned functions or JSX attributes are invoked. + * Conservatively estimate that functions wrapped in objects or other containers + * might never be called (and therefore their property loads are not hoistable). + */ +function useMakeCallback({arr}) { + return { + getElement0: () => arr[0].value, + getElement1: () => arr[1].value, + }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useMakeCallback, + params: [{arr: [1, 2]}], + sequentialRenders: [{arr: [1, 2]}, {arr: []}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md index ca65ce72bc172..53d3d04531bda 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md @@ -41,9 +41,9 @@ function useFoo(t0) { local = $[1]; } let t1; - if ($[2] !== local.b.c) { + if ($[2] !== local) { t1 = () => [() => local.b.c]; - $[2] = local.b.c; + $[2] = local; $[3] = t1; } else { t1 = $[3]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md index 7d75470550dfa..f8a8af1fd4f41 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md @@ -34,13 +34,13 @@ function useFoo(t0) { const $ = _c(4); const { a } = t0; let t1; - if ($[0] !== a.b.c) { + if ($[0] !== a) { t1 = { fn() { return identity(a.b.c); }, }; - $[0] = a.b.c; + $[0] = a; $[1] = t1; } else { t1 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md index ceaa350012258..963024e88715b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md @@ -51,7 +51,7 @@ import { identity } from "shared-runtime"; function Component(props) { const $ = _c(4); let x; - if ($[0] !== props.cond) { + if ($[0] !== props) { const f = () => { if (props.cond) { x = 1; @@ -62,7 +62,7 @@ function Component(props) { const f2 = identity(f); f2(); - $[0] = props.cond; + $[0] = props; $[1] = x; } else { x = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md index d72f34b4fd8a9..f88787019790e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md @@ -82,9 +82,9 @@ function Component(t0) { contextVar = $[2]; } let t1; - if ($[3] !== contextVar.val) { + if ($[3] !== contextVar) { t1 = { cb: () => contextVar.val * 4 }; - $[3] = contextVar.val; + $[3] = contextVar; $[4] = t1; } else { t1 = $[4]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md index d0f3d5dcfefd9..e406f3a7d7d6a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md @@ -43,7 +43,7 @@ const t0 = "module_t0"; const c_0 = "module_c_0"; function useFoo(props) { const $0 = _c(2); - const c_00 = $0[0] !== props.value; + const c_00 = $0[0] !== props; let t1; if (c_00) { const a = { @@ -61,7 +61,7 @@ function useFoo(props) { }; t1 = a.foo().bar(); - $0[0] = props.value; + $0[0] = props; $0[1] = t1; } else { t1 = $0[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md index c3e115fa0d1cf..0cce42e97a5b0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md @@ -35,7 +35,7 @@ function Component(props) { import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions:false function Component(props) { - const $ = _c(9); + const $ = _c(7); const item = useMutable(props.itemId); const dispatch = useDispatch(); useFreeze(dispatch); @@ -51,7 +51,8 @@ function Component(props) { } const exit = t0; let t1; - if ($[2] !== exit || $[3] !== item.value) { + let t2; + if ($[2] !== exit || $[3] !== item) { t1 = () => { const cleanup = GlobalEventEmitter.addListener("onInput", () => { if (item.value) { @@ -60,30 +61,24 @@ function Component(props) { }); return () => cleanup.remove(); }; + t2 = [exit, item]; $[2] = exit; - $[3] = item.value; + $[3] = item; $[4] = t1; + $[5] = t2; } else { t1 = $[4]; - } - let t2; - if ($[5] !== exit || $[6] !== item) { - t2 = [exit, item]; - $[5] = exit; - $[6] = item; - $[7] = t2; - } else { - t2 = $[7]; + t2 = $[5]; } useEffect(t1, t2); maybeMutate(item); let t3; - if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { t3 = <div />; - $[8] = t3; + $[6] = t3; } else { - t3 = $[8]; + t3 = $[6]; } return t3; } diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index 7c487869d333d..62b8a7703fddb 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -450,6 +450,7 @@ const skipFilter = new Set([ 'invalid-jsx-lowercase-localvar', // bugs + 'inner-function/nullable-objects/bug-invalid-array-map-manual', 'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr', `bug-capturing-func-maybealias-captured-mutate`, 'bug-aliased-capture-aliased-mutate', From c2a196174763e0b4f16ed1c512ed4442b062395e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Tue, 18 Mar 2025 19:20:34 -0400 Subject: [PATCH 162/300] Minor Fixes to View Transition Fixture (#32664) Follow up to #32656. Remove touchAction from SwipeRecognizer. I was under the wrong impression that this was only the touch-action applied to this particular element, but that parents would still win but in fact this blocks the parent from scrolling in the other direction. By specifying a fixed direction it also blocked rage-swiping in the other direction early on. Disable pointer-events on view-transition so that the scroll can be hit. This means that touches hit below the items animating above. This allows swiping to happen again before momentum scroll has finished. Previously they were ignored. This only works as long as the SwipeRecognizer is itself not animating. This means you can now rage-swipe in both directions quickly. --- fixtures/view-transition/src/components/Chrome.css | 7 +++++++ fixtures/view-transition/src/components/SwipeRecognizer.js | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fixtures/view-transition/src/components/Chrome.css b/fixtures/view-transition/src/components/Chrome.css index ade2f73952f18..969f1e2d3271a 100644 --- a/fixtures/view-transition/src/components/Chrome.css +++ b/fixtures/view-transition/src/components/Chrome.css @@ -7,3 +7,10 @@ body { padding: 0; font-family: sans-serif; } + +::view-transition-group(*), +::view-transition-image-pair(*), +::view-transition-old(*), +::view-transition-new(*) { + pointer-events: none; +} diff --git a/fixtures/view-transition/src/components/SwipeRecognizer.js b/fixtures/view-transition/src/components/SwipeRecognizer.js index 8e913dfb4e55f..0b16967f51b08 100644 --- a/fixtures/view-transition/src/components/SwipeRecognizer.js +++ b/fixtures/view-transition/src/components/SwipeRecognizer.js @@ -92,7 +92,6 @@ export default function SwipeRecognizer({ width: axis === 'x' ? '100%' : null, height: axis === 'y' ? '100%' : null, overflow: 'scroll hidden', - touchAction: 'pan-' + direction, // Disable overscroll on Safari which moves the sticky content. // Unfortunately, this also means that we disable chaining. We should only disable // it if the parent is not scrollable in this axis. From 646835fb59f9ad8557b9f3641515697c153e3faa Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:49:18 -0400 Subject: [PATCH 163/300] [ci] Properly format commit message (#32668) Using the github variable for the commit message replaces the variable inline. If the commit message contains quotes or other characters that need to be escaped, this breaks the workflow. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32668). * #32669 * __->__ #32668 --- .github/workflows/runtime_commit_artifacts.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 5de51b2664e48..15ac3ea98811c 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -252,9 +252,7 @@ jobs: git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}" git config --global user.name "${{ github.triggering_actor }}" - git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }} - - DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit" + git commit -m "$(git show --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit" - name: Push changes to branch if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true') run: git push @@ -419,9 +417,7 @@ jobs: git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}" git config --global user.name "${{ github.triggering_actor }}" - git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }} - - DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit" + git commit -m "$(git show --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit" - name: Push changes to branch if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true') run: git push From db7dfe05508392ba3bdf7bc24717fe71f9b84a29 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:49:42 -0400 Subject: [PATCH 164/300] [eprh] Don't transpile to es5 (#32669) Now that we've moved the sync location of the plugin, we no longer need this since those restrictions no longer apply. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32669). * __->__ #32669 * #32668 --- scripts/rollup/build.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 1c3010d902f81..5cf0518c41833 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -26,7 +26,6 @@ const {asyncRimRaf} = require('./utils'); const codeFrame = require('@babel/code-frame').default; const Wrappers = require('./wrappers'); const commonjs = require('@rollup/plugin-commonjs'); -const {getBabelOutputPlugin} = require('@rollup/plugin-babel'); const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL; @@ -418,12 +417,6 @@ function getPlugins( bundle ) ), - // For Meta internal requirements this package needs to be built targeting ES5. - bundle.name === 'eslint-plugin-react-hooks' - ? getBabelOutputPlugin({ - presets: ['@babel/preset-env'], - }) - : false, // Remove 'use strict' from individual source files. We skip eslint-plugin-react-hooks because // it bundles compiler-type code that may examine "use strict" used outside of a directive // context, e.g. as a StringLiteral. @@ -503,7 +496,7 @@ function getPlugins( // takes care of it. renaming: false, }), - (needsMinifiedByClosure || bundle.name === 'eslint-plugin-react-hooks') && + needsMinifiedByClosure && // Add the whitespace back prettier({ parser: 'flow', From b0446ff06a484127412638c2be9a0382c6f3a84b Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:01:49 -0400 Subject: [PATCH 165/300] [ci] Properly format commit message take 2 (#32673) We need to use the commit message from `main`, not the builds branch --- .github/workflows/runtime_commit_artifacts.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 15ac3ea98811c..7c93ed20be370 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -246,13 +246,19 @@ jobs: run: | echo ":" git status -u + - name: Check commit message + if: inputs.dry_run + run: | + git fetch origin --quiet + git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:"%B" - name: Commit changes to branch if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true' run: | git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}" git config --global user.name "${{ github.triggering_actor }}" - git commit -m "$(git show --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit" + git fetch origin --quiet + git commit -m "$(git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit" - name: Push changes to branch if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true') run: git push @@ -411,13 +417,19 @@ jobs: run: | git add . git status + - name: Check commit message + if: inputs.dry_run + run: | + git fetch origin --quiet + git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:"%B" - name: Commit changes to branch if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true' run: | git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}" git config --global user.name "${{ github.triggering_actor }}" - git commit -m "$(git show --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit" + git fetch origin --quiet + git commit -m "$(git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit" - name: Push changes to branch if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true') run: git push From e9c3b27b4b86c5030df96d8a1e4228b5341164b0 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:42:45 -0400 Subject: [PATCH 166/300] [ci] Bump all node_modules cache keys (#32671) I'm seeing a lot of instances of > Failed to save: Unable to reserve cache with key runtime-and-compiler-node_modules-v5-X64-Linux-e454609794aae66da9909c77dd6efa073eceff7f44d6527611f8465e102578b4, another job may be creating this cache. which is adding ~20 seconds to every step. Let's try to bust the cache following this [comment](https://github.com/actions/cache/issues/485#issuecomment-744145040) and see if that helps. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32671). * #32672 * __->__ #32671 --- .github/workflows/compiler_playground.yml | 2 +- .github/workflows/compiler_prereleases.yml | 2 +- .github/workflows/compiler_typescript.yml | 6 ++-- .../workflows/devtools_regression_tests.yml | 8 +++--- .github/workflows/runtime_build_and_test.yml | 28 +++++++++---------- .../workflows/runtime_commit_artifacts.yml | 2 +- .../workflows/runtime_eslint_plugin_e2e.yml | 2 +- .github/workflows/runtime_prereleases.yml | 2 +- .../runtime_releases_from_npm_manual.yml | 2 +- .github/workflows/shared_lint.yml | 8 +++--- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index e53158bf60e79..0a2551ce3aa3d 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -38,7 +38,7 @@ jobs: with: path: | **/node_modules - key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: npx playwright install --with-deps chromium - run: CI=true yarn test diff --git a/.github/workflows/compiler_prereleases.yml b/.github/workflows/compiler_prereleases.yml index 8baede7ac2a89..5a3122d0ed80f 100644 --- a/.github/workflows/compiler_prereleases.yml +++ b/.github/workflows/compiler_prereleases.yml @@ -48,7 +48,7 @@ jobs: with: path: | **/node_modules - key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - name: Publish packages to npm run: | diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index dbde1b9962206..1ce668de044e8 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -48,7 +48,7 @@ jobs: with: path: | **/node_modules - key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler lint @@ -69,7 +69,7 @@ jobs: with: path: | **/node_modules - key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: yarn workspace babel-plugin-react-compiler jest @@ -94,7 +94,7 @@ jobs: with: path: | **/node_modules - key: compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} + key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile - run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive' diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 844b82b82cdb9..f8e5fefca9266 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -31,7 +31,7 @@ jobs: with: path: | **/node_modules - key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -65,7 +65,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -123,7 +123,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 @@ -158,7 +158,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - name: Restore all archived build artifacts uses: actions/download-artifact@v4 diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index d685c848e21f4..4304795a6cd54 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -57,7 +57,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -82,7 +82,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -109,7 +109,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -165,7 +165,7 @@ jobs: with: path: | **/node_modules - key: runtime-and-compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -203,7 +203,7 @@ jobs: with: path: | **/node_modules - key: runtime-and-compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -281,7 +281,7 @@ jobs: with: path: | **/node_modules - key: runtime-and-compiler-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -315,7 +315,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -363,7 +363,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -399,7 +399,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -432,7 +432,7 @@ jobs: with: path: | **/node_modules - key: fixtures_dom-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_dom-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn --cwd fixtures/dom install --frozen-lockfile @@ -475,7 +475,7 @@ jobs: with: path: | **/node_modules - key: fixtures_flight-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_flight-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -538,7 +538,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -590,7 +590,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -628,7 +628,7 @@ jobs: with: path: | **/node_modules - key: runtime-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn --cwd scripts/release install --frozen-lockfile diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 7c93ed20be370..03795b7d2d4f0 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -80,7 +80,7 @@ jobs: with: path: | **/node_modules - key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index 2deb958ea79cc..c75f998e8cbb6 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -44,7 +44,7 @@ jobs: with: path: | **/node_modules - key: runtime-and-compiler-eslint_e2e-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + key: runtime-and-compiler-eslint_e2e-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index ce44246df9a75..4e1f8c21cab72 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -41,7 +41,7 @@ jobs: with: path: | **/node_modules - key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index d04f202a7ea71..58972d8a88321 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -73,7 +73,7 @@ jobs: with: path: | **/node_modules - key: runtime-release-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index 08ade6cb7faaa..f9d1e7972c7be 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -30,7 +30,7 @@ jobs: with: path: | **/node_modules - key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -51,7 +51,7 @@ jobs: with: path: | **/node_modules - key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -72,7 +72,7 @@ jobs: with: path: | **/node_modules - key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -93,7 +93,7 @@ jobs: with: path: | **/node_modules - key: shared-lint-node_modules-v5-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile From ada8bbbd6aa76bc472863589128833e30cacaef3 Mon Sep 17 00:00:00 2001 From: Niklas Holm <572511+niklasholm@users.noreply.github.com> Date: Wed, 19 Mar 2025 19:58:08 +0100 Subject: [PATCH 167/300] [eslint-plugin-react-compiler] Fix type error with recommended config (#32666) ## Summary In the recommended configuration for `eslint-plugin-react-compiler`, i.e. `reactCompiler.configs.recommended`, the rule is typed as `string` rather than `eslint.Linter.RuleEntry` or anything assignable thereto, which results in the following type error if you type check your eslint configuration: ``` Property ''react-compiler/react-compiler'' is incompatible with index signature. Type 'string' is not assignable to type 'RuleEntry | undefined'. ``` Simply adding a const assertion fixes the error. ## How did you test this change? I emitted declarations for the module and confirmed that the rule is now typed as the string literal `'error'` --- compiler/packages/eslint-plugin-react-compiler/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/packages/eslint-plugin-react-compiler/src/index.ts b/compiler/packages/eslint-plugin-react-compiler/src/index.ts index 0dd3679d560e0..a3577a101ef43 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/index.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/index.ts @@ -25,7 +25,7 @@ const configs = { }, }, rules: { - 'react-compiler/react-compiler': 'error', + 'react-compiler/react-compiler': 'error' as const, }, }, }; From a4842c92ea241593557a3b0dadfbc5460320d375 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:25:07 -0400 Subject: [PATCH 168/300] [ci] Centralize cache (#32672) To avoid race conditions where multiple jobs try to write to the same cache, we now centralize saving the cache and then reusing it in every subsequent job. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32672). * #32675 * #32674 * __->__ #32672 --- .github/workflows/runtime_build_and_test.yml | 139 ++++++++++++++++--- 1 file changed, 116 insertions(+), 23 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 4304795a6cd54..9bc883a396fd8 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -17,6 +17,75 @@ env: SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 jobs: + # ----- YARN CACHE ----- + # Centralize the yarn/node_modules cache so it is saved once and each subsequent job only needs to + # restore the cache. Prevents race conditions where multiple workflows try to write to the cache. + runtime_yarn_cache: + name: Cache Runtime + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Check cache hit + uses: actions/cache/restore@v4 + id: node_modules + with: + path: | + **/node_modules + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + lookup-only: true + - uses: actions/setup-node@v4 + if: steps.node_modules.outputs.cache-hit != 'true' + with: + node-version-file: '.nvmrc' + cache: yarn + cache-dependency-path: yarn.lock + - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' + - name: Save cache + if: steps.node_modules.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: | + **/node_modules + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + + runtime_compiler_yarn_cache: + name: Cache Runtime, Compiler + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Check cache hit + uses: actions/cache/restore@v4 + id: node_modules + with: + path: | + **/node_modules + key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + lookup-only: true + - uses: actions/setup-node@v4 + if: steps.node_modules.outputs.cache-hit != 'true' + with: + node-version-file: '.nvmrc' + cache: yarn + cache-dependency-path: | + yarn.lock + compiler/yarn.lock + - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' + - run: yarn --cwd compiler install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' + - name: Save cache + if: steps.node_modules.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: | + **/node_modules + key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + # ----- FLOW ----- discover_flow_inline_configs: name: Discover flow inline configs @@ -36,7 +105,7 @@ jobs: flow: name: Flow check ${{ matrix.flow_inline_config_shortname }} - needs: discover_flow_inline_configs + needs: [discover_flow_inline_configs, runtime_yarn_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -52,7 +121,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -61,11 +130,13 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: node ./scripts/tasks/flow-ci ${{ matrix.flow_inline_config_shortname }} # ----- FIZZ ----- check_generated_fizz_runtime: name: Confirm generated inline Fizz runtime is up to date + needs: [runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -77,7 +148,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -86,6 +157,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: | yarn generate-inline-fizz-runtime git diff --quiet || (echo "There was a change to the Fizz runtime. Run `yarn generate-inline-fizz-runtime` and check in the result." && false) @@ -93,6 +165,7 @@ jobs: # ----- FEATURE FLAGS ----- flags: name: Check flags + needs: [runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -104,7 +177,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -113,11 +186,13 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn flags # ----- TESTS ----- test: name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) + needs: [runtime_compiler_yarn_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -160,7 +235,7 @@ jobs: yarn.lock compiler/yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -169,12 +244,15 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd compiler install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }} # ----- BUILD ----- build_and_lint: name: yarn build and lint + needs: [runtime_compiler_yarn_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -198,7 +276,7 @@ jobs: distribution: temurin java-version: 11.0.22 - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -207,7 +285,9 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd compiler install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci env: CI: github @@ -225,7 +305,7 @@ jobs: test_build: name: yarn test-build - needs: build_and_lint + needs: [build_and_lint, runtime_compiler_yarn_cache] strategy: fail-fast: false matrix: @@ -276,7 +356,7 @@ jobs: yarn.lock compiler/yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -285,7 +365,9 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd compiler install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -298,7 +380,7 @@ jobs: process_artifacts_combined: name: Process artifacts combined - needs: build_and_lint + needs: [build_and_lint, runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -310,7 +392,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -319,6 +401,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -346,7 +429,7 @@ jobs: check_error_codes: name: Search build artifacts for unminified errors - needs: build_and_lint + needs: [build_and_lint, runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -358,7 +441,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -367,6 +450,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -382,7 +466,7 @@ jobs: check_release_dependencies: name: Check release dependencies - needs: build_and_lint + needs: [build_and_lint, runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -394,7 +478,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -403,6 +487,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -427,7 +512,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key id: node_modules with: path: | @@ -436,6 +521,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn --cwd fixtures/dom install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -470,7 +556,7 @@ jobs: # That means dependencies of the built packages are not installed. # We need to install dependencies of the workroot to fulfill all dependency constraints - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key id: node_modules with: path: | @@ -479,6 +565,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -517,7 +604,7 @@ jobs: # ----- DEVTOOLS ----- build_devtools_and_process_artifacts: name: Build DevTools and process artifacts - needs: build_and_lint + needs: [build_and_lint, runtime_yarn_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -533,7 +620,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -542,6 +629,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -573,7 +661,7 @@ jobs: run_devtools_e2e_tests: name: Run DevTools e2e tests - needs: build_and_lint + needs: [build_and_lint, runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -585,7 +673,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | @@ -594,6 +682,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -611,7 +700,7 @@ jobs: sizebot: if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }} name: Run sizebot - needs: [build_and_lint] + needs: [build_and_lint, runtime_yarn_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -623,15 +712,18 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache@v4 + uses: actions/cache/restore@v4 id: node_modules with: path: | **/node_modules - key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build + - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd scripts/release install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Download artifacts for base revision run: | GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }}) @@ -647,6 +739,7 @@ jobs: - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build for PR uses: actions/download-artifact@v4 with: From 891a6332e3dc9e6dae087170996afaea42898484 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:41:48 -0400 Subject: [PATCH 169/300] [ci] Bump build_and_lint to 25 workers (#32674) Increases number of workers for `build_and_lint`. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32674). * #32675 * __->__ #32674 --- .github/workflows/runtime_build_and_test.yml | 40 ++++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 9bc883a396fd8..b161a023fd638 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -17,11 +17,11 @@ env: SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 jobs: - # ----- YARN CACHE ----- - # Centralize the yarn/node_modules cache so it is saved once and each subsequent job only needs to + # ----- NODE_MODULES CACHE ----- + # Centralize the node_modules cache so it is saved once and each subsequent job only needs to # restore the cache. Prevents race conditions where multiple workflows try to write to the cache. - runtime_yarn_cache: - name: Cache Runtime + runtime_node_modules_cache: + name: Cache Runtime node_modules runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -51,8 +51,8 @@ jobs: **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - runtime_compiler_yarn_cache: - name: Cache Runtime, Compiler + runtime_compiler_node_modules_cache: + name: Cache Runtime, Compiler node_modules runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -105,7 +105,7 @@ jobs: flow: name: Flow check ${{ matrix.flow_inline_config_shortname }} - needs: [discover_flow_inline_configs, runtime_yarn_cache] + needs: [discover_flow_inline_configs, runtime_node_modules_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -136,7 +136,7 @@ jobs: # ----- FIZZ ----- check_generated_fizz_runtime: name: Confirm generated inline Fizz runtime is up to date - needs: [runtime_yarn_cache] + needs: [runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -165,7 +165,7 @@ jobs: # ----- FEATURE FLAGS ----- flags: name: Check flags - needs: [runtime_yarn_cache] + needs: [runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -192,7 +192,7 @@ jobs: # ----- TESTS ----- test: name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) - needs: [runtime_compiler_yarn_cache] + needs: [runtime_compiler_node_modules_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -252,13 +252,13 @@ jobs: # ----- BUILD ----- build_and_lint: name: yarn build and lint - needs: [runtime_compiler_yarn_cache] + needs: [runtime_compiler_node_modules_cache] runs-on: ubuntu-latest strategy: fail-fast: false matrix: # yml is dumb. update the --total arg to yarn build if you change the number of workers - worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] + worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24] release_channel: [stable, experimental] steps: - uses: actions/checkout@v4 @@ -288,7 +288,7 @@ jobs: if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd compiler install --frozen-lockfile if: steps.node_modules.outputs.cache-hit != 'true' - - run: yarn build --index=${{ matrix.worker_id }} --total=20 --r=${{ matrix.release_channel }} --ci + - run: yarn build --index=${{ matrix.worker_id }} --total=25 --r=${{ matrix.release_channel }} --ci env: CI: github RELEASE_CHANNEL: ${{ matrix.release_channel }} @@ -305,7 +305,7 @@ jobs: test_build: name: yarn test-build - needs: [build_and_lint, runtime_compiler_yarn_cache] + needs: [build_and_lint, runtime_compiler_node_modules_cache] strategy: fail-fast: false matrix: @@ -380,7 +380,7 @@ jobs: process_artifacts_combined: name: Process artifacts combined - needs: [build_and_lint, runtime_yarn_cache] + needs: [build_and_lint, runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -429,7 +429,7 @@ jobs: check_error_codes: name: Search build artifacts for unminified errors - needs: [build_and_lint, runtime_yarn_cache] + needs: [build_and_lint, runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -466,7 +466,7 @@ jobs: check_release_dependencies: name: Check release dependencies - needs: [build_and_lint, runtime_yarn_cache] + needs: [build_and_lint, runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -604,7 +604,7 @@ jobs: # ----- DEVTOOLS ----- build_devtools_and_process_artifacts: name: Build DevTools and process artifacts - needs: [build_and_lint, runtime_yarn_cache] + needs: [build_and_lint, runtime_node_modules_cache] runs-on: ubuntu-latest strategy: fail-fast: false @@ -661,7 +661,7 @@ jobs: run_devtools_e2e_tests: name: Run DevTools e2e tests - needs: [build_and_lint, runtime_yarn_cache] + needs: [build_and_lint, runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -700,7 +700,7 @@ jobs: sizebot: if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }} name: Run sizebot - needs: [build_and_lint, runtime_yarn_cache] + needs: [build_and_lint, runtime_node_modules_cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 208905257f5da4b05f3153388563cabf14eb85bb Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:42:04 -0400 Subject: [PATCH 170/300] [ci] Add cache cleanup workflow (#32675) > Caches have branch scope restriction in place. This means that if caches for a specific branch are using a lot of storage quota, it may result into more frequently used caches from default branch getting thrashed. For example, if there are many pull requests happening on a repo and are creating caches, these cannot be used in default branch scope but will still occupy a lot of space till they get cleaned up by eviction policy. But sometime we want to clean them up on a faster cadence so as to ensure default branch is not thrashing. https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32675). * __->__ #32675 * #32674 --- .../shared_cleanup_branch_caches.yml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/shared_cleanup_branch_caches.yml diff --git a/.github/workflows/shared_cleanup_branch_caches.yml b/.github/workflows/shared_cleanup_branch_caches.yml new file mode 100644 index 0000000000000..4df5d705da68d --- /dev/null +++ b/.github/workflows/shared_cleanup_branch_caches.yml @@ -0,0 +1,35 @@ +# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy + +name: (Shared) Cleanup Branch Caches +on: + pull_request: + types: + - closed + workflow_dispatch: + +jobs: + cleanup: + runs-on: ubuntu-latest + permissions: + # `actions:write` permission is required to delete caches + # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id + actions: write + contents: read + steps: + - name: Cleanup + run: | + echo "Fetching list of cache key" + cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id') + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh cache delete $cacheKey + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge From 995410463a0d74e43d91816bcda5f10cff82a14a Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 16:39:01 -0400 Subject: [PATCH 171/300] [ci] Parameterize branch cleanup (#32677) Allow a PR number to be passed as input --- .github/workflows/shared_cleanup_branch_caches.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/shared_cleanup_branch_caches.yml b/.github/workflows/shared_cleanup_branch_caches.yml index 4df5d705da68d..7c6a1d5e3a48f 100644 --- a/.github/workflows/shared_cleanup_branch_caches.yml +++ b/.github/workflows/shared_cleanup_branch_caches.yml @@ -6,6 +6,10 @@ on: types: - closed workflow_dispatch: + inputs: + pr_number: + required: true + type: string jobs: cleanup: @@ -23,13 +27,13 @@ jobs: ## Setting this to not fail the workflow while deleting cache keys. set +e - echo "Deleting caches..." for cacheKey in $cacheKeysForPR do gh cache delete $cacheKey + echo "Deleting $cacheKey" done echo "Done" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge + BRANCH: refs/pull/${{ inputs.pr_number || github.event.pull_request.number }}/merge From a8c155cab91d4a33d06a904bfc23aadacfba8383 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:22:17 -0400 Subject: [PATCH 172/300] [ci] Cache playwright browsers (#32678) No reason to download them from scratch every time. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32678). * #32680 * #32679 * __->__ #32678 --- .github/workflows/runtime_build_and_test.yml | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index b161a023fd638..1cb487559789b 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -566,6 +566,24 @@ jobs: run: rm -rf build - run: yarn install --frozen-lockfile if: steps.node_modules.outputs.cache-hit != 'true' + - run: yarn --cwd fixtures/flight install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' + - name: Store Playwright version + id: playwright_version + run: | + PLAYWRIGHT_VERSION=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//') + echo "Playwright's Version: $PLAYWRIGHT_VERSION" + echo "playwright_version=$PLAYWRIGHT_VERSION" >> "$GITHUB_OUTPUT" + - name: Cache Playwright Browsers for Playwright's Version + id: cache_playwright_browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }} + - name: Playwright install deps + if: steps.cache_playwright_browsers.outputs.cache-hit != 'true' + working-directory: fixtures/flight + run: npx playwright install --with-deps chromium - name: Restore archived build uses: actions/download-artifact@v4 with: @@ -574,16 +592,6 @@ jobs: merge-multiple: true - name: Display structure of build run: ls -R build - - name: Install fixture dependencies - working-directory: fixtures/flight - run: | - yarn install --frozen-lockfile --cache-folder ~/.cache/yarn - if [ $? -ne 0 ]; then - yarn install --frozen-lockfile --cache-folder ~/.cache/yarn - fi - - name: Playwright install deps - working-directory: fixtures/flight - run: npx playwright install --with-deps chromium - name: Run tests working-directory: fixtures/flight run: yarn test From d16c26da40c96704e24b43832d2f7057f586c415 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:22:40 -0400 Subject: [PATCH 173/300] [ci] Specify if-no-files-found on actions/upload-artifact@v4 (#32679) Defaults to warn, but since some steps require these artifacts to be uploaded we specify an error if its not found. Some other steps like playwright test-results are only uploaded on failure so it's okay to ignore. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32679). * #32680 * __->__ #32679 * #32678 --- .github/workflows/compiler_playground.yml | 1 + .github/workflows/devtools_regression_tests.yml | 5 +++++ .github/workflows/runtime_build_and_test.yml | 6 ++++++ .github/workflows/runtime_commit_artifacts.yml | 2 ++ 4 files changed, 14 insertions(+) diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index 0a2551ce3aa3d..2ef9920c26dda 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -50,3 +50,4 @@ jobs: with: name: test-results path: compiler/apps/playground/test-results + if-no-files-found: ignore diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index f8e5fefca9266..399772cf832de 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -47,6 +47,7 @@ jobs: with: name: build path: build + if-no-files-found: error build_devtools_and_process_artifacts: name: Build DevTools and process artifacts @@ -84,17 +85,20 @@ jobs: with: name: react-devtools path: build/devtools.tgz + if-no-files-found: error # Simplifies getting the extension for local testing - name: Archive chrome extension uses: actions/upload-artifact@v4 with: name: react-devtools-chrome-extension path: build/devtools/chrome-extension.zip + if-no-files-found: error - name: Archive firefox extension uses: actions/upload-artifact@v4 with: name: react-devtools-firefox-extension path: build/devtools/firefox-extension.zip + if-no-files-found: error run_devtools_tests_for_versions: name: Run DevTools tests for versions @@ -179,3 +183,4 @@ jobs: with: name: screenshots path: ./tmp/screenshots + if-no-files-found: warn diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 1cb487559789b..2ebe7ac5f3085 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -302,6 +302,7 @@ jobs: with: name: _build_${{ matrix.worker_id }}_${{ matrix.release_channel }} path: build + if-no-files-found: error test_build: name: yarn test-build @@ -426,6 +427,7 @@ jobs: path: | ./build.tgz ./build2.tgz + if-no-files-found: error check_error_codes: name: Search build artifacts for unminified errors @@ -603,11 +605,13 @@ jobs: with: name: flight-playwright-report path: fixtures/flight/playwright-report + if-no-files-found: warn - name: Archive Flight fixture artifacts uses: actions/upload-artifact@v4 with: name: flight-test-results path: fixtures/flight/test-results + if-no-files-found: ignore # ----- DEVTOOLS ----- build_devtools_and_process_artifacts: @@ -655,6 +659,7 @@ jobs: with: name: react-devtools-${{ matrix.browser }}-extension path: build/devtools/${{ matrix.browser }}-extension.zip + if-no-files-found: error merge_devtools_artifacts: name: Merge DevTools artifacts @@ -767,3 +772,4 @@ jobs: with: name: sizebot-message path: sizebot-message.md + if-no-files-found: ignore diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index 03795b7d2d4f0..ed97d51d985f3 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -170,10 +170,12 @@ jobs: with: name: compiled path: compiled/ + if-no-files-found: error - uses: actions/upload-artifact@v4 with: name: compiled-rn path: compiled-rn/ + if-no-files-found: error commit_www_artifacts: needs: download_artifacts From 19176e3c08dd67879fa134c9de172fcd53df841a Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:24:43 -0400 Subject: [PATCH 174/300] [ci] Use correct revision for Meta builds (#32680) There was a bug previously in our commit artifacts step where the emitted REVISION hash would reference the commit on the builds branch rather than from `main`. Given that our internal manual sync script also does this, let's align them both to always reference the commit from `main` instead. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32680). * __->__ #32680 * #32679 * #32678 --- .github/workflows/runtime_commit_artifacts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index ed97d51d985f3..b645aa0d3af86 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -151,9 +151,9 @@ jobs: ls -R ./compiled-rn - name: Add REVISION files run: | - echo ${{ github.sha }} >> ./compiled/facebook-www/REVISION + echo ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} >> ./compiled/facebook-www/REVISION cp ./compiled/facebook-www/REVISION ./compiled/facebook-www/REVISION_TRANSFORMS - echo ${{ github.sha}} >> ./compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION + echo ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} >> ./compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION - name: "Get current version string" id: get_current_version run: | From ff8f6f21f756c81fba284557357eb6e6ce765149 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Wed, 19 Mar 2025 18:13:06 -0400 Subject: [PATCH 175/300] [ci] Fix Will commit these changes www step (#32681) Unlike the fbsource version of the step, www doesn't add any changes so the `force` input doesn't actually work --- .github/workflows/runtime_commit_artifacts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index b645aa0d3af86..b44d72730a200 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -246,8 +246,8 @@ jobs: - name: Will commit these changes if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true' run: | - echo ":" - git status -u + git add . + git status - name: Check commit message if: inputs.dry_run run: | From a4f9bd586b6108562e5fba2be235d8f6b450cb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Wed, 19 Mar 2025 20:38:27 -0400 Subject: [PATCH 176/300] Enable Fragment refs in Experimental (#32670) That we can test it out in Next.js router conditionally when experimental is on for other reasons. --- packages/shared/ReactFeatureFlags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 6bed7187dd07e..9463be1b95f43 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -162,7 +162,7 @@ export const enableUseEffectCRUDOverload = false; export const enableFastAddPropertiesInDiffing = true; export const enableLazyPublicInstanceInFabric = false; -export const enableFragmentRefs = false; +export const enableFragmentRefs = __EXPERIMENTAL__; // ----------------------------------------------------------------------------- // Ready for next major. From 3bcf8c23debf0c0c746ed11801fa1fe64dfb0159 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 12:21:50 -0400 Subject: [PATCH 177/300] [ci] Warm cache (#32685) Try restoring from old caches as a base to speed up the case where node_modules needs updating. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32685). * #32686 * __->__ #32685 --- .github/workflows/runtime_build_and_test.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 2ebe7ac5f3085..9f83237c495d6 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -41,6 +41,16 @@ jobs: node-version-file: '.nvmrc' cache: yarn cache-dependency-path: yarn.lock + - name: Warm with old cache + if: steps.node_modules.outputs.cache-hit != 'true' + uses: actions/cache/restore@v4 + with: + path: | + **/node_modules + key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - run: yarn install --frozen-lockfile if: steps.node_modules.outputs.cache-hit != 'true' - name: Save cache @@ -74,6 +84,16 @@ jobs: cache-dependency-path: | yarn.lock compiler/yarn.lock + - name: Warm with old cache + if: steps.node_modules.outputs.cache-hit != 'true' + uses: actions/cache/restore@v4 + with: + path: | + **/node_modules + key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + restore-keys: | + runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-and-compiler-node_modules-v6- - run: yarn install --frozen-lockfile if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd compiler install --frozen-lockfile From 87d7e4c55be7fea5efd1e567d52e943ad5d3133d Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 12:22:06 -0400 Subject: [PATCH 178/300] [ci] Fail on cache miss (#32686) Since we use a centralized cache we should fail subsequent steps if the child jobs are unable to restore the cache from the first 2 jobs. Also fix some incorrect hashes used for the fixture tests. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32686). * __->__ #32686 * #32685 --- .github/workflows/runtime_build_and_test.yml | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 9f83237c495d6..303ef92b6e3b5 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -147,6 +147,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -174,6 +175,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -203,6 +205,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -261,6 +264,7 @@ jobs: path: | **/node_modules key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -302,6 +306,7 @@ jobs: path: | **/node_modules key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -383,6 +388,7 @@ jobs: path: | **/node_modules key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -419,6 +425,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -469,6 +476,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -506,6 +514,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -539,7 +548,7 @@ jobs: with: path: | **/node_modules - key: fixtures_dom-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_dom-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'fixtures/dom/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn --cwd fixtures/dom install --frozen-lockfile @@ -583,7 +592,7 @@ jobs: with: path: | **/node_modules - key: fixtures_flight-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + key: fixtures_flight-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'fixtures/flight/yarn.lock') }} - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -658,6 +667,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -712,6 +722,7 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} + fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -733,7 +744,7 @@ jobs: sizebot: if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }} name: Run sizebot - needs: [build_and_lint, runtime_node_modules_cache] + needs: [build_and_lint] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -745,7 +756,7 @@ jobs: cache: yarn cache-dependency-path: yarn.lock - name: Restore cached node_modules - uses: actions/cache/restore@v4 + uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key id: node_modules with: path: | @@ -771,8 +782,6 @@ jobs: run: ls -R base-build - name: Ensure clean build directory run: rm -rf build - - run: yarn install --frozen-lockfile - if: steps.node_modules.outputs.cache-hit != 'true' - name: Restore archived build for PR uses: actions/download-artifact@v4 with: From 112224d8d24b127efa1e680403cca2289c1a261b Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 13:53:31 -0400 Subject: [PATCH 179/300] [ci] Also cache playground playwright browsers (#32687) Following #32678, do the same for the playground e2e test since this step can sometimes take many minutes to complete. --- .github/workflows/compiler_playground.yml | 12 ++++++++++++ .github/workflows/runtime_build_and_test.yml | 9 +++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index 2ef9920c26dda..224c82e6de7c3 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -40,7 +40,19 @@ jobs: **/node_modules key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }} - run: yarn install --frozen-lockfile + - name: Check Playwright version + id: playwright_version + run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT" + - name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }} + id: cache_playwright_browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }} - run: npx playwright install --with-deps chromium + if: steps.cache_playwright_browsers.outputs.cache-hit != 'true' + - run: npx playwright install-deps + if: steps.cache_playwright_browsers.outputs.cache-hit == 'true' - run: CI=true yarn test - run: ls -R test-results if: '!cancelled()' diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 303ef92b6e3b5..165ddac43cd29 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -599,13 +599,10 @@ jobs: if: steps.node_modules.outputs.cache-hit != 'true' - run: yarn --cwd fixtures/flight install --frozen-lockfile if: steps.node_modules.outputs.cache-hit != 'true' - - name: Store Playwright version + - name: Check Playwright version id: playwright_version - run: | - PLAYWRIGHT_VERSION=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//') - echo "Playwright's Version: $PLAYWRIGHT_VERSION" - echo "playwright_version=$PLAYWRIGHT_VERSION" >> "$GITHUB_OUTPUT" - - name: Cache Playwright Browsers for Playwright's Version + run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT" + - name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }} id: cache_playwright_browsers uses: actions/cache@v4 with: From 5f4c5c920fb454f6b8375bdcd4045eaa82e70928 Mon Sep 17 00:00:00 2001 From: Joe Savona <joesavona@fb.com> Date: Thu, 20 Mar 2025 11:02:02 -0700 Subject: [PATCH 180/300] [compiler] Validate static components React uses function identity to determine whether a given JSX expression represents the same type of component and should reconcile (keep state, update props) or replace (teardown state, create a new instance). This PR adds off-by-default validation to check that developers are not dynamically creating components during render. The check is local and intentionally conservative. We specifically look for the results of call expressions, new expressions, or function expressions that are then used directly (or aliased) as a JSX tag. This allows common sketchy but fine-in-practice cases like passing a reference to a component from a parent as props, but catches very obvious mistakes such as: ```js function Example() { const Component = createComponent(); return <Component />; } ``` We could expand this to catch more cases, but this seems like a reasonable starting point. Note that I tried enabling the validation by default and the only fixtures that error are the new ones added here. I'll also test this internally. What i'm imagining is that we enable this in the linter but not the compiler. ghstack-source-id: e7408c0a55478b40d65489703d209e8fa7205e45 Pull Request resolved: https://github.com/facebook/react/pull/32683 --- .../src/Entrypoint/Pipeline.ts | 5 ++ .../src/HIR/Environment.ts | 18 ++++ .../Validation/ValidateStaticComponents.ts | 87 +++++++++++++++++++ ...-constructed-component-in-render.expect.md | 59 +++++++++++++ ...mically-constructed-component-in-render.js | 10 +++ ...ly-construct-component-in-render.expect.md | 41 +++++++++ ...namically-construct-component-in-render.js | 5 ++ ...y-constructed-component-function.expect.md | 46 ++++++++++ ...amically-constructed-component-function.js | 7 ++ ...onstructed-component-method-call.expect.md | 50 +++++++++++ ...cally-constructed-component-method-call.js | 5 ++ ...ically-constructed-component-new.expect.md | 41 +++++++++ ...d-dynamically-constructed-component-new.js | 5 ++ 13 files changed, 379 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 994aa8f18c114..36d96d8c1bf4f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -102,6 +102,7 @@ import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls import {transformFire} from '../Transform'; import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender'; import {CompilerError} from '..'; +import {validateStaticComponents} from '../Validation/ValidateStaticComponents'; export type CompilerPipelineValue = | {kind: 'ast'; name: string; value: CodegenFunction} @@ -293,6 +294,10 @@ function runWithEnvironment( }); if (env.isInferredMemoEnabled) { + if (env.config.validateStaticComponents) { + env.logErrors(validateStaticComponents(hir)); + } + /** * Only create reactive scopes (which directly map to generated memo blocks) * if inferred memoization is enabled. This makes all later passes which diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 1f64452e4bacf..a8f5b15dbd06d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -330,6 +330,11 @@ const EnvironmentConfigSchema = z.object({ */ validateNoJSXInTryStatements: z.boolean().default(false), + /** + * Validates against dynamically creating components during render. + */ + validateStaticComponents: z.boolean().default(false), + /** * Validates that the dependencies of all effect hooks are memoized. This helps ensure * that Forget does not introduce infinite renders caused by a dependency changing, @@ -932,6 +937,19 @@ export class Environment { return makeScopeId(this.#nextScope++); } + logErrors(errors: Result<void, CompilerError>): void { + if (errors.isOk() || this.logger == null) { + return; + } + for (const error of errors.unwrapErr().details) { + this.logger.logEvent(this.filename, { + kind: 'CompileError', + detail: error, + fnLoc: null, + }); + } + } + isContextIdentifier(node: t.Identifier): boolean { return this.#contextIdentifiers.has(node); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts new file mode 100644 index 0000000000000..a453d484ecb6d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts @@ -0,0 +1,87 @@ +/** + * 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 {CompilerError, ErrorSeverity} from '../CompilerError'; +import {HIRFunction, IdentifierId, SourceLocation} from '../HIR'; +import {Err, Ok, Result} from '../Utils/Result'; + +/** + * Validates against components that are created dynamically and whose identity is not guaranteed + * to be stable (which would cause the component to reset on each re-render). + */ +export function validateStaticComponents( + fn: HIRFunction, +): Result<void, CompilerError> { + const error = new CompilerError(); + const knownDynamicComponents = new Map<IdentifierId, SourceLocation>(); + for (const block of fn.body.blocks.values()) { + phis: for (const phi of block.phis) { + for (const operand of phi.operands.values()) { + const loc = knownDynamicComponents.get(operand.identifier.id); + if (loc != null) { + knownDynamicComponents.set(phi.place.identifier.id, loc); + continue phis; + } + } + } + for (const instr of block.instructions) { + const {lvalue, value} = instr; + switch (value.kind) { + case 'FunctionExpression': + case 'NewExpression': + case 'MethodCall': + case 'CallExpression': { + knownDynamicComponents.set(lvalue.identifier.id, value.loc); + break; + } + case 'LoadLocal': { + const loc = knownDynamicComponents.get(value.place.identifier.id); + if (loc != null) { + knownDynamicComponents.set(lvalue.identifier.id, loc); + } + break; + } + case 'StoreLocal': { + const loc = knownDynamicComponents.get(value.value.identifier.id); + if (loc != null) { + knownDynamicComponents.set(lvalue.identifier.id, loc); + knownDynamicComponents.set(value.lvalue.place.identifier.id, loc); + } + break; + } + case 'JsxExpression': { + if (value.tag.kind === 'Identifier') { + const location = knownDynamicComponents.get( + value.tag.identifier.id, + ); + if (location != null) { + error.push({ + reason: `Components created during render will reset their state each time they are created. Declare components outside of render. `, + severity: ErrorSeverity.InvalidReact, + loc: value.tag.loc, + description: null, + suggestions: null, + }); + error.push({ + reason: `The component may be created during render`, + severity: ErrorSeverity.InvalidReact, + loc: location, + description: null, + suggestions: null, + }); + } + } + } + } + } + } + if (error.hasErrors()) { + return Err(error); + } else { + return Ok(undefined); + } +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md new file mode 100644 index 0000000000000..836d0fe7d9db2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +// @logger @validateStaticComponents +function Example(props) { + let Component; + if (props.cond) { + Component = createComponent(); + } else { + Component = DefaultComponent; + } + return <Component />; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateStaticComponents +function Example(props) { + const $ = _c(3); + let Component; + if (props.cond) { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = createComponent(); + $[0] = t0; + } else { + t0 = $[0]; + } + Component = t0; + } else { + Component = DefaultComponent; + } + let t0; + if ($[1] !== Component) { + t0 = <Component />; + $[1] = Component; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Components created during render will reset their state each time they are created. Declare components outside of render. ","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":10,"index":194},"end":{"line":9,"column":19,"index":203},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"reason":"The component may be created during render","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":5,"column":16,"index":116},"end":{"line":5,"column":33,"index":133},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":37},"end":{"line":10,"column":1,"index":209},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"fnName":"Example","memoSlots":3,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js new file mode 100644 index 0000000000000..580d330728509 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js @@ -0,0 +1,10 @@ +// @logger @validateStaticComponents +function Example(props) { + let Component; + if (props.cond) { + Component = createComponent(); + } else { + Component = DefaultComponent; + } + return <Component />; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md new file mode 100644 index 0000000000000..a222bd44cab6e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @logger @validateStaticComponents +function Example(props) { + const Component = createComponent(); + return <Component />; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateStaticComponents +function Example(props) { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const Component = createComponent(); + t0 = <Component />; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Components created during render will reset their state each time they are created. Declare components outside of render. ","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":4,"column":10,"index":112},"end":{"line":4,"column":19,"index":121},"filename":"invalid-dynamically-construct-component-in-render.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"reason":"The component may be created during render","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":3,"column":20,"index":83},"end":{"line":3,"column":37,"index":100},"filename":"invalid-dynamically-construct-component-in-render.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":37},"end":{"line":5,"column":1,"index":127},"filename":"invalid-dynamically-construct-component-in-render.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js new file mode 100644 index 0000000000000..ebbb2b239d20a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js @@ -0,0 +1,5 @@ +// @logger @validateStaticComponents +function Example(props) { + const Component = createComponent(); + return <Component />; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md new file mode 100644 index 0000000000000..41339bd8c4814 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +// @logger @validateStaticComponents +function Example(props) { + function Component() { + return <div />; + } + return <Component />; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateStaticComponents +function Example(props) { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const Component = function Component() { + return <div />; + }; + + t0 = <Component />; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Components created during render will reset their state each time they are created. Declare components outside of render. ","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":6,"column":10,"index":122},"end":{"line":6,"column":19,"index":131},"filename":"invalid-dynamically-constructed-component-function.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"reason":"The component may be created during render","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":3,"column":2,"index":65},"end":{"line":5,"column":3,"index":111},"filename":"invalid-dynamically-constructed-component-function.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":37},"end":{"line":7,"column":1,"index":137},"filename":"invalid-dynamically-constructed-component-function.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js new file mode 100644 index 0000000000000..1ce24193d2f56 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js @@ -0,0 +1,7 @@ +// @logger @validateStaticComponents +function Example(props) { + function Component() { + return <div />; + } + return <Component />; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md new file mode 100644 index 0000000000000..c53cf894cd9e9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @logger @validateStaticComponents +function Example(props) { + const Component = props.foo.bar(); + return <Component />; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateStaticComponents +function Example(props) { + const $ = _c(4); + let t0; + if ($[0] !== props.foo) { + t0 = props.foo.bar(); + $[0] = props.foo; + $[1] = t0; + } else { + t0 = $[1]; + } + const Component = t0; + let t1; + if ($[2] !== Component) { + t1 = <Component />; + $[2] = Component; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Components created during render will reset their state each time they are created. Declare components outside of render. ","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":4,"column":10,"index":110},"end":{"line":4,"column":19,"index":119},"filename":"invalid-dynamically-constructed-component-method-call.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"reason":"The component may be created during render","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":3,"column":20,"index":83},"end":{"line":3,"column":35,"index":98},"filename":"invalid-dynamically-constructed-component-method-call.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":37},"end":{"line":5,"column":1,"index":125},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"fnName":"Example","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js new file mode 100644 index 0000000000000..01c3fb85f93d9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js @@ -0,0 +1,5 @@ +// @logger @validateStaticComponents +function Example(props) { + const Component = props.foo.bar(); + return <Component />; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md new file mode 100644 index 0000000000000..d45f263adba51 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @logger @validateStaticComponents +function Example(props) { + const Component = new ComponentFactory(); + return <Component />; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateStaticComponents +function Example(props) { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const Component = new ComponentFactory(); + t0 = <Component />; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Components created during render will reset their state each time they are created. Declare components outside of render. ","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":4,"column":10,"index":117},"end":{"line":4,"column":19,"index":126},"filename":"invalid-dynamically-constructed-component-new.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"reason":"The component may be created during render","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":3,"column":20,"index":83},"end":{"line":3,"column":42,"index":105},"filename":"invalid-dynamically-constructed-component-new.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":37},"end":{"line":5,"column":1,"index":132},"filename":"invalid-dynamically-constructed-component-new.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js new file mode 100644 index 0000000000000..4d5ba62d1e67d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js @@ -0,0 +1,5 @@ +// @logger @validateStaticComponents +function Example(props) { + const Component = new ComponentFactory(); + return <Component />; +} From e3c06424ae1162319d786a76371d649dee412c29 Mon Sep 17 00:00:00 2001 From: Joe Savona <joesavona@fb.com> Date: Thu, 20 Mar 2025 11:02:02 -0700 Subject: [PATCH 181/300] [compiler] Refactor validations to return Result and log where appropriate Updates ~all of our validations to return a Result, and then updates callers to either unwrap() if they should bailout or else just log. ghstack-source-id: 418b5f5aa2b7dd49ca76b3f98a48a35150691d7e Pull Request resolved: https://github.com/facebook/react/pull/32688 --- .../src/CompilerError.ts | 5 ++ .../src/Entrypoint/Pipeline.ts | 22 +++--- .../src/Validation/ValidateHooksUsage.ts | 9 ++- .../ValidateMemoizedEffectDependencies.ts | 9 ++- .../Validation/ValidateNoCapitalizedCalls.ts | 12 ++- ...s => ValidateNoImpureFunctionsInRender.ts} | 9 ++- .../Validation/ValidateNoJSXInTryStatement.ts | 9 ++- .../Validation/ValidateNoRefAccesInRender.ts | 6 +- .../ValidateNoSetStateInPassiveEffects.ts | 9 ++- .../Validation/ValidateNoSetStateInRender.ts | 14 ++-- .../ValidatePreservedManualMemoization.ts | 12 +-- .../Validation/ValidateStaticComponents.ts | 8 +- .../src/Validation/ValidateUseMemo.ts | 13 +++- ...in-catch-in-outer-try-with-catch.expect.md | 38 ---------- ...or.invalid-jsx-in-try-with-catch.expect.md | 31 -------- ...setState-in-useEffect-transitive.expect.md | 37 ---------- ...or.invalid-setState-in-useEffect.expect.md | 31 -------- ...in-catch-in-outer-try-with-catch.expect.md | 73 +++++++++++++++++++ ...d-jsx-in-catch-in-outer-try-with-catch.js} | 2 +- .../invalid-jsx-in-try-with-catch.expect.md | 50 +++++++++++++ ...ch.js => invalid-jsx-in-try-with-catch.js} | 2 +- ...setState-in-useEffect-transitive.expect.md | 73 +++++++++++++++++++ ...valid-setState-in-useEffect-transitive.js} | 2 +- .../invalid-setState-in-useEffect.expect.md | 53 ++++++++++++++ ...ct.js => invalid-setState-in-useEffect.js} | 2 +- 25 files changed, 331 insertions(+), 200 deletions(-) rename compiler/packages/babel-plugin-react-compiler/src/Validation/{ValiateNoImpureFunctionsInRender.ts => ValidateNoImpureFunctionsInRender.ts} (91%) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.invalid-jsx-in-catch-in-outer-try-with-catch.js => invalid-jsx-in-catch-in-outer-try-with-catch.js} (85%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.invalid-jsx-in-try-with-catch.js => invalid-jsx-in-try-with-catch.js} (73%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.invalid-setState-in-useEffect-transitive.js => invalid-setState-in-useEffect-transitive.js} (83%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.invalid-setState-in-useEffect.js => invalid-setState-in-useEffect.js} (79%) diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index 3a3010dd2f62b..7285140de0a62 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -6,6 +6,7 @@ */ import type {SourceLocation} from './HIR'; +import {Err, Ok, Result} from './Utils/Result'; import {assertExhaustive} from './Utils/utils'; export enum ErrorSeverity { @@ -224,6 +225,10 @@ export class CompilerError extends Error { return this.details.length > 0; } + asResult(): Result<void, CompilerError> { + return this.hasErrors() ? Err(this) : Ok(undefined); + } + /* * An error is critical if it means the compiler has entered into a broken state and cannot * continue safely. Other expected errors such as Todos mean that we can skip over that component diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 36d96d8c1bf4f..ed41ce2eedc46 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -100,7 +100,7 @@ import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHI import {outlineJSX} from '../Optimization/OutlineJsx'; import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls'; import {transformFire} from '../Transform'; -import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender'; +import {validateNoImpureFunctionsInRender} from '../Validation/ValidateNoImpureFunctionsInRender'; import {CompilerError} from '..'; import {validateStaticComponents} from '../Validation/ValidateStaticComponents'; @@ -162,7 +162,7 @@ function runWithEnvironment( log({kind: 'hir', name: 'PruneMaybeThrows', value: hir}); validateContextVariableLValues(hir); - validateUseMemo(hir); + validateUseMemo(hir).unwrap(); if ( env.isInferredMemoEnabled && @@ -203,10 +203,10 @@ function runWithEnvironment( if (env.isInferredMemoEnabled) { if (env.config.validateHooksUsage) { - validateHooksUsage(hir); + validateHooksUsage(hir).unwrap(); } if (env.config.validateNoCapitalizedCalls) { - validateNoCapitalizedCalls(hir); + validateNoCapitalizedCalls(hir).unwrap(); } } @@ -256,23 +256,23 @@ function runWithEnvironment( } if (env.config.validateRefAccessDuringRender) { - validateNoRefAccessInRender(hir); + validateNoRefAccessInRender(hir).unwrap(); } if (env.config.validateNoSetStateInRender) { - validateNoSetStateInRender(hir); + validateNoSetStateInRender(hir).unwrap(); } if (env.config.validateNoSetStateInPassiveEffects) { - validateNoSetStateInPassiveEffects(hir); + env.logErrors(validateNoSetStateInPassiveEffects(hir)); } if (env.config.validateNoJSXInTryStatements) { - validateNoJSXInTryStatement(hir); + env.logErrors(validateNoJSXInTryStatement(hir)); } if (env.config.validateNoImpureFunctionsInRender) { - validateNoImpureFunctionsInRender(hir); + validateNoImpureFunctionsInRender(hir).unwrap(); } } @@ -514,14 +514,14 @@ function runWithEnvironment( }); if (env.config.validateMemoizedEffectDependencies) { - validateMemoizedEffectDependencies(reactiveFunction); + validateMemoizedEffectDependencies(reactiveFunction).unwrap(); } if ( env.config.enablePreserveExistingMemoizationGuarantees || env.config.validatePreserveExistingMemoizationGuarantees ) { - validatePreservedManualMemoization(reactiveFunction); + validatePreservedManualMemoization(reactiveFunction).unwrap(); } const ast = codegenFunction(reactiveFunction, { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts index ed23693f61439..e90f33c74076c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts @@ -26,6 +26,7 @@ import { eachTerminalOperand, } from '../HIR/visitors'; import {assertExhaustive} from '../Utils/utils'; +import {Result} from '../Utils/Result'; /** * Represents the possible kinds of value which may be stored at a given Place during @@ -87,7 +88,9 @@ function joinKinds(a: Kind, b: Kind): Kind { * may not appear as the callee of a conditional call. * See the note for Kind.PotentialHook for sources of potential hooks */ -export function validateHooksUsage(fn: HIRFunction): void { +export function validateHooksUsage( + fn: HIRFunction, +): Result<void, CompilerError> { const unconditionalBlocks = computeUnconditionalBlocks(fn); const errors = new CompilerError(); @@ -423,9 +426,7 @@ export function validateHooksUsage(fn: HIRFunction): void { for (const [, error] of errorsByPlace) { errors.push(error); } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts index 364e78ae6b68d..b33cfb1512349 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts @@ -22,6 +22,7 @@ import { ReactiveFunctionVisitor, visitReactiveFunction, } from '../ReactiveScopes/visitors'; +import {Result} from '../Utils/Result'; /** * Validates that all known effect dependencies are memoized. The algorithm checks two things: @@ -47,12 +48,12 @@ import { * mutate(object); // ... mutable range ends here after this mutation * ``` */ -export function validateMemoizedEffectDependencies(fn: ReactiveFunction): void { +export function validateMemoizedEffectDependencies( + fn: ReactiveFunction, +): Result<void, CompilerError> { const errors = new CompilerError(); visitReactiveFunction(fn, new Visitor(), errors); - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } class Visitor extends ReactiveFunctionVisitor<CompilerError> { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts index 2f9b0a1c7e2ce..4ba70b64a8c94 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts @@ -4,11 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import {CompilerError, EnvironmentConfig} from '..'; +import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..'; import {HIRFunction, IdentifierId} from '../HIR'; import {DEFAULT_GLOBALS} from '../HIR/Globals'; +import {Result} from '../Utils/Result'; -export function validateNoCapitalizedCalls(fn: HIRFunction): void { +export function validateNoCapitalizedCalls( + fn: HIRFunction, +): Result<void, CompilerError> { const envConfig: EnvironmentConfig = fn.env.config; const ALLOW_LIST = new Set([ ...DEFAULT_GLOBALS.keys(), @@ -26,6 +29,7 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { ); }; + const errors = new CompilerError(); const capitalLoadGlobals = new Map<IdentifierId, string>(); const capitalizedProperties = new Map<IdentifierId, string>(); const reason = @@ -73,7 +77,8 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { const propertyIdentifier = value.property.identifier.id; const propertyName = capitalizedProperties.get(propertyIdentifier); if (propertyName != null) { - CompilerError.throwInvalidReact({ + errors.push({ + severity: ErrorSeverity.InvalidReact, reason, description: `${propertyName} may be a component.`, loc: value.loc, @@ -85,4 +90,5 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { } } } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValiateNoImpureFunctionsInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts similarity index 91% rename from compiler/packages/babel-plugin-react-compiler/src/Validation/ValiateNoImpureFunctionsInRender.ts rename to compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts index 287a0aa978b6f..6e88773ecf0bd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValiateNoImpureFunctionsInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts @@ -8,6 +8,7 @@ import {CompilerError, ErrorSeverity} from '..'; import {HIRFunction} from '../HIR'; import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; +import {Result} from '../Utils/Result'; /** * Checks that known-impure functions are not called during render. Examples of invalid functions to @@ -18,7 +19,9 @@ import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; * this in several of our validation passes and should unify those analyses into a reusable helper * and use it here. */ -export function validateNoImpureFunctionsInRender(fn: HIRFunction): void { +export function validateNoImpureFunctionsInRender( + fn: HIRFunction, +): Result<void, CompilerError> { const errors = new CompilerError(); for (const [, block] of fn.body.blocks) { for (const instr of block.instructions) { @@ -46,7 +49,5 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void { } } } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts index b92a89d764301..505302f7d12c2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts @@ -7,6 +7,7 @@ import {CompilerError, ErrorSeverity} from '..'; import {BlockId, HIRFunction} from '../HIR'; +import {Result} from '../Utils/Result'; import {retainWhere} from '../Utils/utils'; /** @@ -19,7 +20,9 @@ import {retainWhere} from '../Utils/utils'; * created within a try block. JSX is allowed within a catch statement, unless that catch * is itself nested inside an outer try. */ -export function validateNoJSXInTryStatement(fn: HIRFunction): void { +export function validateNoJSXInTryStatement( + fn: HIRFunction, +): Result<void, CompilerError> { const activeTryBlocks: Array<BlockId> = []; const errors = new CompilerError(); for (const [, block] of fn.body.blocks) { @@ -46,7 +49,5 @@ export function validateNoJSXInTryStatement(fn: HIRFunction): void { activeTryBlocks.push(block.terminal.handler); } } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts index 779207b22e646..d00302559bb4e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts @@ -99,9 +99,11 @@ class Env extends Map<IdentifierId, RefAccessType> { } } -export function validateNoRefAccessInRender(fn: HIRFunction): void { +export function validateNoRefAccessInRender( + fn: HIRFunction, +): Result<void, CompilerError> { const env = new Env(); - validateNoRefAccessInRenderImpl(fn, env).unwrap(); + return validateNoRefAccessInRenderImpl(fn, env).map(_ => undefined); } function refTypeOfType(place: Place): RefAccessType { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts index 2c6e7d8ac67cd..a36c347faa001 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts @@ -14,6 +14,7 @@ import { Place, } from '../HIR'; import {eachInstructionValueOperand} from '../HIR/visitors'; +import {Result} from '../Utils/Result'; /** * Validates against calling setState in the body of a *passive* effect (useEffect), @@ -23,7 +24,9 @@ import {eachInstructionValueOperand} from '../HIR/visitors'; * often bad for performance and frequently has more efficient and straightforward * alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples. */ -export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void { +export function validateNoSetStateInPassiveEffects( + fn: HIRFunction, +): Result<void, CompilerError> { const setStateFunctions: Map<IdentifierId, Place> = new Map(); const errors = new CompilerError(); for (const [, block] of fn.body.blocks) { @@ -98,9 +101,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void { } } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } function getSetStateCall( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts index 3f378b1289e6c..fc101581b30b0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts @@ -9,7 +9,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError'; import {HIRFunction, IdentifierId, isSetStateType} from '../HIR'; import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks'; import {eachInstructionValueOperand} from '../HIR/visitors'; -import {Err, Ok, Result} from '../Utils/Result'; +import {Result} from '../Utils/Result'; /** * Validates that the given function does not have an infinite update loop @@ -39,9 +39,11 @@ import {Err, Ok, Result} from '../Utils/Result'; * y(); * ``` */ -export function validateNoSetStateInRender(fn: HIRFunction): void { +export function validateNoSetStateInRender( + fn: HIRFunction, +): Result<void, CompilerError> { const unconditionalSetStateFunctions: Set<IdentifierId> = new Set(); - validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions).unwrap(); + return validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions); } function validateNoSetStateInRenderImpl( @@ -145,9 +147,5 @@ function validateNoSetStateInRenderImpl( } } - if (errors.hasErrors()) { - return Err(errors); - } else { - return Ok(undefined); - } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts index 28be6c166d2e4..85fec7a75333b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts @@ -5,9 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, Effect, ErrorSeverity} from '..'; +import {CompilerError, ErrorSeverity} from '../CompilerError'; import { DeclarationId, + Effect, GeneratedSource, Identifier, IdentifierId, @@ -30,6 +31,7 @@ import { ReactiveFunctionVisitor, visitReactiveFunction, } from '../ReactiveScopes/visitors'; +import {Result} from '../Utils/Result'; import {getOrInsertDefault} from '../Utils/utils'; /** @@ -39,15 +41,15 @@ import {getOrInsertDefault} from '../Utils/utils'; * This can occur if a value's mutable range somehow extended to include a hook and * was pruned. */ -export function validatePreservedManualMemoization(fn: ReactiveFunction): void { +export function validatePreservedManualMemoization( + fn: ReactiveFunction, +): Result<void, CompilerError> { const state = { errors: new CompilerError(), manualMemoState: null, }; visitReactiveFunction(fn, new Visitor(), state); - if (state.errors.hasErrors()) { - throw state.errors; - } + return state.errors.asResult(); } const DEBUG = false; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts index a453d484ecb6d..f7adef6ca7128 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts @@ -7,7 +7,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError'; import {HIRFunction, IdentifierId, SourceLocation} from '../HIR'; -import {Err, Ok, Result} from '../Utils/Result'; +import {Result} from '../Utils/Result'; /** * Validates against components that are created dynamically and whose identity is not guaranteed @@ -79,9 +79,5 @@ export function validateStaticComponents( } } } - if (error.hasErrors()) { - return Err(error); - } else { - return Ok(undefined); - } + return error.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts index 8fe62b122c6bd..fd4d781ef3c6f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts @@ -5,10 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError} from '..'; +import {CompilerError, ErrorSeverity} from '..'; import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR'; +import {Result} from '../Utils/Result'; -export function validateUseMemo(fn: HIRFunction): void { +export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> { + const errors = new CompilerError(); const useMemos = new Set<IdentifierId>(); const react = new Set<IdentifierId>(); const functions = new Map<IdentifierId, FunctionExpression>(); @@ -61,7 +63,8 @@ export function validateUseMemo(fn: HIRFunction): void { } if (body.loweredFunc.func.params.length > 0) { - CompilerError.throwInvalidReact({ + errors.push({ + severity: ErrorSeverity.InvalidReact, reason: 'useMemo callbacks may not accept any arguments', description: null, loc: body.loc, @@ -70,7 +73,8 @@ export function validateUseMemo(fn: HIRFunction): void { } if (body.loweredFunc.func.async || body.loweredFunc.func.generator) { - CompilerError.throwInvalidReact({ + errors.push({ + severity: ErrorSeverity.InvalidReact, reason: 'useMemo callbacks may not be async or generator functions', description: null, @@ -84,4 +88,5 @@ export function validateUseMemo(fn: HIRFunction): void { } } } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md deleted file mode 100644 index 40cebff89a75e..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md +++ /dev/null @@ -1,38 +0,0 @@ - -## Input - -```javascript -// @validateNoJSXInTryStatements -import {identity} from 'shared-runtime'; - -function Component(props) { - let el; - try { - let value; - try { - value = identity(props.foo); - } catch { - el = <div value={value} />; - } - } catch { - return null; - } - return el; -} - -``` - - -## Error - -``` - 9 | value = identity(props.foo); - 10 | } catch { -> 11 | el = <div value={value} />; - | ^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) (11:11) - 12 | } - 13 | } catch { - 14 | return null; -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md deleted file mode 100644 index ee1f5335ef624..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md +++ /dev/null @@ -1,31 +0,0 @@ - -## Input - -```javascript -// @validateNoJSXInTryStatements -function Component(props) { - let el; - try { - el = <div />; - } catch { - return null; - } - return el; -} - -``` - - -## Error - -``` - 3 | let el; - 4 | try { -> 5 | el = <div />; - | ^^^^^^^ InvalidReact: Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) (5:5) - 6 | } catch { - 7 | return null; - 8 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md deleted file mode 100644 index ab10aa1bae51f..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md +++ /dev/null @@ -1,37 +0,0 @@ - -## Input - -```javascript -// @validateNoSetStateInPassiveEffects -import {useEffect, useState} from 'react'; - -function Component() { - const [state, setState] = useState(0); - const f = () => { - setState(s => s + 1); - }; - const g = () => { - f(); - }; - useEffect(() => { - g(); - }); - return state; -} - -``` - - -## Error - -``` - 11 | }; - 12 | useEffect(() => { -> 13 | g(); - | ^ InvalidReact: Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect) (13:13) - 14 | }); - 15 | return state; - 16 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md deleted file mode 100644 index 83be2965b8ada..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md +++ /dev/null @@ -1,31 +0,0 @@ - -## Input - -```javascript -// @validateNoSetStateInPassiveEffects -import {useEffect, useState} from 'react'; - -function Component() { - const [state, setState] = useState(0); - useEffect(() => { - setState(s => s + 1); - }); - return state; -} - -``` - - -## Error - -``` - 5 | const [state, setState] = useState(0); - 6 | useEffect(() => { -> 7 | setState(s => s + 1); - | ^^^^^^^^ InvalidReact: Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect) (7:7) - 8 | }); - 9 | return state; - 10 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md new file mode 100644 index 0000000000000..2e052ecf33555 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md @@ -0,0 +1,73 @@ + +## Input + +```javascript +// @logger @validateNoJSXInTryStatements +import {identity} from 'shared-runtime'; + +function Component(props) { + let el; + try { + let value; + try { + value = identity(props.foo); + } catch { + el = <div value={value} />; + } + } catch { + return null; + } + return el; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements +import { identity } from "shared-runtime"; + +function Component(props) { + const $ = _c(4); + let el; + try { + let value; + try { + let t0; + if ($[0] !== props.foo) { + t0 = identity(props.foo); + $[0] = props.foo; + $[1] = t0; + } else { + t0 = $[1]; + } + value = t0; + } catch { + let t0; + if ($[2] !== value) { + t0 = <div value={value} />; + $[2] = value; + $[3] = t0; + } else { + t0 = $[3]; + } + el = t0; + } + } catch { + return null; + } + return el; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":11,"column":11,"index":214},"end":{"line":11,"column":32,"index":235},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":83},"end":{"line":17,"column":1,"index":290},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js similarity index 85% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js index 0935a1a63cd83..2e5a3618b4063 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js @@ -1,4 +1,4 @@ -// @validateNoJSXInTryStatements +// @logger @validateNoJSXInTryStatements import {identity} from 'shared-runtime'; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md new file mode 100644 index 0000000000000..702d317a4b1d0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @logger @validateNoJSXInTryStatements +function Component(props) { + let el; + try { + el = <div />; + } catch { + return null; + } + return el; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements +function Component(props) { + const $ = _c(1); + let el; + try { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <div />; + $[0] = t0; + } else { + t0 = $[0]; + } + el = t0; + } catch { + return null; + } + return el; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":5,"column":9,"index":96},"end":{"line":5,"column":16,"index":103},"filename":"invalid-jsx-in-try-with-catch.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":41},"end":{"line":10,"column":1,"index":152},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js similarity index 73% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js index 3e7747c875b3c..815fb4ae9e5a3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js @@ -1,4 +1,4 @@ -// @validateNoJSXInTryStatements +// @logger @validateNoJSXInTryStatements function Component(props) { let el; try { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md new file mode 100644 index 0000000000000..d220617dc121e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md @@ -0,0 +1,73 @@ + +## Input + +```javascript +// @logger @validateNoSetStateInPassiveEffects +import {useEffect, useState} from 'react'; + +function Component() { + const [state, setState] = useState(0); + const f = () => { + setState(s => s + 1); + }; + const g = () => { + f(); + }; + useEffect(() => { + g(); + }); + return state; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects +import { useEffect, useState } from "react"; + +function Component() { + const $ = _c(2); + const [state, setState] = useState(0); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const f = () => { + setState(_temp); + }; + + t0 = () => { + f(); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const g = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + g(); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + return state; +} +function _temp(s) { + return s + 1; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":264},"end":{"line":13,"column":5,"index":265},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":16,"column":1,"index":292},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js similarity index 83% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js index 099f0f7079bea..b03c08609a3fb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInPassiveEffects +// @logger @validateNoSetStateInPassiveEffects import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md new file mode 100644 index 0000000000000..667f57b7cc23c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md @@ -0,0 +1,53 @@ + +## Input + +```javascript +// @logger @validateNoSetStateInPassiveEffects +import {useEffect, useState} from 'react'; + +function Component() { + const [state, setState] = useState(0); + useEffect(() => { + setState(s => s + 1); + }); + return state; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects +import { useEffect, useState } from "react"; + +function Component() { + const $ = _c(1); + const [state, setState] = useState(0); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + setState(_temp); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + useEffect(t0); + return state; +} +function _temp(s) { + return s + 1; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":179},"end":{"line":7,"column":12,"index":187},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":10,"column":1,"index":224},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js similarity index 79% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js index d7d8e4e8858fd..e806b359c6e43 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInPassiveEffects +// @logger @validateNoSetStateInPassiveEffects import {useEffect, useState} from 'react'; function Component() { From 7943da1e81bb8730db78db99af5f967bdf213a75 Mon Sep 17 00:00:00 2001 From: Jack Pope <jackpope1@gmail.com> Date: Thu, 20 Mar 2025 14:28:55 -0400 Subject: [PATCH 182/300] Set accurate value for alwaysThrottleRetries on www (#32684) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag value was updated in https://github.com/facebook/react/pull/28965 (seemingly unrelated, maybe as part of unit testing). But its still controlled by a dynamic flag in www. Let's update this to VARIANT to accurately represent the state of the rollout. Before: <img width="1340" alt="Screenshot 2025-03-20 at 10 45 30 AM" src="https://github.com/user-attachments/assets/d0405a36-eb71-4108-9e23-8d462cc68fb4" /> After: <img width="1351" alt="Screenshot 2025-03-20 at 10 45 11 AM" src="https://github.com/user-attachments/assets/459d260d-7a25-430b-95a6-d6a91d958417" /> --- .../src/__tests__/ReactDOMFloat-test.js | 99 ++++++++++--------- .../forks/ReactFeatureFlags.www-dynamic.js | 2 +- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index 0eb75f866c8c2..7404cec64a00c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -3360,27 +3360,29 @@ body { }); await waitForAll([]); - // Although the commit suspended, a preload was inserted. - expect(getMeaningfulChildren(document)).toEqual( - <html> - <head> - <link rel="preload" href="foo" as="style" /> - </head> - <body>loading...</body> - </html>, - ); + if (gate(flags => flags.alwaysThrottleRetries)) { + // Although the commit suspended, a preload was inserted. + expect(getMeaningfulChildren(document)).toEqual( + <html> + <head> + <link rel="preload" href="foo" as="style" /> + </head> + <body>loading...</body> + </html>, + ); - loadPreloads(['foo']); - assertLog(['load preload: foo']); - expect(getMeaningfulChildren(document)).toEqual( - <html> - <head> - <link rel="stylesheet" href="foo" data-precedence="default" /> - <link rel="preload" href="foo" as="style" /> - </head> - <body>loading...</body> - </html>, - ); + loadPreloads(['foo']); + assertLog(['load preload: foo']); + expect(getMeaningfulChildren(document)).toEqual( + <html> + <head> + <link rel="stylesheet" href="foo" data-precedence="default" /> + <link rel="preload" href="foo" as="style" /> + </head> + <body>loading...</body> + </html>, + ); + } loadStylesheets(['foo']); assertLog(['load stylesheet: foo']); @@ -3410,35 +3412,36 @@ body { ); }); await waitForAll([]); - expect(getMeaningfulChildren(document)).toEqual( - <html> - <head> - <link rel="stylesheet" href="foo" data-precedence="default" /> - <link rel="preload" href="foo" as="style" /> - <link rel="preload" href="bar" as="style" /> - </head> - <body> - <div style="display: none;">hello</div>loading... - </body> - </html>, - ); - - loadPreloads(['bar']); - assertLog(['load preload: bar']); - expect(getMeaningfulChildren(document)).toEqual( - <html> - <head> - <link rel="stylesheet" href="foo" data-precedence="default" /> - <link rel="stylesheet" href="bar" data-precedence="default" /> - <link rel="preload" href="foo" as="style" /> - <link rel="preload" href="bar" as="style" /> - </head> - <body> - <div style="display: none;">hello</div>loading... - </body> - </html>, - ); + if (gate(flags => flags.alwaysThrottleRetries)) { + expect(getMeaningfulChildren(document)).toEqual( + <html> + <head> + <link rel="stylesheet" href="foo" data-precedence="default" /> + <link rel="preload" href="foo" as="style" /> + <link rel="preload" href="bar" as="style" /> + </head> + <body> + <div style="display: none;">hello</div>loading... + </body> + </html>, + ); + loadPreloads(['bar']); + assertLog(['load preload: bar']); + expect(getMeaningfulChildren(document)).toEqual( + <html> + <head> + <link rel="stylesheet" href="foo" data-precedence="default" /> + <link rel="stylesheet" href="bar" data-precedence="default" /> + <link rel="preload" href="foo" as="style" /> + <link rel="preload" href="bar" as="style" /> + </head> + <body> + <div style="display: none;">hello</div>loading... + </body> + </html>, + ); + } loadStylesheets(['bar']); assertLog(['load stylesheet: bar']); expect(getMeaningfulChildren(document)).toEqual( diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 0116e160b643e..d94a0c0ebd531 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -13,7 +13,7 @@ // Use __VARIANT__ to simulate a GK. The tests will be run twice: once // with the __VARIANT__ set to `true`, and once set to `false`. -export const alwaysThrottleRetries = true; +export const alwaysThrottleRetries = __VARIANT__; export const disableDefaultPropsExceptForClasses = __VARIANT__; export const disableLegacyContextForFunctionComponents = __VARIANT__; export const disableSchedulerTimeoutInWorkLoop = __VARIANT__; From b630219b1377f3117036b1c6118676c16fdb21b7 Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Thu, 20 Mar 2025 16:51:33 -0400 Subject: [PATCH 183/300] [refactor] move isValidElementType to react-is (#32518) --- packages/react-is/src/ReactIs.js | 57 ++++++++++++++++++- packages/react/src/ReactMemo.js | 4 +- packages/shared/isValidElementType.js | 81 --------------------------- 3 files changed, 55 insertions(+), 87 deletions(-) delete mode 100644 packages/shared/isValidElementType.js diff --git a/packages/react-is/src/ReactIs.js b/packages/react-is/src/ReactIs.js index db7552976f37d..a433e91bf1064 100644 --- a/packages/react-is/src/ReactIs.js +++ b/packages/react-is/src/ReactIs.js @@ -24,9 +24,20 @@ import { REACT_SUSPENSE_TYPE, REACT_SUSPENSE_LIST_TYPE, REACT_VIEW_TRANSITION_TYPE, + REACT_SCOPE_TYPE, + REACT_LEGACY_HIDDEN_TYPE, + REACT_TRACING_MARKER_TYPE, } from 'shared/ReactSymbols'; -import isValidElementType from 'shared/isValidElementType'; -import {enableRenderableContext} from 'shared/ReactFeatureFlags'; + +import { + enableRenderableContext, + enableScopeAPI, + enableTransitionTracing, + enableLegacyHidden, + enableViewTransition, +} from 'shared/ReactFeatureFlags'; + +const REACT_CLIENT_REFERENCE: symbol = Symbol.for('react.client.reference'); export function typeOf(object: any): mixed { if (typeof object === 'object' && object !== null) { @@ -91,7 +102,47 @@ export const StrictMode = REACT_STRICT_MODE_TYPE; export const Suspense = REACT_SUSPENSE_TYPE; export const SuspenseList = REACT_SUSPENSE_LIST_TYPE; -export {isValidElementType}; +export function isValidElementType(type: mixed): boolean { + if (typeof type === 'string' || typeof type === 'function') { + return true; + } + + // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). + if ( + type === REACT_FRAGMENT_TYPE || + type === REACT_PROFILER_TYPE || + type === REACT_STRICT_MODE_TYPE || + type === REACT_SUSPENSE_TYPE || + type === REACT_SUSPENSE_LIST_TYPE || + (enableLegacyHidden && type === REACT_LEGACY_HIDDEN_TYPE) || + (enableScopeAPI && type === REACT_SCOPE_TYPE) || + (enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE) || + (enableViewTransition && type === REACT_VIEW_TRANSITION_TYPE) + ) { + return true; + } + + if (typeof type === 'object' && type !== null) { + if ( + type.$$typeof === REACT_LAZY_TYPE || + type.$$typeof === REACT_MEMO_TYPE || + type.$$typeof === REACT_CONTEXT_TYPE || + (!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) || + (enableRenderableContext && type.$$typeof === REACT_CONSUMER_TYPE) || + type.$$typeof === REACT_FORWARD_REF_TYPE || + // This needs to include all possible module reference object + // types supported by any Flight configuration anywhere since + // we don't know which Flight build this will end up being used + // with. + type.$$typeof === REACT_CLIENT_REFERENCE || + type.getModuleId !== undefined + ) { + return true; + } + } + + return false; +} export function isContextConsumer(object: any): boolean { if (enableRenderableContext) { diff --git a/packages/react/src/ReactMemo.js b/packages/react/src/ReactMemo.js index 0149712b054c5..8f2c0f53827c9 100644 --- a/packages/react/src/ReactMemo.js +++ b/packages/react/src/ReactMemo.js @@ -9,14 +9,12 @@ import {REACT_MEMO_TYPE} from 'shared/ReactSymbols'; -import isValidElementType from 'shared/isValidElementType'; - export function memo<Props>( type: React$ElementType, compare?: (oldProps: Props, newProps: Props) => boolean, ) { if (__DEV__) { - if (!isValidElementType(type)) { + if (type == null) { console.error( 'memo: The first argument must be a component. Instead ' + 'received: %s', diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js deleted file mode 100644 index 84e4fe802a930..0000000000000 --- a/packages/shared/isValidElementType.js +++ /dev/null @@ -1,81 +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. - * - * @flow - */ - -import { - REACT_CONTEXT_TYPE, - REACT_CONSUMER_TYPE, - REACT_PROVIDER_TYPE, - REACT_FORWARD_REF_TYPE, - REACT_FRAGMENT_TYPE, - REACT_PROFILER_TYPE, - REACT_STRICT_MODE_TYPE, - REACT_SUSPENSE_TYPE, - REACT_SUSPENSE_LIST_TYPE, - REACT_MEMO_TYPE, - REACT_LAZY_TYPE, - REACT_SCOPE_TYPE, - REACT_LEGACY_HIDDEN_TYPE, - REACT_TRACING_MARKER_TYPE, - REACT_VIEW_TRANSITION_TYPE, - REACT_ACTIVITY_TYPE, -} from 'shared/ReactSymbols'; -import { - enableScopeAPI, - enableTransitionTracing, - enableLegacyHidden, - enableRenderableContext, - enableViewTransition, -} from './ReactFeatureFlags'; - -const REACT_CLIENT_REFERENCE: symbol = Symbol.for('react.client.reference'); - -// This function is deprecated. Don't use. Only the renderer knows what a valid type is. -// TODO: Delete this now that owner stacks shipped. -export default function isValidElementType(type: mixed): boolean { - if (typeof type === 'string' || typeof type === 'function') { - return true; - } - - // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). - if ( - type === REACT_FRAGMENT_TYPE || - type === REACT_PROFILER_TYPE || - type === REACT_STRICT_MODE_TYPE || - type === REACT_SUSPENSE_TYPE || - type === REACT_SUSPENSE_LIST_TYPE || - (enableLegacyHidden && type === REACT_LEGACY_HIDDEN_TYPE) || - type === REACT_ACTIVITY_TYPE || - (enableScopeAPI && type === REACT_SCOPE_TYPE) || - (enableTransitionTracing && type === REACT_TRACING_MARKER_TYPE) || - (enableViewTransition && type === REACT_VIEW_TRANSITION_TYPE) - ) { - return true; - } - - if (typeof type === 'object' && type !== null) { - if ( - type.$$typeof === REACT_LAZY_TYPE || - type.$$typeof === REACT_MEMO_TYPE || - type.$$typeof === REACT_CONTEXT_TYPE || - (!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) || - (enableRenderableContext && type.$$typeof === REACT_CONSUMER_TYPE) || - type.$$typeof === REACT_FORWARD_REF_TYPE || - // This needs to include all possible module reference object - // types supported by any Flight configuration anywhere since - // we don't know which Flight build this will end up being used - // with. - type.$$typeof === REACT_CLIENT_REFERENCE || - type.getModuleId !== undefined - ) { - return true; - } - } - - return false; -} From 74bcf3d0d2c34595e33838149d1d5a2ea7d783c6 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:02:39 -0400 Subject: [PATCH 184/300] [ci] Don't fail on cache miss (#32690) Partially reverts #32686. PR caches inherit from caches generated in `main`. If it cannot find that cache, it will create one scoped to just that PR (and PRs that inherit from it). There is an edge case where cache eviction can happen in the middle of a test run. If cache eviction removes a `main` cache, child jobs that depend on it will start failing because of the `fail-on-cache-miss` setting. This PR reverts the default behavior. If this happens, the workflow will still continue in slow mode where it will `yarn install` child jobs instead of reusing from cache. This is slower but will at least allow workflows to continue. Additionally I added restore keys so that we can fallback to other caches if present so `yarn install` doesn't need to start over from scratch. --- .github/workflows/runtime_build_and_test.yml | 41 ++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 165ddac43cd29..8d9dc7487e4f6 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -147,7 +147,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -175,7 +177,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -205,7 +209,6 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -264,7 +267,9 @@ jobs: path: | **/node_modules key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-and-compiler-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -306,7 +311,9 @@ jobs: path: | **/node_modules key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-and-compiler-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -388,7 +395,9 @@ jobs: path: | **/node_modules key: runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-and-compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-and-compiler-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -425,7 +434,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -476,7 +487,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -514,7 +527,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -664,7 +679,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile @@ -719,7 +736,9 @@ jobs: path: | **/node_modules key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }} - fail-on-cache-miss: true + restore-keys: | + runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}- + runtime-node_modules-v6- - name: Ensure clean build directory run: rm -rf build - run: yarn install --frozen-lockfile From addce2f9f222befc6151251a247247a8463052fa Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:17:44 -0400 Subject: [PATCH 185/300] [ci] Add daily stale branch cache cleanup (#32691) Cleans up stale non-main caches daily --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32691). * #32692 * __->__ #32691 --- .../shared_cleanup_stale_branch_caches.yml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/shared_cleanup_stale_branch_caches.yml diff --git a/.github/workflows/shared_cleanup_stale_branch_caches.yml b/.github/workflows/shared_cleanup_stale_branch_caches.yml new file mode 100644 index 0000000000000..e480a8ae10263 --- /dev/null +++ b/.github/workflows/shared_cleanup_stale_branch_caches.yml @@ -0,0 +1,33 @@ +# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy + +name: (Shared) Cleanup Stale Branch Caches +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: + +jobs: + cleanup: + runs-on: ubuntu-latest + permissions: + # `actions:write` permission is required to delete caches + # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id + actions: write + contents: read + steps: + - name: Cleanup + run: | + echo "Fetching list of cache keys" + cacheKeysForPR=$(gh cache list --limit 100 --json id,ref --jq '.[] | select(.ref != "refs/heads/main") | .id') + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + for cacheKey in $cacheKeysForPR + do + gh cache delete $cacheKey + echo "Deleting $cacheKey" + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} From b88898605427d1e6bb665d7c9261a9be180e5abd Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:19:47 -0400 Subject: [PATCH 186/300] [ci] Rename other stale branch workflow (#32692) Makes it easier to tell what is what --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32692). * __->__ #32692 * #32691 --- ...ranch_caches.yml => shared_cleanup_merged_branch_caches.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{shared_cleanup_branch_caches.yml => shared_cleanup_merged_branch_caches.yml} (96%) diff --git a/.github/workflows/shared_cleanup_branch_caches.yml b/.github/workflows/shared_cleanup_merged_branch_caches.yml similarity index 96% rename from .github/workflows/shared_cleanup_branch_caches.yml rename to .github/workflows/shared_cleanup_merged_branch_caches.yml index 7c6a1d5e3a48f..730eda3623fe6 100644 --- a/.github/workflows/shared_cleanup_branch_caches.yml +++ b/.github/workflows/shared_cleanup_merged_branch_caches.yml @@ -1,6 +1,6 @@ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy -name: (Shared) Cleanup Branch Caches +name: (Shared) Cleanup Merged Branch Caches on: pull_request: types: From 0962f684a066df4fd2a7db7489cb1984799ad674 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:25:08 -0400 Subject: [PATCH 187/300] [compiler][bugfix] Don't insert hook guards in retry pipeline (#32665) Fixing bug from https://github.com/facebook/react/pull/32164 -- prior to this PR, we inserted hook guards even for functions that bailed out of compilation. --- .../src/Entrypoint/Program.ts | 6 +++ .../ReactiveScopes/CodegenReactiveFunction.ts | 22 +++++---- ...ro-dont-add-hook-guards-on-retry.expect.md | 27 ++++++++++ .../repro-dont-add-hook-guards-on-retry.js | 6 +++ ...ro-dont-add-hook-guards-on-retry.expect.md | 49 +++++++++++++++++++ .../repro-dont-add-hook-guards-on-retry.js | 15 ++++++ 6 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 72fa101260df5..4faa35d18f747 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -451,6 +451,12 @@ export function compileProgram( pass.code, ), }; + if ( + !compileResult.compiledFn.hasFireRewrite && + !compileResult.compiledFn.hasLoweredContextAccess + ) { + return null; + } } catch (err) { // TODO: we might want to log error here, but this will also result in duplicate logging if (err instanceof CompilerError) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index f8a2bc4989c12..ce535a9b38667 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -14,7 +14,7 @@ import { renameVariables, } from '.'; import {CompilerError, ErrorSeverity} from '../CompilerError'; -import {Environment, EnvironmentConfig, ExternalFunction} from '../HIR'; +import {Environment, ExternalFunction} from '../HIR'; import { ArrayPattern, BlockId, @@ -156,7 +156,7 @@ export function codegenFunction( const compiled = compileResult.unwrap(); const hookGuard = fn.env.config.enableEmitHookGuards; - if (hookGuard != null) { + if (hookGuard != null && fn.env.isInferredMemoEnabled) { compiled.body = t.blockStatement([ createHookGuard( hookGuard, @@ -250,7 +250,11 @@ export function codegenFunction( } const emitInstrumentForget = fn.env.config.enableEmitInstrumentForget; - if (emitInstrumentForget != null && fn.id != null) { + if ( + emitInstrumentForget != null && + fn.id != null && + fn.env.isInferredMemoEnabled + ) { /* * Technically, this is a conditional hook call. However, we expect * __DEV__ and gating identifier to be runtime constants @@ -548,7 +552,7 @@ function codegenBlockNoReset( } function wrapCacheDep(cx: Context, value: t.Expression): t.Expression { - if (cx.env.config.enableEmitFreeze != null) { + if (cx.env.config.enableEmitFreeze != null && cx.env.isInferredMemoEnabled) { // The import declaration for emitFreeze is inserted in the Babel plugin return t.conditionalExpression( t.identifier('__DEV__'), @@ -1553,7 +1557,7 @@ function createHookGuard( * ``` */ function createCallExpression( - config: EnvironmentConfig, + env: Environment, callee: t.Expression, args: Array<t.Expression | t.SpreadElement>, loc: SourceLocation | null, @@ -1564,8 +1568,8 @@ function createCallExpression( callExpr.loc = loc; } - const hookGuard = config.enableEmitHookGuards; - if (hookGuard != null && isHook) { + const hookGuard = env.config.enableEmitHookGuards; + if (hookGuard != null && isHook && env.isInferredMemoEnabled) { const iife = t.functionExpression( null, [], @@ -1701,7 +1705,7 @@ function codegenInstructionValue( const callee = codegenPlaceToExpression(cx, instrValue.callee); const args = instrValue.args.map(arg => codegenArgument(cx, arg)); value = createCallExpression( - cx.env.config, + cx.env, callee, args, instrValue.loc, @@ -1791,7 +1795,7 @@ function codegenInstructionValue( ); const args = instrValue.args.map(arg => codegenArgument(cx, arg)); value = createCallExpression( - cx.env.config, + cx.env, memberExpr, args, instrValue.loc, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.expect.md new file mode 100644 index 0000000000000..6477654d3d0ea --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @flow @enableEmitHookGuards @panicThreshold(none) @enableFire + +component Foo(useDynamicHook) { + useDynamicHook(); + return <div>hello world</div>; +} + +``` + +## Code + +```javascript +function Foo({ + useDynamicHook, +}: $ReadOnly<{ useDynamicHook: any }>): React.Node { + useDynamicHook(); + return <div>hello world</div>; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.js new file mode 100644 index 0000000000000..d0313aa42fdb4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-dont-add-hook-guards-on-retry.js @@ -0,0 +1,6 @@ +// @flow @enableEmitHookGuards @panicThreshold(none) @enableFire + +component Foo(useDynamicHook) { + useDynamicHook(); + return <div>hello world</div>; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md new file mode 100644 index 0000000000000..5a27845f079e3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md @@ -0,0 +1,49 @@ + +## Input + +```javascript +// @flow @enableEmitHookGuards @panicThreshold(none) @enableFire +import {useEffect, fire} from 'react'; + +function Component(props, useDynamicHook) { + 'use memo'; + useDynamicHook(); + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return <div>hello world</div>; +} + +``` + +## Code + +```javascript +import { $dispatcherGuard } from "react-compiler-runtime"; +import { useFire } from "react/compiler-runtime"; +import { useEffect, fire } from "react"; + +function Component(props, useDynamicHook) { + "use memo"; + + useDynamicHook(); + const foo = _temp; + const t0 = useFire(foo); + + useEffect(() => { + t0(props); + }); + return <div>hello world</div>; +} +function _temp(props_0) { + console.log(props_0); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.js new file mode 100644 index 0000000000000..077982e8d48fd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.js @@ -0,0 +1,15 @@ +// @flow @enableEmitHookGuards @panicThreshold(none) @enableFire +import {useEffect, fire} from 'react'; + +function Component(props, useDynamicHook) { + 'use memo'; + useDynamicHook(); + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return <div>hello world</div>; +} From ac799e569d5899c67d72a3e9af5c18b0672eb998 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:49:36 -0400 Subject: [PATCH 188/300] [ci] Bump number of shards for test_build to 10 (#32693) I noticed `test_build` can take a while so let's bump the number of shards --- .github/workflows/runtime_build_and_test.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 8d9dc7487e4f6..3fc608e01c7a1 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -373,9 +373,16 @@ jobs: # TODO: Test more persistent configurations? ] shard: - - 1/3 - - 2/3 - - 3/3 + - 1/10 + - 2/10 + - 3/10 + - 4/10 + - 5/10 + - 6/10 + - 7/10 + - 8/10 + - 9/10 + - 10/10 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From e1e740717ba85597f03fd837a36c7bab5803a0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= <sebastian@calyptus.eu> Date: Fri, 21 Mar 2025 10:05:31 -0400 Subject: [PATCH 189/300] Force layout before startViewTransition (#32699) This works around this Safari bug. https://bugs.webkit.org/show_bug.cgi?id=290146 This unfortunate because it may cause additional layouts if there's more updates to the tree coming by manual mutation before it gets painted naturally. However, we might end up wanting to read layout early anyway. This affects the fixture because we clone the `<link>` from the `<head>` which is itself another bug. However, it should be possible to have `<link>` tags inserted into the new tree so this is still relevant. --- .../src/client/ReactFiberConfigDOM.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 6efcf65091c73..27a4a9167f29b 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -1669,6 +1669,12 @@ function customizeViewTransitionError( return error; } +/** @noinline */ +function forceLayout(ownerDocument: Document) { + // This function exists to trick minifiers to not remove this unused member expression. + return (ownerDocument.documentElement: any).clientHeight; +} + export function startViewTransition( rootContainer: Container, transitionTypes: null | TransitionTypes, @@ -1698,8 +1704,7 @@ export function startViewTransition( mutationCallback(); if (previousFontLoadingStatus === 'loaded') { // Force layout calculation to trigger font loading. - // eslint-disable-next-line ft-flow/no-unused-expressions - (ownerDocument.documentElement: any).clientHeight; + forceLayout(ownerDocument); if ( // $FlowFixMe[prop-missing] ownerDocument.fonts.status === 'loading' @@ -1898,6 +1903,10 @@ export function startGestureTransition( ? (rootContainer: any) : rootContainer.ownerDocument; try { + // Force layout before we start the Transition. This works around a bug in Safari + // if one of the clones end up being a stylesheet that isn't loaded or uncached. + // https://bugs.webkit.org/show_bug.cgi?id=290146 + forceLayout(ownerDocument); // $FlowFixMe[prop-missing] const transition = ownerDocument.startViewTransition({ update: mutationCallback, From 607615f4f6b399c314a567fdbf3ab53fa572991d Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:40:34 -0400 Subject: [PATCH 190/300] [ci] Scope permissions for runtime_commit_artifacts.yml (#32701) --- .../workflows/runtime_commit_artifacts.yml | 98 ++++++++++++------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index b44d72730a200..fef8e8be71d04 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -22,6 +22,8 @@ on: default: false type: boolean +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout @@ -30,6 +32,40 @@ env: jobs: download_artifacts: runs-on: ubuntu-latest + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read + steps: + - uses: actions/checkout@v4 + - name: Restore cached node_modules + uses: actions/cache@v4 + id: node_modules + with: + path: | + **/node_modules + key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} + - name: Ensure clean build directory + run: rm -rf build + - run: yarn install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' + - run: yarn --cwd scripts/release install --frozen-lockfile + if: steps.node_modules.outputs.cache-hit != 'true' + - name: Download artifacts for base revision + run: | + GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} + - name: Display structure of build + run: ls -R build + - name: Archive build + uses: actions/upload-artifact@v4 + with: + name: build + path: build/ + if-no-files-found: error + + + process_artifacts: + runs-on: ubuntu-latest + needs: [download_artifacts] outputs: www_branch_count: ${{ steps.check_branches.outputs.www_branch_count }} fbsource_branch_count: ${{ steps.check_branches.outputs.fbsource_branch_count }} @@ -69,25 +105,11 @@ jobs: run: | echo "www_branch_count=$(git ls-remote --heads origin "refs/heads/meta-www" | wc -l)" >> "$GITHUB_OUTPUT" echo "fbsource_branch_count=$(git ls-remote --heads origin "refs/heads/meta-fbsource" | wc -l)" >> "$GITHUB_OUTPUT" - - uses: actions/setup-node@v4 + - name: Restore downloaded build + uses: actions/download-artifact@v4 with: - node-version-file: '.nvmrc' - cache: yarn - cache-dependency-path: yarn.lock - - name: Restore cached node_modules - uses: actions/cache@v4 - id: node_modules - with: - path: | - **/node_modules - key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }} - - name: Ensure clean build directory - run: rm -rf build - - run: yarn install --frozen-lockfile - - run: yarn --cwd scripts/release install --frozen-lockfile - - name: Download artifacts for base revision - run: | - GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} + name: build + path: build - name: Display structure of build run: ls -R build - name: Strip @license from eslint plugin and react-refresh @@ -178,8 +200,8 @@ jobs: if-no-files-found: error commit_www_artifacts: - needs: download_artifacts - if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.download_artifacts.outputs.www_branch_count == '0') + needs: [download_artifacts, process_artifacts] + if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.process_artifacts.outputs.www_branch_count == '0') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -192,12 +214,12 @@ jobs: name: compiled path: compiled/ - name: Revert version changes - if: needs.download_artifacts.outputs.last_version_classic != '' && needs.download_artifacts.outputs.last_version_modern != '' + if: needs.process_artifacts.outputs.last_version_classic != '' && needs.process_artifacts.outputs.last_version_modern != '' env: - CURRENT_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.current_version_classic }} - CURRENT_VERSION_MODERN: ${{ needs.download_artifacts.outputs.current_version_modern }} - LAST_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.last_version_classic }} - LAST_VERSION_MODERN: ${{ needs.download_artifacts.outputs.last_version_modern }} + CURRENT_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.current_version_classic }} + CURRENT_VERSION_MODERN: ${{ needs.process_artifacts.outputs.current_version_modern }} + LAST_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.last_version_classic }} + LAST_VERSION_MODERN: ${{ needs.process_artifacts.outputs.last_version_modern }} run: | echo "Reverting $CURRENT_VERSION_CLASSIC to $LAST_VERSION_CLASSIC" grep -rl "$CURRENT_VERSION_CLASSIC" ./compiled || echo "No files found with $CURRENT_VERSION_CLASSIC" @@ -227,12 +249,12 @@ jobs: echo "should_commit=false" >> "$GITHUB_OUTPUT" fi - name: Re-apply version changes - if: inputs.force == true || (steps.check_should_commit.outputs.should_commit == 'true' && needs.download_artifacts.outputs.last_version_classic != '' && needs.download_artifacts.outputs.last_version_modern != '') + if: inputs.force == true || (steps.check_should_commit.outputs.should_commit == 'true' && needs.process_artifacts.outputs.last_version_classic != '' && needs.process_artifacts.outputs.last_version_modern != '') env: - CURRENT_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.current_version_classic }} - CURRENT_VERSION_MODERN: ${{ needs.download_artifacts.outputs.current_version_modern }} - LAST_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.last_version_classic }} - LAST_VERSION_MODERN: ${{ needs.download_artifacts.outputs.last_version_modern }} + CURRENT_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.current_version_classic }} + CURRENT_VERSION_MODERN: ${{ needs.process_artifacts.outputs.current_version_modern }} + LAST_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.last_version_classic }} + LAST_VERSION_MODERN: ${{ needs.process_artifacts.outputs.last_version_modern }} run: | echo "Re-applying $LAST_VERSION_CLASSIC to $CURRENT_VERSION_CLASSIC" grep -rl "$LAST_VERSION_CLASSIC" ./compiled || echo "No files found with $LAST_VERSION_CLASSIC" @@ -266,8 +288,8 @@ jobs: run: git push commit_fbsource_artifacts: - needs: download_artifacts - if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.download_artifacts.outputs.fbsource_branch_count == '0') + needs: [download_artifacts, process_artifacts] + if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.process_artifacts.outputs.fbsource_branch_count == '0') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -280,10 +302,10 @@ jobs: name: compiled-rn path: compiled-rn/ - name: Revert version changes - if: needs.download_artifacts.outputs.last_version_rn != '' + if: needs.process_artifacts.outputs.last_version_rn != '' env: - CURRENT_VERSION: ${{ needs.download_artifacts.outputs.current_version_rn }} - LAST_VERSION: ${{ needs.download_artifacts.outputs.last_version_rn }} + CURRENT_VERSION: ${{ needs.process_artifacts.outputs.current_version_rn }} + LAST_VERSION: ${{ needs.process_artifacts.outputs.last_version_rn }} run: | echo "Reverting $CURRENT_VERSION to $LAST_VERSION" grep -rl "$CURRENT_VERSION" ./compiled-rn || echo "No files found with $CURRENT_VERSION" @@ -309,10 +331,10 @@ jobs: echo "should_commit=false" >> "$GITHUB_OUTPUT" fi - name: Re-apply version changes - if: inputs.force == true || (steps.check_should_commit.outputs.should_commit == 'true' && needs.download_artifacts.outputs.last_version_rn != '') + if: inputs.force == true || (steps.check_should_commit.outputs.should_commit == 'true' && needs.process_artifacts.outputs.last_version_rn != '') env: - CURRENT_VERSION: ${{ needs.download_artifacts.outputs.current_version_rn }} - LAST_VERSION: ${{ needs.download_artifacts.outputs.last_version_rn }} + CURRENT_VERSION: ${{ needs.process_artifacts.outputs.current_version_rn }} + LAST_VERSION: ${{ needs.process_artifacts.outputs.last_version_rn }} run: | echo "Re-applying $LAST_VERSION to $CURRENT_VERSION" grep -rl "$LAST_VERSION" ./compiled-rn || echo "No files found with $LAST_VERSION" From ab693a926f13cc2c4c4a3ab19d5405471f87236b Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:40:55 -0400 Subject: [PATCH 191/300] [ci] Scope permissions for all workflows (#32704) --- .github/workflows/compiler_discord_notify.yml | 2 ++ .github/workflows/compiler_playground.yml | 2 ++ .github/workflows/compiler_prereleases.yml | 3 ++- .github/workflows/compiler_prereleases_manual.yml | 2 ++ .github/workflows/compiler_prereleases_nightly.yml | 2 ++ .github/workflows/compiler_prereleases_weekly.yml | 2 ++ .github/workflows/compiler_typescript.yml | 2 ++ .github/workflows/devtools_regression_tests.yml | 5 +++++ .github/workflows/runtime_build_and_test.yml | 5 +++++ .github/workflows/runtime_discord_notify.yml | 2 ++ .github/workflows/runtime_eslint_plugin_e2e.yml | 2 ++ .github/workflows/runtime_fuzz_tests.yml | 2 ++ .github/workflows/runtime_prereleases.yml | 3 ++- .github/workflows/runtime_prereleases_manual.yml | 2 ++ .github/workflows/runtime_prereleases_nightly.yml | 2 ++ .github/workflows/runtime_releases_from_npm_manual.yml | 3 ++- .github/workflows/shared_check_maintainer.yml | 5 +++++ .github/workflows/shared_cleanup_merged_branch_caches.yml | 2 ++ .github/workflows/shared_cleanup_stale_branch_caches.yml | 2 ++ .github/workflows/shared_close_direct_sync_branch_prs.yml | 2 ++ .github/workflows/shared_label_core_team_prs.yml | 2 ++ .github/workflows/shared_lint.yml | 2 ++ .github/workflows/shared_stale.yml | 2 ++ 23 files changed, 55 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index 5b46d0f87601f..ca7feaae50791 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -7,6 +7,8 @@ on: - compiler/** - .github/workflows/compiler_**.yml +permissions: {} + jobs: check_maintainer: uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml index 224c82e6de7c3..edd95e365e868 100644 --- a/.github/workflows/compiler_playground.yml +++ b/.github/workflows/compiler_playground.yml @@ -8,6 +8,8 @@ on: - compiler/** - .github/workflows/compiler_playground.yml +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/compiler_prereleases.yml b/.github/workflows/compiler_prereleases.yml index 5a3122d0ed80f..7928bd430ef49 100644 --- a/.github/workflows/compiler_prereleases.yml +++ b/.github/workflows/compiler_prereleases.yml @@ -20,11 +20,12 @@ on: NPM_TOKEN: required: true +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 - GH_TOKEN: ${{ github.token }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} defaults: diff --git a/.github/workflows/compiler_prereleases_manual.yml b/.github/workflows/compiler_prereleases_manual.yml index 3e42ae2cf200d..4960489590a4f 100644 --- a/.github/workflows/compiler_prereleases_manual.yml +++ b/.github/workflows/compiler_prereleases_manual.yml @@ -15,6 +15,8 @@ on: required: true type: string +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles diff --git a/.github/workflows/compiler_prereleases_nightly.yml b/.github/workflows/compiler_prereleases_nightly.yml index 82f893aa5ef43..07919d7843b25 100644 --- a/.github/workflows/compiler_prereleases_nightly.yml +++ b/.github/workflows/compiler_prereleases_nightly.yml @@ -5,6 +5,8 @@ on: # At 10 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri - cron: 10 16 * * 1,2,3,4,5 +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles diff --git a/.github/workflows/compiler_prereleases_weekly.yml b/.github/workflows/compiler_prereleases_weekly.yml index 79a9451b6972a..72af00d52117c 100644 --- a/.github/workflows/compiler_prereleases_weekly.yml +++ b/.github/workflows/compiler_prereleases_weekly.yml @@ -5,6 +5,8 @@ on: # At 10 minutes past 9:00 on Mon - cron: 10 9 * * 1 +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml index 1ce668de044e8..9c749a3bfbcdb 100644 --- a/.github/workflows/compiler_typescript.yml +++ b/.github/workflows/compiler_typescript.yml @@ -8,6 +8,8 @@ on: - compiler/** - .github/workflows/compiler_typescript.yml +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 399772cf832de..cb6a5b68a8041 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -9,6 +9,8 @@ on: required: false type: string +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout @@ -18,6 +20,9 @@ jobs: download_build: name: Download base build runs-on: ubuntu-latest + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 3fc608e01c7a1..343c32724e62f 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -7,6 +7,8 @@ on: paths-ignore: - compiler/** +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true @@ -768,6 +770,9 @@ jobs: if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }} name: Run sizebot needs: [build_and_lint] + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index f2be08f904ee2..c6da99646c671 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -7,6 +7,8 @@ on: - compiler/** - .github/workflows/compiler_**.yml +permissions: {} + jobs: check_maintainer: uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml index c75f998e8cbb6..9b3d134204576 100644 --- a/.github/workflows/runtime_eslint_plugin_e2e.yml +++ b/.github/workflows/runtime_eslint_plugin_e2e.yml @@ -7,6 +7,8 @@ on: paths-ignore: - compiler/** +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/runtime_fuzz_tests.yml b/.github/workflows/runtime_fuzz_tests.yml index 66ddba318fa0a..a88ce523a62f6 100644 --- a/.github/workflows/runtime_fuzz_tests.yml +++ b/.github/workflows/runtime_fuzz_tests.yml @@ -8,6 +8,8 @@ on: - main workflow_dispatch: +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 4e1f8c21cab72..147ec0a496dc8 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -17,11 +17,12 @@ on: NPM_TOKEN: required: true +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 - GH_TOKEN: ${{ github.token }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} jobs: diff --git a/.github/workflows/runtime_prereleases_manual.yml b/.github/workflows/runtime_prereleases_manual.yml index 4c25ddc79bd35..77d3fd5e4304b 100644 --- a/.github/workflows/runtime_prereleases_manual.yml +++ b/.github/workflows/runtime_prereleases_manual.yml @@ -6,6 +6,8 @@ on: prerelease_commit_sha: required: true +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles diff --git a/.github/workflows/runtime_prereleases_nightly.yml b/.github/workflows/runtime_prereleases_nightly.yml index fe038042f332c..4622e15f55ad5 100644 --- a/.github/workflows/runtime_prereleases_nightly.yml +++ b/.github/workflows/runtime_prereleases_nightly.yml @@ -5,6 +5,8 @@ on: # At 10 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri - cron: 10 16 * * 1,2,3,4,5 +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml index 58972d8a88321..4bc3957486616 100644 --- a/.github/workflows/runtime_releases_from_npm_manual.yml +++ b/.github/workflows/runtime_releases_from_npm_manual.yml @@ -31,11 +31,12 @@ on: type: boolean default: false +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 - GH_TOKEN: ${{ github.token }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} jobs: diff --git a/.github/workflows/shared_check_maintainer.yml b/.github/workflows/shared_check_maintainer.yml index c20047d600321..3bc1ad1e23d9f 100644 --- a/.github/workflows/shared_check_maintainer.yml +++ b/.github/workflows/shared_check_maintainer.yml @@ -14,6 +14,8 @@ on: is_core_team: value: ${{ jobs.check_maintainer.outputs.is_core_team }} +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout @@ -22,6 +24,9 @@ env: jobs: check_maintainer: runs-on: ubuntu-latest + permissions: + # We fetch the contents of the MAINTAINERS file + contents: read outputs: is_core_team: ${{ steps.check_if_actor_is_maintainer.outputs.result }} steps: diff --git a/.github/workflows/shared_cleanup_merged_branch_caches.yml b/.github/workflows/shared_cleanup_merged_branch_caches.yml index 730eda3623fe6..ed80a505e4281 100644 --- a/.github/workflows/shared_cleanup_merged_branch_caches.yml +++ b/.github/workflows/shared_cleanup_merged_branch_caches.yml @@ -11,6 +11,8 @@ on: required: true type: string +permissions: {} + jobs: cleanup: runs-on: ubuntu-latest diff --git a/.github/workflows/shared_cleanup_stale_branch_caches.yml b/.github/workflows/shared_cleanup_stale_branch_caches.yml index e480a8ae10263..a6d50a79920c3 100644 --- a/.github/workflows/shared_cleanup_stale_branch_caches.yml +++ b/.github/workflows/shared_cleanup_stale_branch_caches.yml @@ -6,6 +6,8 @@ on: - cron: 0 0 * * * workflow_dispatch: +permissions: {} + jobs: cleanup: runs-on: ubuntu-latest diff --git a/.github/workflows/shared_close_direct_sync_branch_prs.yml b/.github/workflows/shared_close_direct_sync_branch_prs.yml index 7575c0e913c7e..abf8db919f7b8 100644 --- a/.github/workflows/shared_close_direct_sync_branch_prs.yml +++ b/.github/workflows/shared_close_direct_sync_branch_prs.yml @@ -5,6 +5,8 @@ on: branches: - 'builds/facebook-**' +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml index dc432b54f72a6..9b9e6149ed701 100644 --- a/.github/workflows/shared_label_core_team_prs.yml +++ b/.github/workflows/shared_label_core_team_prs.yml @@ -3,6 +3,8 @@ name: (Shared) Label Core Team PRs on: pull_request_target: +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml index f9d1e7972c7be..e14e9a252bd99 100644 --- a/.github/workflows/shared_lint.yml +++ b/.github/workflows/shared_lint.yml @@ -5,6 +5,8 @@ on: branches: [main] pull_request: +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/shared_stale.yml b/.github/workflows/shared_stale.yml index 8d505e856e940..a2c707973c927 100644 --- a/.github/workflows/shared_stale.yml +++ b/.github/workflows/shared_stale.yml @@ -6,6 +6,8 @@ on: - cron: '0 * * * *' workflow_dispatch: +permissions: {} + env: TZ: /usr/share/zoneinfo/America/Los_Angeles From daee08562ccf5abf7108b63f274f5ca669ee7dd5 Mon Sep 17 00:00:00 2001 From: Ricky <rickhanlonii@gmail.com> Date: Fri, 21 Mar 2025 14:44:02 -0400 Subject: [PATCH 192/300] [activity] remove ref for now (#32645) Followup from https://github.com/facebook/react/pull/32499 Manual mode is unused and has some bugs such as revealing hidden boundaries when manually toggling. We also want to change how manual mode works, and do some refactors to Activity to make it easier to support. For now we'll remove it, then add it back after the other changes we have planned. --- packages/react-reconciler/src/ReactFiber.js | 12 - .../src/ReactFiberActivityComponent.js | 17 +- .../src/ReactFiberBeginWork.js | 9 +- .../src/ReactFiberCommitWork.js | 88 +-- .../src/ReactFiberCompleteWork.js | 5 +- .../src/__tests__/Activity-test.js | 663 ------------------ packages/shared/ReactTypes.js | 3 +- 7 files changed, 8 insertions(+), 789 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 7f5fbe59b8e90..614df0831f662 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -110,10 +110,6 @@ import { REACT_ACTIVITY_TYPE, } from 'shared/ReactSymbols'; import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent'; -import { - detachOffscreenInstance, - attachOffscreenInstance, -} from './ReactFiberCommitWork'; import {getHostContext} from './ReactFiberHostContext'; import type {ReactComponentInfo} from '../../shared/ReactTypes'; import isArray from 'shared/isArray'; @@ -854,13 +850,9 @@ export function createFiberFromOffscreen( fiber.lanes = lanes; const primaryChildInstance: OffscreenInstance = { _visibility: OffscreenVisible, - _pendingVisibility: OffscreenVisible, _pendingMarkers: null, _retryCache: null, _transitions: null, - _current: null, - detach: () => detachOffscreenInstance(primaryChildInstance), - attach: () => attachOffscreenInstance(primaryChildInstance), }; fiber.stateNode = primaryChildInstance; return fiber; @@ -909,13 +901,9 @@ export function createFiberFromLegacyHidden( // the offscreen implementation, which depends on a state node const instance: OffscreenInstance = { _visibility: OffscreenVisible, - _pendingVisibility: OffscreenVisible, _pendingMarkers: null, _transitions: null, _retryCache: null, - _current: null, - detach: () => detachOffscreenInstance(instance), - attach: () => attachOffscreenInstance(instance), }; fiber.stateNode = instance; return fiber; diff --git a/packages/react-reconciler/src/ReactFiberActivityComponent.js b/packages/react-reconciler/src/ReactFiberActivityComponent.js index 48b5bc0e6d8ee..8d9dcad67e50b 100644 --- a/packages/react-reconciler/src/ReactFiberActivityComponent.js +++ b/packages/react-reconciler/src/ReactFiberActivityComponent.js @@ -10,7 +10,6 @@ import type {ReactNodeList, OffscreenMode, Wakeable} from 'shared/ReactTypes'; import type {Lanes} from './ReactFiberLane'; import type {SpawnedCachePool} from './ReactFiberCacheComponent'; -import type {Fiber} from './ReactInternalTypes'; import type { Transition, TracingMarkerInstance, @@ -47,25 +46,11 @@ export type OffscreenQueue = { type OffscreenVisibility = number; export const OffscreenVisible = /* */ 0b001; -export const OffscreenDetached = /* */ 0b010; -export const OffscreenPassiveEffectsConnected = /* */ 0b100; +export const OffscreenPassiveEffectsConnected = /* */ 0b010; export type OffscreenInstance = { - _pendingVisibility: OffscreenVisibility, _visibility: OffscreenVisibility, _pendingMarkers: Set<TracingMarkerInstance> | null, _transitions: Set<Transition> | null, _retryCache: WeakSet<Wakeable> | Set<Wakeable> | null, - - // Represents the current Offscreen fiber - _current: Fiber | null, - detach: () => void, - attach: () => void, }; - -export function isOffscreenManual(offscreenFiber: Fiber): boolean { - return ( - offscreenFiber.memoizedProps !== null && - offscreenFiber.memoizedProps.mode === 'manual' - ); -} diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 4636c08818d51..a5ff0d1757791 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -33,7 +33,6 @@ import type { ViewTransitionState, } from './ReactFiberViewTransitionComponent'; import {assignViewTransitionAutoName} from './ReactFiberViewTransitionComponent'; -import {OffscreenDetached} from './ReactFiberActivityComponent'; import type { Cache, CacheComponentState, @@ -647,19 +646,13 @@ function updateOffscreenComponent( ) { const nextProps: OffscreenProps = workInProgress.pendingProps; const nextChildren = nextProps.children; - const nextIsDetached = - (workInProgress.stateNode._pendingVisibility & OffscreenDetached) !== 0; const prevState: OffscreenState | null = current !== null ? current.memoizedState : null; - markRef(current, workInProgress); - if ( nextProps.mode === 'hidden' || - (enableLegacyHidden && - nextProps.mode === 'unstable-defer-without-hiding') || - nextIsDetached + (enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding') ) { // Rendering a hidden tree. diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 63bf7b7eb9b15..e73b3fa8fb925 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -18,20 +18,15 @@ import type { } from './ReactFiberConfig'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane'; -import { - includesOnlyViewTransitionEligibleLanes, - SyncLane, -} from './ReactFiberLane'; +import {includesOnlyViewTransitionEligibleLanes} from './ReactFiberLane'; import type {SuspenseState, RetryQueue} from './ReactFiberSuspenseComponent'; import type {UpdateQueue} from './ReactFiberClassUpdateQueue'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks'; import type {Wakeable} from 'shared/ReactTypes'; -import {isOffscreenManual} from './ReactFiberActivityComponent'; import type { OffscreenState, OffscreenInstance, OffscreenQueue, - OffscreenProps, } from './ReactFiberActivityComponent'; import type {Cache} from './ReactFiberCacheComponent'; import type {RootState} from './ReactFiberRoot'; @@ -194,15 +189,12 @@ import {releaseCache, retainCache} from './ReactFiberCacheComponent'; import {clearTransitionsForLanes} from './ReactFiberLane'; import { OffscreenVisible, - OffscreenDetached, OffscreenPassiveEffectsConnected, } from './ReactFiberActivityComponent'; import { TransitionRoot, TransitionTracingMarker, } from './ReactFiberTracingMarkerComponent'; -import {scheduleUpdateOnFiber} from './ReactFiberWorkLoop'; -import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates'; import { commitHookLayoutEffects, commitHookLayoutUnmountEffects, @@ -742,14 +734,6 @@ function commitLayoutEffectOnFiber( committedLanes, ); } - if (flags & Ref) { - const props: OffscreenProps = finishedWork.memoizedProps; - if (props.mode === 'manual') { - safelyAttachRef(finishedWork, finishedWork.return); - } else { - safelyDetachRef(finishedWork, finishedWork.return); - } - } break; } case ViewTransitionComponent: { @@ -1538,9 +1522,6 @@ function commitDeletionEffectsOnFiber( return; } case OffscreenComponent: { - if (!offscreenSubtreeWasHidden) { - safelyDetachRef(deletedFiber, nearestMountedAncestor); - } if (disableLegacyMode || deletedFiber.mode & ConcurrentMode) { // If this offscreen component is hidden, we already unmounted it. Before // deleting the children, track that it's already unmounted so that we @@ -1672,48 +1653,6 @@ function getRetryCache(finishedWork: Fiber) { } } -export function detachOffscreenInstance(instance: OffscreenInstance): void { - const fiber = instance._current; - if (fiber === null) { - throw new Error( - 'Calling Offscreen.detach before instance handle has been set.', - ); - } - - if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) { - // The instance is already detached, this is a noop. - return; - } - - // TODO: There is an opportunity to optimise this by not entering commit phase - // and unmounting effects directly. - const root = enqueueConcurrentRenderForLane(fiber, SyncLane); - if (root !== null) { - instance._pendingVisibility |= OffscreenDetached; - scheduleUpdateOnFiber(root, fiber, SyncLane); - } -} - -export function attachOffscreenInstance(instance: OffscreenInstance): void { - const fiber = instance._current; - if (fiber === null) { - throw new Error( - 'Calling Offscreen.detach before instance handle has been set.', - ); - } - - if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) { - // The instance is already attached, this is a noop. - return; - } - - const root = enqueueConcurrentRenderForLane(fiber, SyncLane); - if (root !== null) { - instance._pendingVisibility &= ~OffscreenDetached; - scheduleUpdateOnFiber(root, fiber, SyncLane); - } -} - function attachSuspenseRetryListeners( finishedWork: Fiber, wakeables: RetryQueue, @@ -2181,12 +2120,6 @@ function commitMutationEffectsOnFiber( break; } case OffscreenComponent: { - if (flags & Ref) { - if (!offscreenSubtreeWasHidden && current !== null) { - safelyDetachRef(current, current.return); - } - } - const newState: OffscreenState | null = finishedWork.memoizedState; const isHidden = newState !== null; const wasHidden = current !== null && current.memoizedState !== null; @@ -2208,18 +2141,9 @@ function commitMutationEffectsOnFiber( commitReconciliationEffects(finishedWork, lanes); - const offscreenInstance: OffscreenInstance = finishedWork.stateNode; - - // TODO: Add explicit effect flag to set _current. - offscreenInstance._current = finishedWork; - - // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is - // to support batching of `attach` and `detach` calls. - offscreenInstance._visibility &= ~OffscreenDetached; - offscreenInstance._visibility |= - offscreenInstance._pendingVisibility & OffscreenDetached; - if (flags & Visibility) { + const offscreenInstance: OffscreenInstance = finishedWork.stateNode; + // Track the current state on the Offscreen instance so we can // read it during an event if (isHidden) { @@ -2250,8 +2174,7 @@ function commitMutationEffectsOnFiber( } } - // Offscreen with manual mode manages visibility manually. - if (supportsMutation && !isOffscreenManual(finishedWork)) { + if (supportsMutation) { // TODO: This needs to run whenever there's an insertion or update // inside a hidden Offscreen tree. hideOrUnhideAllChildren(finishedWork, isHidden); @@ -2667,9 +2590,6 @@ export function disappearLayoutEffects(finishedWork: Fiber) { break; } case OffscreenComponent: { - // TODO (Offscreen) Check: flags & RefStatic - safelyDetachRef(finishedWork, finishedWork.return); - const isHidden = finishedWork.memoizedState !== null; if (isHidden) { // Nested Offscreen tree is already hidden. Don't disappear diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index ce112782757d3..663523dc952de 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -28,7 +28,6 @@ import type { OffscreenState, OffscreenQueue, } from './ReactFiberActivityComponent'; -import {isOffscreenManual} from './ReactFiberActivityComponent'; import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent'; import type {Cache} from './ReactFiberCacheComponent'; import { @@ -384,12 +383,10 @@ function appendAllChildrenToContainer( if (child !== null) { child.return = node; } - // If Offscreen is not in manual mode, detached tree is hidden from user space. - const _needsVisibilityToggle = !isOffscreenManual(node); appendAllChildrenToContainer( containerChildSet, node, - /* needsVisibilityToggle */ _needsVisibilityToggle, + /* needsVisibilityToggle */ true, /* isHidden */ true, ); diff --git a/packages/react-reconciler/src/__tests__/Activity-test.js b/packages/react-reconciler/src/__tests__/Activity-test.js index 191e72e3cb14d..4655d4917d6a3 100644 --- a/packages/react-reconciler/src/__tests__/Activity-test.js +++ b/packages/react-reconciler/src/__tests__/Activity-test.js @@ -9,7 +9,6 @@ let useLayoutEffect; let useEffect; let useInsertionEffect; let useMemo; -let useRef; let startTransition; let waitForPaint; let waitFor; @@ -31,7 +30,6 @@ describe('Activity', () => { useLayoutEffect = React.useLayoutEffect; useEffect = React.useEffect; useMemo = React.useMemo; - useRef = React.useRef; startTransition = React.startTransition; const InternalTestUtils = require('internal-test-utils'); @@ -46,30 +44,6 @@ describe('Activity', () => { return <span prop={props.text}>{props.children}</span>; } - function LoggedText({text, children}) { - useInsertionEffect(() => { - Scheduler.log(`mount insertion ${text}`); - return () => { - Scheduler.log(`unmount insertion ${text}`); - }; - }); - - useEffect(() => { - Scheduler.log(`mount ${text}`); - return () => { - Scheduler.log(`unmount ${text}`); - }; - }); - - useLayoutEffect(() => { - Scheduler.log(`mount layout ${text}`); - return () => { - Scheduler.log(`unmount layout ${text}`); - }; - }); - return <Text text={text}>{children}</Text>; - } - // @gate enableLegacyHidden it('unstable-defer-without-hiding should never toggle the visibility of its children', async () => { function App({mode}) { @@ -1506,641 +1480,4 @@ describe('Activity', () => { assertLog([]); expect(root).toMatchRenderedOutput(<span prop={2} />); }); - - describe('manual interactivity', () => { - // @gate enableActivity - it('should attach ref only for mode null', async () => { - let offscreenRef; - - function App({mode}) { - offscreenRef = useRef(null); - return ( - <Activity - mode={mode} - ref={ref => { - offscreenRef.current = ref; - }}> - <div /> - </Activity> - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App mode={'manual'} />); - }); - - expect(offscreenRef.current).not.toBeNull(); - - await act(() => { - root.render(<App mode={'visible'} />); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(<App mode={'hidden'} />); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(<App mode={'manual'} />); - }); - - expect(offscreenRef.current).not.toBeNull(); - }); - - // @gate enableActivity - it('should lower update priority for detached Activity', async () => { - let updateChildState; - let updateHighPriorityComponentState; - let offscreenRef; - - function Child() { - const [state, _stateUpdate] = useState(0); - updateChildState = _stateUpdate; - const text = 'Child ' + state; - return <Text text={text} />; - } - - function HighPriorityComponent(props) { - const [state, _stateUpdate] = useState(0); - updateHighPriorityComponentState = _stateUpdate; - const text = 'HighPriorityComponent ' + state; - return ( - <> - <Text text={text} /> - {props.children} - </> - ); - } - - function App() { - offscreenRef = useRef(null); - return ( - <> - <HighPriorityComponent> - <Activity mode={'manual'} ref={offscreenRef}> - <Child /> - </Activity> - </HighPriorityComponent> - </> - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App />); - }); - - assertLog(['HighPriorityComponent 0', 'Child 0']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 0" /> - <span prop="Child 0" /> - </>, - ); - - expect(offscreenRef.current).not.toBeNull(); - - // Activity is attached by default. State updates from offscreen are **not defered**. - await act(async () => { - updateChildState(1); - updateHighPriorityComponentState(1); - await waitForPaint(['HighPriorityComponent 1', 'Child 1']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 1" /> - <span prop="Child 1" /> - </>, - ); - }); - - await act(() => { - offscreenRef.current.detach(); - }); - - // Activity is detached. State updates from offscreen are **defered**. - await act(async () => { - updateChildState(2); - updateHighPriorityComponentState(2); - await waitForPaint(['HighPriorityComponent 2']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 2" /> - <span prop="Child 1" /> - </>, - ); - }); - - assertLog(['Child 2']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 2" /> - <span prop="Child 2" /> - </>, - ); - - await act(() => { - offscreenRef.current.attach(); - }); - - // Activity is attached. State updates from offscreen are **not defered**. - await act(async () => { - updateChildState(3); - updateHighPriorityComponentState(3); - await waitForPaint(['HighPriorityComponent 3', 'Child 3']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 3" /> - <span prop="Child 3" /> - </>, - ); - }); - }); - - // @gate enableActivity - it('defers detachment if called during commit', async () => { - let updateChildState; - let updateHighPriorityComponentState; - let offscreenRef; - let nextRenderTriggerDetach = false; - let nextRenderTriggerAttach = false; - - function Child() { - const [state, _stateUpdate] = useState(0); - updateChildState = _stateUpdate; - const text = 'Child ' + state; - return <Text text={text} />; - } - - function HighPriorityComponent(props) { - const [state, _stateUpdate] = useState(0); - updateHighPriorityComponentState = _stateUpdate; - const text = 'HighPriorityComponent ' + state; - useLayoutEffect(() => { - if (nextRenderTriggerDetach) { - _stateUpdate(state + 1); - updateChildState(state + 1); - offscreenRef.current.detach(); - nextRenderTriggerDetach = false; - } - - if (nextRenderTriggerAttach) { - offscreenRef.current.attach(); - nextRenderTriggerAttach = false; - } - }); - return ( - <> - <Text text={text} /> - {props.children} - </> - ); - } - - function App() { - offscreenRef = useRef(null); - return ( - <> - <HighPriorityComponent> - <Activity mode={'manual'} ref={offscreenRef}> - <Child /> - </Activity> - </HighPriorityComponent> - </> - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App />); - }); - - assertLog(['HighPriorityComponent 0', 'Child 0']); - - nextRenderTriggerDetach = true; - - // Activity is attached and gets detached inside useLayoutEffect. - // State updates from offscreen are **defered**. - await act(async () => { - updateChildState(1); - updateHighPriorityComponentState(1); - await waitForPaint([ - 'HighPriorityComponent 1', - 'Child 1', - 'HighPriorityComponent 2', - ]); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 2" /> - <span prop="Child 1" /> - </>, - ); - }); - - assertLog(['Child 2']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 2" /> - <span prop="Child 2" /> - </>, - ); - - nextRenderTriggerAttach = true; - - // Activity is detached. State updates from offscreen are **defered**. - // Activity is attached inside useLayoutEffect; - await act(async () => { - updateChildState(3); - updateHighPriorityComponentState(3); - await waitForPaint(['HighPriorityComponent 3', 'Child 3']); - expect(root).toMatchRenderedOutput( - <> - <span prop="HighPriorityComponent 3" /> - <span prop="Child 3" /> - </>, - ); - }); - }); - }); - - // @gate enableActivity - it('should detach ref if Activity is unmounted', async () => { - let offscreenRef; - - function App({showOffscreen}) { - offscreenRef = useRef(null); - return showOffscreen ? ( - <Activity - mode={'manual'} - ref={ref => { - offscreenRef.current = ref; - }}> - <div /> - </Activity> - ) : null; - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App showOffscreen={true} />); - }); - - expect(offscreenRef.current).not.toBeNull(); - - await act(() => { - root.render(<App showOffscreen={false} />); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(<App showOffscreen={true} />); - }); - - expect(offscreenRef.current).not.toBeNull(); - }); - - // @gate enableActivity - it('should detach ref when parent Activity is hidden', async () => { - let offscreenRef; - - function App({mode}) { - offscreenRef = useRef(null); - return ( - <Activity mode={mode}> - <Activity mode={'manual'} ref={offscreenRef}> - <div /> - </Activity> - </Activity> - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App mode={'hidden'} />); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(<App mode={'visible'} />); - }); - - expect(offscreenRef.current).not.toBeNull(); - await act(() => { - root.render(<App mode={'hidden'} />); - }); - - expect(offscreenRef.current).toBeNull(); - }); - - // @gate enableActivity - it('should change _current', async () => { - let offscreenRef; - const root = ReactNoop.createRoot(); - - function App({children}) { - offscreenRef = useRef(null); - return ( - <Activity mode={'manual'} ref={offscreenRef}> - {children} - </Activity> - ); - } - - await act(() => { - root.render( - <App> - <div /> - </App>, - ); - }); - - expect(offscreenRef.current).not.toBeNull(); - const firstFiber = offscreenRef.current._current; - - await act(() => { - root.render( - <App> - <span /> - </App>, - ); - }); - - expect(offscreenRef.current._current === firstFiber).toBeFalsy(); - }); - - // @gate enableActivity - it('does not mount tree until attach is called', async () => { - let offscreenRef; - let spanRef; - - function Child() { - spanRef = useRef(null); - useEffect(() => { - Scheduler.log('Mount Child'); - return () => { - Scheduler.log('Unmount Child'); - }; - }); - useLayoutEffect(() => { - Scheduler.log('Mount Layout Child'); - return () => { - Scheduler.log('Unmount Layout Child'); - }; - }); - - return <span ref={spanRef}>Child</span>; - } - - function App() { - return ( - <Activity mode={'manual'} ref={el => (offscreenRef = el)}> - <Child /> - </Activity> - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App />); - }); - - expect(offscreenRef).not.toBeNull(); - expect(spanRef.current).not.toBeNull(); - assertLog(['Mount Layout Child', 'Mount Child']); - - await act(() => { - offscreenRef.detach(); - }); - - expect(spanRef.current).toBeNull(); - assertLog(['Unmount Layout Child', 'Unmount Child']); - - // Calling attach on already attached Activity. - await act(() => { - offscreenRef.detach(); - }); - - assertLog([]); - - await act(() => { - offscreenRef.attach(); - }); - - expect(spanRef.current).not.toBeNull(); - assertLog(['Mount Layout Child', 'Mount Child']); - - // Calling attach on already attached Activity - offscreenRef.attach(); - - assertLog([]); - }); - - // @gate enableActivity - it('handles nested manual offscreens', async () => { - let outerOffscreen; - let innerOffscreen; - - function App() { - return ( - <LoggedText text={'outer'}> - <Activity mode={'manual'} ref={el => (outerOffscreen = el)}> - <LoggedText text={'middle'}> - <Activity mode={'manual'} ref={el => (innerOffscreen = el)}> - <LoggedText text={'inner'} /> - </Activity> - </LoggedText> - </Activity> - </LoggedText> - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(<App />); - }); - - assertLog([ - 'outer', - 'middle', - 'inner', - 'mount insertion inner', - 'mount insertion middle', - 'mount insertion outer', - 'mount layout inner', - 'mount layout middle', - 'mount layout outer', - 'mount inner', - 'mount middle', - 'mount outer', - ]); - - expect(outerOffscreen).not.toBeNull(); - expect(innerOffscreen).not.toBeNull(); - - await act(() => { - outerOffscreen.detach(); - }); - - expect(innerOffscreen).toBeNull(); - - assertLog([ - 'unmount layout middle', - 'unmount layout inner', - 'unmount middle', - 'unmount inner', - ]); - - await act(() => { - outerOffscreen.attach(); - }); - - assertLog([ - 'mount layout inner', - 'mount layout middle', - 'mount inner', - 'mount middle', - ]); - - await act(() => { - innerOffscreen.detach(); - }); - - assertLog(['unmount layout inner', 'unmount inner']); - - // Calling detach on already detached Activity. - await act(() => { - innerOffscreen.detach(); - }); - - assertLog([]); - - await act(() => { - innerOffscreen.attach(); - }); - - assertLog(['mount layout inner', 'mount inner']); - - await act(() => { - innerOffscreen.detach(); - outerOffscreen.attach(); - }); - - assertLog(['unmount layout inner', 'unmount inner']); - - await act(() => { - root.render(null); - }); - - assertLog([ - 'unmount insertion outer', - 'unmount layout outer', - 'unmount insertion middle', - 'unmount layout middle', - ...(gate('enableHiddenSubtreeInsertionEffectCleanup') - ? ['unmount insertion inner'] - : []), - 'unmount outer', - 'unmount middle', - ]); - }); - - // @gate enableActivity - it('batches multiple attach and detach calls scheduled from an event handler', async () => { - function Child() { - useEffect(() => { - Scheduler.log('attach child'); - return () => { - Scheduler.log('detach child'); - }; - }, []); - return 'child'; - } - - const offscreen = React.createRef(null); - function App() { - return ( - <Activity ref={offscreen} mode="manual"> - <Child /> - </Activity> - ); - } - - const root = ReactNoop.createRoot(); - await act(() => { - root.render(<App />); - }); - - assertLog(['attach child']); - - await act(() => { - const instance = offscreen.current; - // Detach then immediately attach the instance. - instance.detach(); - instance.attach(); - }); - - assertLog([]); - - await act(() => { - const instance = offscreen.current; - instance.detach(); - }); - - assertLog(['detach child']); - - await act(() => { - const instance = offscreen.current; - // Attach then immediately detach. - instance.attach(); - instance.detach(); - }); - - assertLog([]); - }); - - // @gate enableActivity - it('batches multiple attach and detach calls scheduled from an effect', async () => { - function Child() { - useEffect(() => { - Scheduler.log('attach child'); - return () => { - Scheduler.log('detach child'); - }; - }, []); - return 'child'; - } - - function App() { - const offscreen = useRef(null); - useLayoutEffect(() => { - const instance = offscreen.current; - // Detach then immediately attach the instance. - instance.detach(); - instance.attach(); - }, []); - return ( - <Activity ref={offscreen} mode="manual"> - <Child /> - </Activity> - ); - } - - const root = ReactNoop.createRoot(); - await act(() => { - root.render(<App />); - }); - assertLog(['attach child']); - }); }); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 521575a5041b4..fbbd62f3af547 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -140,8 +140,7 @@ export type Thenable<T> = export type OffscreenMode = | 'hidden' | 'unstable-defer-without-hiding' - | 'visible' - | 'manual'; + | 'visible'; export type StartTransitionOptions = { name?: string, From fe8c10695cc65502b3e9f9db4b6dbf20f521bb7e Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:07:09 -0400 Subject: [PATCH 193/300] [ci] Add missing permissions (#32707) Missed these ones earlier. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32707). * #32708 * __->__ #32707 --- .github/workflows/compiler_discord_notify.yml | 3 +++ .github/workflows/runtime_discord_notify.yml | 3 +++ .github/workflows/shared_close_direct_sync_branch_prs.yml | 3 +++ .github/workflows/shared_label_core_team_prs.yml | 6 ++++++ 4 files changed, 15 insertions(+) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index ca7feaae50791..3840e3787bc00 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -12,6 +12,9 @@ permissions: {} jobs: check_maintainer: uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main + permissions: + # Used by check_maintainer + contents: read with: actor: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index c6da99646c671..ad94e28054d24 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -12,6 +12,9 @@ permissions: {} jobs: check_maintainer: uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main + permissions: + # Used by check_maintainer + contents: read with: actor: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/shared_close_direct_sync_branch_prs.yml b/.github/workflows/shared_close_direct_sync_branch_prs.yml index abf8db919f7b8..01db0907401c0 100644 --- a/.github/workflows/shared_close_direct_sync_branch_prs.yml +++ b/.github/workflows/shared_close_direct_sync_branch_prs.yml @@ -15,6 +15,9 @@ env: jobs: close_pr: runs-on: ubuntu-latest + permissions: + # Used to create a review and close PRs + pull-requests: write steps: - name: Close PR uses: actions/github-script@v7 diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml index 9b9e6149ed701..edc1ad6103602 100644 --- a/.github/workflows/shared_label_core_team_prs.yml +++ b/.github/workflows/shared_label_core_team_prs.yml @@ -13,6 +13,9 @@ env: jobs: check_maintainer: uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main + permissions: + # Used by check_maintainer + contents: read with: actor: ${{ github.event.pull_request.user.login }} @@ -20,6 +23,9 @@ jobs: if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }} runs-on: ubuntu-latest needs: check_maintainer + permissions: + # Used to add labels + issues: write steps: - name: Label PR as React Core Team uses: actions/github-script@v7 From 4f080e498c4c0b373b3e1a8b0e735fceb83c267b Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:17:15 -0400 Subject: [PATCH 194/300] [ci] Also give permissions on pull_requests (#32709) Missed one --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32709). * #32708 * __->__ #32709 --- .github/workflows/shared_label_core_team_prs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml index edc1ad6103602..2cd9f290e0af0 100644 --- a/.github/workflows/shared_label_core_team_prs.yml +++ b/.github/workflows/shared_label_core_team_prs.yml @@ -24,8 +24,10 @@ jobs: runs-on: ubuntu-latest needs: check_maintainer permissions: - # Used to add labels + # Used to add labels on issues issues: write + # Used to add labels on PRs + pull-requests: write steps: - name: Label PR as React Core Team uses: actions/github-script@v7 From 156f0eca20d37f1f5aa2e0f518489f23684c89de Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:17:28 -0400 Subject: [PATCH 195/300] [ci] Don't use pull_request_target (#32708) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pull_request_target` gives access to repository secrets and permissions for use from forks, for example to add a comment. > Due to the dangers inherent to automatic processing of PRs, GitHub’s standard pull_request workflow trigger by default prevents write permissions and secrets access to the target repository. However, in some scenarios such access is needed to properly process the PR. To this end the pull_request_target workflow trigger was introduced. > The reason to introduce the pull_request_target trigger was to enable workflows to label PRs (e.g. needs review) or to comment on the PR. (via https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/) In this case there is no reason for us to allow this, so let's just use the normal `pull_request` trigger which is less permissive. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32708). * __->__ #32708 * #32709 --- .github/workflows/compiler_discord_notify.yml | 2 +- .github/workflows/runtime_discord_notify.yml | 2 +- .github/workflows/shared_label_core_team_prs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index 3840e3787bc00..7d23facf87873 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -1,7 +1,7 @@ name: (Compiler) Discord Notify on: - pull_request_target: + pull_request: types: [opened, ready_for_review] paths: - compiler/** diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index ad94e28054d24..2e81e90d37b91 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -1,7 +1,7 @@ name: (Runtime) Discord Notify on: - pull_request_target: + pull_request: types: [opened, ready_for_review] paths-ignore: - compiler/** diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml index 2cd9f290e0af0..78ff4b2b03fab 100644 --- a/.github/workflows/shared_label_core_team_prs.yml +++ b/.github/workflows/shared_label_core_team_prs.yml @@ -1,7 +1,7 @@ name: (Shared) Label Core Team PRs on: - pull_request_target: + pull_request: permissions: {} From de4aad5ba693be099b215b5819b5f25d05051a84 Mon Sep 17 00:00:00 2001 From: lauren <poteto@users.noreply.github.com> Date: Fri, 21 Mar 2025 18:01:02 -0400 Subject: [PATCH 196/300] [ci] Add missing permissions to runtime_commit_artifacts.yml (#32710) Turns out we need permissions to write to `contents` after all. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32710). * #32711 * __->__ #32710 --- .github/workflows/runtime_commit_artifacts.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml index fef8e8be71d04..cc600d2085e50 100644 --- a/.github/workflows/runtime_commit_artifacts.yml +++ b/.github/workflows/runtime_commit_artifacts.yml @@ -203,6 +203,9 @@ jobs: needs: [download_artifacts, process_artifacts] if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.process_artifacts.outputs.www_branch_count == '0') runs-on: ubuntu-latest + permissions: + # Used to push a commit to builds/facebook-www + contents: write steps: - uses: actions/checkout@v4 with: @@ -289,6 +292,9 @@ jobs: commit_fbsource_artifacts: needs: [download_artifacts, process_artifacts] + permissions: + # Used to push a commit to builds/facebook-fbsource + contents: write if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.process_artifacts.outputs.fbsource_branch_count == '0') runs-on: ubuntu-latest steps: From 6b1a2c1d81630a5f385c5be0f758365b63d92eae Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV <dmytropostolov@gmail.com> Date: Sat, 22 Mar 2025 00:46:02 +0100 Subject: [PATCH 197/300] fix(react-compiler): optimize components declared with arrow function and implicit return and `compilationMode: 'infer'` (#31792) fixes https://github.com/facebook/react/issues/31601 https://github.com/facebook/react/issues/31639 cc @josephsavona --- .../src/Entrypoint/Program.ts | 42 +++++++++++-------- ...ow-function-with-implicit-return.expect.md | 39 +++++++++++++++++ .../arrow-function-with-implicit-return.js | 7 ++++ 3 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 4faa35d18f747..ec1a2d2935fc6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -1008,31 +1008,39 @@ function callsHooksOrCreatesJsx( return invokesHooks || createsJsx; } +function isNonNode(node?: t.Expression | null): boolean { + if (!node) { + return true; + } + switch (node.type) { + case 'ObjectExpression': + case 'ArrowFunctionExpression': + case 'FunctionExpression': + case 'BigIntLiteral': + case 'ClassExpression': + case 'NewExpression': // technically `new Array()` is legit, but unlikely + return true; + } + return false; +} + function returnsNonNode( node: NodePath< t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression >, ): boolean { - let hasReturn = false; let returnsNonNode = false; + if ( + // node.traverse#ArrowFunctionExpression isn't called for the root node + node.type === 'ArrowFunctionExpression' && + node.node.body.type !== 'BlockStatement' + ) { + returnsNonNode = isNonNode(node.node.body); + } node.traverse({ ReturnStatement(ret) { - hasReturn = true; - const argument = ret.node.argument; - if (argument == null) { - returnsNonNode = true; - } else { - switch (argument.type) { - case 'ObjectExpression': - case 'ArrowFunctionExpression': - case 'FunctionExpression': - case 'BigIntLiteral': - case 'ClassExpression': - case 'NewExpression': // technically `new Array()` is legit, but unlikely - returnsNonNode = true; - } - } + returnsNonNode = isNonNode(ret.node.argument); }, // Skip traversing all nested functions and their return statements ArrowFunctionExpression: skipNestedFunctions(node), @@ -1041,7 +1049,7 @@ function returnsNonNode( ObjectMethod: node => node.skip(), }); - return !hasReturn || returnsNonNode; + return returnsNonNode; } /* diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.expect.md new file mode 100644 index 0000000000000..244e70bbbab87 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.expect.md @@ -0,0 +1,39 @@ + +## Input + +```javascript +// @compilationMode(infer) +const Test = () => <div />; + +export const FIXTURE_ENTRYPOINT = { + fn: Test, + params: [{}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @compilationMode(infer) +const Test = () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <div />; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +}; + +export const FIXTURE_ENTRYPOINT = { + fn: Test, + params: [{}], +}; + +``` + +### Eval output +(kind: ok) <div></div> \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.js new file mode 100644 index 0000000000000..d2b29b1889b40 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-with-implicit-return.js @@ -0,0 +1,7 @@ +// @compilationMode(infer) +const Test = () => <div />; + +export const FIXTURE_ENTRYPOINT = { + fn: Test, + params: [{}], +}; From da996a15be4f14aeb9726037f4559ff1cb3c2600 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Fri, 21 Mar 2025 20:05:22 -0400 Subject: [PATCH 198/300] [compiler][be] Move e2e tests to BabelPlugin transformer (#32706) Clean up jest-e2e setup since https://github.com/facebook/react/pull/32663 and other features need program context (e.g. changing imports) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32706). * #32663 * __->__ #32706 --- .../scripts/jest/e2e-forget.config.js | 5 +- .../scripts/jest/makeTransform.ts | 134 +++--------------- .../scripts/jest/setupEnvE2E.js | 16 --- .../src/__tests__/e2e/constant-prop.e2e.js | 4 + .../src/__tests__/e2e/update-button.e2e.js | 1 + .../__tests__/e2e/update-expressions.e2e.js | 1 + 6 files changed, 26 insertions(+), 135 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/scripts/jest/setupEnvE2E.js diff --git a/compiler/packages/babel-plugin-react-compiler/scripts/jest/e2e-forget.config.js b/compiler/packages/babel-plugin-react-compiler/scripts/jest/e2e-forget.config.js index ac921cf5dfef7..133266dd696b3 100644 --- a/compiler/packages/babel-plugin-react-compiler/scripts/jest/e2e-forget.config.js +++ b/compiler/packages/babel-plugin-react-compiler/scripts/jest/e2e-forget.config.js @@ -7,7 +7,4 @@ const makeE2EConfig = require('../jest/makeE2EConfig'); -const config = makeE2EConfig('e2e with forget', true); -config.setupFilesAfterEnv = ['<rootDir>/../scripts/jest/setupEnvE2E.js']; - -module.exports = config; +module.exports = makeE2EConfig('e2e with forget', true); diff --git a/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts b/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts index 99291c2984321..5684005e33d2d 100644 --- a/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts +++ b/compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts @@ -5,19 +5,16 @@ * LICENSE file in the root directory of this source tree. */ -import {jsx} from '@babel/plugin-syntax-jsx'; import babelJest from 'babel-jest'; -import {compile} from 'babel-plugin-react-compiler'; -import {execSync} from 'child_process'; - -import type {NodePath, Visitor} from '@babel/traverse'; -import type {CallExpression, FunctionDeclaration} from '@babel/types'; -import * as t from '@babel/types'; import { - EnvironmentConfig, validateEnvironmentConfig, + EnvironmentConfig, } from 'babel-plugin-react-compiler'; -import {basename} from 'path'; +import {execSync} from 'child_process'; + +import type {NodePath, Visitor} from '@babel/traverse'; +import type {CallExpression} from '@babel/types'; +import BabelPluginReactCompiler from 'babel-plugin-react-compiler'; /** * -- IMPORTANT -- @@ -28,10 +25,19 @@ import {basename} from 'path'; const e2eTransformerCacheKey = 1; const forgetOptions: EnvironmentConfig = validateEnvironmentConfig({ enableAssumeHooksFollowRulesOfReact: true, - enableFunctionOutlining: false, }); const debugMode = process.env['DEBUG_FORGET_COMPILER'] != null; +const compilerCacheKey = execSync( + 'yarn --silent --cwd ../.. hash packages/babel-plugin-react-compiler/dist', +) + .toString() + .trim(); + +if (debugMode) { + console.log('cachebreaker', compilerCacheKey); +} + module.exports = (useForget: boolean) => { function createTransformer() { return babelJest.createTransformer({ @@ -42,15 +48,14 @@ module.exports = (useForget: boolean) => { plugins: [ useForget ? [ - ReactForgetFunctionTransform, + BabelPluginReactCompiler, { + environment: forgetOptions, /* * Jest hashes the babel config as a cache breaker. * (see https://github.com/jestjs/jest/blob/v29.6.2/packages/babel-jest/src/index.ts#L84) */ - compilerCacheKey: execSync( - 'yarn --silent --cwd ../.. hash packages/babel-plugin-react-compiler/dist', - ).toString(), + compilerCacheKey, transformOptionsCacheKey: forgetOptions, e2eTransformerCacheKey, }, @@ -105,104 +110,3 @@ module.exports = (useForget: boolean) => { createTransformer, }; }; - -// Mostly copied from react/scripts/babel/transform-forget.js -function isReactComponentLike(fn: NodePath<FunctionDeclaration>): boolean { - let isReactComponent = false; - let hasNoUseForgetDirective = false; - - /* - * React components start with an upper case letter, - * React hooks start with `use` - */ - if ( - fn.node.id == null || - (fn.node.id.name[0].toUpperCase() !== fn.node.id.name[0] && - !/^use[A-Z0-9]/.test(fn.node.id.name)) - ) { - return false; - } - - fn.traverse({ - DirectiveLiteral(path) { - if (path.node.value === 'use no forget') { - hasNoUseForgetDirective = true; - } - }, - - JSX(path) { - // Is there is a JSX node created in the current function context? - if (path.scope.getFunctionParent()?.path.node === fn.node) { - isReactComponent = true; - } - }, - - CallExpression(path) { - // Is there hook usage? - if ( - path.node.callee.type === 'Identifier' && - !/^use[A-Z0-9]/.test(path.node.callee.name) - ) { - isReactComponent = true; - } - }, - }); - - if (hasNoUseForgetDirective) { - return false; - } - - return isReactComponent; -} - -function ReactForgetFunctionTransform() { - const compiledFns = new Set(); - const visitor = { - FunctionDeclaration(fn: NodePath<FunctionDeclaration>, state: any): void { - if (compiledFns.has(fn.node)) { - return; - } - - if (!isReactComponentLike(fn)) { - return; - } - if (debugMode) { - const filename = basename(state.file.opts.filename); - if (fn.node.loc && fn.node.id) { - console.log( - ` Compiling ${filename}:${fn.node.loc.start.line}:${fn.node.loc.start.column} ${fn.node.id.name}`, - ); - } else { - console.log(` Compiling ${filename} ${fn.node.id?.name}`); - } - } - - const compiled = compile( - fn, - forgetOptions, - 'Other', - 'all_features', - '_c', - null, - null, - null, - ); - compiledFns.add(compiled); - - const fun = t.functionDeclaration( - compiled.id, - compiled.params, - compiled.body, - compiled.generator, - compiled.async, - ); - fn.replaceWith(fun); - fn.skip(); - }, - }; - return { - name: 'react-forget-e2e', - inherits: jsx, - visitor, - }; -} diff --git a/compiler/packages/babel-plugin-react-compiler/scripts/jest/setupEnvE2E.js b/compiler/packages/babel-plugin-react-compiler/scripts/jest/setupEnvE2E.js deleted file mode 100644 index 5daf2ae0ae9ef..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/scripts/jest/setupEnvE2E.js +++ /dev/null @@ -1,16 +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. - */ - -const ReactCompilerRuntime = require('react/compiler-runtime'); - -/* - * Our e2e babel transform currently only compiles functions, not programs. - * As a result, our e2e transpiled code does not contain an import for the - * memo cache function. As a temporary hack, we add a `_c` global, which is - * the name that is used for the import by default. - */ -globalThis._c = ReactCompilerRuntime.c; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/constant-prop.e2e.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/constant-prop.e2e.js index 634bcb579fa1f..229e36206da94 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/constant-prop.e2e.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/constant-prop.e2e.js @@ -12,6 +12,7 @@ globalThis.constantValue = 'global test value'; test('literal-constant-propagation', () => { function Component() { + 'use memo'; const x = 'test value 1'; return <div>{x}</div>; } @@ -38,6 +39,7 @@ test('literal-constant-propagation', () => { test('global-constant-propagation', () => { function Component() { + 'use memo'; const x = constantValue; return <div>{x}</div>; @@ -65,6 +67,7 @@ test('global-constant-propagation', () => { test('lambda-constant-propagation', () => { function Component() { + 'use memo'; const x = 'test value 1'; const getDiv = () => <div>{x}</div>; return getDiv(); @@ -92,6 +95,7 @@ test('lambda-constant-propagation', () => { test('lambda-constant-propagation-of-phi-node', () => { function Component({noopCallback}) { + 'use memo'; const x = 'test value 1'; if (constantValue) { noopCallback(); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-button.e2e.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-button.e2e.js index a478952923919..ac25adeba9d47 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-button.e2e.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-button.e2e.js @@ -16,6 +16,7 @@ function Button({label}) { let currentTheme = 'light'; function useTheme() { + 'use memo'; return currentTheme; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-expressions.e2e.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-expressions.e2e.js index 723a1ba5a26e7..0344c5799fad9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-expressions.e2e.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-expressions.e2e.js @@ -10,6 +10,7 @@ import * as React from 'react'; import {expectLogsAndClear, log} from './expectLogs'; function Counter(props) { + 'use memo'; let value = props.value; let a = value++; expect(a).toBe(props.value); // postfix From 4a9df08157f001c01b078d259748512211233dcf Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" <sebastian.silbermann@vercel.com> Date: Sun, 23 Mar 2025 15:47:03 -0700 Subject: [PATCH 199/300] Stop creating Owner Stacks if many have been created recently (#32529) Co-authored-by: Jack Pope <jackpope1@gmail.com> --- fixtures/owner-stacks/.gitignore | 23 + fixtures/owner-stacks/README.md | 70 + fixtures/owner-stacks/package.json | 36 + fixtures/owner-stacks/public/favicon.ico | Bin 0 -> 3870 bytes fixtures/owner-stacks/public/index.html | 43 + fixtures/owner-stacks/public/logo192.png | Bin 0 -> 5347 bytes fixtures/owner-stacks/public/logo512.png | Bin 0 -> 9664 bytes fixtures/owner-stacks/public/manifest.json | 25 + fixtures/owner-stacks/public/robots.txt | 3 + fixtures/owner-stacks/src/App.css | 62 + fixtures/owner-stacks/src/App.js | 143 + fixtures/owner-stacks/src/index.css | 13 + fixtures/owner-stacks/src/index.js | 17 + fixtures/owner-stacks/src/logo.svg | 1 + fixtures/owner-stacks/src/reportWebVitals.js | 13 + fixtures/owner-stacks/yarn.lock | 9688 +++++++++++++++++ .../src/__tests__/ReactFlight-test.js | 48 +- .../src/ReactFiberWorkLoop.js | 3 + .../src/__tests__/ReactOwnerStacks-test.js | 337 +- .../src/__tests__/ReactFlightDOMEdge-test.js | 6 +- packages/react-server/src/ReactFizzServer.js | 10 + .../react-server/src/ReactFlightServer.js | 9 + .../src/__tests__/ReactFlightServer-test.js | 178 + .../react/src/ReactSharedInternalsClient.js | 4 + .../react/src/ReactSharedInternalsServer.js | 4 + packages/react/src/jsx/ReactJSXElement.js | 78 +- packages/shared/ReactFeatureFlags.js | 2 + packages/shared/ReactOwnerStackReset.js | 42 + .../ReactFeatureFlags.native-fb-dynamic.js | 4 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + ...actFeatureFlags.test-renderer.native-fb.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 5 + .../shared/forks/ReactFeatureFlags.www.js | 1 + 36 files changed, 10835 insertions(+), 38 deletions(-) create mode 100644 fixtures/owner-stacks/.gitignore create mode 100644 fixtures/owner-stacks/README.md create mode 100644 fixtures/owner-stacks/package.json create mode 100644 fixtures/owner-stacks/public/favicon.ico create mode 100644 fixtures/owner-stacks/public/index.html create mode 100644 fixtures/owner-stacks/public/logo192.png create mode 100644 fixtures/owner-stacks/public/logo512.png create mode 100644 fixtures/owner-stacks/public/manifest.json create mode 100644 fixtures/owner-stacks/public/robots.txt create mode 100644 fixtures/owner-stacks/src/App.css create mode 100644 fixtures/owner-stacks/src/App.js create mode 100644 fixtures/owner-stacks/src/index.css create mode 100644 fixtures/owner-stacks/src/index.js create mode 100644 fixtures/owner-stacks/src/logo.svg create mode 100644 fixtures/owner-stacks/src/reportWebVitals.js create mode 100644 fixtures/owner-stacks/yarn.lock create mode 100644 packages/react-server/src/__tests__/ReactFlightServer-test.js create mode 100644 packages/shared/ReactOwnerStackReset.js diff --git a/fixtures/owner-stacks/.gitignore b/fixtures/owner-stacks/.gitignore new file mode 100644 index 0000000000000..4d29575de8048 --- /dev/null +++ b/fixtures/owner-stacks/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/fixtures/owner-stacks/README.md b/fixtures/owner-stacks/README.md new file mode 100644 index 0000000000000..c0541f9c04d53 --- /dev/null +++ b/fixtures/owner-stacks/README.md @@ -0,0 +1,70 @@ +# Getting Started with Create React App + +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.\ +Open [http://localhost:3000](http://localhost:3000) to view it in your browser. + +The page will reload when you make changes.\ +You may also see any lint errors in the console. + +### `yarn test` + +Launches the test runner in the interactive watch mode.\ +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `yarn build` + +Builds the app for production to the `build` folder.\ +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.\ +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `yarn eject` + +**Note: this is a one-way operation. Once you `eject`, you can't go back!** + +If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. + +You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + +### Analyzing the Bundle Size + +This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) + +### Making a Progressive Web App + +This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + +### Advanced Configuration + +This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) + +### Deployment + +This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) + +### `yarn build` fails to minify + +This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/fixtures/owner-stacks/package.json b/fixtures/owner-stacks/package.json new file mode 100644 index 0000000000000..cd288c8baeac7 --- /dev/null +++ b/fixtures/owner-stacks/package.json @@ -0,0 +1,36 @@ +{ + "name": "owner-stacks", + "version": "0.1.0", + "private": true, + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.0" + }, + "scripts": { + "prestart": "cp -a ../../build/oss-experimental/. node_modules", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/fixtures/owner-stacks/public/favicon.ico b/fixtures/owner-stacks/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 zcma);c{J4h9>;%nil|2-o+rCuEF-(I%-F}ijC~o(k~HKAkr0)!FCj~d>`RtpD?8b; zXOC1OD!V*IsqUwzbMF1)-gEDD=A573Z-&G7^LoAC9|WO7Xc0Cx1g^Zu0u_SjAPB<A z`RksU20=ur5rmib*S!+l%h4eS4)^Q+0X>3vGa^W|sj)80f#V0@M_CAZTIO(t--xg= z!sii`1giyH7EKL_+Wi0ab<)&E_0KD!3Rp2^HNB*K2@PHCs4PWSA32*-^7d{9nH2_E zmC{C*N*)(vEF1_aMamw2A{ZH5aIDqiabnFdJ|y0%aS|64E$`s2ccV~3lR!u<){eS` z#^Mx6o(iP1Ix%<jZ{9b!^*}EvPeMb_W#+3mPDk@<s^Oh#VM&a2^K;|820}`)peR}+ zJXt@j)V#7+Js?u;Lb#g$HH)e~Ro^hvl6KSLHq)Y3adj<OOD7?;gwee^gNzCxwD?IA z8?*}E@b*IiVPUPv3?XqzLRv|{4)GKGzjS`)#ukL7W&K6BHn&1}P(skc69cJ?5^C+V z@yyqLJg;V2Ul%gZ*?2WiB%bNfz1}F^UeTpW^N?dSY@NL3zDD+Tzk$Cg_=cj!M^ot0 zu%qYEoTU9K@kMP2H52_@<2On}lNX!oZ(oWk^?eSfXAa3M8S?8tzISV2V&9A+_-47Y z>4dv`t@!&Za-K@mTm#vadc{0aWDV*_%EiGK7qMC_(`exc>-$Gb9~W!w_^{*pYRm~G zBN{nA<l~YIv(*f3@JAyAZDXwp4d;meFk*lN;rx5VQze6aK!n?W9`Uc4pES2K&V3BC zkTJK{PcIXdQ?hM;i7~K{wRSeU-w9_32aC}+7nN6r5o<=I@CyjQAS~;jsb7p#@eUT2 zkh1M~1>;cm^w$VWg1O^^<6vY`1XCD|s_zv*g*5&V#wv&s#h$xlUilPe4U@I&UXZbL z0)%9Uj&@yd03n;!7do+bfixH^FeZ-Ema}s;DQX2gY+7g0s(9;`8GyvPY1*vxiF&|w z>!vA~GA<~JUqH}d;DfBSi^IT*#lrzXl$fNpq0_T1tA+`A$1?(gLb?e#0>UELvljtQ zK+*74m0jn&)5yk8mLBv;=@}c{t0ztT<<S2g5CX`xuBQVwYJOMIsv7paOX6ypYJL$a zJ|Vy}#?V4i+kjXzBq)LcuJEA=z^Z2W4WQ1U@0}*!;_q<!3_ls8PhMM3ii*Ci+cF6= zF!@E<x#%Yvb!P0>v;Avck$S6D`Z)^c0(jiwKhQsn|LDRY&w(Fmi91I7H6S;b0XM{e zXp0~(T@k_r-!jkLwd1_Vre^v$G4|kh4}=Gi?$AaJ)3I+^m|Zyj#*?Kp@w(lQdJZf4 z#|IJW5z+S^e9@(6hW6N~{pj8|NO*>1)E=%?nNUAkmv~OY&ZV<PHdt%yO<W_%O|c-T zC%nAvgv?#h>;m-%?pQ_11)hAr0oAwILrlsGawpxx4D43J&K=n+p3WLnlDsQ$b(9+4 z?mO^hmV^F8MV{4<aA#E-8o{y-by8hR1>Lx>(Q=aHhQ1){0d*(e&s%G=i5rq3;t{JC zmgbn5Nkl)t@fPH$v;af26lyhH!k+#}_&aBK4baYPbZy$5aFx4}ka<ge$nBI}>&qxl z$=Rh$W;U)>-=S-0=?7FH9dUAd2(q#4TCAHky!$^~;Dz^j|8_wuKc*YzfdA<NJp8x7 z`_}_7!m44CG`<6nLk0r3A}8e>ht@Q&ror?91Dm!N03=4=O!a)I*0q~p0g$Fm$pmr$ zb;wD;STDIi$@M%y1>p&_>%?UP($15gou_ue1u0!4(%81;qcIW8NyxFEvXpiJ|H4wz z*mFT(qVx1FKufG11hByuX%lPk4t#WZ{>8ka2efjY`~;AL6vWyQKpJun2nRiZYDij$ zP>4jQXPaP$UC$yIVgGa)jDV;F0l^n(V=HMRB5)20V7&r$<L^Phf(W29K>jmk{UUIe zVjKroK}JAbD>B`2cwNQ&GDLx8{pg`7hbA~grk|W6LgiZ`8y`{Iq0i>t!3p2}MS6S+ zO_ruKyAElt)rdS>CtF7j{&6rP-#c=7evGMt7B6`7HG|-(WL`bDUAjyn+k$mx$C<FS ztTQ#rrhaxTX7@2TN#`pson<p6thk-4?N)^;_(Up!_V=f}<~kR)zD%o0iiqseIMZqh zGU`kZGbN)qs{;AuZP?~%PajDo&b&7)!V!+|VO<ediN}{)OvR~sQ<ZYe%O|)8-DTKw zTXmYP$VLa(Y>H;q2Dz4x;cPP$hW=`pFfLO)!jaCL@V2+F)So3}vg|%O*^T1j>C2lx zsURO-zIJC$^$g2byVbRIo^w>UxK}74^TqUiRR#7s_X$e)$6iYG1(PcW7un-va-S&u zHk9-6Zn&>T==A)lM^D~bk{&rFzCi35>UR!ZjQkdSiNX*-;l4z9j*7|q`TBl~Au`5& z+c)*8?#-tgUR$Zd%Q3bs96w6k7q@#tUn`5rj+r@_sAVVLqco|6O{ILX&U-&-cbVa3 zY?ngHR@%l{;`ri%H*0EhBWrGjv!LE4db?HEWb5mu*t@{kv|XwK8?npOshmzf=vZA@ zVSN9sL~!sn?r(AK)Q7Jk2(|M67Uy3I{eRy<vjA)m;~)jV3DFGzL)eNbs@Sy80roD> z_l&Y@A>;vjkWN5I2xvFFTLX0i+`{qz7C_@bo`ZUzDugfq4+>a3?1v%)O+YTd6@Ul7 zAfLfm=nhZ`)P~&v90$&UcF+yXm9sq!qCx3^9gzIcO|Y(js^Fj)Rvq>nQAHI92ap=P z10A4@prk+<s7nQxb0&o?puD0BStB$NLIA{pVg<pW;2=HJ11ZpVkRkF89w0s#3ef?( zka>AGWCb`2)dQYFuR$|H6iDE8p}9a?#nV2}LBCoCf(Xi2@szia7#gY>b|l!-U`c}@ zLdhvQjc!BdLJvYvzzzngnw51yRYCqh4}$oRCy-z|v3Hc*d|?^Wj=l~18*E~*cR_kU z{XsxM1i{V*4GujHQ3DBpl2w4FgFR48Nma@HPgnyKoIEY-MqmMeY=I<%oG~l!f<+FN z1ZY^;10j4M4<Vo=b&OyEfF!Y);yDCJas8bbVhK~blk}<IGME~h)6n~gdmqP>#HYXP zw5eJpA_y(>uLQ~OucgxDLuf}fVs272FaMxhn4xnDGIyLXnw>Xsd^J8XhcWIwIoQ9} z%FoSJTAGW(SRGwJwb=@pY7r$uQRK3Zd~XbxU)ts!4XsJrCycrWSI?e!IqwqIR8+Jh zlRjZ`UO1I!BtJR_2~7AbkbSm%XQqxEPkz6BTGWx8e}nQ=w7bZ|eVP4?*Tb!$(R)iC z9)&%bS*u(lXqzitAN)Oo=&Ytn>%Hzjc<5liuPi>zC_nw;Z0AE3Y$Jao_Q90R-gl~5 z_xAb2J%eArrC1CN4G$}-zVvCqF1;H;abAu6G*+PDHSYFx@Tdbfox*uEd3}BUyYY-l zTfEsOqsi#f9^FoLO;ChK<554qkri&Av~SIM*{fEYRE?vH7pTAOmu2pz3X?Wn*!ROX ztd54huAk&mFBemMooL33RV-*1f0Q3_(7hl$<#*|WF9P!;r;4_+X~k~uKEqdzZ$5Al zV63X<s4EnR@itBNL^suG_KHV!zgrw6&Bq&`dNv>N<k2!6lBSoSAvQBw$a}{Sg*d5f zJqeF6lxH}v-(s5jl(8V8Bv*((#aw(*iLTd8#?8FnMLG#}AorDTkK*%$ni#S{e-*jA zjy$_xALPmR?$A)F?XdsKy|!Ue+lIR5=csS!ZPu7h{Nc+Sd%?*WHR`S5ByDdhQAsNO zeyx0!D+fx-a_t<57fQ^<7*WTVDog0}WA0F2_h++_I?f`i|C>@)j$FN#cCD;ek1R#l zv%pGrhB~KWgoCj%GT?%{@@o(AJGt*PG#l3i>lhmb_twKH^EYvacVY-6bsCl5*^~L0 zonm@lk2UvvTKr2RS%}T>^~EYqdL1q4nD%0n&Xqr^cK^`J5W;lRRB^R-O<zOhVxo?8 zb#fjP=~|*nH<rZsU&F20QcP*BR|)$r#sFFtYi6hV=2&f<YJ%JC0IAdIRdHjO(;S%3 zC;L{EqcHO368@u|<ql>8b&HENO||mo0xaD+S=I8RTlIfVgqN@SXDr2&-)we--K7w= zJVU8?Z+7k9dy;s;^gDkQa`0nz6N{T?(A&Iz)2!DEecLyRa&FI!id#5Z7B*O2=PsR0 zEvc|8{NS^)!d)MDX(97Xw}m&kEO@5jqRaDZ!+%`wYOI<23q|&js`&o4xvjP7D_xv@ z5hEwpsp{HezI9!~6O{~)lLR@oF7?J7i>1|5a~UuoN=q&6N}EJPV_GD`&M*v8Y`^2j zKII*d_@Fi$+i*YEW+Hbz<W=zs^XxM$!;??OHDS{MUEdOi9{rF;;#a0RO>n{iQk~yP z>7N{S4)r*!NwQ`(qcN#8SRQsNK6>{)X12nbF`*7#ecO7I)Q$uZsV+xS4E7aUn+U(K baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/fixtures/owner-stacks/public/index.html b/fixtures/owner-stacks/public/index.html new file mode 100644 index 0000000000000..aa069f27cbd9d --- /dev/null +++ b/fixtures/owner-stacks/public/index.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="theme-color" content="#000000" /> + <meta + name="description" + content="Web site created using create-react-app" + /> + <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> + <!-- + manifest.json provides metadata used when your web app is installed on a + user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ + --> + <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> + <!-- + Notice the use of %PUBLIC_URL% in the tags above. + It will be replaced with the URL of the `public` folder during the build. + Only files inside the `public` folder can be referenced from the HTML. + + Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will + work correctly both with client-side routing and a non-root public URL. + Learn how to configure a non-root public URL by running `npm run build`. + --> + <title>React App + + + +
+ + + diff --git a/fixtures/owner-stacks/public/logo192.png b/fixtures/owner-stacks/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 zcmZWtbyO6NvR-oO24RV%BvuJ&=?+<7=`LvyB&A_#M7mSDYw1v6DJkiYl9XjT!%$dLEBTQ8R9|wd3008in6lFF3GV-6mLi?MoP_y~}QUnaDCHI#t z7w^m$@6DI)|C8_jrT?q=f8D?0AM?L)Z}xAo^e^W>t$*Y0KlT5=@bBjT9kxb%-KNdk zeOS1tKO#ChhG7%{ApNBzE2ZVNcxbrin#E1TiAw#BlUhXllzhN$qWez5l;h+t^q#Eav8PhR2|T}y5kkflaK`ba-eoE+Z2q@o6P$)=&` z+(8}+-McnNO>e#$Rr{32ngsZIAX>GH??tqgwUuUz6kjns|LjsB37zUEWd|(&O!)DY zQLrq%Y>)Y8G`yYbYCx&aVHi@-vZ3|ebG!f$sTQqMgi0hWRJ^Wc+Ibv!udh_r%2|U) zPi|E^PK?UE!>_4`f`1k4hqqj_$+d!EB_#IYt;f9)fBOumGNyglU(ofY`yHq4Y?B%- zp&G!MRY<~ajTgIHErMe(Z8JG*;D-PJhd@RX@QatggM7+G(Lz8eZ;73)72Hfx5KDOE zkT(m}i2;@X2AT5fW?qVp?@WgN$aT+f_6eo?IsLh;jscNRp|8H}Z9p_UBO^SJXpZew zEK8fz|0Th%(Wr|KZBGTM4yxkA5CFdAj8=QSrT$fKW#tweUFqr0TZ9D~a5lF{)%-tTGMK^2tz(y2v$i%V8XAxIywrZCp=)83p(zIk6@S5AWl|Oa2hF`~~^W zI;KeOSkw1O#TiQ8;U7OPXjZM|KrnN}9arP)m0v$c|L)lF`j_rpG(zW1Qjv$=^|p*f z>)Na{D&>n`jOWMwB^TM}slgTEcjxTlUby89j1)|6ydRfWERn3|7Zd2&e7?!K&5G$x z`5U3uFtn4~SZq|LjFVrz$3iln-+ucY4q$BC{CSm7Xe5c1J<=%Oagztj{ifpaZk_bQ z9Sb-LaQMKp-qJA*bP6DzgE3`}*i1o3GKmo2pn@dj0;He}F=BgINo};6gQF8!n0ULZ zL>kC0nPSFzlcB7p41doao2F7%6IUTi_+!L`MM4o*#Y#0v~WiO8uSeAUNp=vA2KaR&=jNR2iVwG>7t%sG2x_~yXzY)7K& zk3p+O0AFZ1eu^T3s};B%6TpJ6h-Y%B^*zT&SN7C=N;g|#dGIVMSOru3iv^SvO>h4M=t-N1GSLLDqVTcgurco6)3&XpU!FP6Hlrmj}f$ zp95;b)>M~`kxuZF3r~a!rMf4|&1=uMG$;h^g=Kl;H&Np-(pFT9FF@++MMEx3RBsK?AU0fPk-#mdR)Wdkj)`>ZMl#^<80kM87VvsI3r_c@_vX=fdQ`_9-d(xiI z4K;1y1TiPj_RPh*SpDI7U~^QQ?%0&!$Sh#?x_@;ag)P}ZkAik{_WPB4rHyW#%>|Gs zdbhyt=qQPA7`?h2_8T;-E6HI#im9K>au*(j4;kzwMSLgo6u*}-K`$_Gzgu&XE)udQ zmQ72^eZd|vzI)~!20JV-v-T|<4@7ruqrj|o4=JJPlybwMg;M$Ud7>h6g()CT@wXm` zbq=A(t;RJ^{Xxi*Ff~!|3!-l_PS{AyNAU~t{h;(N(PXMEf^R(B+ZVX3 z8y0;0A8hJYp@g+c*`>eTA|3Tgv9U8#BDTO9@a@gVMDxr(fVaEqL1tl?md{v^j8aUv zm&%PX4^|rX|?E4^CkplWWNv*OKM>DxPa z!RJ)U^0-WJMi)Ksc!^ixOtw^egoAZZ2Cg;X7(5xZG7yL_;UJ#yp*ZD-;I^Z9qkP`} zwCTs0*%rIVF1sgLervtnUo&brwz?6?PXRuOCS*JI-WL6GKy7-~yi0giTEMmDs_-UX zo=+nFrW_EfTg>oY72_4Z0*uG>MnXP=c0VpT&*|rvv1iStW;*^={rP1y?Hv+6R6bxFMkxpWkJ>m7Ba{>zc_q zEefC3jsXdyS5??Mz7IET$Kft|EMNJIv7Ny8ZOcKnzf`K5Cd)&`-fTY#W&jnV0l2vt z?Gqhic}l}mCv1yUEy$%DP}4AN;36$=7aNI^*AzV(eYGeJ(Px-j<^gSDp5dBAv2#?; zcMXv#aj>%;MiG^q^$0MSg-(uTl!xm49dH!{X0){Ew7ThWV~Gtj7h%ZD zVN-R-^7Cf0VH!8O)uUHPL2mO2tmE*cecwQv_5CzWeh)ykX8r5Hi`ehYo)d{Jnh&3p z9ndXT$OW51#H5cFKa76c<%nNkP~FU93b5h-|Cb}ScHs@4Q#|}byWg;KDMJ#|l zE=MKD*F@HDBcX@~QJH%56eh~jfPO-uKm}~t7VkHxHT;)4sd+?Wc4* z>CyR*{w@4(gnYRdFq=^(#-ytb^5ESD?x<0Skhb%Pt?npNW1m+Nv`tr9+qN<3H1f<% zZvNEqyK5FgPsQ`QIu9P0x_}wJR~^CotL|n zk?dn;tLRw9jJTur4uWoX6iMm914f0AJfB@C74a;_qRrAP4E7l890P&{v<}>_&GLrW z)klculcg`?zJO~4;BBAa=POU%aN|pmZJn2{hA!d!*lwO%YSIzv8bTJ}=nhC^n}g(ld^rn#kq9Z3)z`k9lvV>y#!F4e{5c$tnr9M{V)0m(Z< z#88vX6-AW7T2UUwW`g<;8I$Jb!R%z@rCcGT)-2k7&x9kZZT66}Ztid~6t0jKb&9mm zpa}LCb`bz`{MzpZR#E*QuBiZXI#<`5qxx=&LMr-UUf~@dRk}YI2hbMsAMWOmDzYtm zjof16D=mc`^B$+_bCG$$@R0t;e?~UkF?7<(vkb70*EQB1rfUWXh$j)R2)+dNAH5%R zEBs^?N;UMdy}V};59Gu#0$q53$}|+q7CIGg_w_WlvE}AdqoS<7DY1LWS9?TrfmcvT zaypmplwn=P4;a8-%l^e?f`OpGb}%(_mFsL&GywhyN(-VROj`4~V~9bGv%UhcA|YW% zs{;nh@aDX11y^HOFXB$a7#Sr3cEtNd4eLm@Y#fc&j)TGvbbMwze zXtekX_wJqxe4NhuW$r}cNy|L{V=t#$%SuWEW)YZTH|!iT79k#?632OFse{+BT_gau zJwQcbH{b}dzKO?^dV&3nTILYlGw{27UJ72ZN){BILd_HV_s$WfI2DC<9LIHFmtyw? zQ;?MuK7g%Ym+4e^W#5}WDLpko%jPOC=aN)3!=8)s#Rnercak&b3ESRX3z{xfKBF8L z5%CGkFmGO@x?_mPGlpEej!3!AMddChabyf~nJNZxx!D&{@xEb!TDyvqSj%Y5@A{}9 zRzoBn0?x}=krh{ok3Nn%e)#~uh;6jpezhA)ySb^b#E>73e*frBFu6IZ^D7Ii&rsiU z%jzygxT-n*joJpY4o&8UXr2s%j^Q{?e-voloX`4DQyEK+DmrZh8A$)iWL#NO9+Y@!sO2f@rI!@jN@>HOA< z?q2l{^%mY*PNx2FoX+A7X3N}(RV$B`g&N=e0uvAvEN1W^{*W?zT1i#fxuw10%~))J zjx#gxoVlXREWZf4hRkgdHx5V_S*;p-y%JtGgQ4}lnA~MBz-AFdxUxU1RIT$`sal|X zPB6sEVRjGbXIP0U+?rT|y5+ev&OMX*5C$n2SBPZr`jqzrmpVrNciR0e*Wm?fK6DY& zl(XQZ60yWXV-|Ps!A{EF;=_z(YAF=T(-MkJXUoX zI{UMQDAV2}Ya?EisdEW;@pE6dt;j0fg5oT2dxCi{wqWJ<)|SR6fxX~5CzblPGr8cb zUBVJ2CQd~3L?7yfTpLNbt)He1D>*KXI^GK%<`bq^cUq$Q@uJifG>p3LU(!H=C)aEL zenk7pVg}0{dKU}&l)Y2Y2eFMdS(JS0}oZUuVaf2+K*YFNGHB`^YGcIpnBlMhO7d4@vV zv(@N}(k#REdul8~fP+^F@ky*wt@~&|(&&meNO>rKDEnB{ykAZ}k>e@lad7to>Ao$B zz<1(L=#J*u4_LB=8w+*{KFK^u00NAmeNN7pr+Pf+N*Zl^dO{LM-hMHyP6N!~`24jd zXYP|Ze;dRXKdF2iJG$U{k=S86l@pytLx}$JFFs8e)*Vi?aVBtGJ3JZUj!~c{(rw5>vuRF$`^p!P8w1B=O!skwkO5yd4_XuG^QVF z`-r5K7(IPSiKQ2|U9+`@Js!g6sfJwAHVd|s?|mnC*q zp|B|z)(8+mxXyxQ{8Pg3F4|tdpgZZSoU4P&9I8)nHo1@)9_9u&NcT^FI)6|hsAZFk zZ+arl&@*>RXBf-OZxhZerOr&dN5LW9@gV=oGFbK*J+m#R-|e6(Loz(;g@T^*oO)0R zN`N=X46b{7yk5FZGr#5&n1!-@j@g02g|X>MOpF3#IjZ_4wg{dX+G9eqS+Es9@6nC7 zD9$NuVJI}6ZlwtUm5cCAiYv0(Yi{%eH+}t)!E^>^KxB5^L~a`4%1~5q6h>d;paC9c zTj0wTCKrhWf+F#5>EgX`sl%POl?oyCq0(w0xoL?L%)|Q7d|Hl92rUYAU#lc**I&^6p=4lNQPa0 znQ|A~i0ip@`B=FW-Q;zh?-wF;Wl5!+q3GXDu-x&}$gUO)NoO7^$BeEIrd~1Dh{Tr` z8s<(Bn@gZ(mkIGnmYh_ehXnq78QL$pNDi)|QcT*|GtS%nz1uKE+E{7jdEBp%h0}%r zD2|KmYGiPa4;md-t_m5YDz#c*oV_FqXd85d@eub?9N61QuYcb3CnVWpM(D-^|CmkL z(F}L&N7qhL2PCq)fRh}XO@U`Yn<?TNGR4L(mF7#4u29{i~@k;pLsgl({YW5`Mo+p=zZn3L*4{JU;++dG9 X@eDJUQo;Ye2mwlRs?y0|+_a0zY+Zo%Dkae}+MySoIppb75o?vUW_?)>@g{U2`ERQIXV zeY$JrWnMZ$QC<=ii4X|@0H8`si75jB(ElJb00HAB%>SlLR{!zO|C9P3zxw_U8?1d8uRZ=({Ga4shyN}3 zAK}WA(ds|``G4jA)9}Bt2Hy0+f3rV1E6b|@?hpGA=PI&r8)ah|)I2s(P5Ic*Ndhn^ z*T&j@gbCTv7+8rpYbR^Ty}1AY)YH;p!m948r#%7x^Z@_-w{pDl|1S4`EM3n_PaXvK z1JF)E3qy$qTj5Xs{jU9k=y%SQ0>8E$;x?p9ayU0bZZeo{5Z@&FKX>}s!0+^>C^D#z z>xsCPvxD3Z=dP}TTOSJhNTPyVt14VCQ9MQFN`rn!c&_p?&4<5_PGm4a;WS&1(!qKE z_H$;dDdiPQ!F_gsN`2>`X}$I=B;={R8%L~`>RyKcS$72ai$!2>d(YkciA^J0@X%G4 z4cu!%Ps~2JuJ8ex`&;Fa0NQOq_nDZ&X;^A=oc1&f#3P1(!5il>6?uK4QpEG8z0Rhu zvBJ+A9RV?z%v?!$=(vcH?*;vRs*+PPbOQ3cdPr5=tOcLqmfx@#hOqX0iN)wTTO21jH<>jpmwRIAGw7`a|sl?9y9zRBh>(_%| zF?h|P7}~RKj?HR+q|4U`CjRmV-$mLW>MScKnNXiv{vD3&2@*u)-6P@h0A`eeZ7}71 zK(w%@R<4lLt`O7fs1E)$5iGb~fPfJ?WxhY7c3Q>T-w#wT&zW522pH-B%r5v#5y^CF zcC30Se|`D2mY$hAlIULL%-PNXgbbpRHgn<&X3N9W!@BUk@9g*P5mz-YnZBb*-$zMM z7Qq}ic0mR8n{^L|=+diODdV}Q!gwr?y+2m=3HWwMq4z)DqYVg0J~^}-%7rMR@S1;9 z7GFj6K}i32X;3*$SmzB&HW{PJ55kT+EI#SsZf}bD7nW^Haf}_gXciYKX{QBxIPSx2Ma? zHQqgzZq!_{&zg{yxqv3xq8YV+`S}F6A>Gtl39_m;K4dA{pP$BW0oIXJ>jEQ!2V3A2 zdpoTxG&V=(?^q?ZTj2ZUpDUdMb)T?E$}CI>r@}PFPWD9@*%V6;4Ag>D#h>!s)=$0R zRXvdkZ%|c}ubej`jl?cS$onl9Tw52rBKT)kgyw~Xy%z62Lr%V6Y=f?2)J|bZJ5(Wx zmji`O;_B+*X@qe-#~`HFP<{8$w@z4@&`q^Q-Zk8JG3>WalhnW1cvnoVw>*R@c&|o8 zZ%w!{Z+MHeZ*OE4v*otkZqz11*s!#s^Gq>+o`8Z5 z^i-qzJLJh9!W-;SmFkR8HEZJWiXk$40i6)7 zZpr=k2lp}SasbM*Nbn3j$sn0;rUI;%EDbi7T1ZI4qL6PNNM2Y%6{LMIKW+FY_yF3) zSKQ2QSujzNMSL2r&bYs`|i2Dnn z=>}c0>a}>|uT!IiMOA~pVT~R@bGlm}Edf}Kq0?*Af6#mW9f9!}RjW7om0c9Qlp;yK z)=XQs(|6GCadQbWIhYF=rf{Y)sj%^Id-ARO0=O^Ad;Ph+ z0?$eE1xhH?{T$QI>0JP75`r)U_$#%K1^BQ8z#uciKf(C701&RyLQWBUp*Q7eyn76} z6JHpC9}R$J#(R0cDCkXoFSp;j6{x{b&0yE@P7{;pCEpKjS(+1RQy38`=&Yxo%F=3y zCPeefABp34U-s?WmU#JJw23dcC{sPPFc2#J$ZgEN%zod}J~8dLm*fx9f6SpO zn^Ww3bt9-r0XaT2a@Wpw;C23XM}7_14#%QpubrIw5aZtP+CqIFmsG4`Cm6rfxl9n5 z7=r2C-+lM2AB9X0T_`?EW&Byv&K?HS4QLoylJ|OAF z`8atBNTzJ&AQ!>sOo$?^0xj~D(;kS$`9zbEGd>f6r`NC3X`tX)sWgWUUOQ7w=$TO&*j;=u%25ay-%>3@81tGe^_z*C7pb9y*Ed^H3t$BIKH2o+olp#$q;)_ zfpjCb_^VFg5fU~K)nf*d*r@BCC>UZ!0&b?AGk_jTPXaSnCuW110wjHPPe^9R^;jo3 zwvzTl)C`Zl5}O2}3lec=hZ*$JnkW#7enKKc)(pM${_$9Hc=Sr_A9Biwe*Y=T?~1CK z6eZ9uPICjy-sMGbZl$yQmpB&`ouS8v{58__t0$JP%i3R&%QR3ianbZqDs<2#5FdN@n5bCn^ZtH992~5k(eA|8|@G9u`wdn7bnpg|@{m z^d6Y`*$Zf2Xr&|g%sai#5}Syvv(>Jnx&EM7-|Jr7!M~zdAyjt*xl;OLhvW-a%H1m0 z*x5*nb=R5u><7lyVpNAR?q@1U59 zO+)QWwL8t zyip?u_nI+K$uh{y)~}qj?(w0&=SE^8`_WMM zTybjG=999h38Yes7}-4*LJ7H)UE8{mE(6;8voE+TYY%33A>S6`G_95^5QHNTo_;Ao ztIQIZ_}49%{8|=O;isBZ?=7kfdF8_@azfoTd+hEJKWE!)$)N%HIe2cplaK`ry#=pV z0q{9w-`i0h@!R8K3GC{ivt{70IWG`EP|(1g7i_Q<>aEAT{5(yD z=!O?kq61VegV+st@XCw475j6vS)_z@efuqQgHQR1T4;|-#OLZNQJPV4k$AX1Uk8Lm z{N*b*ia=I+MB}kWpupJ~>!C@xEN#Wa7V+7{m4j8c?)ChV=D?o~sjT?0C_AQ7B-vxqX30s0I_`2$in86#`mAsT-w?j{&AL@B3$;P z31G4(lV|b}uSDCIrjk+M1R!X7s4Aabn<)zpgT}#gE|mIvV38^ODy@<&yflpCwS#fRf9ZX3lPV_?8@C5)A;T zqmouFLFk;qIs4rA=hh=GL~sCFsXHsqO6_y~*AFt939UYVBSx1s(=Kb&5;j7cSowdE;7()CC2|-i9Zz+_BIw8#ll~-tyH?F3{%`QCsYa*b#s*9iCc`1P1oC26?`g<9))EJ3%xz+O!B3 zZ7$j~To)C@PquR>a1+Dh>-a%IvH_Y7^ys|4o?E%3`I&ADXfC8++hAdZfzIT#%C+Jz z1lU~K_vAm0m8Qk}K$F>|>RPK%<1SI0(G+8q~H zAsjezyP+u!Se4q3GW)`h`NPSRlMoBjCzNPesWJwVTY!o@G8=(6I%4XHGaSiS3MEBK zhgGFv6Jc>L$4jVE!I?TQuwvz_%CyO!bLh94nqK11C2W$*aa2ueGopG8DnBICVUORP zgytv#)49fVXDaR$SukloYC3u7#5H)}1K21=?DKj^U)8G;MS)&Op)g^zR2($<>C*zW z;X7`hLxiIO#J`ANdyAOJle4V%ppa*(+0i3w;8i*BA_;u8gOO6)MY`ueq7stBMJTB; z-a0R>hT*}>z|Gg}@^zDL1MrH+2hsR8 zHc}*9IvuQC^Ju)^#Y{fOr(96rQNPNhxc;mH@W*m206>Lo<*SaaH?~8zg&f&%YiOEG zGiz?*CP>Bci}!WiS=zj#K5I}>DtpregpP_tfZtPa(N<%vo^#WCQ5BTv0vr%Z{)0q+ z)RbfHktUm|lg&U3YM%lMUM(fu}i#kjX9h>GYctkx9Mt_8{@s%!K_EI zScgwy6%_fR?CGJQtmgNAj^h9B#zmaMDWgH55pGuY1Gv7D z;8Psm(vEPiwn#MgJYu4Ty9D|h!?Rj0ddE|&L3S{IP%H4^N!m`60ZwZw^;eg4sk6K{ ziA^`Sbl_4~f&Oo%n;8Ye(tiAdlZKI!Z=|j$5hS|D$bDJ}p{gh$KN&JZYLUjv4h{NY zBJ>X9z!xfDGY z+oh_Z&_e#Q(-}>ssZfm=j$D&4W4FNy&-kAO1~#3Im;F)Nwe{(*75(p=P^VI?X0GFakfh+X-px4a%Uw@fSbmp9hM1_~R>?Z8+ ziy|e9>8V*`OP}4x5JjdWp}7eX;lVxp5qS}0YZek;SNmm7tEeSF*-dI)6U-A%m6YvCgM(}_=k#a6o^%-K4{`B1+}O4x zztDT%hVb;v#?j`lTvlFQ3aV#zkX=7;YFLS$uIzb0E3lozs5`Xy zi~vF+%{z9uLjKvKPhP%x5f~7-Gj+%5N`%^=yk*Qn{`> z;xj&ROY6g`iy2a@{O)V(jk&8#hHACVDXey5a+KDod_Z&}kHM}xt7}Md@pil{2x7E~ zL$k^d2@Ec2XskjrN+IILw;#7((abu;OJii&v3?60x>d_Ma(onIPtcVnX@ELF0aL?T zSmWiL3(dOFkt!x=1O!_0n(cAzZW+3nHJ{2S>tgSK?~cFha^y(l@-Mr2W$%MN{#af8J;V*>hdq!gx=d0h$T7l}>91Wh07)9CTX zh2_ZdQCyFOQ)l(}gft0UZG`Sh2`x-w`5vC2UD}lZs*5 zG76$akzn}Xi))L3oGJ75#pcN=cX3!=57$Ha=hQ2^lwdyU#a}4JJOz6ddR%zae%#4& za)bFj)z=YQela(F#Y|Q#dp}PJghITwXouVaMq$BM?K%cXn9^Y@g43$=O)F&ZlOUom zJiad#dea;-eywBA@e&D6Pdso1?2^(pXiN91?jvcaUyYoKUmvl5G9e$W!okWe*@a<^ z8cQQ6cNSf+UPDx%?_G4aIiybZHHagF{;IcD(dPO!#=u zWfqLcPc^+7Uu#l(Bpxft{*4lv#*u7X9AOzDO z1D9?^jIo}?%iz(_dwLa{ex#T}76ZfN_Z-hwpus9y+4xaUu9cX}&P{XrZVWE{1^0yw zO;YhLEW!pJcbCt3L8~a7>jsaN{V3>tz6_7`&pi%GxZ=V3?3K^U+*ryLSb)8^IblJ0 zSRLNDvIxt)S}g30?s_3NX>F?NKIGrG_zB9@Z>uSW3k2es_H2kU;Rnn%j5qP)!XHKE zPB2mHP~tLCg4K_vH$xv`HbRsJwbZMUV(t=ez;Ec(vyHH)FbfLg`c61I$W_uBB>i^r z&{_P;369-&>23R%qNIULe=1~T$(DA`ev*EWZ6j(B$(te}x1WvmIll21zvygkS%vwG zzkR6Z#RKA2!z!C%M!O>!=Gr0(J0FP=-MN=5t-Ir)of50y10W}j`GtRCsXBakrKtG& zazmITDJMA0C51&BnLY)SY9r)NVTMs);1<=oosS9g31l{4ztjD3#+2H7u_|66b|_*O z;Qk6nalpqdHOjx|K&vUS_6ITgGll;TdaN*ta=M_YtyC)I9Tmr~VaPrH2qb6sd~=AcIxV+%z{E&0@y=DPArw zdV7z(G1hBx7hd{>(cr43^WF%4Y@PXZ?wPpj{OQ#tvc$pABJbvPGvdR`cAtHn)cSEV zrpu}1tJwQ3y!mSmH*uz*x0o|CS<^w%&KJzsj~DU0cLQUxk5B!hWE>aBkjJle8z~;s z-!A=($+}Jq_BTK5^B!`R>!MulZN)F=iXXeUd0w5lUsE5VP*H*oCy(;?S$p*TVvTxwAeWFB$jHyb0593)$zqalVlDX=GcCN1gU0 zlgU)I$LcXZ8Oyc2TZYTPu@-;7<4YYB-``Qa;IDcvydIA$%kHhJKV^m*-zxcvU4viy&Kr5GVM{IT>WRywKQ9;>SEiQD*NqplK-KK4YR`p0@JW)n_{TU3bt0 zim%;(m1=#v2}zTps=?fU5w^(*y)xT%1vtQH&}50ZF!9YxW=&7*W($2kgKyz1mUgfs zfV<*XVVIFnohW=|j+@Kfo!#liQR^x>2yQdrG;2o8WZR+XzU_nG=Ed2rK?ntA;K5B{ z>M8+*A4!Jm^Bg}aW?R?6;@QG@uQ8&oJ{hFixcfEnJ4QH?A4>P=q29oDGW;L;= z9-a0;g%c`C+Ai!UmK$NC*4#;Jp<1=TioL=t^YM)<<%u#hnnfSS`nq63QKGO1L8RzX z@MFDqs1z ztYmxDl@LU)5acvHk)~Z`RW7=aJ_nGD!mOSYD>5Odjn@TK#LY{jf?+piB5AM-CAoT_ z?S-*q7}wyLJzK>N%eMPuFgN)Q_otKP;aqy=D5f!7<=n(lNkYRXVpkB{TAYLYg{|(jtRqYmg$xH zjmq?B(RE4 zQx^~Pt}gxC2~l=K$$-sYy_r$CO(d=+b3H1MB*y_5g6WLaWTXn+TKQ|hNY^>Mp6k*$ zwkovomhu776vQATqT4blf~g;TY(MWCrf^^yfWJvSAB$p5l;jm@o#=!lqw+Lqfq>X= z$6~kxfm7`3q4zUEB;u4qa#BdJxO!;xGm)wwuisj{0y2x{R(IGMrsIzDY9LW>m!Y`= z04sx3IjnYvL<4JqxQ8f7qYd0s2Ig%`ytYPEMKI)s(LD}D@EY>x`VFtqvnADNBdeao zC96X+MxnwKmjpg{U&gP3HE}1=s!lv&D{6(g_lzyF3A`7Jn*&d_kL<;dAFx!UZ>hB8 z5A*%LsAn;VLp>3${0>M?PSQ)9s3}|h2e?TG4_F{}{Cs>#3Q*t$(CUc}M)I}8cPF6% z=+h(Kh^8)}gj(0}#e7O^FQ6`~fd1#8#!}LMuo3A0bN`o}PYsm!Y}sdOz$+Tegc=qT z8x`PH$7lvnhJp{kHWb22l;@7B7|4yL4UOOVM0MP_>P%S1Lnid)+k9{+3D+JFa#Pyf zhVc#&df87APl4W9X)F3pGS>@etfl=_E5tBcVoOfrD4hmVeTY-cj((pkn%n@EgN{0f zwb_^Rk0I#iZuHK!l*lN`ceJn(sI{$Fq6nN& zE<-=0_2WN}m+*ivmIOxB@#~Q-cZ>l136w{#TIJe478`KE7@=a{>SzPHsKLzYAyBQO zAtuuF$-JSDy_S@6GW0MOE~R)b;+0f%_NMrW(+V#c_d&U8Z9+ec4=HmOHw?gdjF(Lu zzra83M_BoO-1b3;9`%&DHfuUY)6YDV21P$C!Rc?mv&{lx#f8oc6?0?x zK08{WP65?#>(vPfA-c=MCY|%*1_<3D4NX zeVTi-JGl2uP_2@0F{G({pxQOXt_d{g_CV6b?jNpfUG9;8yle-^4KHRvZs-_2siata zt+d_T@U$&t*xaD22(fH(W1r$Mo?3dc%Tncm=C6{V9y{v&VT#^1L04vDrLM9qBoZ4@ z6DBN#m57hX7$C(=#$Y5$bJmwA$T8jKD8+6A!-IJwA{WOfs%s}yxUw^?MRZjF$n_KN z6`_bGXcmE#5e4Ym)aQJ)xg3Pg0@k`iGuHe?f(5LtuzSq=nS^5z>vqU0EuZ&75V%Z{ zYyhRLN^)$c6Ds{f7*FBpE;n5iglx5PkHfWrj3`x^j^t z7ntuV`g!9Xg#^3!x)l*}IW=(Tz3>Y5l4uGaB&lz{GDjm2D5S$CExLT`I1#n^lBH7Y zDgpMag@`iETKAI=p<5E#LTkwzVR@=yY|uBVI1HG|8h+d;G-qfuj}-ZR6fN>EfCCW z9~wRQoAPEa#aO?3h?x{YvV*d+NtPkf&4V0k4|L=uj!U{L+oLa(z#&iuhJr3-PjO3R z5s?=nn_5^*^Rawr>>Nr@K(jwkB#JK-=+HqwfdO<+P5byeim)wvqGlP-P|~Nse8=XF zz`?RYB|D6SwS}C+YQv+;}k6$-%D(@+t14BL@vM z2q%q?f6D-A5s$_WY3{^G0F131bbh|g!}#BKw=HQ7mx;Dzg4Z*bTLQSfo{ed{4}NZW zfrRm^Ca$rlE{Ue~uYv>R9{3smwATcdM_6+yWIO z*ZRH~uXE@#p$XTbCt5j7j2=86e{9>HIB6xDzV+vAo&B?KUiMP|ttOElepnl%|DPqL b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/fixtures/owner-stacks/public/manifest.json b/fixtures/owner-stacks/public/manifest.json new file mode 100644 index 0000000000000..080d6c77ac21b --- /dev/null +++ b/fixtures/owner-stacks/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/fixtures/owner-stacks/public/robots.txt b/fixtures/owner-stacks/public/robots.txt new file mode 100644 index 0000000000000..e9e57dc4d41b9 --- /dev/null +++ b/fixtures/owner-stacks/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/fixtures/owner-stacks/src/App.css b/fixtures/owner-stacks/src/App.css new file mode 100644 index 0000000000000..3c9a8db9251d0 --- /dev/null +++ b/fixtures/owner-stacks/src/App.css @@ -0,0 +1,62 @@ +.App-header { + background-color: #282c34; + min-height: 10vh; + display: flex; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.page { + display: flex; + height: 90vh; +} + +.content { + flex: 4; + background-color: #f0f0f0; + padding: 20px; + display: flex; + flex-direction: column; + text-align: left; +} + +.content.highlight { + background-color: yellow; +} + +.sidebar { + flex: 1; + background-color: #d0d0d0; + padding: 20px; +} + +.line-number { + display: inline-block; + width: 30px; + text-align: right; + margin-right: 10px; + color: #888; +} + +.text-symbol { + position: relative; + display: inline-block; +} + +.hovercard { + display: none; + position: absolute; + top: -40px; + left: 0; + background-color: white; + border: 1px solid #ccc; + padding: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + z-index: 10; +} + +.text-symbol:hover .hovercard { + display: block; +} diff --git a/fixtures/owner-stacks/src/App.js b/fixtures/owner-stacks/src/App.js new file mode 100644 index 0000000000000..69d32f00e6b7e --- /dev/null +++ b/fixtures/owner-stacks/src/App.js @@ -0,0 +1,143 @@ +import {useState} from 'react'; +import {flushSync} from 'react-dom'; +import './App.css'; + +const text = ` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + `; + +const loremIpsum = text + text; + +function TextSymbol({char, highlight}) { + const randomColor = highlight + ? `#${Math.floor(Math.random() * 16777215).toString(16)}` + : 'transparent'; + return ( + + {char} +
+

Character: {char}

+

Color: {randomColor}

+
+
+ ); +} + +function TextLine({sentence, highlight, lineNumber}) { + const randomColor = highlight + ? `#${Math.floor(Math.random() * 16777215).toString(16)}` + : 'transparent'; + return ( +
+ {lineNumber} + {sentence.split('').map((char, index) => ( + + ))} +
+ ); +} + +function App() { + const [highlight, setHighlight] = useState(false); + + const toggleHighlight = () => { + console.time('toggleHighlight'); + flushSync(() => { + setHighlight(!highlight); + }); + console.timeEnd('toggleHighlight'); + }; + + return ( +
+
Owner Stacks Stress Test
+
+
+ {loremIpsum + .trim() + .split('\n') + .map((sentence, index) => ( + + ))} +
+
+ +
+
+
+ ); +} + +export default App; diff --git a/fixtures/owner-stacks/src/index.css b/fixtures/owner-stacks/src/index.css new file mode 100644 index 0000000000000..ec2585e8c0bb8 --- /dev/null +++ b/fixtures/owner-stacks/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/fixtures/owner-stacks/src/index.js b/fixtures/owner-stacks/src/index.js new file mode 100644 index 0000000000000..d563c0fb10ba0 --- /dev/null +++ b/fixtures/owner-stacks/src/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/fixtures/owner-stacks/src/logo.svg b/fixtures/owner-stacks/src/logo.svg new file mode 100644 index 0000000000000..9dfc1c058cebb --- /dev/null +++ b/fixtures/owner-stacks/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fixtures/owner-stacks/src/reportWebVitals.js b/fixtures/owner-stacks/src/reportWebVitals.js new file mode 100644 index 0000000000000..b0320f330b548 --- /dev/null +++ b/fixtures/owner-stacks/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/fixtures/owner-stacks/yarn.lock b/fixtures/owner-stacks/yarn.lock new file mode 100644 index 0000000000000..84e18efe8383f --- /dev/null +++ b/fixtures/owner-stacks/yarn.lock @@ -0,0 +1,9688 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@apideck/better-ajv-errors@^0.3.1": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz#957d4c28e886a64a8141f7522783be65733ff097" + integrity sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA== + dependencies: + json-schema "^0.4.0" + jsonpointer "^5.0.0" + leven "^3.1.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.2", "@babel/code-frame@^7.8.3": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" + integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== + +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.7.tgz#0439347a183b97534d52811144d763a17f9d2b24" + integrity sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.5" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.7" + "@babel/parser" "^7.26.7" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.26.7" + "@babel/types" "^7.26.7" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/eslint-parser@^7.16.3": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.26.5.tgz#aa669f4d873f9cd617050cf3c40c19cd96307efb" + integrity sha512-Kkm8C8uxI842AwQADxl0GbcG1rupELYLShazYEZO/2DYjhyWXJIOUVOE3tBYm6JXzUCNJOZEzqc4rCW/jsEQYQ== + dependencies: + "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.1" + +"@babel/generator@^7.26.5", "@babel/generator@^7.7.2": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" + integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== + dependencies: + "@babel/parser" "^7.26.5" + "@babel/types" "^7.26.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" + integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.25.9" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0" + integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + regexpu-core "^6.2.0" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" + integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-remap-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" + integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-wrap-function" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-replace-supers@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helper-wrap-function@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" + integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== + dependencies: + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helpers@^7.26.7": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.7.tgz#fd1d2a7c431b6e39290277aacfd8367857c576a4" + integrity sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A== + dependencies: + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.7" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.25.9", "@babel/parser@^7.26.5", "@babel/parser@^7.26.7": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.7.tgz#e114cd099e5f7d17b05368678da0fb9f69b3385c" + integrity sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w== + dependencies: + "@babel/types" "^7.26.7" + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" + integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" + integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" + integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" + integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" + integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-proposal-class-properties@^7.16.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-decorators@^7.16.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz#8680707f943d1a3da2cd66b948179920f097e254" + integrity sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-decorators" "^7.25.9" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-optional-chaining@^7.16.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.16.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz#986b4ca8b7b5df3f67cee889cedeffc2e2bf14b3" + integrity sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-flow@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz#96507595c21b45fccfc2bc758d5c45452e6164fa" + integrity sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-assertions@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.25.9", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-async-generator-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2" + integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-remap-async-to-generator" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-transform-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" + integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-remap-async-to-generator" "^7.25.9" + +"@babel/plugin-transform-block-scoped-functions@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz#3dc4405d31ad1cbe45293aa57205a6e3b009d53e" + integrity sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + +"@babel/plugin-transform-block-scoping@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" + integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-class-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" + integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-class-static-block@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" + integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-classes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/traverse" "^7.25.9" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/template" "^7.25.9" + +"@babel/plugin-transform-destructuring@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-dotall-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" + integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-duplicate-keys@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" + integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" + integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-dynamic-import@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" + integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-exponentiation-operator@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" + integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-export-namespace-from@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" + integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-flow-strip-types@^7.16.0": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz#2904c85a814e7abb1f4850b8baf4f07d0a2389d4" + integrity sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/plugin-syntax-flow" "^7.26.0" + +"@babel/plugin-transform-for-of@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" + integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + +"@babel/plugin-transform-function-name@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== + dependencies: + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-transform-json-strings@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" + integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-logical-assignment-operators@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" + integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-member-expression-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-modules-amd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" + integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== + dependencies: + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-modules-commonjs@^7.25.9", "@babel/plugin-transform-modules-commonjs@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== + dependencies: + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-modules-systemjs@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" + integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== + dependencies: + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-transform-modules-umd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" + integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== + dependencies: + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" + integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-new-target@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" + integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.26.6": + version "7.26.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz#fbf6b3c92cb509e7b319ee46e3da89c5bedd31fe" + integrity sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + +"@babel/plugin-transform-numeric-separator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" + integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-object-rest-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" + integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== + dependencies: + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" + +"@babel/plugin-transform-object-super@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + +"@babel/plugin-transform-optional-catch-binding@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" + integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" + integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + +"@babel/plugin-transform-parameters@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-private-methods@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-private-property-in-object@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" + integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-property-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-constant-elements@^7.12.1": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz#08a1de35a301929b60fdf2788a54b46cd8ecd0ef" + integrity sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz#4b79746b59efa1f38c8695065a92a9f5afb24f7d" + integrity sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-jsx-development@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz#8fd220a77dd139c07e25225a903b8be8c829e0d7" + integrity sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.25.9" + +"@babel/plugin-transform-react-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz#06367940d8325b36edff5e2b9cbe782947ca4166" + integrity sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/plugin-transform-react-pure-annotations@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz#ea1c11b2f9dbb8e2d97025f43a3b5bc47e18ae62" + integrity sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-regenerator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" + integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-regexp-modifiers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" + integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-reserved-words@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" + integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-runtime@^7.16.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz#62723ea3f5b31ffbe676da9d6dae17138ae580ea" + integrity sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.10.6" + babel-plugin-polyfill-regenerator "^0.6.1" + semver "^6.3.1" + +"@babel/plugin-transform-shorthand-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + +"@babel/plugin-transform-sticky-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" + integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-template-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" + integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-typeof-symbol@^7.26.7": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz#d0e33acd9223744c1e857dbd6fa17bd0a3786937" + integrity sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw== + dependencies: + "@babel/helper-plugin-utils" "^7.26.5" + +"@babel/plugin-transform-typescript@^7.25.9": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.7.tgz#64339515ea3eff610160f62499c3ef437d0ac83d" + integrity sha512-5cJurntg+AT+cgelGP9Bt788DKiAw9gIMSMU2NJrLAilnj0m8WZWUNZPSLOmadYsujHutpgElO+50foX+ib/Wg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-syntax-typescript" "^7.25.9" + +"@babel/plugin-transform-unicode-escapes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" + integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-unicode-property-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" + integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-unicode-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" + integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-unicode-sets-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" + integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.7.tgz#24d38e211f4570b8d806337035cc3ae798e0c36d" + integrity sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-import-assertions" "^7.26.0" + "@babel/plugin-syntax-import-attributes" "^7.26.0" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.25.9" + "@babel/plugin-transform-async-generator-functions" "^7.25.9" + "@babel/plugin-transform-async-to-generator" "^7.25.9" + "@babel/plugin-transform-block-scoped-functions" "^7.26.5" + "@babel/plugin-transform-block-scoping" "^7.25.9" + "@babel/plugin-transform-class-properties" "^7.25.9" + "@babel/plugin-transform-class-static-block" "^7.26.0" + "@babel/plugin-transform-classes" "^7.25.9" + "@babel/plugin-transform-computed-properties" "^7.25.9" + "@babel/plugin-transform-destructuring" "^7.25.9" + "@babel/plugin-transform-dotall-regex" "^7.25.9" + "@babel/plugin-transform-duplicate-keys" "^7.25.9" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-dynamic-import" "^7.25.9" + "@babel/plugin-transform-exponentiation-operator" "^7.26.3" + "@babel/plugin-transform-export-namespace-from" "^7.25.9" + "@babel/plugin-transform-for-of" "^7.25.9" + "@babel/plugin-transform-function-name" "^7.25.9" + "@babel/plugin-transform-json-strings" "^7.25.9" + "@babel/plugin-transform-literals" "^7.25.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" + "@babel/plugin-transform-member-expression-literals" "^7.25.9" + "@babel/plugin-transform-modules-amd" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.26.3" + "@babel/plugin-transform-modules-systemjs" "^7.25.9" + "@babel/plugin-transform-modules-umd" "^7.25.9" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-new-target" "^7.25.9" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.26.6" + "@babel/plugin-transform-numeric-separator" "^7.25.9" + "@babel/plugin-transform-object-rest-spread" "^7.25.9" + "@babel/plugin-transform-object-super" "^7.25.9" + "@babel/plugin-transform-optional-catch-binding" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" + "@babel/plugin-transform-private-methods" "^7.25.9" + "@babel/plugin-transform-private-property-in-object" "^7.25.9" + "@babel/plugin-transform-property-literals" "^7.25.9" + "@babel/plugin-transform-regenerator" "^7.25.9" + "@babel/plugin-transform-regexp-modifiers" "^7.26.0" + "@babel/plugin-transform-reserved-words" "^7.25.9" + "@babel/plugin-transform-shorthand-properties" "^7.25.9" + "@babel/plugin-transform-spread" "^7.25.9" + "@babel/plugin-transform-sticky-regex" "^7.25.9" + "@babel/plugin-transform-template-literals" "^7.25.9" + "@babel/plugin-transform-typeof-symbol" "^7.26.7" + "@babel/plugin-transform-unicode-escapes" "^7.25.9" + "@babel/plugin-transform-unicode-property-regex" "^7.25.9" + "@babel/plugin-transform-unicode-regex" "^7.25.9" + "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.10.6" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.38.1" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa" + integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-transform-react-display-name" "^7.25.9" + "@babel/plugin-transform-react-jsx" "^7.25.9" + "@babel/plugin-transform-react-jsx-development" "^7.25.9" + "@babel/plugin-transform-react-pure-annotations" "^7.25.9" + +"@babel/preset-typescript@^7.16.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-typescript" "^7.25.9" + +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" + integrity sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.25.9", "@babel/template@^7.3.3": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.7", "@babel/traverse@^7.7.2": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.7.tgz#99a0a136f6a75e7fb8b0a1ace421e0b25994b8bb" + integrity sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.5" + "@babel/parser" "^7.26.7" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.7" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.5", "@babel/types@^7.26.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.7.tgz#5e2b89c0768e874d4d061961f3a5a153d71dc17a" + integrity sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@csstools/normalize.css@*": + version "12.1.1" + resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.1.1.tgz#f0ad221b7280f3fc814689786fd9ee092776ef8f" + integrity sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ== + +"@csstools/postcss-cascade-layers@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz#8a997edf97d34071dd2e37ea6022447dd9e795ad" + integrity sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA== + dependencies: + "@csstools/selector-specificity" "^2.0.2" + postcss-selector-parser "^6.0.10" + +"@csstools/postcss-color-function@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz#2bd36ab34f82d0497cfacdc9b18d34b5e6f64b6b" + integrity sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-font-format-keywords@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz#677b34e9e88ae997a67283311657973150e8b16a" + integrity sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-hwb-function@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz#ab54a9fce0ac102c754854769962f2422ae8aa8b" + integrity sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-ic-unit@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz#28237d812a124d1a16a5acc5c3832b040b303e58" + integrity sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-is-pseudo-class@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz#846ae6c0d5a1eaa878fce352c544f9c295509cd1" + integrity sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA== + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +"@csstools/postcss-nested-calc@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz#d7e9d1d0d3d15cf5ac891b16028af2a1044d0c26" + integrity sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-normalize-display-values@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz#15da54a36e867b3ac5163ee12c1d7f82d4d612c3" + integrity sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-oklab-function@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz#88cee0fbc8d6df27079ebd2fa016ee261eecf844" + integrity sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-progressive-custom-properties@^1.1.0", "@csstools/postcss-progressive-custom-properties@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz#542292558384361776b45c85226b9a3a34f276fa" + integrity sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-stepped-value-functions@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz#f8772c3681cc2befed695e2b0b1d68e22f08c4f4" + integrity sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-text-decoration-shorthand@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz#ea96cfbc87d921eca914d3ad29340d9bcc4c953f" + integrity sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-trigonometric-functions@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz#94d3e4774c36d35dcdc88ce091336cb770d32756" + integrity sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-unset-value@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz#c99bb70e2cdc7312948d1eb41df2412330b81f77" + integrity sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g== + +"@csstools/selector-specificity@^2.0.0", "@csstools/selector-specificity@^2.0.2": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" + integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/console@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" + integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== + dependencies: + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== + dependencies: + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" + +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.1.0" + +"@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" + integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.9" + source-map "^0.6.0" + +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== + dependencies: + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-result@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" + integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== + dependencies: + "@jest/console" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" + +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" + integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== + +"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": + version "5.1.1-v1" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" + integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg== + dependencies: + eslint-scope "5.1.1" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.3": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz#f126be97c30b83ed777e2aeabd518bc592e6e7c4" + integrity sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ== + dependencies: + ansi-html "^0.0.9" + core-js-pure "^3.23.3" + error-stack-parser "^2.0.6" + html-entities "^2.1.0" + loader-utils "^2.0.4" + schema-utils "^4.2.0" + source-map "^0.7.3" + +"@rollup/plugin-babel@^5.2.0": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" + integrity sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@rollup/pluginutils" "^3.1.0" + +"@rollup/plugin-node-resolve@^11.2.1": + version "11.2.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz#82aa59397a29cd4e13248b106e6a4a1880362a60" + integrity sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/plugin-replace@^2.4.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a" + integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + magic-string "^0.25.7" + +"@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== + +"@rushstack/eslint-patch@^1.1.0": + version "1.10.5" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz#3a1c12c959010a55c17d46b395ed3047b545c246" + integrity sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A== + +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" + integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== + +"@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@surma/rollup-plugin-off-main-thread@^2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053" + integrity sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ== + dependencies: + ejs "^3.1.6" + json5 "^2.2.0" + magic-string "^0.25.0" + string.prototype.matchall "^4.0.6" + +"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" + integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== + +"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" + integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" + integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" + integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== + +"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" + integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== + +"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" + integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== + +"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" + integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== + +"@svgr/babel-plugin-transform-svg-component@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" + integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== + +"@svgr/babel-preset@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" + integrity sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" + "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" + "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" + "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" + "@svgr/babel-plugin-transform-svg-component" "^5.5.0" + +"@svgr/core@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" + integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ== + dependencies: + "@svgr/plugin-jsx" "^5.5.0" + camelcase "^6.2.0" + cosmiconfig "^7.0.0" + +"@svgr/hast-util-to-babel-ast@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" + integrity sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ== + dependencies: + "@babel/types" "^7.12.6" + +"@svgr/plugin-jsx@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" + integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA== + dependencies: + "@babel/core" "^7.12.3" + "@svgr/babel-preset" "^5.5.0" + "@svgr/hast-util-to-babel-ast" "^5.5.0" + svg-parser "^2.0.2" + +"@svgr/plugin-svgo@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" + integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ== + dependencies: + cosmiconfig "^7.0.0" + deepmerge "^4.2.2" + svgo "^1.2.2" + +"@svgr/webpack@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" + integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g== + dependencies: + "@babel/core" "^7.12.3" + "@babel/plugin-transform-react-constant-elements" "^7.12.1" + "@babel/preset-env" "^7.12.1" + "@babel/preset-react" "^7.12.5" + "@svgr/core" "^5.5.0" + "@svgr/plugin-jsx" "^5.5.0" + "@svgr/plugin-svgo" "^5.5.0" + loader-utils "^2.0.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/eslint@^7.29.0 || ^8.4.1": + version "8.56.12" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" + integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz#41fec4ea20e9c7b22f024ab88a95c6bb288f51b8" + integrity sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express-serve-static-core@^4.17.33": + version "4.19.6" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" + integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c" + integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/express@^4.17.13": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/graceful-fs@^4.1.2": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/http-proxy@^1.17.8": + version "1.17.15" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36" + integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "22.13.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.1.tgz#a2a3fefbdeb7ba6b89f40371842162fac0934f33" + integrity sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew== + dependencies: + undici-types "~6.20.0" + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/prettier@^2.1.5": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + +"@types/q@^1.5.1": + version "1.5.8" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837" + integrity sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw== + +"@types/qs@*": + version "6.9.18" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.18.tgz#877292caa91f7c1b213032b34626505b746624c2" + integrity sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/semver@^7.3.12": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.1": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/sockjs@^0.3.33": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/trusted-types@^2.0.2": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +"@types/ws@^8.5.5": + version "8.5.14" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.14.tgz#93d44b268c9127d96026cf44353725dd9b6c3c21" + integrity sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^16.0.0": + version "16.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" + integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.5.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@^5.0.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" + integrity sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw== + dependencies: + "@typescript-eslint/utils" "5.62.0" + +"@typescript-eslint/parser@^5.5.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== + +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== + +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" + +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== + +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.3, abab@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +accepts@~1.3.4, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.14.0, acorn@^8.2.4, acorn@^8.8.2, acorn@^8.9.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +address@^1.0.1, address@^1.1.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + +adjust-sourcemap-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz#fc4a0fd080f7d10471f30a7320f25560ade28c99" + integrity sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A== + dependencies: + loader-utils "^2.0.0" + regex-parser "^2.2.11" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.6.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-html@^0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.9.tgz#6512d02342ae2cc68131952644a129cb734cd3f0" + integrity sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +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== + +aria-query@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-includes@^3.1.6, array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlast@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.findlastindex@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +array.prototype.reduce@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz#6aadc2f995af29cb887eb866d981dc85ab6f7dc7" + integrity sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-array-method-boxes-properly "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + is-string "^1.0.7" + +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +ast-types-flow@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" + integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== + +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== + +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +autoprefixer@^10.4.13: + version "10.4.20" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" + integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== + dependencies: + browserslist "^4.23.3" + caniuse-lite "^1.0.30001646" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.1" + postcss-value-parser "^4.2.0" + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axe-core@^4.10.0: + version "4.10.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" + integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== + +axobject-query@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" + integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== + +babel-jest@^27.4.2, babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-loader@^8.2.3: + version "8.4.1" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.4.1.tgz#6ccb75c66e62c3b144e1c5f2eaec5b8f6c08c675" + integrity sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.4" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +babel-plugin-named-asset-import@^0.3.8: + version "0.3.8" + resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz#6b7fa43c59229685368683c28bc9734f24524cc2" + integrity sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q== + +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.12" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" + integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.6.3" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.10.6: + version "0.10.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" + integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.2" + core-js-compat "^3.38.0" + +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8" + integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.3" + +babel-plugin-transform-react-remove-prop-types@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" + integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== + +babel-preset-current-node-syntax@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== + dependencies: + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" + +babel-preset-react-app@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz#ed6005a20a24f2c88521809fa9aea99903751584" + integrity sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg== + dependencies: + "@babel/core" "^7.16.0" + "@babel/plugin-proposal-class-properties" "^7.16.0" + "@babel/plugin-proposal-decorators" "^7.16.4" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" + "@babel/plugin-proposal-numeric-separator" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.0" + "@babel/plugin-proposal-private-methods" "^7.16.0" + "@babel/plugin-transform-flow-strip-types" "^7.16.0" + "@babel/plugin-transform-react-display-name" "^7.16.0" + "@babel/plugin-transform-runtime" "^7.16.4" + "@babel/preset-env" "^7.16.4" + "@babel/preset-react" "^7.16.0" + "@babel/preset-typescript" "^7.16.0" + "@babel/runtime" "^7.16.3" + babel-plugin-macros "^3.1.0" + babel-plugin-transform-react-remove-prop-types "^0.4.24" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +bfj@^7.0.2: + version "7.1.0" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.1.0.tgz#c5177d522103f9040e1b12980fe8c38cf41d3f8b" + integrity sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw== + dependencies: + bluebird "^3.7.2" + check-types "^11.2.3" + hoopy "^0.1.4" + jsonpath "^1.1.1" + tryer "^1.0.1" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" + integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.4, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.3: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +builtin-modules@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840" + integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681" + integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA== + dependencies: + call-bind-apply-helpers "^1.0.1" + get-intrinsic "^1.2.6" + +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== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0, camelcase@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: + version "1.0.30001697" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz#040bbbb54463c4b4b3377c716b34a322d16e6fc7" + integrity sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ== + +case-sensitive-paths-webpack-plugin@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" + integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== + +chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +char-regex@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.2.tgz#81385bb071af4df774bff8721d0ca15ef29ea0bb" + integrity sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg== + +check-types@^11.2.3: + version "11.2.3" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71" + integrity sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg== + +chokidar@^3.4.2, chokidar@^3.5.3, chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" + integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== + +clean-css@^5.2.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== + dependencies: + source-map "~0.6.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.10: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +compressible@~2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.5.tgz#fdd256c0a642e39e314c478f6c2cd654edd74c93" + integrity sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q== + dependencies: + bytes "3.1.2" + compressible "~2.0.18" + debug "2.6.9" + negotiator "~0.6.4" + on-headers "~1.0.2" + safe-buffer "5.2.1" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confusing-browser-globals@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "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== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + +core-js-compat@^3.38.0, core-js-compat@^3.38.1: + version "3.40.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.40.0.tgz#7485912a5a4a4315c2fdb2cbdc623e6881c88b38" + integrity sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ== + dependencies: + browserslist "^4.24.3" + +core-js-pure@^3.23.3: + version "3.40.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.40.0.tgz#d9a019e9160f9b042eeb6abb92242680089d486e" + integrity sha512-AtDzVIgRrmRKQai62yuSIN5vNiQjcJakJb4fbhVw3ehxx7Lohphvw9SGNWKhLFqSxC4ilD0g/L1huAYFQU3Q6A== + +core-js@^3.19.2: + version "3.40.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.40.0.tgz#2773f6b06877d8eda102fc42f828176437062476" + integrity sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-blank-pseudo@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz#36523b01c12a25d812df343a32c322d2a2324561" + integrity sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ== + dependencies: + postcss-selector-parser "^6.0.9" + +css-declaration-sorter@^6.3.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" + integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g== + +css-has-pseudo@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz#57f6be91ca242d5c9020ee3e51bbb5b89fc7af73" + integrity sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw== + dependencies: + postcss-selector-parser "^6.0.9" + +css-loader@^6.5.1: + version "6.11.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" + integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + +css-minimizer-webpack-plugin@^3.2.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f" + integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q== + dependencies: + cssnano "^5.0.6" + jest-worker "^27.0.2" + postcss "^8.3.5" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-prefers-color-scheme@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz#ca8a22e5992c10a5b9d315155e7caee625903349" + integrity sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA== + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^3.2.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" + integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssdb@^7.1.0: + version "7.11.2" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.11.2.tgz#127a2f5b946ee653361a5af5333ea85a39df5ae5" + integrity sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^5.2.14: + version "5.2.14" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" + integrity sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A== + dependencies: + css-declaration-sorter "^6.3.1" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.1" + postcss-convert-values "^5.1.3" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.7" + postcss-merge-rules "^5.1.4" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.4" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.1" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.2" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" + integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== + +cssnano@^5.0.6: + version "5.1.15" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" + integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== + dependencies: + cssnano-preset-default "^5.2.14" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.0.2, csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@2.6.9, debug@^2.6.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decimal.js@^10.2.1: + version "10.5.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" + integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-port-alt@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +domelementtype@1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@^3.1.6: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.5.73: + version "1.5.92" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.92.tgz#81e8ebe06f8e2a49fdba84bd10e9ad5b63efffe0" + integrity sha512-BeHgmNobs05N1HMmMZ7YIuHfYBGlq/UmvlsTgg+fsbFs9xVMj+xJHFg19GN04+9Q+r8Xnh9LXqaYIyEWElnNgQ== + +emittery@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" + integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +enhanced-resolve@^5.17.1: + version "5.18.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" + integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +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" + +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es-abstract@^1.17.2, es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: + version "1.23.9" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.9.tgz#5b45994b7de78dada5c1bebf1379646b32b9d606" + integrity sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.2.7" + get-proto "^1.0.0" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-regex "^1.2.1" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.0" + math-intrinsics "^1.1.0" + object-inspect "^1.13.3" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.3" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.18" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-iterator-helpers@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" + integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-abstract "^1.23.6" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.6" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + iterator.prototype "^1.1.4" + safe-array-concat "^1.1.3" + +es-module-lexer@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" + integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== + +es-object-atoms@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^1.8.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-react-app@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz#73ba3929978001c5c86274c017ea57eb5fa644b4" + integrity sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA== + dependencies: + "@babel/core" "^7.16.0" + "@babel/eslint-parser" "^7.16.3" + "@rushstack/eslint-patch" "^1.1.0" + "@typescript-eslint/eslint-plugin" "^5.5.0" + "@typescript-eslint/parser" "^5.5.0" + babel-preset-react-app "^10.0.1" + confusing-browser-globals "^1.0.11" + eslint-plugin-flowtype "^8.0.3" + eslint-plugin-import "^2.25.3" + eslint-plugin-jest "^25.3.0" + eslint-plugin-jsx-a11y "^6.5.1" + eslint-plugin-react "^7.27.1" + eslint-plugin-react-hooks "^4.3.0" + eslint-plugin-testing-library "^5.0.1" + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== + dependencies: + debug "^3.2.7" + +eslint-plugin-flowtype@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912" + integrity sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ== + dependencies: + lodash "^4.17.21" + string-natural-compare "^3.0.1" + +eslint-plugin-import@^2.25.3: + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== + dependencies: + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.12.0" + hasown "^2.0.2" + is-core-module "^2.15.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" + semver "^6.3.1" + string.prototype.trimend "^1.0.8" + tsconfig-paths "^3.15.0" + +eslint-plugin-jest@^25.3.0: + version "25.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz#ff4ac97520b53a96187bad9c9814e7d00de09a6a" + integrity sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + +eslint-plugin-jsx-a11y@^6.5.1: + version "6.10.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" + integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== + dependencies: + aria-query "^5.3.2" + array-includes "^3.1.8" + array.prototype.flatmap "^1.3.2" + ast-types-flow "^0.0.8" + axe-core "^4.10.0" + axobject-query "^4.1.0" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + hasown "^2.0.2" + jsx-ast-utils "^3.3.5" + language-tags "^1.0.9" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + safe-regex-test "^1.0.3" + string.prototype.includes "^2.0.1" + +eslint-plugin-react-hooks@^4.3.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" + integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== + +eslint-plugin-react@^7.27.1: + version "7.37.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz#1b6c80b6175b6ae4b26055ae4d55d04c414c7181" + integrity sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ== + dependencies: + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.3" + array.prototype.tosorted "^1.1.4" + doctrine "^2.1.0" + es-iterator-helpers "^1.2.1" + estraverse "^5.3.0" + hasown "^2.0.2" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.values "^1.2.1" + prop-types "^15.8.1" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.12" + string.prototype.repeat "^1.0.0" + +eslint-plugin-testing-library@^5.0.1: + version "5.11.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz#5b46cdae96d4a78918711c0b4792f90088e62d20" + integrity sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw== + dependencies: + "@typescript-eslint/utils" "^5.58.0" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-webpack-plugin@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz#1978cdb9edc461e4b0195a20da950cf57988347c" + integrity sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w== + dependencies: + "@types/eslint" "^7.29.0 || ^8.4.1" + jest-worker "^28.0.2" + micromatch "^4.0.5" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + +eslint@^8.3.0: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.2.tgz#76a0fd66fcfe154fd292667dc264019750b1657b" + integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== + dependencies: + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + +express@^4.17.3: + version "4.21.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.7.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.12" + proxy-addr "~2.0.7" + qs "6.13.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.2" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9, fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + +fastq@^1.6.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.0.tgz#a82c6b7c2bb4e44766d865f07997785fecfdcb89" + integrity sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +filesize@^8.0.6: + version "8.0.7" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" + integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + +follow-redirects@^1.0.0: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +for-each@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.4.tgz#814517ffc303d1399b2564d8165318e735d0341c" + integrity sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw== + dependencies: + is-callable "^1.2.7" + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +fork-ts-checker-webpack-plugin@^6.5.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3" + integrity sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +form-data@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.2.tgz#83ad9ced7c03feaad97e293d6f6091011e1659c8" + integrity sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0, fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-monkey@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" + integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.7.tgz#dcfcb33d3272e15f445d15124bc0a216189b9044" + integrity sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + function-bind "^1.1.2" + get-proto "^1.0.0" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^11.0.4, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +harmony-reflect@^1.4.6: + version "1.6.2" + resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" + integrity sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g== + +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + +has-symbols@^1.0.1, has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hoopy@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" + integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-entities@^2.1.0, html-entities@^2.3.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-webpack-plugin@^5.5.0: + version "5.6.3" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz#a31145f0fee4184d53a794f9513147df1e653685" + integrity sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.9" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.9.tgz#b817b3ca0edea6236225000d795378707c169cec" + integrity sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-proxy-middleware@^2.0.3: + version "2.0.7" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" + integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +idb@^7.0.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + +identity-obj-proxy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" + integrity sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA== + dependencies: + harmony-reflect "^1.4.6" + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +immer@^9.0.7: + version "9.0.21" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" + integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== + +import-fresh@^3.1.0, import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + +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== + +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-root@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.7, is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2, is-weakref@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +iterator.prototype@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" + integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== + dependencies: + define-data-property "^1.1.4" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.6" + get-proto "^1.0.0" + has-symbols "^1.1.0" + set-function-name "^2.0.2" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== + dependencies: + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" + +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + prompts "^2.0.1" + yargs "^16.2.0" + +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== + dependencies: + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" + +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== + dependencies: + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== + dependencies: + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== + +jest-regex-util@^28.0.0: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" + integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== + +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== + dependencies: + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" + +jest-resolve@^27.4.2, jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + natural-compare "^1.4.0" + pretty-format "^27.5.1" + semver "^7.3.2" + +jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== + dependencies: + "@jest/types" "^27.5.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.5.1" + leven "^3.1.0" + pretty-format "^27.5.1" + +jest-watch-typeahead@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz#b4a6826dfb9c9420da2f7bc900de59dad11266a9" + integrity sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw== + dependencies: + ansi-escapes "^4.3.1" + chalk "^4.0.0" + jest-regex-util "^28.0.0" + jest-watcher "^28.0.0" + slash "^4.0.0" + string-length "^5.0.1" + strip-ansi "^7.0.1" + +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== + dependencies: + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" + +jest-watcher@^28.0.0: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" + integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== + dependencies: + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.10.2" + jest-util "^28.1.3" + string-length "^4.0.1" + +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.0.2, jest-worker@^27.4.5, jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^28.0.2: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" + integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.4.3: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== + dependencies: + "@jest/core" "^27.5.1" + import-local "^3.0.2" + jest-cli "^27.5.1" + +jiti@^1.21.6: + version "1.21.7" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" + integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== + +"js-tokens@^3.0.0 || ^4.0.0", 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@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +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" + +jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.0.0" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +jsesc@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + 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== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2, json5@^2.2.0, 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== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonpath@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901" + integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== + dependencies: + esprima "1.2.2" + static-eval "2.0.2" + underscore "1.12.1" + +jsonpointer@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.4, klona@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +language-subtag-registry@^0.3.20: + version "0.3.23" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== + +language-tags@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" + integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== + dependencies: + language-subtag-registry "^0.3.20" + +launch-editor@^2.6.0: + version "2.9.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.9.1.tgz#253f173bd441e342d4344b4dae58291abb425047" + integrity sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lilconfig@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0, lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + +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== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0, loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.3.1.tgz#735b9a19fd63648ca7adbd31c2327dfe281304e5" + integrity sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg== + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +magic-string@^0.25.0, magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + +make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.1.2, memfs@^3.4.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== + dependencies: + fs-monkey "^1.0.4" + +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +"mime-db@>= 1.43.0 < 2": + version "1.53.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mini-css-extract-plugin@^2.4.5: + version "2.9.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz#966031b468917a5446f4c24a80854b2947503c5b" + integrity sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w== + dependencies: + schema-utils "^4.0.0" + tapable "^2.2.1" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mkdirp@~0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nth-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +nwsapi@^2.2.0: + version "2.2.16" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" + integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4, object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.entries@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.getownpropertydescriptors@^2.1.0: + version "2.1.8" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz#2f1fe0606ec1a7658154ccd4f728504f69667923" + integrity sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A== + dependencies: + array.prototype.reduce "^1.0.6" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + gopd "^1.0.1" + safe-array-concat "^1.1.2" + +object.groupby@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.1.0, object.values@^1.1.6, object.values@^1.2.0, object.values@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9, open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +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.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" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1, pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-attribute-case-insensitive@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741" + integrity sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ== + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-browser-comments@^4: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz#bcfc86134df5807f5d3c0eefa191d42136b5e72a" + integrity sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg== + +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" + integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== + dependencies: + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + +postcss-clamp@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-clamp/-/postcss-clamp-4.1.0.tgz#7263e95abadd8c2ba1bd911b0b5a5c9c93e02363" + integrity sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-functional-notation@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz#21a909e8d7454d3612d1659e471ce4696f28caec" + integrity sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-hex-alpha@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz#c66e2980f2fbc1a63f5b079663340ce8b55f25a5" + integrity sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-rebeccapurple@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz#63fdab91d878ebc4dd4b7c02619a0c3d6a56ced0" + integrity sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-colormin@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" + integrity sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" + integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-custom-media@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz#c8f9637edf45fef761b014c024cee013f80529ea" + integrity sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-custom-properties@^12.1.10: + version "12.1.11" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz#d14bb9b3989ac4d40aaa0e110b43be67ac7845cf" + integrity sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-custom-selectors@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz#1ab4684d65f30fed175520f82d223db0337239d9" + integrity sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-dir-pseudo-class@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz#2bf31de5de76added44e0a25ecf60ae9f7c7c26c" + integrity sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA== + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" + integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== + +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" + integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== + +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" + integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== + +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" + integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== + +postcss-double-position-gradients@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz#b96318fdb477be95997e86edd29c6e3557a49b91" + integrity sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +postcss-env-function@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-4.0.6.tgz#7b2d24c812f540ed6eda4c81f6090416722a8e7a" + integrity sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-flexbugs-fixes@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz#2028e145313074fc9abe276cb7ca14e5401eb49d" + integrity sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ== + +postcss-focus-visible@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz#50c9ea9afa0ee657fb75635fabad25e18d76bf9e" + integrity sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw== + dependencies: + postcss-selector-parser "^6.0.9" + +postcss-focus-within@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz#5b1d2ec603195f3344b716c0b75f61e44e8d2e20" + integrity sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ== + dependencies: + postcss-selector-parser "^6.0.9" + +postcss-font-variant@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz#efd59b4b7ea8bb06127f2d031bfbb7f24d32fa66" + integrity sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA== + +postcss-gap-properties@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz#f7e3cddcf73ee19e94ccf7cb77773f9560aa2fff" + integrity sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg== + +postcss-image-set-function@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz#08353bd756f1cbfb3b6e93182c7829879114481f" + integrity sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-initial@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-4.0.1.tgz#529f735f72c5724a0fb30527df6fb7ac54d7de42" + integrity sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ== + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-lab-function@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz#6fe4c015102ff7cd27d1bd5385582f67ebdbdc98" + integrity sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +postcss-load-config@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-loader@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" + integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.5" + +postcss-logical@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-5.0.4.tgz#ec75b1ee54421acc04d5921576b7d8db6b0e6f73" + integrity sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g== + +postcss-media-minmax@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz#7140bddec173e2d6d657edbd8554a55794e2a5b5" + integrity sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ== + +postcss-merge-longhand@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" + integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.1" + +postcss-merge-rules@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" + integrity sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" + integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" + integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" + integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== + dependencies: + browserslist "^4.21.4" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" + integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + +postcss-modules-local-by-default@^4.0.5: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz#d150f43837831dae25e4085596e84f6f5d6ec368" + integrity sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^7.0.0" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c" + integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA== + dependencies: + postcss-selector-parser "^7.0.0" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-nested@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== + dependencies: + postcss-selector-parser "^6.1.1" + +postcss-nesting@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.2.0.tgz#0b12ce0db8edfd2d8ae0aaf86427370b898890be" + integrity sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA== + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" + integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== + +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" + integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" + integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" + integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" + integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" + integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" + integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" + integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" + integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize/-/postcss-normalize-10.0.1.tgz#464692676b52792a06b06880a176279216540dd7" + integrity sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA== + dependencies: + "@csstools/normalize.css" "*" + postcss-browser-comments "^4" + sanitize.css "*" + +postcss-opacity-percentage@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz#5b89b35551a556e20c5d23eb5260fbfcf5245da6" + integrity sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A== + +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" + integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-overflow-shorthand@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz#7ed6486fec44b76f0eab15aa4866cda5d55d893e" + integrity sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-page-break@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-3.0.4.tgz#7fbf741c233621622b68d435babfb70dd8c1ee5f" + integrity sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ== + +postcss-place@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-7.0.5.tgz#95dbf85fd9656a3a6e60e832b5809914236986c4" + integrity sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-preset-env@^7.0.1: + version "7.8.3" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz#2a50f5e612c3149cc7af75634e202a5b2ad4f1e2" + integrity sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag== + dependencies: + "@csstools/postcss-cascade-layers" "^1.1.1" + "@csstools/postcss-color-function" "^1.1.1" + "@csstools/postcss-font-format-keywords" "^1.0.1" + "@csstools/postcss-hwb-function" "^1.0.2" + "@csstools/postcss-ic-unit" "^1.0.1" + "@csstools/postcss-is-pseudo-class" "^2.0.7" + "@csstools/postcss-nested-calc" "^1.0.0" + "@csstools/postcss-normalize-display-values" "^1.0.1" + "@csstools/postcss-oklab-function" "^1.1.1" + "@csstools/postcss-progressive-custom-properties" "^1.3.0" + "@csstools/postcss-stepped-value-functions" "^1.0.1" + "@csstools/postcss-text-decoration-shorthand" "^1.0.0" + "@csstools/postcss-trigonometric-functions" "^1.0.2" + "@csstools/postcss-unset-value" "^1.0.2" + autoprefixer "^10.4.13" + browserslist "^4.21.4" + css-blank-pseudo "^3.0.3" + css-has-pseudo "^3.0.4" + css-prefers-color-scheme "^6.0.3" + cssdb "^7.1.0" + postcss-attribute-case-insensitive "^5.0.2" + postcss-clamp "^4.1.0" + postcss-color-functional-notation "^4.2.4" + postcss-color-hex-alpha "^8.0.4" + postcss-color-rebeccapurple "^7.1.1" + postcss-custom-media "^8.0.2" + postcss-custom-properties "^12.1.10" + postcss-custom-selectors "^6.0.3" + postcss-dir-pseudo-class "^6.0.5" + postcss-double-position-gradients "^3.1.2" + postcss-env-function "^4.0.6" + postcss-focus-visible "^6.0.4" + postcss-focus-within "^5.0.4" + postcss-font-variant "^5.0.0" + postcss-gap-properties "^3.0.5" + postcss-image-set-function "^4.0.7" + postcss-initial "^4.0.1" + postcss-lab-function "^4.2.1" + postcss-logical "^5.0.4" + postcss-media-minmax "^5.0.0" + postcss-nesting "^10.2.0" + postcss-opacity-percentage "^1.1.2" + postcss-overflow-shorthand "^3.0.4" + postcss-page-break "^3.0.4" + postcss-place "^7.0.5" + postcss-pseudo-class-any-link "^7.1.6" + postcss-replace-overflow-wrap "^4.0.0" + postcss-selector-not "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-pseudo-class-any-link@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz#2693b221902da772c278def85a4d9a64b6e617ab" + integrity sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w== + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-reduce-initial@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" + integrity sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" + integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-replace-overflow-wrap@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz#d2df6bed10b477bf9c52fab28c568b4b29ca4319" + integrity sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw== + +postcss-selector-not@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz#8f0a709bf7d4b45222793fc34409be407537556d" + integrity sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ== + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9, postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c" + integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" + integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" + integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^7.0.35: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.3.5, postcss@^8.4.33, postcss@^8.4.4, postcss@^8.4.47: + version "8.5.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" + integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" + integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== + dependencies: + "@jest/schemas" "^28.1.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== + dependencies: + asap "~2.0.6" + +prompts@^2.0.1, prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +psl@^1.1.33: + version "1.15.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== + dependencies: + punycode "^2.3.1" + +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== + +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-app-polyfill@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7" + integrity sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w== + dependencies: + core-js "^3.19.2" + object-assign "^4.1.1" + promise "^8.1.0" + raf "^3.4.1" + regenerator-runtime "^0.13.9" + whatwg-fetch "^3.6.2" + +react-dev-utils@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" + integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== + dependencies: + "@babel/code-frame" "^7.16.0" + address "^1.1.2" + browserslist "^4.18.1" + chalk "^4.1.2" + cross-spawn "^7.0.3" + detect-port-alt "^1.1.6" + escape-string-regexp "^4.0.0" + filesize "^8.0.6" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.5.0" + global-modules "^2.0.0" + globby "^11.0.4" + gzip-size "^6.0.0" + immer "^9.0.7" + is-root "^2.1.0" + loader-utils "^3.2.0" + open "^8.4.0" + pkg-up "^3.1.0" + prompts "^2.4.2" + react-error-overlay "^6.0.11" + recursive-readdir "^2.2.2" + shell-quote "^1.7.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +react-dom@experimental: + version "0.0.0-experimental-d85cf3e5-20250205" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-experimental-d85cf3e5-20250205.tgz#0a0c4e50ee946041f426615d96913d89bbf0340f" + integrity sha512-3di+xOlrOFfk/Pb3JsZA0N6Bl0zYtzl2ZQeJgLdiU4UAYqbI7OGmhVW2cR7v3Ne5sRzlZZAJaJwzW1d/PvgADw== + dependencies: + scheduler "0.0.0-experimental-d85cf3e5-20250205" + +react-error-overlay@^6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" + integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +react-refresh@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" + integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== + +react-scripts@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003" + integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ== + dependencies: + "@babel/core" "^7.16.0" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" + "@svgr/webpack" "^5.5.0" + babel-jest "^27.4.2" + babel-loader "^8.2.3" + babel-plugin-named-asset-import "^0.3.8" + babel-preset-react-app "^10.0.1" + bfj "^7.0.2" + browserslist "^4.18.1" + camelcase "^6.2.1" + case-sensitive-paths-webpack-plugin "^2.4.0" + css-loader "^6.5.1" + css-minimizer-webpack-plugin "^3.2.0" + dotenv "^10.0.0" + dotenv-expand "^5.1.0" + eslint "^8.3.0" + eslint-config-react-app "^7.0.1" + eslint-webpack-plugin "^3.1.1" + file-loader "^6.2.0" + fs-extra "^10.0.0" + html-webpack-plugin "^5.5.0" + identity-obj-proxy "^3.0.0" + jest "^27.4.3" + jest-resolve "^27.4.2" + jest-watch-typeahead "^1.0.0" + mini-css-extract-plugin "^2.4.5" + postcss "^8.4.4" + postcss-flexbugs-fixes "^5.0.2" + postcss-loader "^6.2.1" + postcss-normalize "^10.0.1" + postcss-preset-env "^7.0.1" + prompts "^2.4.2" + react-app-polyfill "^3.0.0" + react-dev-utils "^12.0.1" + react-refresh "^0.11.0" + resolve "^1.20.0" + resolve-url-loader "^4.0.0" + sass-loader "^12.3.0" + semver "^7.3.5" + source-map-loader "^3.0.0" + style-loader "^3.3.1" + tailwindcss "^3.0.2" + terser-webpack-plugin "^5.2.5" + webpack "^5.64.4" + webpack-dev-server "^4.6.0" + webpack-manifest-plugin "^4.0.2" + workbox-webpack-plugin "^6.4.1" + optionalDependencies: + fsevents "^2.3.2" + +react@experimental: + version "0.0.0-experimental-d85cf3e5-20250205" + resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-experimental-d85cf3e5-20250205.tgz#9cc3334e5092713cd9827e4633d6c76c9256e4b8" + integrity sha512-tKNnCogN6Ly00Otm+rNQrtUyTA1pAhNNK948dmVEOso500sfEv5YjgfwVQIsptefvHtBCC4jjKw5tur518klhA== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" + +regenerate-unicode-properties@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.9: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-parser@^2.2.11: + version "2.3.0" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.3.0.tgz#4bb61461b1a19b8b913f3960364bb57887f920ee" + integrity sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg== + +regexp.prototype.flags@^1.5.3: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + +regexpu-core@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.2.0" + regjsgen "^0.8.0" + regjsparser "^0.12.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsgen@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== + +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== + dependencies: + jsesc "~3.0.2" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +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== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz#d50d4ddc746bb10468443167acf800dcd6c3ad57" + integrity sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA== + dependencies: + adjust-sourcemap-loader "^4.0.0" + convert-source-map "^1.7.0" + loader-utils "^2.0.0" + postcss "^7.0.35" + source-map "0.6.1" + +resolve.exports@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== + +resolve@^1.1.7, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.4, resolve@^1.22.8: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup-plugin-terser@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== + dependencies: + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" + +rollup@^2.43.1: + version "2.79.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.2.tgz#f150e4a5db4b121a21a747d762f701e5e9f49090" + integrity sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ== + optionalDependencies: + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2, safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + +safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sanitize.css@*: + version "13.0.0" + resolved "https://registry.yarnpkg.com/sanitize.css/-/sanitize.css-13.0.0.tgz#2675553974b27964c75562ade3bd85d79879f173" + integrity sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA== + +sass-loader@^12.3.0: + version "12.6.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.6.0.tgz#5148362c8e2cdd4b950f3c63ac5d16dbfed37bcb" + integrity sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA== + dependencies: + klona "^2.0.4" + neo-async "^2.6.2" + +sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +scheduler@0.0.0-experimental-d85cf3e5-20250205: + version "0.0.0-experimental-d85cf3e5-20250205" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-experimental-d85cf3e5-20250205.tgz#b0158d10f637749790d932db97ef139d03dc7522" + integrity sha512-4x4Z4ikaqwOq3/idgZ/8JyEvdglDq9tZsMWhyiQFOQQ60s7Jn4jIW0CRyPZwGqGL3XIOnJsJkCGH1e74tuVcCQ== + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.0.tgz#3b669f04f71ff2dfb5aba7ce2d5a9d79b35622c0" + integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0, serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + +set-function-length@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.7.3, shell-quote@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.2.tgz#d2d83e057959d53ec261311e9e9b8f51dcb2934a" + integrity sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA== + +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.0.6, side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-list-map@^2.0.0, source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-js@^1.0.1, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map-loader@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-3.0.2.tgz#af23192f9b344daa729f6772933194cc5fa54fee" + integrity sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg== + dependencies: + abab "^2.0.5" + iconv-lite "^0.6.3" + source-map-js "^1.0.1" + +source-map-support@^0.5.6, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +source-map@^0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +static-eval@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" + integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== + dependencies: + escodegen "^1.8.1" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-length@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-5.0.1.tgz#3d647f497b6e8e8d41e422f7e0b23bc536c8381e" + integrity sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow== + dependencies: + char-regex "^2.0.0" + strip-ansi "^7.0.1" + +string-natural-compare@^3.0.1: + version "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": + 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@^4.1.0, string-width@^4.2.0: + 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" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.includes@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" + integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + +string.prototype.matchall@^4.0.12, string.prototype.matchall@^4.0.6: + version "4.0.12" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" + integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-abstract "^1.23.6" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.6" + gopd "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + regexp.prototype.flags "^1.5.3" + set-function-name "^2.0.2" + side-channel "^1.1.0" + +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + +string.prototype.trimend@^1.0.8, string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +"strip-ansi-cjs@npm: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@^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" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" + integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@^3.3.1: + version "3.3.4" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" + integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== + +stylehacks@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" + integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== + dependencies: + browserslist "^4.21.4" + postcss-selector-parser "^6.0.4" + +sucrase@^3.35.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +tailwindcss@^3.0.2: + version "3.4.17" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" + integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.6.0" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.2" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.6" + lilconfig "^3.1.3" + micromatch "^4.0.8" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.1.1" + postcss "^8.4.47" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.2" + postcss-nested "^6.2.0" + postcss-selector-parser "^6.1.2" + resolve "^1.22.8" + sucrase "^3.35.0" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +temp-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" + integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== + +tempy@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.6.0.tgz#65e2c35abc06f1124a97f387b08303442bde59f3" + integrity sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw== + dependencies: + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +terser-webpack-plugin@^5.2.5, terser-webpack-plugin@^5.3.10: + version "5.3.11" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz#93c21f44ca86634257cac176f884f942b7ba3832" + integrity sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + serialize-javascript "^6.0.2" + terser "^5.31.1" + +terser@^5.0.0, terser@^5.10.0, terser@^5.31.1: + version "5.38.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.38.0.tgz#df742bb69ee556c29650e926ff154d116f4ccf79" + integrity sha512-a4GD5R1TjEeuCT6ZRiYMHmIf7okbCPEuhQET8bczV6FrQMMlFXA1n+G0KKjdlFCm3TEHV77GxfZB3vZSUQGFpg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +throat@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@^4.0.0: + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +tryer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.3: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + +underscore@1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== + +upath@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-browserslist-db@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" + integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +web-vitals@^2.1.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c" + integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg== + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +webpack-dev-middleware@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" + integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.6.0: + version "4.15.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz#9e0c70a42a012560860adb186986da1248333173" + integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.5" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.4" + ws "^8.13.0" + +webpack-manifest-plugin@^4.0.2: + version "4.1.1" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz#10f8dbf4714ff93a215d5a45bcc416d80506f94f" + integrity sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow== + dependencies: + tapable "^2.0.0" + webpack-sources "^2.2.0" + +webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" + integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.64.4: + version "5.97.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.97.1.tgz#972a8320a438b56ff0f1d94ade9e82eac155fa58" + integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.6" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.14.0" + browserslist "^4.24.0" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.1" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-fetch@^3.6.2: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.16, which-typed-array@^1.1.18: + version "1.1.18" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.18.tgz#df2389ebf3fbb246a71390e90730a9edb6ce17ad" + integrity sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" + for-each "^0.3.3" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5, word-wrap@~1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +workbox-background-sync@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f" + integrity sha512-trJd3ovpWCvzu4sW0E8rV3FUyIcC0W8G+AZ+VcqzzA890AsWZlUGOTSxIMmIHVusUw/FDq1HFWfy/kC/WTRqSg== + dependencies: + idb "^7.0.1" + workbox-core "6.6.1" + +workbox-broadcast-update@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e" + integrity sha512-fBhffRdaANdeQ1V8s692R9l/gzvjjRtydBOvR6WCSB0BNE2BacA29Z4r9/RHd9KaXCPl6JTdI9q0bR25YKP8TQ== + dependencies: + workbox-core "6.6.1" + +workbox-build@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0" + integrity sha512-INPgDx6aRycAugUixbKgiEQBWD0MPZqU5r0jyr24CehvNuLPSXp/wGOpdRJmts656lNiXwqV7dC2nzyrzWEDnw== + dependencies: + "@apideck/better-ajv-errors" "^0.3.1" + "@babel/core" "^7.11.1" + "@babel/preset-env" "^7.11.0" + "@babel/runtime" "^7.11.2" + "@rollup/plugin-babel" "^5.2.0" + "@rollup/plugin-node-resolve" "^11.2.1" + "@rollup/plugin-replace" "^2.4.1" + "@surma/rollup-plugin-off-main-thread" "^2.2.3" + ajv "^8.6.0" + common-tags "^1.8.0" + fast-json-stable-stringify "^2.1.0" + fs-extra "^9.0.1" + glob "^7.1.6" + lodash "^4.17.20" + pretty-bytes "^5.3.0" + rollup "^2.43.1" + rollup-plugin-terser "^7.0.0" + source-map "^0.8.0-beta.0" + stringify-object "^3.3.0" + strip-comments "^2.0.1" + tempy "^0.6.0" + upath "^1.2.0" + workbox-background-sync "6.6.1" + workbox-broadcast-update "6.6.1" + workbox-cacheable-response "6.6.1" + workbox-core "6.6.1" + workbox-expiration "6.6.1" + workbox-google-analytics "6.6.1" + workbox-navigation-preload "6.6.1" + workbox-precaching "6.6.1" + workbox-range-requests "6.6.1" + workbox-recipes "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + workbox-streams "6.6.1" + workbox-sw "6.6.1" + workbox-window "6.6.1" + +workbox-cacheable-response@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9" + integrity sha512-85LY4veT2CnTCDxaVG7ft3NKaFbH6i4urZXgLiU4AiwvKqS2ChL6/eILiGRYXfZ6gAwDnh5RkuDbr/GMS4KSag== + dependencies: + workbox-core "6.6.1" + +workbox-core@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265" + integrity sha512-ZrGBXjjaJLqzVothoE12qTbVnOAjFrHDXpZe7coCb6q65qI/59rDLwuFMO4PcZ7jcbxY+0+NhUVztzR/CbjEFw== + +workbox-expiration@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739" + integrity sha512-qFiNeeINndiOxaCrd2DeL1Xh1RFug3JonzjxUHc5WkvkD2u5abY3gZL1xSUNt3vZKsFFGGORItSjVTVnWAZO4A== + dependencies: + idb "^7.0.1" + workbox-core "6.6.1" + +workbox-google-analytics@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d" + integrity sha512-1TjSvbFSLmkpqLcBsF7FuGqqeDsf+uAXO/pjiINQKg3b1GN0nBngnxLcXDYo1n/XxK4N7RaRrpRlkwjY/3ocuA== + dependencies: + workbox-background-sync "6.6.1" + workbox-core "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + +workbox-navigation-preload@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059" + integrity sha512-DQCZowCecO+wRoIxJI2V6bXWK6/53ff+hEXLGlQL4Rp9ZaPDLrgV/32nxwWIP7QpWDkVEtllTAK5h6cnhxNxDA== + dependencies: + workbox-core "6.6.1" + +workbox-precaching@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2" + integrity sha512-K4znSJ7IKxCnCYEdhNkMr7X1kNh8cz+mFgx9v5jFdz1MfI84pq8C2zG+oAoeE5kFrUf7YkT5x4uLWBNg0DVZ5A== + dependencies: + workbox-core "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + +workbox-range-requests@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39" + integrity sha512-4BDzk28govqzg2ZpX0IFkthdRmCKgAKreontYRC5YsAPB2jDtPNxqx3WtTXgHw1NZalXpcH/E4LqUa9+2xbv1g== + dependencies: + workbox-core "6.6.1" + +workbox-recipes@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae" + integrity sha512-/oy8vCSzromXokDA+X+VgpeZJvtuf8SkQ8KL0xmRivMgJZrjwM3c2tpKTJn6PZA6TsbxGs3Sc7KwMoZVamcV2g== + dependencies: + workbox-cacheable-response "6.6.1" + workbox-core "6.6.1" + workbox-expiration "6.6.1" + workbox-precaching "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + +workbox-routing@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581" + integrity sha512-j4ohlQvfpVdoR8vDYxTY9rA9VvxTHogkIDwGdJ+rb2VRZQ5vt1CWwUUZBeD/WGFAni12jD1HlMXvJ8JS7aBWTg== + dependencies: + workbox-core "6.6.1" + +workbox-strategies@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf" + integrity sha512-WQLXkRnsk4L81fVPkkgon1rZNxnpdO5LsO+ws7tYBC6QQQFJVI6v98klrJEjFtZwzw/mB/HT5yVp7CcX0O+mrw== + dependencies: + workbox-core "6.6.1" + +workbox-streams@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26" + integrity sha512-maKG65FUq9e4BLotSKWSTzeF0sgctQdYyTMq529piEN24Dlu9b6WhrAfRpHdCncRS89Zi2QVpW5V33NX8PgH3Q== + dependencies: + workbox-core "6.6.1" + workbox-routing "6.6.1" + +workbox-sw@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c" + integrity sha512-R7whwjvU2abHH/lR6kQTTXLHDFU2izht9kJOvBRYK65FbwutT4VvnUAJIgHvfWZ/fokrOPhfoWYoPCMpSgUKHQ== + +workbox-webpack-plugin@^6.4.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531" + integrity sha512-zpZ+ExFj9NmiI66cFEApyjk7hGsfJ1YMOaLXGXBoZf0v7Iu6hL0ZBe+83mnDq3YYWAfA3fnyFejritjOHkFcrA== + dependencies: + fast-json-stable-stringify "^2.1.0" + pretty-bytes "^5.4.1" + upath "^1.2.0" + webpack-sources "^1.4.3" + workbox-build "6.6.1" + +workbox-window@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e" + integrity sha512-wil4nwOY58nTdCvif/KEZjQ2NP8uk3gGeRNy2jPBbzypU4BT4D9L8xiwbmDBpZlSgJd2xsT9FvSNU0gsxV51JQ== + dependencies: + "@types/trusted-types" "^2.0.2" + workbox-core "6.6.1" + +"wrap-ansi-cjs@npm: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@^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" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.6: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + +ws@^8.13.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yaml@^2.3.4: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 22e03527bcba2..4449207e8848e 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -315,7 +315,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(greeting)).toEqual( __DEV__ ? [ - {time: 11}, + {time: 12}, { name: 'Greeting', env: 'Server', @@ -327,7 +327,7 @@ describe('ReactFlight', () => { lastName: 'Smith', }, }, - {time: 12}, + {time: 13}, ] : undefined, ); @@ -359,7 +359,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(promise)).toEqual( __DEV__ ? [ - {time: 11}, + {time: 12}, { name: 'Greeting', env: 'Server', @@ -371,7 +371,7 @@ describe('ReactFlight', () => { lastName: 'Smith', }, }, - {time: 12}, + {time: 13}, ] : undefined, ); @@ -2807,7 +2807,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(promise)).toEqual( __DEV__ ? [ - {time: 18}, + {time: 20}, { name: 'ServerComponent', env: 'Server', @@ -2818,7 +2818,7 @@ describe('ReactFlight', () => { transport: expect.arrayContaining([]), }, }, - {time: 19}, + {time: 21}, ] : undefined, ); @@ -2829,7 +2829,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(thirdPartyChildren[0])).toEqual( __DEV__ ? [ - {time: 13}, + {time: 14}, { name: 'ThirdPartyComponent', env: 'third-party', @@ -2838,15 +2838,15 @@ describe('ReactFlight', () => { stack: ' in Object. (at **)', props: {}, }, - {time: 14}, - {time: 21}, // This last one is when the promise resolved into the first party. + {time: 15}, + {time: 23}, // This last one is when the promise resolved into the first party. ] : undefined, ); expect(getDebugInfo(thirdPartyChildren[1])).toEqual( __DEV__ ? [ - {time: 15}, + {time: 16}, { name: 'ThirdPartyLazyComponent', env: 'third-party', @@ -2855,14 +2855,14 @@ describe('ReactFlight', () => { stack: ' in myLazy (at **)\n in lazyInitializer (at **)', props: {}, }, - {time: 16}, + {time: 17}, ] : undefined, ); expect(getDebugInfo(thirdPartyChildren[2])).toEqual( __DEV__ ? [ - {time: 11}, + {time: 12}, { name: 'ThirdPartyFragmentComponent', env: 'third-party', @@ -2871,7 +2871,7 @@ describe('ReactFlight', () => { stack: ' in Object. (at **)', props: {}, }, - {time: 12}, + {time: 13}, ] : undefined, ); @@ -2936,7 +2936,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(promise)).toEqual( __DEV__ ? [ - {time: 14}, + {time: 16}, { name: 'ServerComponent', env: 'Server', @@ -2947,7 +2947,7 @@ describe('ReactFlight', () => { transport: expect.arrayContaining([]), }, }, - {time: 15}, + {time: 17}, ] : undefined, ); @@ -2956,7 +2956,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(thirdPartyFragment)).toEqual( __DEV__ ? [ - {time: 16}, + {time: 18}, { name: 'Keyed', env: 'Server', @@ -2967,7 +2967,7 @@ describe('ReactFlight', () => { children: {}, }, }, - {time: 17}, + {time: 19}, ] : undefined, ); @@ -2975,7 +2975,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(thirdPartyFragment.props.children)).toEqual( __DEV__ ? [ - {time: 11}, + {time: 12}, { name: 'ThirdPartyAsyncIterableComponent', env: 'third-party', @@ -2984,7 +2984,7 @@ describe('ReactFlight', () => { stack: ' in Object. (at **)', props: {}, }, - {time: 12}, + {time: 13}, ] : undefined, ); @@ -3132,7 +3132,7 @@ describe('ReactFlight', () => { expect(getDebugInfo(greeting)).toEqual( __DEV__ ? [ - {time: 11}, + {time: 12}, { name: 'Component', env: 'A', @@ -3144,7 +3144,7 @@ describe('ReactFlight', () => { { env: 'B', }, - {time: 12}, + {time: 13}, ] : undefined, ); @@ -3332,9 +3332,9 @@ describe('ReactFlight', () => { }, }; expect(getDebugInfo(greeting)).toEqual([ - {time: 11}, - greetInfo, {time: 12}, + greetInfo, + {time: 13}, { name: 'Container', env: 'Server', @@ -3350,7 +3350,7 @@ describe('ReactFlight', () => { }), }, }, - {time: 13}, + {time: 14}, ]); // The owner that created the span was the outer server component. // We expect the debug info to be referentially equal to the owner. diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 380fa20c3d1c1..3da001f34ca1e 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -49,6 +49,7 @@ import { enableViewTransition, enableSwipeTransition, } from 'shared/ReactFeatureFlags'; +import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import is from 'shared/objectIs'; @@ -1984,6 +1985,8 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { finishQueueingConcurrentUpdates(); if (__DEV__) { + resetOwnerStackLimit(); + ReactStrictModeWarnings.discardPendingWarnings(); } diff --git a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js index 49a80ceb84239..e7b0581d5f4ae 100644 --- a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js @@ -11,15 +11,43 @@ let React; let ReactNoop; +let ReactNoopServer; +let Scheduler; let act; +let advanceTimersByTime; +let assertLog; +let serverAct; +let waitFor; describe('ReactOwnerStacks', () => { beforeEach(function () { - jest.resetModules(); + let time = 10; + advanceTimersByTime = timeMS => { + jest.advanceTimersByTime(timeMS); + time += timeMS; + }; + + const now = jest.fn().mockImplementation(() => { + return time++; + }); + Object.defineProperty(performance, 'timeOrigin', { + value: time, + configurable: true, + }); + Object.defineProperty(performance, 'now', { + value: now, + configurable: true, + }); + jest.resetModules(); React = require('react'); ReactNoop = require('react-noop-renderer'); + ReactNoopServer = require('react-noop-renderer/server'); + Scheduler = require('scheduler'); act = require('internal-test-utils').act; + assertLog = require('internal-test-utils').assertLog; + serverAct = require('internal-test-utils').serverAct; + waitFor = require('internal-test-utils').waitFor; }); function normalizeCodeLocInfo(str) { @@ -85,4 +113,311 @@ describe('ReactOwnerStacks', () => { expect(React.captureOwnerStack()).toBe(null); } }); + + // @gate __DEV__ + it('cuts off at the owner stack limit', async () => { + function App({siblingsBeforeStackOne}) { + const children = []; + for ( + let i = 0; + i < + siblingsBeforeStackOne - + // callsite + 1 - + // Stop so that OwnerStackOne will be right before cutoff + 1; + i++ + ) { + children.push(); + } + children.push(); + children.push(); + + return children; + } + + function Component() { + return null; + } + + let stackOne; + function OwnerStackOne() { + stackOne = React.captureOwnerStack(); + } + + let stackTwo; + function OwnerStackTwo() { + stackTwo = React.captureOwnerStack(); + } + + await act(() => { + ReactNoop.render( + , + ); + }); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: '\n in App (at **)', + stackTwo: __VARIANT__ + ? // captured right after cutoff + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in App (at **)', + }); + + await act(() => { + ReactNoop.render( + , + ); + }); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: __VARIANT__ + ? // We re-rendered immediately so not enough time has ellapsed to reset the limit. + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in App (at **)', + stackTwo: __VARIANT__ + ? // We re-rendered immediately so not enough time has ellapsed to reset the limit. + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in App (at **)', + }); + + // advance time so that we reset the limit + advanceTimersByTime(1001); + + await act(() => { + ReactNoop.render( + so we need to render one more + // to have similar cutoff as the initial render (key="one") + siblingsBeforeStackOne={501} + />, + ); + }); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: '\n in App (at **)', + stackTwo: __VARIANT__ + ? // captured right after cutoff + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in App (at **)', + }); + }); + + // @gate __DEV__ + it('Fiber: resets the owner stack limit periodically', async () => { + function App({siblingsBeforeStackOne, timeout}) { + const children = []; + for ( + let i = 0; + i < + siblingsBeforeStackOne - + // callsite + 1 - + // Stop so that OwnerStackOne will be right before cutoff + 1; + i++ + ) { + children.push(); + } + children.push(); + children.push(); + + return children; + } + + function Component() { + return null; + } + + let stackOne; + function OwnerStackOne() { + Scheduler.log('render OwnerStackOne'); + stackOne = React.captureOwnerStack(); + } + + let stackTwo; + function OwnerStackTwo() { + Scheduler.log('render OwnerStackTwo'); + stackTwo = React.captureOwnerStack(); + } + function OwnerStackDelayed({timeout}) { + Scheduler.log('render OwnerStackDelayed'); + React.use(timeout); + return ; + } + + React.startTransition(() => { + ReactNoop.render( + + setTimeout( + resolve, + // Must be greater or equal then the reset interval + 1000, + ), + ) + } + />, + ); + }); + + await waitFor(['render OwnerStackOne', 'render OwnerStackDelayed']); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + // 1 for the timeout + pendingTimers: 1, + stackOne: '\n in App (at **)', + stackTwo: undefined, + }); + + // resolve `timeout` Promise + advanceTimersByTime(1000); + + await waitFor(['render OwnerStackDelayed', 'render OwnerStackTwo']); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: '\n in App (at **)', + stackTwo: __VARIANT__ + ? // We don't reset in Fiber until we start a new render. + // Here we just continued after a ping. + '\n in UnknownOwner (at **)' + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in OwnerStackDelayed (at **)' + '\n in App (at **)', + }); + }); + + // @gate __DEV__ + it('Fizz: resets the owner stack limit periodically', async () => { + function App({siblingsBeforeStackOne, timeout}) { + const children = []; + for ( + let i = 0; + i < + siblingsBeforeStackOne - + // callsite + 1 - + // Stop so that OwnerStackOne will be right before cutoff + 1; + i++ + ) { + children.push(); + } + children.push(); + children.push(); + + return children; + } + + function Component() { + return null; + } + + let stackOne; + function OwnerStackOne() { + Scheduler.log('render OwnerStackOne'); + stackOne = React.captureOwnerStack(); + } + + let stackTwo; + function OwnerStackTwo() { + Scheduler.log('render OwnerStackTwo'); + stackTwo = React.captureOwnerStack(); + } + function OwnerStackDelayed({timeout}) { + Scheduler.log('render OwnerStackDelayed'); + React.use(timeout); + return ; + } + + ReactNoopServer.render( + + setTimeout( + resolve, + // Must be greater or equal then the reset interval + 1000, + ), + ) + } + />, + ); + + assertLog(['render OwnerStackOne', 'render OwnerStackDelayed']); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + // 1 for the timeout + pendingTimers: 1, + stackOne: '\n in App (at **)', + stackTwo: undefined, + }); + + await serverAct(() => { + advanceTimersByTime(1000); + }); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: '\n in App (at **)', + stackTwo: __VARIANT__ + ? // We don't reset in Fiber until we start a new render. + // Here we just continued after a ping. + '\n in UnknownOwner (at **)' + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in OwnerStackDelayed (at **)' + '\n in App (at **)', + }); + }); }); diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 30b8e171457bc..75a567b6d468f 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -1050,15 +1050,15 @@ describe('ReactFlightDOMEdge', () => { owner: null, }); expect(lazyWrapper._debugInfo).toEqual([ - {time: 11}, - greetInfo, {time: 12}, + greetInfo, + {time: 13}, expect.objectContaining({ name: 'Container', env: 'Server', owner: greetInfo, }), - {time: 13}, + {time: 14}, ]); // The owner that created the span was the outer server component. // We expect the debug info to be referentially equal to the owner. diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index caf2ac3f8a638..daa77239bad41 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -133,6 +133,7 @@ import { callRenderInDEV, } from './ReactFizzCallUserSpace'; +import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset'; import { getIteratorFn, ASYNC_ITERATOR, @@ -473,6 +474,10 @@ export function createRequest( onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void), formState: void | null | ReactFormState, ): Request { + if (__DEV__) { + resetOwnerStackLimit(); + } + // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors const request: Request = new RequestInstance( resumableState, @@ -571,6 +576,10 @@ export function resumeRequest( onFatalError: void | ((error: mixed) => void), onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void), ): Request { + if (__DEV__) { + resetOwnerStackLimit(); + } + // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors const request: Request = new RequestInstance( postponedState.resumableState, @@ -4616,6 +4625,7 @@ export function performWork(request: Request): void { fatalError(request, error, errorInfo, null); } finally { setCurrentResumableState(prevResumableState); + ReactSharedInternals.H = prevDispatcher; ReactSharedInternals.A = prevAsyncDispatcher; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index b737c23ee045f..2765888bd0076 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -103,6 +103,7 @@ import {DefaultAsyncDispatcher} from './flight/ReactFlightAsyncDispatcher'; import {resolveOwner, setCurrentOwner} from './flight/ReactFlightCurrentOwner'; import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack'; +import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset'; import { callComponentInDEV, @@ -552,6 +553,10 @@ export function createRequest( environmentName: void | string | (() => string), // DEV-only filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only ): Request { + if (__DEV__) { + resetOwnerStackLimit(); + } + // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors return new RequestInstance( RENDER, @@ -580,6 +585,10 @@ export function createPrerenderRequest( environmentName: void | string | (() => string), // DEV-only filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only ): Request { + if (__DEV__) { + resetOwnerStackLimit(); + } + // $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors return new RequestInstance( PRERENDER, diff --git a/packages/react-server/src/__tests__/ReactFlightServer-test.js b/packages/react-server/src/__tests__/ReactFlightServer-test.js new file mode 100644 index 0000000000000..b81a793a7f29d --- /dev/null +++ b/packages/react-server/src/__tests__/ReactFlightServer-test.js @@ -0,0 +1,178 @@ +/** + * 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. + * + * @emails react-core + * @jest-environment node + */ + +'use strict'; + +if (typeof Blob === 'undefined') { + global.Blob = require('buffer').Blob; +} +if (typeof File === 'undefined' || typeof FormData === 'undefined') { + global.File = require('undici').File; + global.FormData = require('undici').FormData; +} + +function normalizeCodeLocInfo(str) { + return ( + str && + str.replace(/^ +(?:at|in) ([\S]+)[^\n]*/gm, function (m, name) { + const dot = name.lastIndexOf('.'); + if (dot !== -1) { + name = name.slice(dot + 1); + } + return ' in ' + name + (/\d/.test(m) ? ' (at **)' : ''); + }) + ); +} + +let ReactServer; +let ReactNoopFlightServer; +let Scheduler; +let advanceTimersByTime; +let assertLog; + +describe('ReactFlight', () => { + beforeEach(() => { + // Mock performance.now for timing tests + let time = 0; + advanceTimersByTime = timeMS => { + time += timeMS; + jest.advanceTimersByTime(timeMS); + }; + const now = jest.fn().mockImplementation(() => { + return time++; + }); + Object.defineProperty(performance, 'timeOrigin', { + value: time, + configurable: true, + }); + Object.defineProperty(performance, 'now', { + value: now, + configurable: true, + }); + + jest.resetModules(); + jest.mock('react', () => require('react/react.react-server')); + ReactServer = require('react'); + ReactNoopFlightServer = require('react-noop-renderer/flight-server'); + Scheduler = require('scheduler'); + const InternalTestUtils = require('internal-test-utils'); + assertLog = InternalTestUtils.assertLog; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + // @gate __DEV__ + it('resets the owner stack limit periodically', async () => { + function App({siblingsBeforeStackOne, timeout}) { + const children = []; + for ( + let i = 0; + i < + siblingsBeforeStackOne - + // callsite + 1 - + // Stop so that OwnerStackOne will be right before cutoff + 1; + i++ + ) { + children.push(ReactServer.createElement(Component, {key: i})); + } + children.push( + ReactServer.createElement(OwnerStackOne, {key: 'stackOne'}), + ); + children.push( + ReactServer.createElement(OwnerStackDelayed, { + key: 'stackTwo', + timeout, + }), + ); + + return children; + } + + function Component() { + return null; + } + + let stackOne; + function OwnerStackOne() { + Scheduler.log('render OwnerStackOne'); + stackOne = ReactServer.captureOwnerStack(); + } + + let stackTwo; + function OwnerStackTwo() { + Scheduler.log('render OwnerStackTwo'); + stackTwo = ReactServer.captureOwnerStack(); + } + function OwnerStackDelayed({timeout}) { + Scheduler.log('render OwnerStackDelayed'); + + // Owner Stacks start fresh after `await`. + // We need to sync delay to observe the reset limit behavior. + // TODO: Is that the right behavior? If you do stack + Ownst Stack you'd get `OwnerStackTwo` twice. + jest.advanceTimersByTime(timeout); + + return ReactServer.createElement(OwnerStackTwo, {}); + } + + ReactNoopFlightServer.render( + ReactServer.createElement(App, { + key: 'one', + // Should be the value with of `ownerStackLimit` with `__VARIANT__` so that we see the cutoff + siblingsBeforeStackOne: 500, + // Must be greater or equal then the reset interval + timeout: 1000, + }), + ); + + assertLog([ + 'render OwnerStackOne', + 'render OwnerStackDelayed', + 'render OwnerStackTwo', + ]); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: '\n in App (at **)', + stackTwo: __VARIANT__ + ? // Didn't advance timers yet to reset + '\n in UnknownOwner (at **)' + '\n in UnknownOwner (at **)' + : // We never hit the limit outside __VARIANT__ + '\n in OwnerStackDelayed (at **)' + '\n in App (at **)', + }); + + // Ensure we reset the limit after the timeout + advanceTimersByTime(1000); + ReactNoopFlightServer.render( + ReactServer.createElement(App, { + key: 'two', + siblingsBeforeStackOne: 0, + timeout: 0, + }), + ); + + expect({ + pendingTimers: jest.getTimerCount(), + stackOne: normalizeCodeLocInfo(stackOne), + stackTwo: normalizeCodeLocInfo(stackTwo), + }).toEqual({ + pendingTimers: 0, + stackOne: '\n in App (at **)', + stackTwo: '\n in OwnerStackDelayed (at **)' + '\n in App (at **)', + }); + }); +}); diff --git a/packages/react/src/ReactSharedInternalsClient.js b/packages/react/src/ReactSharedInternalsClient.js index f899cba24af1b..746ee99c1d035 100644 --- a/packages/react/src/ReactSharedInternalsClient.js +++ b/packages/react/src/ReactSharedInternalsClient.js @@ -38,6 +38,9 @@ export type SharedStateClient = { // ReactDebugCurrentFrame getCurrentStack: null | (() => string), + + // ReactOwnerStackReset + recentlyCreatedOwnerStacks: 0, }; export type RendererTask = boolean => RendererTask | null; @@ -58,6 +61,7 @@ if (__DEV__) { ReactSharedInternals.thrownErrors = []; // Stack implementation injected by the current renderer. ReactSharedInternals.getCurrentStack = (null: null | (() => string)); + ReactSharedInternals.recentlyCreatedOwnerStacks = 0; } export default ReactSharedInternals; diff --git a/packages/react/src/ReactSharedInternalsServer.js b/packages/react/src/ReactSharedInternalsServer.js index d670fa18fe770..c411b77b59dbd 100644 --- a/packages/react/src/ReactSharedInternalsServer.js +++ b/packages/react/src/ReactSharedInternalsServer.js @@ -39,6 +39,9 @@ export type SharedStateServer = { // ReactDebugCurrentFrame getCurrentStack: null | (() => string), + + // ReactOwnerStackReset + recentlyCreatedOwnerStacks: 0, }; export type RendererTask = boolean => RendererTask | null; @@ -59,6 +62,7 @@ if (enableTaint) { if (__DEV__) { // Stack implementation injected by the current renderer. ReactSharedInternals.getCurrentStack = (null: null | (() => string)); + ReactSharedInternals.recentlyCreatedOwnerStacks = 0; } export default ReactSharedInternals; diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index 0f5fb1cd43c3b..a20378a93cb4d 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -16,7 +16,10 @@ import { } from 'shared/ReactSymbols'; import {checkKeyStringCoercion} from 'shared/CheckStringCoercion'; import isArray from 'shared/isArray'; -import {disableDefaultPropsExceptForClasses} from 'shared/ReactFeatureFlags'; +import { + disableDefaultPropsExceptForClasses, + ownerStackLimit, +} from 'shared/ReactFeatureFlags'; const createTask = // eslint-disable-next-line react-internal/no-production-logging @@ -57,12 +60,32 @@ function getOwner() { return null; } +/** @noinline */ +function UnknownOwner() { + /** @noinline */ + return (() => Error('react-stack-top-frame'))(); +} +const createFakeCallStack = { + 'react-stack-bottom-frame': function (callStackForError) { + return callStackForError(); + }, +}; + let specialPropKeyWarningShown; let didWarnAboutElementRef; let didWarnAboutOldJSXRuntime; +let unknownOwnerDebugStack; +let unknownOwnerDebugTask; if (__DEV__) { didWarnAboutElementRef = {}; + + // We use this technique to trick minifiers to preserve the function name. + unknownOwnerDebugStack = createFakeCallStack['react-stack-bottom-frame'].bind( + createFakeCallStack, + UnknownOwner, + )(); + unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner)); } function hasValidRef(config) { @@ -373,6 +396,9 @@ export function jsxProdSignatureRunningInDevWithDynamicChildren( ) { if (__DEV__) { const isStaticChildren = false; + const trackActualOwner = + __DEV__ && + ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit; return jsxDEVImpl( type, config, @@ -380,8 +406,14 @@ export function jsxProdSignatureRunningInDevWithDynamicChildren( isStaticChildren, source, self, - __DEV__ && Error('react-stack-top-frame'), - __DEV__ && createTask(getTaskName(type)), + __DEV__ && + (trackActualOwner + ? Error('react-stack-top-frame') + : unknownOwnerDebugStack), + __DEV__ && + (trackActualOwner + ? createTask(getTaskName(type)) + : unknownOwnerDebugTask), ); } } @@ -395,6 +427,9 @@ export function jsxProdSignatureRunningInDevWithStaticChildren( ) { if (__DEV__) { const isStaticChildren = true; + const trackActualOwner = + __DEV__ && + ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit; return jsxDEVImpl( type, config, @@ -402,8 +437,14 @@ export function jsxProdSignatureRunningInDevWithStaticChildren( isStaticChildren, source, self, - __DEV__ && Error('react-stack-top-frame'), - __DEV__ && createTask(getTaskName(type)), + __DEV__ && + (trackActualOwner + ? Error('react-stack-top-frame') + : unknownOwnerDebugStack), + __DEV__ && + (trackActualOwner + ? createTask(getTaskName(type)) + : unknownOwnerDebugTask), ); } } @@ -417,6 +458,9 @@ const didWarnAboutKeySpread = {}; * @param {string} key */ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) { + const trackActualOwner = + __DEV__ && + ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit; return jsxDEVImpl( type, config, @@ -424,8 +468,14 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) { isStaticChildren, source, self, - __DEV__ && Error('react-stack-top-frame'), - __DEV__ && createTask(getTaskName(type)), + __DEV__ && + (trackActualOwner + ? Error('react-stack-top-frame') + : unknownOwnerDebugStack), + __DEV__ && + (trackActualOwner + ? createTask(getTaskName(type)) + : unknownOwnerDebugTask), ); } @@ -692,7 +742,9 @@ export function createElement(type, config, children) { defineKeyPropWarningGetter(props, displayName); } } - + const trackActualOwner = + __DEV__ && + ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit; return ReactElement( type, key, @@ -700,8 +752,14 @@ export function createElement(type, config, children) { undefined, getOwner(), props, - __DEV__ && Error('react-stack-top-frame'), - __DEV__ && createTask(getTaskName(type)), + __DEV__ && + (trackActualOwner + ? Error('react-stack-top-frame') + : unknownOwnerDebugStack), + __DEV__ && + (trackActualOwner + ? createTask(getTaskName(type)) + : unknownOwnerDebugTask), ); } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 9463be1b95f43..295c7f47630a1 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -268,3 +268,5 @@ export const enableUpdaterTracking = __PROFILE__; // Internal only. export const enableDO_NOT_USE_disableStrictPassiveEffect = false; + +export const ownerStackLimit = 1e4; diff --git a/packages/shared/ReactOwnerStackReset.js b/packages/shared/ReactOwnerStackReset.js new file mode 100644 index 0000000000000..e7922adb3a4bb --- /dev/null +++ b/packages/shared/ReactOwnerStackReset.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) Meta Platforms, Inc. and its 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 ReactSharedInternals from 'shared/ReactSharedInternals'; + +let lastResetTime = 0; + +let getCurrentTime: () => number | DOMHighResTimeStamp; +const hasPerformanceNow = + // $FlowFixMe[method-unbinding] + typeof performance === 'object' && typeof performance.now === 'function'; + +if (hasPerformanceNow) { + const localPerformance = performance; + getCurrentTime = () => localPerformance.now(); +} else { + const localDate = Date; + getCurrentTime = () => localDate.now(); +} + +export function resetOwnerStackLimit() { + if (__DEV__) { + const now = getCurrentTime(); + const timeSinceLastReset = now - lastResetTime; + if (timeSinceLastReset > 1000) { + ReactSharedInternals.recentlyCreatedOwnerStacks = 0; + lastResetTime = now; + } + } else { + // These errors should never make it into a build so we don't need to encode them in codes.json + // eslint-disable-next-line react-internal/prod-error-codes + throw new Error( + 'resetOwnerStackLimit should never be called in production mode. This is a bug in React.', + ); + } +} diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 7711c842691cb..f28fa7c3bac72 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -29,3 +29,7 @@ export const enableUseEffectCRUDOverload = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = __VARIANT__; export const renameElementSymbol = __VARIANT__; +export const ownerStackLimit: number = __VARIANT__ + ? // Some value that doesn't impact existing tests + 500 + : 1e4; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index b9e9bae96cb7d..f74e32b20906a 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -31,6 +31,7 @@ export const { enableFastAddPropertiesInDiffing, enableLazyPublicInstanceInFabric, renameElementSymbol, + ownerStackLimit, } = dynamicFlags; // The rest of the flags are static for better dead code elimination. diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index baeef0b56483d..2eaf6add41e4d 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -75,6 +75,7 @@ export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const ownerStackLimit = 1e4; export const enableFragmentRefs = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 1e3eedc9e2255..e113d724711cf 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -75,6 +75,7 @@ export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = true; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const ownerStackLimit = 1e4; export const enableFragmentRefs = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index f2fa119800fa8..cdb0c832297e2 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -72,6 +72,7 @@ export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; export const enableFragmentRefs = false; +export const ownerStackLimit = 1e4; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 2342b959f99f8..5e235f6c071cf 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -88,6 +88,7 @@ export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; export const enableFragmentRefs = false; +export const ownerStackLimit = 1e4; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index d94a0c0ebd531..7cc0a0cded9af 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -43,6 +43,11 @@ export const enableComponentPerformanceTrack = __VARIANT__; export const enableScrollEndPolyfill = __VARIANT__; export const enableFragmentRefs = __VARIANT__; +export const ownerStackLimit: number = __VARIANT__ + ? // Some value that doesn't impact existing tests + 500 + : 1e4; + // TODO: These flags are hard-coded to the default values used in open source. // Update the tests so that they pass in either mode, then set these // to __VARIANT__. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 0584af1f81826..d321f456e80a3 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -40,6 +40,7 @@ export const { enableComponentPerformanceTrack, enableScrollEndPolyfill, enableFragmentRefs, + ownerStackLimit, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build. From febc09b480903bb803455dc38dc130007d3a2e91 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:07:24 -0400 Subject: [PATCH 200/300] [compiler][fix] mutableOnlyIfOperandsAreMutable does not apply when operands are globals (#32695) Globals, module locals, and other locally defined functions may mutate their arguments. See test fixtures for details --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32695). * #32698 * #32697 * #32696 * __->__ #32695 --- .../src/HIR/Globals.ts | 110 ++++++++++++++-- .../src/Inference/InferReferenceEffects.ts | 54 ++++++-- .../array-from-maybemutates-arg0.expect.md | 39 ++++-- .../compiler/array-from-maybemutates-arg0.js | 7 +- ...-array-filter-capture-mutate-bug.expect.md | 113 +++++++++++++++++ .../repro-array-filter-capture-mutate-bug.tsx | 34 +++++ ...y-filter-known-nonmutate-Boolean.expect.md | 118 ++++++++++++++++++ ...o-array-filter-known-nonmutate-Boolean.tsx | 23 ++++ ...pro-array-map-capture-mutate-bug.expect.md | 91 ++++++++++++++ .../repro-array-map-capture-mutate-bug.tsx | 23 ++++ ...pro-array-map-known-mutate-shape.expect.md | 100 +++++++++++++++ .../repro-array-map-known-mutate-shape.tsx | 27 ++++ 12 files changed, 709 insertions(+), 30 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.tsx create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts index df8196c1d7a0d..670cbd01fcfaf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts @@ -11,6 +11,7 @@ import { BuiltInArrayId, BuiltInFireId, BuiltInMixedReadonlyId, + BuiltInObjectId, BuiltInUseActionStateId, BuiltInUseContextHookId, BuiltInUseEffectHookId, @@ -45,21 +46,17 @@ export const DEFAULT_SHAPES: ShapeRegistry = new Map(BUILTIN_SHAPES); // Hack until we add ObjectShapes for all globals const UNTYPED_GLOBALS: Set = new Set([ - 'String', 'Object', 'Function', - 'Number', 'RegExp', 'Date', 'Error', - 'Function', 'TypeError', 'RangeError', 'ReferenceError', 'SyntaxError', 'URIError', 'EvalError', - 'Boolean', 'DataView', 'Float32Array', 'Float64Array', @@ -75,16 +72,8 @@ const UNTYPED_GLOBALS: Set = new Set([ 'Uint32Array', 'ArrayBuffer', 'JSON', - 'parseFloat', - 'parseInt', 'console', - 'isNaN', 'eval', - 'isFinite', - 'encodeURI', - 'decodeURI', - 'encodeURIComponent', - 'decodeURIComponent', ]); const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ @@ -101,6 +90,23 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ returnValueKind: ValueKind.Mutable, }), ], + [ + /** + * Object.fromEntries(iterable) + * iterable: An iterable, such as an Array or Map, containing a list of + * objects. Each object should have two properties. + * Returns a new object whose properties are given by the entries of the + * iterable. + */ + 'fromEntries', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [Effect.ConditionallyMutate], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInObjectId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + }), + ], ]), ], [ @@ -372,6 +378,86 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ returnValueKind: ValueKind.Primitive, }), ], + [ + 'parseInt', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'parseFloat', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'isNaN', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'isFinite', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'encodeURI', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'encodeURIComponent', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'decodeURI', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'decodeURIComponent', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], // TODO: rest of Global objects ]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index ae71da64b4191..5874fcfb06c99 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -251,7 +251,7 @@ type FreezeAction = {values: Set; reason: Set}; // Maintains a mapping of top-level variables to the kind of value they hold class InferenceState { - #env: Environment; + env: Environment; // The kind of each value, based on its allocation site #values: Map; @@ -267,7 +267,7 @@ class InferenceState { values: Map, variables: Map>, ) { - this.#env = env; + this.env = env; this.#values = values; this.#variables = variables; } @@ -409,8 +409,8 @@ class InferenceState { }); if ( value.kind === 'FunctionExpression' && - (this.#env.config.enablePreserveExistingMemoizationGuarantees || - this.#env.config.enableTransitivelyFreezeFunctionExpressions) + (this.env.config.enablePreserveExistingMemoizationGuarantees || + this.env.config.enableTransitivelyFreezeFunctionExpressions) ) { for (const operand of value.loweredFunc.func.context) { const operandValues = this.#variables.get(operand.identifier.id); @@ -590,7 +590,7 @@ class InferenceState { return null; } else { return new InferenceState( - this.#env, + this.env, nextValues ?? new Map(this.#values), nextVariables ?? new Map(this.#variables), ); @@ -604,7 +604,7 @@ class InferenceState { */ clone(): InferenceState { return new InferenceState( - this.#env, + this.env, new Map(this.#values), new Map(this.#variables), ); @@ -2012,6 +2012,32 @@ export function getFunctionEffects( return results; } +export function isKnownMutableEffect(effect: Effect): boolean { + switch (effect) { + case Effect.Store: + case Effect.ConditionallyMutate: + case Effect.Mutate: { + return true; + } + + case Effect.Unknown: { + CompilerError.invariant(false, { + reason: 'Unexpected unknown effect', + description: null, + loc: GeneratedSource, + suggestions: null, + }); + } + case Effect.Read: + case Effect.Capture: + case Effect.Freeze: { + return false; + } + default: { + assertExhaustive(effect, `Unexpected effect \`${effect}\``); + } + } +} /** * Returns true if all of the arguments are both non-mutable (immutable or frozen) * _and_ are not functions which might mutate their arguments. Note that function @@ -2023,10 +2049,20 @@ function areArgumentsImmutableAndNonMutating( args: MethodCall['args'], ): boolean { for (const arg of args) { + if (arg.kind === 'Identifier' && arg.identifier.type.kind === 'Function') { + const fnShape = state.env.getFunctionSignature(arg.identifier.type); + if (fnShape != null) { + return ( + !fnShape.positionalParams.some(isKnownMutableEffect) && + (fnShape.restParam == null || + !isKnownMutableEffect(fnShape.restParam)) + ); + } + } const place = arg.kind === 'Identifier' ? arg : arg.place; + const kind = state.kind(place).kind; switch (kind) { - case ValueKind.Global: case ValueKind.Primitive: case ValueKind.Frozen: { /* @@ -2037,6 +2073,10 @@ function areArgumentsImmutableAndNonMutating( break; } default: { + /** + * Globals, module locals, and other locally defined functions may + * mutate their arguments. + */ return false; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md index 586124280a3dd..9be174d99864c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md @@ -8,7 +8,12 @@ function Component({value}) { const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; useIdentity(); const derived = Array.from(arr, mutateAndReturn); - return {derived.at(-1)}; + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); } export const FIXTURE_ENTRYPOINT = { @@ -26,28 +31,42 @@ import { c as _c } from "react/compiler-runtime"; import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime"; function Component(t0) { - const $ = _c(4); + const $ = _c(7); const { value } = t0; const arr = [{ value: "foo" }, { value: "bar" }, { value }]; useIdentity(); const derived = Array.from(arr, mutateAndReturn); let t1; if ($[0] !== derived) { - t1 = derived.at(-1); + t1 = derived.at(0); $[0] = derived; $[1] = t1; } else { t1 = $[1]; } let t2; - if ($[2] !== t1) { - t2 = {t1}; - $[2] = t1; + if ($[2] !== derived) { + t2 = derived.at(-1); + $[2] = derived; $[3] = t2; } else { t2 = $[3]; } - return t2; + let t3; + if ($[4] !== t1 || $[5] !== t2) { + t3 = ( + + {t1} + {t2} + + ); + $[4] = t1; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; } export const FIXTURE_ENTRYPOINT = { @@ -59,6 +78,6 @@ export const FIXTURE_ENTRYPOINT = { ``` ### Eval output -(kind: ok)
{"children":{"value":5,"wat0":"joe"}}
-
{"children":{"value":6,"wat0":"joe"}}
-
{"children":{"value":6,"wat0":"joe"}}
\ No newline at end of file +(kind: ok)
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js index edb4e37125843..4e224c8a9ac8e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js @@ -4,7 +4,12 @@ function Component({value}) { const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; useIdentity(); const derived = Array.from(arr, mutateAndReturn); - return {derived.at(-1)}; + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.expect.md new file mode 100644 index 0000000000000..091d050e64008 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.expect.md @@ -0,0 +1,113 @@ + +## Input + +```javascript +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +/** + * Repro for bug with `mutableOnlyIfOperandsAreMutable` flag + * Found differences in evaluator results + * Non-forget (expected): + * (kind: ok) + *
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+ * Forget: + * (kind: ok) + *
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe","wat1":"joe"},{"value":6,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe","wat1":"joe"},{"value":6,"wat0":"joe"}]}
+ + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(null); + const derived = arr.filter(mutateAndReturn); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime"; + +/** + * Repro for bug with `mutableOnlyIfOperandsAreMutable` flag + * Found differences in evaluator results + * Non-forget (expected): + * (kind: ok) + *
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+ * Forget: + * (kind: ok) + *
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe","wat1":"joe"},{"value":6,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe","wat1":"joe"},{"value":6,"wat0":"joe"}]}
+ + */ +function Component(t0) { + const $ = _c(7); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(null); + const derived = arr.filter(mutateAndReturn); + let t1; + if ($[0] !== derived) { + t1 = derived.at(0); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== derived) { + t2 = derived.at(-1); + $[2] = derived; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== t1 || $[5] !== t2) { + t3 = ( + + {t1} + {t2} + + ); + $[4] = t1; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.tsx new file mode 100644 index 0000000000000..33e418a5fda7e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-capture-mutate-bug.tsx @@ -0,0 +1,34 @@ +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +/** + * Repro for bug with `mutableOnlyIfOperandsAreMutable` flag + * Found differences in evaluator results + * Non-forget (expected): + * (kind: ok) + *
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+ * Forget: + * (kind: ok) + *
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe","wat1":"joe"},{"value":6,"wat0":"joe"}]}
+ *
{"children":[{"value":"foo","wat0":"joe","wat1":"joe"},{"value":6,"wat0":"joe"}]}
+ + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(null); + const derived = arr.filter(mutateAndReturn); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.expect.md new file mode 100644 index 0000000000000..0812e46c55be2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.expect.md @@ -0,0 +1,118 @@ + +## Input + +```javascript +import {Stringify, useIdentity} from 'shared-runtime'; + +/** + * Also see repro-array-map-known-mutate-shape, which calls a global function + * that mutates its operands. + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(null); + const derived = arr.filter(Boolean); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { Stringify, useIdentity } from "shared-runtime"; + +/** + * Also see repro-array-map-known-mutate-shape, which calls a global function + * that mutates its operands. + */ +function Component(t0) { + const $ = _c(13); + const { value } = t0; + let t1; + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { value: "foo" }; + t2 = { value: "bar" }; + $[0] = t1; + $[1] = t2; + } else { + t1 = $[0]; + t2 = $[1]; + } + let t3; + if ($[2] !== value) { + t3 = [t1, t2, { value }]; + $[2] = value; + $[3] = t3; + } else { + t3 = $[3]; + } + const arr = t3; + useIdentity(null); + let t4; + if ($[4] !== arr) { + t4 = arr.filter(Boolean); + $[4] = arr; + $[5] = t4; + } else { + t4 = $[5]; + } + const derived = t4; + let t5; + if ($[6] !== derived) { + t5 = derived.at(0); + $[6] = derived; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== derived) { + t6 = derived.at(-1); + $[8] = derived; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] !== t5 || $[11] !== t6) { + t7 = ( + + {t5} + {t6} + + ); + $[10] = t5; + $[11] = t6; + $[12] = t7; + } else { + t7 = $[12]; + } + return t7; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":[{"value":"foo"},{"value":5}]}
+
{"children":[{"value":"foo"},{"value":6}]}
+
{"children":[{"value":"foo"},{"value":6}]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.tsx new file mode 100644 index 0000000000000..cd676d9b9075a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-filter-known-nonmutate-Boolean.tsx @@ -0,0 +1,23 @@ +import {Stringify, useIdentity} from 'shared-runtime'; + +/** + * Also see repro-array-map-known-mutate-shape, which calls a global function + * that mutates its operands. + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(null); + const derived = arr.filter(Boolean); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.expect.md new file mode 100644 index 0000000000000..b6bd4709ca6b4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.expect.md @@ -0,0 +1,91 @@ + +## Input + +```javascript +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +/** + * Copy of repro-array-map-capture-mutate-bug, showing that the same issue applies to any + * function call which captures its callee when applying an operand. + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(null); + const derived = arr.map(mutateAndReturn); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime"; + +/** + * Copy of repro-array-map-capture-mutate-bug, showing that the same issue applies to any + * function call which captures its callee when applying an operand. + */ +function Component(t0) { + const $ = _c(7); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(null); + const derived = arr.map(mutateAndReturn); + let t1; + if ($[0] !== derived) { + t1 = derived.at(0); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== derived) { + t2 = derived.at(-1); + $[2] = derived; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== t1 || $[5] !== t2) { + t3 = ( + + {t1} + {t2} + + ); + $[4] = t1; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.tsx new file mode 100644 index 0000000000000..bda94b92c8372 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-capture-mutate-bug.tsx @@ -0,0 +1,23 @@ +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +/** + * Copy of repro-array-map-capture-mutate-bug, showing that the same issue applies to any + * function call which captures its callee when applying an operand. + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(null); + const derived = arr.map(mutateAndReturn); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.expect.md new file mode 100644 index 0000000000000..27735532326ff --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.expect.md @@ -0,0 +1,100 @@ + +## Input + +```javascript +import {Stringify, useIdentity} from 'shared-runtime'; + +/** + * Also see repro-array-map-known-nonmutate-Boolean, which calls a global + * function that does *not* mutate its operands. + */ +function Component({value}) { + const arr = [ + new Set([['foo', 2]]).values(), + new Set([['bar', 4]]).values(), + [['baz', value]], + ]; + useIdentity(null); + const derived = arr.map(Object.fromEntries); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { Stringify, useIdentity } from "shared-runtime"; + +/** + * Also see repro-array-map-known-nonmutate-Boolean, which calls a global + * function that does *not* mutate its operands. + */ +function Component(t0) { + const $ = _c(7); + const { value } = t0; + const arr = [ + new Set([["foo", 2]]).values(), + new Set([["bar", 4]]).values(), + [["baz", value]], + ]; + + useIdentity(null); + const derived = arr.map(Object.fromEntries); + let t1; + if ($[0] !== derived) { + t1 = derived.at(0); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== derived) { + t2 = derived.at(-1); + $[2] = derived; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== t1 || $[5] !== t2) { + t3 = ( + + {t1} + {t2} + + ); + $[4] = t1; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":[{"foo":2},{"baz":5}]}
+
{"children":[{"foo":2},{"baz":6}]}
+
{"children":[{"foo":2},{"baz":6}]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.tsx new file mode 100644 index 0000000000000..191d0e0d33607 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/repro-array-map-known-mutate-shape.tsx @@ -0,0 +1,27 @@ +import {Stringify, useIdentity} from 'shared-runtime'; + +/** + * Also see repro-array-map-known-nonmutate-Boolean, which calls a global + * function that does *not* mutate its operands. + */ +function Component({value}) { + const arr = [ + new Set([['foo', 2]]).values(), + new Set([['bar', 4]]).values(), + [['baz', value]], + ]; + useIdentity(null); + const derived = arr.map(Object.fromEntries); + return ( + + {derived.at(0)} + {derived.at(-1)} + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; From 45463ab3ac3ed0e65dfdbbfd5e53a50a8384e909 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:07:49 -0400 Subject: [PATCH 201/300] [compiler][be] Refactor similar CallExpression and MethodCall effect handling (#32696) Simplify InferReferenceEffect function signature matching logic for next PRs in stack --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32696). * #32698 * #32697 * __->__ #32696 * #32695 --- .../src/Inference/InferReferenceEffects.ts | 263 ++++++++---------- 1 file changed, 113 insertions(+), 150 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 5874fcfb06c99..0822a326575f8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -23,6 +23,7 @@ import { Phi, Place, SpreadPattern, + TInstruction, Type, ValueKind, ValueReason, @@ -1238,62 +1239,12 @@ function inferBlock( break; } case 'CallExpression': { - const signature = getFunctionCallSignature( - env, - instrValue.callee.identifier.type, + inferCallEffects( + state, + instr as TInstruction, + freezeActions, + getFunctionCallSignature(env, instrValue.callee.identifier.type), ); - - const effects = - signature !== null ? getFunctionEffects(instrValue, signature) : null; - const returnValueKind: AbstractValue = - signature !== null - ? { - kind: signature.returnValueKind, - reason: new Set([ - signature.returnValueReason ?? - ValueReason.KnownReturnSignature, - ]), - context: new Set(), - } - : { - kind: ValueKind.Mutable, - reason: new Set([ValueReason.Other]), - context: new Set(), - }; - let hasCaptureArgument = false; - for (let i = 0; i < instrValue.args.length; i++) { - const arg = instrValue.args[i]; - const place = arg.kind === 'Identifier' ? arg : arg.place; - state.referenceAndRecordEffects( - freezeActions, - place, - getArgumentEffect(effects != null ? effects[i] : null, arg), - ValueReason.Other, - ); - hasCaptureArgument ||= place.effect === Effect.Capture; - } - if (signature !== null) { - state.referenceAndRecordEffects( - freezeActions, - instrValue.callee, - signature.calleeEffect, - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - instrValue.callee, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } - hasCaptureArgument ||= instrValue.callee.effect === Effect.Capture; - - state.initialize(instrValue, returnValueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = hasCaptureArgument - ? Effect.Store - : Effect.ConditionallyMutate; continuation = {kind: 'funeffects'}; break; } @@ -1311,102 +1262,12 @@ function inferBlock( Effect.Read, ValueReason.Other, ); - - const signature = getFunctionCallSignature( - env, - instrValue.property.identifier.type, + inferCallEffects( + state, + instr as TInstruction, + freezeActions, + getFunctionCallSignature(env, instrValue.property.identifier.type), ); - - const returnValueKind: AbstractValue = - signature !== null - ? { - kind: signature.returnValueKind, - reason: new Set([ - signature.returnValueReason ?? - ValueReason.KnownReturnSignature, - ]), - context: new Set(), - } - : { - kind: ValueKind.Mutable, - reason: new Set([ValueReason.Other]), - context: new Set(), - }; - - if ( - signature !== null && - signature.mutableOnlyIfOperandsAreMutable && - areArgumentsImmutableAndNonMutating(state, instrValue.args) - ) { - /* - * None of the args are mutable or mutate their params, we can downgrade to - * treating as all reads (except that the receiver may be captured) - */ - for (const arg of instrValue.args) { - const place = arg.kind === 'Identifier' ? arg : arg.place; - state.referenceAndRecordEffects( - freezeActions, - place, - Effect.Read, - ValueReason.Other, - ); - } - state.referenceAndRecordEffects( - freezeActions, - instrValue.receiver, - Effect.Capture, - ValueReason.Other, - ); - state.initialize(instrValue, returnValueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = - instrValue.receiver.effect === Effect.Capture - ? Effect.Store - : Effect.ConditionallyMutate; - continuation = {kind: 'funeffects'}; - break; - } - - const effects = - signature !== null ? getFunctionEffects(instrValue, signature) : null; - let hasCaptureArgument = false; - for (let i = 0; i < instrValue.args.length; i++) { - const arg = instrValue.args[i]; - const place = arg.kind === 'Identifier' ? arg : arg.place; - /* - * If effects are inferred for an argument, we should fail invalid - * mutating effects - */ - state.referenceAndRecordEffects( - freezeActions, - place, - getArgumentEffect(effects != null ? effects[i] : null, arg), - ValueReason.Other, - ); - hasCaptureArgument ||= place.effect === Effect.Capture; - } - if (signature !== null) { - state.referenceAndRecordEffects( - freezeActions, - instrValue.receiver, - signature.calleeEffect, - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - instrValue.receiver, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } - hasCaptureArgument ||= instrValue.receiver.effect === Effect.Capture; - - state.initialize(instrValue, returnValueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = hasCaptureArgument - ? Effect.Store - : Effect.ConditionallyMutate; continuation = {kind: 'funeffects'}; break; } @@ -2125,3 +1986,105 @@ function getArgumentEffect( return Effect.ConditionallyMutate; } } + +function inferCallEffects( + state: InferenceState, + instr: TInstruction | TInstruction, + freezeActions: Array, + signature: FunctionSignature | null, +): void { + const instrValue = instr.value; + const returnValueKind: AbstractValue = + signature !== null + ? { + kind: signature.returnValueKind, + reason: new Set([ + signature.returnValueReason ?? ValueReason.KnownReturnSignature, + ]), + context: new Set(), + } + : { + kind: ValueKind.Mutable, + reason: new Set([ValueReason.Other]), + context: new Set(), + }; + + if ( + instrValue.kind === 'MethodCall' && + signature !== null && + signature.mutableOnlyIfOperandsAreMutable && + areArgumentsImmutableAndNonMutating(state, instrValue.args) + ) { + /* + * None of the args are mutable or mutate their params, we can downgrade to + * treating as all reads (except that the receiver may be captured) + */ + for (const arg of instrValue.args) { + const place = arg.kind === 'Identifier' ? arg : arg.place; + state.referenceAndRecordEffects( + freezeActions, + place, + Effect.Read, + ValueReason.Other, + ); + } + state.referenceAndRecordEffects( + freezeActions, + instrValue.receiver, + Effect.Capture, + ValueReason.Other, + ); + state.initialize(instrValue, returnValueKind); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = + instrValue.receiver.effect === Effect.Capture + ? Effect.Store + : Effect.ConditionallyMutate; + return; + } + + const effects = + signature !== null ? getFunctionEffects(instrValue, signature) : null; + let hasCaptureArgument = false; + for (let i = 0; i < instrValue.args.length; i++) { + const arg = instrValue.args[i]; + const place = arg.kind === 'Identifier' ? arg : arg.place; + /* + * If effects are inferred for an argument, we should fail invalid + * mutating effects + */ + state.referenceAndRecordEffects( + freezeActions, + place, + getArgumentEffect(effects != null ? effects[i] : null, arg), + ValueReason.Other, + ); + hasCaptureArgument ||= place.effect === Effect.Capture; + } + const callee = + instrValue.kind === 'CallExpression' + ? instrValue.callee + : instrValue.receiver; + if (signature !== null) { + state.referenceAndRecordEffects( + freezeActions, + callee, + signature.calleeEffect, + ValueReason.Other, + ); + } else { + state.referenceAndRecordEffects( + freezeActions, + callee, + Effect.ConditionallyMutate, + ValueReason.Other, + ); + } + hasCaptureArgument ||= callee.effect === Effect.Capture; + + state.initialize(instrValue, returnValueKind); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = hasCaptureArgument + ? Effect.Store + : Effect.ConditionallyMutate; +} From a8e503dce0ec386eef752a1219dd6ef861c48ced Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:19:01 -0400 Subject: [PATCH 202/300] [compiler][optim] Add map and set constructors (#32697) * Adds `isConstructor: boolean` to `FunctionType`. With this PR, each typed function can either be a constructor (currently only known globals) or non constructor. Alternatively, we prefer to encode polymorphic types / effects (and match the closest subtype) * Add Map and Set globals + built-ins --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32697). * #32698 * __->__ #32697 --- .../src/HIR/Globals.ts | 34 ++ .../src/HIR/HIR.ts | 24 +- .../src/HIR/ObjectShape.ts | 312 ++++++++++++++++++ .../src/HIR/Types.ts | 2 + .../src/Inference/InferReferenceEffects.ts | 70 ++-- .../src/TypeInference/InferTypes.ts | 24 +- .../global-types/map-constructor.expect.md | 77 +++++ .../compiler/global-types/map-constructor.ts | 17 + .../global-types/set-add-mutate.expect.md | 76 +++++ .../compiler/global-types/set-add-mutate.ts | 21 ++ .../set-constructor-arg.expect.md | 98 ++++++ .../global-types/set-constructor-arg.ts | 26 ++ .../global-types/set-constructor.expect.md | 77 +++++ .../compiler/global-types/set-constructor.ts | 17 + .../set-copy-constructor-mutate.expect.md | 82 +++++ .../set-copy-constructor-mutate.ts | 22 ++ .../set-for-of-iterate-values.expect.md | 65 ++++ .../global-types/set-for-of-iterate-values.ts | 24 ++ 18 files changed, 1017 insertions(+), 51 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts index 670cbd01fcfaf..68f8e199dbb55 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts @@ -10,8 +10,10 @@ import { BUILTIN_SHAPES, BuiltInArrayId, BuiltInFireId, + BuiltInMapId, BuiltInMixedReadonlyId, BuiltInObjectId, + BuiltInSetId, BuiltInUseActionStateId, BuiltInUseContextHookId, BuiltInUseEffectHookId, @@ -458,6 +460,38 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ returnValueKind: ValueKind.Primitive, }), ], + [ + 'Map', + addFunction( + DEFAULT_SHAPES, + [], + { + positionalParams: [Effect.ConditionallyMutate], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInMapId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + }, + null, + true, + ), + ], + [ + 'Set', + addFunction( + DEFAULT_SHAPES, + [], + { + positionalParams: [Effect.ConditionallyMutate], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInSetId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + }, + null, + true, + ), + ], // TODO: rest of Global objects ]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index 5de4d9ba0c9eb..d2e989890694c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -10,7 +10,7 @@ import * as t from '@babel/types'; import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError'; import {assertExhaustive} from '../Utils/utils'; import {Environment, ReactFunctionType} from './Environment'; -import {HookKind} from './ObjectShape'; +import type {HookKind} from './ObjectShape'; import {Type, makeType} from './Types'; import {z} from 'zod'; @@ -829,6 +829,13 @@ export type CallExpression = { typeArguments?: Array; }; +export type NewExpression = { + kind: 'NewExpression'; + callee: Place; + args: Array; + loc: SourceLocation; +}; + export type LoadLocal = { kind: 'LoadLocal'; place: Place; @@ -894,12 +901,7 @@ export type InstructionValue = right: Place; loc: SourceLocation; } - | { - kind: 'NewExpression'; - callee: Place; - args: Array; - loc: SourceLocation; - } + | NewExpression | CallExpression | MethodCall | { @@ -1649,6 +1651,14 @@ export function isArrayType(id: Identifier): boolean { return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInArray'; } +export function isMapType(id: Identifier): boolean { + return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInMap'; +} + +export function isSetType(id: Identifier): boolean { + return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInSet'; +} + export function isPropsType(id: Identifier): boolean { return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInProps'; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts index 22ae261867b3a..75aa38a71a452 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts @@ -44,6 +44,7 @@ export function addFunction( properties: Iterable<[string, BuiltInType | PolyType]>, fn: Omit, id: string | null = null, + isConstructor: boolean = false, ): FunctionType { const shapeId = id ?? createAnonId(); addShape(registry, shapeId, properties, { @@ -54,6 +55,7 @@ export function addFunction( kind: 'Function', return: fn.returnType, shapeId, + isConstructor, }; } @@ -73,6 +75,7 @@ export function addHook( kind: 'Function', return: fn.returnType, shapeId, + isConstructor: false, }; } @@ -198,6 +201,8 @@ export type ObjectShape = { export type ShapeRegistry = Map; export const BuiltInPropsId = 'BuiltInProps'; export const BuiltInArrayId = 'BuiltInArray'; +export const BuiltInSetId = 'BuiltInSet'; +export const BuiltInMapId = 'BuiltInMap'; export const BuiltInFunctionId = 'BuiltInFunction'; export const BuiltInJsxId = 'BuiltInJsx'; export const BuiltInObjectId = 'BuiltInObject'; @@ -451,6 +456,313 @@ addObject(BUILTIN_SHAPES, BuiltInObjectId, [ */ ]); +/* Built-in Set shape */ +addObject(BUILTIN_SHAPES, BuiltInSetId, [ + [ + /** + * add(value) + * Parameters + * value: the value of the element to add to the Set object. + * Returns the Set object with added value. + */ + 'add', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInSetId}, + calleeEffect: Effect.Store, + // returnValueKind is technically dependent on the ValueKind of the set itself + returnValueKind: ValueKind.Mutable, + }), + ], + [ + /** + * clear() + * Parameters none + * Returns undefined + */ + 'clear', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Store, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + /** + * setInstance.delete(value) + * Returns true if value was already in Set; otherwise false. + */ + 'delete', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Store, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'has', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + ['size', PRIMITIVE_TYPE], + [ + /** + * difference(other) + * Parameters + * other: A Set object, or set-like object. + * Returns a new Set object containing elements in this set but not in the other set. + */ + 'difference', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInSetId}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + /** + * union(other) + * Parameters + * other: A Set object, or set-like object. + * Returns a new Set object containing elements in either this set or the other set. + */ + 'union', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInSetId}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + /** + * symmetricalDifference(other) + * Parameters + * other: A Set object, or set-like object. + * A new Set object containing elements which are in either this set or the other set, but not in both. + */ + 'symmetricalDifference', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInSetId}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + /** + * isSubsetOf(other) + * Parameters + * other: A Set object, or set-like object. + * Returns true if all elements in this set are also in the other set, and false otherwise. + */ + 'isSubsetOf', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + /** + * isSupersetOf(other) + * Parameters + * other: A Set object, or set-like object. + * Returns true if all elements in the other set are also in this set, and false otherwise. + */ + 'isSupersetOf', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + /** + * forEach(callbackFn) + * forEach(callbackFn, thisArg) + */ + 'forEach', + addFunction(BUILTIN_SHAPES, [], { + /** + * see Array.map explanation for why arguments are marked `ConditionallyMutate` + */ + positionalParams: [], + restParam: Effect.ConditionallyMutate, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.ConditionallyMutate, + returnValueKind: ValueKind.Primitive, + noAlias: true, + mutableOnlyIfOperandsAreMutable: true, + }), + ], + /** + * Iterators + */ + [ + 'entries', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + 'keys', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + 'values', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], +]); +addObject(BUILTIN_SHAPES, BuiltInMapId, [ + [ + /** + * clear() + * Parameters none + * Returns undefined + */ + 'clear', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Store, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'delete', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Store, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'get', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + 'has', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + /** + * Params + * key: the key of the element to add to the Map object. The key may be + * any JavaScript type (any primitive value or any type of JavaScript + * object). + * value: the value of the element to add to the Map object. + * Returns the Map object. + */ + 'set', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Capture, Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInMapId}, + calleeEffect: Effect.Store, + returnValueKind: ValueKind.Mutable, + }), + ], + ['size', PRIMITIVE_TYPE], + [ + 'forEach', + addFunction(BUILTIN_SHAPES, [], { + /** + * see Array.map explanation for why arguments are marked `ConditionallyMutate` + */ + positionalParams: [], + restParam: Effect.ConditionallyMutate, + returnType: PRIMITIVE_TYPE, + calleeEffect: Effect.ConditionallyMutate, + returnValueKind: ValueKind.Primitive, + noAlias: true, + mutableOnlyIfOperandsAreMutable: true, + }), + ], + /** + * Iterators + */ + [ + 'entries', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + 'keys', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], + [ + 'values', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: null, + returnType: {kind: 'Poly'}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Mutable, + }), + ], +]); + addObject(BUILTIN_SHAPES, BuiltInUseStateId, [ ['0', {kind: 'Poly'}], [ diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts index 1de81919c391d..53eb8a779d73a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts @@ -38,6 +38,7 @@ export type FunctionType = { kind: 'Function'; shapeId: string | null; return: Type; + isConstructor: boolean; }; export type ObjectType = { @@ -111,6 +112,7 @@ export function duplicateType(type: Type): Type { kind: 'Function', return: duplicateType(type.return), shapeId: type.shapeId, + isConstructor: type.isConstructor, }; } case 'Object': { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 0822a326575f8..3b6725db758e5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -12,6 +12,7 @@ import { BasicBlock, BlockId, CallExpression, + NewExpression, Effect, FunctionEffect, GeneratedSource, @@ -39,7 +40,6 @@ import { printSourceLocation, } from '../HIR/PrintHIR'; import { - eachCallArgument, eachInstructionOperand, eachInstructionValueOperand, eachPatternOperand, @@ -905,43 +905,12 @@ function inferBlock( break; } case 'NewExpression': { - /** - * For new expressions, we infer a `read` effect on the Class / Function type - * to avoid extending mutable ranges of locally created classes, e.g. - * ```js - * const MyClass = getClass(); - * const value = new MyClass(val1, val2) - * ^ (read) ^ (conditionally mutate) - * ``` - * - * Risks: - * Classes / functions created during render could technically capture and - * mutate their enclosing scope, which we currently do not detect. - */ - const valueKind: AbstractValue = { - kind: ValueKind.Mutable, - reason: new Set([ValueReason.Other]), - context: new Set(), - }; - state.referenceAndRecordEffects( + inferCallEffects( + state, + instr as TInstruction, freezeActions, - instrValue.callee, - Effect.Read, - ValueReason.Other, + getFunctionCallSignature(env, instrValue.callee.identifier.type), ); - - for (const operand of eachCallArgument(instrValue.args)) { - state.referenceAndRecordEffects( - freezeActions, - operand, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } - - state.initialize(instrValue, valueKind); - state.define(instr.lvalue, instrValue); - instr.lvalue.effect = Effect.ConditionallyMutate; continuation = {kind: 'funeffects'}; break; } @@ -1844,7 +1813,7 @@ export function getFunctionCallSignature( * @returns Inferred effects of function arguments, or null if inference fails. */ export function getFunctionEffects( - fn: MethodCall | CallExpression, + fn: MethodCall | CallExpression | NewExpression, sig: FunctionSignature, ): Array | null { const results = []; @@ -1989,7 +1958,10 @@ function getArgumentEffect( function inferCallEffects( state: InferenceState, - instr: TInstruction | TInstruction, + instr: + | TInstruction + | TInstruction + | TInstruction, freezeActions: Array, signature: FunctionSignature | null, ): void { @@ -2062,9 +2034,7 @@ function inferCallEffects( hasCaptureArgument ||= place.effect === Effect.Capture; } const callee = - instrValue.kind === 'CallExpression' - ? instrValue.callee - : instrValue.receiver; + instrValue.kind === 'MethodCall' ? instrValue.receiver : instrValue.callee; if (signature !== null) { state.referenceAndRecordEffects( freezeActions, @@ -2073,10 +2043,26 @@ function inferCallEffects( ValueReason.Other, ); } else { + /** + * For new expressions, we infer a `read` effect on the Class / Function type + * to avoid extending mutable ranges of locally created classes, e.g. + * ```js + * const MyClass = getClass(); + * const value = new MyClass(val1, val2) + * ^ (read) ^ (conditionally mutate) + * ``` + * + * Risks: + * Classes / functions created during render could technically capture and + * mutate their enclosing scope, which we currently do not detect. + */ + state.referenceAndRecordEffects( freezeActions, callee, - Effect.ConditionallyMutate, + instrValue.kind === 'NewExpression' + ? Effect.Read + : Effect.ConditionallyMutate, ValueReason.Other, ); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 02e4e60e4b840..69812fc130ded 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -261,6 +261,7 @@ function* generateInstructionTypes( kind: 'Function', shapeId: null, return: returnType, + isConstructor: false, }); yield equation(left, returnType); break; @@ -277,6 +278,7 @@ function* generateInstructionTypes( kind: 'Function', shapeId: null, return: returnType, + isConstructor: false, }); yield equation(left, returnType); break; @@ -333,6 +335,7 @@ function* generateInstructionTypes( kind: 'Function', return: returnType, shapeId: null, + isConstructor: false, }); yield equation(left, returnType); @@ -405,6 +408,7 @@ function* generateInstructionTypes( kind: 'Function', shapeId: BuiltInFunctionId, return: value.loweredFunc.func.returnType, + isConstructor: false, }); break; } @@ -425,9 +429,20 @@ function* generateInstructionTypes( yield equation(left, {kind: 'Object', shapeId: BuiltInJsxId}); break; } + case 'NewExpression': { + const returnType = makeType(); + yield equation(value.callee.identifier.type, { + kind: 'Function', + return: returnType, + shapeId: null, + isConstructor: true, + }); + + yield equation(left, returnType); + break; + } case 'PropertyStore': case 'DeclareLocal': - case 'NewExpression': case 'RegExpLiteral': case 'MetaProperty': case 'ComputedStore': @@ -505,7 +520,11 @@ class Unifier { return; } - if (tB.kind === 'Function' && tA.kind === 'Function') { + if ( + tB.kind === 'Function' && + tA.kind === 'Function' && + tA.isConstructor === tB.isConstructor + ) { this.unify(tA.return, tB.return); return; } @@ -648,6 +667,7 @@ class Unifier { kind: 'Function', return: returnType, shapeId: type.shapeId, + isConstructor: type.isConstructor, }; } case 'ObjectMethod': diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.expect.md new file mode 100644 index 0000000000000..61fe33680fa79 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.expect.md @@ -0,0 +1,77 @@ + +## Input + +```javascript +import {makeArray} from 'shared-runtime'; + +function useHook({el1, el2}) { + const s = new Map(); + s.set(el1, makeArray(el1)); + s.set(el2, makeArray(el2)); + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{el1: 1, el2: 'foo'}], + sequentialRenders: [ + {el1: 1, el2: 'foo'}, + {el1: 2, el2: 'foo'}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { makeArray } from "shared-runtime"; + +function useHook(t0) { + const $ = _c(7); + const { el1, el2 } = t0; + let s; + if ($[0] !== el1 || $[1] !== el2) { + s = new Map(); + let t1; + if ($[3] !== el1) { + t1 = makeArray(el1); + $[3] = el1; + $[4] = t1; + } else { + t1 = $[4]; + } + s.set(el1, t1); + let t2; + if ($[5] !== el2) { + t2 = makeArray(el2); + $[5] = el2; + $[6] = t2; + } else { + t2 = $[6]; + } + s.set(el2, t2); + $[0] = el1; + $[1] = el2; + $[2] = s; + } else { + s = $[2]; + } + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{ el1: 1, el2: "foo" }], + sequentialRenders: [ + { el1: 1, el2: "foo" }, + { el1: 2, el2: "foo" }, + ], +}; + +``` + +### Eval output +(kind: ok) 2 +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.ts new file mode 100644 index 0000000000000..2a0fb6d239079 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/map-constructor.ts @@ -0,0 +1,17 @@ +import {makeArray} from 'shared-runtime'; + +function useHook({el1, el2}) { + const s = new Map(); + s.set(el1, makeArray(el1)); + s.set(el2, makeArray(el2)); + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{el1: 1, el2: 'foo'}], + sequentialRenders: [ + {el1: 1, el2: 'foo'}, + {el1: 2, el2: 'foo'}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.expect.md new file mode 100644 index 0000000000000..cb829ffea2176 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.expect.md @@ -0,0 +1,76 @@ + +## Input + +```javascript +import {makeArray} from 'shared-runtime'; + +function useHook({el1, el2}) { + const s = new Set(); + const arr = makeArray(el1); + s.add(arr); + // Mutate after store + arr.push(el2); + + s.add(makeArray(el2)); + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{el1: 1, el2: 'foo'}], + sequentialRenders: [ + {el1: 1, el2: 'foo'}, + {el1: 2, el2: 'foo'}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { makeArray } from "shared-runtime"; + +function useHook(t0) { + const $ = _c(5); + const { el1, el2 } = t0; + let s; + if ($[0] !== el1 || $[1] !== el2) { + s = new Set(); + const arr = makeArray(el1); + s.add(arr); + + arr.push(el2); + let t1; + if ($[3] !== el2) { + t1 = makeArray(el2); + $[3] = el2; + $[4] = t1; + } else { + t1 = $[4]; + } + s.add(t1); + $[0] = el1; + $[1] = el2; + $[2] = s; + } else { + s = $[2]; + } + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{ el1: 1, el2: "foo" }], + sequentialRenders: [ + { el1: 1, el2: "foo" }, + { el1: 2, el2: "foo" }, + ], +}; + +``` + +### Eval output +(kind: ok) 2 +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.ts new file mode 100644 index 0000000000000..fe49ba813b0c4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-add-mutate.ts @@ -0,0 +1,21 @@ +import {makeArray} from 'shared-runtime'; + +function useHook({el1, el2}) { + const s = new Set(); + const arr = makeArray(el1); + s.add(arr); + // Mutate after store + arr.push(el2); + + s.add(makeArray(el2)); + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{el1: 1, el2: 'foo'}], + sequentialRenders: [ + {el1: 1, el2: 'foo'}, + {el1: 2, el2: 'foo'}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md new file mode 100644 index 0000000000000..3d640c5d2abc9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md @@ -0,0 +1,98 @@ + +## Input + +```javascript +const MODULE_LOCAL = new Set([4, 5, 6]); +function useFoo({propArr}: {propArr: Array}) { + /* TODO: Array can be memoized separately of the Set */ + const s1 = new Set([1, 2, 3]); + s1.add(propArr[0]); + + /* but `.values` cannot be memoized separately */ + const s2 = new Set(MODULE_LOCAL.values()); + s2.add(propArr[1]); + + const s3 = new Set(s2.values()); + s3.add(propArr[2]); + + /** + * TODO: s3 should be memoized separately of s4 + */ + const s4 = new Set(s3); + s4.add(propArr[3]); + return [s1, s2, s3, s4]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{propArr: [7, 8, 9]}], + sequentialRenders: [{propArr: [7, 8, 9]}, {propArr: [7, 8, 10]}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +const MODULE_LOCAL = new Set([4, 5, 6]); +function useFoo(t0) { + const $ = _c(13); + const { propArr } = t0; + let s1; + if ($[0] !== propArr[0]) { + s1 = new Set([1, 2, 3]); + s1.add(propArr[0]); + $[0] = propArr[0]; + $[1] = s1; + } else { + s1 = $[1]; + } + let s2; + let s3; + let s4; + if ($[2] !== propArr[1] || $[3] !== propArr[2] || $[4] !== propArr[3]) { + s2 = new Set(MODULE_LOCAL.values()); + s2.add(propArr[1]); + + s3 = new Set(s2.values()); + s3.add(propArr[2]); + + s4 = new Set(s3); + s4.add(propArr[3]); + $[2] = propArr[1]; + $[3] = propArr[2]; + $[4] = propArr[3]; + $[5] = s2; + $[6] = s3; + $[7] = s4; + } else { + s2 = $[5]; + s3 = $[6]; + s4 = $[7]; + } + let t1; + if ($[8] !== s1 || $[9] !== s2 || $[10] !== s3 || $[11] !== s4) { + t1 = [s1, s2, s3, s4]; + $[8] = s1; + $[9] = s2; + $[10] = s3; + $[11] = s4; + $[12] = t1; + } else { + t1 = $[12]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ propArr: [7, 8, 9] }], + sequentialRenders: [{ propArr: [7, 8, 9] }, { propArr: [7, 8, 10] }], +}; + +``` + +### Eval output +(kind: ok) [{"kind":"Set","value":[1,2,3,7]},{"kind":"Set","value":[4,5,6,8]},{"kind":"Set","value":[4,5,6,8,9]},{"kind":"Set","value":[4,5,6,8,9,null]}] +[{"kind":"Set","value":[1,2,3,7]},{"kind":"Set","value":[4,5,6,8]},{"kind":"Set","value":[4,5,6,8,10]},{"kind":"Set","value":[4,5,6,8,10,null]}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts new file mode 100644 index 0000000000000..9c98fc6e3270d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts @@ -0,0 +1,26 @@ +const MODULE_LOCAL = new Set([4, 5, 6]); +function useFoo({propArr}: {propArr: Array}) { + /* TODO: Array can be memoized separately of the Set */ + const s1 = new Set([1, 2, 3]); + s1.add(propArr[0]); + + /* but `.values` cannot be memoized separately */ + const s2 = new Set(MODULE_LOCAL.values()); + s2.add(propArr[1]); + + const s3 = new Set(s2.values()); + s3.add(propArr[2]); + + /** + * TODO: s3 should be memoized separately of s4 + */ + const s4 = new Set(s3); + s4.add(propArr[3]); + return [s1, s2, s3, s4]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{propArr: [7, 8, 9]}], + sequentialRenders: [{propArr: [7, 8, 9]}, {propArr: [7, 8, 10]}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.expect.md new file mode 100644 index 0000000000000..371e98089f5a8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.expect.md @@ -0,0 +1,77 @@ + +## Input + +```javascript +import {makeArray} from 'shared-runtime'; + +function useHook({el1, el2}) { + const s = new Set(); + s.add(makeArray(el1)); + s.add(makeArray(el2)); + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{el1: 1, el2: 'foo'}], + sequentialRenders: [ + {el1: 1, el2: 'foo'}, + {el1: 2, el2: 'foo'}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { makeArray } from "shared-runtime"; + +function useHook(t0) { + const $ = _c(7); + const { el1, el2 } = t0; + let s; + if ($[0] !== el1 || $[1] !== el2) { + s = new Set(); + let t1; + if ($[3] !== el1) { + t1 = makeArray(el1); + $[3] = el1; + $[4] = t1; + } else { + t1 = $[4]; + } + s.add(t1); + let t2; + if ($[5] !== el2) { + t2 = makeArray(el2); + $[5] = el2; + $[6] = t2; + } else { + t2 = $[6]; + } + s.add(t2); + $[0] = el1; + $[1] = el2; + $[2] = s; + } else { + s = $[2]; + } + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{ el1: 1, el2: "foo" }], + sequentialRenders: [ + { el1: 1, el2: "foo" }, + { el1: 2, el2: "foo" }, + ], +}; + +``` + +### Eval output +(kind: ok) 2 +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.ts new file mode 100644 index 0000000000000..049e411d53ee2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor.ts @@ -0,0 +1,17 @@ +import {makeArray} from 'shared-runtime'; + +function useHook({el1, el2}) { + const s = new Set(); + s.add(makeArray(el1)); + s.add(makeArray(el2)); + return s.size; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{el1: 1, el2: 'foo'}], + sequentialRenders: [ + {el1: 1, el2: 'foo'}, + {el1: 2, el2: 'foo'}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.expect.md new file mode 100644 index 0000000000000..d5fcb7f73de06 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.expect.md @@ -0,0 +1,82 @@ + +## Input + +```javascript +import {makeArray, mutate} from 'shared-runtime'; + +function useFoo({propArr}: {propArr: Array}) { + const s1 = new Set>([1, 2, 3]); + s1.add(makeArray(propArr[0])); + + const s2 = new Set(s1); + // this may also may mutate s1 + mutate(s2); + + return [s1, s2]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{propArr: [7, 8, 9]}], + sequentialRenders: [ + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 10]}, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { makeArray, mutate } from "shared-runtime"; + +function useFoo(t0) { + const $ = _c(6); + const { propArr } = t0; + let s1; + let s2; + if ($[0] !== propArr[0]) { + s1 = new Set([1, 2, 3]); + s1.add(makeArray(propArr[0])); + + s2 = new Set(s1); + + mutate(s2); + $[0] = propArr[0]; + $[1] = s1; + $[2] = s2; + } else { + s1 = $[1]; + s2 = $[2]; + } + let t1; + if ($[3] !== s1 || $[4] !== s2) { + t1 = [s1, s2]; + $[3] = s1; + $[4] = s2; + $[5] = t1; + } else { + t1 = $[5]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ propArr: [7, 8, 9] }], + sequentialRenders: [ + { propArr: [7, 8, 9] }, + { propArr: [7, 8, 9] }, + { propArr: [7, 8, 10] }, + ], +}; + +``` + +### Eval output +(kind: ok) [{"kind":"Set","value":[1,2,3,[7]]},{"kind":"Set","value":[1,2,3,"[[ cyclic ref *2 ]]"]}] +[{"kind":"Set","value":[1,2,3,[7]]},{"kind":"Set","value":[1,2,3,"[[ cyclic ref *2 ]]"]}] +[{"kind":"Set","value":[1,2,3,[7]]},{"kind":"Set","value":[1,2,3,"[[ cyclic ref *2 ]]"]}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.ts new file mode 100644 index 0000000000000..7bd283371e00c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-copy-constructor-mutate.ts @@ -0,0 +1,22 @@ +import {makeArray, mutate} from 'shared-runtime'; + +function useFoo({propArr}: {propArr: Array}) { + const s1 = new Set>([1, 2, 3]); + s1.add(makeArray(propArr[0])); + + const s2 = new Set(s1); + // this may also may mutate s1 + mutate(s2); + + return [s1, s2]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{propArr: [7, 8, 9]}], + sequentialRenders: [ + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 10]}, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.expect.md new file mode 100644 index 0000000000000..47ac8557dfbaa --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.expect.md @@ -0,0 +1,65 @@ + +## Input + +```javascript +import {makeArray, useHook} from 'shared-runtime'; + +function useFoo({propArr}: {propArr: Array}) { + const s1 = new Set>([1, 2, 3]); + s1.add(makeArray(propArr[0])); + + useHook(); + const s2 = new Set(); + for (const el of s1.values()) { + s2.add(el); + } + + return [s1, s2]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{propArr: [7, 8, 9]}], + sequentialRenders: [ + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 10]}, + ], +}; + +``` + +## Code + +```javascript +import { makeArray, useHook } from "shared-runtime"; + +function useFoo(t0) { + const { propArr } = t0; + const s1 = new Set([1, 2, 3]); + s1.add(makeArray(propArr[0])); + + useHook(); + const s2 = new Set(); + for (const el of s1.values()) { + s2.add(el); + } + return [s1, s2]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{ propArr: [7, 8, 9] }], + sequentialRenders: [ + { propArr: [7, 8, 9] }, + { propArr: [7, 8, 9] }, + { propArr: [7, 8, 10] }, + ], +}; + +``` + +### Eval output +(kind: ok) [{"kind":"Set","value":[1,2,3,[7]]},{"kind":"Set","value":[1,2,3,"[[ cyclic ref *2 ]]"]}] +[{"kind":"Set","value":[1,2,3,[7]]},{"kind":"Set","value":[1,2,3,"[[ cyclic ref *2 ]]"]}] +[{"kind":"Set","value":[1,2,3,[7]]},{"kind":"Set","value":[1,2,3,"[[ cyclic ref *2 ]]"]}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.ts new file mode 100644 index 0000000000000..63574c4bc3afd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-for-of-iterate-values.ts @@ -0,0 +1,24 @@ +import {makeArray, useHook} from 'shared-runtime'; + +function useFoo({propArr}: {propArr: Array}) { + const s1 = new Set>([1, 2, 3]); + s1.add(makeArray(propArr[0])); + + useHook(); + const s2 = new Set(); + for (const el of s1.values()) { + s2.add(el); + } + + return [s1, s2]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{propArr: [7, 8, 9]}], + sequentialRenders: [ + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 9]}, + {propArr: [7, 8, 10]}, + ], +}; From 7c908bcf4e6b46135164be961972f0d756378517 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:25:55 -0400 Subject: [PATCH 203/300] [compiler][optim] Add Effect.ConditionallyMutateIterator (#32698) Adds Effect.ConditionallyMutateIterator, which has the following effects: - capture for known array, map, and sets - mutate for all other values An alternative to this approach could be to add polymorphic shape definitions --- .../src/HIR/Globals.ts | 8 +-- .../src/HIR/HIR.ts | 4 +- .../src/Inference/InferMutableLifetimes.ts | 14 ++++ .../src/Inference/InferReactivePlaces.ts | 1 + .../src/Inference/InferReferenceEffects.ts | 38 +++++++++-- .../array-from-arg1-captures-arg0.expect.md | 59 ++++++++++++----- .../array-from-captures-arg0.expect.md | 59 ++++++++++++----- .../array-from-maybemutates-arg0.expect.md | 11 ++-- .../compiler/array-from-maybemutates-arg0.js | 4 +- .../call-spread-argument-set.expect.md | 66 +++++++++++++++++++ .../global-types/call-spread-argument-set.ts | 17 +++++ .../set-constructor-arg.expect.md | 66 +++++++++++-------- .../global-types/set-constructor-arg.ts | 4 +- .../global-types/set-foreach-mutate.expect.md | 57 ++++++++++++++++ .../global-types/set-foreach-mutate.tsx | 14 ++++ ...todo-granular-iterator-semantics.expect.md | 46 +++++++------ .../todo-granular-iterator-semantics.js | 4 +- .../type-inference-array-from.expect.md | 52 ++++++++------- 18 files changed, 398 insertions(+), 126 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts index 68f8e199dbb55..37a8816d0b336 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts @@ -65,8 +65,6 @@ const UNTYPED_GLOBALS: Set = new Set([ 'Int8Array', 'Int16Array', 'Int32Array', - 'Map', - 'Set', 'WeakMap', 'Uint8Array', 'Uint8ClampedArray', @@ -140,7 +138,7 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ 'from', addFunction(DEFAULT_SHAPES, [], { positionalParams: [ - Effect.ConditionallyMutate, + Effect.ConditionallyMutateIterator, Effect.ConditionallyMutate, Effect.ConditionallyMutate, ], @@ -466,7 +464,7 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ DEFAULT_SHAPES, [], { - positionalParams: [Effect.ConditionallyMutate], + positionalParams: [Effect.ConditionallyMutateIterator], restParam: null, returnType: {kind: 'Object', shapeId: BuiltInMapId}, calleeEffect: Effect.Read, @@ -482,7 +480,7 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ DEFAULT_SHAPES, [], { - positionalParams: [Effect.ConditionallyMutate], + positionalParams: [Effect.ConditionallyMutateIterator], restParam: null, returnType: {kind: 'Object', shapeId: BuiltInSetId}, calleeEffect: Effect.Read, diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index d2e989890694c..f58cdfbb081b2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -1396,6 +1396,7 @@ export enum Effect { Read = 'read', // This reference reads and stores the value Capture = 'capture', + ConditionallyMutateIterator = 'mutate-iterator?', /* * This reference *may* write to (mutate) the value. This covers two similar cases: * - The compiler is being conservative and assuming that a value *may* be mutated @@ -1414,11 +1415,11 @@ export enum Effect { // This reference may alias to (mutate) the value Store = 'store', } - export const EffectSchema = z.enum([ Effect.Read, Effect.Mutate, Effect.ConditionallyMutate, + Effect.ConditionallyMutateIterator, Effect.Capture, Effect.Store, Effect.Freeze, @@ -1432,6 +1433,7 @@ export function isMutableEffect( case Effect.Capture: case Effect.Store: case Effect.ConditionallyMutate: + case Effect.ConditionallyMutateIterator: case Effect.Mutate: { return true; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts index 508a970d931b0..45b5462efbe52 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutableLifetimes.ts @@ -11,7 +11,10 @@ import { Identifier, InstructionId, InstructionKind, + isArrayType, + isMapType, isRefOrRefValue, + isSetType, makeInstructionId, Place, } from '../HIR/HIR'; @@ -90,6 +93,17 @@ function inferPlace( infer(place, instrId); } return; + case Effect.ConditionallyMutateIterator: { + const identifier = place.identifier; + if ( + !isArrayType(identifier) && + !isSetType(identifier) && + !isMapType(identifier) + ) { + infer(place, instrId); + } + return; + } case Effect.ConditionallyMutate: case Effect.Mutate: { infer(place, instrId); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts index 344949b020a99..e2deab15dbf0d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReactivePlaces.ts @@ -230,6 +230,7 @@ export function inferReactivePlaces(fn: HIRFunction): void { case Effect.Capture: case Effect.Store: case Effect.ConditionallyMutate: + case Effect.ConditionallyMutateIterator: case Effect.Mutate: { if (isMutable(instruction, operand)) { reactiveIdentifiers.markReactive(operand); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 3b6725db758e5..47ddc85a0b8b7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -29,8 +29,10 @@ import { ValueKind, ValueReason, isArrayType, + isMapType, isMutableEffect, isObjectType, + isSetType, } from '../HIR/HIR'; import {FunctionSignature} from '../HIR/ObjectShape'; import { @@ -469,6 +471,25 @@ class InferenceState { } break; } + case Effect.ConditionallyMutateIterator: { + if ( + valueKind.kind === ValueKind.Mutable || + valueKind.kind === ValueKind.Context + ) { + if ( + isArrayType(place.identifier) || + isSetType(place.identifier) || + isMapType(place.identifier) + ) { + effect = Effect.Capture; + } else { + effect = Effect.ConditionallyMutate; + } + } else { + effect = Effect.Read; + } + break; + } case Effect.Mutate: { effect = Effect.Mutate; break; @@ -880,9 +901,7 @@ function inferBlock( state.referenceAndRecordEffects( freezeActions, element.place, - isArrayType(element.place.identifier) - ? Effect.Capture - : Effect.ConditionallyMutate, + Effect.ConditionallyMutateIterator, ValueReason.Other, ); } else if (element.kind === 'Identifier') { @@ -1643,7 +1662,13 @@ function inferBlock( kind === ValueKind.Mutable || kind === ValueKind.Context; let effect; let valueKind: AbstractValue; - if (!isMutable || isArrayType(instrValue.collection.identifier)) { + const iterator = instrValue.collection.identifier; + if ( + !isMutable || + isArrayType(iterator) || + isMapType(iterator) || + isSetType(iterator) + ) { // Case 1, assume iterator is a separate mutable object effect = { kind: Effect.Read, @@ -1684,7 +1709,7 @@ function inferBlock( state.referenceAndRecordEffects( freezeActions, instrValue.iterator, - Effect.ConditionallyMutate, + Effect.ConditionallyMutateIterator, ValueReason.Other, ); /** @@ -1846,6 +1871,7 @@ export function isKnownMutableEffect(effect: Effect): boolean { switch (effect) { case Effect.Store: case Effect.ConditionallyMutate: + case Effect.ConditionallyMutateIterator: case Effect.Mutate: { return true; } @@ -1949,7 +1975,7 @@ function getArgumentEffect( }); } // effects[i] is Effect.Capture | Effect.Read | Effect.Store - return Effect.ConditionallyMutate; + return Effect.ConditionallyMutateIterator; } } else { return Effect.ConditionallyMutate; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md index 8892c8d484bfb..12be224d8f2cb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md @@ -50,28 +50,55 @@ import { useIdentity, Stringify } from "shared-runtime"; * (2) the 1st argument might mutate its callee */ function Component(t0) { - const $ = _c(4); + const $ = _c(10); const { value } = t0; - const arr = [{ value: "foo" }, { value: "bar" }, { value }]; - useIdentity(); - const derived = Array.from(arr, _temp); let t1; - if ($[0] !== derived) { - t1 = derived.at(-1); - $[0] = derived; - $[1] = t1; + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { value: "foo" }; + t2 = { value: "bar" }; + $[0] = t1; + $[1] = t2; } else { - t1 = $[1]; + t1 = $[0]; + t2 = $[1]; } - let t2; - if ($[2] !== t1) { - t2 = {t1}; - $[2] = t1; - $[3] = t2; + let t3; + if ($[2] !== value) { + t3 = [t1, t2, { value }]; + $[2] = value; + $[3] = t3; + } else { + t3 = $[3]; + } + const arr = t3; + useIdentity(); + let t4; + if ($[4] !== arr) { + t4 = Array.from(arr, _temp); + $[4] = arr; + $[5] = t4; + } else { + t4 = $[5]; + } + const derived = t4; + let t5; + if ($[6] !== derived) { + t5 = derived.at(-1); + $[6] = derived; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== t5) { + t6 = {t5}; + $[8] = t5; + $[9] = t6; } else { - t2 = $[3]; + t6 = $[9]; } - return t2; + return t6; } function _temp(x, idx) { return { ...x, id: idx }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md index 66d0b4258462c..3e89dbeae8afb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md @@ -50,28 +50,55 @@ import { useIdentity, Stringify } from "shared-runtime"; * (2) the 1st argument might mutate its callee */ function Component(t0) { - const $ = _c(4); + const $ = _c(10); const { value } = t0; - const arr = [{ value: "foo" }, { value: "bar" }, { value }]; - useIdentity(); - const derived = Array.from(arr); let t1; - if ($[0] !== derived) { - t1 = derived.at(-1); - $[0] = derived; - $[1] = t1; + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { value: "foo" }; + t2 = { value: "bar" }; + $[0] = t1; + $[1] = t2; } else { - t1 = $[1]; + t1 = $[0]; + t2 = $[1]; } - let t2; - if ($[2] !== t1) { - t2 = {t1}; - $[2] = t1; - $[3] = t2; + let t3; + if ($[2] !== value) { + t3 = [t1, t2, { value }]; + $[2] = value; + $[3] = t3; + } else { + t3 = $[3]; + } + const arr = t3; + useIdentity(); + let t4; + if ($[4] !== arr) { + t4 = Array.from(arr); + $[4] = arr; + $[5] = t4; + } else { + t4 = $[5]; + } + const derived = t4; + let t5; + if ($[6] !== derived) { + t5 = derived.at(-1); + $[6] = derived; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== t5) { + t6 = {t5}; + $[8] = t5; + $[9] = t6; } else { - t2 = $[3]; + t6 = $[9]; } - return t2; + return t6; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md index 9be174d99864c..94211944955f2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md @@ -7,7 +7,7 @@ import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; function Component({value}) { const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; useIdentity(); - const derived = Array.from(arr, mutateAndReturn); + const derived = Array.from(arr).map(mutateAndReturn); return ( {derived.at(0)} @@ -19,7 +19,7 @@ function Component({value}) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{value: 5}], - sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}, {value: 7}], }; ``` @@ -35,7 +35,7 @@ function Component(t0) { const { value } = t0; const arr = [{ value: "foo" }, { value: "bar" }, { value }]; useIdentity(); - const derived = Array.from(arr, mutateAndReturn); + const derived = Array.from(arr).map(mutateAndReturn); let t1; if ($[0] !== derived) { t1 = derived.at(0); @@ -72,7 +72,7 @@ function Component(t0) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ value: 5 }], - sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }, { value: 7 }], }; ``` @@ -80,4 +80,5 @@ export const FIXTURE_ENTRYPOINT = { ### Eval output (kind: ok)
{"children":[{"value":"foo","wat0":"joe"},{"value":5,"wat0":"joe"}]}
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
-
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
\ No newline at end of file +
{"children":[{"value":"foo","wat0":"joe"},{"value":6,"wat0":"joe"}]}
+
{"children":[{"value":"foo","wat0":"joe"},{"value":7,"wat0":"joe"}]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js index 4e224c8a9ac8e..af5bea83a3d09 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js @@ -3,7 +3,7 @@ import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; function Component({value}) { const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; useIdentity(); - const derived = Array.from(arr, mutateAndReturn); + const derived = Array.from(arr).map(mutateAndReturn); return ( {derived.at(0)} @@ -15,5 +15,5 @@ function Component({value}) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{value: 5}], - sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}, {value: 7}], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.expect.md new file mode 100644 index 0000000000000..6882f8f4bded3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.expect.md @@ -0,0 +1,66 @@ + +## Input + +```javascript +import {useIdentity} from 'shared-runtime'; + +/** + * Forked version of call-spread-argument-mutable-iterator that is known to not mutate + * the spread argument since it is a Set + */ +function useFoo() { + const s = new Set([1, 2]); + useIdentity(null); + return [Math.max(...s), s]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity } from "shared-runtime"; + +/** + * Forked version of call-spread-argument-mutable-iterator that is known to not mutate + * the spread argument since it is a Set + */ +function useFoo() { + const $ = _c(2); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = new Set([1, 2]); + $[0] = t0; + } else { + t0 = $[0]; + } + const s = t0; + useIdentity(null); + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = [Math.max(...s), s]; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +### Eval output +(kind: ok) [2,{"kind":"Set","value":[1,2]}] +[2,{"kind":"Set","value":[1,2]}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.ts new file mode 100644 index 0000000000000..b2746b01683a1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/call-spread-argument-set.ts @@ -0,0 +1,17 @@ +import {useIdentity} from 'shared-runtime'; + +/** + * Forked version of call-spread-argument-mutable-iterator that is known to not mutate + * the spread argument since it is a Set + */ +function useFoo() { + const s = new Set([1, 2]); + useIdentity(null); + return [Math.max(...s), s]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md index 3d640c5d2abc9..e0d675a2c746a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.expect.md @@ -4,7 +4,7 @@ ```javascript const MODULE_LOCAL = new Set([4, 5, 6]); function useFoo({propArr}: {propArr: Array}) { - /* TODO: Array can be memoized separately of the Set */ + /* Array can be memoized separately of the Set */ const s1 = new Set([1, 2, 3]); s1.add(propArr[0]); @@ -16,7 +16,7 @@ function useFoo({propArr}: {propArr: Array}) { s3.add(propArr[2]); /** - * TODO: s3 should be memoized separately of s4 + * s4 should be memoized separately from s3 */ const s4 = new Set(s3); s4.add(propArr[3]); @@ -37,52 +37,62 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; const MODULE_LOCAL = new Set([4, 5, 6]); function useFoo(t0) { - const $ = _c(13); + const $ = _c(15); const { propArr } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = [1, 2, 3]; + $[0] = t1; + } else { + t1 = $[0]; + } let s1; - if ($[0] !== propArr[0]) { - s1 = new Set([1, 2, 3]); + if ($[1] !== propArr[0]) { + s1 = new Set(t1); s1.add(propArr[0]); - $[0] = propArr[0]; - $[1] = s1; + $[1] = propArr[0]; + $[2] = s1; } else { - s1 = $[1]; + s1 = $[2]; } let s2; let s3; - let s4; - if ($[2] !== propArr[1] || $[3] !== propArr[2] || $[4] !== propArr[3]) { + if ($[3] !== propArr[1] || $[4] !== propArr[2]) { s2 = new Set(MODULE_LOCAL.values()); s2.add(propArr[1]); s3 = new Set(s2.values()); s3.add(propArr[2]); - - s4 = new Set(s3); - s4.add(propArr[3]); - $[2] = propArr[1]; - $[3] = propArr[2]; - $[4] = propArr[3]; + $[3] = propArr[1]; + $[4] = propArr[2]; $[5] = s2; $[6] = s3; - $[7] = s4; } else { s2 = $[5]; s3 = $[6]; - s4 = $[7]; } - let t1; - if ($[8] !== s1 || $[9] !== s2 || $[10] !== s3 || $[11] !== s4) { - t1 = [s1, s2, s3, s4]; - $[8] = s1; - $[9] = s2; - $[10] = s3; - $[11] = s4; - $[12] = t1; + let s4; + if ($[7] !== propArr[3] || $[8] !== s3) { + s4 = new Set(s3); + s4.add(propArr[3]); + $[7] = propArr[3]; + $[8] = s3; + $[9] = s4; + } else { + s4 = $[9]; + } + let t2; + if ($[10] !== s1 || $[11] !== s2 || $[12] !== s3 || $[13] !== s4) { + t2 = [s1, s2, s3, s4]; + $[10] = s1; + $[11] = s2; + $[12] = s3; + $[13] = s4; + $[14] = t2; } else { - t1 = $[12]; + t2 = $[14]; } - return t1; + return t2; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts index 9c98fc6e3270d..04508ac1753d8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-constructor-arg.ts @@ -1,6 +1,6 @@ const MODULE_LOCAL = new Set([4, 5, 6]); function useFoo({propArr}: {propArr: Array}) { - /* TODO: Array can be memoized separately of the Set */ + /* Array can be memoized separately of the Set */ const s1 = new Set([1, 2, 3]); s1.add(propArr[0]); @@ -12,7 +12,7 @@ function useFoo({propArr}: {propArr: Array}) { s3.add(propArr[2]); /** - * TODO: s3 should be memoized separately of s4 + * s4 should be memoized separately from s3 */ const s4 = new Set(s3); s4.add(propArr[3]); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.expect.md new file mode 100644 index 0000000000000..8c66b1f678855 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = new Set(arr).forEach(mutateAndReturn); + return {[...derived]}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}, {value: 7}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime"; + +function Component(t0) { + const $ = _c(2); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = new Set(arr).forEach(mutateAndReturn); + let t1; + if ($[0] !== derived) { + t1 = {[...derived]}; + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }, { value: 7 }], +}; + +``` + +### Eval output +(kind: ok) [[ (exception in render) TypeError: derived is not iterable ]] +[[ (exception in render) TypeError: derived is not iterable ]] +[[ (exception in render) TypeError: derived is not iterable ]] +[[ (exception in render) TypeError: derived is not iterable ]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.tsx new file mode 100644 index 0000000000000..b5d558e928888 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/global-types/set-foreach-mutate.tsx @@ -0,0 +1,14 @@ +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = new Set(arr).forEach(mutateAndReturn); + return {[...derived]}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}, {value: 7}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md index ea3f1d4f38715..b4aec392e1af6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md @@ -5,7 +5,7 @@ import {useIdentity, ValidateMemoization} from 'shared-runtime'; /** - * TODO fixture for granular iterator semantics: + * Fixture for granular iterator semantics: * 1. ConditionallyMutate the iterator itself, depending on whether the iterator * is a mutable iterator. * 2. Capture effect on elements within the iterator. @@ -26,7 +26,7 @@ function Validate({x, input}) { function useFoo(input) { 'use memo'; /** - * TODO: We should be able to memoize {} separately from `x`. + * We should be able to memoize {} separately from `x`. */ const x = Array.from([{}]); useIdentity(); @@ -48,7 +48,7 @@ import { c as _c } from "react/compiler-runtime"; import { useIdentity, ValidateMemoization } from "shared-runtime"; /** - * TODO fixture for granular iterator semantics: + * Fixture for granular iterator semantics: * 1. ConditionallyMutate the iterator itself, depending on whether the iterator * is a mutable iterator. * 2. Capture effect on elements within the iterator. @@ -68,29 +68,35 @@ function Validate({ x, input }) { } function useFoo(input) { "use memo"; - const $ = _c(5); - - const x = Array.from([{}]); - useIdentity(); + const $ = _c(6); let t0; - if ($[0] !== input) { - t0 = [input]; - $[0] = input; - $[1] = t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = [{}]; + $[0] = t0; } else { - t0 = $[1]; + t0 = $[0]; } - x.push(t0); + const x = Array.from(t0); + useIdentity(); let t1; - if ($[2] !== input || $[3] !== x) { - t1 = ; - $[2] = input; - $[3] = x; - $[4] = t1; + if ($[1] !== input) { + t1 = [input]; + $[1] = input; + $[2] = t1; + } else { + t1 = $[2]; + } + x.push(t1); + let t2; + if ($[3] !== input || $[4] !== x) { + t2 = ; + $[3] = input; + $[4] = x; + $[5] = t2; } else { - t1 = $[4]; + t2 = $[5]; } - return t1; + return t2; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js index 27d861692cfa4..3e24d0b5b2e95 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js @@ -1,7 +1,7 @@ import {useIdentity, ValidateMemoization} from 'shared-runtime'; /** - * TODO fixture for granular iterator semantics: + * Fixture for granular iterator semantics: * 1. ConditionallyMutate the iterator itself, depending on whether the iterator * is a mutable iterator. * 2. Capture effect on elements within the iterator. @@ -22,7 +22,7 @@ function Validate({x, input}) { function useFoo(input) { 'use memo'; /** - * TODO: We should be able to memoize {} separately from `x`. + * We should be able to memoize {} separately from `x`. */ const x = Array.from([{}]); useIdentity(); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md index 5209fd953ec88..ab584c11590b6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md @@ -77,40 +77,46 @@ function Validate({ x, val1, val2 }) { } function useFoo(t0) { "use memo"; - const $ = _c(8); + const $ = _c(9); const { val1, val2 } = t0; - - const x = Array.from([]); - useIdentity(); let t1; - if ($[0] !== val1) { - t1 = [val1]; - $[0] = val1; - $[1] = t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; } else { - t1 = $[1]; + t1 = $[0]; } - x.push(t1); + const x = Array.from(t1); + useIdentity(); let t2; - if ($[2] !== val2) { - t2 = [val2]; - $[2] = val2; - $[3] = t2; + if ($[1] !== val1) { + t2 = [val1]; + $[1] = val1; + $[2] = t2; } else { - t2 = $[3]; + t2 = $[2]; } x.push(t2); let t3; - if ($[4] !== val1 || $[5] !== val2 || $[6] !== x) { - t3 = ; - $[4] = val1; - $[5] = val2; - $[6] = x; - $[7] = t3; + if ($[3] !== val2) { + t3 = [val2]; + $[3] = val2; + $[4] = t3; + } else { + t3 = $[4]; + } + x.push(t3); + let t4; + if ($[5] !== val1 || $[6] !== val2 || $[7] !== x) { + t4 = ; + $[5] = val1; + $[6] = val2; + $[7] = x; + $[8] = t4; } else { - t3 = $[7]; + t4 = $[8]; } - return t3; + return t4; } export const FIXTURE_ENTRYPOINT = { From c61e75b76d5ff6707ad75c8beb777e721d982207 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:31:51 -0400 Subject: [PATCH 204/300] [compiler] Avoid failing builds when import specifiers conflict or shadow vars (#32663) Avoid failing builds when imported function specifiers conflict by using babel's `generateUid`. Failing a build is very disruptive, as it usually presents to developers similar to a javascript parse error. ```js import {logRender as _logRender} from 'instrument-runtime'; const logRender = () => { /* local conflicting implementation */ } function Component_optimized() { _logRender(); // inserted by compiler } ``` Currently, we fail builds (even in `panicThreshold:none` cases) when import specifiers are detected to conflict with existing local variables. The reason we destructively throw (instead of bailing out) is because (1) we first generate identifier references to the conflicting name in compiled functions, (2) replaced original functions with compiled functions, and then (3) finally check for conflicts. When we finally check for conflicts, it's too late to bail out. ```js // import {logRender} from 'instrument-runtime'; const logRender = () => { /* local conflicting implementation */ } function Component_optimized() { logRender(); // inserted by compiler } ``` --- .../src/Entrypoint/Gating.ts | 33 +- .../src/Entrypoint/Imports.ts | 335 +++++++++++------- .../src/Entrypoint/Pipeline.ts | 10 +- .../src/Entrypoint/Program.ts | 131 ++----- .../src/HIR/Environment.ts | 17 +- .../src/HIR/HIR.ts | 17 +- .../src/HIR/HIRBuilder.ts | 1 + .../src/Inference/InferEffectDependencies.ts | 1 + .../src/Optimization/LowerContextAccess.ts | 20 +- .../src/Optimization/OutlineJsx.ts | 4 +- .../ReactiveScopes/CodegenReactiveFunction.ts | 106 ++++-- .../src/ReactiveScopes/RenameVariables.ts | 8 +- .../src/Transform/TransformFire.ts | 25 +- ...el-existing-react-runtime-import.expect.md | 73 ++++ .../babel-existing-react-runtime-import.js | 20 ++ ...codegen-emit-imports-same-source.expect.md | 4 +- .../codegen-instrument-forget-test.expect.md | 2 +- ...nflict-codegen-instrument-forget.expect.md | 97 +++++ .../conflict-codegen-instrument-forget.js | 23 ++ .../emit-freeze-conflicting-imports.expect.md | 37 ++ ....js => emit-freeze-conflicting-imports.js} | 0 ...-nonconflicting-global-reference.expect.md | 33 ++ ...-freeze-nonconflicting-global-reference.js | 4 + ...gen-error-on-conflicting-imports.expect.md | 21 -- ...r.emit-freeze-conflicting-global.expect.md | 27 ++ .../error.emit-freeze-conflicting-global.js | 6 + .../arrow-function-expr-gating-test.expect.md | 4 +- ...en-instrument-forget-gating-test.expect.md | 6 +- ...component-syntax-ref-gating.flow.expect.md | 13 +- .../gating/conflicting-gating-fn.expect.md | 62 ++++ .../compiler/gating/conflicting-gating-fn.js | 16 + ...ccess-function-name-in-component.expect.md | 4 +- ...nreferenced-identifier-collision.expect.md | 4 +- ...ng-preserves-function-properties.expect.md | 4 +- ...ing-test-export-default-function.expect.md | 4 +- ...test-export-function-and-default.expect.md | 4 +- .../gating-test-export-function.expect.md | 4 +- .../compiler/gating/gating-test.expect.md | 4 +- .../gating-use-before-decl-ref.expect.md | 15 +- .../gating/gating-use-before-decl.expect.md | 14 +- ...with-hoisted-type-reference.flow.expect.md | 2 +- ...ion-expression-React-memo-gating.expect.md | 4 +- .../gating/invalid-fnexpr-reference.expect.md | 4 +- ...-expr-export-default-gating-test.expect.md | 4 +- ...ti-arrow-expr-export-gating-test.expect.md | 4 +- .../multi-arrow-expr-gating-test.expect.md | 4 +- .../reassigned-fnexpr-variable.expect.md | 4 +- .../lower-context-access-hook-guard.expect.md | 66 ++++ .../lower-context-access-hook-guard.js | 6 + ...-fire-todo-syntax-shouldnt-throw.expect.md | 3 +- .../compiler/transform-fire/basic.expect.md | 3 +- .../transform-fire/deep-scope.expect.md | 3 +- .../fire-and-autodeps.expect.md | 3 +- .../transform-fire/hook-guard.expect.md | 72 ++++ .../compiler/transform-fire/hook-guard.js | 13 + .../transform-fire/multiple-scope.expect.md | 3 +- .../transform-fire/repeated-calls.expect.md | 3 +- ...ro-dont-add-hook-guards-on-retry.expect.md | 1 - .../transform-fire/rewrite-deps.expect.md | 3 +- .../shared-hook-calls.expect.md | 3 +- .../babel-plugin-react-compiler/src/index.ts | 1 + 61 files changed, 1013 insertions(+), 409 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.codegen-error-on-conflicting-imports.js => emit-freeze-conflicting-imports.js} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts index 16ef7986ef16a..679389277b59e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts @@ -7,8 +7,9 @@ import {NodePath} from '@babel/core'; import * as t from '@babel/types'; -import {PluginOptions} from './Options'; import {CompilerError} from '../CompilerError'; +import {ProgramContext} from './Imports'; +import {ExternalFunction} from '..'; /** * Gating rewrite for function declarations which are referenced before their @@ -34,7 +35,8 @@ import {CompilerError} from '../CompilerError'; function insertAdditionalFunctionDeclaration( fnPath: NodePath, compiled: t.FunctionDeclaration, - gating: NonNullable, + programContext: ProgramContext, + gatingFunctionIdentifierName: string, ): void { const originalFnName = fnPath.node.id; const originalFnParams = fnPath.node.params; @@ -57,14 +59,14 @@ function insertAdditionalFunctionDeclaration( loc: fnPath.node.loc ?? null, }); - const gatingCondition = fnPath.scope.generateUidIdentifier( - `${gating.importSpecifierName}_result`, + const gatingCondition = t.identifier( + programContext.newUid(`${gatingFunctionIdentifierName}_result`), ); - const unoptimizedFnName = fnPath.scope.generateUidIdentifier( - `${originalFnName.name}_unoptimized`, + const unoptimizedFnName = t.identifier( + programContext.newUid(`${originalFnName.name}_unoptimized`), ); - const optimizedFnName = fnPath.scope.generateUidIdentifier( - `${originalFnName.name}_optimized`, + const optimizedFnName = t.identifier( + programContext.newUid(`${originalFnName.name}_optimized`), ); /** * Step 1: rename existing functions @@ -115,7 +117,7 @@ function insertAdditionalFunctionDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( gatingCondition, - t.callExpression(t.identifier(gating.importSpecifierName), []), + t.callExpression(t.identifier(gatingFunctionIdentifierName), []), ), ]), ); @@ -129,19 +131,26 @@ export function insertGatedFunctionDeclaration( | t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression, - gating: NonNullable, + programContext: ProgramContext, + gating: ExternalFunction, referencedBeforeDeclaration: boolean, ): void { + const gatingImportedName = programContext.addImportSpecifier(gating).name; if (referencedBeforeDeclaration && fnPath.isFunctionDeclaration()) { CompilerError.invariant(compiled.type === 'FunctionDeclaration', { reason: 'Expected compiled node type to match input type', description: `Got ${compiled.type} but expected FunctionDeclaration`, loc: fnPath.node.loc ?? null, }); - insertAdditionalFunctionDeclaration(fnPath, compiled, gating); + insertAdditionalFunctionDeclaration( + fnPath, + compiled, + programContext, + gatingImportedName, + ); } else { const gatingExpression = t.conditionalExpression( - t.callExpression(t.identifier(gating.importSpecifierName), []), + t.callExpression(t.identifier(gatingImportedName), []), buildFunctionExpression(compiled), buildFunctionExpression(fnPath.node), ); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts index 1a19d0197665b..704f696b3b86f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts @@ -7,9 +7,19 @@ import {NodePath} from '@babel/core'; import * as t from '@babel/types'; +import {Scope as BabelScope} from '@babel/traverse'; + import {CompilerError, ErrorSeverity} from '../CompilerError'; -import {EnvironmentConfig, ExternalFunction, GeneratedSource} from '../HIR'; -import {getOrInsertDefault} from '../Utils/utils'; +import { + EnvironmentConfig, + GeneratedSource, + NonLocalImportSpecifier, +} from '../HIR'; +import {getOrInsertWith} from '../Utils/utils'; +import {ExternalFunction, isHookName} from '../HIR/Environment'; +import {Err, Ok, Result} from '../Utils/Result'; +import {CompilerReactTarget} from './Options'; +import {getReactCompilerRuntimeModule} from './Program'; export function validateRestrictedImports( path: NodePath, @@ -42,159 +52,226 @@ export function validateRestrictedImports( } } -export function addImportsToProgram( - path: NodePath, - importList: Array, -): void { - const identifiers: Set = new Set(); - const sortedImports: Map> = new Map(); - for (const {importSpecifierName, source} of importList) { - /* - * Codegen currently does not rename import specifiers, so we do additional - * validation here +export class ProgramContext { + /* Program and environment context */ + scope: BabelScope; + reactRuntimeModule: string; + hookPattern: string | null; + + // known generated or referenced identifiers in the program + knownReferencedNames: Set = new Set(); + // generated imports + imports: Map> = new Map(); + + constructor( + program: NodePath, + reactRuntimeModule: CompilerReactTarget, + hookPattern: string | null, + ) { + this.hookPattern = hookPattern; + this.scope = program.scope; + this.reactRuntimeModule = getReactCompilerRuntimeModule(reactRuntimeModule); + } + + isHookName(name: string): boolean { + if (this.hookPattern == null) { + return isHookName(name); + } else { + const match = new RegExp(this.hookPattern).exec(name); + return ( + match != null && typeof match[1] === 'string' && isHookName(match[1]) + ); + } + } + + hasReference(name: string): boolean { + return ( + this.knownReferencedNames.has(name) || + this.scope.hasBinding(name) || + this.scope.hasGlobal(name) || + this.scope.hasReference(name) + ); + } + + newUid(name: string): string { + /** + * Don't call babel's generateUid for known hook imports, as + * InferTypes might eventually type `HookKind` based on callee naming + * convention and `_useFoo` is not named as a hook. + * + * Local uid generation is susceptible to check-before-use bugs since we're + * checking for naming conflicts / references long before we actually insert + * the import. (see similar logic in HIRBuilder:resolveBinding) */ - CompilerError.invariant(identifiers.has(importSpecifierName) === false, { - reason: `Encountered conflicting import specifier for ${importSpecifierName} in Forget config.`, - description: null, - loc: GeneratedSource, - suggestions: null, - }); - CompilerError.invariant( - path.scope.hasBinding(importSpecifierName) === false, + let uid; + if (this.isHookName(name)) { + uid = name; + let i = 0; + while (this.hasReference(uid)) { + this.knownReferencedNames.add(uid); + uid = `${name}_${i++}`; + } + } else if (!this.hasReference(name)) { + uid = name; + } else { + uid = this.scope.generateUid(name); + } + this.knownReferencedNames.add(uid); + return uid; + } + + addMemoCacheImport(): NonLocalImportSpecifier { + return this.addImportSpecifier( { - reason: `Encountered conflicting import specifiers for ${importSpecifierName} in generated program.`, - description: null, - loc: GeneratedSource, - suggestions: null, + source: this.reactRuntimeModule, + importSpecifierName: 'c', }, + '_c', ); - identifiers.add(importSpecifierName); - - const importSpecifierNameList = getOrInsertDefault( - sortedImports, - source, - [], - ); - importSpecifierNameList.push(importSpecifierName); } - const stmts: Array = []; - for (const [source, importSpecifierNameList] of sortedImports) { - const importSpecifiers = importSpecifierNameList.map(name => { - const id = t.identifier(name); - return t.importSpecifier(id, id); - }); + /** + * + * @param externalFunction + * @param nameHint if defined, will be used as the name of the import specifier + * @returns + */ + addImportSpecifier( + {source: module, importSpecifierName: specifier}: ExternalFunction, + nameHint?: string, + ): NonLocalImportSpecifier { + const maybeBinding = this.imports.get(module)?.get(specifier); + if (maybeBinding != null) { + return {...maybeBinding}; + } - stmts.push(t.importDeclaration(importSpecifiers, t.stringLiteral(source))); + const binding: NonLocalImportSpecifier = { + kind: 'ImportSpecifier', + name: this.newUid(nameHint ?? specifier), + module, + imported: specifier, + }; + getOrInsertWith(this.imports, module, () => new Map()).set(specifier, { + ...binding, + }); + return binding; } - path.unshiftContainer('body', stmts); -} - -/* - * Matches `import { ... } from ;` - * but not `import * as React from ;` - */ -function isNonNamespacedImport( - importDeclPath: NodePath, - moduleName: string, -): boolean { - return ( - importDeclPath.get('source').node.value === moduleName && - importDeclPath - .get('specifiers') - .every(specifier => specifier.isImportSpecifier()) && - importDeclPath.node.importKind !== 'type' && - importDeclPath.node.importKind !== 'typeof' - ); -} -function hasExistingNonNamespacedImportOfModule( - program: NodePath, - moduleName: string, -): boolean { - let hasExistingImport = false; - program.traverse({ - ImportDeclaration(importDeclPath) { - if (isNonNamespacedImport(importDeclPath, moduleName)) { - hasExistingImport = true; - } - }, - }); + addNewReference(name: string): void { + this.knownReferencedNames.add(name); + } - return hasExistingImport; + assertGlobalBinding( + name: string, + localScope?: BabelScope, + ): Result { + const scope = localScope ?? this.scope; + if (!scope.hasReference(name) && !scope.hasBinding(name)) { + return Ok(undefined); + } + const error = new CompilerError(); + error.push({ + severity: ErrorSeverity.Todo, + reason: 'Encountered conflicting global in generated program', + description: `Conflict from local binding ${name}`, + loc: scope.getBinding(name)?.path.node.loc ?? null, + suggestions: null, + }); + return Err(error); + } } -/* - * If an existing import of React exists (ie `import { ... } from ''`), inject useMemoCache - * into the list of destructured variables. - */ -function addMemoCacheFunctionSpecifierToExistingImport( +function getExistingImports( program: NodePath, - moduleName: string, - identifierName: string, -): boolean { - let didInsertUseMemoCache = false; +): Map> { + const existingImports = new Map>(); program.traverse({ - ImportDeclaration(importDeclPath) { - if ( - !didInsertUseMemoCache && - isNonNamespacedImport(importDeclPath, moduleName) - ) { - importDeclPath.pushContainer( - 'specifiers', - t.importSpecifier(t.identifier(identifierName), t.identifier('c')), - ); - didInsertUseMemoCache = true; + ImportDeclaration(path) { + if (isNonNamespacedImport(path)) { + existingImports.set(path.node.source.value, path); } }, }); - return didInsertUseMemoCache; + return existingImports; } -export function updateMemoCacheFunctionImport( - program: NodePath, - moduleName: string, - useMemoCacheIdentifier: string, +export function addImportsToProgram( + path: NodePath, + programContext: ProgramContext, ): void { - /* - * If there isn't already an import of * as React, insert it so useMemoCache doesn't - * throw - */ - const hasExistingImport = hasExistingNonNamespacedImportOfModule( - program, - moduleName, + const existingImports = getExistingImports(path); + const stmts: Array = []; + const sortedModules = [...programContext.imports.entries()].sort(([a], [b]) => + a.localeCompare(b), ); + for (const [moduleName, importsMap] of sortedModules) { + for (const [specifierName, loweredImport] of importsMap) { + /** + * Assert that the import identifier hasn't already be declared in the program. + * Note: we use getBinding here since `Scope.hasBinding` pessimistically returns true + * for all allocated uids (from `Scope.getUid`) + */ + CompilerError.invariant( + path.scope.getBinding(loweredImport.name) == null, + { + reason: + 'Encountered conflicting import specifiers in generated program', + description: `Conflict from import ${loweredImport.module}:(${loweredImport.imported} as ${loweredImport.name}).`, + loc: GeneratedSource, + suggestions: null, + }, + ); + CompilerError.invariant( + loweredImport.module === moduleName && + loweredImport.imported === specifierName, + { + reason: + 'Found inconsistent import specifier. This is an internal bug.', + description: `Expected import ${moduleName}:${specifierName} but found ${loweredImport.module}:${loweredImport.imported}`, + loc: GeneratedSource, + }, + ); + } + const sortedImport: Array = [ + ...importsMap.values(), + ].sort(({imported: a}, {imported: b}) => a.localeCompare(b)); + const importSpecifiers = sortedImport.map(specifier => { + return t.importSpecifier( + t.identifier(specifier.name), + t.identifier(specifier.imported), + ); + }); - if (hasExistingImport) { - const didUpdateImport = addMemoCacheFunctionSpecifierToExistingImport( - program, - moduleName, - useMemoCacheIdentifier, - ); - if (!didUpdateImport) { - throw new Error( - `Expected an ImportDeclaration of \`${moduleName}\` in order to update ImportSpecifiers with useMemoCache`, + /** + * If an existing import of this module exists (ie `import { ... } from + * ''`), inject new imported specifiers into the list of + * destructured variables. + */ + const maybeExistingImports = existingImports.get(moduleName); + if (maybeExistingImports != null) { + maybeExistingImports.pushContainer('specifiers', importSpecifiers); + } else { + stmts.push( + t.importDeclaration(importSpecifiers, t.stringLiteral(moduleName)), ); } - } else { - addMemoCacheFunctionImportDeclaration( - program, - moduleName, - useMemoCacheIdentifier, - ); } + path.unshiftContainer('body', stmts); } -function addMemoCacheFunctionImportDeclaration( - program: NodePath, - moduleName: string, - localName: string, -): void { - program.unshiftContainer( - 'body', - t.importDeclaration( - [t.importSpecifier(t.identifier(localName), t.identifier('c'))], - t.stringLiteral(moduleName), - ), +/* + * Matches `import { ... } from ;` + * but not `import * as React from ;` + * `import type { Foo } from ;` + */ +function isNonNamespacedImport( + importDeclPath: NodePath, +): boolean { + return ( + importDeclPath + .get('specifiers') + .every(specifier => specifier.isImportSpecifier()) && + importDeclPath.node.importKind !== 'type' && + importDeclPath.node.importKind !== 'typeof' ); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index ed41ce2eedc46..61f57f68cfeb9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -8,7 +8,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; import prettyFormat from 'pretty-format'; -import {Logger} from '.'; +import {Logger, ProgramContext} from '.'; import { HIRFunction, ReactiveFunction, @@ -117,7 +117,7 @@ function run( config: EnvironmentConfig, fnType: ReactFunctionType, mode: CompilerMode, - useMemoCacheIdentifier: string, + programContext: ProgramContext, logger: Logger | null, filename: string | null, code: string | null, @@ -132,7 +132,7 @@ function run( logger, filename, code, - useMemoCacheIdentifier, + programContext, ); env.logger?.debugLogIRs?.({ kind: 'debug', @@ -552,7 +552,7 @@ export function compileFn( config: EnvironmentConfig, fnType: ReactFunctionType, mode: CompilerMode, - useMemoCacheIdentifier: string, + programContext: ProgramContext, logger: Logger | null, filename: string | null, code: string | null, @@ -562,7 +562,7 @@ export function compileFn( config, fnType, mode, - useMemoCacheIdentifier, + programContext, logger, filename, code, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index ec1a2d2935fc6..c00c672b2cbd6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -12,11 +12,7 @@ import { CompilerErrorDetail, ErrorSeverity, } from '../CompilerError'; -import { - EnvironmentConfig, - ExternalFunction, - ReactFunctionType, -} from '../HIR/Environment'; +import {EnvironmentConfig, ReactFunctionType} from '../HIR/Environment'; import {CodegenFunction} from '../ReactiveScopes'; import {isComponentDeclaration} from '../Utils/ComponentDeclaration'; import {isHookDeclaration} from '../Utils/HookDeclaration'; @@ -24,10 +20,10 @@ import {assertExhaustive} from '../Utils/utils'; import {insertGatedFunctionDeclaration} from './Gating'; import { addImportsToProgram, - updateMemoCacheFunctionImport, + ProgramContext, validateRestrictedImports, } from './Imports'; -import {PluginOptions} from './Options'; +import {CompilerReactTarget, PluginOptions} from './Options'; import {compileFn} from './Pipeline'; import { filterSuppressionsThatAffectFunction, @@ -299,8 +295,12 @@ export function compileProgram( handleError(restrictedImportsErr, pass, null); return null; } - const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c'); + const programContext = new ProgramContext( + program, + pass.opts.target, + environment.hookPattern, + ); /* * Record lint errors and critical errors as depending on Forget's config, * we may still need to run Forget's analysis on every function (even if we @@ -410,7 +410,7 @@ export function compileProgram( environment, fnType, 'all_features', - useMemoCacheIdentifier.name, + programContext, pass.opts.logger, pass.filename, pass.code, @@ -445,7 +445,7 @@ export function compileProgram( environment, fnType, 'no_inferred_memo', - useMemoCacheIdentifier.name, + programContext, pass.opts.logger, pass.filename, pass.code, @@ -453,7 +453,7 @@ export function compileProgram( }; if ( !compileResult.compiledFn.hasFireRewrite && - !compileResult.compiledFn.hasLoweredContextAccess + !compileResult.compiledFn.hasInferredEffect ) { return null; } @@ -554,79 +554,29 @@ export function compileProgram( if (moduleScopeOptOutDirectives.length > 0) { return null; } - let gating: null | { - gatingFn: ExternalFunction; - referencedBeforeDeclared: Set; - } = null; - if (pass.opts.gating != null) { - gating = { - gatingFn: pass.opts.gating, - referencedBeforeDeclared: - getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns), - }; - } - - const hasLoweredContextAccess = compiledFns.some( - c => c.compiledFn.hasLoweredContextAccess, - ); - const externalFunctions: Array = []; - try { - // TODO: check for duplicate import specifiers - if (gating != null) { - externalFunctions.push(gating.gatingFn); - } - - const lowerContextAccess = environment.lowerContextAccess; - if (lowerContextAccess && hasLoweredContextAccess) { - externalFunctions.push(lowerContextAccess); - } - - const enableEmitInstrumentForget = environment.enableEmitInstrumentForget; - if (enableEmitInstrumentForget != null) { - externalFunctions.push(enableEmitInstrumentForget.fn); - if (enableEmitInstrumentForget.gating != null) { - externalFunctions.push(enableEmitInstrumentForget.gating); - } - } - - if (environment.enableEmitFreeze != null) { - externalFunctions.push(environment.enableEmitFreeze); - } - - if (environment.enableEmitHookGuards != null) { - externalFunctions.push(environment.enableEmitHookGuards); - } - - if (environment.enableChangeDetectionForDebugging != null) { - externalFunctions.push(environment.enableChangeDetectionForDebugging); - } - - const hasFireRewrite = compiledFns.some(c => c.compiledFn.hasFireRewrite); - if (environment.enableFire && hasFireRewrite) { - externalFunctions.push({ - source: getReactCompilerRuntimeModule(pass.opts), - importSpecifierName: 'useFire', - }); - } - } catch (err) { - handleError(err, pass, null); - return null; - } - /* * Only insert Forget-ified functions if we have not encountered a critical * error elsewhere in the file, regardless of bailout mode. */ + const referencedBeforeDeclared = + pass.opts.gating != null + ? getFunctionReferencedBeforeDeclarationAtTopLevel(program, compiledFns) + : null; for (const result of compiledFns) { const {kind, originalFn, compiledFn} = result; const transformedFn = createNewFunctionNode(originalFn, compiledFn); - if (gating != null && kind === 'original') { + if (referencedBeforeDeclared != null && kind === 'original') { + CompilerError.invariant(pass.opts.gating != null, { + reason: "Expected 'gating' import to be present", + loc: null, + }); insertGatedFunctionDeclaration( originalFn, transformedFn, - gating.gatingFn, - gating.referencedBeforeDeclared.has(result), + programContext, + pass.opts.gating, + referencedBeforeDeclared.has(result), ); } else { originalFn.replaceWith(transformedFn); @@ -635,22 +585,7 @@ export function compileProgram( // Forget compiled the component, we need to update existing imports of useMemoCache if (compiledFns.length > 0) { - let needsMemoCacheFunctionImport = false; - for (const fn of compiledFns) { - if (fn.compiledFn.memoSlotsUsed > 0) { - needsMemoCacheFunctionImport = true; - break; - } - } - - if (needsMemoCacheFunctionImport) { - updateMemoCacheFunctionImport( - program, - getReactCompilerRuntimeModule(pass.opts), - useMemoCacheIdentifier.name, - ); - } - addImportsToProgram(program, externalFunctions); + addImportsToProgram(program, programContext); } return {retryErrors}; } @@ -683,7 +618,7 @@ function shouldSkipCompilation( if ( hasMemoCacheFunctionImport( program, - getReactCompilerRuntimeModule(pass.opts), + getReactCompilerRuntimeModule(pass.opts.target), ) ) { return true; @@ -1177,16 +1112,18 @@ function getFunctionReferencedBeforeDeclarationAtTopLevel( return referencedBeforeDeclaration; } -function getReactCompilerRuntimeModule(opts: PluginOptions): string { - if (opts.target === '19') { +export function getReactCompilerRuntimeModule( + target: CompilerReactTarget, +): string { + if (target === '19') { return 'react/compiler-runtime'; // from react namespace - } else if (opts.target === '17' || opts.target === '18') { + } else if (target === '17' || target === '18') { return 'react-compiler-runtime'; // npm package } else { CompilerError.invariant( - opts.target != null && - opts.target.kind === 'donotuse_meta_internal' && - typeof opts.target.runtimeModule === 'string', + target != null && + target.kind === 'donotuse_meta_internal' && + typeof target.runtimeModule === 'string', { reason: 'Expected target to already be validated', description: null, @@ -1194,6 +1131,6 @@ function getReactCompilerRuntimeModule(opts: PluginOptions): string { suggestions: null, }, ); - return opts.target.runtimeModule; + return target.runtimeModule; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index a8f5b15dbd06d..2594ac31c6b50 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -15,6 +15,7 @@ import { PanicThresholdOptions, parsePluginOptions, PluginOptions, + ProgramContext, } from '../Entrypoint'; import {Err, Ok, Result} from '../Utils/Result'; import { @@ -84,6 +85,8 @@ export const InstrumentationSchema = z ); export type ExternalFunction = z.infer; +export const USE_FIRE_FUNCTION_NAME = 'useFire'; +export const EMIT_FREEZE_GLOBAL_GATING = '__DEV__'; export const MacroMethodSchema = z.union([ z.object({type: z.literal('wildcard')}), @@ -846,9 +849,9 @@ export class Environment { config: EnvironmentConfig; fnType: ReactFunctionType; compilerMode: CompilerMode; - useMemoCacheIdentifier: string; - hasLoweredContextAccess: boolean; + programContext: ProgramContext; hasFireRewrite: boolean; + hasInferredEffect: boolean; #contextIdentifiers: Set; #hoistedIdentifiers: Set; @@ -862,7 +865,7 @@ export class Environment { logger: Logger | null, filename: string | null, code: string | null, - useMemoCacheIdentifier: string, + programContext: ProgramContext, ) { this.#scope = scope; this.fnType = fnType; @@ -871,11 +874,11 @@ export class Environment { this.filename = filename; this.code = code; this.logger = logger; - this.useMemoCacheIdentifier = useMemoCacheIdentifier; + this.programContext = programContext; this.#shapes = new Map(DEFAULT_SHAPES); this.#globals = new Map(DEFAULT_GLOBALS); - this.hasLoweredContextAccess = false; this.hasFireRewrite = false; + this.hasInferredEffect = false; if ( config.disableMemoizationForDebugging && @@ -937,6 +940,10 @@ export class Environment { return makeScopeId(this.#nextScope++); } + get scope(): BabelScope { + return this.#scope; + } + logErrors(errors: Result): void { if (errors.isOk() || this.logger == null) { return; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index f58cdfbb081b2..3a8cb89ca0fca 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -1167,18 +1167,21 @@ export type VariableBinding = // bindings declard outside the current component/hook | NonLocalBinding; +// `import {bar as baz} from 'foo'`: name=baz, module=foo, imported=bar +export type NonLocalImportSpecifier = { + kind: 'ImportSpecifier'; + name: string; + module: string; + imported: string; +}; + export type NonLocalBinding = // `import Foo from 'foo'`: name=Foo, module=foo | {kind: 'ImportDefault'; name: string; module: string} // `import * as Foo from 'foo'`: name=Foo, module=foo | {kind: 'ImportNamespace'; name: string; module: string} - // `import {bar as baz} from 'foo'`: name=baz, module=foo, imported=bar - | { - kind: 'ImportSpecifier'; - name: string; - module: string; - imported: string; - } + // `import {bar as baz} from 'foo'` + | NonLocalImportSpecifier // let, const, function, etc declared in the module but outside the current component/hook | {kind: 'ModuleLocal'; name: string} // an unresolved binding diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts index 9202f2145f27e..44dd34b7d6cae 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts @@ -331,6 +331,7 @@ export default class HIRBuilder { type: makeType(), loc: node.loc ?? GeneratedSource, }; + this.#env.programContext.addNewReference(name); this.#bindings.set(name, {node, identifier}); return identifier; } else if (mapping.node === node) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts index 4d295aad060f9..85cb0236659e7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts @@ -249,6 +249,7 @@ export function inferEffectDependencies(fn: HIRFunction): void { // Renumber instructions and fix scope ranges markInstructionIds(fn.body); fixScopeAndIdentifierRanges(fn.body); + fn.env.hasInferredEffect = true; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts index b636c7b1718cc..834f60195af29 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts @@ -18,6 +18,7 @@ import { Instruction, LoadGlobal, LoadLocal, + NonLocalImportSpecifier, Place, PropertyLoad, isUseContextHookType, @@ -35,7 +36,7 @@ import {inferTypes} from '../TypeInference'; export function lowerContextAccess( fn: HIRFunction, - loweredContextCallee: ExternalFunction, + loweredContextCalleeConfig: ExternalFunction, ): void { const contextAccess: Map = new Map(); const contextKeys: Map> = new Map(); @@ -79,6 +80,8 @@ export function lowerContextAccess( } } + let importLoweredContextCallee: NonLocalImportSpecifier | null = null; + if (contextAccess.size > 0 && contextKeys.size > 0) { for (const [, block] of fn.body.blocks) { let nextInstructions: Array | null = null; @@ -91,9 +94,13 @@ export function lowerContextAccess( isUseContextHookType(value.callee.identifier) && contextKeys.has(lvalue.identifier.id) ) { + importLoweredContextCallee ??= + fn.env.programContext.addImportSpecifier( + loweredContextCalleeConfig, + ); const loweredContextCalleeInstr = emitLoadLoweredContextCallee( fn.env, - loweredContextCallee, + importLoweredContextCallee, ); if (nextInstructions === null) { @@ -122,21 +129,16 @@ export function lowerContextAccess( } markInstructionIds(fn.body); inferTypes(fn); - fn.env.hasLoweredContextAccess = true; } } function emitLoadLoweredContextCallee( env: Environment, - loweredContextCallee: ExternalFunction, + importedLowerContextCallee: NonLocalImportSpecifier, ): Instruction { const loadGlobal: LoadGlobal = { kind: 'LoadGlobal', - binding: { - kind: 'ImportNamespace', - module: loweredContextCallee.source, - name: loweredContextCallee.importSpecifierName, - }, + binding: {...importedLowerContextCallee}, loc: GeneratedSource, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts index a6b94075cce26..d35c4d77362db 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts @@ -196,7 +196,7 @@ function process( return null; } - const props = collectProps(jsx); + const props = collectProps(fn.env, jsx); if (!props) return null; const outlinedTag = fn.env.generateGloballyUniqueIdentifierName(null).value; @@ -217,6 +217,7 @@ type OutlinedJsxAttribute = { }; function collectProps( + env: Environment, instructions: Array, ): Array | null { let id = 1; @@ -227,6 +228,7 @@ function collectProps( newName = `${oldName}${id++}`; } seen.add(newName); + env.programContext.addNewReference(newName); return newName; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index ce535a9b38667..b90e4e417c6a1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -52,7 +52,8 @@ import {assertExhaustive} from '../Utils/utils'; import {buildReactiveFunction} from './BuildReactiveFunction'; import {SINGLE_CHILD_FBT_TAGS} from './MemoizeFbtAndMacroOperandsInSameScope'; import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors'; -import {ReactFunctionType} from '../HIR/Environment'; +import {EMIT_FREEZE_GLOBAL_GATING, ReactFunctionType} from '../HIR/Environment'; +import {ProgramContext} from '../Entrypoint'; export const MEMO_CACHE_SENTINEL = 'react.memo_cache_sentinel'; export const EARLY_RETURN_SENTINEL = 'react.early_return_sentinel'; @@ -100,9 +101,9 @@ export type CodegenFunction = { }>; /** - * This is true if the compiler has the lowered useContext calls. + * This is true if the compiler has compiled inferred effect dependencies */ - hasLoweredContextAccess: boolean; + hasInferredEffect: boolean; /** * This is true if the compiler has compiled a fire to a useFire call @@ -160,6 +161,7 @@ export function codegenFunction( compiled.body = t.blockStatement([ createHookGuard( hookGuard, + fn.env.programContext, compiled.body.body, GuardKind.PushHookGuard, GuardKind.PopHookGuard, @@ -170,13 +172,15 @@ export function codegenFunction( const cacheCount = compiled.memoSlotsUsed; if (cacheCount !== 0) { const preface: Array = []; + const useMemoCacheIdentifier = + fn.env.programContext.addMemoCacheImport().name; // The import declaration for `useMemoCache` is inserted in the Babel plugin preface.push( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(cx.synthesizeName('$')), - t.callExpression(t.identifier(fn.env.useMemoCacheIdentifier), [ + t.callExpression(t.identifier(useMemoCacheIdentifier), [ t.numericLiteral(cacheCount), ]), ), @@ -259,34 +263,54 @@ export function codegenFunction( * Technically, this is a conditional hook call. However, we expect * __DEV__ and gating identifier to be runtime constants */ - let gating: t.Expression; - if ( - emitInstrumentForget.gating != null && + const gating = + emitInstrumentForget.gating != null + ? t.identifier( + fn.env.programContext.addImportSpecifier( + emitInstrumentForget.gating, + ).name, + ) + : null; + + const globalGating = emitInstrumentForget.globalGating != null - ) { - gating = t.logicalExpression( - '&&', - t.identifier(emitInstrumentForget.globalGating), - t.identifier(emitInstrumentForget.gating.importSpecifierName), + ? t.identifier(emitInstrumentForget.globalGating) + : null; + + if (emitInstrumentForget.globalGating != null) { + const assertResult = fn.env.programContext.assertGlobalBinding( + emitInstrumentForget.globalGating, ); - } else if (emitInstrumentForget.gating != null) { - gating = t.identifier(emitInstrumentForget.gating.importSpecifierName); + if (assertResult.isErr()) { + return assertResult; + } + } + + let ifTest: t.Expression; + if (gating != null && globalGating != null) { + ifTest = t.logicalExpression('&&', globalGating, gating); + } else if (gating != null) { + ifTest = gating; } else { - CompilerError.invariant(emitInstrumentForget.globalGating != null, { + CompilerError.invariant(globalGating != null, { reason: 'Bad config not caught! Expected at least one of gating or globalGating', loc: null, suggestions: null, }); - gating = t.identifier(emitInstrumentForget.globalGating); + ifTest = globalGating; } + + const instrumentFnIdentifier = fn.env.programContext.addImportSpecifier( + emitInstrumentForget.fn, + ).name; const test: t.IfStatement = t.ifStatement( - gating, + ifTest, t.expressionStatement( - t.callExpression( - t.identifier(emitInstrumentForget.fn.importSpecifierName), - [t.stringLiteral(fn.id), t.stringLiteral(fn.env.filename ?? '')], - ), + t.callExpression(t.identifier(instrumentFnIdentifier), [ + t.stringLiteral(fn.id), + t.stringLiteral(fn.env.filename ?? ''), + ]), ), ); compiled.body.body.unshift(test); @@ -363,8 +387,8 @@ function codegenReactiveFunction( prunedMemoBlocks: countMemoBlockVisitor.prunedMemoBlocks, prunedMemoValues: countMemoBlockVisitor.prunedMemoValues, outlined: [], - hasLoweredContextAccess: fn.env.hasLoweredContextAccess, hasFireRewrite: fn.env.hasFireRewrite, + hasInferredEffect: fn.env.hasInferredEffect, }); } @@ -553,13 +577,18 @@ function codegenBlockNoReset( function wrapCacheDep(cx: Context, value: t.Expression): t.Expression { if (cx.env.config.enableEmitFreeze != null && cx.env.isInferredMemoEnabled) { - // The import declaration for emitFreeze is inserted in the Babel plugin + const emitFreezeIdentifier = cx.env.programContext.addImportSpecifier( + cx.env.config.enableEmitFreeze, + ).name; + cx.env.programContext + .assertGlobalBinding(EMIT_FREEZE_GLOBAL_GATING, cx.env.scope) + .unwrap(); return t.conditionalExpression( - t.identifier('__DEV__'), - t.callExpression( - t.identifier(cx.env.config.enableEmitFreeze.importSpecifierName), - [value, t.stringLiteral(cx.fnName)], - ), + t.identifier(EMIT_FREEZE_GLOBAL_GATING), + t.callExpression(t.identifier(emitFreezeIdentifier), [ + value, + t.stringLiteral(cx.fnName), + ]), value, ); } else { @@ -713,16 +742,14 @@ function codegenReactiveScope( let computationBlock = codegenBlock(cx, block); let memoStatement; - if ( - cx.env.config.enableChangeDetectionForDebugging != null && - changeExpressions.length > 0 - ) { + const detectionFunction = cx.env.config.enableChangeDetectionForDebugging; + if (detectionFunction != null && changeExpressions.length > 0) { const loc = typeof scope.loc === 'symbol' ? 'unknown location' : `(${scope.loc.start.line}:${scope.loc.end.line})`; - const detectionFunction = - cx.env.config.enableChangeDetectionForDebugging.importSpecifierName; + const importedDetectionFunctionIdentifier = + cx.env.programContext.addImportSpecifier(detectionFunction).name; const cacheLoadOldValueStatements: Array = []; const changeDetectionStatements: Array = []; const idempotenceDetectionStatements: Array = []; @@ -744,7 +771,7 @@ function codegenReactiveScope( ); changeDetectionStatements.push( t.expressionStatement( - t.callExpression(t.identifier(detectionFunction), [ + t.callExpression(t.identifier(importedDetectionFunctionIdentifier), [ t.identifier(loadName), t.cloneNode(name, true), t.stringLiteral(name.name), @@ -756,7 +783,7 @@ function codegenReactiveScope( ); idempotenceDetectionStatements.push( t.expressionStatement( - t.callExpression(t.identifier(detectionFunction), [ + t.callExpression(t.identifier(importedDetectionFunctionIdentifier), [ t.cloneNode(slot, true), t.cloneNode(name, true), t.stringLiteral(name.name), @@ -1518,15 +1545,15 @@ const createStringLiteral = withLoc(t.stringLiteral); function createHookGuard( guard: ExternalFunction, + context: ProgramContext, stmts: Array, before: GuardKind, after: GuardKind, ): t.TryStatement { + const guardFnName = context.addImportSpecifier(guard).name; function createHookGuardImpl(kind: number): t.ExpressionStatement { return t.expressionStatement( - t.callExpression(t.identifier(guard.importSpecifierName), [ - t.numericLiteral(kind), - ]), + t.callExpression(t.identifier(guardFnName), [t.numericLiteral(kind)]), ); } @@ -1576,6 +1603,7 @@ function createCallExpression( t.blockStatement([ createHookGuard( hookGuard, + env.programContext, [t.returnStatement(callExpr)], GuardKind.AllowHook, GuardKind.DisallowHook, diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts index f84965b92e6ee..5b39055e7d61a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/RenameVariables.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {ProgramContext} from '..'; import {CompilerError} from '../CompilerError'; import { DeclarationId, @@ -47,7 +48,7 @@ import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors'; */ export function renameVariables(fn: ReactiveFunction): Set { const globals = collectReferencedGlobals(fn); - const scopes = new Scopes(globals); + const scopes = new Scopes(globals, fn.env.programContext); renameVariablesImpl(fn, new Visitor(), scopes); return new Set([...scopes.names, ...globals]); } @@ -124,10 +125,12 @@ class Scopes { #seen: Map = new Map(); #stack: Array> = [new Map()]; #globals: Set; + #programContext: ProgramContext; names: Set = new Set(); - constructor(globals: Set) { + constructor(globals: Set, programContext: ProgramContext) { this.#globals = globals; + this.#programContext = programContext; } visit(identifier: Identifier): void { @@ -156,6 +159,7 @@ class Scopes { name = `${originalName.value}$${id++}`; } } + this.#programContext.addNewReference(name); const identifierName = makeIdentifierName(name); identifier.name = identifierName; this.#seen.set(identifier.declarationId, identifierName); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts b/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts index a480b5d7c78de..943b6b8eca2b2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Transform/TransformFire.ts @@ -28,6 +28,7 @@ import { isUseEffectHookType, LoadLocal, makeInstructionId, + NonLocalImportSpecifier, Place, promoteTemporary, } from '../HIR'; @@ -36,6 +37,7 @@ import {getOrInsertWith} from '../Utils/utils'; import {BuiltInFireId, DefaultNonmutatingHook} from '../HIR/ObjectShape'; import {eachInstructionOperand} from '../HIR/visitors'; import {printSourceLocationLine} from '../HIR/PrintHIR'; +import {USE_FIRE_FUNCTION_NAME} from '../HIR/Environment'; /* * TODO(jmbrown): @@ -56,6 +58,7 @@ export function transformFire(fn: HIRFunction): void { } function replaceFireFunctions(fn: HIRFunction, context: Context): void { + let importedUseFire: NonLocalImportSpecifier | null = null; let hasRewrite = false; for (const [, block] of fn.body.blocks) { const rewriteInstrs = new Map>(); @@ -87,7 +90,15 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void { ] of capturedCallees.entries()) { if (!context.hasCalleeWithInsertedFire(fireCalleePlace)) { context.addCalleeWithInsertedFire(fireCalleePlace); - const loadUseFireInstr = makeLoadUseFireInstruction(fn.env); + + importedUseFire ??= fn.env.programContext.addImportSpecifier({ + source: fn.env.programContext.reactRuntimeModule, + importSpecifierName: USE_FIRE_FUNCTION_NAME, + }); + const loadUseFireInstr = makeLoadUseFireInstruction( + fn.env, + importedUseFire, + ); const loadFireCalleeInstr = makeLoadFireCalleeInstruction( fn.env, fireCalleeInfo.capturedCalleeIdentifier, @@ -404,18 +415,16 @@ function ensureNoMoreFireUses(fn: HIRFunction, context: Context): void { } } -function makeLoadUseFireInstruction(env: Environment): Instruction { +function makeLoadUseFireInstruction( + env: Environment, + importedLoadUseFire: NonLocalImportSpecifier, +): Instruction { const useFirePlace = createTemporaryPlace(env, GeneratedSource); useFirePlace.effect = Effect.Read; useFirePlace.identifier.type = DefaultNonmutatingHook; const instrValue: InstructionValue = { kind: 'LoadGlobal', - binding: { - kind: 'ImportSpecifier', - name: 'useFire', - module: 'react', - imported: 'useFire', - }, + binding: {...importedLoadUseFire}, loc: GeneratedSource, }; return { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md new file mode 100644 index 0000000000000..5bb87a2b032c2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.expect.md @@ -0,0 +1,73 @@ + +## Input + +```javascript +import * as React from 'react'; +import {someImport} from 'react/compiler-runtime'; +import {calculateExpensiveNumber} from 'shared-runtime'; + +function Component(props) { + const [x] = React.useState(0); + const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]); + + return ( +
+ {expensiveNumber} + {`${someImport}`} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], +}; + +``` + +## Code + +```javascript +import * as React from "react"; +import { someImport, c as _c } from "react/compiler-runtime"; +import { calculateExpensiveNumber } from "shared-runtime"; + +function Component(props) { + const $ = _c(4); + const [x] = React.useState(0); + let t0; + let t1; + if ($[0] !== x) { + t1 = calculateExpensiveNumber(x); + $[0] = x; + $[1] = t1; + } else { + t1 = $[1]; + } + t0 = t1; + const expensiveNumber = t0; + let t2; + if ($[2] !== expensiveNumber) { + t2 = ( +
+ {expensiveNumber} + {`${someImport}`} +
+ ); + $[2] = expensiveNumber; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], +}; + +``` + +### Eval output +(kind: ok)
0undefined
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js new file mode 100644 index 0000000000000..80a2006dd12b1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/babel-existing-react-runtime-import.js @@ -0,0 +1,20 @@ +import * as React from 'react'; +import {someImport} from 'react/compiler-runtime'; +import {calculateExpensiveNumber} from 'shared-runtime'; + +function Component(props) { + const [x] = React.useState(0); + const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]); + + return ( +
+ {expensiveNumber} + {`${someImport}`} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md index 161b42dc6d7d3..6ec8520f9ec9b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md @@ -14,9 +14,9 @@ function useFoo(props) { ```javascript import { - useRenderCounter, - shouldInstrument, makeReadOnly, + shouldInstrument, + useRenderCounter, } from "react-compiler-runtime"; import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @enableEmitInstrumentForget diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md index 319de18794f2d..8c2f0b94ef888 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md @@ -23,7 +23,7 @@ function Foo(props) { ## Code ```javascript -import { useRenderCounter, shouldInstrument } from "react-compiler-runtime"; +import { shouldInstrument, useRenderCounter } from "react-compiler-runtime"; import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget @compilationMode(annotation) function Bar(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md new file mode 100644 index 0000000000000..a2df134b2a866 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.expect.md @@ -0,0 +1,97 @@ + +## Input + +```javascript +// @enableEmitInstrumentForget @compilationMode(annotation) + +import {identity} from 'shared-runtime'; + +function Bar(props) { + 'use forget'; + const shouldInstrument = identity(null); + const _shouldInstrument = identity(null); + const _x2 = () => { + const _shouldInstrument2 = 'hello world'; + return identity({_shouldInstrument2}); + }; + return ( +
+ {props.bar} +
+ ); +} + +function Foo(props) { + 'use forget'; + return {props.bar}; +} + +``` + +## Code + +```javascript +import { + shouldInstrument as _shouldInstrument3, + useRenderCounter, +} from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget @compilationMode(annotation) + +import { identity } from "shared-runtime"; + +function Bar(props) { + "use forget"; + if (DEV && _shouldInstrument3) + useRenderCounter("Bar", "/conflict-codegen-instrument-forget.ts"); + const $ = _c(4); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = identity(null); + $[0] = t0; + } else { + t0 = $[0]; + } + const shouldInstrument = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = identity(null); + $[1] = t1; + } else { + t1 = $[1]; + } + const _shouldInstrument = t1; + let t2; + if ($[2] !== props.bar) { + t2 = ( +
+ {props.bar} +
+ ); + $[2] = props.bar; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +function Foo(props) { + "use forget"; + if (DEV && _shouldInstrument3) + useRenderCounter("Foo", "/conflict-codegen-instrument-forget.ts"); + const $ = _c(2); + let t0; + if ($[0] !== props.bar) { + t0 = {props.bar}; + $[0] = props.bar; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js new file mode 100644 index 0000000000000..88ffefe220d5e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/conflict-codegen-instrument-forget.js @@ -0,0 +1,23 @@ +// @enableEmitInstrumentForget @compilationMode(annotation) + +import {identity} from 'shared-runtime'; + +function Bar(props) { + 'use forget'; + const shouldInstrument = identity(null); + const _shouldInstrument = identity(null); + const _x2 = () => { + const _shouldInstrument2 = 'hello world'; + return identity({_shouldInstrument2}); + }; + return ( +
+ {props.bar} +
+ ); +} + +function Foo(props) { + 'use forget'; + return {props.bar}; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md new file mode 100644 index 0000000000000..9b30c1b8cbbdb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.expect.md @@ -0,0 +1,37 @@ + +## Input + +```javascript +// @enableEmitFreeze @instrumentForget + +let makeReadOnly = 'conflicting identifier'; +function useFoo(props) { + return foo(props.x); +} + +``` + +## Code + +```javascript +import { makeReadOnly as _makeReadOnly } from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @instrumentForget + +let makeReadOnly = "conflicting identifier"; +function useFoo(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.x) { + t0 = foo(props.x); + $[0] = props.x; + $[1] = __DEV__ ? _makeReadOnly(t0, "useFoo") : t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-conflicting-imports.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md new file mode 100644 index 0000000000000..ee18d3d1a2d1a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + return foo(props.x, __DEV__); +} + +``` + +## Code + +```javascript +import { makeReadOnly } from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @instrumentForget +function useFoo(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.x) { + t0 = foo(props.x, __DEV__); + $[0] = props.x; + $[1] = __DEV__ ? makeReadOnly(t0, "useFoo") : t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js new file mode 100644 index 0000000000000..62c313b67d33c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/emit-freeze-nonconflicting-global-reference.js @@ -0,0 +1,4 @@ +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + return foo(props.x, __DEV__); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md deleted file mode 100644 index 2bd0eee1b1fa8..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.codegen-error-on-conflicting-imports.expect.md +++ /dev/null @@ -1,21 +0,0 @@ - -## Input - -```javascript -// @enableEmitFreeze @instrumentForget - -let makeReadOnly = 'conflicting identifier'; -function useFoo(props) { - return foo(props.x); -} - -``` - - -## Error - -``` -Invariant: Encountered conflicting import specifiers for makeReadOnly in generated program. -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md new file mode 100644 index 0000000000000..a54cc98708f1e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + const __DEV__ = 'conflicting global'; + console.log(__DEV__); + return foo(props.x); +} + +``` + + +## Error + +``` + 1 | // @enableEmitFreeze @instrumentForget + 2 | function useFoo(props) { +> 3 | const __DEV__ = 'conflicting global'; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Todo: Encountered conflicting global in generated program. Conflict from local binding __DEV__ (3:3) + 4 | console.log(__DEV__); + 5 | return foo(props.x); + 6 | } +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js new file mode 100644 index 0000000000000..4391ad76e70af --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.emit-freeze-conflicting-global.js @@ -0,0 +1,6 @@ +// @enableEmitFreeze @instrumentForget +function useFoo(props) { + const __DEV__ = 'conflicting global'; + console.log(__DEV__); + return foo(props.x); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md index 659e2c9ff9e48..298b4d59dfcf4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md @@ -18,8 +18,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() ? (t0) => { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md index fe85e38e106a6..6256e5ec90ff4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md @@ -36,9 +36,9 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { useRenderCounter, shouldInstrument } from "react-compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget @compilationMode(annotation) @gating +import { shouldInstrument, useRenderCounter } from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @enableEmitInstrumentForget @compilationMode(annotation) @gating const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md index 25215580c81f0..410b651244605 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md @@ -20,14 +20,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { Stringify } from "shared-runtime"; import * as React from "react"; const Foo = React.forwardRef(Foo_withRef); -const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); -function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { +const isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { const $ = _c(2); let t0; if ($[0] !== ref) { @@ -39,16 +39,15 @@ function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { } return t0; } -function _Foo_withRef_unoptimized( +function Foo_withRef_unoptimized( _$$empty_props_placeholder$$: $ReadOnly<{}>, ref: React.RefSetter, ): React.Node { return ; } function Foo_withRef(arg0, arg1) { - if (_isForgetEnabled_Fixtures_result) - return _Foo_withRef_optimized(arg0, arg1); - else return _Foo_withRef_unoptimized(arg0, arg1); + if (isForgetEnabled_Fixtures_result) return Foo_withRef_optimized(arg0, arg1); + else return Foo_withRef_unoptimized(arg0, arg1); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md new file mode 100644 index 0000000000000..ca6f518032bc4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +// @gating + +export const isForgetEnabled_Fixtures = () => { + 'use no forget'; + return false; +}; + +export function Bar(props) { + 'use forget'; + return
{props.bar}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures as _isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating + +export const isForgetEnabled_Fixtures = () => { + "use no forget"; + return false; +}; + +export const Bar = _isForgetEnabled_Fixtures() + ? function Bar(props) { + "use forget"; + const $ = _c(2); + let t0; + if ($[0] !== props.bar) { + t0 =
{props.bar}
; + $[0] = props.bar; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; + } + : function Bar(props) { + "use forget"; + return
{props.bar}
; + }; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + +``` + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js new file mode 100644 index 0000000000000..3e5757dc91573 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/conflicting-gating-fn.js @@ -0,0 +1,16 @@ +// @gating + +export const isForgetEnabled_Fixtures = () => { + 'use no forget'; + return false; +}; + +export function Bar(props) { + 'use forget'; + return
{props.bar}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md index e056b6fa1c027..9b3633472d6eb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md @@ -18,8 +18,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating const Component = isForgetEnabled_Fixtures() ? function Component() { const $ = _c(1); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md index a8f2a8dc58b21..40791a2454b6d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-nonreferenced-identifier-collision.expect.md @@ -24,8 +24,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { identity, useHook as useRenamed } from "shared-runtime"; const _ = { useHook: isForgetEnabled_Fixtures() ? () => {} : () => {}, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md index 053fff651e540..cc852d2538449 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md @@ -26,8 +26,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating const Component = isForgetEnabled_Fixtures() ? function Component() { const $ = _c(1); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md index 4e4a35c37557a..ae7896e7a5ec5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md @@ -27,8 +27,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md index 95c629aa11f95..4c368a8254d51 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md @@ -34,8 +34,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md index ab31d2939d44d..c3e2e4a042811 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md @@ -27,8 +27,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) export const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md index e68e4840cf526..8ccce56713446 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md @@ -27,8 +27,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(annotation) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(annotation) const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md index d09c98dffd917..9730119c25049 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md @@ -21,14 +21,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { createRef, forwardRef } from "react"; import { Stringify } from "shared-runtime"; const Foo = forwardRef(Foo_withRef); -const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); -function _Foo_withRef_optimized(props, ref) { +const isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function Foo_withRef_optimized(props, ref) { const $ = _c(3); let t0; if ($[0] !== props || $[1] !== ref) { @@ -41,13 +41,12 @@ function _Foo_withRef_optimized(props, ref) { } return t0; } -function _Foo_withRef_unoptimized(props, ref) { +function Foo_withRef_unoptimized(props, ref) { return ; } function Foo_withRef(arg0, arg1) { - if (_isForgetEnabled_Fixtures_result) - return _Foo_withRef_optimized(arg0, arg1); - else return _Foo_withRef_unoptimized(arg0, arg1); + if (isForgetEnabled_Fixtures_result) return Foo_withRef_optimized(arg0, arg1); + else return Foo_withRef_unoptimized(arg0, arg1); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md index 0bbfc96756778..937af490f1dbc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md @@ -22,14 +22,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { memo } from "react"; import { Stringify } from "shared-runtime"; export default memo(Foo); -const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); -function _Foo_optimized(t0) { +const isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function Foo_optimized(t0) { "use memo"; const $ = _c(3); const { prop1, prop2 } = t0; @@ -44,13 +44,13 @@ function _Foo_optimized(t0) { } return t1; } -function _Foo_unoptimized({ prop1, prop2 }) { +function Foo_unoptimized({ prop1, prop2 }) { "use memo"; return ; } function Foo(arg0) { - if (_isForgetEnabled_Fixtures_result) return _Foo_optimized(arg0); - else return _Foo_unoptimized(arg0); + if (isForgetEnabled_Fixtures_result) return Foo_optimized(arg0); + else return Foo_unoptimized(arg0); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md index 26c6e510d1479..f81680970c4bd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md @@ -23,8 +23,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { memo } from "react"; type Props = React.ElementConfig; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md index 4931b87b01765..aff21ee0bd3d6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md @@ -13,8 +13,8 @@ export default React.forwardRef(function notNamedLikeAComponent(props) { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating @compilationMode(infer) +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode(infer) import React from "react"; export default React.forwardRef( isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md index 47b58453ca035..cdf9acc2b65ba 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md @@ -23,8 +23,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import * as React from "react"; let Foo; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md index b3dc5011f9ca5..1300bc8982d79 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md @@ -19,8 +19,8 @@ export default props => ( ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md index ea2a1c154129d..c8905939391b6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md @@ -24,8 +24,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md index cc5195edb2224..74be7ac5666a1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md @@ -26,8 +26,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import { Stringify } from "shared-runtime"; const ErrorView = isForgetEnabled_Fixtures() diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md index 5f18d98491ff7..267da62b89abf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md @@ -31,8 +31,8 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating import * as React from "react"; /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md new file mode 100644 index 0000000000000..c6e179a6e7fe4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.expect.md @@ -0,0 +1,66 @@ + +## Input + +```javascript +// @lowerContextAccess @enableEmitHookGuards +function App() { + const {foo} = useContext(MyContext); + const {bar} = useContext(MyContext); + return ; +} + +``` + +## Code + +```javascript +import { + $dispatcherGuard, + useContext_withSelector, +} from "react-compiler-runtime"; +import { c as _c } from "react/compiler-runtime"; // @lowerContextAccess @enableEmitHookGuards +function App() { + const $ = _c(3); + try { + $dispatcherGuard(0); + const { foo } = (function () { + try { + $dispatcherGuard(2); + return useContext_withSelector(MyContext, _temp); + } finally { + $dispatcherGuard(3); + } + })(); + const { bar } = (function () { + try { + $dispatcherGuard(2); + return useContext_withSelector(MyContext, _temp2); + } finally { + $dispatcherGuard(3); + } + })(); + let t0; + if ($[0] !== bar || $[1] !== foo) { + t0 = ; + $[0] = bar; + $[1] = foo; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; + } finally { + $dispatcherGuard(1); + } +} +function _temp2(t0) { + return [t0.bar]; +} +function _temp(t0) { + return [t0.foo]; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js new file mode 100644 index 0000000000000..da881ea124a41 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/lower-context-access-hook-guard.js @@ -0,0 +1,6 @@ +// @lowerContextAccess @enableEmitHookGuards +function App() { + const {foo} = useContext(MyContext); + const {bar} = useContext(MyContext); + return ; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md index fecc28bb00158..7ba4ee28115d5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/bailout-retry/no-fire-todo-syntax-shouldnt-throw.expect.md @@ -43,8 +43,7 @@ function FireComponent(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold(none) import { fire } from "react"; /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md index 8d8bc179a245a..36146cff00fa1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/basic.expect.md @@ -21,8 +21,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md index a335fea8867b9..de003f7007f86 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/deep-scope.expect.md @@ -30,8 +30,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md index 5767ff0746c1b..20260bd5e694d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/fire-and-autodeps.expect.md @@ -21,8 +21,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire @inferEffectDependencies +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @inferEffectDependencies import { fire, useEffect } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md new file mode 100644 index 0000000000000..d94cce5588455 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.expect.md @@ -0,0 +1,72 @@ + +## Input + +```javascript +// @enableFire @enableEmitHookGuards +import {fire} from 'react'; + +function Component(props) { + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return null; +} + +``` + +## Code + +```javascript +import { $dispatcherGuard } from "react-compiler-runtime"; +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @enableEmitHookGuards +import { fire } from "react"; + +function Component(props) { + const $ = _c(3); + try { + $dispatcherGuard(0); + const foo = _temp; + const t0 = (function () { + try { + $dispatcherGuard(2); + return useFire(foo); + } finally { + $dispatcherGuard(3); + } + })(); + let t1; + if ($[0] !== props || $[1] !== t0) { + t1 = () => { + t0(props); + }; + $[0] = props; + $[1] = t0; + $[2] = t1; + } else { + t1 = $[2]; + } + (function () { + try { + $dispatcherGuard(2); + return useEffect(t1); + } finally { + $dispatcherGuard(3); + } + })(); + return null; + } finally { + $dispatcherGuard(1); + } +} +function _temp(props_0) { + console.log(props_0); +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js new file mode 100644 index 0000000000000..bc0b1a2d7ceaa --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/hook-guard.js @@ -0,0 +1,13 @@ +// @enableFire @enableEmitHookGuards +import {fire} from 'react'; + +function Component(props) { + const foo = props => { + console.log(props); + }; + useEffect(() => { + fire(foo(props)); + }); + + return null; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md index 02f3935171253..796c4397eeca4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/multiple-scope.expect.md @@ -29,8 +29,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md index 1734ca3ab4584..e528823550f2f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repeated-calls.expect.md @@ -22,8 +22,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md index 5a27845f079e3..c7ed50ceba204 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/repro-dont-add-hook-guards-on-retry.expect.md @@ -23,7 +23,6 @@ function Component(props, useDynamicHook) { ## Code ```javascript -import { $dispatcherGuard } from "react-compiler-runtime"; import { useFire } from "react/compiler-runtime"; import { useEffect, fire } from "react"; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md index ae71f60393281..e569536ad3a08 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/rewrite-deps.expect.md @@ -21,8 +21,7 @@ function Component(props) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md index 9b689b31c7ba0..92dbf9843ad65 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md @@ -26,8 +26,7 @@ function Component({bar, baz}) { ## Code ```javascript -import { useFire } from "react/compiler-runtime"; -import { c as _c } from "react/compiler-runtime"; // @enableFire +import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(t0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/index.ts b/compiler/packages/babel-plugin-react-compiler/src/index.ts index fa330f5582fc1..3310581462dc4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/index.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/index.ts @@ -19,6 +19,7 @@ export { parsePluginOptions, OPT_OUT_DIRECTIVES, OPT_IN_DIRECTIVES, + ProgramContext, findDirectiveEnablingMemoization, findDirectiveDisablingMemoization, type CompilerPipelineValue, From 04bf10e6a9526ea2600005a714c957c47dd8551d Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Mon, 24 Mar 2025 10:19:55 -0400 Subject: [PATCH 205/300] Add getRootNode to fragment instances (#32682) This implements `getRootNode(options)` on fragment instances as the equivalent of calling `getRootNode` on the fragment's parent host node. The parent host instance will also be used to proxy dispatchEvent in an upcoming PR. --- .../src/client/ReactFiberConfigDOM.js | 24 +++++- .../__tests__/ReactDOMFragmentRefs-test.js | 84 ++++++++++++++++++- .../src/createReactNoop.js | 6 +- .../src/ReactFiberTreeReflection.js | 15 ++++ 4 files changed, 125 insertions(+), 4 deletions(-) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 27a4a9167f29b..f01817ce240fd 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -53,7 +53,10 @@ import { markNodeAsHoistable, isOwnedInstance, } from './ReactDOMComponentTree'; -import {traverseFragmentInstance} from 'react-reconciler/src/ReactFiberTreeReflection'; +import { + traverseFragmentInstance, + getFragmentParentHostInstance, +} from 'react-reconciler/src/ReactFiberTreeReflection'; export {detachDeletedInstance}; import {hasRole} from './DOMAccessibilityRoles'; @@ -2239,6 +2242,9 @@ export type FragmentInstanceType = { observeUsing(observer: IntersectionObserver | ResizeObserver): void, unobserveUsing(observer: IntersectionObserver | ResizeObserver): void, getClientRects(): Array, + getRootNode(getRootNodeOptions?: { + composed: boolean, + }): Document | ShadowRoot | FragmentInstanceType, }; function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) { @@ -2338,7 +2344,7 @@ FragmentInstance.prototype.focus = function ( FragmentInstance.prototype.focusLast = function ( this: FragmentInstanceType, focusOptions?: FocusOptions, -) { +): void { const children: Array = []; traverseFragmentInstance(this._fragmentFiber, collectChildren, children); for (let i = children.length - 1; i >= 0; i--) { @@ -2429,6 +2435,20 @@ function collectClientRects(child: Instance, rects: Array): boolean { rects.push.apply(rects, child.getClientRects()); return false; } +// $FlowFixMe[prop-missing] +FragmentInstance.prototype.getRootNode = function ( + this: FragmentInstanceType, + getRootNodeOptions?: {composed: boolean}, +): Document | ShadowRoot | FragmentInstanceType { + const parentHostInstance = getFragmentParentHostInstance(this._fragmentFiber); + if (parentHostInstance === null) { + return this; + } + const rootNode = + // $FlowFixMe[incompatible-cast] Flow expects Node + (parentHostInstance.getRootNode(getRootNodeOptions): Document | ShadowRoot); + return rootNode; +}; function normalizeListenerOptions( opts: ?EventListenerOptionsOrUseCapture, diff --git a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js index c2cfc99ee87b0..c3d3a9ca7e45b 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFragmentRefs-test.js @@ -846,7 +846,7 @@ describe('FragmentRefs', () => { describe('getClientRects', () => { // @gate enableFragmentRefs - it('returns the bounding client recs of all children', async () => { + it('returns the bounding client rects of all children', async () => { const fragmentRef = React.createRef(); const childARef = React.createRef(); const childBRef = React.createRef(); @@ -884,4 +884,86 @@ describe('FragmentRefs', () => { expect(clientRects[2].left).toBe(9); }); }); + + describe('getRootNode', () => { + // @gate enableFragmentRefs + it('returns the root node of the parent', async () => { + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test() { + return ( +
+ +
+ +
+ ); + } + + await act(() => root.render()); + expect(fragmentRef.current.getRootNode()).toBe(document); + }); + + // The desired behavior here is to return the topmost disconnected element when + // fragment + parent are unmounted. Currently we have a pass during unmount that + // recursively cleans up return pointers of the whole tree. We can change this + // with a future refactor. See: https://github.com/facebook/react/pull/32682#discussion_r2008313082 + // @gate enableFragmentRefs + it('returns the topmost disconnected element if the fragment and parent are unmounted', async () => { + const containerRef = React.createRef(); + const parentRef = React.createRef(); + const fragmentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test({mounted}) { + return ( +
+ {mounted && ( +
+ +
+ +
+ )} +
+ ); + } + + await act(() => root.render()); + expect(fragmentRef.current.getRootNode()).toBe(document); + const fragmentHandle = fragmentRef.current; + await act(() => root.render()); + // TODO: The commented out assertion is the desired behavior. For now, we return + // the fragment instance itself. This is currently the same behavior if you unmount + // the fragment but not the parent. See context above. + // expect(fragmentHandle.getRootNode().id).toBe(parentRefHandle.id); + expect(fragmentHandle.getRootNode()).toBe(fragmentHandle); + }); + + // @gate enableFragmentRefs + it('returns self when only the fragment was unmounted', async () => { + const fragmentRef = React.createRef(); + const parentRef = React.createRef(); + const root = ReactDOMClient.createRoot(container); + + function Test({mounted}) { + return ( +
+ {mounted && ( + +
+ + )} +
+ ); + } + + await act(() => root.render()); + expect(fragmentRef.current.getRootNode()).toBe(document); + const fragmentHandle = fragmentRef.current; + await act(() => root.render()); + expect(fragmentHandle.getRootNode()).toBe(fragmentHandle); + }); + }); }); diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 266f860fe6d32..efb5f955cae73 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -512,10 +512,14 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { throw new Error('Not yet implemented.'); }, - createFragmentInstance(parentInstance) { + createFragmentInstance(fragmentFiber) { return null; }, + updateFragmentInstanceFiber(fragmentFiber, fragmentInstance) { + // Noop + }, + commitNewChildToFragmentInstance(child, fragmentInstance) { // Noop }, diff --git a/packages/react-reconciler/src/ReactFiberTreeReflection.js b/packages/react-reconciler/src/ReactFiberTreeReflection.js index 6d113377c35cf..2e2466a4f5a83 100644 --- a/packages/react-reconciler/src/ReactFiberTreeReflection.js +++ b/packages/react-reconciler/src/ReactFiberTreeReflection.js @@ -352,3 +352,18 @@ function traverseFragmentInstanceChildren( child = child.sibling; } } + +export function getFragmentParentHostInstance(fiber: Fiber): null | Instance { + let parent = fiber.return; + while (parent !== null) { + if (parent.tag === HostRoot) { + return parent.stateNode.containerInfo; + } + if (parent.tag === HostComponent) { + return parent.stateNode; + } + parent = parent.return; + } + + return null; +} From 42a57ea8027de8af55e6f4483c3b9a8f4cba31fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 24 Mar 2025 14:04:27 -0400 Subject: [PATCH 206/300] Merge ViewTransition layout/onLayout props into update/onUpdate (#32723) We currently have the ability to have a separate animation for a ViewTransition that relayouts but doesn't actually have any internal mutations. This can be useful if you want to separate just a move from for example flashing an update. However, we're concerned that this might be more confusion than its worth because subtle differences in mutations can cause it to trigger the other case. The existence of the property name might also make you start looking for it to solve something that it's not meant for. We already fallback to using the "update" property if it exists but layout doesn't. So if we ever decide to add this back it would backwards compatible. We've also shown in implementation that it can work. --- .../src/ReactFiberApplyGesture.js | 12 +- .../src/ReactFiberCommitViewTransitions.js | 107 ++---------------- .../src/ReactFiberCommitWork.js | 9 +- .../src/ReactFiberViewTransitionComponent.js | 2 - 4 files changed, 16 insertions(+), 114 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberApplyGesture.js b/packages/react-reconciler/src/ReactFiberApplyGesture.js index ea4a0ce70ae5f..42682e60c4e41 100644 --- a/packages/react-reconciler/src/ReactFiberApplyGesture.js +++ b/packages/react-reconciler/src/ReactFiberApplyGesture.js @@ -308,7 +308,7 @@ function applyNestedViewTransition(child: Fiber): void { const name = getViewTransitionName(props, state); const className: ?string = getViewTransitionClassName( props.className, - props.layout, + props.update, ); if (className !== 'none') { const clones = state.clones; @@ -335,17 +335,13 @@ function applyUpdateViewTransition(current: Fiber, finishedWork: Fiber): void { // we would use. However, since this animation is going in reverse we actually // want the props from "current" since that's the class that would've won if // it was the normal direction. To preserve the same effect in either direction. - let className: ?string = getViewTransitionClassName( + const className: ?string = getViewTransitionClassName( newProps.className, newProps.update, ); if (className === 'none') { - className = getViewTransitionClassName(newProps.className, newProps.layout); - if (className === 'none') { - // If both update and layout are both "none" then we don't have to - // apply a name. Since we won't animate this boundary. - return; - } + // If update is "none" then we don't have to apply a name. Since we won't animate this boundary. + return; } const clones = state.clones; // If there are no clones at this point, that should mean that there are no diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index c8ec019da7f07..2ebebc4e752f0 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -469,17 +469,13 @@ export function commitBeforeUpdateViewTransition( // For example, if update="foo" layout="none" and it turns out this was // a layout only change, then the "foo" class will be applied even though // it was not actually an update. Which is a bug. - let className: ?string = getViewTransitionClassName( + const className: ?string = getViewTransitionClassName( newProps.className, newProps.update, ); if (className === 'none') { - className = getViewTransitionClassName(newProps.className, newProps.layout); - if (className === 'none') { - // If both update and layout are both "none" then we don't have to - // apply a name. Since we won't animate this boundary. - return; - } + // If update is "none" then we don't have to apply a name. Since we won't animate this boundary. + return; } applyViewTransitionToHostInstances( current.child, @@ -500,7 +496,7 @@ export function commitNestedViewTransitions(changedParent: Fiber): void { const name = getViewTransitionName(props, child.stateNode); const className: ?string = getViewTransitionClassName( props.className, - props.layout, + props.update, ); if (className !== 'none') { applyViewTransitionToHostInstances( @@ -590,61 +586,6 @@ export function restoreNestedViewTransitions(changedParent: Fiber): void { } } -export function cancelViewTransitionHostInstances( - child: null | Fiber, - oldName: string, - stopAtNestedViewTransitions: boolean, -): void { - viewTransitionHostInstanceIdx = 0; - cancelViewTransitionHostInstancesRecursive( - child, - oldName, - stopAtNestedViewTransitions, - ); -} - -function cancelViewTransitionHostInstancesRecursive( - child: null | Fiber, - oldName: string, - stopAtNestedViewTransitions: boolean, -): void { - if (!supportsMutation) { - return; - } - while (child !== null) { - if (child.tag === HostComponent) { - const instance: Instance = child.stateNode; - if (viewTransitionCancelableChildren === null) { - viewTransitionCancelableChildren = []; - } - viewTransitionCancelableChildren.push( - instance, - oldName, - child.memoizedProps, - ); - viewTransitionHostInstanceIdx++; - } else if ( - child.tag === OffscreenComponent && - child.memoizedState !== null - ) { - // Skip any hidden subtrees. They were or are effectively not there. - } else if ( - child.tag === ViewTransitionComponent && - stopAtNestedViewTransitions - ) { - // Skip any nested view transitions for updates since in that case the - // inner most one is the one that handles the update. - } else { - cancelViewTransitionHostInstancesRecursive( - child.child, - oldName, - stopAtNestedViewTransitions, - ); - } - child = child.sibling; - } -} - export function measureViewTransitionHostInstances( parentViewTransition: Fiber, child: null | Fiber, @@ -792,40 +733,14 @@ export function measureUpdateViewTransition( const state: ViewTransitionState = newFiber.stateNode; const newName = getViewTransitionName(props, state); const oldName = getViewTransitionName(oldFiber.memoizedProps, state); - const updateClassName: ?string = getViewTransitionClassName( + // Whether it ends up having been updated or relayout we apply the update class name. + const className: ?string = getViewTransitionClassName( props.className, props.update, ); - const layoutClassName: ?string = getViewTransitionClassName( - props.className, - props.layout, - ); - let className: ?string; - if (updateClassName === 'none') { - if (layoutClassName === 'none') { - // If both update and layout class name were none, then we didn't apply any - // names in the before update phase so we shouldn't now neither. - return false; - } - // We don't care if this is mutated or children layout changed, but we still - // measure each instance to see if it moved and therefore should apply layout. - finishedWork.flags &= ~Update; - className = layoutClassName; - } else if ((finishedWork.flags & Update) !== NoFlags) { - // It was updated and we have an appropriate class name to apply. - className = updateClassName; - } else { - if (layoutClassName === 'none') { - // If we did not update, then all changes are considered a layout. We'll - // attempt to cancel. - // This should use the Fiber that got names applied in the snapshot phase - // since those are the ones we're trying to cancel. - cancelViewTransitionHostInstances(oldFiber.child, oldName, true); - return false; - } - // We didn't update but we might still apply layout so we measure each - // instance to see if it moved or resized. - className = layoutClassName; + if (className === 'none') { + // If update is "none" then we don't have to apply a name. Since we won't animate this boundary. + return false; } // If nothing changed due to a mutation, or children changing size // and the measurements end up unchanged, we should restore it to not animate. @@ -873,7 +788,7 @@ export function measureNestedViewTransitions( const name = getViewTransitionName(props, state); const className: ?string = getViewTransitionClassName( props.className, - props.layout, + props.update, ); let previousMeasurements: null | Array; if (gesture) { @@ -902,7 +817,7 @@ export function measureNestedViewTransitions( if (gesture) { // TODO: Schedule gesture events. } else { - scheduleViewTransitionEvent(child, props.onLayout); + scheduleViewTransitionEvent(child, props.onUpdate); } } } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index e73b3fa8fb925..e604d8f0cb85d 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -2440,8 +2440,6 @@ function commitAfterMutationEffectsOnFiber( break; } case ViewTransitionComponent: { - const wasMutated = (finishedWork.flags & Update) !== NoFlags; - const prevContextChanged = viewTransitionContextChanged; const prevCancelableChildren = pushViewTransitionCancelableScope(); viewTransitionContextChanged = false; @@ -2477,12 +2475,7 @@ function commitAfterMutationEffectsOnFiber( // then we should probably issue an event since this instance is part of it. } else { const props: ViewTransitionProps = finishedWork.memoizedProps; - scheduleViewTransitionEvent( - finishedWork, - wasMutated || viewTransitionContextChanged - ? props.onUpdate - : props.onLayout, - ); + scheduleViewTransitionEvent(finishedWork, props.onUpdate); // If this boundary did update, we cannot cancel its children so those are dropped. popViewTransitionCancelableScope(prevCancelableChildren); diff --git a/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js b/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js index ecacf2a439944..3b5bede1a7fa9 100644 --- a/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js +++ b/packages/react-reconciler/src/ReactFiberViewTransitionComponent.js @@ -32,12 +32,10 @@ export type ViewTransitionProps = { className?: ViewTransitionClass, enter?: ViewTransitionClass, exit?: ViewTransitionClass, - layout?: ViewTransitionClass, share?: ViewTransitionClass, update?: ViewTransitionClass, onEnter?: (instance: ViewTransitionInstance, types: Array) => void, onExit?: (instance: ViewTransitionInstance, types: Array) => void, - onLayout?: (instance: ViewTransitionInstance, types: Array) => void, onShare?: (instance: ViewTransitionInstance, types: Array) => void, onUpdate?: (instance: ViewTransitionInstance, types: Array) => void, }; From 254dc4d9f37eb512d4ee8bad6a0fae7ae491caef Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:30:17 -0400 Subject: [PATCH 207/300] [compiler][bugfix] Fix hoisting of let declarations (#32724) (Found when compiling Meta React code) Let variable declarations and reassignments are currently rewritten to `StoreLocal ` instructions, which each translates to a new `const varName` declaration in codegen. ```js // Example input function useHook() { const getX = () => x; let x = CONSTANT1; if (cond) { x += CONSTANT2; } return } // Compiled output, prior to this PR import { c as _c } from "react/compiler-runtime"; function useHook() { const $ = _c(1); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { const getX = () => x; let x = CONSTANT1; if (cond) { let x = x + CONSTANT2; x; } t0 = ; $[0] = t0; } else { t0 = $[0]; } return t0; } ``` This also manifests as a babel internal error when replacing the original function declaration with the compiler output. The below compilation output fails with `Duplicate declaration "x" (This is an error on an internal node. Probably an internal error.)`. ```js // example input let x = CONSTANT1; if (cond) { x += CONSTANT2; x = CONSTANT3; } // current output let x = CONSTANT1; if (playheadDragState) { let x = x + CONSTANT2 x; let x = CONSTANT3; } ``` --- .../ReactiveScopes/PruneHoistedContexts.ts | 101 ++++++++++++++---- .../hoisting-invalid-tdz-let.expect.md | 59 ++++++++++ .../compiler/hoisting-invalid-tdz-let.js | 14 +++ ...oisting-nested-let-declaration-2.expect.md | 3 +- .../hoisting-nested-let-declaration.expect.md | 6 +- ...sting-reassigned-let-declaration.expect.md | 67 ++++++++++++ .../hoisting-reassigned-let-declaration.js | 18 ++++ ...reassigned-twice-let-declaration.expect.md | 69 ++++++++++++ ...isting-reassigned-twice-let-declaration.js | 19 ++++ .../hoisting-simple-let-declaration.expect.md | 6 +- .../mutate-captured-arg-separately.expect.md | 3 +- .../recursive-function-expression.expect.md | 29 +++++ .../compiler/recursive-function-expression.js | 11 ++ 13 files changed, 378 insertions(+), 27 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts index 07b099c2ea5fe..b3754721cab37 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {CompilerError} from '..'; import { DeclarationId, InstructionKind, @@ -27,7 +28,17 @@ export function pruneHoistedContexts(fn: ReactiveFunction): void { visitReactiveFunction(fn, new Visitor(), hoistedIdentifiers); } -type HoistedIdentifiers = Map; +const REWRITTEN_HOISTED_CONST: unique symbol = Symbol( + 'REWRITTEN_HOISTED_CONST', +); +const REWRITTEN_HOISTED_LET: unique symbol = Symbol('REWRITTEN_HOISTED_LET'); + +type HoistedIdentifiers = Map< + DeclarationId, + | InstructionKind + | typeof REWRITTEN_HOISTED_CONST + | typeof REWRITTEN_HOISTED_LET +>; class Visitor extends ReactiveFunctionTransform { override transformInstruction( @@ -35,6 +46,10 @@ class Visitor extends ReactiveFunctionTransform { state: HoistedIdentifiers, ): Transformed { this.visitInstruction(instruction, state); + + /** + * Remove hoisted declarations to preserve TDZ + */ if ( instruction.value.kind === 'DeclareContext' && instruction.value.lvalue.kind === 'HoistedConst' @@ -68,31 +83,75 @@ class Visitor extends ReactiveFunctionTransform { return {kind: 'remove'}; } - if ( - instruction.value.kind === 'StoreContext' && - state.has(instruction.value.lvalue.place.identifier.declarationId) - ) { + if (instruction.value.kind === 'StoreContext') { const kind = state.get( instruction.value.lvalue.place.identifier.declarationId, - )!; - return { - kind: 'replace', - value: { - kind: 'instruction', - instruction: { - ...instruction, + ); + if (kind != null) { + CompilerError.invariant(kind !== REWRITTEN_HOISTED_CONST, { + reason: 'Expected exactly one store to a hoisted const variable', + loc: instruction.loc, + }); + if ( + kind === InstructionKind.Const || + kind === InstructionKind.Function + ) { + state.set( + instruction.value.lvalue.place.identifier.declarationId, + REWRITTEN_HOISTED_CONST, + ); + return { + kind: 'replace', value: { - ...instruction.value, - lvalue: { - ...instruction.value.lvalue, - kind, + kind: 'instruction', + instruction: { + ...instruction, + value: { + ...instruction.value, + lvalue: { + ...instruction.value.lvalue, + kind, + }, + type: null, + kind: 'StoreLocal', + }, }, - type: null, - kind: 'StoreLocal', }, - }, - }, - }; + }; + } else if (kind !== REWRITTEN_HOISTED_LET) { + /** + * Context variables declared with let may have reassignments. Only + * insert a `DeclareContext` for the first encountered `StoreContext` + * instruction. + */ + state.set( + instruction.value.lvalue.place.identifier.declarationId, + REWRITTEN_HOISTED_LET, + ); + return { + kind: 'replace-many', + value: [ + { + kind: 'instruction', + instruction: { + id: instruction.id, + lvalue: null, + value: { + kind: 'DeclareContext', + lvalue: { + kind: InstructionKind.Let, + place: {...instruction.value.lvalue.place}, + }, + loc: instruction.value.loc, + }, + loc: instruction.loc, + }, + }, + {kind: 'instruction', instruction}, + ], + }; + } + } } return {kind: 'keep'}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.expect.md new file mode 100644 index 0000000000000..2c2b559f02622 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +function Foo() { + const getX = () => x; + console.log(getX()); + + let x = 4; + x += 5; + + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function Foo() { + const $ = _c(2); + let getX; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + getX = () => x; + console.log(getX()); + + let x; + x = 4; + x = x + 5; + $[0] = getX; + } else { + getX = $[0]; + } + x; + let t0; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], +}; + +``` + +### Eval output +(kind: exception) Cannot access 'x' before initialization \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.js new file mode 100644 index 0000000000000..4097ee37970cc --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-invalid-tdz-let.js @@ -0,0 +1,14 @@ +function Foo() { + const getX = () => x; + console.log(getX()); + + let x = 4; + x += 5; + + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md index fe0c6618f51ad..aa6ee80d69bf1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration-2.expect.md @@ -36,7 +36,8 @@ function hoisting(cond) { items.push(bar()); }; - let bar = _temp; + let bar; + bar = _temp; foo(); } $[0] = cond; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md index faf7a064d748f..25205ca02372c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-nested-let-declaration.expect.md @@ -41,9 +41,11 @@ function hoisting() { return result; }; - let foo = () => bar + baz; + let foo; + foo = () => bar + baz; - let bar = 3; + let bar; + bar = 3; const baz = 2; t0 = qux(); $[0] = t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.expect.md new file mode 100644 index 0000000000000..3f7b16cf23697 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.expect.md @@ -0,0 +1,67 @@ + +## Input + +```javascript +import {CONST_NUMBER0, CONST_NUMBER1, Stringify} from 'shared-runtime'; + +function useHook({cond}) { + 'use memo'; + const getX = () => x; + + let x = CONST_NUMBER0; + if (cond) { + x += CONST_NUMBER1; + } + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{cond: true}], + sequentialRenders: [{cond: true}, {cond: true}, {cond: false}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { CONST_NUMBER0, CONST_NUMBER1, Stringify } from "shared-runtime"; + +function useHook(t0) { + "use memo"; + const $ = _c(2); + const { cond } = t0; + let t1; + if ($[0] !== cond) { + const getX = () => x; + + let x; + x = CONST_NUMBER0; + if (cond) { + x = x + CONST_NUMBER1; + x; + } + + t1 = ; + $[0] = cond; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{ cond: true }], + sequentialRenders: [{ cond: true }, { cond: true }, { cond: false }], +}; + +``` + +### Eval output +(kind: ok)
{"getX":{"kind":"Function","result":1},"shouldInvokeFns":true}
+
{"getX":{"kind":"Function","result":1},"shouldInvokeFns":true}
+
{"getX":{"kind":"Function","result":0},"shouldInvokeFns":true}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.js new file mode 100644 index 0000000000000..e8d55039befbd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-let-declaration.js @@ -0,0 +1,18 @@ +import {CONST_NUMBER0, CONST_NUMBER1, Stringify} from 'shared-runtime'; + +function useHook({cond}) { + 'use memo'; + const getX = () => x; + + let x = CONST_NUMBER0; + if (cond) { + x += CONST_NUMBER1; + } + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{cond: true}], + sequentialRenders: [{cond: true}, {cond: true}, {cond: false}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.expect.md new file mode 100644 index 0000000000000..54d49b9282cdf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.expect.md @@ -0,0 +1,69 @@ + +## Input + +```javascript +import {CONST_NUMBER0, CONST_NUMBER1, Stringify} from 'shared-runtime'; + +function useHook({cond}) { + 'use memo'; + const getX = () => x; + + let x = CONST_NUMBER0; + if (cond) { + x += CONST_NUMBER1; + x = Math.min(x, 100); + } + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{cond: true}], + sequentialRenders: [{cond: true}, {cond: true}, {cond: false}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { CONST_NUMBER0, CONST_NUMBER1, Stringify } from "shared-runtime"; + +function useHook(t0) { + "use memo"; + const $ = _c(2); + const { cond } = t0; + let t1; + if ($[0] !== cond) { + const getX = () => x; + + let x; + x = CONST_NUMBER0; + if (cond) { + x = x + CONST_NUMBER1; + x; + x = Math.min(x, 100); + } + + t1 = ; + $[0] = cond; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{ cond: true }], + sequentialRenders: [{ cond: true }, { cond: true }, { cond: false }], +}; + +``` + +### Eval output +(kind: ok)
{"getX":{"kind":"Function","result":1},"shouldInvokeFns":true}
+
{"getX":{"kind":"Function","result":1},"shouldInvokeFns":true}
+
{"getX":{"kind":"Function","result":0},"shouldInvokeFns":true}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.js new file mode 100644 index 0000000000000..2c2b8187effaa --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-reassigned-twice-let-declaration.js @@ -0,0 +1,19 @@ +import {CONST_NUMBER0, CONST_NUMBER1, Stringify} from 'shared-runtime'; + +function useHook({cond}) { + 'use memo'; + const getX = () => x; + + let x = CONST_NUMBER0; + if (cond) { + x += CONST_NUMBER1; + x = Math.min(x, 100); + } + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useHook, + params: [{cond: true}], + sequentialRenders: [{cond: true}, {cond: true}, {cond: false}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md index 8d694a984aed5..958f2c7afdf8f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-simple-let-declaration.expect.md @@ -29,8 +29,10 @@ function hoisting() { if ($[0] === Symbol.for("react.memo_cache_sentinel")) { foo = () => bar + baz; - let bar = 3; - let baz = 2; + let bar; + bar = 3; + let baz; + baz = 2; $[0] = foo; } else { foo = $[0]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md index f3f21f8f6222b..990b1bf14713b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mutate-captured-arg-separately.expect.md @@ -33,7 +33,8 @@ function component(a) { m(x); }; - let x = { a }; + let x; + x = { a }; m(x); $[0] = a; $[1] = y; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.expect.md index bc46c1b85578b..1e7295da57f45 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.expect.md @@ -2,6 +2,17 @@ ## Input ```javascript +function Component1() { + const x = callback(10); + function callback(x) { + if (x == 0) { + return null; + } + return callback(x - 1); + } + return x; +} + function Component() { function callback(x) { if (x == 0) { @@ -23,6 +34,24 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; +function Component1() { + const $ = _c(1); + let x; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + x = callback(10); + function callback(x_0) { + if (x_0 == 0) { + return null; + } + return callback(x_0 - 1); + } + $[0] = x; + } else { + x = $[0]; + } + return x; +} + function Component() { const $ = _c(1); let t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.js index 5d50de75bae50..b09769da2f22b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/recursive-function-expression.js @@ -1,3 +1,14 @@ +function Component1() { + const x = callback(10); + function callback(x) { + if (x == 0) { + return null; + } + return callback(x - 1); + } + return x; +} + function Component() { function callback(x) { if (x == 0) { From 2d40460cf768071d3a70b4cdc16075d23ca1ff25 Mon Sep 17 00:00:00 2001 From: Ricky Date: Mon, 24 Mar 2025 15:46:59 -0400 Subject: [PATCH 208/300] [ci] fix notify/label actions for forks (#32725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Need this to run against target for forks to get the notification. This job does not checkout the code in the PR, so it's safe to run from the target. Also fixes failing checks on PRs: Screenshot 2025-03-24 at 3 28 30 PM --- .github/workflows/compiler_discord_notify.yml | 2 +- .github/workflows/runtime_discord_notify.yml | 2 +- .github/workflows/shared_label_core_team_prs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index 7d23facf87873..3840e3787bc00 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -1,7 +1,7 @@ name: (Compiler) Discord Notify on: - pull_request: + pull_request_target: types: [opened, ready_for_review] paths: - compiler/** diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index 2e81e90d37b91..ad94e28054d24 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -1,7 +1,7 @@ name: (Runtime) Discord Notify on: - pull_request: + pull_request_target: types: [opened, ready_for_review] paths-ignore: - compiler/** diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml index 78ff4b2b03fab..2cd9f290e0af0 100644 --- a/.github/workflows/shared_label_core_team_prs.yml +++ b/.github/workflows/shared_label_core_team_prs.yml @@ -1,7 +1,7 @@ name: (Shared) Label Core Team PRs on: - pull_request: + pull_request_target: permissions: {} From ea5f065745b777cb41cc9e54a3b29ed8c727a574 Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 24 Mar 2025 16:40:14 -0400 Subject: [PATCH 209/300] [ci] Make maintainer check always remote (#32727) To prevent local modification of the MAINTAINERS file we now always fetch from `main` instead. --- .github/workflows/shared_check_maintainer.yml | 44 ++++++------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/.github/workflows/shared_check_maintainer.yml b/.github/workflows/shared_check_maintainer.yml index 3bc1ad1e23d9f..f6eb9b9a6d122 100644 --- a/.github/workflows/shared_check_maintainer.yml +++ b/.github/workflows/shared_check_maintainer.yml @@ -6,10 +6,6 @@ on: actor: required: true type: string - is_remote: - required: false - type: boolean - default: false outputs: is_core_team: value: ${{ jobs.check_maintainer.outputs.is_core_team }} @@ -30,7 +26,6 @@ jobs: outputs: is_core_team: ${{ steps.check_if_actor_is_maintainer.outputs.result }} steps: - - uses: actions/checkout@v4 - name: Check if actor is maintainer id: check_if_actor_is_maintainer uses: actions/github-script@v7 @@ -38,33 +33,20 @@ jobs: script: | const fs = require('fs'); const actor = '${{ inputs.actor }}'; - let isRemote = ${{ inputs.is_remote }}; - if (typeof isRemote === 'string') { - isRemote = isRemote === 'true'; + const res = await github.rest.repos.getContent({ + owner: 'facebook', + repo: 'react', + path: 'MAINTAINERS', + ref: 'main', + headers: { Accept: 'application/vnd.github+json' } + }); + if (res.status !== 200) { + console.error(res); + throw new Error('Unable to fetch MAINTAINERS file'); } - if (typeof isRemote !== 'boolean') { - throw new Error(`Invalid \`isRemote\` input. Expected a boolean, got: ${isRemote}`); - } - - let content = null; - if (isRemote === true) { - const res = await github.rest.repos.getContent({ - owner: 'facebook', - repo: 'react', - path: 'MAINTAINERS', - ref: 'main', - headers: { Accept: 'application/vnd.github+json' } - }); - if (res.status !== 200) { - console.error(res); - throw new Error('Unable to fetch MAINTAINERS file'); - } - content = Buffer.from(res.data.content, 'base64').toString(); - } else { - content = await fs.readFileSync('./MAINTAINERS', { encoding: 'utf8' }); - } - if (content === null) { - throw new Error('Unable to retrieve local or http MAINTAINERS file'); + content = Buffer.from(res.data.content, 'base64').toString(); + if (content == null || typeof content !== 'string') { + throw new Error('Unable to retrieve MAINTAINERS file'); } const maintainers = new Set(content.split('\n')); From 07276b8682059cd310cedf574c7f3ecddce68f5c Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 24 Mar 2025 18:13:36 -0400 Subject: [PATCH 210/300] [ci] Add artifact attestation to build (#32711) Adds a signed build provenance attestations via https://github.com/actions/attest-build-provenance --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32711). * #32729 * #32728 * __->__ #32711 --- .github/workflows/runtime_build_and_test.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index 343c32724e62f..4478647aa0b3e 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -426,6 +426,10 @@ jobs: process_artifacts_combined: name: Process artifacts combined needs: [build_and_lint, runtime_node_modules_cache] + permissions: + # https://github.com/actions/attest-build-provenance + id-token: write + attestations: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -468,6 +472,7 @@ jobs: # TODO: Migrate scripts to use `build` directory instead of `build2` - run: cp ./build.tgz ./build2.tgz - name: Archive build artifacts + id: upload_artifacts_combined uses: actions/upload-artifact@v4 with: name: artifacts_combined @@ -475,6 +480,10 @@ jobs: ./build.tgz ./build2.tgz if-no-files-found: error + - uses: actions/attest-build-provenance@v2 + with: + subject-name: artifacts_combined.zip + subject-digest: sha256:${{ steps.upload_artifacts_combined.outputs.artifact-digest }} check_error_codes: name: Search build artifacts for unminified errors From 7e4c258e160d3a2ca690b44a5938271873919ee1 Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 24 Mar 2025 18:24:33 -0400 Subject: [PATCH 211/300] [scripts] Verify artifact integrity when downloading (#32728) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses https://cli.github.com/manual/gh_attestation_verify to verify that the downloaded artifact matches the attestation generated during the build process in runtime_commit_artifacts. Example: On a workflow run of runtime_build_and_test.yml with no attestations: ``` $ scripts/release/download-experimental-build.js --commit=ea5f065745b777cb41cc9e54a3b29ed8c727a574 Command failed: gh attestation verify artifacts_combined.zip --repo=facebook/react Error: failed to fetch attestations from facebook/react: HTTP 404: Not Found (https://api.github.com/repos/facebook/react/attestations/sha256:7adba0992ba477a927aad5a07f95ee2deb7d18427c84279d33fc40a3bc28ebaa?per_page=30) `gh attestation verify artifacts_combined.zip --repo=facebook/react` (exited with error code 1) ``` On one which does: ``` $ scripts/release/download-experimental-build.js --commit=12e85d74c1c233cdc2f3228a97473a4435d50c3b ✓ Downloading artifacts from GitHub for commit 12e85d74c1c233cdc2f3228a97473a4435d50c3b) 10.5 secs An experimental build has been downloaded! You can download this build again by running: scripts/download-experimental-build.js --commit=12e85d74c1c233cdc2f3228a97473a4435d50c3b ``` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32728). * #32729 * __->__ #32728 --- .../download-build-artifacts.js | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/scripts/release/shared-commands/download-build-artifacts.js b/scripts/release/shared-commands/download-build-artifacts.js index cf7301688027a..23e3541110fe1 100644 --- a/scripts/release/shared-commands/download-build-artifacts.js +++ b/scripts/release/shared-commands/download-build-artifacts.js @@ -3,8 +3,9 @@ const {join} = require('path'); const theme = require('../theme'); const {exec} = require('child-process-promise'); -const {existsSync, readFileSync} = require('fs'); +const {existsSync, mkdtempSync, readFileSync} = require('fs'); const {logPromise} = require('../utils'); +const os = require('os'); if (process.env.GH_TOKEN == null) { console.log( @@ -21,6 +22,15 @@ const GITHUB_HEADERS = ` -H "Authorization: Bearer ${process.env.GH_TOKEN}" \ -H "X-GitHub-Api-Version: 2022-11-28"`.trim(); +async function executableIsAvailable(name) { + try { + await exec(`which ${name}`); + return true; + } catch (_error) { + return false; + } +} + function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } @@ -78,10 +88,27 @@ async function getArtifact(workflowRunId, artifactName) { async function processArtifact(artifact, commit, releaseChannel) { // Download and extract artifact const cwd = join(__dirname, '..', '..', '..'); + const tmpDir = mkdtempSync(join(os.tmpdir(), 'react_')); await exec(`rm -rf ./build`, {cwd}); await exec( - `curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \ - > a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`, + `curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} > artifacts_combined.zip`, + { + cwd: tmpDir, + } + ); + + // Use https://cli.github.com/manual/gh_attestation_verify to verify artifact + if (executableIsAvailable('gh')) { + await exec( + `gh attestation verify artifacts_combined.zip --repo=${OWNER}/${REPO}`, + { + cwd: tmpDir, + } + ); + } + + await exec( + `unzip ${tmpDir}/artifacts_combined.zip -d . && rm build2.tgz && tar -xvzf build.tgz && rm build.tgz`, { cwd, } From ee0855f427832e899767f7659c5289364218ab9e Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 24 Mar 2025 18:24:45 -0400 Subject: [PATCH 212/300] [ci] Fix missing permissions for prereleases (#32729) Missed these earlier. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32729). * __->__ #32729 * #32728 --- .github/workflows/runtime_prereleases.yml | 3 +++ .github/workflows/runtime_prereleases_manual.yml | 6 ++++++ .github/workflows/runtime_prereleases_nightly.yml | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 147ec0a496dc8..19cc47f1ce508 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -29,6 +29,9 @@ jobs: publish_prerelease: name: Publish prelease (${{ inputs.release_channel }}) ${{ inputs.commit_sha }} @${{ inputs.dist_tag }} runs-on: ubuntu-latest + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.github/workflows/runtime_prereleases_manual.yml b/.github/workflows/runtime_prereleases_manual.yml index 77d3fd5e4304b..3cf2786cce68f 100644 --- a/.github/workflows/runtime_prereleases_manual.yml +++ b/.github/workflows/runtime_prereleases_manual.yml @@ -16,6 +16,9 @@ jobs: publish_prerelease_canary: name: Publish to Canary channel uses: facebook/react/.github/workflows/runtime_prereleases.yml@main + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read with: commit_sha: ${{ inputs.prerelease_commit_sha }} release_channel: stable @@ -36,6 +39,9 @@ jobs: publish_prerelease_experimental: name: Publish to Experimental channel uses: facebook/react/.github/workflows/runtime_prereleases.yml@main + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read # NOTE: Intentionally running these jobs sequentially because npm # will sometimes fail if you try to concurrently publish two # different versions of the same package, even if they use different diff --git a/.github/workflows/runtime_prereleases_nightly.yml b/.github/workflows/runtime_prereleases_nightly.yml index 4622e15f55ad5..2405283ded116 100644 --- a/.github/workflows/runtime_prereleases_nightly.yml +++ b/.github/workflows/runtime_prereleases_nightly.yml @@ -14,6 +14,9 @@ jobs: publish_prerelease_canary: name: Publish to Canary channel uses: facebook/react/.github/workflows/runtime_prereleases.yml@main + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read with: commit_sha: ${{ github.sha }} release_channel: stable @@ -24,6 +27,9 @@ jobs: publish_prerelease_experimental: name: Publish to Experimental channel uses: facebook/react/.github/workflows/runtime_prereleases.yml@main + permissions: + # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run + actions: read # NOTE: Intentionally running these jobs sequentially because npm # will sometimes fail if you try to concurrently publish two # different versions of the same package, even if they use different From 1cdf6b95901381e30afee8200be60f895c589267 Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 24 Mar 2025 18:59:17 -0400 Subject: [PATCH 213/300] [ci] Add GH_TOKEN as secret input to prereleases (#32732) Seems like this also needs to be specified --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32732). * #32730 * __->__ #32732 --- .github/workflows/runtime_prereleases.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml index 19cc47f1ce508..1449f6af5a318 100644 --- a/.github/workflows/runtime_prereleases.yml +++ b/.github/workflows/runtime_prereleases.yml @@ -14,6 +14,8 @@ on: required: true type: string secrets: + GH_TOKEN: + required: true NPM_TOKEN: required: true @@ -51,6 +53,6 @@ jobs: - run: yarn install --frozen-lockfile - run: yarn --cwd scripts/release install --frozen-lockfile - run: | - scripts/release/prepare-release-from-ci.js --skipTests -r ${{ inputs.release_channel }} --commit=${{ inputs.commit_sha }} + GH_TOKEN=${{ secrets.GH_TOKEN }} scripts/release/prepare-release-from-ci.js --skipTests -r ${{ inputs.release_channel }} --commit=${{ inputs.commit_sha }} cp ./scripts/release/ci-npmrc ~/.npmrc scripts/release/publish.js --ci --tags ${{ inputs.dist_tag }} From e5f275e72ac3c469845ca48452fca672e1427953 Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 24 Mar 2025 19:08:41 -0400 Subject: [PATCH 214/300] [ci] Pass GH_TOKEN to runtime_prereleases (#32730) Seems like this also needs to be specified. Note: #32732 needs to land first. --- .github/workflows/runtime_prereleases_manual.yml | 2 ++ .github/workflows/runtime_prereleases_nightly.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/runtime_prereleases_manual.yml b/.github/workflows/runtime_prereleases_manual.yml index 3cf2786cce68f..71e25ba073a83 100644 --- a/.github/workflows/runtime_prereleases_manual.yml +++ b/.github/workflows/runtime_prereleases_manual.yml @@ -35,6 +35,7 @@ jobs: dist_tag: canary,next secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish_prerelease_experimental: name: Publish to Experimental channel @@ -53,3 +54,4 @@ jobs: dist_tag: experimental secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/runtime_prereleases_nightly.yml b/.github/workflows/runtime_prereleases_nightly.yml index 2405283ded116..f89a0d2c91bc4 100644 --- a/.github/workflows/runtime_prereleases_nightly.yml +++ b/.github/workflows/runtime_prereleases_nightly.yml @@ -23,6 +23,7 @@ jobs: dist_tag: canary,next secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish_prerelease_experimental: name: Publish to Experimental channel @@ -41,3 +42,4 @@ jobs: dist_tag: experimental secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b59f18601179bb06a2c32a76547fd4929aa1ce9c Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Mon, 24 Mar 2025 23:00:47 -0400 Subject: [PATCH 215/300] [flow] Replace $PropertyType with indexed access type in ReactNativeTypes (#32733) --- packages/react-native-renderer/src/ReactNativeTypes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 4fdf4ea09e0f1..75f4a828c9077 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -83,8 +83,8 @@ export type ViewConfig = $ReadOnly<{ }>; export type PartialViewConfig = $ReadOnly<{ - bubblingEventTypes?: $PropertyType, - directEventTypes?: $PropertyType, + bubblingEventTypes?: ViewConfig['bubblingEventTypes'], + directEventTypes?: ViewConfig['directEventTypes'], supportsRawText?: boolean, uiViewClassName: string, validAttributes?: PartialAttributeConfiguration, From dc9b74647e093b531dc876a2438f12dac776e480 Mon Sep 17 00:00:00 2001 From: MU AOHUA Date: Tue, 25 Mar 2025 21:45:48 +0800 Subject: [PATCH 216/300] [DevTools] Add fb local build command (#32644) ## Summary 1. Having a development build for FB will be convenient for fb internal feature development 2. Add a new checkbox to toggle new internal features added to React Devtools. ## How did you test this change? 1. yarn test 2. set extra env variables in bash profile and build an internal version with the new script. 3. toggle on/off the new checkbox, the value is stored in local storage correctly. --------- Co-authored-by: Aohua Mu --- packages/react-devtools-extensions/package.json | 1 + .../src/devtools/views/Settings/GeneralSettings.js | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/packages/react-devtools-extensions/package.json b/packages/react-devtools-extensions/package.json index e1961983f2ef0..c48e4d3b195bb 100644 --- a/packages/react-devtools-extensions/package.json +++ b/packages/react-devtools-extensions/package.json @@ -8,6 +8,7 @@ "build:chrome": "cross-env NODE_ENV=production node ./chrome/build", "build:chrome:fb": "cross-env NODE_ENV=production FEATURE_FLAG_TARGET=extension-fb node ./chrome/build --crx", "build:chrome:local": "cross-env NODE_ENV=development node ./chrome/build", + "build:chrome:fb:local": "cross-env NODE_ENV=development FEATURE_FLAG_TARGET=extension-fb node ./chrome/build", "build:firefox": "cross-env NODE_ENV=production node ./firefox/build", "build:firefox:local": "cross-env NODE_ENV=development node ./firefox/build", "build:edge": "cross-env NODE_ENV=production node ./edge/build", diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js index d56f32f81142c..098845a5fb70e 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js @@ -12,6 +12,7 @@ import {useContext, useMemo} from 'react'; import {SettingsContext} from './SettingsContext'; import {StoreContext} from '../context'; import {CHANGE_LOG_URL} from 'react-devtools-shared/src/devtools/constants'; +import {isInternalFacebookBuild} from 'react-devtools-feature-flags'; import styles from './SettingsShared.css'; @@ -46,6 +47,12 @@ export default function GeneralSettings(_: {}): React.Node { return (
+ {isInternalFacebookBuild && ( +
+ This is an internal build of React DevTools for Meta +
+ )} +
Theme