diff --git a/docs/advanced-features/compiler.md b/docs/advanced-features/compiler.md index 16d415a0a11d9c2..35acc704d1bad6f 100644 --- a/docs/advanced-features/compiler.md +++ b/docs/advanced-features/compiler.md @@ -233,6 +233,89 @@ module.exports = { If you have feedback about `swcMinify`, please share it on the [feedback discussion](https://github.com/vercel/next.js/discussions/30237). +### Modularize Imports + +Allows to modularize imports, similar to [babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports). + +Transforms member style imports: + +```js +import { Row, Grid as MyGrid } from 'react-bootstrap' +import { merge } from 'lodash' +``` + +...into default style imports: + +```js +import Row from 'react-bootstrap/lib/Row' +import MyGrid from 'react-bootstrap/lib/Grid' +import merge from 'lodash/merge' +``` + +Config for the above transform: + +```js +// next.config.js +module.exports = { + experimental: { + modularizeImports: { + 'react-bootstrap': { + transform: 'react-bootstrap/lib/{{member}}', + }, + lodash: { + transform: 'lodash/{{member}}', + }, + }, + }, +} +``` + +Advanced transformations: + +- Using regular expressions + +Similar to `babel-plugin-transform-imports`, but the transform is templated with [handlebars](https://docs.rs/handlebars) and regular expressions are in Rust [regex](https://docs.rs/regex/latest/regex/) crate's syntax. + +The config: + +```js +// next.config.js +module.exports = { + experimental: { + modularizeImports: { + 'my-library/?(((\\w*)?/?)*)': { + transform: 'my-library/{{ matches.[1] }}/{{member}}', + }, + }, + }, +} +``` + +Cause this code: + +```js +import { MyModule } from 'my-library' +import { App } from 'my-library/components' +import { Header, Footer } from 'my-library/components/App' +``` + +To become: + +```js +import MyModule from 'my-library/MyModule' +import App from 'my-library/components/App' +import Header from 'my-library/components/App/Header' +import Footer from 'my-library/components/App/Footer' +``` + +- Handlebars templating + +This transform uses [handlebars](https://docs.rs/handlebars) to template the replacement import path in the `transform` field. These variables and helper functions are available: + +1. `matches`: Has type `string[]`. All groups matched by the regular expression. `matches.[0]` is the full match. +2. `member`: Has type `string`. The name of the member import. +3. `lowerCase`, `upperCase`, `camelCase`: Helper functions to convert a string to lower, upper or camel cases. + ## Unsupported Features When your application has a `.babelrc` file, Next.js will automatically fall back to using Babel for transforming individual files. This ensures backwards compatibility with existing applications that leverage custom Babel plugins. diff --git a/docs/advanced-features/react-18/server-components.md b/docs/advanced-features/react-18/server-components.md index dea51f2014fd231..be4cea72957c379 100644 --- a/docs/advanced-features/react-18/server-components.md +++ b/docs/advanced-features/react-18/server-components.md @@ -7,7 +7,7 @@ Server Components allow us to render React components on the server. This is fun To use React Server Components, ensure you have React 18 installed: ```jsx -npm install next@latest react@rc react-dom@rc +npm install next@canary react@rc react-dom@rc ``` Then, update your `next.config.js`: diff --git a/docs/api-reference/data-fetching/get-static-props.md b/docs/api-reference/data-fetching/get-static-props.md index c5f34276ffa8a30..66d734a6f9fc5cc 100644 --- a/docs/api-reference/data-fetching/get-static-props.md +++ b/docs/api-reference/data-fetching/get-static-props.md @@ -85,7 +85,7 @@ Learn more about [Incremental Static Regeneration](/docs/basic-features/data-fet ### `notFound` -The `notFound` boolean allows the page to return a `404` status and [404 Page](/docs/advanced-features/custom-error-page.md#404-page). With `notFound: true`, the page will return a `404` even if there was a successfully generated page before. This is meant to support use cases like user-generated content getting removed by its author. +The `notFound` boolean allows the page to return a `404` status and [404 Page](/docs/advanced-features/custom-error-page.md#404-page). With `notFound: true`, the page will return a `404` even if there was a successfully generated page before. This is meant to support use cases like user-generated content getting removed by its author. Note, `notFound` follows the same `revalidate` behavior [described here](/docs/api-reference/data-fetching/get-static-props.md#revalidate) ```js export async function getStaticProps(context) { diff --git a/docs/api-reference/next.config.js/rewrites.md b/docs/api-reference/next.config.js/rewrites.md index 47571668dfb2fa2..946dec86981c7c2 100644 --- a/docs/api-reference/next.config.js/rewrites.md +++ b/docs/api-reference/next.config.js/rewrites.md @@ -323,7 +323,7 @@ module.exports = { } ``` -If you're using `trailingSlash: true`, you also need to insert a trailing slash in the `source` paramater. If the destination server is also expecting a trailing slash it should be included in the `destination` parameter as well. +If you're using `trailingSlash: true`, you also need to insert a trailing slash in the `source` parameter. If the destination server is also expecting a trailing slash it should be included in the `destination` parameter as well. ```js module.exports = { diff --git a/docs/basic-features/data-fetching/get-server-side-props.md b/docs/basic-features/data-fetching/get-server-side-props.md index da0463f1fecd5e2..b09a70d0e8a8701 100644 --- a/docs/basic-features/data-fetching/get-server-side-props.md +++ b/docs/basic-features/data-fetching/get-server-side-props.md @@ -21,7 +21,7 @@ export async function getServerSideProps(context) { - When you request this page directly, `getServerSideProps` runs at request time, and this page will be pre-rendered with the returned props - When you request this page on client-side page transitions through [`next/link`](/docs/api-reference/next/link.md) or [`next/router`](/docs/api-reference/next/router.md), Next.js sends an API request to the server, which runs `getServerSideProps` -It then returns `JSON` that contains the result of running `getServerSideProps`, that `JSON` will be used to render the page. All this work will be handled automatically by Next.js, so you don’t need to do anything extra as long as you have `getServerSideProps` defined. +`getServerSideProps` returns JSON which will be used to render the page. All this work will be handled automatically by Next.js, so you don’t need to do anything extra as long as you have `getServerSideProps` defined. You can use the [next-code-elimination tool](https://next-code-elimination.vercel.app/) to verify what Next.js eliminates from the client-side bundle. diff --git a/docs/basic-features/script.md b/docs/basic-features/script.md index 70839f6a457bd2f..1f9806415d43dd4 100644 --- a/docs/basic-features/script.md +++ b/docs/basic-features/script.md @@ -25,7 +25,7 @@ description: Next.js helps you optimize loading third-party scripts with the bui -The Next.js Script component, [`next/script`](/docs/api-reference/next/script.md), is an extension of the HTML `'); +var startScriptSrc = stringToPrecomputedChunk(''); // Allows us to keep track of what we've already written so we can refer back to it. + +var textSeparator = stringToPrecomputedChunk(''); + +var styleAttributeStart = stringToPrecomputedChunk(' style="'); +var styleAssign = stringToPrecomputedChunk(':'); +var styleSeparator = stringToPrecomputedChunk(';'); + +var attributeSeparator = stringToPrecomputedChunk(' '); +var attributeAssign = stringToPrecomputedChunk('="'); +var attributeEnd = stringToPrecomputedChunk('"'); +var attributeEmptyString = stringToPrecomputedChunk('=""'); + +var endOfStartTag = stringToPrecomputedChunk('>'); +var endOfStartTagSelfClosing = stringToPrecomputedChunk('/>'); + +var selectedMarkerAttribute = stringToPrecomputedChunk(' selected=""'); + +var leadingNewline = stringToPrecomputedChunk('\n'); + +var DOCTYPE = stringToPrecomputedChunk(''); +var endTag1 = stringToPrecomputedChunk(''); +// A placeholder is a node inside a hidden partial tree that can be filled in later, but before +// display. It's never visible to users. We use the template tag because it can be used in every +// type of parent. '); +var completeBoundaryScript1Full = stringToPrecomputedChunk(completeBoundaryFunction + ';$RC("'); +var completeBoundaryScript1Partial = stringToPrecomputedChunk('$RC("'); +var completeBoundaryScript2 = stringToPrecomputedChunk('","'); +var completeBoundaryScript3 = stringToPrecomputedChunk('")'); +var clientRenderScript1Full = stringToPrecomputedChunk(clientRenderFunction + ';$RX("'); +var clientRenderScript1Partial = stringToPrecomputedChunk('$RX("'); +var clientRenderScript2 = stringToPrecomputedChunk('")'); + +var rendererSigil; + +{ + // Use this to detect multiple renderers using the same context + rendererSigil = {}; +} // Used to store the parent path of all context overrides in a shared linked list. +// Forming a reverse tree. + + +var rootContextSnapshot = null; // We assume that this runtime owns the "current" field on all ReactContext instances. +// This global (actually thread local) state represents what state all those "current", +// fields are currently in. + +var currentActiveSnapshot = null; + +function popNode(prev) { + { + prev.context._currentValue = prev.parentValue; + } +} + +function pushNode(next) { + { + next.context._currentValue = next.value; + } +} + +function popToNearestCommonAncestor(prev, next) { + if (prev === next) ; else { + popNode(prev); + var parentPrev = prev.parent; + var parentNext = next.parent; + + if (parentPrev === null) { + if (parentNext !== null) { + throw new Error('The stacks must reach the root at the same time. This is a bug in React.'); + } + } else { + if (parentNext === null) { + throw new Error('The stacks must reach the root at the same time. This is a bug in React.'); + } + + popToNearestCommonAncestor(parentPrev, parentNext); // On the way back, we push the new ones that weren't common. + + pushNode(next); + } + } +} + +function popAllPrevious(prev) { + popNode(prev); + var parentPrev = prev.parent; + + if (parentPrev !== null) { + popAllPrevious(parentPrev); + } +} + +function pushAllNext(next) { + var parentNext = next.parent; + + if (parentNext !== null) { + pushAllNext(parentNext); + } + + pushNode(next); +} + +function popPreviousToCommonLevel(prev, next) { + popNode(prev); + var parentPrev = prev.parent; + + if (parentPrev === null) { + throw new Error('The depth must equal at least at zero before reaching the root. This is a bug in React.'); + } + + if (parentPrev.depth === next.depth) { + // We found the same level. Now we just need to find a shared ancestor. + popToNearestCommonAncestor(parentPrev, next); + } else { + // We must still be deeper. + popPreviousToCommonLevel(parentPrev, next); + } +} + +function popNextToCommonLevel(prev, next) { + var parentNext = next.parent; + + if (parentNext === null) { + throw new Error('The depth must equal at least at zero before reaching the root. This is a bug in React.'); + } + + if (prev.depth === parentNext.depth) { + // We found the same level. Now we just need to find a shared ancestor. + popToNearestCommonAncestor(prev, parentNext); + } else { + // We must still be deeper. + popNextToCommonLevel(prev, parentNext); + } + + pushNode(next); +} // Perform context switching to the new snapshot. +// To make it cheap to read many contexts, while not suspending, we make the switch eagerly by +// updating all the context's current values. That way reads, always just read the current value. +// At the cost of updating contexts even if they're never read by this subtree. + + +function switchContext(newSnapshot) { + // The basic algorithm we need to do is to pop back any contexts that are no longer on the stack. + // We also need to update any new contexts that are now on the stack with the deepest value. + // The easiest way to update new contexts is to just reapply them in reverse order from the + // perspective of the backpointers. To avoid allocating a lot when switching, we use the stack + // for that. Therefore this algorithm is recursive. + // 1) First we pop which ever snapshot tree was deepest. Popping old contexts as we go. + // 2) Then we find the nearest common ancestor from there. Popping old contexts as we go. + // 3) Then we reapply new contexts on the way back up the stack. + var prev = currentActiveSnapshot; + var next = newSnapshot; + + if (prev !== next) { + if (prev === null) { + // $FlowFixMe: This has to be non-null since it's not equal to prev. + pushAllNext(next); + } else if (next === null) { + popAllPrevious(prev); + } else if (prev.depth === next.depth) { + popToNearestCommonAncestor(prev, next); + } else if (prev.depth > next.depth) { + popPreviousToCommonLevel(prev, next); + } else { + popNextToCommonLevel(prev, next); + } + + currentActiveSnapshot = next; + } +} +function pushProvider(context, nextValue) { + var prevValue; + + { + prevValue = context._currentValue; + context._currentValue = nextValue; + + { + if (context._currentRenderer !== undefined && context._currentRenderer !== null && context._currentRenderer !== rendererSigil) { + error('Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.'); + } + + context._currentRenderer = rendererSigil; + } + } + + var prevNode = currentActiveSnapshot; + var newNode = { + parent: prevNode, + depth: prevNode === null ? 0 : prevNode.depth + 1, + context: context, + parentValue: prevValue, + value: nextValue + }; + currentActiveSnapshot = newNode; + return newNode; +} +function popProvider() { + var prevSnapshot = currentActiveSnapshot; + + if (prevSnapshot === null) { + throw new Error('Tried to pop a Context at the root of the app. This is a bug in React.'); + } + + { + var value = prevSnapshot.parentValue; + + if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { + prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue; + } else { + prevSnapshot.context._currentValue = value; + } + } + + return currentActiveSnapshot = prevSnapshot.parent; +} +function getActiveContext() { + return currentActiveSnapshot; +} +function readContext(context) { + var value = context._currentValue ; + return value; +} + +function readContext$1(context) { + { + if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { + error('Only ServerContext is supported in Flight'); + } + + if (currentCache === null) { + error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().'); + } + } + + return readContext(context); +} + +var Dispatcher = { + useMemo: function (nextCreate) { + return nextCreate(); + }, + useCallback: function (callback) { + return callback; + }, + useDebugValue: function () {}, + useDeferredValue: unsupportedHook, + useTransition: unsupportedHook, + getCacheForType: function (resourceType) { + if (!currentCache) { + throw new Error('Reading the cache is only supported while rendering.'); + } + + var entry = currentCache.get(resourceType); + + if (entry === undefined) { + entry = resourceType(); // TODO: Warn if undefined? + + currentCache.set(resourceType, entry); + } + + return entry; + }, + readContext: readContext$1, + useContext: readContext$1, + useReducer: unsupportedHook, + useRef: unsupportedHook, + useState: unsupportedHook, + useInsertionEffect: unsupportedHook, + useLayoutEffect: unsupportedHook, + useImperativeHandle: unsupportedHook, + useEffect: unsupportedHook, + useId: unsupportedHook, + useMutableSource: unsupportedHook, + useSyncExternalStore: unsupportedHook, + useCacheRefresh: function () { + return unsupportedRefresh; + } +}; + +function unsupportedHook() { + throw new Error('This Hook is not supported in Server Components.'); +} + +function unsupportedRefresh() { + if (!currentCache) { + throw new Error('Refreshing the cache is not supported in Server Components.'); + } +} + +var currentCache = null; +function setCurrentCache(cache) { + currentCache = cache; + return currentCache; +} +function getCurrentCache() { + return currentCache; +} + +var ContextRegistry = ReactSharedInternals.ContextRegistry; +function getOrCreateServerContext(globalName) { + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = React.createServerContext(globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED); + } + + return ContextRegistry[globalName]; +} + var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; function defaultErrorHandler(error) { @@ -185,7 +957,7 @@ function defaultErrorHandler(error) { var OPEN = 0; var CLOSING = 1; var CLOSED = 2; -function createRequest(model, bundlerConfig, onError) { +function createRequest(model, bundlerConfig, onError, context) { var pingedSegments = []; var request = { status: OPEN, @@ -201,17 +973,25 @@ function createRequest(model, bundlerConfig, onError) { completedErrorChunks: [], writtenSymbols: new Map(), writtenModules: new Map(), + writtenProviders: new Map(), onError: onError === undefined ? defaultErrorHandler : onError, toJSON: function (key, value) { return resolveModelToJSON(request, this, key, value); } }; request.pendingChunks++; - var rootSegment = createSegment(request, model); + var rootContext = createRootContext(context); + var rootSegment = createSegment(request, model, rootContext); pingedSegments.push(rootSegment); return request; } +function createRootContext(reqContext) { + return importServerContexts(reqContext); +} + +var POP = {}; + function attemptResolveElement(type, key, ref, props) { if (ref !== null && ref !== undefined) { // When the ref moves to the regular props object this will implicitly @@ -245,6 +1025,14 @@ function attemptResolveElement(type, key, ref, props) { } switch (type.$$typeof) { + case REACT_LAZY_TYPE: + { + var payload = type._payload; + var init = type._init; + var wrappedType = init(payload); + return attemptResolveElement(wrappedType, key, ref, props); + } + case REACT_FORWARD_REF_TYPE: { var render = type.render; @@ -255,6 +1043,32 @@ function attemptResolveElement(type, key, ref, props) { { return attemptResolveElement(type.type, key, ref, props); } + + case REACT_PROVIDER_TYPE: + { + pushProvider(type._context, props.value); + + { + var extraKeys = Object.keys(props).filter(function (value) { + if (value === 'children' || value === 'value') { + return false; + } + + return true; + }); + + if (extraKeys.length !== 0) { + error('ServerContext can only have a value prop and children. Found: %s', JSON.stringify(extraKeys)); + } + } + + return [REACT_ELEMENT_TYPE, type, key, // Rely on __popProvider being serialized last to pop the provider. + { + value: props.value, + children: props.children, + __pop: POP + }]; + } } } @@ -272,11 +1086,12 @@ function pingSegment(request, segment) { } } -function createSegment(request, model) { +function createSegment(request, model, context) { var id = request.nextChunkId++; var segment = { id: id, model: model, + context: context, ping: function () { return pingSegment(request, segment); } @@ -305,8 +1120,7 @@ function escapeStringValue(value) { function isObjectPrototype(object) { if (!object) { return false; - } // $FlowFixMe - + } var ObjectPrototype = Object.prototype; @@ -405,8 +1219,7 @@ function describeValueForErrorMessage(value) { function describeObjectForErrorMessage(objectOrArray, expandedName) { if (isArray(objectOrArray)) { - var str = '['; // $FlowFixMe: Should be refined by now. - + var str = '['; var array = objectOrArray; for (var i = 0; i < array.length; i++) { @@ -431,8 +1244,7 @@ function describeObjectForErrorMessage(objectOrArray, expandedName) { str += ']'; return str; } else { - var _str = '{'; // $FlowFixMe: Should be refined by now. - + var _str = '{'; var object = objectOrArray; var names = Object.keys(object); @@ -462,6 +1274,8 @@ function describeObjectForErrorMessage(objectOrArray, expandedName) { } } +var insideContextProps = null; +var isInsideContextValue = false; function resolveModelToJSON(request, parent, key, value) { { // $FlowFixMe @@ -476,29 +1290,55 @@ function resolveModelToJSON(request, parent, key, value) { switch (value) { case REACT_ELEMENT_TYPE: return '$'; + } - case REACT_LAZY_TYPE: - throw new Error('React Lazy Components are not yet supported on the server.'); + { + if (parent[0] === REACT_ELEMENT_TYPE && parent[1] && parent[1].$$typeof === REACT_PROVIDER_TYPE && key === '3') { + insideContextProps = value; + } else if (insideContextProps === parent && key === 'value') { + isInsideContextValue = true; + } else if (insideContextProps === parent && key === 'children') { + isInsideContextValue = false; + } } // Resolve server components. - while (typeof value === 'object' && value !== null && value.$$typeof === REACT_ELEMENT_TYPE) { - // TODO: Concatenate keys of parents onto children. - var element = value; + while (typeof value === 'object' && value !== null && (value.$$typeof === REACT_ELEMENT_TYPE || value.$$typeof === REACT_LAZY_TYPE)) { + { + if (isInsideContextValue) { + error('React elements are not allowed in ServerContext'); + } + } try { - // Attempt to render the server component. - value = attemptResolveElement(element.type, element.key, element.ref, element.props); + switch (value.$$typeof) { + case REACT_ELEMENT_TYPE: + { + // TODO: Concatenate keys of parents onto children. + var element = value; // Attempt to render the server component. + + value = attemptResolveElement(element.type, element.key, element.ref, element.props); + break; + } + + case REACT_LAZY_TYPE: + { + var payload = value._payload; + var init = value._init; + value = init(payload); + break; + } + } } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended, we'll need to create a new segment and resolve it later. request.pendingChunks++; - var newSegment = createSegment(request, value); + var newSegment = createSegment(request, value, getActiveContext()); var ping = newSegment.ping; x.then(ping, ping); return serializeByRefID(newSegment.id); } else { - reportError(request, x); // Something errored. We'll still send everything we have up until this point. + logRecoverableError(request, x); // Something errored. We'll still send everything we have up until this point. // We'll replace this element with a lazy reference that throws on the client // once it gets rendered. @@ -559,6 +1399,28 @@ function resolveModelToJSON(request, parent, key, value) { emitErrorChunk(request, _errorId, x); return serializeByValueID(_errorId); } + } else if (value.$$typeof === REACT_PROVIDER_TYPE) { + var providerKey = value._context._globalName; + var writtenProviders = request.writtenProviders; + var providerId = writtenProviders.get(key); + + if (providerId === undefined) { + request.pendingChunks++; + providerId = request.nextChunkId++; + writtenProviders.set(providerKey, providerId); + emitProviderChunk(request, providerId, providerKey); + } + + return serializeByValueID(providerId); + } else if (value === POP) { + popProvider(); + + { + insideContextProps = null; + isInsideContextValue = false; + } + + return undefined; } { @@ -627,7 +1489,7 @@ function resolveModelToJSON(request, parent, key, value) { throw new Error("Type " + typeof value + " is not supported in client component props. " + ("Remove " + describeKeyForErrorMessage(key) + " from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent))); } -function reportError(request, error) { +function logRecoverableError(request, error) { var onError = request.onError; onError(error); } @@ -677,7 +1539,14 @@ function emitSymbolChunk(request, id, name) { request.completedModuleChunks.push(processedChunk); } +function emitProviderChunk(request, id, contextName) { + var processedChunk = processProviderChunk(request, id, contextName); + request.completedJSONChunks.push(processedChunk); +} + function retrySegment(request, segment) { + switchContext(segment.context); + try { var _value3 = segment.model; @@ -700,7 +1569,7 @@ function retrySegment(request, segment) { x.then(ping, ping); return; } else { - reportError(request, x); // This errored, we need to serialize this error to the + logRecoverableError(request, x); // This errored, we need to serialize this error to the emitErrorChunk(request, segment.id, x); } @@ -709,9 +1578,9 @@ function retrySegment(request, segment) { function performWork(request) { var prevDispatcher = ReactCurrentDispatcher.current; - var prevCache = currentCache; + var prevCache = getCurrentCache(); ReactCurrentDispatcher.current = Dispatcher; - currentCache = request.cache; + setCurrentCache(request.cache); try { var pingedSegments = request.pingedSegments; @@ -726,15 +1595,16 @@ function performWork(request) { flushCompletedChunks(request, request.destination); } } catch (error) { - reportError(request, error); + logRecoverableError(request, error); fatalError(request, error); } finally { ReactCurrentDispatcher.current = prevDispatcher; - currentCache = prevCache; + setCurrentCache(prevCache); } } function flushCompletedChunks(request, destination) { + beginWriting(); try { // We emit module chunks first in the stream so that @@ -745,8 +1615,9 @@ function flushCompletedChunks(request, destination) { for (; i < moduleChunks.length; i++) { request.pendingChunks--; var chunk = moduleChunks[i]; + var keepWriting = writeChunkAndReturn(destination, chunk); - if (!writeChunk(destination, chunk)) { + if (!keepWriting) { request.destination = null; i++; break; @@ -762,7 +1633,9 @@ function flushCompletedChunks(request, destination) { request.pendingChunks--; var _chunk = jsonChunks[i]; - if (!writeChunk(destination, _chunk)) { + var _keepWriting = writeChunkAndReturn(destination, _chunk); + + if (!_keepWriting) { request.destination = null; i++; break; @@ -780,7 +1653,9 @@ function flushCompletedChunks(request, destination) { request.pendingChunks--; var _chunk2 = errorChunks[i]; - if (!writeChunk(destination, _chunk2)) { + var _keepWriting2 = writeChunkAndReturn(destination, _chunk2); + + if (!_keepWriting2) { request.destination = null; i++; break; @@ -789,6 +1664,7 @@ function flushCompletedChunks(request, destination) { errorChunks.splice(0, i); } finally { + completeWriting(destination); } if (request.pendingChunks === 0) { @@ -813,83 +1689,51 @@ function startFlowing(request, destination) { return; } + if (request.destination !== null) { + // We're already flowing. + return; + } + request.destination = destination; try { flushCompletedChunks(request, destination); } catch (error) { - reportError(request, error); + logRecoverableError(request, error); fatalError(request, error); } } -function unsupportedHook() { - throw new Error('This Hook is not supported in Server Components.'); -} - -function unsupportedRefresh() { - if (!currentCache) { - throw new Error('Refreshing the cache is not supported in Server Components.'); - } -} - -var currentCache = null; -var Dispatcher = { - useMemo: function (nextCreate) { - return nextCreate(); - }, - useCallback: function (callback) { - return callback; - }, - useDebugValue: function () {}, - useDeferredValue: unsupportedHook, - useTransition: unsupportedHook, - getCacheForType: function (resourceType) { - if (!currentCache) { - throw new Error('Reading the cache is only supported while rendering.'); - } - - var entry = currentCache.get(resourceType); - - if (entry === undefined) { - entry = resourceType(); // TODO: Warn if undefined? - - currentCache.set(resourceType, entry); +function importServerContexts(contexts) { + if (contexts) { + var prevContext = getActiveContext(); + switchContext(rootContextSnapshot); + + for (var i = 0; i < contexts.length; i++) { + var _contexts$i = contexts[i], + name = _contexts$i[0], + _value4 = _contexts$i[1]; + var context = getOrCreateServerContext(name); + pushProvider(context, _value4); } - return entry; - }, - readContext: unsupportedHook, - useContext: unsupportedHook, - useReducer: unsupportedHook, - useRef: unsupportedHook, - useState: unsupportedHook, - useInsertionEffect: unsupportedHook, - useLayoutEffect: unsupportedHook, - useImperativeHandle: unsupportedHook, - useEffect: unsupportedHook, - useId: unsupportedHook, - useMutableSource: unsupportedHook, - useSyncExternalStore: unsupportedHook, - useCacheRefresh: function () { - return unsupportedRefresh; + var importedContext = getActiveContext(); + switchContext(prevContext); + return importedContext; } -}; -function renderToReadableStream(model, webpackMap, options) { - var request = createRequest(model, webpackMap, options ? options.onError : undefined); + return rootContextSnapshot; +} + +function renderToReadableStream(model, webpackMap, options, context) { + var request = createRequest(model, webpackMap, options ? options.onError : undefined, context); var stream = new ReadableStream({ + type: 'bytes', start: function (controller) { startWork(request); }, pull: function (controller) { - // Pull is called immediately even if the stream is not passed to anything. - // That's buffering too early. We want to start buffering once the stream - // is actually used by something so we can give it the best result possible - // at that point. - if (stream.locked) { - startFlowing(request, controller); - } + startFlowing(request, controller); }, cancel: function (reason) {} }); diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js index 2977e6eea4d4f99..bb235427138f69e 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js @@ -1,4 +1,5 @@ -/** @license React vundefined +/** + * @license React * react-server-dom-webpack-writer.browser.production.min.server.js * * Copyright (c) Facebook, Inc. and its affiliates. @@ -6,21 +7,39 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict';var h=require("react");function n(a,d){a.enqueue(d);return 0=a.length?a:a.substr(0,10)+"...");case "object":if(B(a))return"[...]";a=N(a);return"Object"===a?"{...}":a;case "function":return"function";default:return String(a)}} -function P(a,d){if(B(a)){for(var c="[",b=0;b