From 45e6a413a09de1707739acab7c9f6fbd6ab3ded4 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Sun, 7 Jan 2024 23:49:14 -0500 Subject: [PATCH] Update React from 0cdfef19b to f1039be4a - https://github.com/facebook/react/pull/27888 - https://github.com/facebook/react/pull/27870 - https://github.com/facebook/react/pull/27871 - https://github.com/facebook/react/pull/27850 - https://github.com/facebook/react/pull/27839 - https://github.com/facebook/react/pull/27842 - https://github.com/facebook/react/pull/27841 - https://github.com/facebook/react/pull/27840 - https://github.com/facebook/react/pull/27761 - https://github.com/facebook/react/pull/27831 - https://github.com/facebook/react/pull/27801 --- package.json | 22 +- ...t-dom-server-legacy.browser.development.js | 454 ++++++-------- ...ct-dom-server-legacy.browser.production.js | 544 ++++++++++++++--- ...om-server-legacy.browser.production.min.js | 334 +++++----- ...eact-dom-server-legacy.node.development.js | 454 ++++++-------- ...react-dom-server-legacy.node.production.js | 544 ++++++++++++++--- ...t-dom-server-legacy.node.production.min.js | 362 +++++------ ...t-dom-server-rendering-stub.development.js | 2 +- ...ct-dom-server-rendering-stub.production.js | 2 +- ...om-server-rendering-stub.production.min.js | 2 +- .../react-dom-server.browser.development.js | 458 ++++++-------- .../react-dom-server.browser.production.js | 548 ++++++++++++++--- ...react-dom-server.browser.production.min.js | 341 ++++++----- .../cjs/react-dom-server.bun.production.js | 544 ++++++++++++++--- .../cjs/react-dom-server.edge.development.js | 458 ++++++-------- .../cjs/react-dom-server.edge.production.js | 548 ++++++++++++++--- .../react-dom-server.edge.production.min.js | 357 +++++------ .../cjs/react-dom-server.node.development.js | 458 ++++++-------- .../cjs/react-dom-server.node.production.js | 548 ++++++++++++++--- .../react-dom-server.node.production.min.js | 369 +++++------ .../react-dom-unstable_testing.development.js | 51 +- .../react-dom-unstable_testing.production.js | 51 +- ...act-dom-unstable_testing.production.min.js | 509 ++++++++-------- .../cjs/react-dom.development.js | 51 +- .../cjs/react-dom.production.js | 51 +- .../cjs/react-dom.production.min.js | 549 ++++++++--------- .../cjs/react-dom.profiling.js | 51 +- .../cjs/react-dom.profiling.min.js | 572 +++++++++--------- .../react-dom-experimental/package.json | 4 +- ...t-dom-server-legacy.browser.development.js | 418 ++++++------- ...ct-dom-server-legacy.browser.production.js | 515 +++++++++++++--- ...om-server-legacy.browser.production.min.js | 294 ++++----- ...eact-dom-server-legacy.node.development.js | 418 ++++++------- ...react-dom-server-legacy.node.production.js | 515 +++++++++++++--- ...t-dom-server-legacy.node.production.min.js | 349 +++++------ ...t-dom-server-rendering-stub.development.js | 2 +- ...ct-dom-server-rendering-stub.production.js | 2 +- ...om-server-rendering-stub.production.min.js | 2 +- .../react-dom-server.browser.development.js | 418 ++++++------- .../react-dom-server.browser.production.js | 515 +++++++++++++--- ...react-dom-server.browser.production.min.js | 322 +++++----- .../cjs/react-dom-server.bun.production.js | 515 +++++++++++++--- .../cjs/react-dom-server.edge.development.js | 418 ++++++------- .../cjs/react-dom-server.edge.production.js | 515 +++++++++++++--- .../react-dom-server.edge.production.min.js | 343 ++++++----- .../cjs/react-dom-server.node.development.js | 418 ++++++------- .../cjs/react-dom-server.node.production.js | 515 +++++++++++++--- .../react-dom-server.node.production.min.js | 338 ++++++----- .../react-dom/cjs/react-dom.development.js | 50 +- .../react-dom/cjs/react-dom.production.js | 50 +- .../react-dom/cjs/react-dom.production.min.js | 546 ++++++++--------- .../react-dom/cjs/react-dom.profiling.js | 50 +- .../react-dom/cjs/react-dom.profiling.min.js | 572 +++++++++--------- .../next/src/compiled/react-dom/package.json | 4 +- .../cjs/react-jsx-dev-runtime.development.js | 2 +- .../cjs/react-jsx-runtime.development.js | 2 +- .../cjs/react.development.js | 4 +- .../cjs/react.production.js | 2 +- .../cjs/react.production.min.js | 2 +- .../cjs/react.shared-subset.development.js | 4 +- .../cjs/react.shared-subset.production.js | 2 +- .../cjs/react.shared-subset.production.min.js | 2 +- ...r-dom-turbopack-server.edge.development.js | 31 +- ...er-dom-turbopack-server.edge.production.js | 12 +- ...om-turbopack-server.edge.production.min.js | 6 +- ...r-dom-turbopack-server.node.development.js | 21 + ...opack-server.node.unbundled.development.js | 21 + .../package.json | 4 +- ...r-dom-turbopack-server.edge.development.js | 10 +- ...er-dom-turbopack-server.edge.production.js | 12 +- ...om-turbopack-server.edge.production.min.js | 6 +- .../react-server-dom-turbopack/package.json | 4 +- ...ver-dom-webpack-server.edge.development.js | 31 +- ...rver-dom-webpack-server.edge.production.js | 12 +- ...-dom-webpack-server.edge.production.min.js | 6 +- ...ver-dom-webpack-server.node.development.js | 21 + ...bpack-server.node.unbundled.development.js | 21 + .../package.json | 4 +- ...ver-dom-webpack-server.edge.development.js | 10 +- ...rver-dom-webpack-server.edge.production.js | 12 +- ...-dom-webpack-server.edge.production.min.js | 6 +- .../react-server-dom-webpack/package.json | 4 +- .../cjs/react-jsx-dev-runtime.development.js | 2 +- .../cjs/react-jsx-runtime.development.js | 2 +- .../compiled/react/cjs/react.development.js | 4 +- .../compiled/react/cjs/react.production.js | 2 +- .../react/cjs/react.production.min.js | 2 +- .../cjs/react.shared-subset.development.js | 4 +- .../cjs/react.shared-subset.production.js | 2 +- .../cjs/react.shared-subset.production.min.js | 2 +- pnpm-lock.yaml | 104 ++-- 91 files changed, 10936 insertions(+), 6804 deletions(-) diff --git a/package.json b/package.json index 5ba42f4c6d35b4..9b27f17e3f3d76 100644 --- a/package.json +++ b/package.json @@ -194,16 +194,16 @@ "random-seed": "0.3.0", "react": "18.2.0", "react-17": "npm:react@17.0.2", - "react-builtin": "npm:react@18.3.0-canary-0cdfef19b-20231211", + "react-builtin": "npm:react@18.3.0-canary-f1039be4a-20240107", "react-dom": "18.2.0", "react-dom-17": "npm:react-dom@17.0.2", - "react-dom-builtin": "npm:react-dom@18.3.0-canary-0cdfef19b-20231211", - "react-dom-experimental-builtin": "npm:react-dom@0.0.0-experimental-0cdfef19b-20231211", - "react-experimental-builtin": "npm:react@0.0.0-experimental-0cdfef19b-20231211", - "react-server-dom-turbopack": "18.3.0-canary-0cdfef19b-20231211", - "react-server-dom-turbopack-experimental": "npm:react-server-dom-turbopack@0.0.0-experimental-0cdfef19b-20231211", - "react-server-dom-webpack": "18.3.0-canary-0cdfef19b-20231211", - "react-server-dom-webpack-experimental": "npm:react-server-dom-webpack@0.0.0-experimental-0cdfef19b-20231211", + "react-dom-builtin": "npm:react-dom@18.3.0-canary-f1039be4a-20240107", + "react-dom-experimental-builtin": "npm:react-dom@0.0.0-experimental-f1039be4a-20240107", + "react-experimental-builtin": "npm:react@0.0.0-experimental-f1039be4a-20240107", + "react-server-dom-turbopack": "18.3.0-canary-f1039be4a-20240107", + "react-server-dom-turbopack-experimental": "npm:react-server-dom-turbopack@0.0.0-experimental-f1039be4a-20240107", + "react-server-dom-webpack": "18.3.0-canary-f1039be4a-20240107", + "react-server-dom-webpack-experimental": "npm:react-server-dom-webpack@0.0.0-experimental-f1039be4a-20240107", "react-ssr-prepass": "1.0.8", "react-virtualized": "9.22.3", "relay-compiler": "13.0.2", @@ -213,8 +213,8 @@ "resolve-from": "5.0.0", "sass": "1.54.0", "satori": "0.10.9", - "scheduler-builtin": "npm:scheduler@0.24.0-canary-0cdfef19b-20231211", - "scheduler-experimental-builtin": "npm:scheduler@0.0.0-experimental-0cdfef19b-20231211", + "scheduler-builtin": "npm:scheduler@0.24.0-canary-f1039be4a-20240107", + "scheduler-experimental-builtin": "npm:scheduler@0.0.0-experimental-f1039be4a-20240107", "seedrandom": "3.0.5", "selenium-webdriver": "4.0.0-beta.4", "semver": "7.3.7", @@ -255,4 +255,4 @@ "pnpm": "8.14.0" }, "packageManager": "pnpm@8.14.0" -} +} \ No newline at end of file diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js index 706efae8ff8c59..2cf4461b3bb816 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js @@ -17,7 +17,7 @@ if (process.env.NODE_ENV !== "production") { var React = require("next/dist/compiled/react-experimental"); var ReactDOM = require('react-dom'); -var ReactVersion = '18.3.0-experimental-0cdfef19b-20231211'; +var ReactVersion = '18.3.0-experimental-f1039be4a-20240107'; var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; @@ -9078,7 +9078,7 @@ function createRequest(children, resumableState, renderState, rootFormatContext, false, false); // There is no parent so conceptually, we're unblocked to flush this segment. rootSegment.parentFlushed = true; - var rootTask = createRenderTask(request, null, children, -1, null, rootSegment, abortSet, null, rootFormatContext, emptyContextObject, rootContextSnapshot, emptyTreeContext); + var rootTask = createRenderTask(request, null, children, -1, null, rootSegment, abortSet, null, rootFormatContext, emptyContextObject, rootContextSnapshot, emptyTreeContext, null); pingedTasks.push(rootTask); return request; } @@ -9117,7 +9117,7 @@ function createSuspenseBoundary(request, fallbackAbortableTasks) { }; } -function createRenderTask(request, thenableState, node, childIndex, blockedBoundary, blockedSegment, abortSet, keyPath, formatContext, legacyContext, context, treeContext) { +function createRenderTask(request, thenableState, node, childIndex, blockedBoundary, blockedSegment, abortSet, keyPath, formatContext, legacyContext, context, treeContext, componentStack) { request.allPendingTasks++; if (blockedBoundary === null) { @@ -9141,18 +9141,14 @@ function createRenderTask(request, thenableState, node, childIndex, blockedBound legacyContext: legacyContext, context: context, treeContext: treeContext, + componentStack: componentStack, thenableState: thenableState }; - - { - task.componentStack = null; - } - abortSet.add(task); return task; } -function createReplayTask(request, thenableState, replay, node, childIndex, blockedBoundary, abortSet, keyPath, formatContext, legacyContext, context, treeContext) { +function createReplayTask(request, thenableState, replay, node, childIndex, blockedBoundary, abortSet, keyPath, formatContext, legacyContext, context, treeContext, componentStack) { request.allPendingTasks++; if (blockedBoundary === null) { @@ -9177,13 +9173,9 @@ function createReplayTask(request, thenableState, replay, node, childIndex, bloc legacyContext: legacyContext, context: context, treeContext: treeContext, + componentStack: componentStack, thenableState: thenableState }; - - { - task.componentStack = null; - } - abortSet.add(task); return task; } @@ -9217,51 +9209,54 @@ function getCurrentStackInDEV() { } } -function pushBuiltInComponentStackInDEV(task, type) { - { - task.componentStack = { - tag: 0, - parent: task.componentStack, - type: type - }; - } +function getStackFromNode(stackNode) { + return getStackByComponentStackNode(stackNode); } -function pushFunctionComponentStackInDEV(task, type) { - { - task.componentStack = { - tag: 1, - parent: task.componentStack, - type: type - }; - } +function createBuiltInComponentStack(task, type) { + return { + tag: 0, + parent: task.componentStack, + type: type + }; } -function pushClassComponentStackInDEV(task, type) { - { - task.componentStack = { - tag: 2, - parent: task.componentStack, - type: type - }; - } +function createFunctionComponentStack(task, type) { + return { + tag: 1, + parent: task.componentStack, + type: type + }; } -function popComponentStackInDEV(task) { - { - if (task.componentStack === null) { - error('Unexpectedly popped too many stack frames. This is a bug in React.'); - } else { - task.componentStack = task.componentStack.parent; - } - } -} // stash the component stack of an unwinding error until it is processed +function createClassComponentStack(task, type) { + return { + tag: 2, + parent: task.componentStack, + type: type + }; +} // While we track component stacks in prod all the time we only produce a reified stack in dev and +// during prerender in Prod. The reason for this is that the stack is useful for prerender where the timeliness +// of the request is less critical than the observability of the execution. For renders and resumes however we +// prioritize speed of the request. + +function getThrownInfo(request, node) { + if (node && ( // Always produce a stack in dev + true )) { + return { + componentStack: getStackFromNode(node) + }; + } else { + return {}; + } +} -var lastBoundaryErrorComponentStackDev = null; +function encodeErrorForBoundary(boundary, digest, error, thrownInfo) { + boundary.errorDigest = digest; -function captureBoundaryErrorDetailsDev(boundary, error) { { + // In dev we additionally encode the error message and component stack on the boundary var errorMessage; if (typeof error === 'string') { @@ -9273,27 +9268,30 @@ function captureBoundaryErrorDetailsDev(boundary, error) { errorMessage = String(error); } - var errorComponentStack = lastBoundaryErrorComponentStackDev || getCurrentStackInDEV(); - lastBoundaryErrorComponentStackDev = null; boundary.errorMessage = errorMessage; - boundary.errorComponentStack = errorComponentStack; + boundary.errorComponentStack = thrownInfo.componentStack; } } -function logPostpone(request, reason) { +function logPostpone(request, reason, postponeInfo) { // 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. - request.onPostpone(reason); + request.onPostpone(reason, postponeInfo); } -function logRecoverableError(request, error) { +function logRecoverableError(request, error$1, errorInfo) { // 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. - var errorDigest = request.onError(error); + var errorDigest = request.onError(error$1, errorInfo); if (errorDigest != null && typeof errorDigest !== 'string') { - // eslint-disable-next-line react-internal/prod-error-codes - throw new Error("onError returned something with a type other than \"string\". onError should return a string and may return null or undefined but must not return anything else. It received something of type \"" + typeof errorDigest + "\" instead"); + // We used to throw here but since this gets called from a variety of unprotected places it + // seems better to just warn and discard the returned value. + { + error('onError returned something with a type other than "string". onError should return a string and may return null or undefined but must not return anything else. It received something of type "%s" instead', typeof errorDigest); + } + + return; } return errorDigest; @@ -9337,7 +9335,10 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { var task = someTask; - pushBuiltInComponentStackInDEV(task, 'Suspense'); + var previousComponentStack = task.componentStack; // If we end up creating the fallback task we need it to have the correct stack which is + // the stack for the boundary itself. We stash it here so we can use it if needed later + + var suspenseComponentStack = task.componentStack = createBuiltInComponentStack(task, 'Suspense'); var prevKeyPath = task.keyPath; var parentBoundary = task.blockedBoundary; var parentSegment = task.blockedSegment; // Each time we enter a suspense boundary, we split out into a new segment for @@ -9391,35 +9392,32 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { queueCompletedSegment(newBoundary, contentRootSegment); if (newBoundary.pendingTasks === 0 && newBoundary.status === PENDING) { - newBoundary.status = COMPLETED; // This must have been the last segment we were waiting on. This boundary is now complete. + // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + newBoundary.status = COMPLETED; // We are returning early so we need to restore the - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; return; } } catch (error) { contentRootSegment.status = ERRORED; newBoundary.status = CLIENT_RENDERED; + var thrownInfo = getThrownInfo(request, task.componentStack); var errorDigest; if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { var postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, thrownInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, thrownInfo); } - newBoundary.errorDigest = errorDigest; - - { - captureBoundaryErrorDetailsDev(newBoundary, error); - } // We don't need to decrement any task numbers because we didn't spawn any new task. + encodeErrorForBoundary(newBoundary, errorDigest, error, thrownInfo); // We don't need to decrement any task numbers because we didn't spawn any new task. // We don't need to schedule any task because we know the parent has written yet. // We do need to fallthrough to create the fallback though. - } finally { { setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, parentBoundary ? parentBoundary.resources : null); @@ -9428,6 +9426,7 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { task.blockedBoundary = parentBoundary; task.blockedSegment = parentSegment; task.keyPath = prevKeyPath; + task.componentStack = previousComponentStack; } var fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]]; @@ -9451,20 +9450,19 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { // on it yet in case we finish the main content, so we queue for later. - var suspendedFallbackTask = createRenderTask(request, null, fallback, -1, parentBoundary, boundarySegment, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); - - { - suspendedFallbackTask.componentStack = task.componentStack; - } // TODO: This should be queued at a separate lower priority queue so that we only work + var suspendedFallbackTask = createRenderTask(request, null, fallback, -1, parentBoundary, boundarySegment, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // This stack should be the Suspense boundary stack because while the fallback is actually a child segment + // of the parent boundary from a component standpoint the fallback is a child of the Suspense boundary itself + suspenseComponentStack); // TODO: This should be queued at a separate lower priority queue so that we only work // on preparing fallbacks if we don't have any more main content to task on. - request.pingedTasks.push(suspendedFallbackTask); - popComponentStackInDEV(task); } function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, childSlots, fallbackNodes, fallbackSlots) { - pushBuiltInComponentStackInDEV(task, 'Suspense'); + var previousComponentStack = task.componentStack; // If we end up creating the fallback task we need it to have the correct stack which is + // the stack for the boundary itself. We stash it here so we can use it if needed later + + var suspenseComponentStack = task.componentStack = createBuiltInComponentStack(task, 'Suspense'); var prevKeyPath = task.keyPath; var previousReplaySet = task.replay; var parentBoundary = task.blockedBoundary; @@ -9500,33 +9498,31 @@ function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, c task.replay.pendingTasks--; if (resumedBoundary.pendingTasks === 0 && resumedBoundary.status === PENDING) { - resumedBoundary.status = COMPLETED; - request.completedBoundaries.push(resumedBoundary); // This must have been the last segment we were waiting on. This boundary is now complete. + // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + resumedBoundary.status = COMPLETED; + request.completedBoundaries.push(resumedBoundary); // We restore the parent componentStack. Semantically this is the same as + // popComponentStack(task) but we do this instead because it should be slightly + // faster - popComponentStackInDEV(task); return; } } catch (error) { resumedBoundary.status = CLIENT_RENDERED; + var thrownInfo = getThrownInfo(request, task.componentStack); var errorDigest; if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { var postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, thrownInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); - } - - resumedBoundary.errorDigest = errorDigest; - - { - captureBoundaryErrorDetailsDev(resumedBoundary, error); + errorDigest = logRecoverableError(request, error, thrownInfo); } + encodeErrorForBoundary(resumedBoundary, errorDigest, error, thrownInfo); task.replay.pendingTasks--; // The parent already flushed in the prerender so we need to schedule this to be emitted. request.clientRenderedBoundaries.push(resumedBoundary); // We don't need to decrement any task numbers because we didn't spawn any new task. @@ -9540,6 +9536,7 @@ function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, c task.blockedBoundary = parentBoundary; task.replay = previousReplaySet; task.keyPath = prevKeyPath; + task.componentStack = previousComponentStack; } var fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]]; // We create suspended task for the fallback because we don't want to actually work @@ -9550,20 +9547,17 @@ function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, c slots: fallbackSlots, pendingTasks: 0 }; - var suspendedFallbackTask = createReplayTask(request, null, fallbackReplay, fallback, -1, parentBoundary, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); - - { - suspendedFallbackTask.componentStack = task.componentStack; - } // TODO: This should be queued at a separate lower priority queue so that we only work + var suspendedFallbackTask = createReplayTask(request, null, fallbackReplay, fallback, -1, parentBoundary, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // This stack should be the Suspense boundary stack because while the fallback is actually a child segment + // of the parent boundary from a component standpoint the fallback is a child of the Suspense boundary itself + suspenseComponentStack); // TODO: This should be queued at a separate lower priority queue so that we only work // on preparing fallbacks if we don't have any more main content to task on. - request.pingedTasks.push(suspendedFallbackTask); - popComponentStackInDEV(task); } function renderHostElement(request, task, keyPath, type, props) { - pushBuiltInComponentStackInDEV(task, type); + var previousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, type); var segment = task.blockedSegment; if (segment === null) { @@ -9601,7 +9595,7 @@ function renderHostElement(request, task, keyPath, type, props) { segment.lastPushedText = false; } - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; } function shouldConstruct(Component) { @@ -9648,12 +9642,13 @@ function finishClassComponent(request, task, keyPath, instance, Component, props } function renderClassComponent(request, task, keyPath, Component, props) { - pushClassComponentStackInDEV(task, Component); + var previousComponentStack = task.componentStack; + task.componentStack = createClassComponentStack(task, Component); var maskedContext = getMaskedContext(Component, task.legacyContext) ; var instance = constructClassInstance(Component, props, maskedContext); mountClassInstance(instance, Component, props, maskedContext); finishClassComponent(request, task, keyPath, instance, Component, props); - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; } var didWarnAboutBadClass = {}; @@ -9674,7 +9669,8 @@ function renderIndeterminateComponent(request, task, keyPath, prevThenableState, legacyContext = getMaskedContext(Component, task.legacyContext); } - pushFunctionComponentStackInDEV(task, Component); + var previousComponentStack = task.componentStack; + task.componentStack = createFunctionComponentStack(task, Component); { if (Component.prototype && typeof Component.prototype.render === 'function') { @@ -9731,7 +9727,7 @@ function renderIndeterminateComponent(request, task, keyPath, prevThenableState, finishFunctionComponent(request, task, keyPath, value, hasId, formStateCount, formStateMatchingIndex); } - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; } function finishFunctionComponent(request, task, keyPath, children, hasId, formStateCount, formStateMatchingIndex) { @@ -9847,13 +9843,14 @@ function resolveDefaultProps(Component, baseProps) { } function renderForwardRef(request, task, keyPath, prevThenableState, type, props, ref) { - pushFunctionComponentStackInDEV(task, type.render); + var previousComponentStack = task.componentStack; + task.componentStack = createFunctionComponentStack(task, type.render); var children = renderWithHooks(request, task, keyPath, prevThenableState, type.render, props, ref); var hasId = checkDidRenderIdHook(); var formStateCount = getFormStateCount(); var formStateMatchingIndex = getFormStateMatchingIndex(); finishFunctionComponent(request, task, keyPath, children, hasId, formStateCount, formStateMatchingIndex); - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; } function renderMemo(request, task, keyPath, prevThenableState, type, props, ref) { @@ -9928,13 +9925,14 @@ function renderContextProvider(request, task, keyPath, type, props) { } function renderLazyComponent(request, task, keyPath, prevThenableState, lazyComponent, props, ref) { - pushBuiltInComponentStackInDEV(task, 'Lazy'); + var previousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, 'Lazy'); var payload = lazyComponent._payload; var init = lazyComponent._init; var Component = init(payload); var resolvedProps = resolveDefaultProps(Component, props); renderElement(request, task, keyPath, prevThenableState, Component, resolvedProps, ref); - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; } function renderOffscreen(request, task, keyPath, props) { @@ -9997,13 +9995,14 @@ function renderElement(request, task, keyPath, prevThenableState, type, props, r case REACT_SUSPENSE_LIST_TYPE: { - pushBuiltInComponentStackInDEV(task, 'SuspenseList'); // TODO: SuspenseList should control the boundaries. + var preiousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, 'SuspenseList'); // TODO: SuspenseList should control the boundaries. var _prevKeyPath3 = task.keyPath; task.keyPath = keyPath; renderNodeDestructive(request, task, null, props.children, -1); task.keyPath = _prevKeyPath3; - popComponentStackInDEV(task); + task.componentStack = preiousComponentStack; return; } @@ -10153,7 +10152,8 @@ function replayElement(request, task, keyPath, prevThenableState, name, keyOrInd // replay nodes which might be Suspense boundaries which are able to // absorb the error and we can still continue with siblings. - erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots); + var thrownInfo = getThrownInfo(request, task.componentStack); + erroredReplay(request, task.blockedBoundary, x, thrownInfo, childNodes, childSlots); } task.replay = replay; @@ -10199,32 +10199,13 @@ function validateIterable(iterable, iteratorFn) { didWarnAboutMaps = true; } } -} - -function renderNodeDestructive(request, task, // The thenable state reused from the previous attempt, if any. This is almost -// always null, except when called by retryTask. -prevThenableState, node, childIndex) { - { - // In Dev we wrap renderNodeDestructiveImpl in a try / catch so we can capture - // a component stack at the right place in the tree. We don't do this in renderNode - // becuase it is not called at every layer of the tree and we may lose frames - try { - return renderNodeDestructiveImpl(request, task, prevThenableState, node, childIndex); - } catch (x) { - if (typeof x === 'object' && x !== null && typeof x.then === 'function') ; else { - // This is an error, stash the component stack if it is null. - lastBoundaryErrorComponentStackDev = lastBoundaryErrorComponentStackDev !== null ? lastBoundaryErrorComponentStackDev : getCurrentStackInDEV(); - } // rethrow so normal suspense logic can handle thrown value accordingly - - - throw x; - } - } } // This function by it self renders a node and consumes the task by mutating it // to update the current execution state. -function renderNodeDestructiveImpl(request, task, prevThenableState, node, childIndex) { +function renderNodeDestructive(request, task, // The thenable state reused from the previous attempt, if any. This is almost +// always null, except when called by retryTask. +prevThenableState, node, childIndex) { if (task.replay !== null && typeof task.replay.slots === 'number') { // TODO: Figure out a cheaper place than this hot path to do this check. var resumeSegmentID = task.replay.slots; @@ -10266,26 +10247,15 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node, child case REACT_LAZY_TYPE: { + var previousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, 'Lazy'); var lazyNode = node; var payload = lazyNode._payload; var init = lazyNode._init; - var resolvedNode; + var resolvedNode = init(payload); // We restore the stack before rendering the resolved node because once the Lazy + // has resolved any future errors - { - try { - resolvedNode = init(payload); - } catch (x) { - if (typeof x === 'object' && x !== null && typeof x.then === 'function') { - // this Lazy initializer is suspending. push a temporary frame onto the stack so it can be - // popped off in spawnNewSuspendedTask. This aligns stack behavior between Lazy in element position - // vs Component position. We do not want the frame for Errors so we exclusively do this in - // the wakeable branch - pushBuiltInComponentStackInDEV(task, 'Lazy'); - } - - throw x; - } - } + task.componentStack = previousComponentStack; // Now we render the resolved node renderNodeDestructive(request, task, null, resolvedNode, childIndex); return; @@ -10343,12 +10313,12 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node, child if (typeof maybeUsable.then === 'function') { var thenable = maybeUsable; - return renderNodeDestructiveImpl(request, task, null, unwrapThenable(thenable), childIndex); + return renderNodeDestructive(request, task, null, unwrapThenable(thenable), childIndex); } if (maybeUsable.$$typeof === REACT_CONTEXT_TYPE || maybeUsable.$$typeof === REACT_SERVER_CONTEXT_TYPE) { var context = maybeUsable; - return renderNodeDestructiveImpl(request, task, null, readContext$1(context), childIndex); + return renderNodeDestructive(request, task, null, readContext$1(context), childIndex); } // $FlowFixMe[method-unbinding] @@ -10426,7 +10396,8 @@ function replayFragment(request, task, children, childIndex) { // absorb the error and we can still continue with siblings. // This is an error, stash the component stack if it is null. - erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots); + var thrownInfo = getThrownInfo(request, task.componentStack); + erroredReplay(request, task.blockedBoundary, x, thrownInfo, childNodes, childSlots); } task.replay = replay; // We finished rendering this node, so now we can consume this @@ -10627,8 +10598,8 @@ function trackPostpone(request, trackedPostpones, task, segment) { } } -function injectPostponedHole(request, task, reason) { - logPostpone(request, reason); // Something suspended, we'll need to create a new segment and resolve it later. +function injectPostponedHole(request, task, reason, thrownInfo) { + logPostpone(request, reason, thrownInfo); // Something suspended, we'll need to create a new segment and resolve it later. var segment = task.blockedSegment; var insertionIndex = segment.chunks.length; @@ -10642,16 +10613,9 @@ function injectPostponedHole(request, task, reason) { } function spawnNewSuspendedReplayTask(request, task, thenableState, x) { - var newTask = createReplayTask(request, thenableState, task.replay, task.node, task.childIndex, task.blockedBoundary, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); - - { - if (task.componentStack !== null) { - // We pop one task off the stack because the node that suspended will be tried again, - // which will add it back onto the stack. - newTask.componentStack = task.componentStack.parent; - } - } - + var newTask = createReplayTask(request, thenableState, task.replay, task.node, task.childIndex, task.blockedBoundary, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // We pop one task off the stack because the node that suspended will be tried again, + // which will add it back onto the stack. + task.componentStack !== null ? task.componentStack.parent : null); var ping = newTask.ping; x.then(ping, ping); } @@ -10666,16 +10630,9 @@ function spawnNewSuspendedRenderTask(request, task, thenableState, x) { segment.children.push(newSegment); // Reset lastPushedText for current Segment since the new Segment "consumed" it segment.lastPushedText = false; - var newTask = createRenderTask(request, thenableState, task.node, task.childIndex, task.blockedBoundary, newSegment, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); - - { - if (task.componentStack !== null) { - // We pop one task off the stack because the node that suspended will be tried again, - // which will add it back onto the stack. - newTask.componentStack = task.componentStack.parent; - } - } - + var newTask = createRenderTask(request, thenableState, task.node, task.childIndex, task.blockedBoundary, newSegment, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // We pop one task off the stack because the node that suspended will be tried again, + // which will add it back onto the stack. + task.componentStack !== null ? task.componentStack.parent : null); var ping = newTask.ping; x.then(ping, ping); } // This is a non-destructive form of rendering a node. If it suspends it spawns @@ -10690,12 +10647,7 @@ function renderNode(request, task, node, childIndex) { var previousContext = task.context; var previousKeyPath = task.keyPath; var previousTreeContext = task.treeContext; - var previousComponentStack = null; - - { - previousComponentStack = task.componentStack; - } - + var previousComponentStack = task.componentStack; var x; // Store how much we've pushed at this point so we can reset it in case something // suspended partially through writing something. @@ -10727,14 +10679,10 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; + task.componentStack = previousComponentStack; // Restore all active ReactContexts to what they were before. switchContext(previousContext); - - { - task.componentStack = previousComponentStack; - } - return; } } // TODO: Abort any undiscovered Suspense boundaries in the ReplayNode. @@ -10774,27 +10722,24 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; + task.componentStack = previousComponentStack; // Restore all active ReactContexts to what they were before. switchContext(previousContext); - - { - task.componentStack = previousComponentStack; - } - return; } - if (request.trackedPostpones !== null && x.$$typeof === REACT_POSTPONE_TYPE && task.blockedBoundary !== null // bubble if we're postponing in the shell + if (x.$$typeof === REACT_POSTPONE_TYPE && request.trackedPostpones !== null && task.blockedBoundary !== null // bubble if we're postponing in the shell ) { // If we're tracking postpones, we inject a hole here and continue rendering // sibling. Similar to suspending. If we're not tracking, we treat it more like // an error. Notably this doesn't spawn a new task since nothing will fill it // in during this prerender. - var postponeInstance = x; var trackedPostpones = request.trackedPostpones; + var postponeInstance = x; + var thrownInfo = getThrownInfo(request, task.componentStack); var postponedSegment = injectPostponedHole(request, task, // We don't use ReplayTasks in prerenders. - postponeInstance.message); + postponeInstance.message, thrownInfo); trackPostpone(request, trackedPostpones, task, postponedSegment); // Restore the context. We assume that this will be restored by the inner // functions in case nothing throws so we don't use "finally" here. @@ -10802,15 +10747,10 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; + task.componentStack = previousComponentStack; // Restore all active ReactContexts to what they were before. switchContext(previousContext); - - { - task.componentStack = previousComponentStack; - } - - lastBoundaryErrorComponentStackDev = null; return; } } @@ -10823,20 +10763,16 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; // We intentionally do not restore the component stack on the error pathway + // Whatever handles the error needs to use this stack which is the location of the + // error. We must restore the stack wherever we handle this + // Restore all active ReactContexts to what they were before. switchContext(previousContext); - - { - task.componentStack = previousComponentStack; - } // We assume that we don't need the correct context. - // Let's terminate the rest of the tree and don't render any siblings. - - throw x; } -function erroredReplay(request, boundary, error, replayNodes, resumeSlots) { +function erroredReplay(request, boundary, error, errorInfo, replayNodes, resumeSlots) { // Erroring during a replay doesn't actually cause an error by itself because // that component has already rendered. What causes the error is the resumable // points that we did not yet finish which will be below the point of the reset. @@ -10848,45 +10784,39 @@ function erroredReplay(request, boundary, error, replayNodes, resumeSlots) { if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { var postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, errorInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, errorInfo); } - abortRemainingReplayNodes(request, boundary, replayNodes, resumeSlots, error, errorDigest); + abortRemainingReplayNodes(request, boundary, replayNodes, resumeSlots, error, errorDigest, errorInfo); } -function erroredTask(request, boundary, error) { +function erroredTask(request, boundary, error, errorInfo) { // Report the error to a global handler. var errorDigest; if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { var postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, errorInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, errorInfo); } if (boundary === null) { - lastBoundaryErrorComponentStackDev = null; fatalError(request, error); } else { boundary.pendingTasks--; if (boundary.status !== CLIENT_RENDERED) { boundary.status = CLIENT_RENDERED; - boundary.errorDigest = errorDigest; - - { - captureBoundaryErrorDetailsDev(boundary, error); - } // Regardless of what happens next, this boundary won't be displayed, + encodeErrorForBoundary(boundary, errorDigest, error, errorInfo); // Regardless of what happens next, this boundary won't be displayed, // so we can flush it, if the parent already flushed. - if (boundary.parentFlushed) { // We don't have a preference where in the queue this goes since it's likely // to error on the client anyway. However, intentionally client-rendered @@ -10894,8 +10824,6 @@ function erroredTask(request, boundary, error) { // We reuse the same queue for errors. request.clientRenderedBoundaries.push(boundary); } - } else { - lastBoundaryErrorComponentStackDev = null; } } @@ -10920,17 +10848,16 @@ function abortTaskSoft(task) { } } -function abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDigest) { +function abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDigest, errorInfo) { var resumedBoundary = createSuspenseBoundary(request, new Set()); resumedBoundary.parentFlushed = true; // We restore the same id of this boundary as was used during prerender. resumedBoundary.rootSegmentID = rootSegmentID; resumedBoundary.status = CLIENT_RENDERED; - resumedBoundary.errorDigest = errorDigest; + var errorMessage = error; { var errorPrefix = 'The server did not finish this Suspense boundary: '; - var errorMessage; if (error && typeof error.message === 'string') { errorMessage = errorPrefix + error.message; @@ -10938,32 +10865,25 @@ function abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDige // eslint-disable-next-line react-internal/safe-string-coercion errorMessage = errorPrefix + String(error); } - - var previousTaskInDev = currentTaskInDEV; - currentTaskInDEV = null; - - try { - captureBoundaryErrorDetailsDev(resumedBoundary, errorMessage); - } finally { - currentTaskInDEV = previousTaskInDev; - } } + encodeErrorForBoundary(resumedBoundary, errorDigest, errorMessage, errorInfo); + if (resumedBoundary.parentFlushed) { request.clientRenderedBoundaries.push(resumedBoundary); } } -function abortRemainingReplayNodes(request, boundary, nodes, slots, error, errorDigest) { +function abortRemainingReplayNodes(request, boundary, nodes, slots, error, errorDigest, errorInfo) { for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.length === 4) { - abortRemainingReplayNodes(request, boundary, node[2], node[3], error, errorDigest); + abortRemainingReplayNodes(request, boundary, node[2], node[3], error, errorDigest, errorInfo); } else { var boundaryNode = node; var rootSegmentID = boundaryNode[5]; - abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDigest); + abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDigest, errorInfo); } } // Empty the set, since we've cleared it now. @@ -10977,11 +10897,7 @@ function abortRemainingReplayNodes(request, boundary, nodes, slots, error, error throw new Error('We should not have any resumable nodes in the shell. ' + 'This is a bug in React.'); } else if (boundary.status !== CLIENT_RENDERED) { boundary.status = CLIENT_RENDERED; - boundary.errorDigest = errorDigest; - - { - captureBoundaryErrorDetailsDev(boundary, error); - } + encodeErrorForBoundary(boundary, errorDigest, error, errorInfo); if (boundary.parentFlushed) { request.clientRenderedBoundaries.push(boundary); @@ -11008,13 +10924,15 @@ function abortTask(task, request, error) { } if (boundary === null) { + var errorInfo = {}; + if (request.status !== CLOSING && request.status !== CLOSED) { var replay = task.replay; if (replay === null) { // We didn't complete the root so we have nothing to show. We can close // the request; - logRecoverableError(request, error); + logRecoverableError(request, error, errorInfo); fatalError(request, error); return; } else { @@ -11024,8 +10942,8 @@ function abortTask(task, request, error) { replay.pendingTasks--; if (replay.pendingTasks === 0 && replay.nodes.length > 0) { - var errorDigest = logRecoverableError(request, error); - abortRemainingReplayNodes(request, null, replay.nodes, replay.slots, error, errorDigest); + var errorDigest = logRecoverableError(request, error, errorInfo); + abortRemainingReplayNodes(request, null, replay.nodes, replay.slots, error, errorDigest, errorInfo); } request.pendingRootTasks--; @@ -11039,12 +10957,17 @@ function abortTask(task, request, error) { boundary.pendingTasks--; if (boundary.status !== CLIENT_RENDERED) { - boundary.status = CLIENT_RENDERED; - boundary.errorDigest = logRecoverableError(request, error); + boundary.status = CLIENT_RENDERED; // We construct an errorInfo from the boundary's componentStack so the error in dev will indicate which + // boundary the message is referring to + + var _errorInfo = getThrownInfo(request, task.componentStack); + + var _errorDigest = logRecoverableError(request, error, _errorInfo); + + var errorMessage = error; { var errorPrefix = 'The server did not finish this Suspense boundary: '; - var errorMessage; if (error && typeof error.message === 'string') { errorMessage = errorPrefix + error.message; @@ -11052,17 +10975,10 @@ function abortTask(task, request, error) { // eslint-disable-next-line react-internal/safe-string-coercion errorMessage = errorPrefix + String(error); } - - var previousTaskInDev = currentTaskInDEV; - currentTaskInDEV = task; - - try { - captureBoundaryErrorDetailsDev(boundary, errorMessage); - } finally { - currentTaskInDEV = previousTaskInDev; - } } + encodeErrorForBoundary(boundary, _errorDigest, errorMessage, _errorInfo); + if (boundary.parentFlushed) { request.clientRenderedBoundaries.push(boundary); } @@ -11088,7 +11004,8 @@ function safelyEmitEarlyPreloads(request, shellComplete) { emitEarlyPreloads(request.renderState, request.resumableState, shellComplete); } catch (error) { // We assume preloads are optimistic and thus non-fatal if errored. - logRecoverableError(request, error); + var errorInfo = {}; + logRecoverableError(request, error, errorInfo); } } // I extracted this function out because we want to ensure we consistently emit preloads before // transitioning to the next request stage and this transition can happen in multiple places in this @@ -11298,17 +11215,18 @@ function retryRenderTask(request, task, segment) { var trackedPostpones = request.trackedPostpones; task.abortSet.delete(task); var postponeInstance = x; - logPostpone(request, postponeInstance.message); + var postponeInfo = getThrownInfo(request, task.componentStack); + logPostpone(request, postponeInstance.message, postponeInfo); trackPostpone(request, trackedPostpones, task, segment); finishedTask(request, task.blockedBoundary, segment); - lastBoundaryErrorComponentStackDev = null; return; } } + var errorInfo = getThrownInfo(request, task.componentStack); task.abortSet.delete(task); segment.status = ERRORED; - erroredTask(request, task.blockedBoundary, x); + erroredTask(request, task.blockedBoundary, x, errorInfo); return; } finally { { @@ -11377,7 +11295,8 @@ function retryReplayTask(request, task) { task.replay.pendingTasks--; task.abortSet.delete(task); - erroredReplay(request, task.blockedBoundary, x, task.replay.nodes, task.replay.slots); + var errorInfo = getThrownInfo(request, task.componentStack); + erroredReplay(request, task.blockedBoundary, x, errorInfo, task.replay.nodes, task.replay.slots); request.pendingRootTasks--; if (request.pendingRootTasks === 0) { @@ -11444,7 +11363,8 @@ function performWork(request) { flushCompletedQueues(request, request.destination); } } catch (error) { - logRecoverableError(request, error); + var errorInfo = {}; + logRecoverableError(request, error, errorInfo); fatalError(request, error); } finally { setCurrentResumableState(prevResumableState); @@ -11895,7 +11815,8 @@ function startFlowing(request, destination) { try { flushCompletedQueues(request, destination); } catch (error) { - logRecoverableError(request, error); + var errorInfo = {}; + logRecoverableError(request, error, errorInfo); fatalError(request, error); } } @@ -11919,7 +11840,8 @@ function abort(request, reason) { flushCompletedQueues(request, request.destination); } } catch (error) { - logRecoverableError(request, error); + var errorInfo = {}; + logRecoverableError(request, error, errorInfo); fatalError(request, error); } } diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.js index 8e67dbea1731a2..edf204ea3deccd 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.js @@ -26,7 +26,7 @@ function formatProdErrorMessage(code) { return "Minified React error #" + code + "; visit " + url + " for the full message or " + 'use the non-minified dev environment for full errors and additional ' + 'helpful warnings.'; } -var ReactVersion = '18.3.0-experimental-0cdfef19b-20231211'; +var ReactVersion = '18.3.0-experimental-f1039be4a-20240107'; // A pure JS implementation of a string hashing function. We do not use it for // security or obfuscation purposes, only to create compact hashes. So we @@ -5084,6 +5084,246 @@ function getComponentNameFromType(type) { return null; } +let prefix; +function describeBuiltInComponentFrame(name, source, ownerFn) { + { + if (prefix === undefined) { + // Extract the VM specific prefix used by each line. + try { + throw Error(); + } catch (x) { + const match = x.stack.trim().match(/\n( *(at )?)/); + prefix = match && match[1] || ''; + } + } // We use the prefix to ensure our stacks line up with native stack frames. + + + return '\n' + prefix + name; + } +} +let reentry = false; +let componentFrameCache; +/** + * Leverages native browser/VM stack frames to get proper details (e.g. + * filename, line + col number) for a single component in a component stack. We + * do this by: + * (1) throwing and catching an error in the function - this will be our + * control error. + * (2) calling the component which will eventually throw an error that we'll + * catch - this will be our sample error. + * (3) diffing the control and sample error stacks to find the stack frame + * which represents our component. + */ + + +function describeNativeComponentFrame(fn, construct) { + // If something asked for a stack inside a fake render, it should get ignored. + if (!fn || reentry) { + return ''; + } + + reentry = true; + const previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe[incompatible-type] It does accept undefined. + + Error.prepareStackTrace = undefined; + /** + * Finding a common stack frame between sample and control errors can be + * tricky given the different types and levels of stack trace truncation from + * different JS VMs. So instead we'll attempt to control what that common + * frame should be through this object method: + * Having both the sample and control errors be in the function under the + * `DescribeNativeComponentFrameRoot` property, + setting the `name` and + * `displayName` properties of the function ensures that a stack + * frame exists that has the method name `DescribeNativeComponentFrameRoot` in + * it for both control and sample stacks. + */ + + + const RunInRootFrame = { + DetermineComponentFrameRoot() { + let control; + + try { + // This should throw. + if (construct) { + // Something should be setting the props in the constructor. + const Fake = function () { + throw Error(); + }; // $FlowFixMe[prop-missing] + + + Object.defineProperty(Fake.prototype, 'props', { + set: function () { + // We use a throwing setter instead of frozen or non-writable props + // because that won't throw in a non-strict mode function. + throw Error(); + } + }); + + if (typeof Reflect === 'object' && Reflect.construct) { + // We construct a different control for this case to include any extra + // frames added by the construct call. + try { + Reflect.construct(Fake, []); + } catch (x) { + control = x; + } + + Reflect.construct(fn, [], Fake); + } else { + try { + Fake.call(); + } catch (x) { + control = x; + } // $FlowFixMe[prop-missing] found when upgrading Flow + + + fn.call(Fake.prototype); + } + } else { + try { + throw Error(); + } catch (x) { + control = x; + } // TODO(luna): This will currently only throw if the function component + // tries to access React/ReactDOM/props. We should probably make this throw + // in simple components too + + + const maybePromise = fn(); // If the function component returns a promise, it's likely an async + // component, which we don't yet support. Attach a noop catch handler to + // silence the error. + // TODO: Implement component stacks for async client components? + + if (maybePromise && typeof maybePromise.catch === 'function') { + maybePromise.catch(() => {}); + } + } + } catch (sample) { + // This is inlined manually because closure doesn't do it for us. + if (sample && control && typeof sample.stack === 'string') { + return [sample.stack, control.stack]; + } + } + + return [null, null]; + } + + }; // $FlowFixMe[prop-missing] + + RunInRootFrame.DetermineComponentFrameRoot.displayName = 'DetermineComponentFrameRoot'; + const namePropDescriptor = Object.getOwnPropertyDescriptor(RunInRootFrame.DetermineComponentFrameRoot, 'name'); // Before ES6, the `name` property was not configurable. + + if (namePropDescriptor && namePropDescriptor.configurable) { + // V8 utilizes a function's `name` property when generating a stack trace. + Object.defineProperty(RunInRootFrame.DetermineComponentFrameRoot, // Configurable properties can be updated even if its writable descriptor + // is set to `false`. + // $FlowFixMe[cannot-write] + 'name', { + value: 'DetermineComponentFrameRoot' + }); + } + + try { + const _RunInRootFrame$Deter = RunInRootFrame.DetermineComponentFrameRoot(), + sampleStack = _RunInRootFrame$Deter[0], + controlStack = _RunInRootFrame$Deter[1]; + + if (sampleStack && controlStack) { + // This extracts the first frame from the sample that isn't also in the control. + // Skipping one frame that we assume is the frame that calls the two. + const sampleLines = sampleStack.split('\n'); + const controlLines = controlStack.split('\n'); + let s = 0; + let c = 0; + + while (s < sampleLines.length && !sampleLines[s].includes('DetermineComponentFrameRoot')) { + s++; + } + + while (c < controlLines.length && !controlLines[c].includes('DetermineComponentFrameRoot')) { + c++; + } // We couldn't find our intentionally injected common root frame, attempt + // to find another common root frame by search from the bottom of the + // control stack... + + + if (s === sampleLines.length || c === controlLines.length) { + s = sampleLines.length - 1; + c = controlLines.length - 1; + + while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) { + // We expect at least one stack frame to be shared. + // Typically this will be the root most one. However, stack frames may be + // cut off due to maximum stack limits. In this case, one maybe cut off + // earlier than the other. We assume that the sample is longer or the same + // and there for cut off earlier. So we should find the root most frame in + // the sample somewhere in the control. + c--; + } + } + + for (; s >= 1 && c >= 0; s--, c--) { + // Next we find the first one that isn't the same which should be the + // frame that called our sample function and the control. + if (sampleLines[s] !== controlLines[c]) { + // In V8, the first line is describing the message but other VMs don't. + // If we're about to return the first line, and the control is also on the same + // line, that's a pretty good indicator that our sample threw at same line as + // the control. I.e. before we entered the sample frame. So we ignore this result. + // This can happen if you passed a class to function component, or non-function. + if (s !== 1 || c !== 1) { + do { + s--; + c--; // We may still have similar intermediate frames from the construct call. + // The next one that isn't the same should be our match though. + + if (c < 0 || sampleLines[s] !== controlLines[c]) { + // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier. + let frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "" + // but we have a user-provided "displayName" + // splice it in to make the stack more readable. + + if (fn.displayName && frame.includes('')) { + frame = frame.replace('', fn.displayName); + } + + if (false) ; // Return the line we found. + + + return frame; + } + } while (s >= 1 && c >= 0); + } + + break; + } + } + } + } finally { + reentry = false; + + Error.prepareStackTrace = previousPrepareStackTrace; + } // Fallback to just using the name if we couldn't make it throw. + + + const name = fn ? fn.displayName || fn.name : ''; + const syntheticFrame = name ? describeBuiltInComponentFrame(name) : ''; + + return syntheticFrame; +} + +function describeClassComponentFrame(ctor, source, ownerFn) { + { + return describeNativeComponentFrame(ctor, true); + } +} +function describeFunctionComponentFrame(fn, source, ownerFn) { + { + return describeNativeComponentFrame(fn, false); + } +} + const emptyContextObject = {}; function getMaskedContext(type, unmaskedContext) { @@ -6350,6 +6590,36 @@ const DefaultCacheDispatcher = { getCacheForType }; +function getStackByComponentStackNode(componentStack) { + try { + let info = ''; + let node = componentStack; + + do { + switch (node.tag) { + case 0: + info += describeBuiltInComponentFrame(node.type, null, null); + break; + + case 1: + info += describeFunctionComponentFrame(node.type, null, null); + break; + + case 2: + info += describeClassComponentFrame(node.type, null, null); + break; + } // $FlowFixMe[incompatible-type] we bail out when we get a null + + + node = node.parent; + } while (node); + + return info; + } catch (x) { + return '\nError generating stack: ' + x.message + '\n' + x.stack; + } +} + const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; const ReactCurrentCache = ReactSharedInternals.ReactCurrentCache; // The name might be minified but we assume that it's going to be the same generated name. Typically @@ -6428,7 +6698,7 @@ function createRequest(children, resumableState, renderState, rootFormatContext, false, false); // There is no parent so conceptually, we're unblocked to flush this segment. rootSegment.parentFlushed = true; - const rootTask = createRenderTask(request, null, children, -1, null, rootSegment, abortSet, null, rootFormatContext, emptyContextObject, rootContextSnapshot, emptyTreeContext); + const rootTask = createRenderTask(request, null, children, -1, null, rootSegment, abortSet, null, rootFormatContext, emptyContextObject, rootContextSnapshot, emptyTreeContext, null); pingedTasks.push(rootTask); return request; } @@ -6465,7 +6735,7 @@ function createSuspenseBoundary(request, fallbackAbortableTasks) { }; } -function createRenderTask(request, thenableState, node, childIndex, blockedBoundary, blockedSegment, abortSet, keyPath, formatContext, legacyContext, context, treeContext) { +function createRenderTask(request, thenableState, node, childIndex, blockedBoundary, blockedSegment, abortSet, keyPath, formatContext, legacyContext, context, treeContext, componentStack) { request.allPendingTasks++; if (blockedBoundary === null) { @@ -6487,14 +6757,14 @@ function createRenderTask(request, thenableState, node, childIndex, blockedBound legacyContext, context, treeContext, + componentStack, thenableState }; - abortSet.add(task); return task; } -function createReplayTask(request, thenableState, replay, node, childIndex, blockedBoundary, abortSet, keyPath, formatContext, legacyContext, context, treeContext) { +function createReplayTask(request, thenableState, replay, node, childIndex, blockedBoundary, abortSet, keyPath, formatContext, legacyContext, context, treeContext, componentStack) { request.allPendingTasks++; if (blockedBoundary === null) { @@ -6517,9 +6787,9 @@ function createReplayTask(request, thenableState, replay, node, childIndex, bloc legacyContext, context, treeContext, + componentStack, thenableState }; - abortSet.add(task); return task; } @@ -6540,23 +6810,68 @@ function createPendingSegment(request, index, boundary, parentFormatContext, las }; } // DEV-only global reference to the currently executing task -function popComponentStackInDEV(task) { -} // stash the component stack of an unwinding error until it is processed +function getStackFromNode(stackNode) { + return getStackByComponentStackNode(stackNode); +} -function logPostpone(request, reason) { +function createBuiltInComponentStack(task, type) { + return { + tag: 0, + parent: task.componentStack, + type + }; +} + +function createFunctionComponentStack(task, type) { + return { + tag: 1, + parent: task.componentStack, + type + }; +} + +function createClassComponentStack(task, type) { + return { + tag: 2, + parent: task.componentStack, + type + }; +} // While we track component stacks in prod all the time we only produce a reified stack in dev and +// during prerender in Prod. The reason for this is that the stack is useful for prerender where the timeliness +// of the request is less critical than the observability of the execution. For renders and resumes however we +// prioritize speed of the request. + + +function getThrownInfo(request, node) { + if (node && ( // Always produce a stack in dev + // Produce a stack in prod if we're in a prerender + request.trackedPostpones !== null)) { + return { + componentStack: getStackFromNode(node) + }; + } else { + return {}; + } +} + +function encodeErrorForBoundary(boundary, digest, error, thrownInfo) { + boundary.errorDigest = digest; +} + +function logPostpone(request, reason, postponeInfo) { // 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. - request.onPostpone(reason); + request.onPostpone(reason, postponeInfo); } -function logRecoverableError(request, error) { +function logRecoverableError(request, error, errorInfo) { // 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 errorDigest = request.onError(error); + const errorDigest = request.onError(error, errorInfo); if (errorDigest != null && typeof errorDigest !== 'string') { - // eslint-disable-next-line react-internal/prod-error-codes - throw new Error("onError returned something with a type other than \"string\". onError should return a string and may return null or undefined but must not return anything else. It received something of type \"" + typeof errorDigest + "\" instead"); + + return; } return errorDigest; @@ -6600,6 +6915,10 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { const task = someTask; + const previousComponentStack = task.componentStack; // If we end up creating the fallback task we need it to have the correct stack which is + // the stack for the boundary itself. We stash it here so we can use it if needed later + + const suspenseComponentStack = task.componentStack = createBuiltInComponentStack(task, 'Suspense'); const prevKeyPath = task.keyPath; const parentBoundary = task.blockedBoundary; const parentSegment = task.blockedSegment; // Each time we enter a suspense boundary, we split out into a new segment for @@ -6653,31 +6972,32 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { queueCompletedSegment(newBoundary, contentRootSegment); if (newBoundary.pendingTasks === 0 && newBoundary.status === PENDING) { - newBoundary.status = COMPLETED; // This must have been the last segment we were waiting on. This boundary is now complete. + // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + newBoundary.status = COMPLETED; // We are returning early so we need to restore the - popComponentStackInDEV(task); + task.componentStack = previousComponentStack; return; } } catch (error) { contentRootSegment.status = ERRORED; newBoundary.status = CLIENT_RENDERED; + const thrownInfo = getThrownInfo(request, task.componentStack); let errorDigest; if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { const postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, thrownInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, thrownInfo); } - newBoundary.errorDigest = errorDigest; + encodeErrorForBoundary(newBoundary, errorDigest); // We don't need to decrement any task numbers because we didn't spawn any new task. // We don't need to schedule any task because we know the parent has written yet. // We do need to fallthrough to create the fallback though. - } finally { { setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, parentBoundary ? parentBoundary.resources : null); @@ -6686,6 +7006,7 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { task.blockedBoundary = parentBoundary; task.blockedSegment = parentSegment; task.keyPath = prevKeyPath; + task.componentStack = previousComponentStack; } const fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]]; @@ -6709,14 +7030,19 @@ function renderSuspenseBoundary(request, someTask, keyPath, props) { // on it yet in case we finish the main content, so we queue for later. - const suspendedFallbackTask = createRenderTask(request, null, fallback, -1, parentBoundary, boundarySegment, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); + const suspendedFallbackTask = createRenderTask(request, null, fallback, -1, parentBoundary, boundarySegment, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // This stack should be the Suspense boundary stack because while the fallback is actually a child segment + // of the parent boundary from a component standpoint the fallback is a child of the Suspense boundary itself + suspenseComponentStack); // TODO: This should be queued at a separate lower priority queue so that we only work // on preparing fallbacks if we don't have any more main content to task on. - request.pingedTasks.push(suspendedFallbackTask); } function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, childSlots, fallbackNodes, fallbackSlots) { + const previousComponentStack = task.componentStack; // If we end up creating the fallback task we need it to have the correct stack which is + // the stack for the boundary itself. We stash it here so we can use it if needed later + + const suspenseComponentStack = task.componentStack = createBuiltInComponentStack(task, 'Suspense'); const prevKeyPath = task.keyPath; const previousReplaySet = task.replay; const parentBoundary = task.blockedBoundary; @@ -6752,29 +7078,31 @@ function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, c task.replay.pendingTasks--; if (resumedBoundary.pendingTasks === 0 && resumedBoundary.status === PENDING) { - resumedBoundary.status = COMPLETED; - request.completedBoundaries.push(resumedBoundary); // This must have been the last segment we were waiting on. This boundary is now complete. + // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + resumedBoundary.status = COMPLETED; + request.completedBoundaries.push(resumedBoundary); // We restore the parent componentStack. Semantically this is the same as + // popComponentStack(task) but we do this instead because it should be slightly + // faster - popComponentStackInDEV(task); return; } } catch (error) { resumedBoundary.status = CLIENT_RENDERED; + const thrownInfo = getThrownInfo(request, task.componentStack); let errorDigest; if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { const postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, thrownInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, thrownInfo); } - resumedBoundary.errorDigest = errorDigest; - + encodeErrorForBoundary(resumedBoundary, errorDigest); task.replay.pendingTasks--; // The parent already flushed in the prerender so we need to schedule this to be emitted. request.clientRenderedBoundaries.push(resumedBoundary); // We don't need to decrement any task numbers because we didn't spawn any new task. @@ -6788,6 +7116,7 @@ function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, c task.blockedBoundary = parentBoundary; task.replay = previousReplaySet; task.keyPath = prevKeyPath; + task.componentStack = previousComponentStack; } const fallbackKeyPath = [keyPath[0], 'Suspense Fallback', keyPath[2]]; // We create suspended task for the fallback because we don't want to actually work @@ -6798,14 +7127,17 @@ function replaySuspenseBoundary(request, task, keyPath, props, id, childNodes, c slots: fallbackSlots, pendingTasks: 0 }; - const suspendedFallbackTask = createReplayTask(request, null, fallbackReplay, fallback, -1, parentBoundary, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); + const suspendedFallbackTask = createReplayTask(request, null, fallbackReplay, fallback, -1, parentBoundary, fallbackAbortSet, fallbackKeyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // This stack should be the Suspense boundary stack because while the fallback is actually a child segment + // of the parent boundary from a component standpoint the fallback is a child of the Suspense boundary itself + suspenseComponentStack); // TODO: This should be queued at a separate lower priority queue so that we only work // on preparing fallbacks if we don't have any more main content to task on. - request.pingedTasks.push(suspendedFallbackTask); } function renderHostElement(request, task, keyPath, type, props) { + const previousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, type); const segment = task.blockedSegment; if (segment === null) { @@ -6841,6 +7173,8 @@ function renderHostElement(request, task, keyPath, type, props) { pushEndInstance(segment.chunks, type, props, request.resumableState, prevContext); segment.lastPushedText = false; } + + task.componentStack = previousComponentStack; } function shouldConstruct(Component) { @@ -6877,10 +7211,13 @@ function finishClassComponent(request, task, keyPath, instance, Component, props } function renderClassComponent(request, task, keyPath, Component, props) { + const previousComponentStack = task.componentStack; + task.componentStack = createClassComponentStack(task, Component); const maskedContext = getMaskedContext(Component, task.legacyContext) ; const instance = constructClassInstance(Component, props, maskedContext); mountClassInstance(instance, Component, props, maskedContext); finishClassComponent(request, task, keyPath, instance, Component); + task.componentStack = previousComponentStack; } // components for some reason. @@ -6891,6 +7228,9 @@ function renderIndeterminateComponent(request, task, keyPath, prevThenableState, legacyContext = getMaskedContext(Component, task.legacyContext); } + const previousComponentStack = task.componentStack; + task.componentStack = createFunctionComponentStack(task, Component); + const value = renderWithHooks(request, task, keyPath, prevThenableState, Component, props, legacyContext); const hasId = checkDidRenderIdHook(); const formStateCount = getFormStateCount(); @@ -6906,6 +7246,8 @@ function renderIndeterminateComponent(request, task, keyPath, prevThenableState, finishFunctionComponent(request, task, keyPath, value, hasId, formStateCount, formStateMatchingIndex); } + + task.componentStack = previousComponentStack; } function finishFunctionComponent(request, task, keyPath, children, hasId, formStateCount, formStateMatchingIndex) { @@ -6981,11 +7323,14 @@ function resolveDefaultProps(Component, baseProps) { } function renderForwardRef(request, task, keyPath, prevThenableState, type, props, ref) { + const previousComponentStack = task.componentStack; + task.componentStack = createFunctionComponentStack(task, type.render); const children = renderWithHooks(request, task, keyPath, prevThenableState, type.render, props, ref); const hasId = checkDidRenderIdHook(); const formStateCount = getFormStateCount(); const formStateMatchingIndex = getFormStateMatchingIndex(); finishFunctionComponent(request, task, keyPath, children, hasId, formStateCount, formStateMatchingIndex); + task.componentStack = previousComponentStack; } function renderMemo(request, task, keyPath, prevThenableState, type, props, ref) { @@ -7020,11 +7365,14 @@ function renderContextProvider(request, task, keyPath, type, props) { } function renderLazyComponent(request, task, keyPath, prevThenableState, lazyComponent, props, ref) { + const previousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, 'Lazy'); const payload = lazyComponent._payload; const init = lazyComponent._init; const Component = init(payload); const resolvedProps = resolveDefaultProps(Component, props); renderElement(request, task, keyPath, prevThenableState, Component, resolvedProps, ref); + task.componentStack = previousComponentStack; } function renderOffscreen(request, task, keyPath, props) { @@ -7087,11 +7435,14 @@ function renderElement(request, task, keyPath, prevThenableState, type, props, r case REACT_SUSPENSE_LIST_TYPE: { + const preiousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, 'SuspenseList'); // TODO: SuspenseList should control the boundaries. const prevKeyPath = task.keyPath; task.keyPath = keyPath; renderNodeDestructive(request, task, null, props.children, -1); task.keyPath = prevKeyPath; + task.componentStack = preiousComponentStack; return; } @@ -7235,7 +7586,8 @@ function replayElement(request, task, keyPath, prevThenableState, name, keyOrInd // replay nodes which might be Suspense boundaries which are able to // absorb the error and we can still continue with siblings. - erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots); + const thrownInfo = getThrownInfo(request, task.componentStack); + erroredReplay(request, task.blockedBoundary, x, thrownInfo, childNodes, childSlots); } task.replay = replay; @@ -7258,18 +7610,12 @@ function replayElement(request, task, keyPath, prevThenableState, name, keyOrInd // rendered in the prelude and skip it. } // $FlowFixMe[missing-local-annot] +// to update the current execution state. + function renderNodeDestructive(request, task, // The thenable state reused from the previous attempt, if any. This is almost // always null, except when called by retryTask. prevThenableState, node, childIndex) { - { - return renderNodeDestructiveImpl(request, task, prevThenableState, node, childIndex); - } -} // This function by it self renders a node and consumes the task by mutating it -// to update the current execution state. - - -function renderNodeDestructiveImpl(request, task, prevThenableState, node, childIndex) { if (task.replay !== null && typeof task.replay.slots === 'number') { // TODO: Figure out a cheaper place than this hot path to do this check. const resumeSegmentID = task.replay.slots; @@ -7311,14 +7657,15 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node, child case REACT_LAZY_TYPE: { + const previousComponentStack = task.componentStack; + task.componentStack = createBuiltInComponentStack(task, 'Lazy'); const lazyNode = node; const payload = lazyNode._payload; const init = lazyNode._init; - let resolvedNode; + const resolvedNode = init(payload); // We restore the stack before rendering the resolved node because once the Lazy + // has resolved any future errors - { - resolvedNode = init(payload); - } + task.componentStack = previousComponentStack; // Now we render the resolved node renderNodeDestructive(request, task, null, resolvedNode, childIndex); return; @@ -7373,12 +7720,12 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node, child if (typeof maybeUsable.then === 'function') { const thenable = maybeUsable; - return renderNodeDestructiveImpl(request, task, null, unwrapThenable(thenable), childIndex); + return renderNodeDestructive(request, task, null, unwrapThenable(thenable), childIndex); } if (maybeUsable.$$typeof === REACT_CONTEXT_TYPE || maybeUsable.$$typeof === REACT_SERVER_CONTEXT_TYPE) { const context = maybeUsable; - return renderNodeDestructiveImpl(request, task, null, readContext$1(context), childIndex); + return renderNodeDestructive(request, task, null, readContext$1(context), childIndex); } // $FlowFixMe[method-unbinding] @@ -7450,7 +7797,8 @@ function replayFragment(request, task, children, childIndex) { // absorb the error and we can still continue with siblings. // This is an error, stash the component stack if it is null. - erroredReplay(request, task.blockedBoundary, x, childNodes, childSlots); + const thrownInfo = getThrownInfo(request, task.componentStack); + erroredReplay(request, task.blockedBoundary, x, thrownInfo, childNodes, childSlots); } task.replay = replay; // We finished rendering this node, so now we can consume this @@ -7648,8 +7996,8 @@ function trackPostpone(request, trackedPostpones, task, segment) { } } -function injectPostponedHole(request, task, reason) { - logPostpone(request, reason); // Something suspended, we'll need to create a new segment and resolve it later. +function injectPostponedHole(request, task, reason, thrownInfo) { + logPostpone(request, reason, thrownInfo); // Something suspended, we'll need to create a new segment and resolve it later. const segment = task.blockedSegment; const insertionIndex = segment.chunks.length; @@ -7663,8 +8011,9 @@ function injectPostponedHole(request, task, reason) { } function spawnNewSuspendedReplayTask(request, task, thenableState, x) { - const newTask = createReplayTask(request, thenableState, task.replay, task.node, task.childIndex, task.blockedBoundary, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); - + const newTask = createReplayTask(request, thenableState, task.replay, task.node, task.childIndex, task.blockedBoundary, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // We pop one task off the stack because the node that suspended will be tried again, + // which will add it back onto the stack. + task.componentStack !== null ? task.componentStack.parent : null); const ping = newTask.ping; x.then(ping, ping); } @@ -7679,8 +8028,9 @@ function spawnNewSuspendedRenderTask(request, task, thenableState, x) { segment.children.push(newSegment); // Reset lastPushedText for current Segment since the new Segment "consumed" it segment.lastPushedText = false; - const newTask = createRenderTask(request, thenableState, task.node, task.childIndex, task.blockedBoundary, newSegment, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext); - + const newTask = createRenderTask(request, thenableState, task.node, task.childIndex, task.blockedBoundary, newSegment, task.abortSet, task.keyPath, task.formatContext, task.legacyContext, task.context, task.treeContext, // We pop one task off the stack because the node that suspended will be tried again, + // which will add it back onto the stack. + task.componentStack !== null ? task.componentStack.parent : null); const ping = newTask.ping; x.then(ping, ping); } // This is a non-destructive form of rendering a node. If it suspends it spawns @@ -7695,7 +8045,7 @@ function renderNode(request, task, node, childIndex) { const previousContext = task.context; const previousKeyPath = task.keyPath; const previousTreeContext = task.treeContext; - + const previousComponentStack = task.componentStack; let x; // Store how much we've pushed at this point so we can reset it in case something // suspended partially through writing something. @@ -7727,10 +8077,10 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; + task.componentStack = previousComponentStack; // Restore all active ReactContexts to what they were before. switchContext(previousContext); - return; } } // TODO: Abort any undiscovered Suspense boundaries in the ReplayNode. @@ -7768,23 +8118,24 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; + task.componentStack = previousComponentStack; // Restore all active ReactContexts to what they were before. switchContext(previousContext); - return; } - if (request.trackedPostpones !== null && x.$$typeof === REACT_POSTPONE_TYPE && task.blockedBoundary !== null // bubble if we're postponing in the shell + if (x.$$typeof === REACT_POSTPONE_TYPE && request.trackedPostpones !== null && task.blockedBoundary !== null // bubble if we're postponing in the shell ) { // If we're tracking postpones, we inject a hole here and continue rendering // sibling. Similar to suspending. If we're not tracking, we treat it more like // an error. Notably this doesn't spawn a new task since nothing will fill it // in during this prerender. - const postponeInstance = x; const trackedPostpones = request.trackedPostpones; + const postponeInstance = x; + const thrownInfo = getThrownInfo(request, task.componentStack); const postponedSegment = injectPostponedHole(request, task, // We don't use ReplayTasks in prerenders. - postponeInstance.message); + postponeInstance.message, thrownInfo); trackPostpone(request, trackedPostpones, task, postponedSegment); // Restore the context. We assume that this will be restored by the inner // functions in case nothing throws so we don't use "finally" here. @@ -7792,7 +8143,8 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; + task.componentStack = previousComponentStack; // Restore all active ReactContexts to what they were before. switchContext(previousContext); return; @@ -7807,16 +8159,16 @@ function renderNode(request, task, node, childIndex) { task.legacyContext = previousLegacyContext; task.context = previousContext; task.keyPath = previousKeyPath; - task.treeContext = previousTreeContext; // Restore all active ReactContexts to what they were before. + task.treeContext = previousTreeContext; // We intentionally do not restore the component stack on the error pathway + // Whatever handles the error needs to use this stack which is the location of the + // error. We must restore the stack wherever we handle this + // Restore all active ReactContexts to what they were before. switchContext(previousContext); - // Let's terminate the rest of the tree and don't render any siblings. - - throw x; } -function erroredReplay(request, boundary, error, replayNodes, resumeSlots) { +function erroredReplay(request, boundary, error, errorInfo, replayNodes, resumeSlots) { // Erroring during a replay doesn't actually cause an error by itself because // that component has already rendered. What causes the error is the resumable // points that we did not yet finish which will be below the point of the reset. @@ -7828,27 +8180,27 @@ function erroredReplay(request, boundary, error, replayNodes, resumeSlots) { if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { const postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, errorInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, errorInfo); } abortRemainingReplayNodes(request, boundary, replayNodes, resumeSlots, error, errorDigest); } -function erroredTask(request, boundary, error) { +function erroredTask(request, boundary, error, errorInfo) { // Report the error to a global handler. let errorDigest; if (typeof error === 'object' && error !== null && error.$$typeof === REACT_POSTPONE_TYPE) { const postponeInstance = error; - logPostpone(request, postponeInstance.message); // TODO: Figure out a better signal than a magic digest value. + logPostpone(request, postponeInstance.message, errorInfo); // TODO: Figure out a better signal than a magic digest value. errorDigest = 'POSTPONE'; } else { - errorDigest = logRecoverableError(request, error); + errorDigest = logRecoverableError(request, error, errorInfo); } if (boundary === null) { @@ -7858,10 +8210,9 @@ function erroredTask(request, boundary, error) { if (boundary.status !== CLIENT_RENDERED) { boundary.status = CLIENT_RENDERED; - boundary.errorDigest = errorDigest; + encodeErrorForBoundary(boundary, errorDigest); // Regardless of what happens next, this boundary won't be displayed, // so we can flush it, if the parent already flushed. - if (boundary.parentFlushed) { // We don't have a preference where in the queue this goes since it's likely // to error on the client anyway. However, intentionally client-rendered @@ -7893,20 +8244,21 @@ function abortTaskSoft(task) { } } -function abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDigest) { +function abortRemainingSuspenseBoundary(request, rootSegmentID, error, errorDigest, errorInfo) { const resumedBoundary = createSuspenseBoundary(request, new Set()); resumedBoundary.parentFlushed = true; // We restore the same id of this boundary as was used during prerender. resumedBoundary.rootSegmentID = rootSegmentID; resumedBoundary.status = CLIENT_RENDERED; - resumedBoundary.errorDigest = errorDigest; + + encodeErrorForBoundary(resumedBoundary, errorDigest); if (resumedBoundary.parentFlushed) { request.clientRenderedBoundaries.push(resumedBoundary); } } -function abortRemainingReplayNodes(request, boundary, nodes, slots, error, errorDigest) { +function abortRemainingReplayNodes(request, boundary, nodes, slots, error, errorDigest, errorInfo) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; @@ -7929,7 +8281,7 @@ function abortRemainingReplayNodes(request, boundary, nodes, slots, error, error throw Error(formatProdErrorMessage(487)); } else if (boundary.status !== CLIENT_RENDERED) { boundary.status = CLIENT_RENDERED; - boundary.errorDigest = errorDigest; + encodeErrorForBoundary(boundary, errorDigest); if (boundary.parentFlushed) { request.clientRenderedBoundaries.push(boundary); @@ -7956,13 +8308,15 @@ function abortTask(task, request, error) { } if (boundary === null) { + const errorInfo = {}; + if (request.status !== CLOSING && request.status !== CLOSED) { const replay = task.replay; if (replay === null) { // We didn't complete the root so we have nothing to show. We can close // the request; - logRecoverableError(request, error); + logRecoverableError(request, error, errorInfo); fatalError(request, error); return; } else { @@ -7972,7 +8326,7 @@ function abortTask(task, request, error) { replay.pendingTasks--; if (replay.pendingTasks === 0 && replay.nodes.length > 0) { - const errorDigest = logRecoverableError(request, error); + const errorDigest = logRecoverableError(request, error, errorInfo); abortRemainingReplayNodes(request, null, replay.nodes, replay.slots, error, errorDigest); } @@ -7987,8 +8341,13 @@ function abortTask(task, request, error) { boundary.pendingTasks--; if (boundary.status !== CLIENT_RENDERED) { - boundary.status = CLIENT_RENDERED; - boundary.errorDigest = logRecoverableError(request, error); + boundary.status = CLIENT_RENDERED; // We construct an errorInfo from the boundary's componentStack so the error in dev will indicate which + // boundary the message is referring to + + const errorInfo = getThrownInfo(request, task.componentStack); + const errorDigest = logRecoverableError(request, error, errorInfo); + + encodeErrorForBoundary(boundary, errorDigest); if (boundary.parentFlushed) { request.clientRenderedBoundaries.push(boundary); @@ -8013,7 +8372,8 @@ function safelyEmitEarlyPreloads(request, shellComplete) { emitEarlyPreloads(request.renderState, request.resumableState, shellComplete); } catch (error) { // We assume preloads are optimistic and thus non-fatal if errored. - logRecoverableError(request, error); + const errorInfo = {}; + logRecoverableError(request, error, errorInfo); } } // I extracted this function out because we want to ensure we consistently emit preloads before // transitioning to the next request stage and this transition can happen in multiple places in this @@ -8217,16 +8577,18 @@ function retryRenderTask(request, task, segment) { const trackedPostpones = request.trackedPostpones; task.abortSet.delete(task); const postponeInstance = x; - logPostpone(request, postponeInstance.message); + const postponeInfo = getThrownInfo(request, task.componentStack); + logPostpone(request, postponeInstance.message, postponeInfo); trackPostpone(request, trackedPostpones, task, segment); finishedTask(request, task.blockedBoundary, segment); return; } } + const errorInfo = getThrownInfo(request, task.componentStack); task.abortSet.delete(task); segment.status = ERRORED; - erroredTask(request, task.blockedBoundary, x); + erroredTask(request, task.blockedBoundary, x, errorInfo); return; } finally { { @@ -8285,7 +8647,8 @@ function retryReplayTask(request, task) { task.replay.pendingTasks--; task.abortSet.delete(task); - erroredReplay(request, task.blockedBoundary, x, task.replay.nodes, task.replay.slots); + const errorInfo = getThrownInfo(request, task.componentStack); + erroredReplay(request, task.blockedBoundary, x, errorInfo, task.replay.nodes, task.replay.slots); request.pendingRootTasks--; if (request.pendingRootTasks === 0) { @@ -8342,7 +8705,8 @@ function performWork(request) { flushCompletedQueues(request, request.destination); } } catch (error) { - logRecoverableError(request, error); + const errorInfo = {}; + logRecoverableError(request, error, errorInfo); fatalError(request, error); } finally { setCurrentResumableState(prevResumableState); @@ -8779,7 +9143,8 @@ function startFlowing(request, destination) { try { flushCompletedQueues(request, destination); } catch (error) { - logRecoverableError(request, error); + const errorInfo = {}; + logRecoverableError(request, error, errorInfo); fatalError(request, error); } } @@ -8801,7 +9166,8 @@ function abort(request, reason) { flushCompletedQueues(request, request.destination); } } catch (error) { - logRecoverableError(request, error); + const errorInfo = {}; + logRecoverableError(request, error, errorInfo); fatalError(request, error); } } diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.min.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.min.js index d6f7aa7b6fdafd..8dd258377fe477 100644 --- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.min.js +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.production.min.js @@ -29,180 +29,188 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -'use strict';var aa=require("next/dist/compiled/react-experimental"),da=require("react-dom");function q(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c>>16)&65535)<<16)&4294967295;f=f<<15|f>>>17;f=461845907*(f&65535)+((461845907*(f>>>16)&65535)<<16)&4294967295;e^=f;e=e<<13|e>>>19;e=5*(e&65535)+((5*(e>>>16)&65535)<<16)&4294967295;e=(e&65535)+27492+(((e>>>16)+58964&65535)<<16)}f=0;switch(c){case 3:f^=(a.charCodeAt(b+2)&255)<< +'use strict';var ba=require("next/dist/compiled/react-experimental"),ca=require("react-dom");function q(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c>>16)&65535)<<16)&4294967295;f=f<<15|f>>>17;f=461845907*(f&65535)+((461845907*(f>>>16)&65535)<<16)&4294967295;e^=f;e=e<<13|e>>>19;e=5*(e&65535)+((5*(e>>>16)&65535)<<16)&4294967295;e=(e&65535)+27492+(((e>>>16)+58964&65535)<<16)}f=0;switch(c){case 3:f^=(a.charCodeAt(b+2)&255)<< 16;case 2:f^=(a.charCodeAt(b+1)&255)<<8;case 1:f^=a.charCodeAt(b)&255,f=3432918353*(f&65535)+((3432918353*(f>>>16)&65535)<<16)&4294967295,f=f<<15|f>>>17,e^=461845907*(f&65535)+((461845907*(f>>>16)&65535)<<16)&4294967295}e^=a.length;e^=e>>>16;e=2246822507*(e&65535)+((2246822507*(e>>>16)&65535)<<16)&4294967295;e^=e>>>13;e=3266489909*(e&65535)+((3266489909*(e>>>16)&65535)<<16)&4294967295;return(e^e>>>16)>>>0} -var t=Object.assign,A=Object.prototype.hasOwnProperty,ja=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$"),ka={},wa={}; -function xa(a){if(A.call(wa,a))return!0;if(A.call(ka,a))return!1;if(ja.test(a))return wa[a]=!0;ka[a]=!0;return!1} -var ya=new Set("animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp".split(" ")),za= +var w=Object.assign,z=Object.prototype.hasOwnProperty,ha=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$"),ma={},na={}; +function oa(a){if(z.call(na,a))return!0;if(z.call(ma,a))return!1;if(ha.test(a))return na[a]=!0;ma[a]=!0;return!1} +var Aa=new Set("animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp".split(" ")),Ba= new Map([["acceptCharset","accept-charset"],["htmlFor","for"],["httpEquiv","http-equiv"],["crossOrigin","crossorigin"],["accentHeight","accent-height"],["alignmentBaseline","alignment-baseline"],["arabicForm","arabic-form"],["baselineShift","baseline-shift"],["capHeight","cap-height"],["clipPath","clip-path"],["clipRule","clip-rule"],["colorInterpolation","color-interpolation"],["colorInterpolationFilters","color-interpolation-filters"],["colorProfile","color-profile"],["colorRendering","color-rendering"], ["dominantBaseline","dominant-baseline"],["enableBackground","enable-background"],["fillOpacity","fill-opacity"],["fillRule","fill-rule"],["floodColor","flood-color"],["floodOpacity","flood-opacity"],["fontFamily","font-family"],["fontSize","font-size"],["fontSizeAdjust","font-size-adjust"],["fontStretch","font-stretch"],["fontStyle","font-style"],["fontVariant","font-variant"],["fontWeight","font-weight"],["glyphName","glyph-name"],["glyphOrientationHorizontal","glyph-orientation-horizontal"],["glyphOrientationVertical", "glyph-orientation-vertical"],["horizAdvX","horiz-adv-x"],["horizOriginX","horiz-origin-x"],["imageRendering","image-rendering"],["letterSpacing","letter-spacing"],["lightingColor","lighting-color"],["markerEnd","marker-end"],["markerMid","marker-mid"],["markerStart","marker-start"],["overlinePosition","overline-position"],["overlineThickness","overline-thickness"],["paintOrder","paint-order"],["panose-1","panose-1"],["pointerEvents","pointer-events"],["renderingIntent","rendering-intent"],["shapeRendering", "shape-rendering"],["stopColor","stop-color"],["stopOpacity","stop-opacity"],["strikethroughPosition","strikethrough-position"],["strikethroughThickness","strikethrough-thickness"],["strokeDasharray","stroke-dasharray"],["strokeDashoffset","stroke-dashoffset"],["strokeLinecap","stroke-linecap"],["strokeLinejoin","stroke-linejoin"],["strokeMiterlimit","stroke-miterlimit"],["strokeOpacity","stroke-opacity"],["strokeWidth","stroke-width"],["textAnchor","text-anchor"],["textDecoration","text-decoration"], ["textRendering","text-rendering"],["transformOrigin","transform-origin"],["underlinePosition","underline-position"],["underlineThickness","underline-thickness"],["unicodeBidi","unicode-bidi"],["unicodeRange","unicode-range"],["unitsPerEm","units-per-em"],["vAlphabetic","v-alphabetic"],["vHanging","v-hanging"],["vIdeographic","v-ideographic"],["vMathematical","v-mathematical"],["vectorEffect","vector-effect"],["vertAdvY","vert-adv-y"],["vertOriginX","vert-origin-x"],["vertOriginY","vert-origin-y"], -["wordSpacing","word-spacing"],["writingMode","writing-mode"],["xmlnsXlink","xmlns:xlink"],["xHeight","x-height"]]),Aa=/["'&<>]/; -function B(a){if("boolean"===typeof a||"number"===typeof a)return""+a;a=""+a;var b=Aa.exec(a);if(b){var c="",d,e=0;for(d=b.index;d")} -function Fb(a,b,c,d,e,f,g,h){var k=null;"function"===typeof d&&("function"===typeof d.$$FORM_ACTION?(e=Cb(b),b=d.$$FORM_ACTION(e),h=b.name,d=b.action||"",e=b.encType,f=b.method,g=b.target,k=b.data):(a.push(" ","formAction",'="',Db,'"'),g=f=e=d=h=null,Gb(b,c)));null!=h&&K(a,"name",h);null!=d&&K(a,"formAction",d);null!=e&&K(a,"formEncType",e);null!=f&&K(a,"formMethod",f);null!=g&&K(a,"formTarget",g);return k} -function K(a,b,c){switch(b){case "className":J(a,"class",c);break;case "tabIndex":J(a,"tabindex",c);break;case "dir":case "role":case "viewBox":case "width":case "height":J(a,b,c);break;case "style":qb(a,c);break;case "src":case "href":if(""===c)break;case "action":case "formAction":if(null==c||"function"===typeof c||"symbol"===typeof c||"boolean"===typeof c)break;a.push(" ",b,'="',B(""+c),'"');break;case "defaultValue":case "defaultChecked":case "innerHTML":case "suppressContentEditableWarning":case "suppressHydrationWarning":break; -case "autoFocus":case "multiple":case "muted":Bb(a,b.toLowerCase(),c);break;case "xlinkHref":if("function"===typeof c||"symbol"===typeof c||"boolean"===typeof c)break;a.push(" ","xlink:href",'="',B(""+c),'"');break;case "contentEditable":case "spellCheck":case "draggable":case "value":case "autoReverse":case "externalResourcesRequired":case "focusable":case "preserveAlpha":"function"!==typeof c&&"symbol"!==typeof c&&a.push(" ",b,'="',B(c),'"');break;case "allowFullScreen":case "async":case "autoPlay":case "controls":case "default":case "defer":case "disabled":case "disablePictureInPicture":case "disableRemotePlayback":case "formNoValidate":case "hidden":case "loop":case "noModule":case "noValidate":case "open":case "playsInline":case "readOnly":case "required":case "reversed":case "scoped":case "seamless":case "itemScope":c&& -"function"!==typeof c&&"symbol"!==typeof c&&a.push(" ",b,'=""');break;case "capture":case "download":!0===c?a.push(" ",b,'=""'):!1!==c&&"function"!==typeof c&&"symbol"!==typeof c&&a.push(" ",b,'="',B(c),'"');break;case "cols":case "rows":case "size":case "span":"function"!==typeof c&&"symbol"!==typeof c&&!isNaN(c)&&1<=c&&a.push(" ",b,'="',B(c),'"');break;case "rowSpan":case "start":"function"===typeof c||"symbol"===typeof c||isNaN(c)||a.push(" ",b,'="',B(c),'"');break;case "xlinkActuate":J(a,"xlink:actuate", -c);break;case "xlinkArcrole":J(a,"xlink:arcrole",c);break;case "xlinkRole":J(a,"xlink:role",c);break;case "xlinkShow":J(a,"xlink:show",c);break;case "xlinkTitle":J(a,"xlink:title",c);break;case "xlinkType":J(a,"xlink:type",c);break;case "xmlBase":J(a,"xml:base",c);break;case "xmlLang":J(a,"xml:lang",c);break;case "xmlSpace":J(a,"xml:space",c);break;default:if(!(2"))} -function Ib(a,b,c,d,e,f,g){var h=b.rel,k=b.href,l=b.precedence;if(3===f||g||null!=b.itemProp||"string"!==typeof h||"string"!==typeof k||""===k)return N(a,b),null;if("stylesheet"===b.rel){if("string"!==typeof l||null!=b.disabled||b.onLoad||b.onError)return N(a,b);f=d.styles.get(l);g=c.styleResources.hasOwnProperty(k)?c.styleResources[k]:void 0;null!==g?(c.styleResources[k]=null,f||(f={precedence:B(l),rules:[],hrefs:[],sheets:new Map},d.styles.set(l,f)),b={state:0,props:t({},b,{"data-precedence":b.precedence, -precedence:null})},g&&(2===g.length&&Jb(b.props,g),(c=d.preloads.stylesheets.get(k))&&0");return null}function Kb(a,b,c){a.push(O(c));for(var d in b)if(A.call(b,d)){var e=b[d];if(null!=e)switch(d){case "children":case "dangerouslySetInnerHTML":throw Error(q(399,c));default:K(a,d,e)}}a.push("/>");return null} -function Lb(a,b){a.push(O("title"));var c=null,d=null,e;for(e in b)if(A.call(b,e)){var f=b[e];if(null!=f)switch(e){case "children":c=f;break;case "dangerouslySetInnerHTML":d=f;break;default:K(a,e,f)}}a.push(">");b=Array.isArray(c)?2>c.length?c[0]:null:c;"function"!==typeof b&&"symbol"!==typeof b&&null!==b&&void 0!==b&&a.push(B(""+b));M(a,d,c);a.push(Mb("title"));return null} -function Nb(a,b){a.push(O("script"));var c=null,d=null,e;for(e in b)if(A.call(b,e)){var f=b[e];if(null!=f)switch(e){case "children":c=f;break;case "dangerouslySetInnerHTML":d=f;break;default:K(a,e,f)}}a.push(">");M(a,d,c);"string"===typeof c&&a.push(B(c));a.push(Mb("script"));return null} -function Ob(a,b,c){a.push(O(c));var d=c=null,e;for(e in b)if(A.call(b,e)){var f=b[e];if(null!=f)switch(e){case "children":c=f;break;case "dangerouslySetInnerHTML":d=f;break;default:K(a,e,f)}}a.push(">");M(a,d,c);return"string"===typeof c?(a.push(B(c)),null):c}var Pb=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,Qb=new Map;function O(a){var b=Qb.get(a);if(void 0===b){if(!Pb.test(a))throw Error(q(65,a));b="<"+a;Qb.set(a,b)}return b} -function Rb(a,b,c,d,e,f,g){switch(b){case "div":case "span":case "svg":case "path":case "a":case "g":case "p":case "li":break;case "select":a.push(O("select"));var h=null,k=null,l;for(l in c)if(A.call(c,l)){var n=c[l];if(null!=n)switch(l){case "children":h=n;break;case "dangerouslySetInnerHTML":k=n;break;case "defaultValue":case "value":break;default:K(a,l,n)}}a.push(">");M(a,k,h);return h;case "option":var r=f.selectedValue;a.push(O("option"));var m=null,x=null,D=null,P=null,w;for(w in c)if(A.call(c, -w)){var u=c[w];if(null!=u)switch(w){case "children":m=u;break;case "selected":D=u;break;case "dangerouslySetInnerHTML":P=u;break;case "value":x=u;default:K(a,w,u)}}if(null!=r){var p=null!==x?""+x:Hb(m);if(Ja(r))for(var H=0;H");M(a,P,m);return m;case "textarea":a.push(O("textarea"));var v=null,y=null,F=null,E;for(E in c)if(A.call(c,E)){var z=c[E];if(null!=z)switch(E){case "children":F= -z;break;case "value":v=z;break;case "defaultValue":y=z;break;case "dangerouslySetInnerHTML":throw Error(q(91));default:K(a,E,z)}}null===v&&null!==y&&(v=y);a.push(">");if(null!=F){if(null!=v)throw Error(q(92));if(Ja(F)){if(1");null!==sb&&sb.forEach(Eb,a);return null;case "button":a.push(O("button"));var na=null,oa=null,ba=null,pa=null,qa=null,Ua=null,ra=null,Va;for(Va in c)if(A.call(c,Va)){var ca=c[Va];if(null!=ca)switch(Va){case "children":na=ca;break;case "dangerouslySetInnerHTML":oa=ca;break;case "name":ba=ca;break;case "formAction":pa=ca;break;case "formEncType":qa=ca;break;case "formMethod":Ua=ca;break;case "formTarget":ra=ca;break;default:K(a,Va,ca)}}var Qc=Fb(a,d,e,pa,qa,Ua,ra,ba);a.push(">");null!== -Qc&&Qc.forEach(Eb,a);M(a,oa,na);if("string"===typeof na){a.push(B(na));var Rc=null}else Rc=na;return Rc;case "form":a.push(O("form"));var Wa=null,Sc=null,fa=null,Xa=null,Ya=null,Za=null,$a;for($a in c)if(A.call(c,$a)){var ha=c[$a];if(null!=ha)switch($a){case "children":Wa=ha;break;case "dangerouslySetInnerHTML":Sc=ha;break;case "action":fa=ha;break;case "encType":Xa=ha;break;case "method":Ya=ha;break;case "target":Za=ha;break;default:K(a,$a,ha)}}var Zb=null,$b=null;if("function"===typeof fa)if("function"=== -typeof fa.$$FORM_ACTION){var xe=Cb(d),Da=fa.$$FORM_ACTION(xe);fa=Da.action||"";Xa=Da.encType;Ya=Da.method;Za=Da.target;Zb=Da.data;$b=Da.name}else a.push(" ","action",'="',Db,'"'),Za=Ya=Xa=fa=null,Gb(d,e);null!=fa&&K(a,"action",fa);null!=Xa&&K(a,"encType",Xa);null!=Ya&&K(a,"method",Ya);null!=Za&&K(a,"target",Za);a.push(">");null!==$b&&(a.push('"),null!==Zb&&Zb.forEach(Eb,a));M(a,Sc,Wa);if("string"===typeof Wa){a.push(B(Wa));var Tc=null}else Tc=Wa;return Tc; -case "menuitem":a.push(O("menuitem"));for(var tb in c)if(A.call(c,tb)){var Uc=c[tb];if(null!=Uc)switch(tb){case "children":case "dangerouslySetInnerHTML":throw Error(q(400));default:K(a,tb,Uc)}}a.push(">");return null;case "title":if(3===f.insertionMode||f.tagScope&1||null!=c.itemProp)var Vc=Lb(a,c);else Lb(e.hoistableChunks,c),Vc=null;return Vc;case "link":return Ib(a,c,d,e,g,f.insertionMode,!!(f.tagScope&1));case "script":var ac=c.async;if("string"!==typeof c.src||!c.src||!ac||"function"===typeof ac|| -"symbol"===typeof ac||c.onLoad||c.onError||3===f.insertionMode||f.tagScope&1||null!=c.itemProp)var Wc=Nb(a,c);else{var ub=c.src;if("module"===c.type){var vb=d.moduleScriptResources;var Xc=e.preloads.moduleScripts}else vb=d.scriptResources,Xc=e.preloads.scripts;var wb=vb.hasOwnProperty(ub)?vb[ub]:void 0;if(null!==wb){vb[ub]=null;var bc=c;if(wb){2===wb.length&&(bc=t({},c),Jb(bc,wb));var Yc=Xc.get(ub);Yc&&(Yc.length=0)}var Zc=[];e.scripts.add(Zc);Nb(Zc,bc)}g&&a.push("\x3c!-- --\x3e");Wc=null}return Wc; -case "style":var xb=c.precedence,sa=c.href;if(3===f.insertionMode||f.tagScope&1||null!=c.itemProp||"string"!==typeof xb||"string"!==typeof sa||""===sa){a.push(O("style"));var Ea=null,$c=null,ab;for(ab in c)if(A.call(c,ab)){var yb=c[ab];if(null!=yb)switch(ab){case "children":Ea=yb;break;case "dangerouslySetInnerHTML":$c=yb;break;default:K(a,ab,yb)}}a.push(">");var bb=Array.isArray(Ea)?2>Ea.length?Ea[0]:null:Ea;"function"!==typeof bb&&"symbol"!==typeof bb&&null!==bb&&void 0!==bb&&a.push(B(""+bb));M(a, -$c,Ea);a.push(Mb("style"));var ad=null}else{var ta=e.styles.get(xb);if(null!==(d.styleResources.hasOwnProperty(sa)?d.styleResources[sa]:void 0)){d.styleResources[sa]=null;ta?ta.hrefs.push(B(sa)):(ta={precedence:B(xb),rules:[],hrefs:[B(sa)],sheets:new Map},e.styles.set(xb,ta));var bd=ta.rules,Fa=null,cd=null,zb;for(zb in c)if(A.call(c,zb)){var cc=c[zb];if(null!=cc)switch(zb){case "children":Fa=cc;break;case "dangerouslySetInnerHTML":cd=cc}}var cb=Array.isArray(Fa)?2>Fa.length?Fa[0]:null:Fa;"function"!== -typeof cb&&"symbol"!==typeof cb&&null!==cb&&void 0!==cb&&bd.push(B(""+cb));M(bd,cd,Fa)}ta&&e.boundaryResources&&e.boundaryResources.styles.add(ta);g&&a.push("\x3c!-- --\x3e");ad=void 0}return ad;case "meta":if(3===f.insertionMode||f.tagScope&1||null!=c.itemProp)var dd=Kb(a,c,"meta");else g&&a.push("\x3c!-- --\x3e"),dd="string"===typeof c.charSet?Kb(e.charsetChunks,c,"meta"):"viewport"===c.name?Kb(e.preconnectChunks,c,"meta"):Kb(e.hoistableChunks,c,"meta");return dd;case "listing":case "pre":a.push(O(b)); -var db=null,eb=null,fb;for(fb in c)if(A.call(c,fb)){var Ab=c[fb];if(null!=Ab)switch(fb){case "children":db=Ab;break;case "dangerouslySetInnerHTML":eb=Ab;break;default:K(a,fb,Ab)}}a.push(">");if(null!=eb){if(null!=db)throw Error(q(60));if("object"!==typeof eb||!("__html"in eb))throw Error(q(61));var ua=eb.__html;null!==ua&&void 0!==ua&&("string"===typeof ua&&0e.highImagePreloads.size)dc.delete(Ga),e.highImagePreloads.add(va)}else if(!d.imageResources.hasOwnProperty(Ga)){d.imageResources[Ga]=C;var ec=c.crossOrigin;var fd="string"===typeof ec?"use-credentials"===ec?ec:"":void 0;var Y=e.headers,fc;Y&&0Y.highImagePreloads.length)&&(fc=Sb(L,"image",{imageSrcSet:c.srcSet,imageSizes:c.sizes,crossOrigin:fd,integrity:c.integrity,nonce:c.nonce,type:c.type,fetchPriority:c.fetchPriority, -referrerPolicy:c.refererPolicy}),2<=(Y.remainingCapacity-=fc.length))?(e.resets.image[Ga]=C,Y.highImagePreloads&&(Y.highImagePreloads+=", "),Y.highImagePreloads+=fc):(va=[],N(va,{rel:"preload",as:"image",href:I?void 0:L,imageSrcSet:I,imageSizes:ed,crossOrigin:fd,integrity:c.integrity,type:c.type,fetchPriority:c.fetchPriority,referrerPolicy:c.referrerPolicy}),"high"===c.fetchPriority||10>e.highImagePreloads.size?e.highImagePreloads.add(va):(e.bulkPreloads.add(va),dc.set(Ga,va)))}}return Kb(a,c,"img"); -case "base":case "area":case "br":case "col":case "embed":case "hr":case "keygen":case "param":case "source":case "track":case "wbr":return Kb(a,c,b);case "annotation-xml":case "color-profile":case "font-face":case "font-face-src":case "font-face-uri":case "font-face-format":case "font-face-name":case "missing-glyph":break;case "head":if(2>f.insertionMode&&null===e.headChunks){e.headChunks=[];var gd=Ob(e.headChunks,c,"head")}else gd=Ob(a,c,"head");return gd;case "html":if(0===f.insertionMode&&null=== -e.htmlChunks){e.htmlChunks=[""];var hd=Ob(e.htmlChunks,c,"html")}else hd=Ob(a,c,"html");return hd;default:if(-1!==b.indexOf("-")){a.push(O(b));var gc=null,id=null,Ha;for(Ha in c)if(A.call(c,Ha)){var T=c[Ha];if(null!=T){var jd=Ha;switch(Ha){case "children":gc=T;break;case "dangerouslySetInnerHTML":id=T;break;case "style":qb(a,T);break;case "suppressContentEditableWarning":case "suppressHydrationWarning":break;case "className":jd="class";default:if(xa(Ha)&&"function"!==typeof T&&"symbol"!==typeof T&& -!1!==T){if(!0===T)T="";else if("object"===typeof T)continue;a.push(" ",jd,'="',B(T),'"')}}}}a.push(">");M(a,id,gc);return gc}}return Ob(a,c,b)}var Tb=new Map;function Mb(a){var b=Tb.get(a);void 0===b&&(b="",Tb.set(a,b));return b}function Ub(a,b){b=b.bootstrapChunks;for(var c=0;c')} -function Wb(a,b,c,d){switch(c.insertionMode){case 0:case 1:case 2:return a.push('