From 4f1be5d999a13444b4d976f2b22cc058b2309770 Mon Sep 17 00:00:00 2001 From: OJ Kwon <1210596+kwonoj@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:51:20 -0700 Subject: [PATCH 01/11] test(next-dev): migrate styled-jsx integration test (#55079) ### What? There are tests under `next-dev-tests` which used native binary to run tests for Turbopack. This should belong to next.js integration tests, and also indeed there are overlaps. As a first step, PR removes duplicated styled-jsx test and mark existing test under turbopack test filter as enabled. Closes WEB-1510 --- .../next/styled-jsx/input/index.js | 25 ------------------- .../src/server/lib/router-utils/setup-dev.ts | 9 +++++++ test/e2e/styled-jsx/index.test.ts | 6 +++-- test/turbopack-tests-manifest.js | 1 + 4 files changed, 14 insertions(+), 27 deletions(-) delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/styled-jsx/input/index.js diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/styled-jsx/input/index.js b/packages/next-swc/crates/next-dev-tests/tests/integration/next/styled-jsx/input/index.js deleted file mode 100644 index f2e030e055fa..000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/styled-jsx/input/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' -import TestRenderer from 'react-test-renderer' - -describe('styled-jsx', () => { - it('compiles away - - ) - - expect(test.toJSON()).toMatchObject({ - children: ['This should be color: red'], - props: { - className: /jsx\-[0-9a-f]+/, - }, - type: 'span', - }) - }) -}) diff --git a/packages/next/src/server/lib/router-utils/setup-dev.ts b/packages/next/src/server/lib/router-utils/setup-dev.ts index 24b8a095d113..2faaf385fa39 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev.ts @@ -186,6 +186,15 @@ async function startWatcher(opts: SetupOpts) { const { jsConfig } = await loadJsConfig(dir, opts.nextConfig) + // For the debugging purpose, check if createNext or equivalent next instance setup in test cases + // works correctly. Normally `run-test` hides output so only will be visible when `--debug` flag is used. + if (process.env.TURBOPACK && process.env.NEXT_TEST_MODE) { + require('console').log('Creating turbopack project', { + dir, + testMode: process.env.NEXT_TEST_MODE, + }) + } + const project = await bindings.turbo.createProject({ projectPath: dir, rootPath: opts.nextConfig.experimental.outputFileTracingRoot || dir, diff --git a/test/e2e/styled-jsx/index.test.ts b/test/e2e/styled-jsx/index.test.ts index aa22928ece66..c6767579c1c4 100644 --- a/test/e2e/styled-jsx/index.test.ts +++ b/test/e2e/styled-jsx/index.test.ts @@ -1,7 +1,7 @@ import path from 'path' import { createNext, FileRef } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' -import { renderViaHTTP } from 'next-test-utils' +import { renderViaHTTP, shouldRunTurboDevTest } from 'next-test-utils' import webdriver from 'next-webdriver' const appDir = path.join(__dirname, 'app') @@ -11,6 +11,8 @@ function runTest() { let next: NextInstance beforeAll(async () => { + const devCommand = shouldRunTurboDevTest() ? 'dev --turbo' : 'dev' + next = await createNext({ files: { node_modules_bak: new FileRef(path.join(appDir, 'node_modules_bak')), @@ -21,7 +23,7 @@ function runTest() { scripts: { setup: `cp -r ./node_modules_bak/my-comps ./node_modules;`, build: `yarn setup && next build`, - dev: `yarn setup && next dev`, + dev: `yarn setup && next ${devCommand}`, start: 'next start', }, }, diff --git a/test/turbopack-tests-manifest.js b/test/turbopack-tests-manifest.js index 61e99157470a..cf7b40653624 100644 --- a/test/turbopack-tests-manifest.js +++ b/test/turbopack-tests-manifest.js @@ -21,6 +21,7 @@ const enabledTests = [ 'test/e2e/type-module-interop/index.test.ts', 'test/e2e/undici-fetch/index.test.ts', 'test/integration/bigint/test/index.test.js', + 'test/e2e/styled-jsx/index.test.ts', // TODO: re-enable once the logging is aligned // 'test/integration/middleware-basic/test/index.test.js', ] From a8f300dd0f8d3c0b24b4f5504513e34cfac69d86 Mon Sep 17 00:00:00 2001 From: Dalton McPhaden <44533832+daltonmcphaden@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:27:57 -0400 Subject: [PATCH 02/11] Update 01-server-components.mdx (#55085) --- .../03-rendering/01-server-components.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx b/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx index f54290791cdb..4284a137212e 100644 --- a/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx +++ b/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx @@ -29,7 +29,7 @@ There are a couple of benefits to doing the rendering work on the server, includ ## Using Server Components in Next.js -By default, Next.js uses Server Components. This allows you to automatically implement server rendering with no additional configuration, and you can opt into using Client Components when you needed, see [Client Components](/docs/app/building-your-application/rendering/client-components). +By default, Next.js uses Server Components. This allows you to automatically implement server rendering with no additional configuration, and you can opt into using Client Components when needed, see [Client Components](/docs/app/building-your-application/rendering/client-components). ## How are Server Components rendered? From 9bb9f07e82357eeb4a15f9c1f5ecb21d600dd433 Mon Sep 17 00:00:00 2001 From: vercel-release-bot Date: Thu, 7 Sep 2023 06:36:23 +0000 Subject: [PATCH 03/11] v13.4.20-canary.19 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/eslint-config-next/package.json | 4 ++-- packages/eslint-plugin-next/package.json | 2 +- packages/font/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-env/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next-swc/package.json | 2 +- packages/next/package.json | 14 +++++++------- packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- packages/third-parties/package.json | 4 ++-- pnpm-lock.yaml | 16 ++++++++-------- 18 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lerna.json b/lerna.json index 19acc69763d4..967e25d5906d 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "13.4.20-canary.18" + "version": "13.4.20-canary.19" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 39368ecbda9b..dd37943c6023 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 40b39de435e8..c0705d75727c 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "ESLint configuration used by Next.js.", "main": "index.js", "license": "MIT", @@ -10,7 +10,7 @@ }, "homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config", "dependencies": { - "@next/eslint-plugin-next": "13.4.20-canary.18", + "@next/eslint-plugin-next": "13.4.20-canary.19", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", "eslint-import-resolver-node": "^0.3.6", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index bf8c14b93007..78d707f7e436 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "ESLint plugin for NextJS.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index 5e9555792474..b263ea34f81e 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index ee431062f783..6dee8d6b152f 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 2975b947fedc..d81de7ff9d11 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 27a38c5c7f2e..f8736c746bde 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 19808d2a42ee..9bbe796d6a80 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 63ffdaf3b8ef..f05f95c9d708 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index e6b62dd55494..2dc3d9891c01 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 154996666e17..9252f2f5ebbd 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 23e310684c26..066239ce49c5 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index a5d5ac891d2b..bebc8aa2b209 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -89,7 +89,7 @@ ] }, "dependencies": { - "@next/env": "13.4.20-canary.18", + "@next/env": "13.4.20-canary.19", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -144,11 +144,11 @@ "@mswjs/interceptors": "0.23.0", "@napi-rs/cli": "2.16.2", "@napi-rs/triples": "1.1.0", - "@next/polyfill-module": "13.4.20-canary.18", - "@next/polyfill-nomodule": "13.4.20-canary.18", - "@next/react-dev-overlay": "13.4.20-canary.18", - "@next/react-refresh-utils": "13.4.20-canary.18", - "@next/swc": "13.4.20-canary.18", + "@next/polyfill-module": "13.4.20-canary.19", + "@next/polyfill-nomodule": "13.4.20-canary.19", + "@next/react-dev-overlay": "13.4.20-canary.19", + "@next/react-refresh-utils": "13.4.20-canary.19", + "@next/swc": "13.4.20-canary.19", "@opentelemetry/api": "1.4.1", "@playwright/test": "^1.35.1", "@segment/ajv-human-errors": "2.1.2", diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 04836aa7c5a5..934729edf8f1 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 12830b5389eb..0db14f16bc12 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index ece657014fca..65f33b9a2789 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "13.4.20-canary.18", + "version": "13.4.20-canary.19", "private": true, "repository": { "url": "vercel/next.js", @@ -23,7 +23,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "13.4.20-canary.18", + "next": "13.4.20-canary.19", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aea9761ebbc4..2ce02614924c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -729,7 +729,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.3.3 @@ -790,7 +790,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../next-env '@swc/helpers': specifier: 0.5.1 @@ -917,19 +917,19 @@ importers: specifier: 1.1.0 version: 1.1.0 '@next/polyfill-module': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../next-polyfill-nomodule '@next/react-dev-overlay': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../react-dev-overlay '@next/react-refresh-utils': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../react-refresh-utils '@next/swc': - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../next-swc '@opentelemetry/api': specifier: 1.4.1 @@ -1685,7 +1685,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 13.4.20-canary.18 + specifier: 13.4.20-canary.19 version: link:../next outdent: specifier: 0.8.0 From a5b7c77c1ff0096af7609a8bfc1e064d30db4e30 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Thu, 7 Sep 2023 17:51:49 +0200 Subject: [PATCH 04/11] 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 8c46d8c02736..feb49b2ecbbf 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 000000000000..944ba5a8e827 --- /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 000000000000..8ebf54889577 --- /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 000000000000..83dc3aa56c9a --- /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 000000000000..0957c472383f --- /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 000000000000..8f603094bd28 --- /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 000000000000..e06229eee063 --- /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 fdec9ffc360f..38f27b3c1c7a 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 951f91570d92..25437fec08ef 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 abdf23d0fd7d..c4e1bce96aa5 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 6182b2d7d453..cecafb35f77a 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 e5af263c3d6b..738f5a4bf938 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 54c7dd3331a6..cfe9eebf19f9 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 de0bd2f2e7bf..05326de25520 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 e05ac926c139..c530f3bcb929 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 e7485ff100ad..1d242498b36d 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 013f87f9fd5b..ea7f044c12cc 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 000000000000..a5ad94c85fb0 --- /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 000000000000..c80db085946c --- /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 000000000000..39e97b4ccc85 --- /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 ce775cb8df7e..000000000000 --- 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 e2c2f40a06dd..108b763da879 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 72c048d7b648..000000000000 --- 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 72c048d7b648..000000000000 --- 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 000000000000..a5ad94c85fb0 --- /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 000000000000..c80db085946c --- /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 78fe148a8dc9..2da1ee3c4029 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 2da980d8b006..651074863809 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 bebc8aa2b209..75b2e78962ff 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 8ad7407b4be4..b0e0870ca139 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 c75509904c3a..f0d2ab692e2a 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 50a8b6165a74..b4b8e5b0fe6c 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 a48822f9ed75..eaeec836cb61 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 3f3527e6650d..b5def5d13c55 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 867429c2ba90..423beb27f6dc 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 f1f053d05eb9..d1b7487618fe 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 2be12daecdbc..c2b1089df5b3 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 8f82d244837e..47c0bd13f369 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 58b1b9f02ed0..e9143c4117bd 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 2382d6a83925..2c3c541e02b8 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 76356e05304a..799398b5f300 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 d090264391e7..a0a27a184cbf 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 3e410a93fb0c..1f0ffff7e2de 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 bf6a56100080..b3d69dcb065e 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 4d92a279c3ed..d37fce9851e9 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 23e5493ae83f..8d407fd6e9d6 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 10e72bc1ccbe..b9a2cfebd883 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 be486842c4f3..c1755cc5056b 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 e7a2f11a84f4..003d0a5cde9e 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 a6052636ef25..414b553c6324 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 7f7cca2003b0..94fdabb9b577 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 28f8c3412ab3..648069ea7698 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 81df295dba30..42df61a952af 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 187f86a47875..ac888a3ede0f 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 5d48eaee9ef9..7e9a93699fb6 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 606440a96f9c..1edbeffd7b3e 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 c5ddedd52351..f558edfab2f1 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 915f09cae0ca..8c23c47d42d7 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 ac343f8d7967..d637d850b145 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 65ce9e42c05e..bdd819b0614d 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 820e5909bf03..1ec39ae9e35f 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 807374c85557..2d4cdef348b1 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 f4d5e768b980..08dcefc65f2c 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 6ff039d14cf5..4ed01fed08c8 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 fe8a4a24c4b8..e47c42b2aa60 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 8055123367a9..bbbee6ff5f2a 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 1ffbb376e2f9..9ed54e8994d2 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 cd87ef3802b0..bd6dfc4ef904 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 b11f39b141cc..36c978926517 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 3b8fa6acb401..0c6caaba746c 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 37ef29296c89..0540c02079cb 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 48840a29db6d..defbb657c7c4 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 c5072218f035..4d3515066425 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 3f2183c004b1..321b07ecd0a5 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 f7c163f020d3..0f8a95c93746 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 d1456477bacf..07ec2e217c20 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 94226c8caa5c..7a15dee249e2 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 342ecb623df7..f43126691e5e 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 a4b2c6dcd318..f695e691c482 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 31f58755996a..b9fb7b9afd60 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 8e40472953f8..d0939d9148b9 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 fc94397c6aca..46f85311a9f0 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 a8526011c5d3..f0a29cb15d1f 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 6b8bc3228a00..9f6059ec48f5 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 30749ac66a83..1dd2eb8f7c63 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 6bc5fd7e7ace..0cc53e0214d8 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 f044c24feaba..764dc6279207 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 50795855c53d..1376ecfb197c 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 a28ac0e8ecb2..d5adfc9de38b 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 d826f2bdd4e0..fab999db35dd 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 1eca11b7358c..839fa1b13c0e 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 68932df7a36b..ddd6526e52f2 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 3283eb00a53f..6f70685df7e7 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 000000000000..78601739acbe --- /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 418e37420d7e..daa0291a1c8b 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 000000000000..e986c1bad389 --- /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 000000000000..f5909104bc77 --- /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 bf47738d35fe..d1d36d31501e 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 000000000000..e6139d5a6940 --- /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 000000000000..ed74c41adb91 --- /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 88dbda73b464..976daeeec4a8 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 000000000000..a935b62abdca --- /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 dac8ae554644..e2730ef66890 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 000000000000..55cdfbdeca37 --- /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 52188ed506df..a8e5dd6c5945 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 ae2b11b4dc00..3032aa685300 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 fb4d02c97b25..080361704b71 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 8e7bd60a8855..023cbde2d25d 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 50eb4528c7de..1b45358bdf8e 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 d2b563493ebe..39da802ff39b 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 000000000000..c390e9180b3d --- /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 087752225090..7abf3605541a 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 b93dfce5fae2..2a8252e4fd14 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 ac0989082aa9..fe36e793348e 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 1f53b3b47910..9b2e1526eec0 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 6d135da26e93..7bd6710cc4a8 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 e37ccca31481..f255fdd5412d 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 1ea32a75956c..5c6ad3e7ed5a 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 df3c369eef87..d44ea986cad6 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 8d7cd68bd3a9..7c7bff8c2f78 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 aad3ed2baf20..1e85b5c29097 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 04518b238935..8edd21db9c29 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 cb497fc587d4..390410edda29 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 3156e259f656..42f95767bfa4 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 1592d9855109..82ba84182701 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 ce68a8bec1e8..29f92dda8dc0 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 fa8e48f2fc08..e47ce2174dd3 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 f967be459022..4e8036a6894a 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 e242e19c9041..e3928f775f61 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 000000000000..04495d7b3621 --- /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 f38289c3d498..321490234153 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 000000000000..8e7c3063fa5e --- /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 2ce02614924c..c0bf75436a03 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 f4f30ac97ce2..08f9125a4f0c 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 a61e222c3872..3e5e6b82ea05 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 4433c9c2ee84..da17edc01839 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 ae798ec7c5c2..13e0fefea278 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 c23b1d2d05cd..34adb36ce5bd 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 f21b4224fe23..1ff677e583b1 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 74c3153f1b83..000000000000 --- 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 6e665b646a6a..000000000000 --- 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 c5bd706a3a95..000000000000 --- 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 9ceb7bee3db1..000000000000 --- 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 bba968de1658..000000000000 --- 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 91f084ce1d55..4ad014d0e8f0 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 77c84e7edfbb..bc3c635b9ce9 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') From 3062462156f29a29718d0f4c760402f520c4e473 Mon Sep 17 00:00:00 2001 From: vercel-release-bot Date: Thu, 7 Sep 2023 16:06:41 +0000 Subject: [PATCH 05/11] v13.4.20-canary.20 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/eslint-config-next/package.json | 4 ++-- packages/eslint-plugin-next/package.json | 2 +- packages/font/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-env/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next-swc/package.json | 2 +- packages/next/package.json | 14 +++++++------- packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- packages/third-parties/package.json | 4 ++-- pnpm-lock.yaml | 16 ++++++++-------- 18 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lerna.json b/lerna.json index 967e25d5906d..9473ac4b6bb8 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "13.4.20-canary.19" + "version": "13.4.20-canary.20" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index dd37943c6023..cca799bdb09c 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index c0705d75727c..8e028a9ab6d7 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "ESLint configuration used by Next.js.", "main": "index.js", "license": "MIT", @@ -10,7 +10,7 @@ }, "homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config", "dependencies": { - "@next/eslint-plugin-next": "13.4.20-canary.19", + "@next/eslint-plugin-next": "13.4.20-canary.20", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", "eslint-import-resolver-node": "^0.3.6", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 78d707f7e436..61d7b0aa5012 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "ESLint plugin for NextJS.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index b263ea34f81e..590100e48738 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 6dee8d6b152f..11e767e1d13e 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index d81de7ff9d11..42ac260ab258 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index f8736c746bde..dbac7a4a5d26 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 9bbe796d6a80..75739c7731f1 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index f05f95c9d708..1af2a0530ad1 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 2dc3d9891c01..f333bb901008 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 9252f2f5ebbd..c7b23dcd1e42 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 066239ce49c5..87ac07c84398 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index 75b2e78962ff..b80b6eb52090 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -90,7 +90,7 @@ ] }, "dependencies": { - "@next/env": "13.4.20-canary.19", + "@next/env": "13.4.20-canary.20", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -145,11 +145,11 @@ "@mswjs/interceptors": "0.23.0", "@napi-rs/cli": "2.16.2", "@napi-rs/triples": "1.1.0", - "@next/polyfill-module": "13.4.20-canary.19", - "@next/polyfill-nomodule": "13.4.20-canary.19", - "@next/react-dev-overlay": "13.4.20-canary.19", - "@next/react-refresh-utils": "13.4.20-canary.19", - "@next/swc": "13.4.20-canary.19", + "@next/polyfill-module": "13.4.20-canary.20", + "@next/polyfill-nomodule": "13.4.20-canary.20", + "@next/react-dev-overlay": "13.4.20-canary.20", + "@next/react-refresh-utils": "13.4.20-canary.20", + "@next/swc": "13.4.20-canary.20", "@opentelemetry/api": "1.4.1", "@playwright/test": "^1.35.1", "@segment/ajv-human-errors": "2.1.2", diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 934729edf8f1..cc6d2a5b422a 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 0db14f16bc12..20e4acbdcca7 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index 65f33b9a2789..7677d8733511 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "13.4.20-canary.19", + "version": "13.4.20-canary.20", "private": true, "repository": { "url": "vercel/next.js", @@ -23,7 +23,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "13.4.20-canary.19", + "next": "13.4.20-canary.20", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0bf75436a03..8478bb10eb18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -729,7 +729,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.3.3 @@ -790,7 +790,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../next-env '@swc/helpers': specifier: 0.5.1 @@ -917,19 +917,19 @@ importers: specifier: 1.1.0 version: 1.1.0 '@next/polyfill-module': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../next-polyfill-nomodule '@next/react-dev-overlay': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../react-dev-overlay '@next/react-refresh-utils': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../react-refresh-utils '@next/swc': - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../next-swc '@opentelemetry/api': specifier: 1.4.1 @@ -1688,7 +1688,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 13.4.20-canary.19 + specifier: 13.4.20-canary.20 version: link:../next outdent: specifier: 0.8.0 From a8fea15b506074b0bfe4fd8324c9f3c55e4a066a Mon Sep 17 00:00:00 2001 From: Leah Date: Thu, 7 Sep 2023 19:19:32 +0200 Subject: [PATCH 06/11] chore: add structured app page path type (#55070) Makes it easier to reason about the path and easy to convert to the router pathname Closes WEB-1507 --- packages/next-swc/crates/next-api/src/app.rs | 34 +- .../next-build/src/next_app/app_entries.rs | 18 +- packages/next-swc/crates/next-core/Cargo.toml | 1 + .../crates/next-core/src/app_source.rs | 78 ++-- .../crates/next-core/src/app_structure.rs | 340 +++++++----------- .../src/next_app/app_favicon_entry.rs | 12 +- .../next-core/src/next_app/app_page_entry.rs | 10 +- .../next-core/src/next_app/app_route_entry.rs | 8 +- .../crates/next-core/src/next_app/mod.rs | 287 ++++++++++++++- 9 files changed, 488 insertions(+), 300 deletions(-) diff --git a/packages/next-swc/crates/next-api/src/app.rs b/packages/next-swc/crates/next-api/src/app.rs index 12ed8e549887..295b57369a6d 100644 --- a/packages/next-swc/crates/next-api/src/app.rs +++ b/packages/next-swc/crates/next-api/src/app.rs @@ -8,7 +8,7 @@ use next_core::{ mode::NextMode, next_app::{ get_app_client_references_chunks, get_app_client_shared_chunks, get_app_page_entry, - get_app_route_entry, AppEntry, + get_app_route_entry, AppEntry, AppPage, }, next_client::{ get_client_module_options_context, get_client_resolve_options_context, @@ -344,8 +344,7 @@ impl AppProject { .map(|(pathname, app_entrypoint)| async { Ok(( pathname.clone(), - *app_entry_point_to_route(self, app_entrypoint.clone(), pathname.clone()) - .await?, + *app_entry_point_to_route(self, app_entrypoint.clone()).await?, )) }) .try_join() @@ -360,13 +359,9 @@ impl AppProject { pub async fn app_entry_point_to_route( app_project: Vc, entrypoint: AppEntrypoint, - pathname: String, ) -> Vc { match entrypoint { - AppEntrypoint::AppPage { - original_name, - loader_tree, - } => Route::AppPage { + AppEntrypoint::AppPage { page, loader_tree } => Route::AppPage { html_endpoint: Vc::upcast( AppEndpoint { ty: AppEndpointType::Page { @@ -374,8 +369,7 @@ pub async fn app_entry_point_to_route( loader_tree, }, app_project, - pathname: pathname.clone(), - original_name: original_name.clone(), + page: page.clone(), } .cell(), ), @@ -386,22 +380,17 @@ pub async fn app_entry_point_to_route( loader_tree, }, app_project, - pathname, - original_name, + page, } .cell(), ), }, - AppEntrypoint::AppRoute { - original_name, - path, - } => Route::AppRoute { + AppEntrypoint::AppRoute { page, path } => Route::AppRoute { endpoint: Vc::upcast( AppEndpoint { ty: AppEndpointType::Route { path }, app_project, - pathname, - original_name, + page, } .cell(), ), @@ -431,8 +420,7 @@ enum AppEndpointType { struct AppEndpoint { ty: AppEndpointType, app_project: Vc, - pathname: String, - original_name: String, + page: AppPage, } #[turbo_tasks::value_impl] @@ -444,8 +432,7 @@ impl AppEndpoint { self.app_project.edge_rsc_module_context(), loader_tree, self.app_project.app_dir(), - self.pathname.clone(), - self.original_name.clone(), + self.page.clone(), self.app_project.project().project_path(), ) } @@ -456,8 +443,7 @@ impl AppEndpoint { self.app_project.rsc_module_context(), self.app_project.edge_rsc_module_context(), Vc::upcast(FileSource::new(path)), - self.pathname.clone(), - self.original_name.clone(), + self.page.clone(), self.app_project.project().project_path(), ) } diff --git a/packages/next-swc/crates/next-build/src/next_app/app_entries.rs b/packages/next-swc/crates/next-build/src/next_app/app_entries.rs index 973b30ea7d54..9c7271ecdd66 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_entries.rs +++ b/packages/next-swc/crates/next-build/src/next_app/app_entries.rs @@ -187,31 +187,23 @@ pub async fn get_app_entries( let mut entries = entrypoints .await? .iter() - .map(|(pathname, entrypoint)| async move { + .map(|(_, entrypoint)| async move { Ok(match entrypoint { - Entrypoint::AppPage { - original_name, - loader_tree, - } => get_app_page_entry( + Entrypoint::AppPage { page, loader_tree } => get_app_page_entry( rsc_context, // TODO add edge support rsc_context, *loader_tree, app_dir, - pathname.clone(), - original_name.clone(), + page.clone(), project_root, ), - Entrypoint::AppRoute { - original_name, - path, - } => get_app_route_entry( + Entrypoint::AppRoute { page, path } => get_app_route_entry( rsc_context, // TODO add edge support rsc_context, Vc::upcast(FileSource::new(*path)), - pathname.clone(), - original_name.clone(), + page.clone(), project_root, ), }) diff --git a/packages/next-swc/crates/next-core/Cargo.toml b/packages/next-swc/crates/next-core/Cargo.toml index 5b50aff1185c..38bcb02a3cda 100644 --- a/packages/next-swc/crates/next-core/Cargo.toml +++ b/packages/next-swc/crates/next-core/Cargo.toml @@ -39,6 +39,7 @@ turbopack-binding = { workspace = true, features = [ "__turbo_tasks_hash", "__turbopack", "__turbopack_build", + "__turbopack_cli_utils", "__turbopack_core", "__turbopack_dev", "__turbopack_dev_server", 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 738f5a4bf938..4265f52139b4 100644 --- a/packages/next-swc/crates/next-core/src/app_source.rs +++ b/packages/next-swc/crates/next-core/src/app_source.rs @@ -65,7 +65,7 @@ use crate::{ fallback::get_fallback_page, loader_tree::{LoaderTreeModule, ServerComponentTransition}, mode::NextMode, - next_app::UnsupportedDynamicMetadataIssue, + next_app::{AppPage, AppPath, PathSegment, UnsupportedDynamicMetadataIssue}, next_client::{ context::{ get_client_assets_path, get_client_module_options_context, @@ -95,31 +95,28 @@ use crate::{ util::{render_data, NextRuntime}, }; -fn pathname_to_segments(pathname: &str) -> Result<(Vec, RouteType)> { +fn app_path_to_segments(path: &AppPath) -> Result<(Vec, RouteType)> { let mut segments = Vec::new(); - let mut split = pathname.split('/'); - while let Some(segment) = split.next() { - if segment.is_empty() - || (segment.starts_with('(') && segment.ends_with(')') || segment.starts_with('@')) - { - // ignore - } else if segment.starts_with("[[...") && segment.ends_with("]]") - || segment.starts_with("[...") && segment.ends_with(']') - { - // (optional) catch all segment - if split.remainder().is_some() { - bail!( - "Invalid route {}, catch all segment must be the last segment", - pathname - ) + let mut iter = path.iter().peekable(); + + while let Some(segment) = iter.next() { + match segment { + PathSegment::Static(s) => { + segments.push(BaseSegment::Static(s.to_string())); + } + PathSegment::Dynamic(_) => { + segments.push(BaseSegment::Dynamic); + } + PathSegment::CatchAll(_) | PathSegment::OptionalCatchAll(_) => { + if iter.peek().is_some() { + bail!( + "Invalid route {}, catch all segment must be the last segment", + path + ) + } + + return Ok((segments, RouteType::CatchAll)); } - return Ok((segments, RouteType::CatchAll)); - } else if segment.starts_with('[') || segment.ends_with(']') { - // dynamic segment - segments.push(BaseSegment::Dynamic); - } else { - // normal segment - segments.push(BaseSegment::Static(segment.to_string())); } } Ok((segments, RouteType::Exact)) @@ -654,12 +651,12 @@ pub async fn create_app_source( let entrypoints = entrypoints.await?; let mut sources: Vec<_> = entrypoints .iter() - .map(|(pathname, entrypoint)| match *entrypoint { + .map(|(_, entrypoint)| match *entrypoint { Entrypoint::AppPage { - original_name: _, + ref page, loader_tree, } => create_app_page_source_for_route( - pathname.clone(), + page.clone(), loader_tree, context_ssr, context, @@ -672,11 +669,8 @@ pub async fn create_app_source( output_path, render_data, ), - Entrypoint::AppRoute { - original_name: _, - path, - } => create_app_route_source_for_route( - pathname.clone(), + Entrypoint::AppRoute { ref page, path } => create_app_route_source_for_route( + page.clone(), path, context_ssr, project_path, @@ -696,7 +690,7 @@ pub async fn create_app_source( .collect(); if let Some(&Entrypoint::AppPage { - original_name: _, + page: _, loader_tree, }) = entrypoints.get("/_not-found") { @@ -769,7 +763,7 @@ async fn create_global_metadata_source( #[turbo_tasks::function] async fn create_app_page_source_for_route( - pathname: String, + page: AppPage, loader_tree: Vc, context_ssr: Vc, context: Vc, @@ -782,11 +776,12 @@ async fn create_app_page_source_for_route( intermediate_output_path_root: Vc, render_data: Vc, ) -> Result>> { - let pathname_vc = Vc::cell(pathname.clone()); + let app_path = AppPath::from(page.clone()); + let pathname_vc = Vc::cell(app_path.to_string()); let params_matcher = NextParamsMatcher::new(pathname_vc); - let (base_segments, route_type) = pathname_to_segments(&pathname)?; + let (base_segments, route_type) = app_path_to_segments(&app_path)?; let source = create_node_rendered_source( project_path, @@ -814,7 +809,7 @@ async fn create_app_page_source_for_route( should_debug("app_source"), ); - Ok(source.issue_file_path(app_dir, format!("Next.js App Page Route {pathname}"))) + Ok(source.issue_file_path(app_dir, format!("Next.js App Page Route {app_path}"))) } #[turbo_tasks::function] @@ -864,7 +859,7 @@ async fn create_app_not_found_page_source( #[turbo_tasks::function] async fn create_app_route_source_for_route( - pathname: String, + page: AppPage, entry_path: Vc, context_ssr: Vc, project_path: Vc, @@ -875,11 +870,12 @@ async fn create_app_route_source_for_route( intermediate_output_path_root: Vc, render_data: Vc, ) -> Result>> { - let pathname_vc = Vc::cell(pathname.to_string()); + let app_path = AppPath::from(page.clone()); + let pathname_vc = Vc::cell(app_path.to_string()); let params_matcher = NextParamsMatcher::new(pathname_vc); - let (base_segments, route_type) = pathname_to_segments(&pathname)?; + let (base_segments, route_type) = app_path_to_segments(&app_path)?; let source = create_node_api_source( project_path, @@ -906,7 +902,7 @@ async fn create_app_route_source_for_route( should_debug("app_source"), ); - Ok(source.issue_file_path(app_dir, format!("Next.js App Route {pathname}"))) + Ok(source.issue_file_path(app_dir, format!("Next.js App Route {app_path}"))) } /// The renderer for pages in app directory diff --git a/packages/next-swc/crates/next-core/src/app_structure.rs b/packages/next-swc/crates/next-core/src/app_structure.rs index c09ccdc644c9..a3597079af6c 100644 --- a/packages/next-swc/crates/next-core/src/app_structure.rs +++ b/packages/next-swc/crates/next-core/src/app_structure.rs @@ -1,7 +1,11 @@ use std::collections::{BTreeMap, HashMap}; use anyhow::{bail, Result}; -use indexmap::{indexmap, map::Entry, IndexMap}; +use indexmap::{ + indexmap, + map::{Entry, OccupiedEntry}, + IndexMap, +}; use once_cell::sync::Lazy; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -14,7 +18,11 @@ use turbopack_binding::{ turbopack::core::issue::{Issue, IssueExt, IssueSeverity}, }; -use crate::{next_config::NextConfig, next_import_map::get_next_package}; +use crate::{ + next_app::{AppPage, AppPath}, + next_config::NextConfig, + next_import_map::get_next_package, +}; /// A final route in the app directory. #[turbo_tasks::value] @@ -447,11 +455,11 @@ async fn merge_loader_trees( )] pub enum Entrypoint { AppPage { - original_name: String, + page: AppPage, loader_tree: Vc, }, AppRoute { - original_name: String, + page: AppPage, path: Vc, }, } @@ -487,131 +495,119 @@ async fn add_parallel_route( Ok(()) } +fn conflict_issue( + app_dir: Vc, + e: &OccupiedEntry, + a: &str, + b: &str, + value_a: &AppPage, + value_b: &AppPage, +) { + let item_names = if a == b { + format!("{}s", a) + } else { + format!("{} and {}", a, b) + }; + + DirectoryTreeIssue { + app_dir, + message: Vc::cell(format!( + "Conflicting {} at {}: {a} at {value_a} and {b} at {value_b}", + item_names, + e.key(), + )), + severity: IssueSeverity::Error.cell(), + } + .cell() + .emit(); +} + async fn add_app_page( app_dir: Vc, result: &mut IndexMap, - key: String, - original_name: String, + page: AppPage, loader_tree: Vc, ) -> Result<()> { - match result.entry(key) { - Entry::Occupied(mut e) => { - let value = e.get(); - match value { - Entrypoint::AppPage { - original_name: existing_original_name, - .. - } => { - if *existing_original_name != original_name { - DirectoryTreeIssue { - app_dir, - message: Vc::cell(format!( - "Conflicting pages at {}: {existing_original_name} and \ - {original_name}", - e.key() - )), - severity: IssueSeverity::Error.cell(), - } - .cell() - .emit(); - return Ok(()); - } - if let Entrypoint::AppPage { - loader_tree: value, .. - } = e.get_mut() - { - *value = merge_loader_trees(app_dir, *value, loader_tree) - .resolve() - .await?; - } - } - Entrypoint::AppRoute { - original_name: existing_original_name, - .. - } => { - DirectoryTreeIssue { - app_dir, - message: Vc::cell(format!( - "Conflicting page and route at {}: route at {existing_original_name} \ - and page at {original_name}", - e.key() - )), - severity: IssueSeverity::Error.cell(), - } - .cell() - .emit(); - return Ok(()); - } + let pathname = AppPath::from(page.clone()); + + let mut e = match result.entry(format!("{pathname}")) { + Entry::Occupied(e) => e, + Entry::Vacant(e) => { + e.insert(Entrypoint::AppPage { page, loader_tree }); + return Ok(()); + } + }; + + let conflict = |existing_name: &str, existing_page: &AppPage| { + conflict_issue(app_dir, &e, "page", existing_name, &page, existing_page); + }; + + let value = e.get(); + match value { + Entrypoint::AppPage { + page: existing_page, + .. + } => { + if *existing_page != page { + conflict("page", existing_page); + return Ok(()); + } + + if let Entrypoint::AppPage { + loader_tree: value, .. + } = e.get_mut() + { + *value = merge_loader_trees(app_dir, *value, loader_tree) + .resolve() + .await?; } } - Entry::Vacant(e) => { - e.insert(Entrypoint::AppPage { - original_name, - loader_tree, - }); + Entrypoint::AppRoute { + page: existing_page, + .. + } => { + conflict("route", existing_page); } } + Ok(()) } -async fn add_app_route( +fn add_app_route( app_dir: Vc, result: &mut IndexMap, - key: String, - original_name: String, + page: AppPage, path: Vc, -) -> Result<()> { - match result.entry(key) { - Entry::Occupied(mut e) => { - let value = e.get(); - match value { - Entrypoint::AppPage { - original_name: existing_original_name, - .. - } => { - DirectoryTreeIssue { - app_dir, - message: Vc::cell(format!( - "Conflicting route and page at {}: route at {original_name} and page \ - at {existing_original_name}", - e.key() - )), - severity: IssueSeverity::Error.cell(), - } - .cell() - .emit(); - } - Entrypoint::AppRoute { - original_name: existing_original_name, - .. - } => { - DirectoryTreeIssue { - app_dir, - message: Vc::cell(format!( - "Conflicting routes at {}: {existing_original_name} and \ - {original_name}", - e.key() - )), - severity: IssueSeverity::Error.cell(), - } - .cell() - .emit(); - return Ok(()); - } - } - *e.get_mut() = Entrypoint::AppRoute { - original_name, - path, - }; - } +) { + let pathname = AppPath::from(page.clone()); + + let e = match result.entry(format!("{pathname}")) { + Entry::Occupied(e) => e, Entry::Vacant(e) => { - e.insert(Entrypoint::AppRoute { - original_name, - path, - }); + e.insert(Entrypoint::AppRoute { page, path }); + return; + } + }; + + let conflict = |existing_name: &str, existing_page: &AppPage| { + conflict_issue(app_dir, &e, "route", existing_name, &page, existing_page); + }; + + let value = e.get(); + match value { + Entrypoint::AppPage { + page: existing_page, + .. + } => { + conflict("page", existing_page); + } + Entrypoint::AppRoute { + page: existing_page, + .. + } => { + conflict("route", existing_page); } } - Ok(()) } #[turbo_tasks::function] @@ -627,13 +623,7 @@ fn directory_tree_to_entrypoints( app_dir: Vc, directory_tree: Vc, ) -> Vc { - directory_tree_to_entrypoints_internal( - app_dir, - "".to_string(), - directory_tree, - "/".to_string(), - "/".to_string(), - ) + directory_tree_to_entrypoints_internal(app_dir, "".to_string(), directory_tree, AppPage::new()) } #[turbo_tasks::function] @@ -641,8 +631,7 @@ async fn directory_tree_to_entrypoints_internal( app_dir: Vc, directory_name: String, directory_tree: Vc, - path_prefix: String, - original_name_prefix: String, + app_page: AppPage, ) -> Result> { let mut result = IndexMap::new(); @@ -657,8 +646,7 @@ async fn directory_tree_to_entrypoints_internal( add_app_page( app_dir, &mut result, - path_prefix.to_string(), - original_name_prefix.to_string(), + app_page.clone(), if current_level_is_parallel_route { LoaderTree { segment: "__PAGE__".to_string(), @@ -697,8 +685,7 @@ async fn directory_tree_to_entrypoints_internal( add_app_page( app_dir, &mut result, - path_prefix.to_string(), - original_name_prefix.to_string(), + app_page.clone(), if current_level_is_parallel_route { LoaderTree { segment: "__DEFAULT__".to_string(), @@ -734,17 +721,11 @@ async fn directory_tree_to_entrypoints_internal( } if let Some(route) = components.route { - add_app_route( - app_dir, - &mut result, - path_prefix.to_string(), - original_name_prefix.to_string(), - route, - ) - .await?; + add_app_route(app_dir, &mut result, app_page.clone(), route); } - if path_prefix == "/" { + // root path: / + if app_page.len() == 0 { // Next.js has this logic in "collect-app-paths", where the root not-found page // is considered as its own entry point. if let Some(_not_found) = components.not_found { @@ -766,22 +747,14 @@ async fn directory_tree_to_entrypoints_internal( } .cell(); - add_app_page( - app_dir, - &mut result, - "/not-found".to_string(), - "/not-found".to_string(), - dev_not_found_tree, - ) - .await?; - add_app_page( - app_dir, - &mut result, - "/_not-found".to_string(), - "/_not-found".to_string(), - dev_not_found_tree, - ) - .await?; + { + let app_page = app_page.clone_push_str("not-found")?; + add_app_page(app_dir, &mut result, app_page, dev_not_found_tree).await?; + } + { + let app_page = app_page.clone_push_str("_not-found")?; + add_app_page(app_dir, &mut result, app_page, dev_not_found_tree).await?; + } } else { // Create default not-found page for production if there's no customized // not-found @@ -803,55 +776,35 @@ async fn directory_tree_to_entrypoints_internal( } .cell(); - add_app_page( - app_dir, - &mut result, - "/_not-found".to_string(), - "/_not-found".to_string(), - prod_not_found_tree, - ) - .await?; + let app_page = app_page.clone_push_str("_not-found")?; + add_app_page(app_dir, &mut result, app_page, prod_not_found_tree).await?; } } for (subdir_name, &subdirectory) in subdirectories.iter() { - let is_route_group = subdir_name.starts_with('(') && subdir_name.ends_with(')'); let parallel_route_key = match_parallel_route(subdir_name); + + let mut app_page = app_page.clone(); + if parallel_route_key.is_none() { + app_page.push_str(subdir_name)?; + } + let map = directory_tree_to_entrypoints_internal( app_dir, subdir_name.to_string(), subdirectory, - if is_route_group || parallel_route_key.is_some() { - path_prefix.clone() - } else if path_prefix == "/" { - format!("/{subdir_name}") - } else { - format!("{path_prefix}/{subdir_name}") - }, - if parallel_route_key.is_some() { - original_name_prefix.clone() - } else if original_name_prefix == "/" { - format!("/{subdir_name}") - } else { - format!("{original_name_prefix}/{subdir_name}") - }, + app_page, ) .await?; - for (full_path, entrypoint) in map.iter() { + + for (_, entrypoint) in map.iter() { match *entrypoint { Entrypoint::AppPage { - ref original_name, + ref page, loader_tree, } => { if current_level_is_parallel_route { - add_app_page( - app_dir, - &mut result, - full_path.clone(), - original_name.clone(), - loader_tree, - ) - .await?; + add_app_page(app_dir, &mut result, page.clone(), loader_tree).await?; } else { let key = parallel_route_key.unwrap_or("children").to_string(); let child_loader_tree = LoaderTree { @@ -862,28 +815,11 @@ async fn directory_tree_to_entrypoints_internal( components: components.without_leafs().cell(), } .cell(); - add_app_page( - app_dir, - &mut result, - full_path.clone(), - original_name.clone(), - child_loader_tree, - ) - .await?; + add_app_page(app_dir, &mut result, page.clone(), child_loader_tree).await?; } } - Entrypoint::AppRoute { - ref original_name, - path, - } => { - add_app_route( - app_dir, - &mut result, - full_path.clone(), - original_name.clone(), - path, - ) - .await?; + Entrypoint::AppRoute { ref page, path } => { + add_app_route(app_dir, &mut result, page.clone(), path); } } } diff --git a/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs index 0f4df5eb01f3..b72fc9af399a 100644 --- a/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs @@ -14,7 +14,10 @@ use turbopack_binding::{ }; use super::app_route_entry::get_app_route_entry; -use crate::{app_structure::MetadataItem, next_app::AppEntry}; +use crate::{ + app_structure::MetadataItem, + next_app::{AppEntry, AppPage, PageSegment}, +}; /// Computes the entry for a Next.js favicon file. #[turbo_tasks::function] @@ -57,7 +60,7 @@ pub async fn get_app_route_favicon_entry( const contentType = {content_type} const cacheControl = {cache_control} const buffer = Buffer.from({original_file_content_b64}, 'base64') - + export function GET() {{ return new NextResponse(buffer, {{ headers: {{ @@ -66,7 +69,7 @@ pub async fn get_app_route_favicon_entry( }}, }}) }} - + export const dynamic = 'force-static' "#, content_type = StringifyJs(&content_type), @@ -84,8 +87,7 @@ pub async fn get_app_route_favicon_entry( edge_context, Vc::upcast(source), // TODO(alexkirsz) Get this from the metadata? - "/favicon.ico".to_string(), - "/favicon.ico".to_string(), + AppPage(vec![PageSegment::Static("/favicon.ico".to_string())]), project_root, )) } diff --git a/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs index bde79e2c38b4..fd99d2566467 100644 --- a/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs @@ -19,7 +19,7 @@ use crate::{ app_structure::LoaderTree, loader_tree::{LoaderTreeModule, ServerComponentTransition}, mode::NextMode, - next_app::UnsupportedDynamicMetadataIssue, + next_app::{AppPage, AppPath, UnsupportedDynamicMetadataIssue}, next_server_component::NextServerComponentTransition, parse_segment_config_from_loader_tree, util::{load_next_js_template, virtual_next_js_template_path, NextRuntime}, @@ -32,8 +32,7 @@ pub async fn get_app_page_entry( edge_context: Vc, loader_tree: Vc, app_dir: Vc, - pathname: String, - original_name: String, + page: AppPage, project_root: Vc, ) -> Result> { let config = parse_segment_config_from_loader_tree(loader_tree, Vc::upcast(nodejs_context)); @@ -79,6 +78,9 @@ pub async fn get_app_page_entry( let pages = pages.iter().map(|page| page.to_string()).try_join().await?; + let original_name = page.to_string(); + let pathname = AppPath::from(page.clone()).to_string(); + let original_page_name = get_original_page_name(&original_name); let template_file = "build/templates/app-page.js"; @@ -90,7 +92,7 @@ pub async fn get_app_page_entry( .to_str()? .replace( "\"VAR_DEFINITION_PAGE\"", - &StringifyJs(&original_name).to_string(), + &StringifyJs(&page.to_string()).to_string(), ) .replace( "\"VAR_DEFINITION_PATHNAME\"", diff --git a/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs index 45cf5470f40e..e60c963e0ece 100644 --- a/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs @@ -21,7 +21,7 @@ use turbopack_binding::{ }; use crate::{ - next_app::AppEntry, + next_app::{AppEntry, AppPage, AppPath}, parse_segment_config_from_source, util::{load_next_js_template, virtual_next_js_template_path, NextRuntime}, }; @@ -32,8 +32,7 @@ pub async fn get_app_route_entry( nodejs_context: Vc, edge_context: Vc, source: Vc>, - pathname: String, - original_name: String, + page: AppPage, project_root: Vc, ) -> Result> { let config = parse_segment_config_from_source( @@ -52,6 +51,9 @@ pub async fn get_app_route_entry( let mut result = RopeBuilder::default(); + let original_name = page.to_string(); + let pathname = AppPath::from(page.clone()).to_string(); + let original_page_name = get_original_route_name(&original_name); let path = source.ident().path(); diff --git a/packages/next-swc/crates/next-core/src/next_app/mod.rs b/packages/next-swc/crates/next-core/src/next_app/mod.rs index 7f40ca34fb94..885ecdf4cd71 100644 --- a/packages/next-swc/crates/next-core/src/next_app/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_app/mod.rs @@ -6,12 +6,283 @@ pub(crate) mod app_page_entry; pub(crate) mod app_route_entry; pub(crate) mod unsupported_dynamic_metadata_issue; -pub use app_client_references_chunks::{ - get_app_client_references_chunks, ClientReferenceChunks, ClientReferencesChunks, +use std::{ + fmt::{Display, Formatter, Write}, + ops::Deref, }; -pub use app_client_shared_chunks::get_app_client_shared_chunks; -pub use app_entry::AppEntry; -pub use app_favicon_entry::get_app_route_favicon_entry; -pub use app_page_entry::get_app_page_entry; -pub use app_route_entry::get_app_route_entry; -pub use unsupported_dynamic_metadata_issue::UnsupportedDynamicMetadataIssue; + +use anyhow::{bail, Result}; +use serde::{Deserialize, Serialize}; +use turbo_tasks::{trace::TraceRawVcs, TaskInput}; + +pub use crate::next_app::{ + app_client_references_chunks::{ + get_app_client_references_chunks, ClientReferenceChunks, ClientReferencesChunks, + }, + app_client_shared_chunks::get_app_client_shared_chunks, + app_entry::AppEntry, + app_favicon_entry::get_app_route_favicon_entry, + app_page_entry::get_app_page_entry, + app_route_entry::get_app_route_entry, + unsupported_dynamic_metadata_issue::UnsupportedDynamicMetadataIssue, +}; + +#[derive(Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, TaskInput, TraceRawVcs)] +pub enum PageSegment { + Static(String), + Dynamic(String), + CatchAll(String), + OptionalCatchAll(String), + Group(String), + Parallel(String), + PageType(PageType), +} + +impl PageSegment { + pub fn parse(segment: &str) -> Result { + if segment.is_empty() { + bail!("empty segments are not allowed"); + } + + if segment.contains('/') { + bail!("slashes are not allowed in segments"); + } + + if let Some(s) = segment.strip_prefix('(').and_then(|s| s.strip_suffix(')')) { + return Ok(PageSegment::Group(s.to_string())); + } + + if let Some(s) = segment.strip_prefix('@') { + return Ok(PageSegment::Parallel(s.to_string())); + } + + if let Some(s) = segment + .strip_prefix("[[...") + .and_then(|s| s.strip_suffix("]]")) + { + return Ok(PageSegment::OptionalCatchAll(s.to_string())); + } + + if let Some(s) = segment + .strip_prefix("[...") + .and_then(|s| s.strip_suffix(']')) + { + return Ok(PageSegment::CatchAll(s.to_string())); + } + + if let Some(s) = segment.strip_prefix('[').and_then(|s| s.strip_suffix(']')) { + return Ok(PageSegment::Dynamic(s.to_string())); + } + + Ok(PageSegment::Static(segment.to_string())) + } +} + +impl Display for PageSegment { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + PageSegment::Static(s) => f.write_str(s), + PageSegment::Dynamic(s) => { + f.write_char('[')?; + f.write_str(s)?; + f.write_char(']') + } + PageSegment::CatchAll(s) => { + f.write_str("[...")?; + f.write_str(s)?; + f.write_char(']') + } + PageSegment::OptionalCatchAll(s) => { + f.write_str("[[...")?; + f.write_str(s)?; + f.write_str("]]") + } + PageSegment::Group(s) => { + f.write_char('(')?; + f.write_str(s)?; + f.write_char(')') + } + PageSegment::Parallel(s) => { + f.write_char('@')?; + f.write_str(s) + } + PageSegment::PageType(s) => Display::fmt(s, f), + } + } +} + +#[derive(Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, TaskInput, TraceRawVcs)] +pub enum PageType { + Page, + Route, +} + +impl Display for PageType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + PageType::Page => "page", + PageType::Route => "route", + }) + } +} + +/// Describes the pathname including all internal modifiers such as +/// intercepting routes, parallel routes and route/page suffixes that are not +/// part of the pathname. +#[derive( + Clone, Debug, Hash, PartialEq, Eq, Default, Serialize, Deserialize, TaskInput, TraceRawVcs, +)] +pub struct AppPage(pub Vec); + +impl AppPage { + pub fn new() -> Self { + Self::default() + } + + pub fn push(&mut self, segment: PageSegment) -> Result<()> { + if matches!( + self.0.last(), + Some(PageSegment::CatchAll(..) | PageSegment::OptionalCatchAll(..)) + ) && !matches!(segment, PageSegment::PageType(..)) + { + bail!( + "Invalid segment {}, catch all segment must be the last segment", + segment + ) + } + + self.0.push(segment); + Ok(()) + } + + pub fn push_str(&mut self, segment: &str) -> Result<()> { + if segment.is_empty() { + return Ok(()); + } + + self.push(PageSegment::parse(segment)?) + } + + pub fn clone_push(&self, segment: PageSegment) -> Result { + let mut cloned = self.clone(); + cloned.push(segment)?; + Ok(cloned) + } + + pub fn clone_push_str(&self, segment: &str) -> Result { + let mut cloned = self.clone(); + cloned.push_str(segment)?; + Ok(cloned) + } + + pub fn parse(page: &str) -> Result { + let mut app_page = Self::new(); + + for segment in page.split('/') { + app_page.push_str(segment)?; + } + + Ok(app_page) + } +} + +impl Display for AppPage { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.0.is_empty() { + return f.write_char('/'); + } + + for segment in &self.0 { + f.write_char('/')?; + Display::fmt(segment, f)?; + } + + Ok(()) + } +} + +impl Deref for AppPage { + type Target = [PageSegment]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, TaskInput, TraceRawVcs)] +pub enum PathSegment { + Static(String), + Dynamic(String), + CatchAll(String), + OptionalCatchAll(String), +} + +impl Display for PathSegment { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + PathSegment::Static(s) => f.write_str(s), + PathSegment::Dynamic(s) => { + f.write_char('[')?; + f.write_str(s)?; + f.write_char(']') + } + PathSegment::CatchAll(s) => { + f.write_str("[...")?; + f.write_str(s)?; + f.write_char(']') + } + PathSegment::OptionalCatchAll(s) => { + f.write_str("[[...")?; + f.write_str(s)?; + f.write_str("]]") + } + } + } +} + +/// The pathname (including dynamic placeholders) for a route to resolve. +#[derive( + Clone, Debug, Hash, PartialEq, Eq, Default, Serialize, Deserialize, TaskInput, TraceRawVcs, +)] +pub struct AppPath(pub Vec); + +impl Deref for AppPath { + type Target = [PathSegment]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for AppPath { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.0.is_empty() { + return f.write_char('/'); + } + + for segment in &self.0 { + f.write_char('/')?; + Display::fmt(segment, f)?; + } + + Ok(()) + } +} + +impl From for AppPath { + fn from(value: AppPage) -> Self { + AppPath( + value + .0 + .into_iter() + .filter_map(|segment| match segment { + PageSegment::Static(s) => Some(PathSegment::Static(s)), + PageSegment::Dynamic(s) => Some(PathSegment::Dynamic(s)), + PageSegment::CatchAll(s) => Some(PathSegment::CatchAll(s)), + PageSegment::OptionalCatchAll(s) => Some(PathSegment::OptionalCatchAll(s)), + _ => None, + }) + .collect(), + ) + } +} From 18980a6411030245ae2f1c50fb461ba3e9eb987f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 7 Sep 2023 12:20:41 -0600 Subject: [PATCH 07/11] Fixed i18n data route RegExp (#55109) The previous RegExp for data routes when i18n was enabled yielded a pattern like: ``` ^\/_next\/data\/development\/(?.+?)\/about.json$ ^\/_next\/data\/development\/(?.+?)\/blog/about.json$ ``` But the capture group for the `nextLocale` did so greedily, where the following: ``` /_next/data/development/en-US/blog/about.json ``` Would actually match both routes. This changes it to prevent the locale from including a `/` via `[^/]`, resulting in the new expressions: ``` ^\/_next\/data\/development\/(?[^/]+?)\/about.json$ ^\/_next\/data\/development\/(?[^/]+?)\/blog/about.json$ ``` --- .../src/server/lib/router-utils/filesystem.ts | 2 +- .../src/server/lib/router-utils/setup-dev.ts | 2 +- test/e2e/i18n-data-route/components/page.tsx | 12 +++ .../i18n-data-route/i18n-data-route.test.ts | 83 +++++++++++++++++++ test/e2e/i18n-data-route/next.config.js | 9 ++ .../pages/[slug]/about/index.tsx | 5 ++ .../e2e/i18n-data-route/pages/about/index.tsx | 5 ++ 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 test/e2e/i18n-data-route/components/page.tsx create mode 100644 test/e2e/i18n-data-route/i18n-data-route.test.ts create mode 100644 test/e2e/i18n-data-route/next.config.js create mode 100644 test/e2e/i18n-data-route/pages/[slug]/about/index.tsx create mode 100644 test/e2e/i18n-data-route/pages/about/index.tsx diff --git a/packages/next/src/server/lib/router-utils/filesystem.ts b/packages/next/src/server/lib/router-utils/filesystem.ts index d442b87fff85..270580f1f81a 100644 --- a/packages/next/src/server/lib/router-utils/filesystem.ts +++ b/packages/next/src/server/lib/router-utils/filesystem.ts @@ -249,7 +249,7 @@ export async function setupFsCheck(opts: { ? new RegExp( route.dataRouteRegex.replace( `/${escapedBuildId}/`, - `/${escapedBuildId}/(?.+?)/` + `/${escapedBuildId}/(?[^/]+?)/` ) ) : new RegExp(route.dataRouteRegex), diff --git a/packages/next/src/server/lib/router-utils/setup-dev.ts b/packages/next/src/server/lib/router-utils/setup-dev.ts index 2faaf385fa39..cf17fb2d7d9e 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev.ts @@ -1784,7 +1784,7 @@ async function startWatcher(opts: SetupOpts) { ? new RegExp( route.dataRouteRegex.replace( `/development/`, - `/development/(?.+?)/` + `/development/(?[^/]+?)/` ) ) : new RegExp(route.dataRouteRegex), diff --git a/test/e2e/i18n-data-route/components/page.tsx b/test/e2e/i18n-data-route/components/page.tsx new file mode 100644 index 000000000000..60418c58cba4 --- /dev/null +++ b/test/e2e/i18n-data-route/components/page.tsx @@ -0,0 +1,12 @@ +import React from 'react' + +export function Page({ page }) { + return

{page}

+} + +export function createGetServerSideProps(page: string) { + return async function getServerSideProps(ctx) { + const output = ctx.req.headers['x-invoke-output'] ?? null + return { props: { page, output } } + } +} diff --git a/test/e2e/i18n-data-route/i18n-data-route.test.ts b/test/e2e/i18n-data-route/i18n-data-route.test.ts new file mode 100644 index 000000000000..a25d8e8adad2 --- /dev/null +++ b/test/e2e/i18n-data-route/i18n-data-route.test.ts @@ -0,0 +1,83 @@ +import { createNextDescribe } from 'e2e-utils' + +const { i18n } = require('./next.config') + +const pages = [ + { url: '/about', page: '/about', params: null }, + { url: '/blog/about', page: '/[slug]/about', params: { slug: 'blog' } }, +] + +function checkDataRoute(data: any, page: string) { + expect(data).toHaveProperty('pageProps') + expect(data.pageProps).toHaveProperty('page', page) + expect(data.pageProps).toHaveProperty('output', page) +} + +createNextDescribe( + 'i18n-data-route', + { + files: __dirname, + }, + ({ next }) => { + describe('with locale prefix', () => { + describe.each(i18n.locales)('/%s', (locale) => { + const prefixed = pages.map((page) => ({ + ...page, + url: `/${locale}${page.url}`, + })) + + it.each(prefixed)( + 'should render $page via $url', + async ({ url, page }) => { + const $ = await next.render$(url) + expect($('[data-page]').data('page')).toBe(page) + } + ) + + it.each(prefixed)( + 'should serve data for $page', + async ({ url, page, params }) => { + url = `/_next/data/${next.buildId}${url}.json` + if (params) { + const query = new URLSearchParams(params) + // Ensure the query is sorted so it's deterministic. + query.sort() + url += `?${query.toString()}` + } + + const res = await next.fetch(url) + expect(res.status).toBe(200) + expect(res.headers.get('content-type')).toBe('application/json') + const data = await res.json() + checkDataRoute(data, page) + } + ) + }) + }) + + describe('without locale prefix', () => { + it.each(pages)('should render $page via $url', async ({ url, page }) => { + const $ = await next.render$(url) + expect($('[data-page]').data('page')).toBe(page) + }) + + it.each(pages)( + 'should serve data for $page', + async ({ url, page, params }) => { + url = `/_next/data/${next.buildId}/${i18n.defaultLocale}${url}.json` + if (params) { + const query = new URLSearchParams(params) + // Ensure the query is sorted so it's deterministic. + query.sort() + url += `?${query.toString()}` + } + const res = await next.fetch(url) + expect(res.status).toBe(200) + expect(res.headers.get('content-type')).toBe('application/json') + const data = await res.json() + checkDataRoute(data, page) + } + ) + }) + } +) diff --git a/test/e2e/i18n-data-route/next.config.js b/test/e2e/i18n-data-route/next.config.js new file mode 100644 index 000000000000..32a015bd37ca --- /dev/null +++ b/test/e2e/i18n-data-route/next.config.js @@ -0,0 +1,9 @@ +/** + * @type {import('next').NextConfig} + */ +module.exports = { + i18n: { + locales: ['en-CA', 'fr-CA'], + defaultLocale: 'en-CA', + }, +} diff --git a/test/e2e/i18n-data-route/pages/[slug]/about/index.tsx b/test/e2e/i18n-data-route/pages/[slug]/about/index.tsx new file mode 100644 index 000000000000..3c8a96c601f4 --- /dev/null +++ b/test/e2e/i18n-data-route/pages/[slug]/about/index.tsx @@ -0,0 +1,5 @@ +import { Page, createGetServerSideProps } from '../../../components/page' + +export default Page + +export const getServerSideProps = createGetServerSideProps('/[slug]/about') diff --git a/test/e2e/i18n-data-route/pages/about/index.tsx b/test/e2e/i18n-data-route/pages/about/index.tsx new file mode 100644 index 000000000000..83ef2115f532 --- /dev/null +++ b/test/e2e/i18n-data-route/pages/about/index.tsx @@ -0,0 +1,5 @@ +import { Page, createGetServerSideProps } from '../../components/page' + +export default Page + +export const getServerSideProps = createGetServerSideProps('/about') From 068002bb3a294368de9ef62f8831fe7177f27a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 7 Sep 2023 20:46:05 +0200 Subject: [PATCH 08/11] chore: upgrade to TypeScript 5.2.2 (#55105) ### What? Upgrade TypeScript to the latest version as of this PR. **This does not affect users, as the change is only for our repository.** ### Why? Part of some upcoming PRs to try to clean up cookie handling, now that `getSetCookie` is available. Since we use `undici`, which [implements it](https://github.com/nodejs/undici/pull/1915), we can get rid of some code to rely more on the platform. This PR is needed to get the types for `Headers#getSetCookie` which was added in 5.2 ### How? I needed to update some dependency types to get build to pass, but other than that, only needed to bump from `5.1.6` to `5.2.2`, so hopefully all is fine. --- .vscode/settings.json | 9 +- package.json | 4 +- .../crates/next-core/js/tsconfig.json | 7 +- packages/next/package.json | 4 +- packages/next/src/compiled/debug/index.js | 2 +- .../src/server/web/spec-extension/response.ts | 1 - packages/react-dev-overlay/tsconfig.json | 1 + pnpm-lock.yaml | 136 +++++++++--------- .../tsconfig-verifier/test/index.test.js | 4 +- 9 files changed, 77 insertions(+), 91 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2de2ef61939c..3b28ea4bab51 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,19 +18,15 @@ ], // Disable Jest autoRun as otherwise it will start running all tests the first time. "jest.autoRun": "off", - // Debugging. "debug.javascript.unmapMissingSources": true, - "files.exclude": { "**/node_modules": false, "node_modules": true, "*[!test]**/node_modules": true }, - // Ensure enough terminal history is preserved when running tests. "terminal.integrated.scrollback": 10000, - // Configure todo-tree to exclude node_modules, dist, and compiled. "todo-tree.filtering.excludeGlobs": [ "**/node_modules", @@ -48,10 +44,8 @@ "[x]", "TODO-APP" ], - // Disable TypeScript surveys. "typescript.surveys.enabled": false, - // Enable file nesting for unit test files. "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { @@ -82,5 +76,6 @@ "language": "markdown", "scheme": "file" } - ] + ], + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/package.json b/package.json index 5582290ae2d4..21a75dfea927 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "@types/node-fetch": "2.6.1", "@types/react": "18.2.8", "@types/react-dom": "18.2.4", - "@types/relay-runtime": "13.0.0", + "@types/relay-runtime": "14.1.13", "@types/selenium-webdriver": "4.0.15", "@types/sharp": "0.29.3", "@types/string-hash": "1.1.1", @@ -226,7 +226,7 @@ "tree-kill": "1.2.2", "tsec": "0.2.1", "turbo": "1.10.9", - "typescript": "5.1.3", + "typescript": "5.2.2", "unfetch": "4.2.0", "wait-port": "0.2.2", "webpack": "5.86.0", diff --git a/packages/next-swc/crates/next-core/js/tsconfig.json b/packages/next-swc/crates/next-core/js/tsconfig.json index a10e86411737..f7c18306acf8 100644 --- a/packages/next-swc/crates/next-core/js/tsconfig.json +++ b/packages/next-swc/crates/next-core/js/tsconfig.json @@ -4,23 +4,19 @@ "strict": true, "noFallthroughCasesInSwitch": true, "skipLibCheck": true, - // interop constraints "allowSyntheticDefaultImports": true, "esModuleInterop": true, - // js support "allowJs": true, "checkJs": false, - // environment "jsx": "react-jsx", "lib": ["ESNext", "DOM"], "target": "esnext", - // modules "baseUrl": ".", - "module": "esnext", + "module": "node16", "moduleResolution": "node16", "paths": { "@vercel/turbopack-next/*": ["src/*"], @@ -31,7 +27,6 @@ }, "resolveJsonModule": true, "types": ["react/next"], - // emit "noEmit": true, "stripInternal": true diff --git a/packages/next/package.json b/packages/next/package.json index b80b6eb52090..30ee061047fa 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -173,7 +173,7 @@ "@types/fresh": "0.5.0", "@types/glob": "7.1.1", "@types/jsonwebtoken": "9.0.0", - "@types/lodash": "4.14.149", + "@types/lodash": "4.14.198", "@types/lodash.curry": "4.1.6", "@types/lru-cache": "5.1.0", "@types/micromatch": "4.0.2", @@ -254,7 +254,7 @@ "lru-cache": "5.1.1", "micromatch": "4.0.4", "mini-css-extract-plugin": "2.4.3", - "msw": "^1.2.2", + "msw": "1.3.0", "nanoid": "3.1.32", "native-url": "0.3.4", "neo-async": "2.6.1", diff --git a/packages/next/src/compiled/debug/index.js b/packages/next/src/compiled/debug/index.js index 0bdf3d36ef5d..853a1a2db787 100644 --- a/packages/next/src/compiled/debug/index.js +++ b/packages/next/src/compiled/debug/index.js @@ -1 +1 @@ -(()=>{var e={237:(e,t,r)=>{t.log=log;t.formatArgs=formatArgs;t.save=save;t.load=load;t.useColors=useColors;t.storage=localstorage();t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function useColors(){if(typeof window!=="undefined"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs)){return true}if(typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)){return false}return typeof document!=="undefined"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window!=="undefined"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function formatArgs(t){t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff);if(!this.useColors){return}const r="color: "+this.color;t.splice(1,0,r,"color: inherit");let s=0;let n=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{if(e==="%%"){return}s++;if(e==="%c"){n=s}}));t.splice(n,0,r)}function log(...e){return typeof console==="object"&&console.log&&console.log(...e)}function save(e){try{if(e){t.storage.setItem("debug",e)}else{t.storage.removeItem("debug")}}catch(e){}}function load(){let e;try{e=t.storage.getItem("debug")}catch(e){}if(!e&&typeof process!=="undefined"&&"env"in process){e=process.env.DEBUG}return e}function localstorage(){try{return localStorage}catch(e){}}e.exports=r(573)(t);const{formatters:s}=e.exports;s.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},573:(e,t,r)=>{function setup(e){createDebug.debug=createDebug;createDebug.default=createDebug;createDebug.coerce=coerce;createDebug.disable=disable;createDebug.enable=enable;createDebug.enabled=enabled;createDebug.humanize=r(79);Object.keys(e).forEach((t=>{createDebug[t]=e[t]}));createDebug.instances=[];createDebug.names=[];createDebug.skips=[];createDebug.formatters={};function selectColor(e){let t=0;for(let r=0;r{if(t==="%%"){return t}o++;const n=createDebug.formatters[s];if(typeof n==="function"){const s=e[o];t=n.call(r,s);e.splice(o,1);o--}return t}));createDebug.formatArgs.call(r,e);const c=r.log||createDebug.log;c.apply(r,e)}debug.namespace=e;debug.enabled=createDebug.enabled(e);debug.useColors=createDebug.useColors();debug.color=selectColor(e);debug.destroy=destroy;debug.extend=extend;if(typeof createDebug.init==="function"){createDebug.init(debug)}createDebug.instances.push(debug);return debug}function destroy(){const e=createDebug.instances.indexOf(this);if(e!==-1){createDebug.instances.splice(e,1);return true}return false}function extend(e,t){const r=createDebug(this.namespace+(typeof t==="undefined"?":":t)+e);r.log=this.log;return r}function enable(e){createDebug.save(e);createDebug.names=[];createDebug.skips=[];let t;const r=(typeof e==="string"?e:"").split(/[\s,]+/);const s=r.length;for(t=0;t"-"+e))].join(",");createDebug.enable("");return e}function enabled(e){if(e[e.length-1]==="*"){return true}let t;let r;for(t=0,r=createDebug.skips.length;t{if(typeof process==="undefined"||process.type==="renderer"||process.browser===true||process.__nwjs){e.exports=r(237)}else{e.exports=r(354)}},354:(e,t,r)=>{const s=r(224);const n=r(837);t.init=init;t.log=log;t.formatArgs=formatArgs;t.save=save;t.load=load;t.useColors=useColors;t.colors=[6,2,3,4,5,1];try{const e=r(220);if(e&&(e.stderr||e).level>=2){t.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221]}}catch(e){}t.inspectOpts=Object.keys(process.env).filter((e=>/^debug_/i.test(e))).reduce(((e,t)=>{const r=t.substring(6).toLowerCase().replace(/_([a-z])/g,((e,t)=>t.toUpperCase()));let s=process.env[t];if(/^(yes|on|true|enabled)$/i.test(s)){s=true}else if(/^(no|off|false|disabled)$/i.test(s)){s=false}else if(s==="null"){s=null}else{s=Number(s)}e[r]=s;return e}),{});function useColors(){return"colors"in t.inspectOpts?Boolean(t.inspectOpts.colors):s.isatty(process.stderr.fd)}function formatArgs(t){const{namespace:r,useColors:s}=this;if(s){const s=this.color;const n="[3"+(s<8?s:"8;5;"+s);const o=` ${n};1m${r} `;t[0]=o+t[0].split("\n").join("\n"+o);t.push(n+"m+"+e.exports.humanize(this.diff)+"")}else{t[0]=getDate()+r+" "+t[0]}}function getDate(){if(t.inspectOpts.hideDate){return""}return(new Date).toISOString()+" "}function log(...e){return process.stderr.write(n.format(...e)+"\n")}function save(e){if(e){process.env.DEBUG=e}else{delete process.env.DEBUG}}function load(){return process.env.DEBUG}function init(e){e.inspectOpts={};const r=Object.keys(t.inspectOpts);for(let s=0;s{"use strict";e.exports=(e,t)=>{t=t||process.argv;const r=e.startsWith("-")?"":e.length===1?"-":"--";const s=t.indexOf(r+e);const n=t.indexOf("--");return s!==-1&&(n===-1?true:s{var t=1e3;var r=t*60;var s=r*60;var n=s*24;var o=n*7;var c=n*365.25;e.exports=function(e,t){t=t||{};var r=typeof e;if(r==="string"&&e.length>0){return parse(e)}else if(r==="number"&&isFinite(e)){return t.long?fmtLong(e):fmtShort(e)}throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function parse(e){e=String(e);if(e.length>100){return}var a=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!a){return}var u=parseFloat(a[1]);var i=(a[2]||"ms").toLowerCase();switch(i){case"years":case"year":case"yrs":case"yr":case"y":return u*c;case"weeks":case"week":case"w":return u*o;case"days":case"day":case"d":return u*n;case"hours":case"hour":case"hrs":case"hr":case"h":return u*s;case"minutes":case"minute":case"mins":case"min":case"m":return u*r;case"seconds":case"second":case"secs":case"sec":case"s":return u*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return u;default:return undefined}}function fmtShort(e){var o=Math.abs(e);if(o>=n){return Math.round(e/n)+"d"}if(o>=s){return Math.round(e/s)+"h"}if(o>=r){return Math.round(e/r)+"m"}if(o>=t){return Math.round(e/t)+"s"}return e+"ms"}function fmtLong(e){var o=Math.abs(e);if(o>=n){return plural(e,o,n,"day")}if(o>=s){return plural(e,o,s,"hour")}if(o>=r){return plural(e,o,r,"minute")}if(o>=t){return plural(e,o,t,"second")}return e+" ms"}function plural(e,t,r,s){var n=t>=r*1.5;return Math.round(e/r)+" "+s+(n?"s":"")}},220:(e,t,r)=>{"use strict";const s=r(37);const n=r(343);const o=process.env;let c;if(n("no-color")||n("no-colors")||n("color=false")){c=false}else if(n("color")||n("colors")||n("color=true")||n("color=always")){c=true}if("FORCE_COLOR"in o){c=o.FORCE_COLOR.length===0||parseInt(o.FORCE_COLOR,10)!==0}function translateLevel(e){if(e===0){return false}return{level:e,hasBasic:true,has256:e>=2,has16m:e>=3}}function supportsColor(e){if(c===false){return 0}if(n("color=16m")||n("color=full")||n("color=truecolor")){return 3}if(n("color=256")){return 2}if(e&&!e.isTTY&&c!==true){return 0}const t=c?1:0;if(process.platform==="win32"){const e=s.release().split(".");if(Number(process.versions.node.split(".")[0])>=8&&Number(e[0])>=10&&Number(e[2])>=10586){return Number(e[2])>=14931?3:2}return 1}if("CI"in o){if(["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some((e=>e in o))||o.CI_NAME==="codeship"){return 1}return t}if("TEAMCITY_VERSION"in o){return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(o.TEAMCITY_VERSION)?1:0}if(o.COLORTERM==="truecolor"){return 3}if("TERM_PROGRAM"in o){const e=parseInt((o.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(o.TERM_PROGRAM){case"iTerm.app":return e>=3?3:2;case"Apple_Terminal":return 2}}if(/-256(color)?$/i.test(o.TERM)){return 2}if(/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(o.TERM)){return 1}if("COLORTERM"in o){return 1}if(o.TERM==="dumb"){return t}return t}function getSupportLevel(e){const t=supportsColor(e);return translateLevel(t)}e.exports={supportsColor:getSupportLevel,stdout:getSupportLevel(process.stdout),stderr:getSupportLevel(process.stderr)}},37:e=>{"use strict";e.exports=require("os")},224:e=>{"use strict";e.exports=require("tty")},837:e=>{"use strict";e.exports=require("util")}};var t={};function __nccwpck_require__(r){var s=t[r];if(s!==undefined){return s.exports}var n=t[r]={exports:{}};var o=true;try{e[r](n,n.exports,__nccwpck_require__);o=false}finally{if(o)delete t[r]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r=__nccwpck_require__(792);module.exports=r})(); \ No newline at end of file +(()=>{var e={237:(e,t,r)=>{t.log=log;t.formatArgs=formatArgs;t.save=save;t.load=load;t.useColors=useColors;t.storage=localstorage();t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function useColors(){if(typeof window!=="undefined"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs)){return true}if(typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)){return false}return typeof document!=="undefined"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window!=="undefined"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function formatArgs(t){t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff);if(!this.useColors){return}const r="color: "+this.color;t.splice(1,0,r,"color: inherit");let s=0;let n=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{if(e==="%%"){return}s++;if(e==="%c"){n=s}}));t.splice(n,0,r)}function log(...e){return typeof console==="object"&&console.log&&console.log(...e)}function save(e){try{if(e){t.storage.setItem("debug",e)}else{t.storage.removeItem("debug")}}catch(e){}}function load(){let e;try{e=t.storage.getItem("debug")}catch(e){}if(!e&&typeof process!=="undefined"&&"env"in process){e=process.env.DEBUG}return e}function localstorage(){try{return localStorage}catch(e){}}e.exports=r(573)(t);const{formatters:s}=e.exports;s.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},573:(e,t,r)=>{function setup(e){createDebug.debug=createDebug;createDebug.default=createDebug;createDebug.coerce=coerce;createDebug.disable=disable;createDebug.enable=enable;createDebug.enabled=enabled;createDebug.humanize=r(958);Object.keys(e).forEach((t=>{createDebug[t]=e[t]}));createDebug.instances=[];createDebug.names=[];createDebug.skips=[];createDebug.formatters={};function selectColor(e){let t=0;for(let r=0;r{if(t==="%%"){return t}o++;const n=createDebug.formatters[s];if(typeof n==="function"){const s=e[o];t=n.call(r,s);e.splice(o,1);o--}return t}));createDebug.formatArgs.call(r,e);const c=r.log||createDebug.log;c.apply(r,e)}debug.namespace=e;debug.enabled=createDebug.enabled(e);debug.useColors=createDebug.useColors();debug.color=selectColor(e);debug.destroy=destroy;debug.extend=extend;if(typeof createDebug.init==="function"){createDebug.init(debug)}createDebug.instances.push(debug);return debug}function destroy(){const e=createDebug.instances.indexOf(this);if(e!==-1){createDebug.instances.splice(e,1);return true}return false}function extend(e,t){const r=createDebug(this.namespace+(typeof t==="undefined"?":":t)+e);r.log=this.log;return r}function enable(e){createDebug.save(e);createDebug.names=[];createDebug.skips=[];let t;const r=(typeof e==="string"?e:"").split(/[\s,]+/);const s=r.length;for(t=0;t"-"+e))].join(",");createDebug.enable("");return e}function enabled(e){if(e[e.length-1]==="*"){return true}let t;let r;for(t=0,r=createDebug.skips.length;t{if(typeof process==="undefined"||process.type==="renderer"||process.browser===true||process.__nwjs){e.exports=r(237)}else{e.exports=r(354)}},354:(e,t,r)=>{const s=r(224);const n=r(837);t.init=init;t.log=log;t.formatArgs=formatArgs;t.save=save;t.load=load;t.useColors=useColors;t.colors=[6,2,3,4,5,1];try{const e=r(220);if(e&&(e.stderr||e).level>=2){t.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221]}}catch(e){}t.inspectOpts=Object.keys(process.env).filter((e=>/^debug_/i.test(e))).reduce(((e,t)=>{const r=t.substring(6).toLowerCase().replace(/_([a-z])/g,((e,t)=>t.toUpperCase()));let s=process.env[t];if(/^(yes|on|true|enabled)$/i.test(s)){s=true}else if(/^(no|off|false|disabled)$/i.test(s)){s=false}else if(s==="null"){s=null}else{s=Number(s)}e[r]=s;return e}),{});function useColors(){return"colors"in t.inspectOpts?Boolean(t.inspectOpts.colors):s.isatty(process.stderr.fd)}function formatArgs(t){const{namespace:r,useColors:s}=this;if(s){const s=this.color;const n="[3"+(s<8?s:"8;5;"+s);const o=` ${n};1m${r} `;t[0]=o+t[0].split("\n").join("\n"+o);t.push(n+"m+"+e.exports.humanize(this.diff)+"")}else{t[0]=getDate()+r+" "+t[0]}}function getDate(){if(t.inspectOpts.hideDate){return""}return(new Date).toISOString()+" "}function log(...e){return process.stderr.write(n.format(...e)+"\n")}function save(e){if(e){process.env.DEBUG=e}else{delete process.env.DEBUG}}function load(){return process.env.DEBUG}function init(e){e.inspectOpts={};const r=Object.keys(t.inspectOpts);for(let s=0;s{"use strict";e.exports=(e,t)=>{t=t||process.argv;const r=e.startsWith("-")?"":e.length===1?"-":"--";const s=t.indexOf(r+e);const n=t.indexOf("--");return s!==-1&&(n===-1?true:s{var t=1e3;var r=t*60;var s=r*60;var n=s*24;var o=n*7;var c=n*365.25;e.exports=function(e,t){t=t||{};var r=typeof e;if(r==="string"&&e.length>0){return parse(e)}else if(r==="number"&&isFinite(e)){return t.long?fmtLong(e):fmtShort(e)}throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function parse(e){e=String(e);if(e.length>100){return}var a=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!a){return}var u=parseFloat(a[1]);var i=(a[2]||"ms").toLowerCase();switch(i){case"years":case"year":case"yrs":case"yr":case"y":return u*c;case"weeks":case"week":case"w":return u*o;case"days":case"day":case"d":return u*n;case"hours":case"hour":case"hrs":case"hr":case"h":return u*s;case"minutes":case"minute":case"mins":case"min":case"m":return u*r;case"seconds":case"second":case"secs":case"sec":case"s":return u*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return u;default:return undefined}}function fmtShort(e){var o=Math.abs(e);if(o>=n){return Math.round(e/n)+"d"}if(o>=s){return Math.round(e/s)+"h"}if(o>=r){return Math.round(e/r)+"m"}if(o>=t){return Math.round(e/t)+"s"}return e+"ms"}function fmtLong(e){var o=Math.abs(e);if(o>=n){return plural(e,o,n,"day")}if(o>=s){return plural(e,o,s,"hour")}if(o>=r){return plural(e,o,r,"minute")}if(o>=t){return plural(e,o,t,"second")}return e+" ms"}function plural(e,t,r,s){var n=t>=r*1.5;return Math.round(e/r)+" "+s+(n?"s":"")}},220:(e,t,r)=>{"use strict";const s=r(37);const n=r(343);const o=process.env;let c;if(n("no-color")||n("no-colors")||n("color=false")){c=false}else if(n("color")||n("colors")||n("color=true")||n("color=always")){c=true}if("FORCE_COLOR"in o){c=o.FORCE_COLOR.length===0||parseInt(o.FORCE_COLOR,10)!==0}function translateLevel(e){if(e===0){return false}return{level:e,hasBasic:true,has256:e>=2,has16m:e>=3}}function supportsColor(e){if(c===false){return 0}if(n("color=16m")||n("color=full")||n("color=truecolor")){return 3}if(n("color=256")){return 2}if(e&&!e.isTTY&&c!==true){return 0}const t=c?1:0;if(process.platform==="win32"){const e=s.release().split(".");if(Number(process.versions.node.split(".")[0])>=8&&Number(e[0])>=10&&Number(e[2])>=10586){return Number(e[2])>=14931?3:2}return 1}if("CI"in o){if(["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some((e=>e in o))||o.CI_NAME==="codeship"){return 1}return t}if("TEAMCITY_VERSION"in o){return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(o.TEAMCITY_VERSION)?1:0}if(o.COLORTERM==="truecolor"){return 3}if("TERM_PROGRAM"in o){const e=parseInt((o.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(o.TERM_PROGRAM){case"iTerm.app":return e>=3?3:2;case"Apple_Terminal":return 2}}if(/-256(color)?$/i.test(o.TERM)){return 2}if(/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(o.TERM)){return 1}if("COLORTERM"in o){return 1}if(o.TERM==="dumb"){return t}return t}function getSupportLevel(e){const t=supportsColor(e);return translateLevel(t)}e.exports={supportsColor:getSupportLevel,stdout:getSupportLevel(process.stdout),stderr:getSupportLevel(process.stderr)}},37:e=>{"use strict";e.exports=require("os")},224:e=>{"use strict";e.exports=require("tty")},837:e=>{"use strict";e.exports=require("util")}};var t={};function __nccwpck_require__(r){var s=t[r];if(s!==undefined){return s.exports}var n=t[r]={exports:{}};var o=true;try{e[r](n,n.exports,__nccwpck_require__);o=false}finally{if(o)delete t[r]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r=__nccwpck_require__(792);module.exports=r})(); \ No newline at end of file diff --git a/packages/next/src/server/web/spec-extension/response.ts b/packages/next/src/server/web/spec-extension/response.ts index 18fa7158c5ad..86b09e3616a8 100644 --- a/packages/next/src/server/web/spec-extension/response.ts +++ b/packages/next/src/server/web/spec-extension/response.ts @@ -71,7 +71,6 @@ export class NextResponse extends Response { body: JsonBody, init?: ResponseInit ): NextResponse { - // @ts-expect-error This is not in lib/dom right now, and we can't augment it. const response: Response = Response.json(body, init) return new NextResponse(response.body, response) } diff --git a/packages/react-dev-overlay/tsconfig.json b/packages/react-dev-overlay/tsconfig.json index 59e05adbe1d2..01235b6a1bf8 100644 --- a/packages/react-dev-overlay/tsconfig.json +++ b/packages/react-dev-overlay/tsconfig.json @@ -12,6 +12,7 @@ "jsx": "react", "noFallthroughCasesInSwitch": true, "skipLibCheck": true, + "module": "Node16", "moduleResolution": "Node16" }, "include": ["src/**/*.ts", "src/**/*.tsx", "types/local.d.ts"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8478bb10eb18..68790eaa3a0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,8 +150,8 @@ importers: specifier: 18.2.4 version: 18.2.4 '@types/relay-runtime': - specifier: 13.0.0 - version: 13.0.0 + specifier: 14.1.13 + version: 14.1.13 '@types/selenium-webdriver': specifier: 4.0.15 version: 4.0.15 @@ -166,10 +166,10 @@ importers: version: 2.0.3 '@typescript-eslint/eslint-plugin': specifier: 6.1.0 - version: 6.1.0(@typescript-eslint/parser@6.1.0)(eslint@7.24.0)(typescript@5.1.3) + version: 6.1.0(@typescript-eslint/parser@6.1.0)(eslint@7.24.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: 6.1.0 - version: 6.1.0(eslint@7.24.0)(typescript@5.1.3) + version: 6.1.0(eslint@7.24.0)(typescript@5.2.2) '@vercel/fetch': specifier: 6.1.1 version: 6.1.1(@types/node-fetch@2.6.1)(node-fetch@2.6.7) @@ -250,7 +250,7 @@ importers: version: 2.22.1(@typescript-eslint/parser@6.1.0)(eslint@7.24.0) eslint-plugin-jest: specifier: 24.3.5 - version: 24.3.5(@typescript-eslint/eslint-plugin@6.1.0)(eslint@7.24.0)(typescript@5.1.3) + version: 24.3.5(@typescript-eslint/eslint-plugin@6.1.0)(eslint@7.24.0)(typescript@5.2.2) eslint-plugin-jsdoc: specifier: 39.6.4 version: 39.6.4(eslint@7.24.0) @@ -520,13 +520,13 @@ importers: version: 1.2.2 tsec: specifier: 0.2.1 - version: 0.2.1(@bazel/bazelisk@1.12.1)(typescript@5.1.3) + version: 0.2.1(@bazel/bazelisk@1.12.1)(typescript@5.2.2) turbo: specifier: 1.10.9 version: 1.10.9 typescript: - specifier: 5.1.3 - version: 5.1.3 + specifier: 5.2.2 + version: 5.2.2 unfetch: specifier: 4.2.0 version: 4.2.0 @@ -1001,8 +1001,8 @@ importers: specifier: 9.0.0 version: 9.0.0 '@types/lodash': - specifier: 4.14.149 - version: 4.14.149 + specifier: 4.14.198 + version: 4.14.198 '@types/lodash.curry': specifier: 4.1.6 version: 4.1.6 @@ -1244,8 +1244,8 @@ importers: specifier: 2.4.3 version: 2.4.3(webpack@5.86.0) msw: - specifier: ^1.2.2 - version: 1.2.2(typescript@5.1.3) + specifier: 1.3.0 + version: 1.3.0(typescript@5.2.2) nanoid: specifier: 3.1.32 version: 3.1.32 @@ -6848,7 +6848,7 @@ packages: '@types/debug': 4.1.8 '@xmldom/xmldom': 0.8.10 debug: 4.3.4 - headers-polyfill: 3.1.2 + headers-polyfill: 3.2.3 outvariant: 1.4.0 strict-event-emitter: 0.2.8 web-encoding: 1.1.5 @@ -8336,11 +8336,11 @@ packages: /@types/lodash.curry@4.1.6: resolution: {integrity: sha512-x3ctCcmOYqRrihNNnQJW6fe/yZFCgnrIa6p80AiPQRO8Jis29bBdy1dEw1FwngoF/mCZa3Bx+33fUZvOEE635Q==} dependencies: - '@types/lodash': 4.14.149 + '@types/lodash': 4.14.198 dev: true - /@types/lodash@4.14.149: - resolution: {integrity: sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==} + /@types/lodash@4.14.198: + resolution: {integrity: sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==} dev: true /@types/long@4.0.1: @@ -8463,8 +8463,8 @@ packages: '@types/scheduler': 0.16.2 csstype: 3.0.10 - /@types/relay-runtime@13.0.0: - resolution: {integrity: sha512-yzv6F8EZPWA2rtfFP2qMluS8tsz1q4lfdYxLegCshdAjX5uqxTR2pAliATj9wrzD6OMZF4fl9aU+Y+zmSfm2EA==} + /@types/relay-runtime@14.1.13: + resolution: {integrity: sha512-NODqEnGjERJr02M0YQclUnXWCldmerNUkpFfuO317h/od1uXuwAW5131vpeiROE11BizPC/Qhup5VrwKsENazw==} dev: true /@types/resolve@1.17.1: @@ -8621,7 +8621,7 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin@6.1.0(@typescript-eslint/parser@6.1.0)(eslint@7.24.0)(typescript@5.1.3): + /@typescript-eslint/eslint-plugin@6.1.0(@typescript-eslint/parser@6.1.0)(eslint@7.24.0)(typescript@5.2.2): resolution: {integrity: sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8633,10 +8633,10 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 6.1.0(eslint@7.24.0)(typescript@5.1.3) + '@typescript-eslint/parser': 6.1.0(eslint@7.24.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 6.1.0 - '@typescript-eslint/type-utils': 6.1.0(eslint@7.24.0)(typescript@5.1.3) - '@typescript-eslint/utils': 6.1.0(eslint@7.24.0)(typescript@5.1.3) + '@typescript-eslint/type-utils': 6.1.0(eslint@7.24.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.1.0(eslint@7.24.0)(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.1.0 debug: 4.3.4 eslint: 7.24.0 @@ -8645,13 +8645,13 @@ packages: natural-compare: 1.4.0 natural-compare-lite: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@5.1.3) - typescript: 5.1.3 + ts-api-utils: 1.0.1(typescript@5.2.2) + typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/experimental-utils@4.29.1(eslint@7.24.0)(typescript@5.1.3): + /@typescript-eslint/experimental-utils@4.29.1(eslint@7.24.0)(typescript@5.2.2): resolution: {integrity: sha512-kl6QG6qpzZthfd2bzPNSJB2YcZpNOrP6r9jueXupcZHnL74WiuSjaft7WSu17J9+ae9zTlk0KJMXPUj0daBxMw==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -8660,7 +8660,7 @@ packages: '@types/json-schema': 7.0.12 '@typescript-eslint/scope-manager': 4.29.1 '@typescript-eslint/types': 4.29.1 - '@typescript-eslint/typescript-estree': 4.29.1(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 4.29.1(typescript@5.2.2) eslint: 7.24.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0(eslint@7.24.0) @@ -8669,7 +8669,7 @@ packages: - typescript dev: true - /@typescript-eslint/parser@6.1.0(eslint@7.24.0)(typescript@5.1.3): + /@typescript-eslint/parser@6.1.0(eslint@7.24.0)(typescript@5.2.2): resolution: {integrity: sha512-hIzCPvX4vDs4qL07SYzyomamcs2/tQYXg5DtdAfj35AyJ5PIUqhsLf4YrEIFzZcND7R2E8tpQIZKayxg8/6Wbw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8681,11 +8681,11 @@ packages: dependencies: '@typescript-eslint/scope-manager': 6.1.0 '@typescript-eslint/types': 6.1.0 - '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.1.0 debug: 4.3.4 eslint: 7.24.0 - typescript: 5.1.3 + typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true @@ -8726,7 +8726,7 @@ packages: '@typescript-eslint/types': 6.1.0 '@typescript-eslint/visitor-keys': 6.1.0 - /@typescript-eslint/type-utils@6.1.0(eslint@7.24.0)(typescript@5.1.3): + /@typescript-eslint/type-utils@6.1.0(eslint@7.24.0)(typescript@5.2.2): resolution: {integrity: sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8736,12 +8736,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.1.3) - '@typescript-eslint/utils': 6.1.0(eslint@7.24.0)(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.1.0(eslint@7.24.0)(typescript@5.2.2) debug: 4.3.4 eslint: 7.24.0 - ts-api-utils: 1.0.1(typescript@5.1.3) - typescript: 5.1.3 + ts-api-utils: 1.0.1(typescript@5.2.2) + typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true @@ -8755,7 +8755,7 @@ packages: resolution: {integrity: sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ==} engines: {node: ^16.0.0 || >=18.0.0} - /@typescript-eslint/typescript-estree@4.29.1(typescript@5.1.3): + /@typescript-eslint/typescript-estree@4.29.1(typescript@5.2.2): resolution: {integrity: sha512-lIkkrR9E4lwZkzPiRDNq0xdC3f2iVCUjw/7WPJ4S2Sl6C3nRWkeE1YXCQ0+KsiaQRbpY16jNaokdWnm9aUIsfw==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -8770,8 +8770,8 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.7 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 + tsutils: 3.21.0(typescript@5.2.2) + typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true @@ -8797,7 +8797,7 @@ packages: - supports-color dev: false - /@typescript-eslint/typescript-estree@6.1.0(typescript@5.1.3): + /@typescript-eslint/typescript-estree@6.1.0(typescript@5.2.2): resolution: {integrity: sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8812,13 +8812,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@5.1.3) - typescript: 5.1.3 + ts-api-utils: 1.0.1(typescript@5.2.2) + typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.1.0(eslint@7.24.0)(typescript@5.1.3): + /@typescript-eslint/utils@6.1.0(eslint@7.24.0)(typescript@5.2.2): resolution: {integrity: sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8829,7 +8829,7 @@ packages: '@types/semver': 7.5.0 '@typescript-eslint/scope-manager': 6.1.0 '@typescript-eslint/types': 6.1.0 - '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.1.3) + '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.2.2) eslint: 7.24.0 semver: 7.5.4 transitivePeerDependencies: @@ -10710,14 +10710,6 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@4.1.1: - resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -12179,7 +12171,7 @@ packages: supports-color: optional: true dependencies: - ms: 2.1.2 + ms: 2.1.3 /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} @@ -13132,7 +13124,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.1.0(eslint@7.24.0)(typescript@5.1.3) + '@typescript-eslint/parser': 6.1.0(eslint@7.24.0)(typescript@5.2.2) debug: 3.2.7 eslint-import-resolver-node: 0.3.6 find-up: 2.1.0 @@ -13191,7 +13183,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.1.0(eslint@7.24.0)(typescript@5.1.3) + '@typescript-eslint/parser': 6.1.0(eslint@7.24.0)(typescript@5.2.2) array-includes: 3.1.4 array.prototype.flat: 1.2.5 contains-path: 0.1.0 @@ -13247,7 +13239,7 @@ packages: - supports-color dev: false - /eslint-plugin-jest@24.3.5(@typescript-eslint/eslint-plugin@6.1.0)(eslint@7.24.0)(typescript@5.1.3): + /eslint-plugin-jest@24.3.5(@typescript-eslint/eslint-plugin@6.1.0)(eslint@7.24.0)(typescript@5.2.2): resolution: {integrity: sha512-XG4rtxYDuJykuqhsOqokYIR84/C8pRihRtEpVskYLbIIKGwPNW2ySxdctuVzETZE+MbF/e7wmsnbNVpzM0rDug==} engines: {node: '>=10'} peerDependencies: @@ -13257,8 +13249,8 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.1.0(@typescript-eslint/parser@6.1.0)(eslint@7.24.0)(typescript@5.1.3) - '@typescript-eslint/experimental-utils': 4.29.1(eslint@7.24.0)(typescript@5.1.3) + '@typescript-eslint/eslint-plugin': 6.1.0(@typescript-eslint/parser@6.1.0)(eslint@7.24.0)(typescript@5.2.2) + '@typescript-eslint/experimental-utils': 4.29.1(eslint@7.24.0)(typescript@5.2.2) eslint: 7.24.0 transitivePeerDependencies: - supports-color @@ -15331,6 +15323,10 @@ packages: resolution: {integrity: sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA==} dev: true + /headers-polyfill@3.2.3: + resolution: {integrity: sha512-oj6MO8sdFQ9gQQedSVdMGh96suxTNp91vPQu7C4qx/57FqYsA5TiNr92nhIZwVQq8zygn4nu3xS1aEqpakGqdw==} + dev: true + /hex-color-regex@1.1.0: resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==} dev: true @@ -19487,13 +19483,13 @@ packages: isarray: 1.0.0 dev: true - /msw@1.2.2(typescript@5.1.3): - resolution: {integrity: sha512-GsW3PE/Es/a1tYThXcM8YHOZ1S1MtivcS3He/LQbbTCx3rbWJYCtWD5XXyJ53KlNPT7O1VI9sCW3xMtgFe8XpQ==} + /msw@1.3.0(typescript@5.2.2): + resolution: {integrity: sha512-nnWAZlQyQOKeYRblCpseT1kSPt1aF5e/jHz1hn/18IxbsMFreSVV1cJriT0uV+YG6+wvwFRMHXU3zVuMvuwERQ==} engines: {node: '>=14'} hasBin: true requiresBuild: true peerDependencies: - typescript: '>= 4.4.x <= 5.1.x' + typescript: '>= 4.4.x <= 5.2.x' peerDependenciesMeta: typescript: optional: true @@ -19503,11 +19499,11 @@ packages: '@open-draft/until': 1.0.3 '@types/cookie': 0.4.1 '@types/js-levenshtein': 1.1.1 - chalk: 4.1.1 + chalk: 4.1.2 chokidar: 3.5.3 cookie: 0.4.2 graphql: 16.7.1 - headers-polyfill: 3.1.2 + headers-polyfill: 3.2.3 inquirer: 8.2.0 is-node-process: 1.2.0 js-levenshtein: 1.1.6 @@ -19516,7 +19512,7 @@ packages: path-to-regexp: 6.2.1 strict-event-emitter: 0.4.6 type-fest: 2.19.0 - typescript: 5.1.3 + typescript: 5.2.2 yargs: 17.5.1 transitivePeerDependencies: - encoding @@ -25709,13 +25705,13 @@ packages: typescript: 4.8.2 dev: false - /ts-api-utils@1.0.1(typescript@5.1.3): + /ts-api-utils@1.0.1(typescript@5.2.2): resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.1.3 + typescript: 5.2.2 dev: true /tsconfig-paths@3.14.1: @@ -25736,7 +25732,7 @@ packages: strip-bom: 3.0.0 dev: false - /tsec@0.2.1(@bazel/bazelisk@1.12.1)(typescript@5.1.3): + /tsec@0.2.1(@bazel/bazelisk@1.12.1)(typescript@5.2.2): resolution: {integrity: sha512-RP9vhbRbRI9VH4CfOlQvo5W9HdfiPKq0gdiUOWI5oKmLaZKNFN8CsPwBfT5ySmhnKNwmmAS/BtY3WoTfABwwig==} hasBin: true peerDependencies: @@ -25746,7 +25742,7 @@ packages: '@bazel/bazelisk': 1.12.1 glob: 7.2.0 minimatch: 3.1.2 - typescript: 5.1.3 + typescript: 5.2.2 dev: true /tslib@1.11.1: @@ -25766,14 +25762,14 @@ packages: /tslib@2.5.3: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} - /tsutils@3.21.0(typescript@5.1.3): + /tsutils@3.21.0(typescript@5.2.2): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.11.1 - typescript: 5.1.3 + typescript: 5.2.2 dev: true /tty-browserify@0.0.1: @@ -25976,8 +25972,8 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - /typescript@5.1.3: - resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==} + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true dev: true diff --git a/test/integration/tsconfig-verifier/test/index.test.js b/test/integration/tsconfig-verifier/test/index.test.js index fdb648b46ee1..5102737198a8 100644 --- a/test/integration/tsconfig-verifier/test/index.test.js +++ b/test/integration/tsconfig-verifier/test/index.test.js @@ -298,7 +298,7 @@ describe('tsconfig.json verifier', () => { await writeFile( tsConfig, - `{ "compilerOptions": { "esModuleInterop": false, "moduleResolution": "node16" } }` + `{ "compilerOptions": { "esModuleInterop": false, "moduleResolution": "node16", "module": "node16" } }` ) await new Promise((resolve) => setTimeout(resolve, 500)) const { code, stderr, stdout } = await nextBuild(appDir, undefined, { @@ -313,6 +313,7 @@ describe('tsconfig.json verifier', () => { \\"compilerOptions\\": { \\"esModuleInterop\\": true, \\"moduleResolution\\": \\"node16\\", + \\"module\\": \\"node16\\", \\"lib\\": [ \\"dom\\", \\"dom.iterable\\", @@ -323,7 +324,6 @@ describe('tsconfig.json verifier', () => { \\"strict\\": false, \\"noEmit\\": true, \\"incremental\\": true, - \\"module\\": \\"esnext\\", \\"resolveJsonModule\\": true, \\"isolatedModules\\": true, \\"jsx\\": \\"preserve\\", From d330f7b02c2151cf2d6e76c5ae6852d96fd43763 Mon Sep 17 00:00:00 2001 From: Zack Tanner Date: Thu, 7 Sep 2023 13:53:07 -0700 Subject: [PATCH 09/11] fix: ensure mpa navigation render side effects are only fired once (#55032) This is to fix an issue where these redirect side effects can be fired multiple times when the router reducer state changes. This block is still run when the router state updates, which can lead to superfluous attempts to redirect to a page. With these changes, we keep track of the page that is being redirected to. If a re-render occurs while that request is in flight, we don't trigger the side effects. [Slack x-ref](https://vercel.slack.com/archives/C04DUD7EB1B/p1694049914264839) --- .../next/src/client/components/app-router.tsx | 33 +++++++++------- .../reducers/navigate-reducer.test.tsx | 23 ++++++----- .../reducers/refresh-reducer.test.tsx | 12 ++++-- .../reducers/server-patch-reducer.test.tsx | 12 ++++-- .../router-reducer/router-reducer-types.ts | 6 ++- .../navigation/app/mpa-nav-test/page.js | 38 +++++++++++++++++++ .../e2e/app-dir/navigation/navigation.test.ts | 31 ++++++++++++++- .../e2e/app-dir/navigation/pages/slow-page.js | 13 +++++++ 8 files changed, 134 insertions(+), 34 deletions(-) create mode 100644 test/e2e/app-dir/navigation/app/mpa-nav-test/page.js create mode 100644 test/e2e/app-dir/navigation/pages/slow-page.js diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx index 2c3c541e02b8..a8de967825e1 100644 --- a/packages/next/src/client/components/app-router.tsx +++ b/packages/next/src/client/components/app-router.tsx @@ -33,12 +33,12 @@ import { ACTION_RESTORE, ACTION_SERVER_ACTION, ACTION_SERVER_PATCH, + Mutable, PrefetchKind, ReducerActions, RouterChangeByServerResponse, RouterNavigate, ServerActionDispatcher, - ServerActionMutable, } from './router-reducer/router-reducer-types' import { createHrefFromUrl } from './router-reducer/create-href-from-url' import { @@ -73,7 +73,7 @@ export function getServerActionDispatcher() { return globalServerActionDispatcher } -let globalServerActionMutable: ServerActionMutable['globalMutable'] = { +let globalMutable: Mutable['globalMutable'] = { refresh: () => {}, // noop until the router is initialized } @@ -145,7 +145,7 @@ function useServerActionDispatcher(dispatch: React.Dispatch) { dispatch({ ...actionPayload, type: ACTION_SERVER_ACTION, - mutable: { globalMutable: globalServerActionMutable }, + mutable: { globalMutable }, cache: createEmptyCacheNode(), }) }) @@ -174,7 +174,7 @@ function useChangeByServerResponse( previousTree, overrideCanonicalUrl, cache: createEmptyCacheNode(), - mutable: {}, + mutable: { globalMutable }, }) }) }, @@ -186,7 +186,7 @@ function useNavigate(dispatch: React.Dispatch): RouterNavigate { return useCallback( (href, navigateType, forceOptimisticNavigation, shouldScroll) => { const url = new URL(addBasePath(href), location.href) - globalServerActionMutable.pendingNavigatePath = href + globalMutable.pendingNavigatePath = href return dispatch({ type: ACTION_NAVIGATE, @@ -197,7 +197,7 @@ function useNavigate(dispatch: React.Dispatch): RouterNavigate { shouldScroll: shouldScroll ?? true, navigateType, cache: createEmptyCacheNode(), - mutable: {}, + mutable: { globalMutable }, }) }, [dispatch] @@ -322,7 +322,7 @@ function Router({ dispatch({ type: ACTION_REFRESH, cache: createEmptyCacheNode(), - mutable: {}, + mutable: { globalMutable }, origin: window.location.origin, }) }) @@ -338,7 +338,7 @@ function Router({ dispatch({ type: ACTION_FAST_REFRESH, cache: createEmptyCacheNode(), - mutable: {}, + mutable: { globalMutable }, origin: window.location.origin, }) }) @@ -357,7 +357,7 @@ function Router({ }, [appRouter]) useEffect(() => { - globalServerActionMutable.refresh = appRouter.refresh + globalMutable.refresh = appRouter.refresh }, [appRouter.refresh]) if (process.env.NODE_ENV !== 'production') { @@ -409,11 +409,16 @@ function Router({ // in . At least I hope so. (It will run twice in dev strict mode, // but that's... fine?) if (pushRef.mpaNavigation) { - const location = window.location - if (pushRef.pendingPush) { - location.assign(canonicalUrl) - } else { - location.replace(canonicalUrl) + // if there's a re-render, we don't want to trigger another redirect if one is already in flight to the same URL + if (globalMutable.pendingMpaPath !== canonicalUrl) { + const location = window.location + if (pushRef.pendingPush) { + location.assign(canonicalUrl) + } else { + location.replace(canonicalUrl) + } + + globalMutable.pendingMpaPath = canonicalUrl } // TODO-APP: Should we listen to navigateerror here to catch failed // navigations somehow? And should we call window.stop() if a SPA navigation 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 4ed01fed08c8..fcde9b963e9f 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 @@ -106,6 +106,10 @@ const getInitialRouterStateTree = (): FlightRouterState => [ true, ] +const globalMutable = { + refresh: () => {}, +} + async function runPromiseThrowChain(fn: any): Promise { try { return await fn() @@ -194,7 +198,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } const newState = await runPromiseThrowChain(() => @@ -438,7 +442,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -633,7 +637,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -792,7 +796,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -948,7 +952,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -1147,7 +1151,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -1317,7 +1321,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -1630,7 +1634,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => navigateReducer(state, action)) @@ -1841,6 +1845,7 @@ describe('navigateReducer', () => { hashFragment: '#hash', pendingPush: true, shouldScroll: true, + globalMutable, }, } @@ -1983,7 +1988,7 @@ describe('navigateReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } const newState = await runPromiseThrowChain(() => 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 9ed54e8994d2..bbf36b1ec538 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 @@ -66,6 +66,10 @@ const getInitialRouterStateTree = (): FlightRouterState => [ true, ] +const globalMutable = { + refresh: () => {}, +} + async function runPromiseThrowChain(fn: any): Promise { try { return await fn() @@ -139,7 +143,7 @@ describe('refreshReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, origin: new URL('/linking', 'https://localhost').origin, } @@ -300,7 +304,7 @@ describe('refreshReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, origin: new URL('/linking', 'https://localhost').origin, } @@ -487,7 +491,7 @@ describe('refreshReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, origin: new URL('/linking', 'https://localhost').origin, } @@ -723,7 +727,7 @@ describe('refreshReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, origin: new URL('/linking', 'https://localhost').origin, } 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 0540c02079cb..9e7035dc7e81 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 @@ -7,6 +7,10 @@ import type { const buildId = 'development' +const globalMutable = { + refresh: () => {}, +} + jest.mock('../fetch-server-response', () => { const flightData: FlightData = [ [ @@ -184,7 +188,7 @@ describe('serverPatchReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } const newState = await runPromiseThrowChain(() => @@ -375,7 +379,7 @@ describe('serverPatchReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } await runPromiseThrowChain(() => serverPatchReducer(state, action)) @@ -514,7 +518,7 @@ describe('serverPatchReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } const state = createInitialRouterState({ @@ -556,7 +560,7 @@ describe('serverPatchReducer', () => { subTreeData: null, parallelRoutes: new Map(), }, - mutable: {}, + mutable: { globalMutable }, } const newState = await runPromiseThrowChain(() => 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 defbb657c7c4..d9c641c4045b 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 @@ -38,11 +38,15 @@ export interface Mutable { prefetchCache?: AppRouterState['prefetchCache'] hashFragment?: string shouldScroll?: boolean + globalMutable: { + pendingNavigatePath?: string + pendingMpaPath?: string + refresh: () => void + } } export interface ServerActionMutable extends Mutable { inFlightServerAction?: Promise | null - globalMutable: { pendingNavigatePath?: string; refresh: () => void } actionResultResolved?: boolean } diff --git a/test/e2e/app-dir/navigation/app/mpa-nav-test/page.js b/test/e2e/app-dir/navigation/app/mpa-nav-test/page.js new file mode 100644 index 000000000000..9a335e2d0995 --- /dev/null +++ b/test/e2e/app-dir/navigation/app/mpa-nav-test/page.js @@ -0,0 +1,38 @@ +'use client' +import Link from 'next/link' +import { useEffect, useRef } from 'react' + +export default function Page() { + const prefetchRef = useRef() + const slowPageRef = useRef() + + useEffect(() => { + function triggerPrefetch() { + const event = new MouseEvent('mouseover', { + view: window, + bubbles: true, + cancelable: true, + }) + + prefetchRef.current.dispatchEvent(event) + console.log('dispatched') + } + + slowPageRef.current.click() + + setInterval(() => { + triggerPrefetch() + }, 1000) + }, []) + + return ( + <> + + To /slow-page + + + Prefetch link + + + ) +} diff --git a/test/e2e/app-dir/navigation/navigation.test.ts b/test/e2e/app-dir/navigation/navigation.test.ts index 67beabf483fe..b3e1cf19c426 100644 --- a/test/e2e/app-dir/navigation/navigation.test.ts +++ b/test/e2e/app-dir/navigation/navigation.test.ts @@ -1,5 +1,5 @@ import { createNextDescribe } from 'e2e-utils' -import { check } from 'next-test-utils' +import { check, waitFor } from 'next-test-utils' import type { Request } from 'playwright-chromium' createNextDescribe( @@ -497,6 +497,33 @@ createNextDescribe( .waitForElementByCss('#link-to-app') expect(await browser.url()).toBe(next.url + '/some') }) + + if (!isNextDev) { + // this test is pretty hard to test in playwright, so most of the heavy lifting is in the page component itself + // it triggers a hover on a link to initiate a prefetch request every second, and so we check that + // it doesn't repeatedly initiate the mpa navigation request + it('should not continously initiate a mpa navigation to the same URL when router state changes', async () => { + let requestCount = 0 + const browser = await next.browser('/mpa-nav-test', { + beforePageLoad(page) { + page.on('request', (request) => { + const url = new URL(request.url()) + // skip rsc prefetches + if (url.pathname === '/slow-page' && !url.search) { + requestCount++ + } + }) + }, + }) + + await browser.waitForElementByCss('#link-to-slow-page') + + // wait a few seconds since prefetches are triggered in 1s intervals in the page component + await waitFor(5000) + + expect(requestCount).toBe(1) + }) + } }) describe('nested navigation', () => { @@ -562,7 +589,7 @@ createNextDescribe( ) }) - it('should emit refresh meta tag (peramnent) for redirect page when streaming', async () => { + it('should emit refresh meta tag (permanent) for redirect page when streaming', async () => { const html = await next.render('/redirect/suspense-2') expect(html).toContain( '' diff --git a/test/e2e/app-dir/navigation/pages/slow-page.js b/test/e2e/app-dir/navigation/pages/slow-page.js new file mode 100644 index 000000000000..ad37357cb6dc --- /dev/null +++ b/test/e2e/app-dir/navigation/pages/slow-page.js @@ -0,0 +1,13 @@ +export default function Page() { + return 'Hello from slow page' +} + +export async function getServerSideProps({ resolvedUrl }) { + if (!resolvedUrl.includes('?_rsc')) { + // only stall on the navigation, not prefetch + await new Promise((resolve) => setTimeout(resolve, 100000)) + } + return { + props: {}, + } +} From 7267538e0019e8b0ab49c959272af86e54777a14 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 7 Sep 2023 14:07:53 -0700 Subject: [PATCH 10/11] Revert "perf: add bundled rendering runtimes (#52997)" (#55117) This reverts commit a5b7c77c1ff0096af7609a8bfc1e064d30db4e30. Our E2E tests are failing with this change this reverts to allow investigating async x-ref: https://github.com/vercel/next.js/actions/runs/6112149126/job/16589769954 --- .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.external.ts => action-async-storage.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.external.ts => request-async-storage.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 +- ....ts => static-generation-async-storage.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.shared-runtime.ts => amp-context.ts} | 0 packages/next/src/shared/lib/amp.ts | 2 +- ...hared-runtime.ts => app-router-context.ts} | 0 packages/next/src/shared/lib/dynamic.tsx | 2 +- ...red-runtime.ts => head-manager-context.ts} | 0 packages/next/src/shared/lib/head.tsx | 4 +- ...red-runtime.ts => hooks-client-context.ts} | 0 ...text.shared-runtime.ts => html-context.ts} | 0 ...red-runtime.ts => image-config-context.ts} | 0 ....shared-runtime.ts => loadable-context.ts} | 0 ...adable.shared-runtime.tsx => loadable.tsx} | 2 +- ...xt.shared-runtime.ts => router-context.ts} | 0 .../src/shared/lib/router/adapters.test.tsx | 2 +- ...apters.shared-runtime.tsx => adapters.tsx} | 7 +- ...ig.shared-runtime.ts => runtime-config.ts} | 0 ...d-runtime.tsx => server-inserted-html.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, 949 insertions(+), 1504 deletions(-) delete mode 100644 bench/basic-app/app/api/app/route.js delete mode 100644 bench/basic-app/app/layout.js delete mode 100644 bench/basic-app/app/page.js delete mode 100644 bench/basic-app/next.config.js delete mode 100644 bench/basic-app/pages/api/index.js delete mode 100644 bench/basic-app/pages/pages/index.js delete 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 delete 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 delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt delete 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 delete 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.external.ts => action-async-storage.ts} (100%) rename packages/next/src/client/components/{request-async-storage.external.ts => request-async-storage.ts} (100%) rename packages/next/src/client/components/{static-generation-async-storage.external.ts => static-generation-async-storage.ts} (100%) delete mode 100644 packages/next/src/server/future/route-modules/app-page/module.compiled.ts delete mode 100644 packages/next/src/server/future/route-modules/app-page/shared-modules.ts delete mode 100644 packages/next/src/server/future/route-modules/app-route/module.compiled.ts delete mode 100644 packages/next/src/server/future/route-modules/app-route/shared-modules.ts delete mode 100644 packages/next/src/server/future/route-modules/pages-api/module.compiled.ts delete mode 100644 packages/next/src/server/future/route-modules/pages/module.compiled.ts delete mode 100644 packages/next/src/server/future/route-modules/pages/shared-modules.ts delete mode 100644 packages/next/src/server/load-default-error-components.ts rename packages/next/src/shared/lib/{amp-context.shared-runtime.ts => amp-context.ts} (100%) rename packages/next/src/shared/lib/{app-router-context.shared-runtime.ts => app-router-context.ts} (100%) rename packages/next/src/shared/lib/{head-manager-context.shared-runtime.ts => head-manager-context.ts} (100%) rename packages/next/src/shared/lib/{hooks-client-context.shared-runtime.ts => hooks-client-context.ts} (100%) rename packages/next/src/shared/lib/{html-context.shared-runtime.ts => html-context.ts} (100%) rename packages/next/src/shared/lib/{image-config-context.shared-runtime.ts => image-config-context.ts} (100%) rename packages/next/src/shared/lib/{loadable-context.shared-runtime.ts => loadable-context.ts} (100%) rename packages/next/src/shared/lib/{loadable.shared-runtime.tsx => loadable.tsx} (99%) rename packages/next/src/shared/lib/{router-context.shared-runtime.ts => router-context.ts} (100%) rename packages/next/src/shared/lib/router/{adapters.shared-runtime.tsx => adapters.tsx} (95%) rename packages/next/src/shared/lib/{runtime-config.shared-runtime.ts => runtime-config.ts} (100%) rename packages/next/src/shared/lib/{server-inserted-html.shared-runtime.tsx => server-inserted-html.tsx} (100%) delete mode 100644 packages/next/taskfile-webpack.js delete mode 100644 packages/next/webpack.config.js create mode 100644 test/integration/externalize-next-server/app/node_modules/comps/index.js create mode 100644 test/integration/externalize-next-server/app/node_modules/comps/package.json create mode 100644 test/integration/externalize-next-server/app/package.json create mode 100644 test/integration/externalize-next-server/app/pages/index.js create mode 100644 test/integration/externalize-next-server/test/index.test.js diff --git a/.eslintrc.json b/.eslintrc.json index feb49b2ecbbf..8c46d8c02736 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,10 +162,7 @@ }, { "files": ["packages/**"], - "excludedFiles": [ - "packages/next/taskfile*.js", - "packages/next/webpack.config.js" - ], + "excludedFiles": ["packages/next/taskfile.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 deleted file mode 100644 index 944ba5a8e827..000000000000 --- a/bench/basic-app/app/api/app/route.js +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 8ebf54889577..000000000000 --- a/bench/basic-app/app/layout.js +++ /dev/null @@ -1,12 +0,0 @@ -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 deleted file mode 100644 index 83dc3aa56c9a..000000000000 --- a/bench/basic-app/app/page.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 0957c472383f..000000000000 --- a/bench/basic-app/next.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - experimental: { - serverMinification: true, - }, -} diff --git a/bench/basic-app/pages/api/index.js b/bench/basic-app/pages/api/index.js deleted file mode 100644 index 8f603094bd28..000000000000 --- a/bench/basic-app/pages/api/index.js +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index e06229eee063..000000000000 --- a/bench/basic-app/pages/pages/index.js +++ /dev/null @@ -1,7 +0,0 @@ -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 38f27b3c1c7a..fdec9ffc360f 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,8 +2,6 @@ // 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 25437fec08ef..951f91570d92 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,15 +3,13 @@ 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' @@ -25,10 +23,6 @@ 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 c4e1bce96aa5..abdf23d0fd7d 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.shared-runtime' +import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context' 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 cecafb35f77a..6182b2d7d453 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,12 +3,11 @@ 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 type { RenderOpts } from 'next/dist/server/render' +import { renderToHTML, 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' @@ -22,9 +21,6 @@ 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 4265f52139b4..cf35f3795f88 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::formatdoc; +use indoc::indoc; use serde_json::Value as JsonValue; use turbo_tasks::Vc; use turbopack_binding::{ @@ -964,18 +964,13 @@ impl AppRenderer { .emit(); } - let mut result = RopeBuilder::from( - formatdoc!( - " - \"TURBOPACK {{ chunking-type: isolatedParallel; transition: {rsc_transition} }}\"; + let mut result = RopeBuilder::from(indoc! {" + \"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\"; import GlobalErrorMod from \"next/dist/client/components/error-boundary\" - const {{ GlobalError }} = GlobalErrorMod; - \"TURBOPACK {{ chunking-type: isolatedParallel; transition: {rsc_transition} }}\"; + const { GlobalError } = GlobalErrorMod; + \"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\"; 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 cfe9eebf19f9..54c7dd3331a6 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,9 +96,10 @@ pub async fn get_edge_resolve_options_context( ]; match ty { - ServerContextType::AppRSC { .. } => custom_conditions.push("react-server".to_string()), - ServerContextType::AppRoute { .. } - | ServerContextType::Pages { .. } + ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { + custom_conditions.push("react-server".to_string()) + } + 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 05326de25520..de0bd2f2e7bf 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,26 +58,24 @@ impl Transition for NextEdgeRouteTransition { #[turbo_tasks::function] async fn process_module( - self: Vc, + &self, asset: Vc>, context: Vc, ) -> Result>> { - let new_context = self.process_context(context); - let this = self.await?; let new_asset = route_bootstrap( asset, - Vc::upcast(new_context), - this.base_path, - this.bootstrap_asset, + Vc::upcast(context), + self.base_path, + self.bootstrap_asset, Vc::cell(indexmap! { - "NAME".to_string() => this.entry_name.clone(), + "NAME".to_string() => self.entry_name.clone(), }), ); let asset = ChunkGroupFilesAsset { module: Vc::upcast(new_asset), - client_root: this.output_path, - chunking_context: this.edge_chunking_context, + client_root: self.output_path, + chunking_context: self.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 c530f3bcb929..e05ac926c139 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,26 +216,25 @@ 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: Vc = ImportMapping::External(None).cell(); + let external = 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_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); + import_map.insert_exact_alias("react-server-dom-webpack/", external); } ServerContextType::AppSSR { .. } | ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { match mode { - NextMode::Build => { + NextMode::Development | NextMode::Build => { import_map.insert_wildcard_alias("next/dist/server/", external); import_map.insert_wildcard_alias("next/dist/shared/", external); } @@ -243,7 +242,6 @@ 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", @@ -379,11 +377,6 @@ 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( @@ -420,7 +413,12 @@ 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 }) => { + ( + NextMode::DevServer, + ServerContextType::AppSSR { app_dir } + | ServerContextType::AppRSC { app_dir, .. } + | ServerContextType::AppRoute { app_dir }, + ) => { import_map.insert_exact_alias( "@opentelemetry/api", // TODO(WEB-625) this actually need to prefer the local version of @@ -429,40 +427,28 @@ async fn insert_next_server_special_aliases( ); import_map.insert_exact_alias( "react", - passthrough_external_if_node(app_dir, "next/dist/compiled/react"), + request_to_import_mapping(app_dir, "next/dist/compiled/react"), ); import_map.insert_wildcard_alias( "react/", - passthrough_external_if_node(app_dir, "next/dist/compiled/react/*"), + request_to_import_mapping(app_dir, "next/dist/compiled/react/*"), ); import_map.insert_exact_alias( "react-dom", - passthrough_external_if_node( + request_to_import_mapping( app_dir, "next/dist/compiled/react-dom/server-rendering-stub.js", ), ); import_map.insert_wildcard_alias( "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/*"), + request_to_import_mapping(app_dir, "next/dist/compiled/react-dom/*"), ); import_map.insert_wildcard_alias( "react-server-dom-webpack/", - passthrough_external_if_node( - app_dir, - "next/dist/compiled/react-server-dom-webpack/*", - ), + request_to_import_mapping(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: // @@ -474,7 +460,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::DevServer, + NextMode::Build | NextMode::Development, ServerContextType::AppRSC { app_dir, .. } | ServerContextType::AppRoute { app_dir }, ) => { import_map.insert_exact_alias( @@ -483,20 +469,10 @@ async fn insert_next_server_special_aliases( // @opentelemetry/api request_to_import_mapping(app_dir, "next/dist/compiled/@opentelemetry/api"), ); - 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", + request_to_import_mapping(app_dir, "next/dist/compiled/react/react.shared-subset"), + ); 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 1d242498b36d..e7485ff100ad 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,10 +46,7 @@ use crate::{ next_import_map::{get_next_server_import_map, mdx_import_source_file}, next_server::resolve::ExternalPredicate, next_shared::{ - resolve::{ - ModuleFeatureReportResolvePlugin, NextExternalResolvePlugin, - UnsupportedModulesResolvePlugin, - }, + resolve::{ModuleFeatureReportResolvePlugin, UnsupportedModulesResolvePlugin}, transforms::{ emotion::get_emotion_transform_plugin, get_relay_transform_plugin, styled_components::get_styled_components_transform_plugin, @@ -111,9 +108,10 @@ 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 { .. } => custom_conditions.push("react-server".to_string()), - ServerContextType::AppRoute { .. } - | ServerContextType::Pages { .. } + ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { + custom_conditions.push("react-server".to_string()) + } + ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } | ServerContextType::AppSSR { .. } | ServerContextType::Middleware { .. } => {} @@ -123,15 +121,12 @@ 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 { .. } @@ -142,7 +137,6 @@ 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 ea7f044c12cc..013f87f9fd5b 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}, - ResolveResult, ResolveResultItem, ResolveResultOption, + ResolveResultOption, }, }, }; @@ -102,55 +102,6 @@ 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 deleted file mode 100644 index a5ad94c85fb0..000000000000 --- 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 +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index c80db085946c..000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 39e97b4ccc85..000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt +++ /dev/null @@ -1,47 +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 - 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 new file mode 100644 index 000000000000..ce775cb8df7e --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt @@ -0,0 +1,6 @@ +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 108b763da879..e2c2f40a06dd 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: 'default', + reactServer: 'react-server', }) }) @@ -129,7 +129,7 @@ function runTests() { expect(json).toMatchObject({ edgeThenNode: 'edge', nodeThenEdge: 'edge', - reactServer: 'default', + reactServer: 'react-server', }) }) 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 new file mode 100644 index 000000000000..72c048d7b648 --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt @@ -0,0 +1,13 @@ +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 new file mode 100644 index 000000000000..72c048d7b648 --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt @@ -0,0 +1,13 @@ +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 deleted file mode 100644 index a5ad94c85fb0..000000000000 --- 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 +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index c80db085946c..000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt +++ /dev/null @@ -1,15 +0,0 @@ -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 2da1ee3c4029..78fe148a8dc9 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.shared-runtime' -export * from './dist/shared/lib/runtime-config.shared-runtime' +import getConfig from './dist/shared/lib/runtime-config' +export * from './dist/shared/lib/runtime-config' export default getConfig diff --git a/packages/next/config.js b/packages/next/config.js index 651074863809..2da980d8b006 100644 --- a/packages/next/config.js +++ b/packages/next/config.js @@ -1 +1 @@ -module.exports = require('./dist/shared/lib/runtime-config.shared-runtime') +module.exports = require('./dist/shared/lib/runtime-config') diff --git a/packages/next/package.json b/packages/next/package.json index 30ee061047fa..28fbd949a897 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -83,7 +83,6 @@ }, "taskr": { "requires": [ - "./taskfile-webpack.js", "./taskfile-ncc.js", "./taskfile-swc.js", "./taskfile-watch.js" @@ -300,7 +299,6 @@ "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 b0e0870ca139..8ad7407b4be4 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -143,13 +143,8 @@ 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, - defaultOverrides, - experimentalOverrides, -} from '../server/require-hook' +import { baseOverrides, experimentalOverrides } from '../server/require-hook' import { initialize } from '../server/lib/incremental-cache-server' import { nodeFs } from '../server/lib/node-fs-methods' @@ -1248,7 +1243,6 @@ 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: @@ -2090,25 +2084,6 @@ 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 @@ -2134,7 +2109,10 @@ export default async function build( const minimalServerEntries = [ ...sharedEntriesSet, require.resolve( - 'next/dist/compiled/next-server/server.runtime.prod' + 'next/dist/compiled/minimal-next-server/next-server-cached.js' + ), + require.resolve( + 'next/dist/compiled/minimal-next-server/next-server.js' ), ].filter(Boolean) diff --git a/packages/next/src/build/templates/app-page.ts b/packages/next/src/build/templates/app-page.ts index f0d2ab692e2a..c75509904c3a 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.compiled' +import * as module from 'next/dist/server/future/route-modules/app-page/module' 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 b4b8e5b0fe6c..50a8b6165a74 100644 --- a/packages/next/src/build/templates/app-route.ts +++ b/packages/next/src/build/templates/app-route.ts @@ -1,8 +1,7 @@ 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.compiled' - +import * as module from 'next/dist/server/future/route-modules/app-route/module' 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 eaeec836cb61..a48822f9ed75 100644 --- a/packages/next/src/build/templates/pages-api.ts +++ b/packages/next/src/build/templates/pages-api.ts @@ -1,6 +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-api/module.compiled' - +import * as module from 'next/dist/server/future/route-modules/pages-api/module' 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 b5def5d13c55..3f3527e6650d 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.compiled' +import * as module from 'next/dist/server/future/route-modules/pages/module' 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 423beb27f6dc..867429c2ba90 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.external' +import type { StaticGenerationAsyncStorage } from '../client/components/static-generation-async-storage' import '../server/require-hook' import '../server/node-polyfill-fetch' @@ -65,9 +65,7 @@ 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' -const { AppRouteRouteModule } = - require('../server/future/route-modules/app-route/module.compiled') as typeof import('../server/future/route-modules/app-route/module') +import { AppRouteRouteModule } from '../server/future/route-modules/app-route/module' export type ROUTER_TYPE = 'pages' | 'app' @@ -1391,9 +1389,7 @@ export async function isPageStatic({ const isPageStaticSpan = trace('is-page-static-utils', parentId) return isPageStaticSpan .traceAsyncFn(async () => { - require('../shared/lib/runtime-config.shared-runtime').setConfig( - runtimeEnvConfig - ) + require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) setHttpClientAndAgentOptions({ httpAgentOptions, }) @@ -1677,9 +1673,7 @@ export async function hasCustomGetInitialProps( runtimeEnvConfig: any, checkingApp: boolean ): Promise { - require('../shared/lib/runtime-config.shared-runtime').setConfig( - runtimeEnvConfig - ) + require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) const components = await loadComponents({ distDir, @@ -1702,9 +1696,7 @@ export async function getDefinedNamedExports( distDir: string, runtimeEnvConfig: any ): Promise> { - require('../shared/lib/runtime-config.shared-runtime').setConfig( - runtimeEnvConfig - ) + require('../shared/lib/runtime-config').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 d1b7487618fe..f1f053d05eb9 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -103,19 +103,6 @@ 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', @@ -1024,7 +1011,7 @@ export default async function getBaseWebpackConfig( const customRootAliases: { [key: string]: string[] } = {} if (dev) { - const nextDistPath = 'next/dist/' + (isEdgeServer ? 'esm/' : '') + const nextDist = 'next/dist/' + (isEdgeServer ? 'esm/' : '') customAppAliases[`${PAGES_DIR_ALIAS}/_app`] = [ ...(pagesDir ? pageExtensions.reduce((prev, ext) => { @@ -1032,7 +1019,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDistPath}pages/_app.js`, + `${nextDist}pages/_app.js`, ] customAppAliases[`${PAGES_DIR_ALIAS}/_error`] = [ ...(pagesDir @@ -1041,7 +1028,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDistPath}pages/_error.js`, + `${nextDist}pages/_error.js`, ] customDocumentAliases[`${PAGES_DIR_ALIAS}/_document`] = [ ...(pagesDir @@ -1050,7 +1037,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDistPath}pages/_document.js`, + `${nextDist}pages/_document.js`, ] } @@ -1324,7 +1311,6 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.serverSideRendering, WEBPACK_LAYERS.appPagesBrowser, WEBPACK_LAYERS.actionBrowser, - WEBPACK_LAYERS.appRouteHandler, ].includes(layer!) if ( @@ -1381,7 +1367,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|router)$)|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)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ if (notExternalModules.test(request)) { return } @@ -1404,59 +1390,41 @@ 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) => { - 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}` - } + // 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. - const name = path.parse(localRes).name.replace('.shared-runtime', '') + // Treat next internals as non-external for server layer + if (isWebpackServerLayer(layer)) { + return + } - const camelCaseName = name.replace(/-([a-z])/g, (_, w) => - w.toUpperCase() + 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, '/') ) - - // 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, - ] + return `commonjs ${externalRequest}` } } @@ -1477,10 +1445,6 @@ 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) @@ -2067,14 +2031,6 @@ 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. @@ -2283,7 +2239,7 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.appPagesBrowser, ], }, - exclude: [codeCondition.exclude], + exclude: [asyncStoragesRegex, 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 c2b1089df5b3..2be12daecdbc 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.shared-runtime.js' + import type { NavigateOptions, AppRouterInstance as OriginalAppRouterInstance } from 'next/dist/shared/lib/app-router-context.js' interface AppRouterInstance extends OriginalAppRouterInstance { /** * Navigate to the provided href. @@ -575,11 +575,8 @@ export class NextTypesPlugin { } return } - if ( - mod.layer !== WEBPACK_LAYERS.reactServerComponents && - mod.layer !== WEBPACK_LAYERS.appRouteHandler - ) - return + + if (mod.layer !== WEBPACK_LAYERS.reactServerComponents) 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 47c0bd13f369..8f82d244837e 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.shared-runtime' -import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context.shared-runtime' +import { HeadManagerContext } from '../shared/lib/head-manager-context' +import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context' 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 e9143c4117bd..58b1b9f02ed0 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.shared-runtime' +import { RouterContext } from '../../shared/lib/router-context' import { NextRouter } from '../router' /** diff --git a/packages/next/src/client/components/action-async-storage.external.ts b/packages/next/src/client/components/action-async-storage.ts similarity index 100% rename from packages/next/src/client/components/action-async-storage.external.ts rename to packages/next/src/client/components/action-async-storage.ts diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx index a8de967825e1..db181118a674 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.shared-runtime' +} from '../../shared/lib/app-router-context' import type { CacheNode, AppRouterInstance, -} from '../../shared/lib/app-router-context.shared-runtime' +} from '../../shared/lib/app-router-context' 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.shared-runtime' +} from '../../shared/lib/hooks-client-context' 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 799398b5f300..76356e05304a 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.external' +import { staticGenerationAsyncStorage } from './static-generation-async-storage' 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 a0a27a184cbf..d090264391e7 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.external' -import { actionAsyncStorage } from './action-async-storage.external' +import { requestAsyncStorage } from './request-async-storage' +import { actionAsyncStorage } from './action-async-storage' import { staticGenerationBailout } from './static-generation-bailout' import { DraftMode } from './draft-mode' @@ -17,6 +17,7 @@ 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 1f0ffff7e2de..3e410a93fb0c 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.shared-runtime' +import type { ChildSegmentMap } from '../../shared/lib/app-router-context' import type { FlightRouterState, FlightSegmentPath, @@ -17,7 +17,7 @@ import { LayoutRouterContext, GlobalLayoutRouterContext, TemplateContext, -} from '../../shared/lib/app-router-context.shared-runtime' +} from '../../shared/lib/app-router-context' 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 b3d69dcb065e..bf6a56100080 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.shared-runtime' +} from '../../shared/lib/app-router-context' import { SearchParamsContext, PathnameContext, -} from '../../shared/lib/hooks-client-context.shared-runtime' +} from '../../shared/lib/hooks-client-context' 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.shared-runtime' +} from '../../shared/lib/server-inserted-html' /** * Get the router methods. For example router.push('/dashboard') */ -export function useRouter(): import('../../shared/lib/app-router-context.shared-runtime').AppRouterInstance { +export function useRouter(): import('../../shared/lib/app-router-context').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 d37fce9851e9..4d92a279c3ed 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.shared-runtime' +import { GlobalLayoutRouterContext } from '../../../../../shared/lib/app-router-context' 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 8d407fd6e9d6..23e5493ae83f 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.shared-runtime' +import { AppRouterInstance } from '../../shared/lib/app-router-context' 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 b9a2cfebd883..10e72bc1ccbe 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.external' +import { requestAsyncStorage } from './request-async-storage' 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 c1755cc5056b..be486842c4f3 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.shared-runtime' +import { TemplateContext } from '../../shared/lib/app-router-context' export default function RenderFromTemplateContext(): JSX.Element { const children = useContext(TemplateContext) diff --git a/packages/next/src/client/components/request-async-storage.external.ts b/packages/next/src/client/components/request-async-storage.ts similarity index 100% rename from packages/next/src/client/components/request-async-storage.external.ts rename to packages/next/src/client/components/request-async-storage.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 003d0a5cde9e..e7a2f11a84f4 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,7 +1,4 @@ -import { - CacheNode, - CacheStates, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' 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 414b553c6324..a6052636ef25 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,9 +1,6 @@ import React from 'react' import type { FlightRouterState } from '../../../server/app-render/types' -import { - CacheNode, - CacheStates, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' 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 94fdabb9b577..7f7cca2003b0 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.shared-runtime' +import type { CacheNode } from '../../../shared/lib/app-router-context' import type { FlightRouterState } from '../../../server/app-render/types' -import { CacheStates } from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheStates } from '../../../shared/lib/app-router-context' 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 648069ea7698..28f8c3412ab3 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,10 +1,7 @@ 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.shared-runtime' +import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' 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 42df61a952af..81df295dba30 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,8 +1,5 @@ import { FlightSegmentPath } from '../../../server/app-render/types' -import { - CacheNode, - CacheStates, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' 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 ac888a3ede0f..187f86a47875 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,9 +1,6 @@ import React from 'react' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' -import { - CacheStates, - CacheNode, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' 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 7e9a93699fb6..5d48eaee9ef9 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,7 +1,4 @@ -import { - CacheNode, - CacheStates, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' 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 1edbeffd7b3e..606440a96f9c 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,9 +1,6 @@ import React from 'react' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' -import { - CacheStates, - CacheNode, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' 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 f558edfab2f1..c5ddedd52351 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,7 +1,4 @@ -import { - CacheNode, - CacheStates, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' 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 8c23c47d42d7..915f09cae0ca 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,10 +1,7 @@ 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.shared-runtime' +import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' 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 d637d850b145..ac343f8d7967 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.shared-runtime' +import type { CacheNode } from '../../../shared/lib/app-router-context' 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 bdd819b0614d..65ce9e42c05e 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,9 +1,6 @@ import React from 'react' import { invalidateCacheByRouterState } from './invalidate-cache-by-router-state' -import { - CacheStates, - CacheNode, -} from '../../../shared/lib/app-router-context.shared-runtime' +import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' 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 1ec39ae9e35f..820e5909bf03 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.shared-runtime' +import type { CacheNode } from '../../../shared/lib/app-router-context' 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 2d4cdef348b1..807374c85557 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.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 08dcefc65f2c..f4d5e768b980 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.shared-runtime' +import type { CacheNode } from '../../../../shared/lib/app-router-context' 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 fcde9b963e9f..db40adfa3c5b 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.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 e47c42b2aa60..fe8a4a24c4b8 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.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 bbbee6ff5f2a..8055123367a9 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.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 bbf36b1ec538..90ce7dc9423a 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.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 bd6dfc4ef904..cd87ef3802b0 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.shared-runtime' +import { CacheStates } from '../../../../shared/lib/app-router-context' 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 36c978926517..b11f39b141cc 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.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 0c6caaba746c..3b8fa6acb401 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.shared-runtime' +import { CacheStates } from '../../../../shared/lib/app-router-context' 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 9e7035dc7e81..db72a92fce74 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 @@ -45,7 +45,7 @@ jest.mock('../fetch-server-response', () => { import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context.shared-runtime' +} from '../../../../shared/lib/app-router-context' 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 d9c641c4045b..284628dbf909 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.shared-runtime' +import type { CacheNode } from '../../../shared/lib/app-router-context' import type { FlightRouterState, FlightData, diff --git a/packages/next/src/client/components/static-generation-async-storage.external.ts b/packages/next/src/client/components/static-generation-async-storage.ts similarity index 100% rename from packages/next/src/client/components/static-generation-async-storage.external.ts rename to packages/next/src/client/components/static-generation-async-storage.ts diff --git a/packages/next/src/client/components/static-generation-bailout.ts b/packages/next/src/client/components/static-generation-bailout.ts index 4d3515066425..c5072218f035 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.external' +import { staticGenerationAsyncStorage } from './static-generation-async-storage' 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 321b07ecd0a5..3f2183c004b1 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.shared-runtime' +import { ImageConfigContext } from '../shared/lib/image-config-context' import { warnOnce } from '../shared/lib/utils/warn-once' -import { RouterContext } from '../shared/lib/router-context.shared-runtime' +import { RouterContext } from '../shared/lib/router-context' // @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 0f8a95c93746..f7c163f020d3 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.shared-runtime' +import { HeadManagerContext } from '../shared/lib/head-manager-context' import mitt, { MittEmitter } from '../shared/lib/mitt' -import { RouterContext } from '../shared/lib/router-context.shared-runtime' +import { RouterContext } from '../shared/lib/router-context' 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.shared-runtime' +import { setConfig } from '../shared/lib/runtime-config' 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.shared-runtime' +import { ImageConfigContext } from '../shared/lib/image-config-context' 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.shared-runtime' +import { AppRouterContext } from '../shared/lib/app-router-context' import { adaptForAppRouterInstance, adaptForSearchParams, PathnameContextProviderAdapter, -} from '../shared/lib/router/adapters.shared-runtime' -import { SearchParamsContext } from '../shared/lib/hooks-client-context.shared-runtime' +} from '../shared/lib/router/adapters' +import { SearchParamsContext } from '../shared/lib/hooks-client-context' 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 07ec2e217c20..d1456477bacf 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.shared-runtime' +import { ImageConfigContext } from '../../shared/lib/image-config-context' 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 7a15dee249e2..94226c8caa5c 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.shared-runtime' +import { RouterContext } from '../shared/lib/router-context' import { AppRouterContext, AppRouterInstance, PrefetchOptions as AppRouterPrefetchOptions, -} from '../shared/lib/app-router-context.shared-runtime' +} from '../shared/lib/app-router-context' 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 f43126691e5e..342ecb623df7 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.shared-runtime' +import { RouterContext } from '../shared/lib/router-context' 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 f695e691c482..a4b2c6dcd318 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.shared-runtime' +import { HeadManagerContext } from '../shared/lib/head-manager-context' 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 b9fb7b9afd60..31f58755996a 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.shared-runtime') +const envConfig = require('../shared/lib/runtime-config') ;(globalThis as any).__NEXT_DATA__ = { nextExport: true, @@ -307,10 +307,8 @@ export default async function exportPage({ await promises.mkdir(baseDir, { recursive: true }) let renderResult: RenderResult | undefined let curRenderOpts: RenderOpts = {} - const renderToHTML = - require('../server/future/route-modules/pages/module.compiled') - .renderToHTML as typeof import('../server/render').renderToHTML - + const { renderToHTML } = + require('../server/render') as typeof import('../server/render') let renderMethod = renderToHTML let inAmpMode = false, hybridAmp = false @@ -481,6 +479,7 @@ 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) @@ -536,9 +535,8 @@ export default async function exportPage({ results.fromBuildExportRevalidate = 0 } } else { - const renderToHTMLOrFlight = - require('../server/future/route-modules/app-page/module.compiled') - .renderToHTMLOrFlight as typeof import('../server/app-render/app-render').renderToHTMLOrFlight + const { renderToHTMLOrFlight } = + require('../server/app-render/app-render') as typeof import('../server/app-render/app-render') try { curRenderOpts.params ||= {} diff --git a/packages/next/src/lib/chalk.ts b/packages/next/src/lib/chalk.ts index d0939d9148b9..8e40472953f8 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' || process.env.NEXT_MINIMAL) { +if (process.env.NEXT_RUNTIME === 'edge') { 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 46f85311a9f0..fc94397c6aca 100644 --- a/packages/next/src/lib/constants.ts +++ b/packages/next/src/lib/constants.ts @@ -132,10 +132,6 @@ 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 = { @@ -145,7 +141,6 @@ 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 f0a29cb15d1f..a8526011c5d3 100644 --- a/packages/next/src/pages/_document.tsx +++ b/packages/next/src/pages/_document.tsx @@ -17,11 +17,8 @@ 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.shared-runtime' -import type { HtmlProps } from '../shared/lib/html-context.shared-runtime' +import { HtmlContext, useHtmlContext } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context' 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 9f6059ec48f5..6b8bc3228a00 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.external' +import { StaticGenerationStore } from '../../client/components/static-generation-async-storage' import { FlightRenderResult } from './flight-render-result' import { ActionResult } from './types' -import { ActionAsyncStorage } from '../../client/components/action-async-storage.external' +import { ActionAsyncStorage } from '../../client/components/action-async-storage' import { filterReqHeaders, actionsForbiddenHeaders, @@ -31,8 +31,7 @@ import { appendMutableCookies, getModifiedCookieValues, } from '../web/spec-extension/adapters/request-cookies' - -import { RequestStore } from '../../client/components/request-async-storage.external' +import { RequestStore } from '../../client/components/request-async-storage' 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 1dd2eb8f7c63..30749ac66a83 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.external' +import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' import type { StaticGenerationBailout } from '../../client/components/static-generation-bailout' -import type { RequestAsyncStorage } from '../../client/components/request-async-storage.external' +import type { RequestAsyncStorage } from '../../client/components/request-async-storage' import React from 'react' import { createServerComponentRenderer } from './create-server-components-renderer' @@ -286,13 +286,10 @@ export async function renderToHTMLOrFlight( * that we need to resolve the final metadata. */ - let requestId: string - - if (process.env.NEXT_RUNTIME === 'edge') { - requestId = crypto.randomUUID() - } else { - requestId = require('next/dist/compiled/nanoid').nanoid() - } + const requestId = + process.env.NEXT_RUNTIME === 'edge' + ? crypto.randomUUID() + : require('next/dist/compiled/nanoid').nanoid() const LayoutRouter = ComponentMod.LayoutRouter as typeof import('../../client/components/layout-router').default @@ -1395,7 +1392,7 @@ export async function renderToHTMLOrFlight( ) const { HeadManagerContext } = - require('../../shared/lib/head-manager-context.shared-runtime') as typeof import('../../shared/lib/head-manager-context.shared-runtime') + require('../../shared/lib/head-manager-context') as typeof import('../../shared/lib/head-manager-context') // 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 0cc53e0214d8..6bc5fd7e7ace 100644 --- a/packages/next/src/server/app-render/entry-base.ts +++ b/packages/next/src/server/app-render/entry-base.ts @@ -1,26 +1,36 @@ +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') - -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 { preloadStyle, preloadFont, preconnect } = + require('next/dist/server/app-render/rsc/preloads') as typeof import('../../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 764dc6279207..f044c24feaba 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.shared-runtime' +import { ServerInsertedHTMLContext } from '../../shared/lib/server-inserted-html' 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 1376ecfb197c..50795855c53d 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.external' +import type { RequestStore } from '../../client/components/request-async-storage' 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 d5adfc9de38b..a28ac0e8ecb2 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.external' +import type { StaticGenerationStore } from '../../client/components/static-generation-async-storage' 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 fab999db35dd..d826f2bdd4e0 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.shared-runtime' +import { setConfig } from '../shared/lib/runtime-config' import { setRevalidateHeaders } from './send-payload/revalidate-headers' import { execOnce } from '../shared/lib/utils' @@ -427,11 +427,7 @@ export default abstract class Server { } = this.nextConfig this.buildId = this.getBuildId() - // 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.minimalMode = 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 839fa1b13c0e..1eca11b7358c 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-default-error-components' +import { loadDefaultErrorComponents } from '../load-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 ddd6526e52f2..68932df7a36b 100644 --- a/packages/next/src/server/dev/static-paths-worker.ts +++ b/packages/next/src/server/dev/static-paths-worker.ts @@ -14,10 +14,8 @@ 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.external' - -const { AppRouteRouteModule } = - require('../future/route-modules/app-route/module.compiled') as typeof import('../future/route-modules/app-route/module') +import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' +import { AppRouteRouteModule } from '../future/route-modules/app-route/module' type RuntimeConfig = any @@ -58,7 +56,7 @@ export async function loadStaticPaths({ fallback?: boolean | 'blocking' }> { // update work memory runtime-config - require('../../shared/lib/runtime-config.shared-runtime').setConfig(config) + require('../../shared/lib/runtime-config').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 6f70685df7e7..3283eb00a53f 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,10 +7,7 @@ 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 (process.env.NEXT_MINIMAL - ? // @ts-ignore - __non_webpack_require__(id) - : require(id)) + return await 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 deleted file mode 100644 index 78601739acbe..000000000000 --- a/packages/next/src/server/future/route-modules/app-page/module.compiled.ts +++ /dev/null @@ -1,11 +0,0 @@ -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 daa0291a1c8b..418e37420d7e 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,7 +11,6 @@ import { type RouteModuleOptions, type RouteModuleHandleContext, } from '../route-module' -import * as sharedModules from './shared-modules' type AppPageUserlandModule = { /** @@ -35,8 +34,6 @@ export class AppPageRouteModule extends RouteModule< AppPageRouteDefinition, AppPageUserlandModule > { - static readonly sharedModules = sharedModules - public render( req: IncomingMessage, res: ServerResponse, @@ -52,6 +49,4 @@ 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 deleted file mode 100644 index e986c1bad389..000000000000 --- a/packages/next/src/server/future/route-modules/app-page/shared-modules.ts +++ /dev/null @@ -1,13 +0,0 @@ -// 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 deleted file mode 100644 index f5909104bc77..000000000000 --- a/packages/next/src/server/future/route-modules/app-route/module.compiled.ts +++ /dev/null @@ -1,11 +0,0 @@ -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 d1d36d31501e..bf47738d35fe 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,14 +35,22 @@ import { appendMutableCookies } from '../../../web/spec-extension/adapters/reque import { RouteKind } from '../../route-kind' import { parsedUrlQueryToParams } from './helpers/parsed-url-query-to-params' -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' +// 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') /** * AppRouteRouteHandlerContext is the context that is passed to the route @@ -139,8 +147,6 @@ 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 deleted file mode 100644 index e6139d5a6940..000000000000 --- a/packages/next/src/server/future/route-modules/app-route/shared-modules.ts +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index ed74c41adb91..000000000000 --- a/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts +++ /dev/null @@ -1,11 +0,0 @@ -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 976daeeec4a8..88dbda73b464 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,16 +100,6 @@ 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 deleted file mode 100644 index a935b62abdca..000000000000 --- a/packages/next/src/server/future/route-modules/pages/module.compiled.ts +++ /dev/null @@ -1,11 +0,0 @@ -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 e2730ef66890..dac8ae554644 100644 --- a/packages/next/src/server/future/route-modules/pages/module.ts +++ b/packages/next/src/server/future/route-modules/pages/module.ts @@ -17,8 +17,7 @@ import { type RouteModuleHandleContext, type RouteModuleOptions, } from '../route-module' -import { renderToHTMLImpl, renderToHTML } from '../../../render' -import * as sharedModules from './shared-modules' +import { renderToHTMLImpl } from '../../../render' /** * The userland module for a page. This is the module that is exported from the @@ -105,8 +104,6 @@ export class PagesRouteModule extends RouteModule< > { private readonly components: PagesComponents - static readonly sharedModules = sharedModules - constructor(options: PagesRouteModuleOptions) { super(options) @@ -132,7 +129,4 @@ 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 deleted file mode 100644 index 55cdfbdeca37..000000000000 --- a/packages/next/src/server/future/route-modules/pages/shared-modules.ts +++ /dev/null @@ -1,12 +0,0 @@ -// 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 a8e5dd6c5945..52188ed506df 100644 --- a/packages/next/src/server/future/route-modules/route-module.ts +++ b/packages/next/src/server/future/route-modules/route-module.ts @@ -44,11 +44,6 @@ 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 3032aa685300..ae2b11b4dc00 100644 --- a/packages/next/src/server/lib/incremental-cache/index.ts +++ b/packages/next/src/server/lib/incremental-cache/index.ts @@ -131,10 +131,7 @@ export class IncrementalCache { maxMemoryCacheSize = parseInt(process.env.__NEXT_TEST_MAX_ISR_CACHE, 10) } this.dev = dev - // 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.minimalMode = 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 080361704b71..fb4d02c97b25 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.external' +import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' 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 023cbde2d25d..8e7bd60a8855 100644 --- a/packages/next/src/server/lib/server-ipc/index.ts +++ b/packages/next/src/server/lib/server-ipc/index.ts @@ -115,7 +115,6 @@ 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 1b45358bdf8e..50eb4528c7de 100644 --- a/packages/next/src/server/lib/trace/constants.ts +++ b/packages/next/src/server/lib/trace/constants.ts @@ -43,6 +43,7 @@ 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 39da802ff39b..d2b563493ebe 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. */ -export async function loadManifestWithRetries( +async function loadManifestWithRetries( manifestPath: string, attempts = 3 ): Promise { @@ -87,6 +87,34 @@ 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, @@ -177,3 +205,8 @@ 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 deleted file mode 100644 index c390e9180b3d..000000000000 --- a/packages/next/src/server/load-default-error-components.ts +++ /dev/null @@ -1,78 +0,0 @@ -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 7abf3605541a..087752225090 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 } from '../build' +import type { PrerenderManifest, RoutesManifest } 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,6 +26,7 @@ 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' @@ -48,6 +49,7 @@ 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' @@ -94,6 +96,7 @@ 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' @@ -379,6 +382,14 @@ 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, @@ -441,7 +452,7 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, pathname: string, query: NextParsedUrlQuery, - renderOpts: import('./render').RenderOpts + renderOpts: RenderOpts ): Promise { return getTracer().trace(NextNodeServerSpan.renderHTML, async () => this.renderHTMLImpl(req, res, pathname, query, renderOpts) @@ -453,35 +464,17 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, pathname: string, query: NextParsedUrlQuery, - renderOpts: import('./render').RenderOpts + renderOpts: RenderOpts ): Promise { - 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( + // 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( req.originalRequest, res.originalResponse, pathname, @@ -489,6 +482,17 @@ 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( @@ -496,63 +500,55 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, paramsResult: import('./image-optimizer').ImageParamsResult ): Promise<{ buffer: Buffer; contentType: string; maxAge: number }> { - 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') - - 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` - ) - } + 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 - ) - - for (const key of Object.keys(filteredResHeaders)) { - newRes.setHeader(key, filteredResHeaders[key] || '') + 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), } - newRes.statusCode = invokeRes.status || 200 + ) + const filteredResHeaders = filterReqHeaders( + toNodeOutgoingHttpHeaders(invokeRes.headers), + ipcForbiddenHeaders + ) - if (invokeRes.body) { - await pipeReadable(invokeRes.body, newRes) - } else { - res.send() - } - return + for (const key of Object.keys(filteredResHeaders)) { + newRes.setHeader(key, filteredResHeaders[key] || '') } - return this.getRequestHandler()( - new NodeNextRequest(newReq), - new NodeNextResponse(newRes), - newParsedUrl - ) + newRes.statusCode = invokeRes.status || 200 + + if (invokeRes.body) { + await pipeReadable(invokeRes.body, newRes) + } else { + res.send() + } + return } - ) - } + return this.getRequestHandler()( + new NodeNextRequest(newReq), + new NodeNextResponse(newRes), + newParsedUrl + ) + } + ) } protected getPagePath(pathname: string, locales?: string[]): string { @@ -723,109 +719,99 @@ export default class NextNodeServer extends BaseServer { res: BaseNextResponse, parsedUrl: NextUrlWithParsedQuery ) { - if ( - this.minimalMode || - this.nextConfig.output === 'export' || - process.env.NEXT_MINIMAL - ) { + if (this.minimalMode || this.nextConfig.output === 'export') { res.statusCode = 400 res.body('Bad Request').send() return { finished: true, } - // 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 } = + require('./image-optimizer') as typeof import('./image-optimizer') - const { getHash, sendResponse, ImageError } = - require('./image-optimizer') as typeof import('./image-optimizer') + const imageOptimizerCache = new ImageOptimizerCache({ + distDir: this.distDir, + nextConfig: this.nextConfig, + }) - if (!this.imageResponseCache) { - throw new Error('invariant image optimizer cache was not initialized') - } - const imagesConfig = this.nextConfig.images + const { getHash, sendResponse, ImageError } = + require('./image-optimizer') as typeof import('./image-optimizer') - 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 (!this.imageResponseCache) { + throw new Error('invariant image optimizer cache was not initialized') + } + const imagesConfig = this.nextConfig.images - if ('errorMessage' in paramsResult) { - res.statusCode = 400 - res.body(paramsResult.errorMessage).send() - return { finished: true } - } - const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult) + 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 + ) - 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 ('errorMessage' in paramsResult) { + res.statusCode = 400 + res.body(paramsResult.errorMessage).send() + return { finished: true } + } + const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult) - if (cacheEntry?.value?.kind !== 'IMAGE') { - throw new Error( - 'invariant did not get entry from image response cache' + try { + const cacheEntry = await this.imageResponseCache.get( + cacheKey, + async () => { + const { buffer, contentType, maxAge } = await this.imageOptimizer( + req as NodeNextRequest, + res as NodeNextResponse, + paramsResult ) - } - 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() + const etag = getHash([buffer]) + return { - finished: true, + value: { + kind: 'IMAGE', + buffer, + etag, + extension: getExtension(contentType) as string, + }, + revalidate: maxAge, } + }, + { + incrementalCache: imageOptimizerCache, } - throw err + ) + + if (cacheEntry?.value?.kind !== 'IMAGE') { + throw new Error('invariant did not get entry from image response cache') } - return { finished: true } + 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 } + return { finished: true } } protected async handleCatchallRenderRequest( @@ -1026,7 +1012,6 @@ 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 @@ -1447,12 +1432,6 @@ 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) @@ -1696,7 +1675,10 @@ export default class NextNodeServer extends BaseServer { protected getRoutesManifest(): NormalizedRouteManifest | undefined { return getTracer().trace(NextNodeServerSpan.getRoutesManifest, () => { - const manifest = loadManifest(join(this.distDir, ROUTES_MANIFEST)) + const manifest: RoutesManifest = require(join( + this.distDir, + ROUTES_MANIFEST + )) let rewrites = manifest.rewrites ?? { beforeFiles: [], @@ -1754,11 +1736,6 @@ 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 2a8252e4fd14..b93dfce5fae2 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.external' +import { StaticGenerationStore } from '../client/components/static-generation-async-storage' 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 fe36e793348e..ac0989082aa9 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.shared-runtime' +import type { HtmlProps } from '../shared/lib/html-context' 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.shared-runtime' +import { AmpStateContext } from '../shared/lib/amp-context' import { defaultHead } from '../shared/lib/head' -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 { 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 { 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.shared-runtime' +import { HtmlContext } from '../shared/lib/html-context' 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.shared-runtime' +import { ImageConfigContext } from '../shared/lib/image-config-context' import stripAnsi from 'next/dist/compiled/strip-ansi' import { stripInternalQueries } from './internal-utils' import { adaptForAppRouterInstance, adaptForSearchParams, PathnameContextProviderAdapter, -} 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' +} from '../shared/lib/router/adapters' +import { AppRouterContext } from '../shared/lib/app-router-context' +import { SearchParamsContext } from '../shared/lib/hooks-client-context' 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 9b2e1526eec0..1f53b3b47910 100644 --- a/packages/next/src/server/require-hook.ts +++ b/packages/next/src/server/require-hook.ts @@ -2,13 +2,11 @@ // 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 @@ -21,9 +19,10 @@ 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(resolve('styled-jsx/package.json')), - 'styled-jsx/style': resolve('styled-jsx/style'), + 'styled-jsx': dirname(require.resolve('styled-jsx/package.json')), + 'styled-jsx/style': require.resolve('styled-jsx/style'), } export const baseOverrides = { @@ -79,6 +78,7 @@ export function addHookAliases(aliases: [string, string][] = []) { } } +// Add default aliases addHookAliases(toResolveMap(defaultOverrides)) // Override built-in React packages if necessary @@ -117,29 +117,3 @@ 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 7bd6710cc4a8..6d135da26e93 100644 --- a/packages/next/src/server/response-cache/index.ts +++ b/packages/next/src/server/response-cache/index.ts @@ -20,10 +20,7 @@ export default class ResponseCache { constructor(minimalMode: boolean) { this.pendingResponses = new Map() - // 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.minimalMode = minimalMode } public get( diff --git a/packages/next/src/server/response-cache/web.ts b/packages/next/src/server/response-cache/web.ts index f255fdd5412d..e37ccca31481 100644 --- a/packages/next/src/server/response-cache/web.ts +++ b/packages/next/src/server/response-cache/web.ts @@ -15,9 +15,7 @@ export default class WebResponseCache { constructor(minimalMode: boolean) { this.pendingResponses = new Map() - // 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 }) + this.minimalMode = minimalMode } public get( diff --git a/packages/next/src/server/web/adapter.ts b/packages/next/src/server/web/adapter.ts index 5c6ad3e7ed5a..1ea32a75956c 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.external' +import { requestAsyncStorage } from '../../client/components/request-async-storage' 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 d44ea986cad6..df3c369eef87 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.external' +import { StaticGenerationStore } from '../../../../client/components/static-generation-async-storage' 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 7c7bff8c2f78..8d7cd68bd3a9 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.external' +} from '../../../client/components/static-generation-async-storage' 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 1e85b5c29097..aad3ed2baf20 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.external' +} from '../../../client/components/static-generation-async-storage' import { CACHE_ONE_YEAR } from '../../../lib/constants' import { addImplicitTags } from '../../lib/patch-fetch' diff --git a/packages/next/src/shared/lib/amp-context.shared-runtime.ts b/packages/next/src/shared/lib/amp-context.ts similarity index 100% rename from packages/next/src/shared/lib/amp-context.shared-runtime.ts rename to packages/next/src/shared/lib/amp-context.ts diff --git a/packages/next/src/shared/lib/amp.ts b/packages/next/src/shared/lib/amp.ts index 8edd21db9c29..04518b238935 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.shared-runtime' +import { AmpStateContext } from './amp-context' import { isInAmpMode } from './amp-mode' export function useAmp(): boolean { diff --git a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts b/packages/next/src/shared/lib/app-router-context.ts similarity index 100% rename from packages/next/src/shared/lib/app-router-context.shared-runtime.ts rename to packages/next/src/shared/lib/app-router-context.ts diff --git a/packages/next/src/shared/lib/dynamic.tsx b/packages/next/src/shared/lib/dynamic.tsx index 390410edda29..cb497fc587d4 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.shared-runtime' +import Loadable from './loadable' const isServerSide = typeof window === 'undefined' diff --git a/packages/next/src/shared/lib/head-manager-context.shared-runtime.ts b/packages/next/src/shared/lib/head-manager-context.ts similarity index 100% rename from packages/next/src/shared/lib/head-manager-context.shared-runtime.ts rename to packages/next/src/shared/lib/head-manager-context.ts diff --git a/packages/next/src/shared/lib/head.tsx b/packages/next/src/shared/lib/head.tsx index 42f95767bfa4..3156e259f656 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.shared-runtime' -import { HeadManagerContext } from './head-manager-context.shared-runtime' +import { AmpStateContext } from './amp-context' +import { HeadManagerContext } from './head-manager-context' import { isInAmpMode } from './amp-mode' import { warnOnce } from './utils/warn-once' diff --git a/packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts b/packages/next/src/shared/lib/hooks-client-context.ts similarity index 100% rename from packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts rename to packages/next/src/shared/lib/hooks-client-context.ts diff --git a/packages/next/src/shared/lib/html-context.shared-runtime.ts b/packages/next/src/shared/lib/html-context.ts similarity index 100% rename from packages/next/src/shared/lib/html-context.shared-runtime.ts rename to packages/next/src/shared/lib/html-context.ts diff --git a/packages/next/src/shared/lib/image-config-context.shared-runtime.ts b/packages/next/src/shared/lib/image-config-context.ts similarity index 100% rename from packages/next/src/shared/lib/image-config-context.shared-runtime.ts rename to packages/next/src/shared/lib/image-config-context.ts diff --git a/packages/next/src/shared/lib/loadable-context.shared-runtime.ts b/packages/next/src/shared/lib/loadable-context.ts similarity index 100% rename from packages/next/src/shared/lib/loadable-context.shared-runtime.ts rename to packages/next/src/shared/lib/loadable-context.ts diff --git a/packages/next/src/shared/lib/loadable.shared-runtime.tsx b/packages/next/src/shared/lib/loadable.tsx similarity index 99% rename from packages/next/src/shared/lib/loadable.shared-runtime.tsx rename to packages/next/src/shared/lib/loadable.tsx index 82ba84182701..1592d9855109 100644 --- a/packages/next/src/shared/lib/loadable.shared-runtime.tsx +++ b/packages/next/src/shared/lib/loadable.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.shared-runtime' +import { LoadableContext } from './loadable-context' function resolve(obj: any) { return obj && obj.default ? obj.default : obj diff --git a/packages/next/src/shared/lib/router-context.shared-runtime.ts b/packages/next/src/shared/lib/router-context.ts similarity index 100% rename from packages/next/src/shared/lib/router-context.shared-runtime.ts rename to packages/next/src/shared/lib/router-context.ts diff --git a/packages/next/src/shared/lib/router/adapters.test.tsx b/packages/next/src/shared/lib/router/adapters.test.tsx index e47ce2174dd3..fa8e48f2fc08 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.shared-runtime' +import { adaptForAppRouterInstance } from './adapters' import { NextRouter } from './router' describe('adaptForAppRouterInstance', () => { diff --git a/packages/next/src/shared/lib/router/adapters.shared-runtime.tsx b/packages/next/src/shared/lib/router/adapters.tsx similarity index 95% rename from packages/next/src/shared/lib/router/adapters.shared-runtime.tsx rename to packages/next/src/shared/lib/router/adapters.tsx index 29f92dda8dc0..ce68a8bec1e8 100644 --- a/packages/next/src/shared/lib/router/adapters.shared-runtime.tsx +++ b/packages/next/src/shared/lib/router/adapters.tsx @@ -1,10 +1,7 @@ import type { ParsedUrlQuery } from 'node:querystring' import React, { useMemo, useRef } from 'react' -import type { - AppRouterInstance, - NavigateOptions, -} from '../app-router-context.shared-runtime' -import { PathnameContext } from '../hooks-client-context.shared-runtime' +import type { AppRouterInstance, NavigateOptions } from '../app-router-context' +import { PathnameContext } from '../hooks-client-context' import type { NextRouter } from './router' import { isDynamicRoute } from './utils' diff --git a/packages/next/src/shared/lib/runtime-config.shared-runtime.ts b/packages/next/src/shared/lib/runtime-config.ts similarity index 100% rename from packages/next/src/shared/lib/runtime-config.shared-runtime.ts rename to packages/next/src/shared/lib/runtime-config.ts diff --git a/packages/next/src/shared/lib/server-inserted-html.shared-runtime.tsx b/packages/next/src/shared/lib/server-inserted-html.tsx similarity index 100% rename from packages/next/src/shared/lib/server-inserted-html.shared-runtime.tsx rename to packages/next/src/shared/lib/server-inserted-html.tsx diff --git a/packages/next/src/shared/lib/utils.ts b/packages/next/src/shared/lib/utils.ts index 4e8036a6894a..f967be459022 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.shared-runtime' +import type { HtmlProps } from './html-context' 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 e3928f775f61..e242e19c9041 100644 --- a/packages/next/src/trace/index.ts +++ b/packages/next/src/trace/index.ts @@ -1,5 +1,4 @@ import { trace, flushAllTraces, Span, SpanStatus } from './trace' import { SpanId, setGlobal } from './shared' -export { trace, flushAllTraces, Span, setGlobal, SpanStatus } -export type { SpanId } +export { trace, flushAllTraces, SpanId, Span, SpanStatus, setGlobal } diff --git a/packages/next/taskfile-webpack.js b/packages/next/taskfile-webpack.js deleted file mode 100644 index 04495d7b3621..000000000000 --- a/packages/next/taskfile-webpack.js +++ /dev/null @@ -1,35 +0,0 @@ -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 321490234153..f38289c3d498 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2357,7 +2357,7 @@ export async function ncc(task, opts) { ) } -export async function next_compile(task, opts) { +export async function compile(task, opts) { await task.parallel( [ 'cli', @@ -2388,16 +2388,12 @@ export async function next_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', ]) } @@ -2662,38 +2658,157 @@ export async function release(task) { await task.clear('dist').start('build') } -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', +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_dev(task, opts) { - await task.source('dist').webpack({ - watch: opts.dev, - config: require('./webpack.config')({ - dev: true, - }), - name: 'next-bundle-dev', + 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_turbo_prod(task, opts) { - await task.source('dist').webpack({ - watch: opts.dev, - config: require('./webpack.config')({ - turbo: true, - }), - name: 'next-bundle-prod-turbo', + 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(task, opts) { - await task.parallel( - ['next_bundle_prod', 'next_bundle_dev', 'next_bundle_turbo_prod'], - opts + + script.runInThisContext()(exports, require, module, __filename, __dirname) + + const buffer = script.createCachedData() + + await fs.writeFile( + join(__dirname, `dist/compiled/minimal-next-server/${cachedOutputName}`), + buffer ) } diff --git a/packages/next/webpack.config.js b/packages/next/webpack.config.js deleted file mode 100644 index 8e7c3063fa5e..000000000000 --- a/packages/next/webpack.config.js +++ /dev/null @@ -1,145 +0,0 @@ -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 68790eaa3a0a..50f7630acfd8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1381,9 +1381,6 @@ 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 08f9125a4f0c..f4f30ac97ce2 100644 --- a/scripts/minimal-server.js +++ b/scripts/minimal-server.js @@ -1,4 +1,3 @@ -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. @@ -45,13 +44,11 @@ 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) } } @@ -59,9 +56,10 @@ if (process.env.LOG_READFILE) { console.time('next-cold-start') const NextServer = process.env.USE_BUNDLED_NEXT - ? require('next/dist/compiled/next-server/server.runtime.prod').default + ? require('next/dist/compiled/minimal-next-server/next-server-cached').default : require('next/dist/server/next-server').default +console.timeEnd('next-cold-start') if (process.env.LOG_READFILE) { console.log(`readFileCount: ${readFileCount + readFileSyncCount}`) } @@ -103,20 +101,9 @@ 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 3e5e6b82ea05..a61e222c3872 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 da17edc01839..4433c9c2ee84 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.shared-runtime' +import { RouterContext } from 'next/dist/shared/lib/router-context' 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 13e0fefea278..ae798ec7c5c2 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,39 +160,37 @@ 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.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, - }, + }, + 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", }, - ] - `) + "kind": 1, + "name": "GET /api/app/param/data", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -204,52 +202,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') }) @@ -259,52 +257,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') }) @@ -314,38 +312,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 34adb36ce5bd..c23b1d2d05cd 100644 --- a/test/e2e/prerender-native-module.test.ts +++ b/test/e2e/prerender-native-module.test.ts @@ -85,6 +85,8 @@ 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: [], @@ -97,6 +99,7 @@ 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 1ff677e583b1..f21b4224fe23 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -2070,6 +2070,7 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, + /node_modules\/next/, ], notTests: [], }, @@ -2081,6 +2082,7 @@ 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: [ @@ -2096,6 +2098,9 @@ 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 new file mode 100644 index 000000000000..74c3153f1b83 --- /dev/null +++ b/test/integration/externalize-next-server/app/node_modules/comps/index.js @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000000..6e665b646a6a --- /dev/null +++ b/test/integration/externalize-next-server/app/node_modules/comps/package.json @@ -0,0 +1,6 @@ +{ + "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 new file mode 100644 index 000000000000..c5bd706a3a95 --- /dev/null +++ b/test/integration/externalize-next-server/app/package.json @@ -0,0 +1,6 @@ +{ + "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 new file mode 100644 index 000000000000..9ceb7bee3db1 --- /dev/null +++ b/test/integration/externalize-next-server/app/pages/index.js @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000000..bba968de1658 --- /dev/null +++ b/test/integration/externalize-next-server/test/index.test.js @@ -0,0 +1,19 @@ +/* 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 4ad014d0e8f0..91f084ce1d55 100644 --- a/test/integration/jsconfig-baseurl/test/index.test.js +++ b/test/integration/jsconfig-baseurl/test/index.test.js @@ -72,6 +72,12 @@ 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 bc3c635b9ce9..77c84e7edfbb 100644 --- a/test/integration/jsconfig-paths/test/index.test.js +++ b/test/integration/jsconfig-paths/test/index.test.js @@ -89,6 +89,9 @@ 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') ) @@ -104,7 +107,9 @@ 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') From 2b514ea8e33041b820be498c8a6eef5b99303b9f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 7 Sep 2023 16:08:53 -0600 Subject: [PATCH 11/11] Strip internal routing headers (#55114) This strips the internal routing headers added by the routing server. Based on some of the changes being introduced in https://github.com/vercel/next.js/pull/54813 this strips the headers, but this also adds some internal flags to turn this off during testing to validate correct routing beheviour. --- packages/next/src/server/base-server.ts | 27 ++++++++++++++++++ packages/next/src/server/internal-utils.ts | 28 ++++++++++++++++++- packages/next/src/server/next-server.ts | 3 ++ .../i18n-data-route/i18n-data-route.test.ts | 4 +++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index d826f2bdd4e0..bacb1cc38e4f 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -124,6 +124,7 @@ import { } from './web/spec-extension/adapters/next-request' import { matchNextDataPathname } from './lib/match-next-data-pathname' import getRouteFromAssetPath from '../shared/lib/router/utils/get-route-from-asset-path' +import { stripInternalHeaders } from './internal-utils' export type FindComponentsResult = { components: LoadComponentsReturnType @@ -1538,6 +1539,28 @@ export default abstract class Server { ) } + protected stripInternalHeaders(req: BaseNextRequest): void { + // Skip stripping internal headers in test mode while the header stripping + // has been explicitly disabled. This allows tests to verify internal + // routing behavior. + if ( + process.env.__NEXT_TEST_MODE && + process.env.__NEXT_NO_STRIP_INTERNAL_HEADERS === '1' + ) { + return + } + + // Strip the internal headers from both the request and the original + // request. + stripInternalHeaders(req.headers) + if ( + 'originalRequest' in req && + 'headers' in (req as NodeNextRequest).originalRequest + ) { + stripInternalHeaders((req as NodeNextRequest).originalRequest.headers) + } + } + private async renderToResponseWithComponentsImpl( { req, res, pathname, renderOpts: opts }: RequestContext, { components, query }: FindComponentsResult @@ -1546,6 +1569,10 @@ export default abstract class Server { // For edge runtime 404 page, /_not-found needs to be treated as 404 page (process.env.NEXT_RUNTIME === 'edge' && pathname === '/_not-found') || pathname === '/404' + + // Strip the internal headers. + this.stripInternalHeaders(req) + const is500Page = pathname === '/500' const isAppPath = components.isAppPath const hasServerProps = !!components.getServerSideProps diff --git a/packages/next/src/server/internal-utils.ts b/packages/next/src/server/internal-utils.ts index 1ae559ea09db..1e712b16379a 100644 --- a/packages/next/src/server/internal-utils.ts +++ b/packages/next/src/server/internal-utils.ts @@ -1,6 +1,8 @@ -import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers' +import type { IncomingHttpHeaders } from 'http' import type { NextParsedUrlQuery } from './request-meta' +import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers' + const INTERNAL_QUERY_NAMES = [ '__nextFallback', '__nextLocale', @@ -36,3 +38,27 @@ export function stripInternalSearchParams( return (isStringUrl ? instance.toString() : instance) as T } + +/** + * Headers that are set by the Next.js server and should be stripped from the + * request headers going to the user's application. + */ +const INTERNAL_HEADERS = [ + 'x-invoke-path', + 'x-invoke-status', + 'x-invoke-error', + 'x-invoke-query', + 'x-invoke-output', + 'x-middleware-invoke', +] as const + +/** + * Strip internal headers from the request headers. + * + * @param headers the headers to strip of internal headers + */ +export function stripInternalHeaders(headers: IncomingHttpHeaders) { + for (const key of INTERNAL_HEADERS) { + delete headers[key] + } +} diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 087752225090..0fe80339d1a0 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -1585,6 +1585,9 @@ export default class NextNodeServer extends BaseServer { ReturnType > + // Strip the internal headers. + this.stripInternalHeaders(req) + try { await this.ensureMiddleware() diff --git a/test/e2e/i18n-data-route/i18n-data-route.test.ts b/test/e2e/i18n-data-route/i18n-data-route.test.ts index a25d8e8adad2..1231f78f0267 100644 --- a/test/e2e/i18n-data-route/i18n-data-route.test.ts +++ b/test/e2e/i18n-data-route/i18n-data-route.test.ts @@ -17,6 +17,10 @@ createNextDescribe( 'i18n-data-route', { files: __dirname, + env: { + // Disable internal header stripping so we can test the invoke output. + __NEXT_NO_STRIP_INTERNAL_HEADERS: '1', + }, }, ({ next }) => { describe('with locale prefix', () => {