From a5b7c77c1ff0096af7609a8bfc1e064d30db4e30 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Thu, 7 Sep 2023 17:51:49 +0200 Subject: [PATCH] perf: add bundled rendering runtimes (#52997) ## What? In Next, rendering a route involves 3 layers: - the routing layer, which will direct the request to the correct route to render - the rendering layer, which will take a route and render it appropriately - the user layer, which contains the user code In #51831, in order to optimise the boot time of Next.js, I introduced a change that allowed the routing layer to be bundled. In this PR, I'm doing the same for the rendering layer. This is building up on @wyattjoh's work that initially split the routing and the rendering layer into separate entry-points. The benefits of having this approach is that this allows us to compartmentalise the different part of Next, optimise them individually and making sure that serving a request is as efficient as possible, e.g. rendering a `pages` route should not need code from the `app router` to be used. There are now 4 different rendering runtimes, depending on the route type: - app pages: for App Router pages - app routes: for App Router route handlers - pages: for legacy pages - pages api: for legacy API routes This change should be transparent to the end user, beside faster cold boots. ## Notable changes Doing this change required a lot of changes for Next.js under the hood in order to make the different layers play well together. ### New conventions for externals/shared modules The big issue of bundling the rendering runtimes is that the user code needs to be able to reference an instance of a module/value created in Next during the render. This is the case when the user wants to access the router context during SSR via `next/link` for example; when you call `useContext(value)` the value needs to be the exact same reference to one as the one created by `createContext` earlier. Previously, we were handling this case by making all files from Next that were affected by this `externals`, meaning that we were marking them not to be bundled. **Why not keep it this way?** The goal of this PR as stated previously was to make the rendering process as efficient as possible, so I really wanted to avoid extraneous fs reads to unoptimised code. In order to "fix" it, I introduced two new conventions to the codebase: - all files that explicitly need to be shared between a rendering runtime and the user code must be suffixed by `.shared-runtime` and exposed via adding a reference in the relevant `externals` file. At compilation time, a reference to a file ending with this will get re-written to the appropriate runtime. - all files that need to be truly externals need to be suffixed by `.external`. At compilation time, a reference to it will stay as-is. This special case is needed mostly only for the async local storages that need to be shared with all three layers of Next. As a side effect, we should be bundling more of the Next code in the user bundles, so it should be slightly more efficient. ### App route handlers are compiled on their own layer App route handlers should be compiled in their own layer, this allows us to separate more cleanly the compilation logic here (we don't need to run the RSC logic for example). ### New rendering bundles We now generate a prod and a dev bundle for: - the routing server - the app/pages SSR rendering process - the API routes process The development bundle is needed because: - there is code in Next that relies on NODE_ENV - because we opt out of the logic referencing the correct rendering runtime in dev for a `shared-runtime` file. This is because we don't need to and that Turbopack does not support rewriting an external to something that looks like this `require('foo').bar.baz` yet. We will need to fix that when Turbopack build ships. ### New development pipeline Bundling Next is now required when developing on the repo so I extended the taskfile setup to account for that. The webpack config for Next itself lives in `webpack.config.js` and contains the logic for all the new bundles generated. ### Misc changes There are some misc reshuffling in the code to better use the tree shaking abilities that we can now use. fixes NEXT-1573 Co-authored-by: Alex Kirszenberg <1621758+alexkirsz@users.noreply.github.com> --- .eslintrc.json | 5 +- bench/basic-app/app/api/app/route.js | 5 + bench/basic-app/app/layout.js | 12 + bench/basic-app/app/page.js | 7 + bench/basic-app/next.config.js | 5 + bench/basic-app/pages/api/index.js | 3 + bench/basic-app/pages/pages/index.js | 7 + .../js/src/entry/app-edge-renderer.tsx | 2 + .../next-core/js/src/entry/app-renderer.tsx | 8 +- .../next-core/js/src/entry/app/hydrate.tsx | 2 +- .../js/src/internal/page-server-handler.tsx | 6 +- .../crates/next-core/src/app_source.rs | 17 +- .../crates/next-core/src/next_edge/context.rs | 7 +- .../src/next_edge/route_transition.rs | 16 +- .../crates/next-core/src/next_import_map.rs | 66 ++- .../next-core/src/next_server/context.rs | 16 +- .../next-core/src/next_shared/resolve.rs | 51 +- ...File(__q____q____q____star__0__-3e4dd8.txt | 17 + ...FileSync(__q____q____q____star_-e11df4.txt | 15 + .../Error during SSR Rendering-8ad1c9.txt | 47 ++ .../Error during SSR Rendering-d9114a.txt | 6 - .../next/import/conditions/input/app/test.js | 4 +- ...rror resolving commonjs request-b2593b.txt | 13 - ...rror resolving commonjs request-dd84e7.txt | 13 - ...File(__q____q____q____star__0__-76c34b.txt | 17 + ...FileSync(__q____q____q____star_-f7e52c.txt | 15 + packages/next/config.d.ts | 4 +- packages/next/config.js | 2 +- packages/next/package.json | 2 + packages/next/src/build/index.ts | 32 +- packages/next/src/build/templates/app-page.ts | 2 +- .../next/src/build/templates/app-route.ts | 3 +- .../next/src/build/templates/pages-api.ts | 3 +- packages/next/src/build/templates/pages.ts | 2 +- packages/next/src/build/utils.ts | 18 +- packages/next/src/build/webpack-config.ts | 118 +++-- .../plugins/next-types-plugin/index.ts | 9 +- packages/next/src/client/app-index.tsx | 4 +- packages/next/src/client/compat/router.ts | 2 +- ...ge.ts => action-async-storage.external.ts} | 0 .../next/src/client/components/app-router.tsx | 6 +- .../components/bailout-to-client-rendering.ts | 2 +- .../next/src/client/components/headers.ts | 5 +- .../src/client/components/layout-router.tsx | 4 +- .../next/src/client/components/navigation.ts | 8 +- .../internal/helpers/use-websocket.ts | 2 +- .../client/components/redirect-boundary.tsx | 2 +- .../next/src/client/components/redirect.ts | 2 +- .../render-from-template-context.tsx | 2 +- ...e.ts => request-async-storage.external.ts} | 0 .../router-reducer/apply-flight-data.ts | 5 +- .../create-initial-router-state.test.tsx | 5 +- .../create-initial-router-state.ts | 4 +- .../fill-cache-with-data-property.test.tsx | 5 +- .../fill-cache-with-data-property.ts | 5 +- .../fill-cache-with-new-subtree-data.test.tsx | 5 +- .../fill-cache-with-new-subtree-data.ts | 5 +- ...ll-lazy-items-till-leaf-with-head.test.tsx | 5 +- .../fill-lazy-items-till-leaf-with-head.ts | 5 +- ...te-cache-below-flight-segmentpath.test.tsx | 5 +- ...validate-cache-below-flight-segmentpath.ts | 2 +- .../invalidate-cache-by-router-state.test.tsx | 5 +- .../invalidate-cache-by-router-state.ts | 2 +- .../reducers/find-head-in-cache.test.tsx | 2 +- .../reducers/find-head-in-cache.ts | 2 +- .../reducers/navigate-reducer.test.tsx | 2 +- .../reducers/navigate-reducer.ts | 2 +- .../reducers/prefetch-reducer.test.tsx | 2 +- .../reducers/refresh-reducer.test.tsx | 2 +- .../reducers/refresh-reducer.ts | 2 +- .../reducers/restore-reducer.test.tsx | 2 +- .../reducers/server-action-reducer.ts | 2 +- .../reducers/server-patch-reducer.test.tsx | 2 +- .../router-reducer/router-reducer-types.ts | 2 +- ...atic-generation-async-storage.external.ts} | 0 .../components/static-generation-bailout.ts | 2 +- packages/next/src/client/image-component.tsx | 4 +- packages/next/src/client/index.tsx | 14 +- packages/next/src/client/legacy/image.tsx | 2 +- packages/next/src/client/link.tsx | 4 +- packages/next/src/client/router.ts | 2 +- packages/next/src/client/script.tsx | 2 +- packages/next/src/export/worker.ts | 14 +- packages/next/src/lib/chalk.ts | 2 +- packages/next/src/lib/constants.ts | 5 + packages/next/src/pages/_document.tsx | 7 +- .../src/server/app-render/action-handler.ts | 7 +- .../next/src/server/app-render/app-render.tsx | 17 +- .../next/src/server/app-render/entry-base.ts | 44 +- .../app-render/server-inserted-html.tsx | 2 +- .../request-async-storage-wrapper.ts | 2 +- ...static-generation-async-storage-wrapper.ts | 2 +- packages/next/src/server/base-server.ts | 8 +- .../next/src/server/dev/next-dev-server.ts | 2 +- .../src/server/dev/static-paths-worker.ts | 8 +- .../module-loader/node-module-loader.ts | 5 +- .../route-modules/app-page/module.compiled.ts | 11 + .../future/route-modules/app-page/module.ts | 5 + .../route-modules/app-page/shared-modules.ts | 13 + .../app-route/module.compiled.ts | 11 + .../future/route-modules/app-route/module.ts | 26 +- .../route-modules/app-route/shared-modules.ts | 3 + .../pages-api/module.compiled.ts | 11 + .../future/route-modules/pages-api/module.ts | 10 + .../route-modules/pages/module.compiled.ts | 11 + .../future/route-modules/pages/module.ts | 8 +- .../route-modules/pages/shared-modules.ts | 12 + .../future/route-modules/route-module.ts | 5 + .../src/server/lib/incremental-cache/index.ts | 5 +- packages/next/src/server/lib/patch-fetch.ts | 2 +- .../next/src/server/lib/server-ipc/index.ts | 1 + .../next/src/server/lib/trace/constants.ts | 1 - packages/next/src/server/load-components.ts | 35 +- .../server/load-default-error-components.ts | 78 +++ packages/next/src/server/next-server.ts | 339 +++++++------ packages/next/src/server/render-result.ts | 2 +- packages/next/src/server/render.tsx | 22 +- packages/next/src/server/require-hook.ts | 36 +- .../next/src/server/response-cache/index.ts | 5 +- .../next/src/server/response-cache/web.ts | 4 +- packages/next/src/server/web/adapter.ts | 2 +- .../adapters/request-cookies.ts | 2 +- .../web/spec-extension/revalidate-tag.ts | 2 +- .../web/spec-extension/unstable-cache.ts | 2 +- ...ntext.ts => amp-context.shared-runtime.ts} | 0 packages/next/src/shared/lib/amp.ts | 2 +- ...s => app-router-context.shared-runtime.ts} | 0 packages/next/src/shared/lib/dynamic.tsx | 2 +- ...=> head-manager-context.shared-runtime.ts} | 0 packages/next/src/shared/lib/head.tsx | 4 +- ...=> hooks-client-context.shared-runtime.ts} | 0 ...text.ts => html-context.shared-runtime.ts} | 0 ...=> image-config-context.shared-runtime.ts} | 0 ....ts => loadable-context.shared-runtime.ts} | 0 ...adable.tsx => loadable.shared-runtime.tsx} | 2 +- ...xt.ts => router-context.shared-runtime.ts} | 0 ...apters.tsx => adapters.shared-runtime.tsx} | 7 +- .../src/shared/lib/router/adapters.test.tsx | 2 +- ...ig.ts => runtime-config.shared-runtime.ts} | 0 ...> server-inserted-html.shared-runtime.tsx} | 0 packages/next/src/shared/lib/utils.ts | 2 +- packages/next/src/trace/index.ts | 3 +- packages/next/taskfile-webpack.js | 35 ++ packages/next/taskfile.js | 183 ++----- packages/next/webpack.config.js | 145 ++++++ pnpm-lock.yaml | 3 + scripts/minimal-server.js | 19 +- .../app-action-size-limit-invalid.test.ts | 2 +- .../e2e/getserversideprops/app/pages/index.js | 2 +- test/e2e/opentelemetry/opentelemetry.test.ts | 456 +++++++++--------- test/e2e/prerender-native-module.test.ts | 3 - test/e2e/prerender.test.ts | 5 - .../app/node_modules/comps/index.js | 5 - .../app/node_modules/comps/package.json | 6 - .../externalize-next-server/app/package.json | 6 - .../app/pages/index.js | 12 - .../test/index.test.js | 19 - .../jsconfig-baseurl/test/index.test.js | 6 - .../jsconfig-paths/test/index.test.js | 7 +- 159 files changed, 1504 insertions(+), 949 deletions(-) create mode 100644 bench/basic-app/app/api/app/route.js create mode 100644 bench/basic-app/app/layout.js create mode 100644 bench/basic-app/app/page.js create mode 100644 bench/basic-app/next.config.js create mode 100644 bench/basic-app/pages/api/index.js create mode 100644 bench/basic-app/pages/pages/index.js create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt rename packages/next/src/client/components/{action-async-storage.ts => action-async-storage.external.ts} (100%) rename packages/next/src/client/components/{request-async-storage.ts => request-async-storage.external.ts} (100%) rename packages/next/src/client/components/{static-generation-async-storage.ts => static-generation-async-storage.external.ts} (100%) create mode 100644 packages/next/src/server/future/route-modules/app-page/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/app-page/shared-modules.ts create mode 100644 packages/next/src/server/future/route-modules/app-route/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/app-route/shared-modules.ts create mode 100644 packages/next/src/server/future/route-modules/pages-api/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/pages/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/pages/shared-modules.ts create mode 100644 packages/next/src/server/load-default-error-components.ts rename packages/next/src/shared/lib/{amp-context.ts => amp-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{app-router-context.ts => app-router-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{head-manager-context.ts => head-manager-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{hooks-client-context.ts => hooks-client-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{html-context.ts => html-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{image-config-context.ts => image-config-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{loadable-context.ts => loadable-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{loadable.tsx => loadable.shared-runtime.tsx} (99%) rename packages/next/src/shared/lib/{router-context.ts => router-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/router/{adapters.tsx => adapters.shared-runtime.tsx} (95%) rename packages/next/src/shared/lib/{runtime-config.ts => runtime-config.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{server-inserted-html.tsx => server-inserted-html.shared-runtime.tsx} (100%) create mode 100644 packages/next/taskfile-webpack.js create mode 100644 packages/next/webpack.config.js delete mode 100644 test/integration/externalize-next-server/app/node_modules/comps/index.js delete mode 100644 test/integration/externalize-next-server/app/node_modules/comps/package.json delete mode 100644 test/integration/externalize-next-server/app/package.json delete mode 100644 test/integration/externalize-next-server/app/pages/index.js delete mode 100644 test/integration/externalize-next-server/test/index.test.js diff --git a/.eslintrc.json b/.eslintrc.json index 8c46d8c02736f..feb49b2ecbbff 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,10 @@ }, { "files": ["packages/**"], - "excludedFiles": ["packages/next/taskfile.js"], + "excludedFiles": [ + "packages/next/taskfile*.js", + "packages/next/webpack.config.js" + ], "rules": { "no-shadow": ["warn", { "builtinGlobals": false }], "import/no-extraneous-dependencies": [ diff --git a/bench/basic-app/app/api/app/route.js b/bench/basic-app/app/api/app/route.js new file mode 100644 index 0000000000000..944ba5a8e827f --- /dev/null +++ b/bench/basic-app/app/api/app/route.js @@ -0,0 +1,5 @@ +export function GET() { + return { name: 'John Doe' } +} + +export const dynamic = 'force-dynamic' diff --git a/bench/basic-app/app/layout.js b/bench/basic-app/app/layout.js new file mode 100644 index 0000000000000..8ebf54889577d --- /dev/null +++ b/bench/basic-app/app/layout.js @@ -0,0 +1,12 @@ +import React from 'react' + +export default function Layout({ children }) { + return ( + + + My App + + {children} + + ) +} diff --git a/bench/basic-app/app/page.js b/bench/basic-app/app/page.js new file mode 100644 index 0000000000000..83dc3aa56c9a0 --- /dev/null +++ b/bench/basic-app/app/page.js @@ -0,0 +1,7 @@ +import React from 'react' + +export default function Page() { + return

My Page

+} + +export const dynamic = 'force-dynamic' diff --git a/bench/basic-app/next.config.js b/bench/basic-app/next.config.js new file mode 100644 index 0000000000000..0957c472383fa --- /dev/null +++ b/bench/basic-app/next.config.js @@ -0,0 +1,5 @@ +module.exports = { + experimental: { + serverMinification: true, + }, +} diff --git a/bench/basic-app/pages/api/index.js b/bench/basic-app/pages/api/index.js new file mode 100644 index 0000000000000..8f603094bd288 --- /dev/null +++ b/bench/basic-app/pages/api/index.js @@ -0,0 +1,3 @@ +export default function handler(req, res) { + res.status(200).json({ name: 'John Doe' }) +} diff --git a/bench/basic-app/pages/pages/index.js b/bench/basic-app/pages/pages/index.js new file mode 100644 index 0000000000000..e06229eee0637 --- /dev/null +++ b/bench/basic-app/pages/pages/index.js @@ -0,0 +1,7 @@ +export default () => 'Hello World' + +export function getServerSideProps() { + return { + props: {}, + } +} diff --git a/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx b/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx index fdec9ffc360fe..38f27b3c1c7a3 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx @@ -2,6 +2,8 @@ // the other imports import startOperationStreamHandler from '../internal/operation-stream' +import 'next/dist/server/node-polyfill-fetch' + import { join } from 'path' import { parse as parseUrl } from 'node:url' diff --git a/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx b/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx index 951f91570d92d..25437fec08ef4 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx @@ -3,13 +3,15 @@ import startOperationStreamHandler from '../internal/operation-stream' import '../polyfill/app-polyfills.ts' +// TODO: when actions are supported, this should be removed/changed +process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = 'next' +import 'next/dist/server/require-hook' import type { IncomingMessage } from 'node:http' import type { RenderData } from 'types/turbopack' import type { RenderOpts } from 'next/dist/server/app-render/types' -import { renderToHTMLOrFlight } from 'next/dist/server/app-render/app-render' import { RSC_VARY_HEADER } from 'next/dist/client/components/app-router-headers' import { headersFromEntries, initProxiedHeaders } from '../internal/headers' import { parse, ParsedUrlQuery } from 'node:querystring' @@ -23,6 +25,10 @@ import { join } from 'node:path' import { nodeFs } from 'next/dist/server/lib/node-fs-methods' import { IncrementalCache } from 'next/dist/server/lib/incremental-cache' +const { + renderToHTMLOrFlight, +} = require('next/dist/compiled/next-server/app-page.runtime.dev') + installRequireAndChunkLoad() const MIME_TEXT_HTML_UTF8 = 'text/html; charset=utf-8' diff --git a/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx b/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx index abdf23d0fd7d3..c4e1bce96aa5e 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx @@ -6,7 +6,7 @@ import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-we import { callServer } from 'next/dist/client/app-call-server' import { linkGc } from 'next/dist/client/app-link-gc' -import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context' +import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context.shared-runtime' import { initializeHMR } from '@vercel/turbopack-next/dev/client' diff --git a/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx b/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx index 6182b2d7d4539..cecafb35f77a7 100644 --- a/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx +++ b/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx @@ -3,11 +3,12 @@ import { IPC } from '@vercel/turbopack-node/ipc/index' import 'next/dist/server/node-polyfill-fetch.js' +import 'next/dist/server/require-hook' import './shims' import type { IncomingMessage, ServerResponse } from 'node:http' -import { renderToHTML, RenderOpts } from 'next/dist/server/render' +import type { RenderOpts } from 'next/dist/server/render' import { getRedirectStatus } from 'next/dist/lib/redirect-status' import { PERMANENT_REDIRECT_STATUS } from 'next/dist/shared/lib/constants' import { buildStaticPaths } from 'next/dist/build/utils' @@ -21,6 +22,9 @@ import type { RenderData } from 'types/turbopack' import type { ChunkGroup } from 'types/next' import type { NextComponentType } from 'next/types' import { parse } from 'node:querystring' +const { + renderToHTML, +} = require('next/dist/compiled/next-server/pages.runtime.dev') const ipc = IPC as Ipc diff --git a/packages/next-swc/crates/next-core/src/app_source.rs b/packages/next-swc/crates/next-core/src/app_source.rs index e5af263c3d6bd..738f5a4bf938f 100644 --- a/packages/next-swc/crates/next-core/src/app_source.rs +++ b/packages/next-swc/crates/next-core/src/app_source.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, io::Write as _, iter::once}; use anyhow::{bail, Result}; use indexmap::indexmap; -use indoc::indoc; +use indoc::formatdoc; use serde_json::Value as JsonValue; use turbo_tasks::Vc; use turbopack_binding::{ @@ -968,13 +968,18 @@ impl AppRenderer { .emit(); } - let mut result = RopeBuilder::from(indoc! {" - \"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\"; + let mut result = RopeBuilder::from( + formatdoc!( + " + \"TURBOPACK {{ chunking-type: isolatedParallel; transition: {rsc_transition} }}\"; import GlobalErrorMod from \"next/dist/client/components/error-boundary\" - const { GlobalError } = GlobalErrorMod; - \"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\"; + const {{ GlobalError }} = GlobalErrorMod; + \"TURBOPACK {{ chunking-type: isolatedParallel; transition: {rsc_transition} }}\"; import base from \"next/dist/server/app-render/entry-base\"\n - "}); + " + ) + .into_bytes(), + ); for import in loader_tree_module.imports { writeln!(result, "{import}")?; diff --git a/packages/next-swc/crates/next-core/src/next_edge/context.rs b/packages/next-swc/crates/next-core/src/next_edge/context.rs index 54c7dd3331a63..cfe9eebf19f94 100644 --- a/packages/next-swc/crates/next-core/src/next_edge/context.rs +++ b/packages/next-swc/crates/next-core/src/next_edge/context.rs @@ -96,10 +96,9 @@ pub async fn get_edge_resolve_options_context( ]; match ty { - ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { - custom_conditions.push("react-server".to_string()) - } - ServerContextType::Pages { .. } + ServerContextType::AppRSC { .. } => custom_conditions.push("react-server".to_string()), + ServerContextType::AppRoute { .. } + | ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } | ServerContextType::AppSSR { .. } | ServerContextType::Middleware { .. } => {} diff --git a/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs b/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs index de0bd2f2e7bf7..05326de255203 100644 --- a/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs +++ b/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs @@ -58,24 +58,26 @@ impl Transition for NextEdgeRouteTransition { #[turbo_tasks::function] async fn process_module( - &self, + self: Vc, asset: Vc>, context: Vc, ) -> Result>> { + let new_context = self.process_context(context); + let this = self.await?; let new_asset = route_bootstrap( asset, - Vc::upcast(context), - self.base_path, - self.bootstrap_asset, + Vc::upcast(new_context), + this.base_path, + this.bootstrap_asset, Vc::cell(indexmap! { - "NAME".to_string() => self.entry_name.clone(), + "NAME".to_string() => this.entry_name.clone(), }), ); let asset = ChunkGroupFilesAsset { module: Vc::upcast(new_asset), - client_root: self.output_path, - chunking_context: self.edge_chunking_context, + client_root: this.output_path, + chunking_context: this.edge_chunking_context, runtime_entries: None, }; diff --git a/packages/next-swc/crates/next-core/src/next_import_map.rs b/packages/next-swc/crates/next-core/src/next_import_map.rs index e05ac926c139f..c530f3bcb9290 100644 --- a/packages/next-swc/crates/next-core/src/next_import_map.rs +++ b/packages/next-swc/crates/next-core/src/next_import_map.rs @@ -216,25 +216,26 @@ pub async fn get_next_server_import_map( let ty = ty.into_value(); insert_next_server_special_aliases(&mut import_map, ty, mode, NextRuntime::NodeJs).await?; - let external = ImportMapping::External(None).cell(); + let external: Vc = ImportMapping::External(None).cell(); + import_map.insert_exact_alias("next/dist/server/require-hook", external); match ty { ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } => { - import_map.insert_exact_alias("next", external); - import_map.insert_wildcard_alias("next/", external); import_map.insert_exact_alias("react", external); import_map.insert_wildcard_alias("react/", external); import_map.insert_exact_alias("react-dom", external); import_map.insert_wildcard_alias("react-dom/", external); import_map.insert_exact_alias("styled-jsx", external); import_map.insert_wildcard_alias("styled-jsx/", external); - import_map.insert_exact_alias("react-server-dom-webpack/", external); + import_map.insert_wildcard_alias("react-server-dom-webpack/", external); + // TODO: we should not bundle next/dist/build/utils in the pages renderer at all + import_map.insert_wildcard_alias("next/dist/build/utils", external); } ServerContextType::AppSSR { .. } | ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { match mode { - NextMode::Development | NextMode::Build => { + NextMode::Build => { import_map.insert_wildcard_alias("next/dist/server/", external); import_map.insert_wildcard_alias("next/dist/shared/", external); } @@ -242,6 +243,7 @@ pub async fn get_next_server_import_map( // The sandbox can't be bundled and needs to be external import_map.insert_exact_alias("next/dist/server/web/sandbox", external); } + NextMode::Development => {} } import_map.insert_exact_alias( "next/head", @@ -377,6 +379,11 @@ async fn insert_next_server_special_aliases( NextRuntime::Edge => request_to_import_mapping(context_dir, request), NextRuntime::NodeJs => external_request_to_import_mapping(request), }; + let passthrough_external_if_node = + move |context_dir: Vc, request: &str| match runtime { + NextRuntime::Edge => request_to_import_mapping(context_dir, request), + NextRuntime::NodeJs => ImportMapping::External(None).cell(), + }; match (mode, ty) { (_, ServerContextType::Pages { pages_dir }) => { import_map.insert_exact_alias( @@ -413,12 +420,7 @@ async fn insert_next_server_special_aliases( (_, ServerContextType::PagesData { .. }) => {} // In development, we *always* use the bundled version of React, even in // SSR, since we're bundling Next.js alongside it. - ( - NextMode::DevServer, - ServerContextType::AppSSR { app_dir } - | ServerContextType::AppRSC { app_dir, .. } - | ServerContextType::AppRoute { app_dir }, - ) => { + (NextMode::DevServer, ServerContextType::AppSSR { app_dir }) => { import_map.insert_exact_alias( "@opentelemetry/api", // TODO(WEB-625) this actually need to prefer the local version of @@ -427,28 +429,40 @@ async fn insert_next_server_special_aliases( ); import_map.insert_exact_alias( "react", - request_to_import_mapping(app_dir, "next/dist/compiled/react"), + passthrough_external_if_node(app_dir, "next/dist/compiled/react"), ); import_map.insert_wildcard_alias( "react/", - request_to_import_mapping(app_dir, "next/dist/compiled/react/*"), + passthrough_external_if_node(app_dir, "next/dist/compiled/react/*"), ); import_map.insert_exact_alias( "react-dom", - request_to_import_mapping( + passthrough_external_if_node( app_dir, "next/dist/compiled/react-dom/server-rendering-stub.js", ), ); import_map.insert_wildcard_alias( "react-dom/", - request_to_import_mapping(app_dir, "next/dist/compiled/react-dom/*"), + passthrough_external_if_node(app_dir, "next/dist/compiled/react-dom/*"), + ); + import_map.insert_exact_alias( + "styled-jsx", + passthrough_external_if_node(app_dir, "next/dist/compiled/styled-jsx"), + ); + import_map.insert_wildcard_alias( + "styled-jsx/", + passthrough_external_if_node(app_dir, "next/dist/compiled/styled-jsx/*"), ); import_map.insert_wildcard_alias( "react-server-dom-webpack/", - request_to_import_mapping(app_dir, "next/dist/compiled/react-server-dom-webpack/*"), + passthrough_external_if_node( + app_dir, + "next/dist/compiled/react-server-dom-webpack/*", + ), ); } + // NOTE(alexkirsz) This logic maps loosely to // `next.js/packages/next/src/build/webpack-config.ts`, where: // @@ -460,7 +474,7 @@ async fn insert_next_server_special_aliases( // * passes through (react|react-dom|react-server-dom-webpack)/(.*) to // next/dist/compiled/$1/$2 ( - NextMode::Build | NextMode::Development, + NextMode::Build | NextMode::Development | NextMode::DevServer, ServerContextType::AppRSC { app_dir, .. } | ServerContextType::AppRoute { app_dir }, ) => { import_map.insert_exact_alias( @@ -469,10 +483,20 @@ async fn insert_next_server_special_aliases( // @opentelemetry/api request_to_import_mapping(app_dir, "next/dist/compiled/@opentelemetry/api"), ); - import_map.insert_exact_alias( - "react", - request_to_import_mapping(app_dir, "next/dist/compiled/react/react.shared-subset"), - ); + if matches!(ty, ServerContextType::AppRSC { .. }) { + import_map.insert_exact_alias( + "react", + request_to_import_mapping( + app_dir, + "next/dist/compiled/react/react.shared-subset", + ), + ); + } else { + import_map.insert_exact_alias( + "react", + request_to_import_mapping(app_dir, "next/dist/compiled/react"), + ); + } import_map.insert_exact_alias( "react-dom", request_to_import_mapping( diff --git a/packages/next-swc/crates/next-core/src/next_server/context.rs b/packages/next-swc/crates/next-core/src/next_server/context.rs index e7485ff100ade..1d242498b36df 100644 --- a/packages/next-swc/crates/next-core/src/next_server/context.rs +++ b/packages/next-swc/crates/next-core/src/next_server/context.rs @@ -46,7 +46,10 @@ use crate::{ next_import_map::{get_next_server_import_map, mdx_import_source_file}, next_server::resolve::ExternalPredicate, next_shared::{ - resolve::{ModuleFeatureReportResolvePlugin, UnsupportedModulesResolvePlugin}, + resolve::{ + ModuleFeatureReportResolvePlugin, NextExternalResolvePlugin, + UnsupportedModulesResolvePlugin, + }, transforms::{ emotion::get_emotion_transform_plugin, get_relay_transform_plugin, styled_components::get_styled_components_transform_plugin, @@ -108,10 +111,9 @@ pub async fn get_server_resolve_options_context( let mut custom_conditions = vec![mode.node_env().to_string(), "node".to_string()]; match ty { - ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { - custom_conditions.push("react-server".to_string()) - } - ServerContextType::Pages { .. } + ServerContextType::AppRSC { .. } => custom_conditions.push("react-server".to_string()), + ServerContextType::AppRoute { .. } + | ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } | ServerContextType::AppSSR { .. } | ServerContextType::Middleware { .. } => {} @@ -121,12 +123,15 @@ pub async fn get_server_resolve_options_context( ExternalPredicate::AllExcept(next_config.transpile_packages()).cell(), ); + let next_external_plugin = NextExternalResolvePlugin::new(project_path); + let plugins = match ty { ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } => { vec![ Vc::upcast(module_feature_report_resolve_plugin), Vc::upcast(external_cjs_modules_plugin), Vc::upcast(unsupported_modules_resolve_plugin), + Vc::upcast(next_external_plugin), ] } ServerContextType::AppSSR { .. } @@ -137,6 +142,7 @@ pub async fn get_server_resolve_options_context( Vc::upcast(module_feature_report_resolve_plugin), Vc::upcast(server_component_externals_plugin), Vc::upcast(unsupported_modules_resolve_plugin), + Vc::upcast(next_external_plugin), ] } }; diff --git a/packages/next-swc/crates/next-core/src/next_shared/resolve.rs b/packages/next-swc/crates/next-core/src/next_shared/resolve.rs index 013f87f9fd5b6..ea7f044c12cc8 100644 --- a/packages/next-swc/crates/next-core/src/next_shared/resolve.rs +++ b/packages/next-swc/crates/next-core/src/next_shared/resolve.rs @@ -13,7 +13,7 @@ use turbopack_binding::{ parse::Request, pattern::Pattern, plugin::{ResolvePlugin, ResolvePluginCondition}, - ResolveResultOption, + ResolveResult, ResolveResultItem, ResolveResultOption, }, }, }; @@ -102,6 +102,55 @@ impl ResolvePlugin for UnsupportedModulesResolvePlugin { } } +#[turbo_tasks::value] +pub(crate) struct NextExternalResolvePlugin { + root: Vc, +} + +#[turbo_tasks::value_impl] +impl NextExternalResolvePlugin { + #[turbo_tasks::function] + pub fn new(root: Vc) -> Vc { + NextExternalResolvePlugin { root }.cell() + } +} + +#[turbo_tasks::value_impl] +impl ResolvePlugin for NextExternalResolvePlugin { + #[turbo_tasks::function] + fn after_resolve_condition(&self) -> Vc { + ResolvePluginCondition::new( + self.root.root(), + Glob::new( + "**/next/dist/**/*.{external,shared-runtime,runtime.dev,runtime.prod}.js" + .to_string(), + ), + ) + } + + #[turbo_tasks::function] + async fn after_resolve( + &self, + fs_path: Vc, + _context: Vc, + _request: Vc, + ) -> Result> { + let raw_fs_path = &*fs_path.await?; + let path = raw_fs_path.path.to_string(); + // Find the starting index of 'next/dist' and slice from that point. It should + // always be found since the glob pattern above is specific enough. + let starting_index = path.find("next/dist").unwrap(); + // Replace '/esm/' with '/' to match the CJS version of the file. + let modified_path = &path[starting_index..].replace("/esm/", "/"); + Ok(Vc::cell(Some( + ResolveResult::primary(ResolveResultItem::OriginalReferenceTypeExternal( + modified_path.to_string(), + )) + .into(), + ))) + } +} + /// A resolver plugin tracks the usage of certain import paths, emit /// telemetry events if there is a match. #[turbo_tasks::value] diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt new file mode 100644 index 0000000000000..a5ad94c85fb0a --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt @@ -0,0 +1,17 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:64:56 lint TP1004 fs.readFile(???*0*) is very dynamic + 60 | } + 61 | async function loadWasm(wasm) { + 62 | const modules = {}; + 63 | await Promise.all(wasm.map(async (binding)=>{ + + v + 64 + const module1 = await WebAssembly.compile(await _fs.promises.readFile(binding.filePath)); + + ^ + 65 | modules[binding.name] = module1; + 66 | })); + 67 | return modules; + 68 | } + + - *0* ???*1*["filePath"] + ⚠️ unknown object + - *1* binding + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt new file mode 100644 index 0000000000000..c80db085946c2 --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt @@ -0,0 +1,15 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:355:28 lint TP1004 fs.readFileSync(???*0*, "utf-8") is very dynamic + 351 | } + 352 | const moduleContext = lazyModuleContext; + 353 | const evaluateInContext = (filepath)=>{ + 354 | if (!moduleContext.paths.has(filepath)) { + + v + 355 + const content = (0, _fs.readFileSync)(filepath, "utf-8"); + + ^ + 356 | try { + 357 | (0, _vm.runInContext)(content, moduleContext.runtime.context, { + 358 | filename: filepath + 359 | }); + + - *0* filepath + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt new file mode 100644 index 0000000000000..39e97b4ccc85e --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt @@ -0,0 +1,47 @@ +error - [rendering] [root of the server]/invalid Error during SSR Rendering + Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` + + Debug info: + - Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` + See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host + at defaultLoader (packages/next/dist/shared/lib/image-loader.js:41:27) + 37 | process.env.NEXT_RUNTIME !== "edge") { + 38 | // We use dynamic require because this should only error in development + 39 | const { hasMatch } = require("./match-remote-pattern"); + 40 | if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) { + | v + 41 + throw new Error("Invalid src prop (" + src + ') on `next/image`, hostname "' + parsedSrc.hostname + '" i...xt.config.js`\n' + "See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host"); + | ^ + 42 | } + 43 | } + 44 | } + 45 | } + + at (packages/next/dist/shared/lib/get-img-props.js:101:36) + 97 | const { widths , kind } = getWidths(config, width, sizes); + 98 | const last = widths.length - 1; + 99 | return { + 100 | sizes: !sizes && kind === "w" ? "100vw" : sizes, + | v + 101 + srcSet: widths.map((w, i)=>loader({ + | ^ + 102 | config, + 103 | src, + 104 | quality, + 105 | width: w + + at generateImgAttrs (packages/next/dist/shared/lib/get-img-props.js:101:24) + 97 | const { widths , kind } = getWidths(config, width, sizes); + 98 | const last = widths.length - 1; + 99 | return { + 100 | sizes: !sizes && kind === "w" ? "100vw" : sizes, + | v + 101 + srcSet: widths.map((w, i)=>loader({ + | ^ + 102 | config, + 103 | src, + 104 | quality, + 105 | width: w + + at getImgProps (packages/next/dist/shared/lib/get-img-props.js:392:27) + at (packages/next/dist/client/image-component.js:275:82) \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt deleted file mode 100644 index ce775cb8df7e8..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt +++ /dev/null @@ -1,6 +0,0 @@ -error - [rendering] [root of the server]/invalid Error during SSR Rendering - Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` - - Debug info: - - Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` - See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js index e2c2f40a06dd7..108b763da879c 100644 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js @@ -120,7 +120,7 @@ function runTests() { expect(json).toMatchObject({ edgeThenNode: 'node', nodeThenEdge: 'node', - reactServer: 'react-server', + reactServer: 'default', }) }) @@ -129,7 +129,7 @@ function runTests() { expect(json).toMatchObject({ edgeThenNode: 'edge', nodeThenEdge: 'edge', - reactServer: 'react-server', + reactServer: 'default', }) }) diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt deleted file mode 100644 index 72c048d7b6481..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt +++ /dev/null @@ -1,13 +0,0 @@ -error - [resolve] [project]/packages/next/dist/compiled/nanoid/index.cjs /packages/next/dist/compiled/nanoid/index.cjs:1:45 Error resolving commonjs request - + v---------------v - 1 + (()=>{var e={113:e=>{"use strict";e.exports=require("crypto")},660:(e,r,t)=>{let l=t(113);let{urlAlphabet:a}=t(591);const n=128;let _,u;let fillPool=e=>{if(!_||...ndefined")__nccwpck_require__.ab=__dirname+"/";var t=__nccwpck_require__(660);module.exports=t})(); - + ^---------------^ - - unable to resolve module "crypto" - - | It was not possible to find the requested file. - | Parsed request as written in source code: module "crypto" - | Path where resolving has started: [project]/packages/next/dist/compiled/nanoid/index.cjs - | Type of request: commonjs request - | Import map: No import map entry - | \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt deleted file mode 100644 index 72c048d7b6481..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt +++ /dev/null @@ -1,13 +0,0 @@ -error - [resolve] [project]/packages/next/dist/compiled/nanoid/index.cjs /packages/next/dist/compiled/nanoid/index.cjs:1:45 Error resolving commonjs request - + v---------------v - 1 + (()=>{var e={113:e=>{"use strict";e.exports=require("crypto")},660:(e,r,t)=>{let l=t(113);let{urlAlphabet:a}=t(591);const n=128;let _,u;let fillPool=e=>{if(!_||...ndefined")__nccwpck_require__.ab=__dirname+"/";var t=__nccwpck_require__(660);module.exports=t})(); - + ^---------------^ - - unable to resolve module "crypto" - - | It was not possible to find the requested file. - | Parsed request as written in source code: module "crypto" - | Path where resolving has started: [project]/packages/next/dist/compiled/nanoid/index.cjs - | Type of request: commonjs request - | Import map: No import map entry - | \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt new file mode 100644 index 0000000000000..a5ad94c85fb0a --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt @@ -0,0 +1,17 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:64:56 lint TP1004 fs.readFile(???*0*) is very dynamic + 60 | } + 61 | async function loadWasm(wasm) { + 62 | const modules = {}; + 63 | await Promise.all(wasm.map(async (binding)=>{ + + v + 64 + const module1 = await WebAssembly.compile(await _fs.promises.readFile(binding.filePath)); + + ^ + 65 | modules[binding.name] = module1; + 66 | })); + 67 | return modules; + 68 | } + + - *0* ???*1*["filePath"] + ⚠️ unknown object + - *1* binding + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt new file mode 100644 index 0000000000000..c80db085946c2 --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt @@ -0,0 +1,15 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:355:28 lint TP1004 fs.readFileSync(???*0*, "utf-8") is very dynamic + 351 | } + 352 | const moduleContext = lazyModuleContext; + 353 | const evaluateInContext = (filepath)=>{ + 354 | if (!moduleContext.paths.has(filepath)) { + + v + 355 + const content = (0, _fs.readFileSync)(filepath, "utf-8"); + + ^ + 356 | try { + 357 | (0, _vm.runInContext)(content, moduleContext.runtime.context, { + 358 | filename: filepath + 359 | }); + + - *0* filepath + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next/config.d.ts b/packages/next/config.d.ts index 78fe148a8dc9b..2da1ee3c4029c 100644 --- a/packages/next/config.d.ts +++ b/packages/next/config.d.ts @@ -1,3 +1,3 @@ -import getConfig from './dist/shared/lib/runtime-config' -export * from './dist/shared/lib/runtime-config' +import getConfig from './dist/shared/lib/runtime-config.shared-runtime' +export * from './dist/shared/lib/runtime-config.shared-runtime' export default getConfig diff --git a/packages/next/config.js b/packages/next/config.js index 2da980d8b0065..6510748638097 100644 --- a/packages/next/config.js +++ b/packages/next/config.js @@ -1 +1 @@ -module.exports = require('./dist/shared/lib/runtime-config') +module.exports = require('./dist/shared/lib/runtime-config.shared-runtime') diff --git a/packages/next/package.json b/packages/next/package.json index bebc8aa2b209b..75b2e78962ffc 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -83,6 +83,7 @@ }, "taskr": { "requires": [ + "./taskfile-webpack.js", "./taskfile-ncc.js", "./taskfile-swc.js", "./taskfile-watch.js" @@ -299,6 +300,7 @@ "tar": "6.1.15", "taskr": "1.1.0", "terser": "5.14.1", + "terser-webpack-plugin": "5.3.9", "text-table": "0.2.0", "timers-browserify": "2.0.12", "tty-browserify": "0.0.1", diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 8ad7407b4be45..b0e0870ca1392 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -143,8 +143,13 @@ import { createClientRouterFilter } from '../lib/create-client-router-filter' import { createValidFileMatcher } from '../server/lib/find-page-file' import { startTypeChecking } from './type-check' import { generateInterceptionRoutesRewrites } from '../lib/generate-interception-routes-rewrites' + import { buildDataRoute } from '../server/lib/router-utils/build-data-route' -import { baseOverrides, experimentalOverrides } from '../server/require-hook' +import { + baseOverrides, + defaultOverrides, + experimentalOverrides, +} from '../server/require-hook' import { initialize } from '../server/lib/incremental-cache-server' import { nodeFs } from '../server/lib/node-fs-methods' @@ -1243,6 +1248,7 @@ export default async function build( forkOptions: { env: { ...process.env, + __NEXT_PRIVATE_RENDER_RUNTIME: type, __NEXT_INCREMENTAL_CACHE_IPC_PORT: ipcPort + '', __NEXT_INCREMENTAL_CACHE_IPC_KEY: ipcValidationKey, __NEXT_PRIVATE_PREBUNDLED_REACT: @@ -2084,6 +2090,25 @@ export default async function build( ...Object.values(experimentalOverrides).map((override) => require.resolve(override) ), + ...(config.experimental.turbotrace + ? [] + : Object.keys(defaultOverrides).map((value) => + require.resolve(value, { + paths: [require.resolve('next/dist/server/require-hook')], + }) + )), + require.resolve( + 'next/dist/compiled/next-server/app-page.runtime.prod' + ), + require.resolve( + 'next/dist/compiled/next-server/app-route.runtime.prod' + ), + require.resolve( + 'next/dist/compiled/next-server/pages.runtime.prod' + ), + require.resolve( + 'next/dist/compiled/next-server/pages-api.runtime.prod' + ), ] // ensure we trace any dependencies needed for custom @@ -2109,10 +2134,7 @@ export default async function build( const minimalServerEntries = [ ...sharedEntriesSet, require.resolve( - 'next/dist/compiled/minimal-next-server/next-server-cached.js' - ), - require.resolve( - 'next/dist/compiled/minimal-next-server/next-server.js' + 'next/dist/compiled/next-server/server.runtime.prod' ), ].filter(Boolean) diff --git a/packages/next/src/build/templates/app-page.ts b/packages/next/src/build/templates/app-page.ts index c75509904c3a8..f0d2ab692e2aa 100644 --- a/packages/next/src/build/templates/app-page.ts +++ b/packages/next/src/build/templates/app-page.ts @@ -1,7 +1,7 @@ import type { LoaderTree } from '../../server/lib/app-dir-module' // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/app-page/module' +import * as module from 'next/dist/server/future/route-modules/app-page/module.compiled' import { RouteKind } from '../../server/future/route-kind' const AppPageRouteModule = diff --git a/packages/next/src/build/templates/app-route.ts b/packages/next/src/build/templates/app-route.ts index 50a8b6165a747..b4b8e5b0fe6cd 100644 --- a/packages/next/src/build/templates/app-route.ts +++ b/packages/next/src/build/templates/app-route.ts @@ -1,7 +1,8 @@ import '../../server/node-polyfill-headers' // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/app-route/module' +import * as module from 'next/dist/server/future/route-modules/app-route/module.compiled' + import type { AppRouteRouteModuleOptions } from '../../server/future/route-modules/app-route/module' import { RouteKind } from '../../server/future/route-kind' diff --git a/packages/next/src/build/templates/pages-api.ts b/packages/next/src/build/templates/pages-api.ts index a48822f9ed75a..eaeec836cb61e 100644 --- a/packages/next/src/build/templates/pages-api.ts +++ b/packages/next/src/build/templates/pages-api.ts @@ -1,5 +1,6 @@ // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/pages-api/module' +import * as module from 'next/dist/server/future/route-modules/pages-api/module.compiled' + import { RouteKind } from '../../server/future/route-kind' import { hoist } from './helpers' diff --git a/packages/next/src/build/templates/pages.ts b/packages/next/src/build/templates/pages.ts index 3f3527e6650d6..b5def5d13c552 100644 --- a/packages/next/src/build/templates/pages.ts +++ b/packages/next/src/build/templates/pages.ts @@ -1,5 +1,5 @@ // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/pages/module' +import * as module from 'next/dist/server/future/route-modules/pages/module.compiled' import { RouteKind } from '../../server/future/route-kind' import { hoist } from './helpers' diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index 867429c2ba901..423beb27f6dca 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -14,7 +14,7 @@ import type { EdgeFunctionDefinition, MiddlewareManifest, } from './webpack/plugins/middleware-plugin' -import type { StaticGenerationAsyncStorage } from '../client/components/static-generation-async-storage' +import type { StaticGenerationAsyncStorage } from '../client/components/static-generation-async-storage.external' import '../server/require-hook' import '../server/node-polyfill-fetch' @@ -65,7 +65,9 @@ import { nodeFs } from '../server/lib/node-fs-methods' import * as ciEnvironment from '../telemetry/ci-info' import { normalizeAppPath } from '../shared/lib/router/utils/app-paths' import { denormalizeAppPagePath } from '../shared/lib/page-path/denormalize-app-path' -import { AppRouteRouteModule } from '../server/future/route-modules/app-route/module' +// import { AppRouteRouteModule } from '../server/future/route-modules/app-route/module' +const { AppRouteRouteModule } = + require('../server/future/route-modules/app-route/module.compiled') as typeof import('../server/future/route-modules/app-route/module') export type ROUTER_TYPE = 'pages' | 'app' @@ -1389,7 +1391,9 @@ export async function isPageStatic({ const isPageStaticSpan = trace('is-page-static-utils', parentId) return isPageStaticSpan .traceAsyncFn(async () => { - require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) + require('../shared/lib/runtime-config.shared-runtime').setConfig( + runtimeEnvConfig + ) setHttpClientAndAgentOptions({ httpAgentOptions, }) @@ -1673,7 +1677,9 @@ export async function hasCustomGetInitialProps( runtimeEnvConfig: any, checkingApp: boolean ): Promise { - require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) + require('../shared/lib/runtime-config.shared-runtime').setConfig( + runtimeEnvConfig + ) const components = await loadComponents({ distDir, @@ -1696,7 +1702,9 @@ export async function getDefinedNamedExports( distDir: string, runtimeEnvConfig: any ): Promise> { - require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) + require('../shared/lib/runtime-config.shared-runtime').setConfig( + runtimeEnvConfig + ) const components = await loadComponents({ distDir, pathname: page, diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index f1f053d05eb9b..d1b7487618fe2 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -103,6 +103,19 @@ const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/ const asyncStoragesRegex = /next[\\/]dist[\\/](esm[\\/])?client[\\/]components[\\/](static-generation-async-storage|action-async-storage|request-async-storage)/ +const pathSeparators = '[/\\\\]' +const optionalEsmPart = `((${pathSeparators}esm)?${pathSeparators})` +const sharedRuntimeFileEnd = '(\\.shared-runtime(\\.js)?)$' +const externalFileEnd = '(\\.external(\\.js)?)$' +const nextDist = `next${pathSeparators}dist` + +const sharedRuntimePattern = new RegExp( + `${nextDist}${optionalEsmPart}.*${sharedRuntimeFileEnd}` +) +const externalPattern = new RegExp( + `${nextDist}${optionalEsmPart}.*${externalFileEnd}` +) + // exports. const edgeConditionNames = [ 'edge-light', @@ -1011,7 +1024,7 @@ export default async function getBaseWebpackConfig( const customRootAliases: { [key: string]: string[] } = {} if (dev) { - const nextDist = 'next/dist/' + (isEdgeServer ? 'esm/' : '') + const nextDistPath = 'next/dist/' + (isEdgeServer ? 'esm/' : '') customAppAliases[`${PAGES_DIR_ALIAS}/_app`] = [ ...(pagesDir ? pageExtensions.reduce((prev, ext) => { @@ -1019,7 +1032,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDist}pages/_app.js`, + `${nextDistPath}pages/_app.js`, ] customAppAliases[`${PAGES_DIR_ALIAS}/_error`] = [ ...(pagesDir @@ -1028,7 +1041,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDist}pages/_error.js`, + `${nextDistPath}pages/_error.js`, ] customDocumentAliases[`${PAGES_DIR_ALIAS}/_document`] = [ ...(pagesDir @@ -1037,7 +1050,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDist}pages/_document.js`, + `${nextDistPath}pages/_document.js`, ] } @@ -1311,6 +1324,7 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.serverSideRendering, WEBPACK_LAYERS.appPagesBrowser, WEBPACK_LAYERS.actionBrowser, + WEBPACK_LAYERS.appRouteHandler, ].includes(layer!) if ( @@ -1367,7 +1381,7 @@ export default async function getBaseWebpackConfig( } const notExternalModules = - /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|legacy\/image|constants|dynamic|script|navigation|headers)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ + /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|legacy\/image|constants|dynamic|script|navigation|headers|router)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ if (notExternalModules.test(request)) { return } @@ -1390,41 +1404,59 @@ export default async function getBaseWebpackConfig( // Also disable esm request when appDir is enabled const isEsmRequested = dependencyType === 'esm' + /** + * @param localRes the full path to the file + * @returns the externalized path + * @description returns an externalized path if the file is a Next.js file and ends with either `.shared-runtime.js` or `.external.js` + * This is used to ensure that files used across the rendering runtime(s) and the user code are one and the same. The logic in this function + * will rewrite the require to the correct bundle location depending on the layer at which the file is being used. + */ const isLocalCallback = (localRes: string) => { - // Makes sure dist/shared and dist/server are not bundled - // we need to process shared `router/router`, `head` and `dynamic`, - // so that the DefinePlugin can inject process.env values. - - // Treat next internals as non-external for server layer - if (isWebpackServerLayer(layer)) { - return + const isSharedRuntime = sharedRuntimePattern.test(localRes) + const isExternal = externalPattern.test(localRes) + + // if the file ends with .external, we need to make it a commonjs require in all cases + // this is used mainly to share the async local storage across the routing, rendering and user layers. + if (isExternal) { + // it's important we return the path that starts with `next/dist/` here instead of the absolute path + // otherwise NFT will get tripped up + return `commonjs ${localRes.replace(/.*?next[/\\]dist/, 'next/dist')}` } + // if the file ends with .shared-runtime, we need to make it point to the correct bundle depending on the layer + // this is because each shared-runtime files are unique per bundle, so if you use app-router context in pages, + // it'll be a different instance than the one used in the app-router runtime. + if (isSharedRuntime) { + if (dev) { + return `commonjs ${localRes}` + } - const isNextExternal = - /next[/\\]dist[/\\](esm[\\/])?(shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic|app-dynamic|image-external|lazy-dynamic|head[^-]))/.test( - localRes - ) || - // There's no need to bundle the dev overlay - (process.env.NODE_ENV === 'development' && - /next[/\\]dist[/\\](esm[/\\])?client[/\\]components[/\\]react-dev-overlay[/\\]/.test( - localRes - )) - - if (isNextExternal) { - // Generate Next.js external import - const externalRequest = path.posix.join( - 'next', - 'dist', - path - .relative( - // Root of Next.js package: - path.join(__dirname, '..'), - localRes - ) - // Windows path normalization - .replace(/\\/g, '/') + const name = path.parse(localRes).name.replace('.shared-runtime', '') + + const camelCaseName = name.replace(/-([a-z])/g, (_, w) => + w.toUpperCase() ) - return `commonjs ${externalRequest}` + + // there's no externals for API routes but if need be, they'll need to be added here and have + // their own layer + const runtime = + layer === 'app-route-handler' + ? 'app-route' + : isAppLayer + ? 'app-page' + : 'pages' + return [ + 'commonjs ' + + path.posix.join( + 'next', + 'dist', + 'compiled', + 'next-server', + `${runtime}.runtime.${dev ? 'dev' : 'prod'}` + ), + 'default', + 'sharedModules', + camelCaseName, + ] } } @@ -1445,6 +1477,10 @@ export default async function getBaseWebpackConfig( return } + if (/^next\/dist\/compiled\/next-server/.test(request)) { + return `commonjs ${request}` + } + if ( /^next\/dist\/shared\/(?!lib\/router\/router)/.test(request) || /^next\/dist\/compiled\/.*\.c?js$/.test(request) @@ -2031,6 +2067,14 @@ export default async function getBaseWebpackConfig( }, ...(hasAppDir ? [ + { + layer: WEBPACK_LAYERS.appRouteHandler, + test: new RegExp( + `private-next-app-dir\\/.*\\/route\\.(${pageExtensions.join( + '|' + )})$` + ), + }, { // Make sure that AsyncLocalStorage module instance is shared between server and client // layers. @@ -2239,7 +2283,7 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.appPagesBrowser, ], }, - exclude: [asyncStoragesRegex, codeCondition.exclude], + exclude: [codeCondition.exclude], use: [ ...(dev && isClient ? [ diff --git a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts index 2be12daecdbc6..c2b1089df5b3f 100644 --- a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts +++ b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts @@ -442,7 +442,7 @@ declare module 'next/link' { declare module 'next/navigation' { export * from 'next/dist/client/components/navigation.js' - import type { NavigateOptions, AppRouterInstance as OriginalAppRouterInstance } from 'next/dist/shared/lib/app-router-context.js' + import type { NavigateOptions, AppRouterInstance as OriginalAppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime.js' interface AppRouterInstance extends OriginalAppRouterInstance { /** * Navigate to the provided href. @@ -575,8 +575,11 @@ export class NextTypesPlugin { } return } - - if (mod.layer !== WEBPACK_LAYERS.reactServerComponents) return + if ( + mod.layer !== WEBPACK_LAYERS.reactServerComponents && + mod.layer !== WEBPACK_LAYERS.appRouteHandler + ) + return const IS_LAYOUT = /[/\\]layout\.[^./\\]+$/.test(mod.resource) const IS_PAGE = !IS_LAYOUT && /[/\\]page\.[^.]+$/.test(mod.resource) diff --git a/packages/next/src/client/app-index.tsx b/packages/next/src/client/app-index.tsx index 8f82d244837e6..47c0bd13f369e 100644 --- a/packages/next/src/client/app-index.tsx +++ b/packages/next/src/client/app-index.tsx @@ -7,8 +7,8 @@ import React, { use } from 'react' // eslint-disable-next-line import/no-extraneous-dependencies import { createFromReadableStream } from 'react-server-dom-webpack/client' -import { HeadManagerContext } from '../shared/lib/head-manager-context' -import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' +import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context.shared-runtime' import onRecoverableError from './on-recoverable-error' import { callServer } from './app-call-server' import { isNextRouterError } from './components/is-next-router-error' diff --git a/packages/next/src/client/compat/router.ts b/packages/next/src/client/compat/router.ts index 58b1b9f02ed05..e9143c4117bd7 100644 --- a/packages/next/src/client/compat/router.ts +++ b/packages/next/src/client/compat/router.ts @@ -1,5 +1,5 @@ import { useContext } from 'react' -import { RouterContext } from '../../shared/lib/router-context' +import { RouterContext } from '../../shared/lib/router-context.shared-runtime' import { NextRouter } from '../router' /** diff --git a/packages/next/src/client/components/action-async-storage.ts b/packages/next/src/client/components/action-async-storage.external.ts similarity index 100% rename from packages/next/src/client/components/action-async-storage.ts rename to packages/next/src/client/components/action-async-storage.external.ts diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx index 2382d6a83925e..2c3c541e02b8e 100644 --- a/packages/next/src/client/components/app-router.tsx +++ b/packages/next/src/client/components/app-router.tsx @@ -14,11 +14,11 @@ import { LayoutRouterContext, GlobalLayoutRouterContext, CacheStates, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import type { CacheNode, AppRouterInstance, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightData, @@ -44,7 +44,7 @@ import { createHrefFromUrl } from './router-reducer/create-href-from-url' import { SearchParamsContext, PathnameContext, -} from '../../shared/lib/hooks-client-context' +} from '../../shared/lib/hooks-client-context.shared-runtime' import { useReducerWithReduxDevtools } from './use-reducer-with-devtools' import { ErrorBoundary } from './error-boundary' import { diff --git a/packages/next/src/client/components/bailout-to-client-rendering.ts b/packages/next/src/client/components/bailout-to-client-rendering.ts index 76356e05304a8..799398b5f300c 100644 --- a/packages/next/src/client/components/bailout-to-client-rendering.ts +++ b/packages/next/src/client/components/bailout-to-client-rendering.ts @@ -1,5 +1,5 @@ import { suspense } from '../../shared/lib/lazy-dynamic/dynamic-no-ssr' -import { staticGenerationAsyncStorage } from './static-generation-async-storage' +import { staticGenerationAsyncStorage } from './static-generation-async-storage.external' export function bailoutToClientRendering(): boolean | never { const staticGenerationStore = staticGenerationAsyncStorage.getStore() diff --git a/packages/next/src/client/components/headers.ts b/packages/next/src/client/components/headers.ts index d090264391e7c..a0a27a184cbfe 100644 --- a/packages/next/src/client/components/headers.ts +++ b/packages/next/src/client/components/headers.ts @@ -4,8 +4,8 @@ import { } from '../../server/web/spec-extension/adapters/request-cookies' import { HeadersAdapter } from '../../server/web/spec-extension/adapters/headers' import { RequestCookies } from '../../server/web/spec-extension/cookies' -import { requestAsyncStorage } from './request-async-storage' -import { actionAsyncStorage } from './action-async-storage' +import { requestAsyncStorage } from './request-async-storage.external' +import { actionAsyncStorage } from './action-async-storage.external' import { staticGenerationBailout } from './static-generation-bailout' import { DraftMode } from './draft-mode' @@ -17,7 +17,6 @@ export function headers() { ) { return HeadersAdapter.seal(new Headers({})) } - const requestStore = requestAsyncStorage.getStore() if (!requestStore) { throw new Error( diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx index 3e410a93fb0ca..1f0ffff7e2de8 100644 --- a/packages/next/src/client/components/layout-router.tsx +++ b/packages/next/src/client/components/layout-router.tsx @@ -1,6 +1,6 @@ 'use client' -import type { ChildSegmentMap } from '../../shared/lib/app-router-context' +import type { ChildSegmentMap } from '../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightSegmentPath, @@ -17,7 +17,7 @@ import { LayoutRouterContext, GlobalLayoutRouterContext, TemplateContext, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import { fetchServerResponse } from './router-reducer/fetch-server-response' import { createInfinitePromise } from './infinite-promise' import { ErrorBoundary } from './error-boundary' diff --git a/packages/next/src/client/components/navigation.ts b/packages/next/src/client/components/navigation.ts index bf6a56100080d..b3d69dcb065e8 100644 --- a/packages/next/src/client/components/navigation.ts +++ b/packages/next/src/client/components/navigation.ts @@ -4,11 +4,11 @@ import { AppRouterContext, GlobalLayoutRouterContext, LayoutRouterContext, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import { SearchParamsContext, PathnameContext, -} from '../../shared/lib/hooks-client-context' +} from '../../shared/lib/hooks-client-context.shared-runtime' import { clientHookInServerComponentError } from './client-hook-in-server-component-error' import { getSegmentValue } from './router-reducer/reducers/get-segment-value' @@ -111,12 +111,12 @@ export function usePathname(): string { export { ServerInsertedHTMLContext, useServerInsertedHTML, -} from '../../shared/lib/server-inserted-html' +} from '../../shared/lib/server-inserted-html.shared-runtime' /** * Get the router methods. For example router.push('/dashboard') */ -export function useRouter(): import('../../shared/lib/app-router-context').AppRouterInstance { +export function useRouter(): import('../../shared/lib/app-router-context.shared-runtime').AppRouterInstance { clientHookInServerComponentError('useRouter') const router = useContext(AppRouterContext) if (router === null) { diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts index 4d92a279c3ed5..d37fce9851e91 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts @@ -1,5 +1,5 @@ import { useCallback, useContext, useEffect, useRef } from 'react' -import { GlobalLayoutRouterContext } from '../../../../../shared/lib/app-router-context' +import { GlobalLayoutRouterContext } from '../../../../../shared/lib/app-router-context.shared-runtime' import { getSocketUrl } from './get-socket-url' export function useWebsocket(assetPrefix: string) { diff --git a/packages/next/src/client/components/redirect-boundary.tsx b/packages/next/src/client/components/redirect-boundary.tsx index 23e5493ae83fb..8d407fd6e9d6e 100644 --- a/packages/next/src/client/components/redirect-boundary.tsx +++ b/packages/next/src/client/components/redirect-boundary.tsx @@ -1,6 +1,6 @@ 'use client' import React, { useEffect } from 'react' -import { AppRouterInstance } from '../../shared/lib/app-router-context' +import { AppRouterInstance } from '../../shared/lib/app-router-context.shared-runtime' import { useRouter } from './navigation' import { RedirectType, diff --git a/packages/next/src/client/components/redirect.ts b/packages/next/src/client/components/redirect.ts index 10e72bc1ccbef..b9a2cfebd883f 100644 --- a/packages/next/src/client/components/redirect.ts +++ b/packages/next/src/client/components/redirect.ts @@ -1,4 +1,4 @@ -import { requestAsyncStorage } from './request-async-storage' +import { requestAsyncStorage } from './request-async-storage.external' import type { ResponseCookies } from '../../server/web/spec-extension/cookies' const REDIRECT_ERROR_CODE = 'NEXT_REDIRECT' diff --git a/packages/next/src/client/components/render-from-template-context.tsx b/packages/next/src/client/components/render-from-template-context.tsx index be486842c4f33..c1755cc5056bf 100644 --- a/packages/next/src/client/components/render-from-template-context.tsx +++ b/packages/next/src/client/components/render-from-template-context.tsx @@ -1,7 +1,7 @@ 'use client' import React, { useContext } from 'react' -import { TemplateContext } from '../../shared/lib/app-router-context' +import { TemplateContext } from '../../shared/lib/app-router-context.shared-runtime' export default function RenderFromTemplateContext(): JSX.Element { const children = useContext(TemplateContext) diff --git a/packages/next/src/client/components/request-async-storage.ts b/packages/next/src/client/components/request-async-storage.external.ts similarity index 100% rename from packages/next/src/client/components/request-async-storage.ts rename to packages/next/src/client/components/request-async-storage.external.ts diff --git a/packages/next/src/client/components/router-reducer/apply-flight-data.ts b/packages/next/src/client/components/router-reducer/apply-flight-data.ts index e7a2f11a84f48..003d0a5cde9e4 100644 --- a/packages/next/src/client/components/router-reducer/apply-flight-data.ts +++ b/packages/next/src/client/components/router-reducer/apply-flight-data.ts @@ -1,4 +1,7 @@ -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import { FlightDataPath } from '../../../server/app-render/types' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx b/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx index a6052636ef256..414b553c63249 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import type { FlightRouterState } from '../../../server/app-render/types' -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from './create-initial-router-state' const buildId = 'development' diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts index 7f7cca2003b0b..94fdabb9b577a 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts @@ -1,8 +1,8 @@ import type { ReactNode } from 'react' -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' -import { CacheStates } from '../../../shared/lib/app-router-context' +import { CacheStates } from '../../../shared/lib/app-router-context.shared-runtime' import { createHrefFromUrl } from './create-href-from-url' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' import { extractPathFromFlightRouterState } from './compute-changed-path' diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx index 28f8c3412ab3a..648069ea76986 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx @@ -1,7 +1,10 @@ import React from 'react' import { fetchServerResponse } from './fetch-server-response' import { fillCacheWithDataProperty } from './fill-cache-with-data-property' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' describe('fillCacheWithDataProperty', () => { it('should add data property', () => { const fetchServerResponseMock: jest.Mock< diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts index 81df295dba302..42df61a952af5 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts @@ -1,5 +1,8 @@ import { FlightSegmentPath } from '../../../server/app-render/types' -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import { createRouterCacheKey } from './create-router-cache-key' import { fetchServerResponse } from './fetch-server-response' diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx index 187f86a478751..ac888a3ede0ff 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightData } from '../../../server/app-render/types' const getFlightData = (): FlightData => { diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts index 5d48eaee9ef9f..7e9a93699fb65 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts @@ -1,4 +1,7 @@ -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightDataPath } from '../../../server/app-render/types' import { invalidateCacheByRouterState } from './invalidate-cache-by-router-state' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' diff --git a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx index 606440a96f9c9..1edbeffd7b3e9 100644 --- a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx +++ b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightData } from '../../../server/app-render/types' const getFlightData = (): FlightData => { diff --git a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts index c5ddedd52351e..f558edfab2f1e 100644 --- a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts +++ b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts @@ -1,4 +1,7 @@ -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' import { createRouterCacheKey } from './create-router-cache-key' diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx index 915f09cae0cae..8c23c47d42d74 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx @@ -1,7 +1,10 @@ import React from 'react' import type { FlightData } from '../../../server/app-render/types' import { invalidateCacheBelowFlightSegmentPath } from './invalidate-cache-below-flight-segmentpath' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' const getFlightData = (): FlightData => { diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts index ac343f8d79679..d637d850b145a 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts @@ -1,4 +1,4 @@ -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightSegmentPath } from '../../../server/app-render/types' import { createRouterCacheKey } from './create-router-cache-key' diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx index 65ce9e42c05ee..bdd819b0614d9 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import { invalidateCacheByRouterState } from './invalidate-cache-by-router-state' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' describe('invalidateCacheByRouterState', () => { diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts index 820e5909bf031..1ec39ae9e35fd 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts @@ -1,4 +1,4 @@ -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' import { createRouterCacheKey } from './create-router-cache-key' diff --git a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx index 807374c855577..2d4cdef348b1e 100644 --- a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx @@ -3,7 +3,7 @@ import type { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { findHeadInCache } from './find-head-in-cache' describe('findHeadInCache', () => { diff --git a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts index f4d5e768b9808..08dcefc65f2ce 100644 --- a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts +++ b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts @@ -1,5 +1,5 @@ import type { FlightRouterState } from '../../../../server/app-render/types' -import type { CacheNode } from '../../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../../shared/lib/app-router-context.shared-runtime' import { createRouterCacheKey } from '../create-router-cache-key' export function findHeadInCache( diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx index 6ff039d14cf54..4ed01fed08c83 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx @@ -79,7 +79,7 @@ import { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { NavigateAction, diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index fe8a4a24c4b8b..e47c42b2aa60a 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -1,7 +1,7 @@ import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightSegmentPath, diff --git a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx index 8055123367a94..bbbee6ff5f2a4 100644 --- a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx @@ -36,7 +36,7 @@ import { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { PrefetchAction, diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx index 1ffbb376e2f96..9ed54e8994d2e 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx @@ -46,7 +46,7 @@ import { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { RefreshAction, ACTION_REFRESH } from '../router-reducer-types' import { refreshReducer } from './refresh-reducer' diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts index cd87ef3802b00..bd6dfc4ef9047 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts @@ -11,7 +11,7 @@ import { } from '../router-reducer-types' import { handleExternalUrl } from './navigate-reducer' import { handleMutable } from '../handle-mutable' -import { CacheStates } from '../../../../shared/lib/app-router-context' +import { CacheStates } from '../../../../shared/lib/app-router-context.shared-runtime' import { fillLazyItemsTillLeafWithHead } from '../fill-lazy-items-till-leaf-with-head' export function refreshReducer( diff --git a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx index b11f39b141ccf..36c978926517f 100644 --- a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx @@ -3,7 +3,7 @@ import type { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { RestoreAction, ACTION_RESTORE } from '../router-reducer-types' import { restoreReducer } from './restore-reducer' diff --git a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts index 3b8fa6acb4013..0c6caaba746ca 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts @@ -27,7 +27,7 @@ import { createHrefFromUrl } from '../create-href-from-url' import { handleExternalUrl } from './navigate-reducer' import { applyRouterStatePatchToTree } from '../apply-router-state-patch-to-tree' import { isNavigatingToNewRootLayout } from '../is-navigating-to-new-root-layout' -import { CacheStates } from '../../../../shared/lib/app-router-context' +import { CacheStates } from '../../../../shared/lib/app-router-context.shared-runtime' import { handleMutable } from '../handle-mutable' import { fillLazyItemsTillLeafWithHead } from '../fill-lazy-items-till-leaf-with-head' diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx index 37ef29296c891..0540c02079cb1 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx @@ -41,7 +41,7 @@ jest.mock('../fetch-server-response', () => { import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { ServerPatchAction, diff --git a/packages/next/src/client/components/router-reducer/router-reducer-types.ts b/packages/next/src/client/components/router-reducer/router-reducer-types.ts index 48840a29db6db..defbb657c7c42 100644 --- a/packages/next/src/client/components/router-reducer/router-reducer-types.ts +++ b/packages/next/src/client/components/router-reducer/router-reducer-types.ts @@ -1,4 +1,4 @@ -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightData, diff --git a/packages/next/src/client/components/static-generation-async-storage.ts b/packages/next/src/client/components/static-generation-async-storage.external.ts similarity index 100% rename from packages/next/src/client/components/static-generation-async-storage.ts rename to packages/next/src/client/components/static-generation-async-storage.external.ts diff --git a/packages/next/src/client/components/static-generation-bailout.ts b/packages/next/src/client/components/static-generation-bailout.ts index c5072218f035c..4d35150664251 100644 --- a/packages/next/src/client/components/static-generation-bailout.ts +++ b/packages/next/src/client/components/static-generation-bailout.ts @@ -1,5 +1,5 @@ import { DynamicServerError } from './hooks-server-context' -import { staticGenerationAsyncStorage } from './static-generation-async-storage' +import { staticGenerationAsyncStorage } from './static-generation-async-storage.external' class StaticGenBailoutError extends Error { code = 'NEXT_STATIC_GEN_BAILOUT' diff --git a/packages/next/src/client/image-component.tsx b/packages/next/src/client/image-component.tsx index 3f2183c004b10..321b07ecd0a5f 100644 --- a/packages/next/src/client/image-component.tsx +++ b/packages/next/src/client/image-component.tsx @@ -25,9 +25,9 @@ import type { ImageLoaderProps, } from '../shared/lib/image-config' import { imageConfigDefault } from '../shared/lib/image-config' -import { ImageConfigContext } from '../shared/lib/image-config-context' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' import { warnOnce } from '../shared/lib/utils/warn-once' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' // @ts-ignore - This is replaced by webpack alias import defaultLoader from 'next/dist/shared/lib/image-loader' diff --git a/packages/next/src/client/index.tsx b/packages/next/src/client/index.tsx index f7c163f020d31..0f8a95c93746e 100644 --- a/packages/next/src/client/index.tsx +++ b/packages/next/src/client/index.tsx @@ -10,16 +10,16 @@ import type { import React from 'react' import ReactDOM from 'react-dom/client' -import { HeadManagerContext } from '../shared/lib/head-manager-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' import mitt, { MittEmitter } from '../shared/lib/mitt' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import { handleSmoothScroll } from '../shared/lib/router/utils/handle-smooth-scroll' import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import { urlQueryToSearchParams, assign, } from '../shared/lib/router/utils/querystring' -import { setConfig } from '../shared/lib/runtime-config' +import { setConfig } from '../shared/lib/runtime-config.shared-runtime' import { getURL, loadGetInitialProps, @@ -34,17 +34,17 @@ import measureWebVitals from './performance-relayer' import { RouteAnnouncer } from './route-announcer' import { createRouter, makePublicRouterInstance } from './router' import { getProperError } from '../lib/is-error' -import { ImageConfigContext } from '../shared/lib/image-config-context' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' import { ImageConfigComplete } from '../shared/lib/image-config' import { removeBasePath } from './remove-base-path' import { hasBasePath } from './has-base-path' -import { AppRouterContext } from '../shared/lib/app-router-context' +import { AppRouterContext } from '../shared/lib/app-router-context.shared-runtime' import { adaptForAppRouterInstance, adaptForSearchParams, PathnameContextProviderAdapter, -} from '../shared/lib/router/adapters' -import { SearchParamsContext } from '../shared/lib/hooks-client-context' +} from '../shared/lib/router/adapters.shared-runtime' +import { SearchParamsContext } from '../shared/lib/hooks-client-context.shared-runtime' import onRecoverableError from './on-recoverable-error' import tracer from './tracing/tracer' import reportToSocket from './tracing/report-to-socket' diff --git a/packages/next/src/client/legacy/image.tsx b/packages/next/src/client/legacy/image.tsx index d1456477bacf6..07ec2e217c200 100644 --- a/packages/next/src/client/legacy/image.tsx +++ b/packages/next/src/client/legacy/image.tsx @@ -16,7 +16,7 @@ import { VALID_LOADERS, } from '../../shared/lib/image-config' import { useIntersection } from '../use-intersection' -import { ImageConfigContext } from '../../shared/lib/image-config-context' +import { ImageConfigContext } from '../../shared/lib/image-config-context.shared-runtime' import { warnOnce } from '../../shared/lib/utils/warn-once' import { normalizePathTrailingSlash } from '../normalize-trailing-slash' diff --git a/packages/next/src/client/link.tsx b/packages/next/src/client/link.tsx index 94226c8caa5c0..7a15dee249e26 100644 --- a/packages/next/src/client/link.tsx +++ b/packages/next/src/client/link.tsx @@ -12,12 +12,12 @@ import { isLocalURL } from '../shared/lib/router/utils/is-local-url' import { formatUrl } from '../shared/lib/router/utils/format-url' import { isAbsoluteUrl } from '../shared/lib/utils' import { addLocale } from './add-locale' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import { AppRouterContext, AppRouterInstance, PrefetchOptions as AppRouterPrefetchOptions, -} from '../shared/lib/app-router-context' +} from '../shared/lib/app-router-context.shared-runtime' import { useIntersection } from './use-intersection' import { getDomainLocale } from './get-domain-locale' import { addBasePath } from './add-base-path' diff --git a/packages/next/src/client/router.ts b/packages/next/src/client/router.ts index 342ecb623df74..f43126691e5e5 100644 --- a/packages/next/src/client/router.ts +++ b/packages/next/src/client/router.ts @@ -2,7 +2,7 @@ import React from 'react' import Router from '../shared/lib/router/router' import type { NextRouter } from '../shared/lib/router/router' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import isError from '../lib/is-error' type SingletonRouterBase = { diff --git a/packages/next/src/client/script.tsx b/packages/next/src/client/script.tsx index a4b2c6dcd3184..f695e691c482d 100644 --- a/packages/next/src/client/script.tsx +++ b/packages/next/src/client/script.tsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom' import React, { useEffect, useContext, useRef } from 'react' import { ScriptHTMLAttributes } from 'react' -import { HeadManagerContext } from '../shared/lib/head-manager-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' import { DOMAttributeNames } from './head-manager' import { requestIdleCallback } from './request-idle-callback' diff --git a/packages/next/src/export/worker.ts b/packages/next/src/export/worker.ts index 31f58755996a4..b9fb7b9afd60a 100644 --- a/packages/next/src/export/worker.ts +++ b/packages/next/src/export/worker.ts @@ -59,7 +59,7 @@ import { RSC, } from '../client/components/app-router-headers' -const envConfig = require('../shared/lib/runtime-config') +const envConfig = require('../shared/lib/runtime-config.shared-runtime') ;(globalThis as any).__NEXT_DATA__ = { nextExport: true, @@ -307,8 +307,10 @@ export default async function exportPage({ await promises.mkdir(baseDir, { recursive: true }) let renderResult: RenderResult | undefined let curRenderOpts: RenderOpts = {} - const { renderToHTML } = - require('../server/render') as typeof import('../server/render') + const renderToHTML = + require('../server/future/route-modules/pages/module.compiled') + .renderToHTML as typeof import('../server/render').renderToHTML + let renderMethod = renderToHTML let inAmpMode = false, hybridAmp = false @@ -479,7 +481,6 @@ export default async function exportPage({ const module = await RouteModuleLoader.load( filename ) - // Call the handler with the request and context from the module. const response = await module.handle(request, context) @@ -535,8 +536,9 @@ export default async function exportPage({ results.fromBuildExportRevalidate = 0 } } else { - const { renderToHTMLOrFlight } = - require('../server/app-render/app-render') as typeof import('../server/app-render/app-render') + const renderToHTMLOrFlight = + require('../server/future/route-modules/app-page/module.compiled') + .renderToHTMLOrFlight as typeof import('../server/app-render/app-render').renderToHTMLOrFlight try { curRenderOpts.params ||= {} diff --git a/packages/next/src/lib/chalk.ts b/packages/next/src/lib/chalk.ts index 8e40472953f8f..d0939d9148b97 100644 --- a/packages/next/src/lib/chalk.ts +++ b/packages/next/src/lib/chalk.ts @@ -1,6 +1,6 @@ let chalk: typeof import('next/dist/compiled/chalk') -if (process.env.NEXT_RUNTIME === 'edge') { +if (process.env.NEXT_RUNTIME === 'edge' || process.env.NEXT_MINIMAL) { chalk = require('./web/chalk').default } else { chalk = require('next/dist/compiled/chalk') diff --git a/packages/next/src/lib/constants.ts b/packages/next/src/lib/constants.ts index fc94397c6aca7..46f85311a9f0a 100644 --- a/packages/next/src/lib/constants.ts +++ b/packages/next/src/lib/constants.ts @@ -132,6 +132,10 @@ const WEBPACK_LAYERS_NAMES = { * The server bundle layer for metadata routes. */ appMetadataRoute: 'app-metadata-route', + /** + * The layer for the server bundle for App Route handlers. + */ + appRouteHandler: 'app-route-handler', } export const WEBPACK_LAYERS = { @@ -141,6 +145,7 @@ export const WEBPACK_LAYERS = { WEBPACK_LAYERS_NAMES.reactServerComponents, WEBPACK_LAYERS_NAMES.actionBrowser, WEBPACK_LAYERS_NAMES.appMetadataRoute, + WEBPACK_LAYERS_NAMES.appRouteHandler, ], }, } diff --git a/packages/next/src/pages/_document.tsx b/packages/next/src/pages/_document.tsx index a8526011c5d33..f0a29cb15d1fc 100644 --- a/packages/next/src/pages/_document.tsx +++ b/packages/next/src/pages/_document.tsx @@ -17,8 +17,11 @@ import { BuildManifest, getPageFiles } from '../server/get-page-files' import { htmlEscapeJsonString } from '../server/htmlescape' import isError from '../lib/is-error' -import { HtmlContext, useHtmlContext } from '../shared/lib/html-context' -import type { HtmlProps } from '../shared/lib/html-context' +import { + HtmlContext, + useHtmlContext, +} from '../shared/lib/html-context.shared-runtime' +import type { HtmlProps } from '../shared/lib/html-context.shared-runtime' export { DocumentContext, DocumentInitialProps, DocumentProps } diff --git a/packages/next/src/server/app-render/action-handler.ts b/packages/next/src/server/app-render/action-handler.ts index 6b8bc3228a005..9f6059ec48f5a 100644 --- a/packages/next/src/server/app-render/action-handler.ts +++ b/packages/next/src/server/app-render/action-handler.ts @@ -19,10 +19,10 @@ import { isRedirectError, } from '../../client/components/redirect' import RenderResult from '../render-result' -import { StaticGenerationStore } from '../../client/components/static-generation-async-storage' +import { StaticGenerationStore } from '../../client/components/static-generation-async-storage.external' import { FlightRenderResult } from './flight-render-result' import { ActionResult } from './types' -import { ActionAsyncStorage } from '../../client/components/action-async-storage' +import { ActionAsyncStorage } from '../../client/components/action-async-storage.external' import { filterReqHeaders, actionsForbiddenHeaders, @@ -31,7 +31,8 @@ import { appendMutableCookies, getModifiedCookieValues, } from '../web/spec-extension/adapters/request-cookies' -import { RequestStore } from '../../client/components/request-async-storage' + +import { RequestStore } from '../../client/components/request-async-storage.external' import { NEXT_CACHE_REVALIDATED_TAGS_HEADER, NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER, diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 30749ac66a83d..1dd2eb8f7c639 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -11,9 +11,9 @@ import type { Segment, } from './types' -import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' +import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' import type { StaticGenerationBailout } from '../../client/components/static-generation-bailout' -import type { RequestAsyncStorage } from '../../client/components/request-async-storage' +import type { RequestAsyncStorage } from '../../client/components/request-async-storage.external' import React from 'react' import { createServerComponentRenderer } from './create-server-components-renderer' @@ -286,10 +286,13 @@ export async function renderToHTMLOrFlight( * that we need to resolve the final metadata. */ - const requestId = - process.env.NEXT_RUNTIME === 'edge' - ? crypto.randomUUID() - : require('next/dist/compiled/nanoid').nanoid() + let requestId: string + + if (process.env.NEXT_RUNTIME === 'edge') { + requestId = crypto.randomUUID() + } else { + requestId = require('next/dist/compiled/nanoid').nanoid() + } const LayoutRouter = ComponentMod.LayoutRouter as typeof import('../../client/components/layout-router').default @@ -1392,7 +1395,7 @@ export async function renderToHTMLOrFlight( ) const { HeadManagerContext } = - require('../../shared/lib/head-manager-context') as typeof import('../../shared/lib/head-manager-context') + require('../../shared/lib/head-manager-context.shared-runtime') as typeof import('../../shared/lib/head-manager-context.shared-runtime') // On each render, create a new `ServerInsertedHTML` context to capture // injected nodes from user code (`useServerInsertedHTML`). diff --git a/packages/next/src/server/app-render/entry-base.ts b/packages/next/src/server/app-render/entry-base.ts index 6bc5fd7e7ace1..0cc53e0214d86 100644 --- a/packages/next/src/server/app-render/entry-base.ts +++ b/packages/next/src/server/app-render/entry-base.ts @@ -1,36 +1,26 @@ -const { default: AppRouter } = - require('next/dist/client/components/app-router') as typeof import('../../client/components/app-router') -const { default: LayoutRouter } = - require('next/dist/client/components/layout-router') as typeof import('../../client/components/layout-router') -const { default: RenderFromTemplateContext } = - require('next/dist/client/components/render-from-template-context') as typeof import('../../client/components/render-from-template-context') - -const { staticGenerationAsyncStorage } = - require('next/dist/client/components/static-generation-async-storage') as typeof import('../../client/components/static-generation-async-storage') - -const { requestAsyncStorage } = - require('next/dist/client/components/request-async-storage') as typeof import('../../client/components/request-async-storage') -const { actionAsyncStorage } = - require('next/dist/client/components/action-async-storage') as typeof import('../../client/components/action-async-storage') - -const { staticGenerationBailout } = - require('next/dist/client/components/static-generation-bailout') as typeof import('../../client/components/static-generation-bailout') -const { default: StaticGenerationSearchParamsBailoutProvider } = - require('next/dist/client/components/static-generation-searchparams-bailout-provider') as typeof import('../../client/components/static-generation-searchparams-bailout-provider') -const { createSearchParamsBailoutProxy } = - require('next/dist/client/components/searchparams-bailout-proxy') as typeof import('../../client/components/searchparams-bailout-proxy') - -const serverHooks = - require('next/dist/client/components/hooks-server-context') as typeof import('../../client/components/hooks-server-context') - const { renderToReadableStream, decodeReply, decodeAction, // eslint-disable-next-line import/no-extraneous-dependencies } = require('react-server-dom-webpack/server.edge') -const { preloadStyle, preloadFont, preconnect } = - require('next/dist/server/app-render/rsc/preloads') as typeof import('../../server/app-render/rsc/preloads') + +import AppRouter from '../../client/components/app-router' +import LayoutRouter from '../../client/components/layout-router' +import RenderFromTemplateContext from '../../client/components/render-from-template-context' +import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' +import { requestAsyncStorage } from '../../client/components/request-async-storage.external' +import { actionAsyncStorage } from '../../client/components/action-async-storage.external' +import { staticGenerationBailout } from '../../client/components/static-generation-bailout' +import StaticGenerationSearchParamsBailoutProvider from '../../client/components/static-generation-searchparams-bailout-provider' +import { createSearchParamsBailoutProxy } from '../../client/components/searchparams-bailout-proxy' +import * as serverHooks from '../../client/components/hooks-server-context' + +import { + preloadStyle, + preloadFont, + preconnect, +} from '../../server/app-render/rsc/preloads' const { NotFoundBoundary } = require('next/dist/client/components/not-found-boundary') as typeof import('../../client/components/not-found-boundary') diff --git a/packages/next/src/server/app-render/server-inserted-html.tsx b/packages/next/src/server/app-render/server-inserted-html.tsx index f044c24feaba3..764dc62792077 100644 --- a/packages/next/src/server/app-render/server-inserted-html.tsx +++ b/packages/next/src/server/app-render/server-inserted-html.tsx @@ -2,7 +2,7 @@ // elements into the HTML stream. import React from 'react' -import { ServerInsertedHTMLContext } from '../../shared/lib/server-inserted-html' +import { ServerInsertedHTMLContext } from '../../shared/lib/server-inserted-html.shared-runtime' export function createServerInsertedHTML() { const serverInsertedHTMLCallbacks: (() => React.ReactNode)[] = [] diff --git a/packages/next/src/server/async-storage/request-async-storage-wrapper.ts b/packages/next/src/server/async-storage/request-async-storage-wrapper.ts index 50795855c53d4..1376ecfb197cd 100644 --- a/packages/next/src/server/async-storage/request-async-storage-wrapper.ts +++ b/packages/next/src/server/async-storage/request-async-storage-wrapper.ts @@ -1,7 +1,7 @@ import type { BaseNextRequest, BaseNextResponse } from '../base-http' import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http' import type { AsyncLocalStorage } from 'async_hooks' -import type { RequestStore } from '../../client/components/request-async-storage' +import type { RequestStore } from '../../client/components/request-async-storage.external' import type { RenderOpts } from '../app-render/types' import type { AsyncStorageWrapper } from './async-storage-wrapper' import type { NextRequest } from '../web/spec-extension/request' diff --git a/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts b/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts index a28ac0e8ecb2a..d5adfc9de38b4 100644 --- a/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts +++ b/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts @@ -1,5 +1,5 @@ import type { AsyncStorageWrapper } from './async-storage-wrapper' -import type { StaticGenerationStore } from '../../client/components/static-generation-async-storage' +import type { StaticGenerationStore } from '../../client/components/static-generation-async-storage.external' import type { AsyncLocalStorage } from 'async_hooks' import type { IncrementalCache } from '../lib/incremental-cache' diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index d826f2bdd4e04..fab999db35ddc 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -55,7 +55,7 @@ import { getCookieParser, checkIsOnDemandRevalidate, } from './api-utils' -import { setConfig } from '../shared/lib/runtime-config' +import { setConfig } from '../shared/lib/runtime-config.shared-runtime' import { setRevalidateHeaders } from './send-payload/revalidate-headers' import { execOnce } from '../shared/lib/utils' @@ -427,7 +427,11 @@ export default abstract class Server { } = this.nextConfig this.buildId = this.getBuildId() - this.minimalMode = minimalMode || !!process.env.NEXT_PRIVATE_MINIMAL_MODE + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + const minimalModeKey = 'minimalMode' + this[minimalModeKey] = + minimalMode || !!process.env.NEXT_PRIVATE_MINIMAL_MODE this.hasAppDir = this.getHasAppDir(dev) const serverComponents = this.hasAppDir diff --git a/packages/next/src/server/dev/next-dev-server.ts b/packages/next/src/server/dev/next-dev-server.ts index 1eca11b7358cb..839fa1b13c0e4 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -41,7 +41,7 @@ import { UnwrapPromise, withCoalescedInvoke, } from '../../lib/coalesced-function' -import { loadDefaultErrorComponents } from '../load-components' +import { loadDefaultErrorComponents } from '../load-default-error-components' import { DecodeError, MiddlewareNotFoundError } from '../../shared/lib/utils' import * as Log from '../../build/output/log' import isError, { getProperError } from '../../lib/is-error' diff --git a/packages/next/src/server/dev/static-paths-worker.ts b/packages/next/src/server/dev/static-paths-worker.ts index 68932df7a36b0..ddd6526e52f23 100644 --- a/packages/next/src/server/dev/static-paths-worker.ts +++ b/packages/next/src/server/dev/static-paths-worker.ts @@ -14,8 +14,10 @@ import { loadComponents } from '../load-components' import { setHttpClientAndAgentOptions } from '../setup-http-agent-env' import { IncrementalCache } from '../lib/incremental-cache' import * as serverHooks from '../../client/components/hooks-server-context' -import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' -import { AppRouteRouteModule } from '../future/route-modules/app-route/module' +import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' + +const { AppRouteRouteModule } = + require('../future/route-modules/app-route/module.compiled') as typeof import('../future/route-modules/app-route/module') type RuntimeConfig = any @@ -56,7 +58,7 @@ export async function loadStaticPaths({ fallback?: boolean | 'blocking' }> { // update work memory runtime-config - require('../../shared/lib/runtime-config').setConfig(config) + require('../../shared/lib/runtime-config.shared-runtime').setConfig(config) setHttpClientAndAgentOptions({ httpAgentOptions, }) diff --git a/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts b/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts index 3283eb00a53f5..6f70685df7e75 100644 --- a/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts +++ b/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts @@ -7,7 +7,10 @@ export class NodeModuleLoader implements ModuleLoader { public async load(id: string): Promise { if (process.env.NEXT_RUNTIME !== 'edge') { // Need to `await` to cover the case that route is marked ESM modules by ESM escalation. - return await require(id) + return await (process.env.NEXT_MINIMAL + ? // @ts-ignore + __non_webpack_require__(id) + : require(id)) } throw new Error('NodeModuleLoader is not supported in edge runtime.') diff --git a/packages/next/src/server/future/route-modules/app-page/module.compiled.ts b/packages/next/src/server/future/route-modules/app-page/module.compiled.ts new file mode 100644 index 0000000000000..78601739acbe5 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-page/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/app-page/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/app-page.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/app-page-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/app-page.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/app-page/module.ts b/packages/next/src/server/future/route-modules/app-page/module.ts index 418e37420d7e9..daa0291a1c8b4 100644 --- a/packages/next/src/server/future/route-modules/app-page/module.ts +++ b/packages/next/src/server/future/route-modules/app-page/module.ts @@ -11,6 +11,7 @@ import { type RouteModuleOptions, type RouteModuleHandleContext, } from '../route-module' +import * as sharedModules from './shared-modules' type AppPageUserlandModule = { /** @@ -34,6 +35,8 @@ export class AppPageRouteModule extends RouteModule< AppPageRouteDefinition, AppPageUserlandModule > { + static readonly sharedModules = sharedModules + public render( req: IncomingMessage, res: ServerResponse, @@ -49,4 +52,6 @@ export class AppPageRouteModule extends RouteModule< } } +export { renderToHTMLOrFlight } + export default AppPageRouteModule diff --git a/packages/next/src/server/future/route-modules/app-page/shared-modules.ts b/packages/next/src/server/future/route-modules/app-page/shared-modules.ts new file mode 100644 index 0000000000000..e986c1bad3894 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-page/shared-modules.ts @@ -0,0 +1,13 @@ +// the name of the export has to be the camelCase version of the file name (without the extension) +export * as headManagerContext from '../../../../shared/lib/head-manager-context.shared-runtime' +export * as serverInsertedHtml from '../../../../shared/lib/server-inserted-html.shared-runtime' +export * as appRouterContext from '../../../../shared/lib/app-router-context.shared-runtime' +export * as hooksClientContext from '../../../../shared/lib/hooks-client-context.shared-runtime' +export * as routerContext from '../../../../shared/lib/router-context.shared-runtime' +export * as htmlContext from '../../../../shared/lib/html-context.shared-runtime' +export * as ampContext from '../../../../shared/lib/amp-context.shared-runtime' +export * as adapters from '../../../../shared/lib/router/adapters.shared-runtime' +export * as loadableContext from '../../../../shared/lib/loadable-context.shared-runtime' +export * as imageConfigContext from '../../../../shared/lib/image-config-context.shared-runtime' +export * as runtimeConfig from '../../../../shared/lib/runtime-config.shared-runtime' +export * as loadable from '../../../../shared/lib/loadable.shared-runtime' diff --git a/packages/next/src/server/future/route-modules/app-route/module.compiled.ts b/packages/next/src/server/future/route-modules/app-route/module.compiled.ts new file mode 100644 index 0000000000000..f5909104bc772 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-route/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/app-route/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/app-route.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/app-route-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/app-route.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/app-route/module.ts b/packages/next/src/server/future/route-modules/app-route/module.ts index bf47738d35fea..d1d36d31501e4 100644 --- a/packages/next/src/server/future/route-modules/app-route/module.ts +++ b/packages/next/src/server/future/route-modules/app-route/module.ts @@ -35,22 +35,14 @@ import { appendMutableCookies } from '../../../web/spec-extension/adapters/reque import { RouteKind } from '../../route-kind' import { parsedUrlQueryToParams } from './helpers/parsed-url-query-to-params' -// These are imported weirdly like this because of the way that the bundling -// works. We need to import the built files from the dist directory, but we -// can't do that directly because we need types from the source files. So we -// import the types from the source files and then import the built files. -const { requestAsyncStorage } = - require('next/dist/client/components/request-async-storage') as typeof import('../../../../client/components/request-async-storage') -const { staticGenerationAsyncStorage } = - require('next/dist/client/components/static-generation-async-storage') as typeof import('../../../../client/components/static-generation-async-storage') -const serverHooks = - require('next/dist/client/components/hooks-server-context') as typeof import('../../../../client/components/hooks-server-context') -const headerHooks = - require('next/dist/client/components/headers') as typeof import('../../../../client/components/headers') -const { staticGenerationBailout } = - require('next/dist/client/components/static-generation-bailout') as typeof import('../../../../client/components/static-generation-bailout') -const { actionAsyncStorage } = - require('next/dist/client/components/action-async-storage') as typeof import('../../../../client/components/action-async-storage') +import * as serverHooks from '../../../../client/components/hooks-server-context' +import * as headerHooks from '../../../../client/components/headers' +import { staticGenerationBailout } from '../../../../client/components/static-generation-bailout' + +import { requestAsyncStorage } from '../../../../client/components/request-async-storage.external' +import { staticGenerationAsyncStorage } from '../../../../client/components/static-generation-async-storage.external' +import { actionAsyncStorage } from '../../../../client/components/action-async-storage.external' +import * as sharedModules from './shared-modules' /** * AppRouteRouteHandlerContext is the context that is passed to the route @@ -147,6 +139,8 @@ export class AppRouteRouteModule extends RouteModule< */ public readonly staticGenerationBailout = staticGenerationBailout + public static readonly sharedModules = sharedModules + /** * A reference to the mutation related async storage, such as mutations of * cookies. diff --git a/packages/next/src/server/future/route-modules/app-route/shared-modules.ts b/packages/next/src/server/future/route-modules/app-route/shared-modules.ts new file mode 100644 index 0000000000000..e6139d5a69404 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-route/shared-modules.ts @@ -0,0 +1,3 @@ +// the name of the export has to be the camelCase version of the file name (without the extension) +// TODO: remove this. We need it because using notFound from next/navigation imports this file :( +export * as appRouterContext from '../../../../shared/lib/app-router-context.shared-runtime' diff --git a/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts b/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts new file mode 100644 index 0000000000000..ed74c41adb918 --- /dev/null +++ b/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/pages-api/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/pages-api.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/pages-api-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/pages-api.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/pages-api/module.ts b/packages/next/src/server/future/route-modules/pages-api/module.ts index 88dbda73b464c..976daeeec4a87 100644 --- a/packages/next/src/server/future/route-modules/pages-api/module.ts +++ b/packages/next/src/server/future/route-modules/pages-api/module.ts @@ -100,6 +100,16 @@ export class PagesAPIRouteModule extends RouteModule< PagesAPIRouteDefinition, PagesAPIUserlandModule > { + constructor(options: PagesAPIRouteModuleOptions) { + super(options) + + if (typeof options.userland.default !== 'function') { + throw new Error( + `Page ${options.definition.page} does not export a default function.` + ) + } + } + /** * * @param req the incoming server request diff --git a/packages/next/src/server/future/route-modules/pages/module.compiled.ts b/packages/next/src/server/future/route-modules/pages/module.compiled.ts new file mode 100644 index 0000000000000..a935b62abdcad --- /dev/null +++ b/packages/next/src/server/future/route-modules/pages/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/pages/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/pages.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/pages-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/pages.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/pages/module.ts b/packages/next/src/server/future/route-modules/pages/module.ts index dac8ae5546441..e2730ef668901 100644 --- a/packages/next/src/server/future/route-modules/pages/module.ts +++ b/packages/next/src/server/future/route-modules/pages/module.ts @@ -17,7 +17,8 @@ import { type RouteModuleHandleContext, type RouteModuleOptions, } from '../route-module' -import { renderToHTMLImpl } from '../../../render' +import { renderToHTMLImpl, renderToHTML } from '../../../render' +import * as sharedModules from './shared-modules' /** * The userland module for a page. This is the module that is exported from the @@ -104,6 +105,8 @@ export class PagesRouteModule extends RouteModule< > { private readonly components: PagesComponents + static readonly sharedModules = sharedModules + constructor(options: PagesRouteModuleOptions) { super(options) @@ -129,4 +132,7 @@ export class PagesRouteModule extends RouteModule< } } +// needed for the static build +export { renderToHTML } + export default PagesRouteModule diff --git a/packages/next/src/server/future/route-modules/pages/shared-modules.ts b/packages/next/src/server/future/route-modules/pages/shared-modules.ts new file mode 100644 index 0000000000000..55cdfbdeca37c --- /dev/null +++ b/packages/next/src/server/future/route-modules/pages/shared-modules.ts @@ -0,0 +1,12 @@ +// the name of the export has to be the camelCase version of the file name (without the extension) +export * as htmlContext from '../../../../shared/lib/html-context.shared-runtime' +export * as routerContext from '../../../../shared/lib/router-context.shared-runtime' +export * as ampContext from '../../../../shared/lib/amp-context.shared-runtime' +export * as headManagerContext from '../../../../shared/lib/head-manager-context.shared-runtime' +export * as adapters from '../../../../shared/lib/router/adapters.shared-runtime' +export * as loadableContext from '../../../../shared/lib/loadable-context.shared-runtime' +export * as appRouterContext from '../../../../shared/lib/app-router-context.shared-runtime' +export * as hooksClientContext from '../../../../shared/lib/hooks-client-context.shared-runtime' +export * as imageConfigContext from '../../../../shared/lib/image-config-context.shared-runtime' +export * as runtimeConfig from '../../../../shared/lib/runtime-config.shared-runtime' +export * as loadable from '../../../../shared/lib/loadable.shared-runtime' diff --git a/packages/next/src/server/future/route-modules/route-module.ts b/packages/next/src/server/future/route-modules/route-module.ts index 52188ed506dff..a8e5dd6c5945a 100644 --- a/packages/next/src/server/future/route-modules/route-module.ts +++ b/packages/next/src/server/future/route-modules/route-module.ts @@ -44,6 +44,11 @@ export abstract class RouteModule< */ public readonly definition: Readonly + /** + * The shared modules that are exposed and required for the route module. + */ + public static readonly sharedModules: any + constructor({ userland, definition }: RouteModuleOptions) { this.userland = userland this.definition = definition diff --git a/packages/next/src/server/lib/incremental-cache/index.ts b/packages/next/src/server/lib/incremental-cache/index.ts index ae2b11b4dc00f..3032aa685300b 100644 --- a/packages/next/src/server/lib/incremental-cache/index.ts +++ b/packages/next/src/server/lib/incremental-cache/index.ts @@ -131,7 +131,10 @@ export class IncrementalCache { maxMemoryCacheSize = parseInt(process.env.__NEXT_TEST_MAX_ISR_CACHE, 10) } this.dev = dev - this.minimalMode = minimalMode + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + const minimalModeKey = 'minimalMode' + this[minimalModeKey] = minimalMode this.requestHeaders = requestHeaders this.requestProtocol = requestProtocol this.allowedRevalidateHeaderKeys = allowedRevalidateHeaderKeys diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index fb4d02c97b251..080361704b718 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -1,4 +1,4 @@ -import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' +import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' import type * as ServerHooks from '../../client/components/hooks-server-context' import { AppRenderSpan, NextNodeServerSpan } from './trace/constants' diff --git a/packages/next/src/server/lib/server-ipc/index.ts b/packages/next/src/server/lib/server-ipc/index.ts index 8e7bd60a8855d..023cbde2d25d8 100644 --- a/packages/next/src/server/lib/server-ipc/index.ts +++ b/packages/next/src/server/lib/server-ipc/index.ts @@ -115,6 +115,7 @@ export const createWorker = async ( __NEXT_PRIVATE_STANDALONE_CONFIG: process.env.__NEXT_PRIVATE_STANDALONE_CONFIG, NODE_ENV: process.env.NODE_ENV, + __NEXT_PRIVATE_RENDER_RUNTIME: type, __NEXT_PRIVATE_PREBUNDLED_REACT: type === 'app' ? (useServerActions ? 'experimental' : 'next') : '', ...(process.env.NEXT_CPU_PROF diff --git a/packages/next/src/server/lib/trace/constants.ts b/packages/next/src/server/lib/trace/constants.ts index 50eb4528c7dec..1b45358bdf8ed 100644 --- a/packages/next/src/server/lib/trace/constants.ts +++ b/packages/next/src/server/lib/trace/constants.ts @@ -43,7 +43,6 @@ enum NextNodeServerSpan { generatePublicRoutes = 'NextNodeServer.generatePublicRoutes', generateImageRoutes = 'NextNodeServer.generateImageRoutes.route', sendRenderResult = 'NextNodeServer.sendRenderResult', - sendStatic = 'NextNodeServer.sendStatic', proxyRequest = 'NextNodeServer.proxyRequest', runApi = 'NextNodeServer.runApi', render = 'NextNodeServer.render', diff --git a/packages/next/src/server/load-components.ts b/packages/next/src/server/load-components.ts index d2b563493ebe6..39da802ff39b1 100644 --- a/packages/next/src/server/load-components.ts +++ b/packages/next/src/server/load-components.ts @@ -55,7 +55,7 @@ export type LoadComponentsReturnType = { /** * Load manifest file with retries, defaults to 3 attempts. */ -async function loadManifestWithRetries( +export async function loadManifestWithRetries( manifestPath: string, attempts = 3 ): Promise { @@ -87,34 +87,6 @@ async function loadJSManifest( } } -async function loadDefaultErrorComponentsImpl( - distDir: string -): Promise { - const Document = interopDefault(require('next/dist/pages/_document')) - const AppMod = require('next/dist/pages/_app') - const App = interopDefault(AppMod) - - // Load the compiled route module for this builtin error. - // TODO: (wyattjoh) replace this with just exporting the route module when the transition is complete - const ComponentMod = - require('./future/route-modules/pages/builtin/_error') as typeof import('./future/route-modules/pages/builtin/_error') - const Component = ComponentMod.routeModule.userland.default - - return { - App, - Document, - Component, - pageConfig: {}, - buildManifest: await loadManifestWithRetries( - join(distDir, `fallback-${BUILD_MANIFEST}`) - ), - reactLoadableManifest: {}, - ComponentMod, - pathname: '/_error', - routeModule: ComponentMod.routeModule, - } -} - async function loadComponentsImpl({ distDir, pathname, @@ -205,8 +177,3 @@ export const loadComponents = getTracer().wrap( LoadComponentsSpan.loadComponents, loadComponentsImpl ) - -export const loadDefaultErrorComponents = getTracer().wrap( - LoadComponentsSpan.loadDefaultErrorComponents, - loadDefaultErrorComponentsImpl -) diff --git a/packages/next/src/server/load-default-error-components.ts b/packages/next/src/server/load-default-error-components.ts new file mode 100644 index 0000000000000..c390e9180b3d9 --- /dev/null +++ b/packages/next/src/server/load-default-error-components.ts @@ -0,0 +1,78 @@ +import type { + AppType, + DocumentType, + NextComponentType, +} from '../shared/lib/utils' +import type { ClientReferenceManifest } from '../build/webpack/plugins/flight-manifest-plugin' +import type { + PageConfig, + GetStaticPaths, + GetServerSideProps, + GetStaticProps, +} from 'next/types' +import type { RouteModule } from './future/route-modules/route-module' + +import { BUILD_MANIFEST } from '../shared/lib/constants' +import { join } from 'path' +import { BuildManifest } from './get-page-files' +import { interopDefault } from '../lib/interop-default' +import { getTracer } from './lib/trace/tracer' +import { LoadComponentsSpan } from './lib/trace/constants' +import { loadManifestWithRetries } from './load-components' +export type ManifestItem = { + id: number | string + files: string[] +} + +export type ReactLoadableManifest = { [moduleId: string]: ManifestItem } + +export type LoadComponentsReturnType = { + Component: NextComponentType + pageConfig: PageConfig + buildManifest: BuildManifest + subresourceIntegrityManifest?: Record + reactLoadableManifest: ReactLoadableManifest + clientReferenceManifest?: ClientReferenceManifest + serverActionsManifest?: any + Document: DocumentType + App: AppType + getStaticProps?: GetStaticProps + getStaticPaths?: GetStaticPaths + getServerSideProps?: GetServerSideProps + ComponentMod: any + routeModule?: RouteModule + isAppPath?: boolean + pathname: string +} + +async function loadDefaultErrorComponentsImpl( + distDir: string +): Promise { + const Document = interopDefault(require('next/dist/pages/_document')) + const AppMod = require('next/dist/pages/_app') + const App = interopDefault(AppMod) + + // Load the compiled route module for this builtin error. + // TODO: (wyattjoh) replace this with just exporting the route module when the transition is complete + const ComponentMod = + require('./future/route-modules/pages/builtin/_error') as typeof import('./future/route-modules/pages/builtin/_error') + const Component = ComponentMod.routeModule.userland.default + + return { + App, + Document, + Component, + pageConfig: {}, + buildManifest: await loadManifestWithRetries( + join(distDir, `fallback-${BUILD_MANIFEST}`) + ), + reactLoadableManifest: {}, + ComponentMod, + pathname: '/_error', + routeModule: ComponentMod.routeModule, + } +} +export const loadDefaultErrorComponents = getTracer().wrap( + LoadComponentsSpan.loadDefaultErrorComponents, + loadDefaultErrorComponentsImpl +) diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 087752225090f..7abf3605541ad 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -15,7 +15,7 @@ import { import type { MiddlewareManifest } from '../build/webpack/plugins/middleware-plugin' import type RenderResult from './render-result' import type { FetchEventResult } from './web/types' -import type { PrerenderManifest, RoutesManifest } from '../build' +import type { PrerenderManifest } from '../build' import { BaseNextRequest, BaseNextResponse } from './base-http' import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin' import type { PayloadOptions } from './send-payload' @@ -26,7 +26,6 @@ import { } from '../shared/lib/router/utils/route-matcher' import type { MiddlewareRouteMatch } from '../shared/lib/router/utils/middleware-route-matcher' import type { RouteMatch } from './future/route-matches/route-match' -import { renderToHTML, type RenderOpts } from './render' import fs from 'fs' import { join, resolve, isAbsolute } from 'path' @@ -49,7 +48,6 @@ import { findDir } from '../lib/find-pages-dir' import { UrlWithParsedQuery } from 'url' import { NodeNextRequest, NodeNextResponse } from './base-http/node' import { sendRenderResult } from './send-payload' -import { getExtension, serveStatic } from './serve-static' import { ParsedUrlQuery } from 'querystring' import { ParsedUrl, parseUrl } from '../shared/lib/router/utils/parse-url' import * as Log from '../build/output/log' @@ -96,7 +94,6 @@ import { invokeRequest } from './lib/server-ipc/invoke-request' import { pipeReadable } from './pipe-readable' import { filterReqHeaders, ipcForbiddenHeaders } from './lib/server-ipc/utils' import { createRequestResponseMocks } from './lib/mock-request' -import chalk from 'next/dist/compiled/chalk' import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers' import { signalFromNodeResponse } from './web/spec-extension/adapters/next-request' import { RouteModuleLoader } from './future/helpers/module-loader/route-module-loader' @@ -382,14 +379,6 @@ export default class NextNodeServer extends BaseServer { }) } - protected sendStatic( - req: NodeNextRequest, - res: NodeNextResponse, - path: string - ): Promise { - return serveStatic(req.originalRequest, res.originalResponse, path) - } - protected async runApi( req: BaseNextRequest | NodeNextRequest, res: BaseNextResponse | NodeNextResponse, @@ -452,7 +441,7 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, pathname: string, query: NextParsedUrlQuery, - renderOpts: RenderOpts + renderOpts: import('./render').RenderOpts ): Promise { return getTracer().trace(NextNodeServerSpan.renderHTML, async () => this.renderHTMLImpl(req, res, pathname, query, renderOpts) @@ -464,17 +453,35 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, pathname: string, query: NextParsedUrlQuery, - renderOpts: RenderOpts + renderOpts: import('./render').RenderOpts ): Promise { - // Due to the way we pass data by mutating `renderOpts`, we can't extend the - // object here but only updating its `nextFontManifest` field. - // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 - renderOpts.nextFontManifest = this.nextFontManifest - - if (this.hasAppDir && renderOpts.isAppPath) { - const { renderToHTMLOrFlight: appRenderToHTMLOrFlight } = - require('./app-render/app-render') as typeof import('./app-render/app-render') - return appRenderToHTMLOrFlight( + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'invariant: renderHTML should not be called in minimal mode' + ) + // the `else` branch is needed for tree-shaking + } else { + // Due to the way we pass data by mutating `renderOpts`, we can't extend the + // object here but only updating its `nextFontManifest` field. + // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 + renderOpts.nextFontManifest = this.nextFontManifest + + if (this.hasAppDir && renderOpts.isAppPath) { + const { renderToHTMLOrFlight: appRenderToHTMLOrFlight } = + require('./future/route-modules/app-page/module.compiled') as typeof import('./app-render/app-render') + return appRenderToHTMLOrFlight( + req.originalRequest, + res.originalResponse, + pathname, + query, + renderOpts + ) + } + + // TODO: re-enable this once we've refactored to use implicit matches + // throw new Error('Invariant: render should have used routeModule') + + return require('./future/route-modules/pages/module.compiled').renderToHTML( req.originalRequest, res.originalResponse, pathname, @@ -482,17 +489,6 @@ export default class NextNodeServer extends BaseServer { renderOpts ) } - - // TODO: re-enable this once we've refactored to use implicit matches - // throw new Error('Invariant: render should have used routeModule') - - return renderToHTML( - req.originalRequest, - res.originalResponse, - pathname, - query, - renderOpts - ) } protected async imageOptimizer( @@ -500,55 +496,63 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, paramsResult: import('./image-optimizer').ImageParamsResult ): Promise<{ buffer: Buffer; contentType: string; maxAge: number }> { - const { imageOptimizer } = - require('./image-optimizer') as typeof import('./image-optimizer') - - return imageOptimizer( - req.originalRequest, - res.originalResponse, - paramsResult, - this.nextConfig, - this.renderOpts.dev, - async (newReq, newRes, newParsedUrl) => { - if (newReq.url === req.url) { - throw new Error(`Invariant attempted to optimize _next/image itself`) - } - - if (this.isRenderWorker) { - const invokeRes = await invokeRequest( - `http://${this.fetchHostname || 'localhost'}:${this.port}${ - newReq.url || '' - }`, - { - method: newReq.method || 'GET', - headers: newReq.headers, - signal: signalFromNodeResponse(res.originalResponse), - } - ) - const filteredResHeaders = filterReqHeaders( - toNodeOutgoingHttpHeaders(invokeRes.headers), - ipcForbiddenHeaders - ) + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'invariant: imageOptimizer should not be called in minimal mode' + ) + } else { + const { imageOptimizer } = + require('./image-optimizer') as typeof import('./image-optimizer') - for (const key of Object.keys(filteredResHeaders)) { - newRes.setHeader(key, filteredResHeaders[key] || '') + return imageOptimizer( + req.originalRequest, + res.originalResponse, + paramsResult, + this.nextConfig, + this.renderOpts.dev, + async (newReq, newRes, newParsedUrl) => { + if (newReq.url === req.url) { + throw new Error( + `Invariant attempted to optimize _next/image itself` + ) } - newRes.statusCode = invokeRes.status || 200 - if (invokeRes.body) { - await pipeReadable(invokeRes.body, newRes) - } else { - res.send() + if (this.isRenderWorker) { + const invokeRes = await invokeRequest( + `http://${this.fetchHostname || 'localhost'}:${this.port}${ + newReq.url || '' + }`, + { + method: newReq.method || 'GET', + headers: newReq.headers, + signal: signalFromNodeResponse(res.originalResponse), + } + ) + const filteredResHeaders = filterReqHeaders( + toNodeOutgoingHttpHeaders(invokeRes.headers), + ipcForbiddenHeaders + ) + + for (const key of Object.keys(filteredResHeaders)) { + newRes.setHeader(key, filteredResHeaders[key] || '') + } + newRes.statusCode = invokeRes.status || 200 + + if (invokeRes.body) { + await pipeReadable(invokeRes.body, newRes) + } else { + res.send() + } + return } - return + return this.getRequestHandler()( + new NodeNextRequest(newReq), + new NodeNextResponse(newRes), + newParsedUrl + ) } - return this.getRequestHandler()( - new NodeNextRequest(newReq), - new NodeNextResponse(newRes), - newParsedUrl - ) - } - ) + ) + } } protected getPagePath(pathname: string, locales?: string[]): string { @@ -719,99 +723,109 @@ export default class NextNodeServer extends BaseServer { res: BaseNextResponse, parsedUrl: NextUrlWithParsedQuery ) { - if (this.minimalMode || this.nextConfig.output === 'export') { + if ( + this.minimalMode || + this.nextConfig.output === 'export' || + process.env.NEXT_MINIMAL + ) { res.statusCode = 400 res.body('Bad Request').send() return { finished: true, } - } - const { ImageOptimizerCache } = - require('./image-optimizer') as typeof import('./image-optimizer') + // the `else` branch is needed for tree-shaking + } else { + const { ImageOptimizerCache } = + require('./image-optimizer') as typeof import('./image-optimizer') - const imageOptimizerCache = new ImageOptimizerCache({ - distDir: this.distDir, - nextConfig: this.nextConfig, - }) + const imageOptimizerCache = new ImageOptimizerCache({ + distDir: this.distDir, + nextConfig: this.nextConfig, + }) - const { getHash, sendResponse, ImageError } = - require('./image-optimizer') as typeof import('./image-optimizer') + const { getHash, sendResponse, ImageError } = + require('./image-optimizer') as typeof import('./image-optimizer') - if (!this.imageResponseCache) { - throw new Error('invariant image optimizer cache was not initialized') - } - const imagesConfig = this.nextConfig.images + if (!this.imageResponseCache) { + throw new Error('invariant image optimizer cache was not initialized') + } + const imagesConfig = this.nextConfig.images - if (imagesConfig.loader !== 'default' || imagesConfig.unoptimized) { - await this.render404(req, res) - return { finished: true } - } - const paramsResult = ImageOptimizerCache.validateParams( - (req as NodeNextRequest).originalRequest, - parsedUrl.query, - this.nextConfig, - !!this.renderOpts.dev - ) + if (imagesConfig.loader !== 'default' || imagesConfig.unoptimized) { + await this.render404(req, res) + return { finished: true } + } + const paramsResult = ImageOptimizerCache.validateParams( + (req as NodeNextRequest).originalRequest, + parsedUrl.query, + this.nextConfig, + !!this.renderOpts.dev + ) - if ('errorMessage' in paramsResult) { - res.statusCode = 400 - res.body(paramsResult.errorMessage).send() - return { finished: true } - } - const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult) + if ('errorMessage' in paramsResult) { + res.statusCode = 400 + res.body(paramsResult.errorMessage).send() + return { finished: true } + } + const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult) - try { - const cacheEntry = await this.imageResponseCache.get( - cacheKey, - async () => { - const { buffer, contentType, maxAge } = await this.imageOptimizer( - req as NodeNextRequest, - res as NodeNextResponse, - paramsResult - ) - const etag = getHash([buffer]) + try { + const { getExtension } = + require('./serve-static') as typeof import('./serve-static') + const cacheEntry = await this.imageResponseCache.get( + cacheKey, + async () => { + const { buffer, contentType, maxAge } = await this.imageOptimizer( + req as NodeNextRequest, + res as NodeNextResponse, + paramsResult + ) + const etag = getHash([buffer]) + + return { + value: { + kind: 'IMAGE', + buffer, + etag, + extension: getExtension(contentType) as string, + }, + revalidate: maxAge, + } + }, + { + incrementalCache: imageOptimizerCache, + } + ) + if (cacheEntry?.value?.kind !== 'IMAGE') { + throw new Error( + 'invariant did not get entry from image response cache' + ) + } + sendResponse( + (req as NodeNextRequest).originalRequest, + (res as NodeNextResponse).originalResponse, + paramsResult.href, + cacheEntry.value.extension, + cacheEntry.value.buffer, + paramsResult.isStatic, + cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT', + imagesConfig, + cacheEntry.revalidate || 0, + Boolean(this.renderOpts.dev) + ) + } catch (err) { + if (err instanceof ImageError) { + res.statusCode = err.statusCode + res.body(err.message).send() return { - value: { - kind: 'IMAGE', - buffer, - etag, - extension: getExtension(contentType) as string, - }, - revalidate: maxAge, + finished: true, } - }, - { - incrementalCache: imageOptimizerCache, - } - ) - - if (cacheEntry?.value?.kind !== 'IMAGE') { - throw new Error('invariant did not get entry from image response cache') - } - sendResponse( - (req as NodeNextRequest).originalRequest, - (res as NodeNextResponse).originalResponse, - paramsResult.href, - cacheEntry.value.extension, - cacheEntry.value.buffer, - paramsResult.isStatic, - cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT', - imagesConfig, - cacheEntry.revalidate || 0, - Boolean(this.renderOpts.dev) - ) - } catch (err) { - if (err instanceof ImageError) { - res.statusCode = err.statusCode - res.body(err.message).send() - return { - finished: true, } + throw err } - throw err + return { finished: true } } - return { finished: true } } protected async handleCatchallRenderRequest( @@ -1012,6 +1026,7 @@ export default class NextNodeServer extends BaseServer { const enabledVerboseLogging = this.nextConfig.experimental.logging === 'verbose' if (this.renderOpts.dev) { + const chalk = require('next/dist/compiled/chalk') const _req = req as NodeNextRequest | IncomingMessage const _res = res as NodeNextResponse | ServerResponse const origReq = 'originalRequest' in _req ? _req.originalRequest : _req @@ -1432,6 +1447,12 @@ export default class NextNodeServer extends BaseServer { parsed: UrlWithParsedQuery onWarning?: (warning: Error) => void }) { + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'invariant: runMiddleware should not be called in minimal mode' + ) + } + // Middleware is skipped for on-demand revalidate requests if ( checkIsOnDemandRevalidate(params.request, this.renderOpts.previewProps) @@ -1675,10 +1696,7 @@ export default class NextNodeServer extends BaseServer { protected getRoutesManifest(): NormalizedRouteManifest | undefined { return getTracer().trace(NextNodeServerSpan.getRoutesManifest, () => { - const manifest: RoutesManifest = require(join( - this.distDir, - ROUTES_MANIFEST - )) + const manifest = loadManifest(join(this.distDir, ROUTES_MANIFEST)) let rewrites = manifest.rewrites ?? { beforeFiles: [], @@ -1736,6 +1754,11 @@ export default class NextNodeServer extends BaseServer { match?: RouteMatch onWarning?: (warning: Error) => void }): Promise { + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'Middleware is not supported in minimal mode. Please remove the `NEXT_MINIMAL` environment variable.' + ) + } let edgeInfo: ReturnType | undefined const { query, page, match } = params diff --git a/packages/next/src/server/render-result.ts b/packages/next/src/server/render-result.ts index b93dfce5fae21..2a8252e4fd143 100644 --- a/packages/next/src/server/render-result.ts +++ b/packages/next/src/server/render-result.ts @@ -1,4 +1,4 @@ -import { StaticGenerationStore } from '../client/components/static-generation-async-storage' +import { StaticGenerationStore } from '../client/components/static-generation-async-storage.external' import { pipeReadable, PipeTarget } from './pipe-readable' type ContentTypeOption = string | undefined diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx index ac0989082aa9b..fe36e793348e2 100644 --- a/packages/next/src/server/render.tsx +++ b/packages/next/src/server/render.tsx @@ -1,7 +1,7 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { ParsedUrlQuery } from 'querystring' import type { NextRouter } from '../shared/lib/router/router' -import type { HtmlProps } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context.shared-runtime' import type { DomainLocale } from './config' import type { AppType, @@ -52,12 +52,12 @@ import { } from '../shared/lib/constants' import { isSerializableProps } from '../lib/is-serializable-props' import { isInAmpMode } from '../shared/lib/amp-mode' -import { AmpStateContext } from '../shared/lib/amp-context' +import { AmpStateContext } from '../shared/lib/amp-context.shared-runtime' import { defaultHead } from '../shared/lib/head' -import { HeadManagerContext } from '../shared/lib/head-manager-context' -import Loadable from '../shared/lib/loadable' -import { LoadableContext } from '../shared/lib/loadable-context' -import { RouterContext } from '../shared/lib/router-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' +import Loadable from '../shared/lib/loadable.shared-runtime' +import { LoadableContext } from '../shared/lib/loadable-context.shared-runtime' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import { ComponentsEnhancer, @@ -65,7 +65,7 @@ import { isResSent, loadGetInitialProps, } from '../shared/lib/utils' -import { HtmlContext } from '../shared/lib/html-context' +import { HtmlContext } from '../shared/lib/html-context.shared-runtime' import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import { getRequestMeta, NextParsedUrlQuery } from './request-meta' @@ -79,16 +79,16 @@ import { renderToInitialStream, continueFromInitialStream, } from './stream-utils/node-web-streams-helper' -import { ImageConfigContext } from '../shared/lib/image-config-context' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' import stripAnsi from 'next/dist/compiled/strip-ansi' import { stripInternalQueries } from './internal-utils' import { adaptForAppRouterInstance, adaptForSearchParams, PathnameContextProviderAdapter, -} from '../shared/lib/router/adapters' -import { AppRouterContext } from '../shared/lib/app-router-context' -import { SearchParamsContext } from '../shared/lib/hooks-client-context' +} from '../shared/lib/router/adapters.shared-runtime' +import { AppRouterContext } from '../shared/lib/app-router-context.shared-runtime' +import { SearchParamsContext } from '../shared/lib/hooks-client-context.shared-runtime' import { getTracer } from './lib/trace/tracer' import { RenderSpan } from './lib/trace/constants' import { ReflectAdapter } from './web/spec-extension/adapters/reflect' diff --git a/packages/next/src/server/require-hook.ts b/packages/next/src/server/require-hook.ts index 1f53b3b479109..9b2e1526eec0e 100644 --- a/packages/next/src/server/require-hook.ts +++ b/packages/next/src/server/require-hook.ts @@ -2,11 +2,13 @@ // This is needed for userland plugins to attach to the same webpack instance as Next.js'. // Individually compiled modules are as defined for the compilation in bundles/webpack/packages/*. +import path, { dirname } from 'path' + // This module will only be loaded once per process. -const { dirname } = require('path') const mod = require('module') const resolveFilename = mod._resolveFilename +const originalRequire = mod.prototype.require const hookPropertyMap = new Map() let aliasedPrebundledReact = false @@ -19,10 +21,9 @@ const resolve = process.env.NEXT_MINIMAL const toResolveMap = (map: Record): [string, string][] => Object.entries(map).map(([key, value]) => [key, resolve(value)]) -// these must use require.resolve to be statically analyzable export const defaultOverrides = { - 'styled-jsx': dirname(require.resolve('styled-jsx/package.json')), - 'styled-jsx/style': require.resolve('styled-jsx/style'), + 'styled-jsx': dirname(resolve('styled-jsx/package.json')), + 'styled-jsx/style': resolve('styled-jsx/style'), } export const baseOverrides = { @@ -78,7 +79,6 @@ export function addHookAliases(aliases: [string, string][] = []) { } } -// Add default aliases addHookAliases(toResolveMap(defaultOverrides)) // Override built-in React packages if necessary @@ -117,3 +117,29 @@ mod._resolveFilename = function ( // We use `bind` here to avoid referencing outside variables to create potential memory leaks. }.bind(null, resolveFilename, hookPropertyMap) + +// This is a hack to make sure that if a user requires a Next.js module that wasn't bundled +// that needs to point to the rendering runtime version, it will point to the correct one. +// This can happen on `pages` when a user requires a dependency that uses next/image for example. +// This is only needed in production as in development we fallback to the external version. +if ( + process.env.NODE_ENV !== 'development' && + process.env.__NEXT_PRIVATE_RENDER_RUNTIME && + !process.env.TURBOPACK +) { + const currentRuntime = `${ + process.env.__NEXT_PRIVATE_RENDER_RUNTIME === 'pages' + ? 'next/dist/compiled/next-server/pages.runtime' + : 'next/dist/compiled/next-server/app-page.runtime' + }.prod` + + mod.prototype.require = function (request: string) { + if (request.endsWith('.shared-runtime')) { + const base = path.basename(request, '.shared-runtime') + const camelized = base.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) + const instance = originalRequire.call(this, currentRuntime) + return instance.default.sharedModules[camelized] + } + return originalRequire.call(this, request) + } +} diff --git a/packages/next/src/server/response-cache/index.ts b/packages/next/src/server/response-cache/index.ts index 6d135da26e939..7bd6710cc4a80 100644 --- a/packages/next/src/server/response-cache/index.ts +++ b/packages/next/src/server/response-cache/index.ts @@ -20,7 +20,10 @@ export default class ResponseCache { constructor(minimalMode: boolean) { this.pendingResponses = new Map() - this.minimalMode = minimalMode + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + const minimalModeKey = 'minimalMode' + this[minimalModeKey] = minimalMode } public get( diff --git a/packages/next/src/server/response-cache/web.ts b/packages/next/src/server/response-cache/web.ts index e37ccca314812..f255fdd5412d4 100644 --- a/packages/next/src/server/response-cache/web.ts +++ b/packages/next/src/server/response-cache/web.ts @@ -15,7 +15,9 @@ export default class WebResponseCache { constructor(minimalMode: boolean) { this.pendingResponses = new Map() - this.minimalMode = minimalMode + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + Object.assign(this, { minimalMode }) } public get( diff --git a/packages/next/src/server/web/adapter.ts b/packages/next/src/server/web/adapter.ts index 1ea32a75956cd..5c6ad3e7ed5ab 100644 --- a/packages/next/src/server/web/adapter.ts +++ b/packages/next/src/server/web/adapter.ts @@ -18,7 +18,7 @@ import { import { NEXT_QUERY_PARAM_PREFIX } from '../../lib/constants' import { ensureInstrumentationRegistered } from './globals' import { RequestAsyncStorageWrapper } from '../async-storage/request-async-storage-wrapper' -import { requestAsyncStorage } from '../../client/components/request-async-storage' +import { requestAsyncStorage } from '../../client/components/request-async-storage.external' import { PrerenderManifest } from '../../build' class NextRequestHint extends NextRequest { diff --git a/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts b/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts index df3c369eef877..d44ea986cad65 100644 --- a/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts +++ b/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts @@ -1,5 +1,5 @@ import type { RequestCookies } from '../cookies' -import { StaticGenerationStore } from '../../../../client/components/static-generation-async-storage' +import { StaticGenerationStore } from '../../../../client/components/static-generation-async-storage.external' import { ResponseCookies } from '../cookies' import { ReflectAdapter } from './reflect' diff --git a/packages/next/src/server/web/spec-extension/revalidate-tag.ts b/packages/next/src/server/web/spec-extension/revalidate-tag.ts index 8d7cd68bd3a9a..7c7bff8c2f784 100644 --- a/packages/next/src/server/web/spec-extension/revalidate-tag.ts +++ b/packages/next/src/server/web/spec-extension/revalidate-tag.ts @@ -1,7 +1,7 @@ import { StaticGenerationAsyncStorage, StaticGenerationStore, -} from '../../../client/components/static-generation-async-storage' +} from '../../../client/components/static-generation-async-storage.external' export function revalidateTag(tag: string) { const staticGenerationAsyncStorage = ( diff --git a/packages/next/src/server/web/spec-extension/unstable-cache.ts b/packages/next/src/server/web/spec-extension/unstable-cache.ts index aad3ed2baf20a..1e85b5c290971 100644 --- a/packages/next/src/server/web/spec-extension/unstable-cache.ts +++ b/packages/next/src/server/web/spec-extension/unstable-cache.ts @@ -2,7 +2,7 @@ import { StaticGenerationStore, staticGenerationAsyncStorage as _staticGenerationAsyncStorage, StaticGenerationAsyncStorage, -} from '../../../client/components/static-generation-async-storage' +} from '../../../client/components/static-generation-async-storage.external' import { CACHE_ONE_YEAR } from '../../../lib/constants' import { addImplicitTags } from '../../lib/patch-fetch' diff --git a/packages/next/src/shared/lib/amp-context.ts b/packages/next/src/shared/lib/amp-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/amp-context.ts rename to packages/next/src/shared/lib/amp-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/amp.ts b/packages/next/src/shared/lib/amp.ts index 04518b2389357..8edd21db9c299 100644 --- a/packages/next/src/shared/lib/amp.ts +++ b/packages/next/src/shared/lib/amp.ts @@ -1,5 +1,5 @@ import React from 'react' -import { AmpStateContext } from './amp-context' +import { AmpStateContext } from './amp-context.shared-runtime' import { isInAmpMode } from './amp-mode' export function useAmp(): boolean { diff --git a/packages/next/src/shared/lib/app-router-context.ts b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/app-router-context.ts rename to packages/next/src/shared/lib/app-router-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/dynamic.tsx b/packages/next/src/shared/lib/dynamic.tsx index cb497fc587d4f..390410edda29e 100644 --- a/packages/next/src/shared/lib/dynamic.tsx +++ b/packages/next/src/shared/lib/dynamic.tsx @@ -1,5 +1,5 @@ import React from 'react' -import Loadable from './loadable' +import Loadable from './loadable.shared-runtime' const isServerSide = typeof window === 'undefined' diff --git a/packages/next/src/shared/lib/head-manager-context.ts b/packages/next/src/shared/lib/head-manager-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/head-manager-context.ts rename to packages/next/src/shared/lib/head-manager-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/head.tsx b/packages/next/src/shared/lib/head.tsx index 3156e259f656f..42f95767bfa4c 100644 --- a/packages/next/src/shared/lib/head.tsx +++ b/packages/next/src/shared/lib/head.tsx @@ -2,8 +2,8 @@ import React, { useContext } from 'react' import Effect from './side-effect' -import { AmpStateContext } from './amp-context' -import { HeadManagerContext } from './head-manager-context' +import { AmpStateContext } from './amp-context.shared-runtime' +import { HeadManagerContext } from './head-manager-context.shared-runtime' import { isInAmpMode } from './amp-mode' import { warnOnce } from './utils/warn-once' diff --git a/packages/next/src/shared/lib/hooks-client-context.ts b/packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/hooks-client-context.ts rename to packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/html-context.ts b/packages/next/src/shared/lib/html-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/html-context.ts rename to packages/next/src/shared/lib/html-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/image-config-context.ts b/packages/next/src/shared/lib/image-config-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/image-config-context.ts rename to packages/next/src/shared/lib/image-config-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/loadable-context.ts b/packages/next/src/shared/lib/loadable-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/loadable-context.ts rename to packages/next/src/shared/lib/loadable-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/loadable.tsx b/packages/next/src/shared/lib/loadable.shared-runtime.tsx similarity index 99% rename from packages/next/src/shared/lib/loadable.tsx rename to packages/next/src/shared/lib/loadable.shared-runtime.tsx index 1592d98551093..82ba84182701f 100644 --- a/packages/next/src/shared/lib/loadable.tsx +++ b/packages/next/src/shared/lib/loadable.shared-runtime.tsx @@ -23,7 +23,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE // Modified to be compatible with webpack 4 / Next.js import React from 'react' -import { LoadableContext } from './loadable-context' +import { LoadableContext } from './loadable-context.shared-runtime' function resolve(obj: any) { return obj && obj.default ? obj.default : obj diff --git a/packages/next/src/shared/lib/router-context.ts b/packages/next/src/shared/lib/router-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/router-context.ts rename to packages/next/src/shared/lib/router-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/router/adapters.tsx b/packages/next/src/shared/lib/router/adapters.shared-runtime.tsx similarity index 95% rename from packages/next/src/shared/lib/router/adapters.tsx rename to packages/next/src/shared/lib/router/adapters.shared-runtime.tsx index ce68a8bec1e8b..29f92dda8dc08 100644 --- a/packages/next/src/shared/lib/router/adapters.tsx +++ b/packages/next/src/shared/lib/router/adapters.shared-runtime.tsx @@ -1,7 +1,10 @@ import type { ParsedUrlQuery } from 'node:querystring' import React, { useMemo, useRef } from 'react' -import type { AppRouterInstance, NavigateOptions } from '../app-router-context' -import { PathnameContext } from '../hooks-client-context' +import type { + AppRouterInstance, + NavigateOptions, +} from '../app-router-context.shared-runtime' +import { PathnameContext } from '../hooks-client-context.shared-runtime' import type { NextRouter } from './router' import { isDynamicRoute } from './utils' diff --git a/packages/next/src/shared/lib/router/adapters.test.tsx b/packages/next/src/shared/lib/router/adapters.test.tsx index fa8e48f2fc088..e47ce2174dd35 100644 --- a/packages/next/src/shared/lib/router/adapters.test.tsx +++ b/packages/next/src/shared/lib/router/adapters.test.tsx @@ -1,4 +1,4 @@ -import { adaptForAppRouterInstance } from './adapters' +import { adaptForAppRouterInstance } from './adapters.shared-runtime' import { NextRouter } from './router' describe('adaptForAppRouterInstance', () => { diff --git a/packages/next/src/shared/lib/runtime-config.ts b/packages/next/src/shared/lib/runtime-config.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/runtime-config.ts rename to packages/next/src/shared/lib/runtime-config.shared-runtime.ts diff --git a/packages/next/src/shared/lib/server-inserted-html.tsx b/packages/next/src/shared/lib/server-inserted-html.shared-runtime.tsx similarity index 100% rename from packages/next/src/shared/lib/server-inserted-html.tsx rename to packages/next/src/shared/lib/server-inserted-html.shared-runtime.tsx diff --git a/packages/next/src/shared/lib/utils.ts b/packages/next/src/shared/lib/utils.ts index f967be459022b..4e8036a6894a2 100644 --- a/packages/next/src/shared/lib/utils.ts +++ b/packages/next/src/shared/lib/utils.ts @@ -1,4 +1,4 @@ -import type { HtmlProps } from './html-context' +import type { HtmlProps } from './html-context.shared-runtime' import type { ComponentType } from 'react' import type { DomainLocale } from '../../server/config' import type { Env } from '@next/env' diff --git a/packages/next/src/trace/index.ts b/packages/next/src/trace/index.ts index e242e19c9041f..e3928f775f613 100644 --- a/packages/next/src/trace/index.ts +++ b/packages/next/src/trace/index.ts @@ -1,4 +1,5 @@ import { trace, flushAllTraces, Span, SpanStatus } from './trace' import { SpanId, setGlobal } from './shared' -export { trace, flushAllTraces, SpanId, Span, SpanStatus, setGlobal } +export { trace, flushAllTraces, Span, setGlobal, SpanStatus } +export type { SpanId } diff --git a/packages/next/taskfile-webpack.js b/packages/next/taskfile-webpack.js new file mode 100644 index 0000000000000..04495d7b3621c --- /dev/null +++ b/packages/next/taskfile-webpack.js @@ -0,0 +1,35 @@ +const webpack = require('webpack') + +module.exports = function (task) { + task.plugin('webpack', {}, function* (_, options) { + options = options || {} + + const compiler = webpack(options.config) + + if (options.watch) { + compiler.watch({}, (err, stats) => { + if (err || stats.hasErrors()) { + console.error(err || stats.toString()) + } else { + console.log(`${options.name} compiled successfully.`) + } + }) + } else { + yield new Promise((resolve, reject) => { + compiler.run((err, stats) => { + if (err || stats.hasErrors()) { + console.error(err || stats.toString()) + reject(err || stats.toString()) + } + if (process.env.ANALYZE) { + require('fs').writeFileSync( + require('path').join(__dirname, options.name + '-stats.json'), + JSON.stringify(stats.toJson()) + ) + } + resolve() + }) + }) + } + }) +} diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index f38289c3d4984..321490234153b 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2357,7 +2357,7 @@ export async function ncc(task, opts) { ) } -export async function compile(task, opts) { +export async function next_compile(task, opts) { await task.parallel( [ 'cli', @@ -2388,12 +2388,16 @@ export async function compile(task, opts) { ], opts ) +} + +export async function compile(task, opts) { + await task.serial(['next_compile', 'next_bundle'], opts) + await task.serial([ 'ncc_react_refresh_utils', 'ncc_next__react_dev_overlay', 'ncc_next_font', 'capsize_metrics', - 'minimal_next_server', ]) } @@ -2658,157 +2662,38 @@ export async function release(task) { await task.clear('dist').start('build') } -export async function minimal_next_server(task) { - const outputName = 'next-server.js' - const cachedOutputName = `${outputName}.cache` - - const minimalExternals = [ - 'react', - 'react/package.json', - 'react/jsx-runtime', - 'react/jsx-dev-runtime', - 'react-dom', - 'react-dom/package.json', - 'react-dom/client', - 'react-dom/server', - 'react-dom/server.browser', - 'react-dom/server.edge', - 'react-server-dom-webpack/client', - 'react-server-dom-webpack/client.edge', - 'react-server-dom-webpack/server.edge', - 'react-server-dom-webpack/server.node', - 'styled-jsx', - 'styled-jsx/style', - '@opentelemetry/api', - 'next/dist/compiled/@next/react-dev-overlay/dist/middleware', - 'next/dist/compiled/@ampproject/toolbox-optimizer', - 'next/dist/compiled/edge-runtime', - 'next/dist/compiled/@edge-runtime/ponyfill', - 'next/dist/compiled/undici', - 'next/dist/compiled/raw-body', - 'next/dist/server/capsize-font-metrics.json', - 'critters', - 'next/dist/compiled/node-html-parser', - 'next/dist/compiled/compression', - 'next/dist/compiled/jsonwebtoken', - 'next/dist/compiled/@mswjs/interceptors/ClientRequest', - ].reduce((acc, pkg) => { - acc[pkg] = pkg - return acc - }, {}) - - Object.assign(minimalExternals, { - '/(.*)config$/': 'next/dist/server/config', - './web/sandbox': 'next/dist/server/web/sandbox', +export async function next_bundle_prod(task, opts) { + await task.source('dist').webpack({ + watch: opts.dev, + config: require('./webpack.config')({ + dev: false, + }), + name: 'next-bundle-prod', }) +} - const webpack = require('webpack') - const TerserPlugin = require('terser-webpack-plugin') - // const BundleAnalyzerPlugin = - // require('webpack-bundle-analyzer').BundleAnalyzerPlugin - /** @type {webpack.Configuration} */ - const config = { - entry: join(__dirname, 'dist/server/next-server.js'), - target: 'node', - mode: 'production', - output: { - path: join(__dirname, 'dist/compiled/minimal-next-server'), - filename: outputName, - libraryTarget: 'commonjs2', - }, - // left in for debugging - optimization: { - moduleIds: 'named', - // minimize: false, - minimize: true, - minimizer: [ - new TerserPlugin({ - extractComments: false, - terserOptions: { - format: { - comments: false, - }, - compress: { - passes: 2, - }, - }, - }), - ], - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('production'), - 'process.env.NEXT_MINIMAL': JSON.stringify('true'), - 'process.env.NEXT_RUNTIME': JSON.stringify('nodejs'), - }), - // new BundleAnalyzerPlugin({}), - ], - externals: [minimalExternals], - } - - await new Promise((resolve, reject) => { - webpack(config, (err, stats) => { - if (err) return reject(err) - if (stats.hasErrors()) { - return reject(new Error(stats.toString('errors-only'))) - } - resolve() - }) +export async function next_bundle_dev(task, opts) { + await task.source('dist').webpack({ + watch: opts.dev, + config: require('./webpack.config')({ + dev: true, + }), + name: 'next-bundle-dev', }) +} - const wrappedTemplate = ` -const filename = ${JSON.stringify(outputName)} -const { readFileSync } = require('fs'), - { Script } = require('vm'), - { wrap } = require('module'), - { join } = require('path'); -const basename = join(__dirname, filename) - -const source = readFileSync(basename, 'utf-8') - -const cachedData = - !process.pkg && - require('process').platform !== 'win32' && - readFileSync(join(__dirname, '${cachedOutputName}')) - -const scriptOpts = { filename: basename, columnOffset: 0 } - -const script = new Script( - wrap(source), - cachedData ? Object.assign({ cachedData }, scriptOpts) : scriptOpts -) - -script.runInThisContext()(exports, require, module, __filename, __dirname) -` - - await fs.writeFile( - join(__dirname, `dist/compiled/minimal-next-server/next-server-cached.js`), - wrappedTemplate - ) - - const Module = require('module') - const vm = require('vm') - const filename = resolve( - __dirname, - 'dist/compiled/minimal-next-server', - outputName - ) - - const content = require('fs').readFileSync(filename, 'utf8') - - const wrapper = Module.wrap(content) - var script = new vm.Script(wrapper, { - filename: filename, - lineOffset: 0, - displayErrors: true, +export async function next_bundle_turbo_prod(task, opts) { + await task.source('dist').webpack({ + watch: opts.dev, + config: require('./webpack.config')({ + turbo: true, + }), + name: 'next-bundle-prod-turbo', }) - - script.runInThisContext()(exports, require, module, __filename, __dirname) - - const buffer = script.createCachedData() - - await fs.writeFile( - join(__dirname, `dist/compiled/minimal-next-server/${cachedOutputName}`), - buffer +} +export async function next_bundle(task, opts) { + await task.parallel( + ['next_bundle_prod', 'next_bundle_dev', 'next_bundle_turbo_prod'], + opts ) } diff --git a/packages/next/webpack.config.js b/packages/next/webpack.config.js new file mode 100644 index 0000000000000..8e7c3063fa5ed --- /dev/null +++ b/packages/next/webpack.config.js @@ -0,0 +1,145 @@ +const webpack = require('webpack') +const path = require('path') +const TerserPlugin = require('terser-webpack-plugin') +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') + +const minimalExternals = [ + 'react', + 'react/package.json', + 'react/jsx-runtime', + 'react/jsx-dev-runtime', + 'react-dom', + 'react-dom/package.json', + 'react-dom/client', + 'react-dom/server', + 'react-dom/server.browser', + 'react-dom/server.edge', + 'react-server-dom-webpack/client', + 'react-server-dom-webpack/client.edge', + 'react-server-dom-webpack/server.edge', + 'react-server-dom-webpack/server.node', + 'styled-jsx', + 'styled-jsx/style', + '@opentelemetry/api', + 'next/dist/compiled/@next/react-dev-overlay/dist/middleware', + 'next/dist/compiled/@ampproject/toolbox-optimizer', + 'next/dist/compiled/edge-runtime', + 'next/dist/compiled/@edge-runtime/ponyfill', + 'next/dist/compiled/undici', + 'next/dist/compiled/raw-body', + 'next/dist/server/capsize-font-metrics.json', + 'critters', + 'next/dist/compiled/node-html-parser', + 'next/dist/compiled/compression', + 'next/dist/compiled/jsonwebtoken', + 'next/dist/compiled/@opentelemetry/api', + 'next/dist/compiled/@mswjs/interceptors/ClientRequest', +] + +const externalsMap = { + './web/sandbox': 'next/dist/server/web/sandbox', +} + +const externalsRegexMap = { + '(.*)trace/tracer$': 'next/dist/server/lib/trace/tracer', +} + +module.exports = ({ dev, turbo }) => { + const externalHandler = ({ context, request, getResolve }, callback) => { + ;(async () => { + if ( + ((dev || turbo) && request.endsWith('.shared-runtime')) || + request.endsWith('.external') + ) { + const resolve = getResolve() + const resolved = await resolve(context, request) + const relative = path.relative( + path.join(__dirname, '..'), + resolved.replace('esm' + path.sep, '') + ) + callback(null, `commonjs ${relative}`) + } else { + const regexMatch = Object.keys(externalsRegexMap).find((regex) => + new RegExp(regex).test(request) + ) + if (regexMatch) { + return callback(null, 'commonjs ' + externalsRegexMap[regexMatch]) + } + callback() + } + })() + } + + /** @type {webpack.Configuration} */ + return { + entry: { + server: path.join(__dirname, 'dist/esm/server/next-server.js'), + 'app-page': path.join( + __dirname, + 'dist/esm/server/future/route-modules/app-page/module.js' + ), + 'app-route': path.join( + __dirname, + 'dist/esm/server/future/route-modules/app-route/module.js' + ), + pages: path.join( + __dirname, + 'dist/esm/server/future/route-modules/pages/module.js' + ), + 'pages-api': path.join( + __dirname, + 'dist/esm/server/future/route-modules/pages-api/module.js' + ), + }, + target: 'node', + mode: 'production', + output: { + path: path.join(__dirname, 'dist/compiled/next-server'), + filename: `[name]${turbo ? '-turbo' : ''}.runtime.${ + dev ? 'dev' : 'prod' + }.js`, + libraryTarget: 'commonjs2', + }, + optimization: { + moduleIds: 'named', + minimize: true, + // splitChunks: { + // chunks: 'all', + // }, + concatenateModules: true, + minimizer: [ + new TerserPlugin({ + extractComments: false, + terserOptions: { + format: { + comments: false, + }, + compress: { + passes: 2, + }, + }, + }), + ], + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NEXT_MINIMAL': JSON.stringify('true'), + 'this.serverOptions.experimentalTestProxy': JSON.stringify(false), + 'this.minimalMode': JSON.stringify(true), + 'this.renderOpts.dev': JSON.stringify(dev), + 'process.env.NODE_ENV': JSON.stringify( + dev ? 'development' : 'production' + ), + 'process.env.NEXT_RUNTIME': JSON.stringify('nodejs'), + }), + !!process.env.ANALYZE && + new BundleAnalyzerPlugin({ + analyzerPort: 8888 + (dev ? 0 : 1) + (turbo ? 1 : 0), + }), + ].filter(Boolean), + stats: { + optimizationBailout: true, + }, + externals: [...minimalExternals, externalsMap, externalHandler], + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ce02614924c0..c0bf75436a03f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1381,6 +1381,9 @@ importers: terser: specifier: 5.14.1 version: 5.14.1 + terser-webpack-plugin: + specifier: 5.3.9 + version: 5.3.9(@swc/core@1.3.55)(webpack@5.86.0) text-table: specifier: 0.2.0 version: 0.2.0 diff --git a/scripts/minimal-server.js b/scripts/minimal-server.js index f4f30ac97ce25..08f9125a4f0cc 100644 --- a/scripts/minimal-server.js +++ b/scripts/minimal-server.js @@ -1,3 +1,4 @@ +console.time('next-wall-time') // Usage: node scripts/minimal-server.js // This script is used to run a minimal Next.js server in production mode. @@ -44,11 +45,13 @@ if (process.env.LOG_READFILE) { require('fs').readFile = function (path, options, callback) { readFileCount++ + console.log(`readFile: ${path}`) return originalReadFile.apply(this, arguments) } require('fs').readFileSync = function (path, options) { readFileSyncCount++ + console.log(`readFileSync: ${path}`) return originalReadFileSync.apply(this, arguments) } } @@ -56,10 +59,9 @@ if (process.env.LOG_READFILE) { console.time('next-cold-start') const NextServer = process.env.USE_BUNDLED_NEXT - ? require('next/dist/compiled/minimal-next-server/next-server-cached').default + ? require('next/dist/compiled/next-server/server.runtime.prod').default : require('next/dist/server/next-server').default -console.timeEnd('next-cold-start') if (process.env.LOG_READFILE) { console.log(`readFileCount: ${readFileCount + readFileSyncCount}`) } @@ -101,9 +103,20 @@ require('http') if (process.env.LOG_READFILE) { console.log(`readFileCount: ${readFileCount + readFileSyncCount}`) } - require('process').exit(0) }) }) .listen(3000, () => { console.timeEnd('next-cold-start') + fetch('http://localhost:3000/') + .then((res) => res.text()) + .then((text) => { + console.log(text) + }) + .catch((err) => { + console.error(err) + }) + .finally(() => { + console.timeEnd('next-wall-time') + require('process').exit(0) + }) }) diff --git a/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts b/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts index a61e222c3872e..3e5e6b82ea050 100644 --- a/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts +++ b/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts @@ -113,7 +113,7 @@ createNextDescribe( await check(() => { const fullLog = logs.join('') - return fullLog.includes('Error: Body exceeded 1.5mb limit') && + return fullLog.includes('[Error]: Body exceeded 1.5mb limit') && fullLog.includes( 'To configure the body size limit for Server Actions, see' ) diff --git a/test/e2e/getserversideprops/app/pages/index.js b/test/e2e/getserversideprops/app/pages/index.js index 4433c9c2ee84e..da17edc01839d 100644 --- a/test/e2e/getserversideprops/app/pages/index.js +++ b/test/e2e/getserversideprops/app/pages/index.js @@ -1,6 +1,6 @@ import Link from 'next/link' import ReactDOM from 'react-dom/server' -import { RouterContext } from 'next/dist/shared/lib/router-context' +import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime' import { useRouter } from 'next/router' function RouterComp(props) { diff --git a/test/e2e/opentelemetry/opentelemetry.test.ts b/test/e2e/opentelemetry/opentelemetry.test.ts index ae798ec7c5c2d..13e0fefea2787 100644 --- a/test/e2e/opentelemetry/opentelemetry.test.ts +++ b/test/e2e/opentelemetry/opentelemetry.test.ts @@ -77,80 +77,80 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.url": "https://vercel.com/", - "net.peer.name": "vercel.com", - "next.span_name": "fetch GET https://vercel.com/", - "next.span_type": "AppRender.fetch", - }, - "kind": 2, - "name": "fetch GET https://vercel.com/", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/app/[param]/rsc-fetch", - "next.span_name": "render route (app) /app/[param]/rsc-fetch", - "next.span_type": "AppRender.getBodyResult", - }, - "kind": 0, - "name": "render route (app) /app/[param]/rsc-fetch", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/app/[param]/rsc-fetch", - "http.status_code": 200, - "http.target": "/app/param/rsc-fetch", - "next.route": "/app/[param]/rsc-fetch", - "next.span_name": "GET /app/[param]/rsc-fetch", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /app/[param]/rsc-fetch", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.page": "/app/[param]/layout", - "next.span_name": "generateMetadata /app/[param]/layout", - "next.span_type": "ResolveMetadata.generateMetadata", - }, - "kind": 0, - "name": "generateMetadata /app/[param]/layout", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.page": "/app/[param]/rsc-fetch/page", - "next.span_name": "generateMetadata /app/[param]/rsc-fetch/page", - "next.span_type": "ResolveMetadata.generateMetadata", - }, - "kind": 0, - "name": "generateMetadata /app/[param]/rsc-fetch/page", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.url": "https://vercel.com/", + "net.peer.name": "vercel.com", + "next.span_name": "fetch GET https://vercel.com/", + "next.span_type": "AppRender.fetch", + }, + "kind": 2, + "name": "fetch GET https://vercel.com/", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/app/[param]/rsc-fetch", + "next.span_name": "render route (app) /app/[param]/rsc-fetch", + "next.span_type": "AppRender.getBodyResult", + }, + "kind": 0, + "name": "render route (app) /app/[param]/rsc-fetch", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/app/[param]/rsc-fetch", + "http.status_code": 200, + "http.target": "/app/param/rsc-fetch", + "next.route": "/app/[param]/rsc-fetch", + "next.span_name": "GET /app/[param]/rsc-fetch", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /app/[param]/rsc-fetch", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.page": "/app/[param]/layout", + "next.span_name": "generateMetadata /app/[param]/layout", + "next.span_type": "ResolveMetadata.generateMetadata", + }, + "kind": 0, + "name": "generateMetadata /app/[param]/layout", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.page": "/app/[param]/rsc-fetch/page", + "next.span_name": "generateMetadata /app/[param]/rsc-fetch/page", + "next.span_type": "ResolveMetadata.generateMetadata", + }, + "kind": 0, + "name": "generateMetadata /app/[param]/rsc-fetch/page", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -160,37 +160,39 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "next.route": "/api/app/[param]/data/route", - "next.span_name": "executing api route (app) /api/app/[param]/data/route", - "next.span_type": "AppRouteRouteHandlers.runHandler", - }, - "kind": 0, - "name": "executing api route (app) /api/app/[param]/data/route", - "parentId": "[parent-id]", - "status": Object { - "code": 0, + Array [ + Object { + "attributes": Object { + "next.route": "/api/app/[param]/data/route", + "next.span_name": "executing api route (app) /api/app/[param]/data/route", + "next.span_type": "AppRouteRouteHandlers.runHandler", + }, + "kind": 0, + "name": "executing api route (app) /api/app/[param]/data/route", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, }, - }, - Object { - "attributes": Object { - "http.method": "GET", - "http.status_code": 200, - "http.target": "/api/app/param/data", - "next.span_name": "GET /api/app/param/data", - "next.span_type": "BaseServer.handleRequest", + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/api/app/[param]/data/route", + "http.status_code": 200, + "http.target": "/api/app/param/data", + "next.route": "/api/app/[param]/data/route", + "next.span_name": "GET /api/app/[param]/data/route", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /api/app/[param]/data/route", + "parentId": undefined, + "status": Object { + "code": 0, + }, }, - "kind": 1, - "name": "GET /api/app/param/data", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - ] - `) + ] + `) return 'success' }, 'success') }) @@ -202,52 +204,52 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/pages/[param]/getServerSideProps", - "http.status_code": 200, - "http.target": "/pages/param/getServerSideProps", - "next.route": "/pages/[param]/getServerSideProps", - "next.span_name": "GET /pages/[param]/getServerSideProps", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /pages/[param]/getServerSideProps", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getServerSideProps", - "next.span_name": "getServerSideProps /pages/[param]/getServerSideProps", - "next.span_type": "Render.getServerSideProps", - }, - "kind": 0, - "name": "getServerSideProps /pages/[param]/getServerSideProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getServerSideProps", - "next.span_name": "render route (pages) /pages/[param]/getServerSideProps", - "next.span_type": "Render.renderDocument", - }, - "kind": 0, - "name": "render route (pages) /pages/[param]/getServerSideProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/pages/[param]/getServerSideProps", + "http.status_code": 200, + "http.target": "/pages/param/getServerSideProps", + "next.route": "/pages/[param]/getServerSideProps", + "next.span_name": "GET /pages/[param]/getServerSideProps", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /pages/[param]/getServerSideProps", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getServerSideProps", + "next.span_name": "getServerSideProps /pages/[param]/getServerSideProps", + "next.span_type": "Render.getServerSideProps", + }, + "kind": 0, + "name": "getServerSideProps /pages/[param]/getServerSideProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getServerSideProps", + "next.span_name": "render route (pages) /pages/[param]/getServerSideProps", + "next.span_type": "Render.renderDocument", + }, + "kind": 0, + "name": "render route (pages) /pages/[param]/getServerSideProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -257,52 +259,52 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/pages/[param]/getStaticProps", - "http.status_code": 200, - "http.target": "/pages/param/getStaticProps", - "next.route": "/pages/[param]/getStaticProps", - "next.span_name": "GET /pages/[param]/getStaticProps", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /pages/[param]/getStaticProps", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getStaticProps", - "next.span_name": "getStaticProps /pages/[param]/getStaticProps", - "next.span_type": "Render.getStaticProps", - }, - "kind": 0, - "name": "getStaticProps /pages/[param]/getStaticProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getStaticProps", - "next.span_name": "render route (pages) /pages/[param]/getStaticProps", - "next.span_type": "Render.renderDocument", - }, - "kind": 0, - "name": "render route (pages) /pages/[param]/getStaticProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/pages/[param]/getStaticProps", + "http.status_code": 200, + "http.target": "/pages/param/getStaticProps", + "next.route": "/pages/[param]/getStaticProps", + "next.span_name": "GET /pages/[param]/getStaticProps", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /pages/[param]/getStaticProps", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getStaticProps", + "next.span_name": "getStaticProps /pages/[param]/getStaticProps", + "next.span_type": "Render.getStaticProps", + }, + "kind": 0, + "name": "getStaticProps /pages/[param]/getStaticProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getStaticProps", + "next.span_name": "render route (pages) /pages/[param]/getStaticProps", + "next.span_type": "Render.renderDocument", + }, + "kind": 0, + "name": "render route (pages) /pages/[param]/getStaticProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -312,38 +314,38 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/api/pages/[param]/basic", - "http.status_code": 200, - "http.target": "/api/pages/param/basic", - "next.route": "/api/pages/[param]/basic", - "next.span_name": "GET /api/pages/[param]/basic", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /api/pages/[param]/basic", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.span_name": "executing api route (pages) /api/pages/[param]/basic", - "next.span_type": "Node.runHandler", - }, - "kind": 0, - "name": "executing api route (pages) /api/pages/[param]/basic", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/api/pages/[param]/basic", + "http.status_code": 200, + "http.target": "/api/pages/param/basic", + "next.route": "/api/pages/[param]/basic", + "next.span_name": "GET /api/pages/[param]/basic", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /api/pages/[param]/basic", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.span_name": "executing api route (pages) /api/pages/[param]/basic", + "next.span_type": "Node.runHandler", + }, + "kind": 0, + "name": "executing api route (pages) /api/pages/[param]/basic", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) diff --git a/test/e2e/prerender-native-module.test.ts b/test/e2e/prerender-native-module.test.ts index c23b1d2d05cd0..34adb36ce5bd8 100644 --- a/test/e2e/prerender-native-module.test.ts +++ b/test/e2e/prerender-native-module.test.ts @@ -85,8 +85,6 @@ describe('prerender native module', () => { /node_modules\/sqlite3\/.*?\.node/, /node_modules\/sqlite\/.*?\.js/, /node_modules\/next/, - /next\/router\.js/, - /next\/dist\/client\/router\.js/, /\/data\.sqlite/, ], notTests: [], @@ -99,7 +97,6 @@ describe('prerender native module', () => { ) const { version, files } = JSON.parse(contents) expect(version).toBe(1) - expect( check.tests.every((item) => files.some((file) => item.test(file))) ).toBe(true) diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index f21b4224fe236..1ff677e583b15 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -2070,7 +2070,6 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, - /node_modules\/next/, ], notTests: [], }, @@ -2082,7 +2081,6 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, - /node_modules\/next/, /\/world.txt/, ], notTests: [ @@ -2098,9 +2096,6 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, - /node_modules\/next/, - /next\/router\.js/, - /next\/dist\/client\/router\.js/, /node_modules\/@firebase\/firestore\/.*?\.js/, ], notTests: [/\/world.txt/], diff --git a/test/integration/externalize-next-server/app/node_modules/comps/index.js b/test/integration/externalize-next-server/app/node_modules/comps/index.js deleted file mode 100644 index 74c3153f1b835..0000000000000 --- a/test/integration/externalize-next-server/app/node_modules/comps/index.js +++ /dev/null @@ -1,5 +0,0 @@ -const react = require('react') - -module.exports = function() { - return react.createElement('p', null, 'MyComp:', typeof window) -} diff --git a/test/integration/externalize-next-server/app/node_modules/comps/package.json b/test/integration/externalize-next-server/app/node_modules/comps/package.json deleted file mode 100644 index 6e665b646a6ad..0000000000000 --- a/test/integration/externalize-next-server/app/node_modules/comps/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "comps", - "version": "1.0.0", - "main": "index.js", - "license": "MIT" -} diff --git a/test/integration/externalize-next-server/app/package.json b/test/integration/externalize-next-server/app/package.json deleted file mode 100644 index c5bd706a3a950..0000000000000 --- a/test/integration/externalize-next-server/app/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "externalize-next-server-app", - "version": "1.0.0", - "main": "index.js", - "license": "MIT" -} diff --git a/test/integration/externalize-next-server/app/pages/index.js b/test/integration/externalize-next-server/app/pages/index.js deleted file mode 100644 index 9ceb7bee3db17..0000000000000 --- a/test/integration/externalize-next-server/app/pages/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import MyComp from 'comps' - -const Page = () => ( - <> -

Hello {typeof window}

- - -) - -Page.getInitialProps = () => ({}) - -export default Page diff --git a/test/integration/externalize-next-server/test/index.test.js b/test/integration/externalize-next-server/test/index.test.js deleted file mode 100644 index bba968de16585..0000000000000 --- a/test/integration/externalize-next-server/test/index.test.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-env jest */ -import path from 'path' -import { nextBuild, readNextBuildServerPageFile } from 'next-test-utils' - -const appDir = path.join(__dirname, '../app') - -describe('externalize next/dist/shared', () => { - beforeAll(async () => { - await nextBuild(appDir) - }) - - it('Bundle next/dist/shared/lib/head.js but not next/dist/shared/lib/head-manager-context.js in _error', async () => { - const content = readNextBuildServerPageFile(appDir, '/_error') - expect(content).toContain( - `require("next/dist/shared/lib/head-manager-context.js")` - ) - expect(content).not.toContain(`require("next/dist/shared/lib/head.js")`) - }) -}) diff --git a/test/integration/jsconfig-baseurl/test/index.test.js b/test/integration/jsconfig-baseurl/test/index.test.js index 91f084ce1d55e..4ad014d0e8f03 100644 --- a/test/integration/jsconfig-baseurl/test/index.test.js +++ b/test/integration/jsconfig-baseurl/test/index.test.js @@ -72,12 +72,6 @@ describe('TypeScript Features', () => { const helloTrace = await fs.readJSON( join(appDir, '.next/server/pages/hello.js.nft.json') ) - const appTrace = await fs.readJSON( - join(appDir, '.next/server/pages/_app.js.nft.json') - ) - expect( - appTrace.files.some((file) => file.includes('node_modules/next')) - ).toBe(true) expect( helloTrace.files.some((file) => file.includes('components/world.js')) ).toBe(false) diff --git a/test/integration/jsconfig-paths/test/index.test.js b/test/integration/jsconfig-paths/test/index.test.js index 77c84e7edfbb4..bc3c635b9ce96 100644 --- a/test/integration/jsconfig-paths/test/index.test.js +++ b/test/integration/jsconfig-paths/test/index.test.js @@ -89,9 +89,6 @@ function runTests() { await nextBuild(appDir) }) it('should trace correctly', async () => { - const appTrace = await fs.readJSON( - join(appDir, '.next/server/pages/_app.js.nft.json') - ) const singleAliasTrace = await fs.readJSON( join(appDir, '.next/server/pages/single-alias.js.nft.json') ) @@ -107,9 +104,7 @@ function runTests() { const basicAliasTrace = await fs.readJSON( join(appDir, '.next/server/pages/basic-alias.js.nft.json') ) - expect( - appTrace.files.some((file) => file.includes('node_modules/next')) - ).toBe(true) + expect( singleAliasTrace.files.some((file) => file.includes('components/hello.js')