From cc34ea52ff043606cb2fe62afc64fe177cf7dbd4 Mon Sep 17 00:00:00 2001 From: Callum Silcock Date: Thu, 7 Sep 2023 08:52:51 +1000 Subject: [PATCH 01/16] docs: example of generated nonce to use base64 encoding as per spec (#55039) nonce's are limited to characters found in base64 encoding, uuids contain '-' which breaks the spec, converting to a base64 string after generating simplifies this --- This was a bit gotcha in our project, there are a few tools that only expect there to be a single `-` and do a split based off it (so when there are >1 they fail) ## Rules for nonce's - The nonce must be unique for each HTTP response - The nonce should be generated using a cryptographically secure random generator - The nonce should have sufficient length, aim for at least 128 bits of entropy (32 hex characters, or about 24 base64 characters). - Script tags that have a nonce attribute must not have any untrusted / unescaped variables within them. - The characters that can be used in the nonce string are limited to the characters found in base64 encoding. --- .../07-configuring/15-content-security-policy.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx b/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx index b946459119033..0f998316acfef 100644 --- a/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx +++ b/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx @@ -40,7 +40,7 @@ For example: import { NextRequest, NextResponse } from 'next/server' export function middleware(request: NextRequest) { - const nonce = crypto.randomUUID() + const nonce = Buffer.from(crypto.randomUUID()).toString('base64') const cspHeader = ` default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; @@ -76,7 +76,7 @@ export function middleware(request: NextRequest) { import { NextResponse } from 'next/server' export function middleware(request) { - const nonce = crypto.randomUUID() + const nonce = Buffer.from(crypto.randomUUID()).toString('base64') const cspHeader = ` default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; 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 02/16] 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 f2e030e055fa3..0000000000000 --- 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 24b8a095d113e..2faaf385fa393 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 aa22928ece663..c6767579c1c44 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 61e99157470a2..cf7b406536247 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 03/16] 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 f54290791cdb9..4284a137212e9 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 04/16] 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 19acc69763d4a..967e25d5906d3 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 39368ecbda9b8..dd37943c6023c 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 40b39de435e8c..c0705d75727c5 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 bf8c14b930070..78d707f7e4365 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 5e95557924742..b263ea34f81ef 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 ee431062f7834..6dee8d6b152fb 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 2975b947fedc1..d81de7ff9d117 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 27a38c5c7f2e3..f8736c746bded 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 19808d2a42ee6..9bbe796d6a80d 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 63ffdaf3b8ef0..f05f95c9d7080 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 e6b62dd55494c..2dc3d9891c01b 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 154996666e17d..9252f2f5ebbd7 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 23e310684c26e..066239ce49c5c 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 a5d5ac891d2b8..bebc8aa2b209b 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 04836aa7c5a5b..934729edf8f1d 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 12830b5389eb7..0db14f16bc121 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 ece657014fca4..65f33b9a2789b 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 aea9761ebbc45..2ce02614924c0 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 05/16] perf: add bundled rendering runtimes (#52997) ## What? In Next, rendering a route involves 3 layers: - the routing layer, which will direct the request to the correct route to render - the rendering layer, which will take a route and render it appropriately - the user layer, which contains the user code In #51831, in order to optimise the boot time of Next.js, I introduced a change that allowed the routing layer to be bundled. In this PR, I'm doing the same for the rendering layer. This is building up on @wyattjoh's work that initially split the routing and the rendering layer into separate entry-points. The benefits of having this approach is that this allows us to compartmentalise the different part of Next, optimise them individually and making sure that serving a request is as efficient as possible, e.g. rendering a `pages` route should not need code from the `app router` to be used. There are now 4 different rendering runtimes, depending on the route type: - app pages: for App Router pages - app routes: for App Router route handlers - pages: for legacy pages - pages api: for legacy API routes This change should be transparent to the end user, beside faster cold boots. ## Notable changes Doing this change required a lot of changes for Next.js under the hood in order to make the different layers play well together. ### New conventions for externals/shared modules The big issue of bundling the rendering runtimes is that the user code needs to be able to reference an instance of a module/value created in Next during the render. This is the case when the user wants to access the router context during SSR via `next/link` for example; when you call `useContext(value)` the value needs to be the exact same reference to one as the one created by `createContext` earlier. Previously, we were handling this case by making all files from Next that were affected by this `externals`, meaning that we were marking them not to be bundled. **Why not keep it this way?** The goal of this PR as stated previously was to make the rendering process as efficient as possible, so I really wanted to avoid extraneous fs reads to unoptimised code. In order to "fix" it, I introduced two new conventions to the codebase: - all files that explicitly need to be shared between a rendering runtime and the user code must be suffixed by `.shared-runtime` and exposed via adding a reference in the relevant `externals` file. At compilation time, a reference to a file ending with this will get re-written to the appropriate runtime. - all files that need to be truly externals need to be suffixed by `.external`. At compilation time, a reference to it will stay as-is. This special case is needed mostly only for the async local storages that need to be shared with all three layers of Next. As a side effect, we should be bundling more of the Next code in the user bundles, so it should be slightly more efficient. ### App route handlers are compiled on their own layer App route handlers should be compiled in their own layer, this allows us to separate more cleanly the compilation logic here (we don't need to run the RSC logic for example). ### New rendering bundles We now generate a prod and a dev bundle for: - the routing server - the app/pages SSR rendering process - the API routes process The development bundle is needed because: - there is code in Next that relies on NODE_ENV - because we opt out of the logic referencing the correct rendering runtime in dev for a `shared-runtime` file. This is because we don't need to and that Turbopack does not support rewriting an external to something that looks like this `require('foo').bar.baz` yet. We will need to fix that when Turbopack build ships. ### New development pipeline Bundling Next is now required when developing on the repo so I extended the taskfile setup to account for that. The webpack config for Next itself lives in `webpack.config.js` and contains the logic for all the new bundles generated. ### Misc changes There are some misc reshuffling in the code to better use the tree shaking abilities that we can now use. fixes NEXT-1573 Co-authored-by: Alex Kirszenberg <1621758+alexkirsz@users.noreply.github.com> --- .eslintrc.json | 5 +- bench/basic-app/app/api/app/route.js | 5 + bench/basic-app/app/layout.js | 12 + bench/basic-app/app/page.js | 7 + bench/basic-app/next.config.js | 5 + bench/basic-app/pages/api/index.js | 3 + bench/basic-app/pages/pages/index.js | 7 + .../js/src/entry/app-edge-renderer.tsx | 2 + .../next-core/js/src/entry/app-renderer.tsx | 8 +- .../next-core/js/src/entry/app/hydrate.tsx | 2 +- .../js/src/internal/page-server-handler.tsx | 6 +- .../crates/next-core/src/app_source.rs | 17 +- .../crates/next-core/src/next_edge/context.rs | 7 +- .../src/next_edge/route_transition.rs | 16 +- .../crates/next-core/src/next_import_map.rs | 66 ++- .../next-core/src/next_server/context.rs | 16 +- .../next-core/src/next_shared/resolve.rs | 51 +- ...File(__q____q____q____star__0__-3e4dd8.txt | 17 + ...FileSync(__q____q____q____star_-e11df4.txt | 15 + .../Error during SSR Rendering-8ad1c9.txt | 47 ++ .../Error during SSR Rendering-d9114a.txt | 6 - .../next/import/conditions/input/app/test.js | 4 +- ...rror resolving commonjs request-b2593b.txt | 13 - ...rror resolving commonjs request-dd84e7.txt | 13 - ...File(__q____q____q____star__0__-76c34b.txt | 17 + ...FileSync(__q____q____q____star_-f7e52c.txt | 15 + packages/next/config.d.ts | 4 +- packages/next/config.js | 2 +- packages/next/package.json | 2 + packages/next/src/build/index.ts | 32 +- packages/next/src/build/templates/app-page.ts | 2 +- .../next/src/build/templates/app-route.ts | 3 +- .../next/src/build/templates/pages-api.ts | 3 +- packages/next/src/build/templates/pages.ts | 2 +- packages/next/src/build/utils.ts | 18 +- packages/next/src/build/webpack-config.ts | 118 +++-- .../plugins/next-types-plugin/index.ts | 9 +- packages/next/src/client/app-index.tsx | 4 +- packages/next/src/client/compat/router.ts | 2 +- ...ge.ts => action-async-storage.external.ts} | 0 .../next/src/client/components/app-router.tsx | 6 +- .../components/bailout-to-client-rendering.ts | 2 +- .../next/src/client/components/headers.ts | 5 +- .../src/client/components/layout-router.tsx | 4 +- .../next/src/client/components/navigation.ts | 8 +- .../internal/helpers/use-websocket.ts | 2 +- .../client/components/redirect-boundary.tsx | 2 +- .../next/src/client/components/redirect.ts | 2 +- .../render-from-template-context.tsx | 2 +- ...e.ts => request-async-storage.external.ts} | 0 .../router-reducer/apply-flight-data.ts | 5 +- .../create-initial-router-state.test.tsx | 5 +- .../create-initial-router-state.ts | 4 +- .../fill-cache-with-data-property.test.tsx | 5 +- .../fill-cache-with-data-property.ts | 5 +- .../fill-cache-with-new-subtree-data.test.tsx | 5 +- .../fill-cache-with-new-subtree-data.ts | 5 +- ...ll-lazy-items-till-leaf-with-head.test.tsx | 5 +- .../fill-lazy-items-till-leaf-with-head.ts | 5 +- ...te-cache-below-flight-segmentpath.test.tsx | 5 +- ...validate-cache-below-flight-segmentpath.ts | 2 +- .../invalidate-cache-by-router-state.test.tsx | 5 +- .../invalidate-cache-by-router-state.ts | 2 +- .../reducers/find-head-in-cache.test.tsx | 2 +- .../reducers/find-head-in-cache.ts | 2 +- .../reducers/navigate-reducer.test.tsx | 2 +- .../reducers/navigate-reducer.ts | 2 +- .../reducers/prefetch-reducer.test.tsx | 2 +- .../reducers/refresh-reducer.test.tsx | 2 +- .../reducers/refresh-reducer.ts | 2 +- .../reducers/restore-reducer.test.tsx | 2 +- .../reducers/server-action-reducer.ts | 2 +- .../reducers/server-patch-reducer.test.tsx | 2 +- .../router-reducer/router-reducer-types.ts | 2 +- ...atic-generation-async-storage.external.ts} | 0 .../components/static-generation-bailout.ts | 2 +- packages/next/src/client/image-component.tsx | 4 +- packages/next/src/client/index.tsx | 14 +- packages/next/src/client/legacy/image.tsx | 2 +- packages/next/src/client/link.tsx | 4 +- packages/next/src/client/router.ts | 2 +- packages/next/src/client/script.tsx | 2 +- packages/next/src/export/worker.ts | 14 +- packages/next/src/lib/chalk.ts | 2 +- packages/next/src/lib/constants.ts | 5 + packages/next/src/pages/_document.tsx | 7 +- .../src/server/app-render/action-handler.ts | 7 +- .../next/src/server/app-render/app-render.tsx | 17 +- .../next/src/server/app-render/entry-base.ts | 44 +- .../app-render/server-inserted-html.tsx | 2 +- .../request-async-storage-wrapper.ts | 2 +- ...static-generation-async-storage-wrapper.ts | 2 +- packages/next/src/server/base-server.ts | 8 +- .../next/src/server/dev/next-dev-server.ts | 2 +- .../src/server/dev/static-paths-worker.ts | 8 +- .../module-loader/node-module-loader.ts | 5 +- .../route-modules/app-page/module.compiled.ts | 11 + .../future/route-modules/app-page/module.ts | 5 + .../route-modules/app-page/shared-modules.ts | 13 + .../app-route/module.compiled.ts | 11 + .../future/route-modules/app-route/module.ts | 26 +- .../route-modules/app-route/shared-modules.ts | 3 + .../pages-api/module.compiled.ts | 11 + .../future/route-modules/pages-api/module.ts | 10 + .../route-modules/pages/module.compiled.ts | 11 + .../future/route-modules/pages/module.ts | 8 +- .../route-modules/pages/shared-modules.ts | 12 + .../future/route-modules/route-module.ts | 5 + .../src/server/lib/incremental-cache/index.ts | 5 +- packages/next/src/server/lib/patch-fetch.ts | 2 +- .../next/src/server/lib/server-ipc/index.ts | 1 + .../next/src/server/lib/trace/constants.ts | 1 - packages/next/src/server/load-components.ts | 35 +- .../server/load-default-error-components.ts | 78 +++ packages/next/src/server/next-server.ts | 339 +++++++------ packages/next/src/server/render-result.ts | 2 +- packages/next/src/server/render.tsx | 22 +- packages/next/src/server/require-hook.ts | 36 +- .../next/src/server/response-cache/index.ts | 5 +- .../next/src/server/response-cache/web.ts | 4 +- packages/next/src/server/web/adapter.ts | 2 +- .../adapters/request-cookies.ts | 2 +- .../web/spec-extension/revalidate-tag.ts | 2 +- .../web/spec-extension/unstable-cache.ts | 2 +- ...ntext.ts => amp-context.shared-runtime.ts} | 0 packages/next/src/shared/lib/amp.ts | 2 +- ...s => app-router-context.shared-runtime.ts} | 0 packages/next/src/shared/lib/dynamic.tsx | 2 +- ...=> head-manager-context.shared-runtime.ts} | 0 packages/next/src/shared/lib/head.tsx | 4 +- ...=> hooks-client-context.shared-runtime.ts} | 0 ...text.ts => html-context.shared-runtime.ts} | 0 ...=> image-config-context.shared-runtime.ts} | 0 ....ts => loadable-context.shared-runtime.ts} | 0 ...adable.tsx => loadable.shared-runtime.tsx} | 2 +- ...xt.ts => router-context.shared-runtime.ts} | 0 ...apters.tsx => adapters.shared-runtime.tsx} | 7 +- .../src/shared/lib/router/adapters.test.tsx | 2 +- ...ig.ts => runtime-config.shared-runtime.ts} | 0 ...> server-inserted-html.shared-runtime.tsx} | 0 packages/next/src/shared/lib/utils.ts | 2 +- packages/next/src/trace/index.ts | 3 +- packages/next/taskfile-webpack.js | 35 ++ packages/next/taskfile.js | 183 ++----- packages/next/webpack.config.js | 145 ++++++ pnpm-lock.yaml | 3 + scripts/minimal-server.js | 19 +- .../app-action-size-limit-invalid.test.ts | 2 +- .../e2e/getserversideprops/app/pages/index.js | 2 +- test/e2e/opentelemetry/opentelemetry.test.ts | 456 +++++++++--------- test/e2e/prerender-native-module.test.ts | 3 - test/e2e/prerender.test.ts | 5 - .../app/node_modules/comps/index.js | 5 - .../app/node_modules/comps/package.json | 6 - .../externalize-next-server/app/package.json | 6 - .../app/pages/index.js | 12 - .../test/index.test.js | 19 - .../jsconfig-baseurl/test/index.test.js | 6 - .../jsconfig-paths/test/index.test.js | 7 +- 159 files changed, 1504 insertions(+), 949 deletions(-) create mode 100644 bench/basic-app/app/api/app/route.js create mode 100644 bench/basic-app/app/layout.js create mode 100644 bench/basic-app/app/page.js create mode 100644 bench/basic-app/next.config.js create mode 100644 bench/basic-app/pages/api/index.js create mode 100644 bench/basic-app/pages/pages/index.js create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt delete mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt create mode 100644 packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt rename packages/next/src/client/components/{action-async-storage.ts => action-async-storage.external.ts} (100%) rename packages/next/src/client/components/{request-async-storage.ts => request-async-storage.external.ts} (100%) rename packages/next/src/client/components/{static-generation-async-storage.ts => static-generation-async-storage.external.ts} (100%) create mode 100644 packages/next/src/server/future/route-modules/app-page/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/app-page/shared-modules.ts create mode 100644 packages/next/src/server/future/route-modules/app-route/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/app-route/shared-modules.ts create mode 100644 packages/next/src/server/future/route-modules/pages-api/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/pages/module.compiled.ts create mode 100644 packages/next/src/server/future/route-modules/pages/shared-modules.ts create mode 100644 packages/next/src/server/load-default-error-components.ts rename packages/next/src/shared/lib/{amp-context.ts => amp-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{app-router-context.ts => app-router-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{head-manager-context.ts => head-manager-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{hooks-client-context.ts => hooks-client-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{html-context.ts => html-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{image-config-context.ts => image-config-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{loadable-context.ts => loadable-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{loadable.tsx => loadable.shared-runtime.tsx} (99%) rename packages/next/src/shared/lib/{router-context.ts => router-context.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/router/{adapters.tsx => adapters.shared-runtime.tsx} (95%) rename packages/next/src/shared/lib/{runtime-config.ts => runtime-config.shared-runtime.ts} (100%) rename packages/next/src/shared/lib/{server-inserted-html.tsx => server-inserted-html.shared-runtime.tsx} (100%) create mode 100644 packages/next/taskfile-webpack.js create mode 100644 packages/next/webpack.config.js delete mode 100644 test/integration/externalize-next-server/app/node_modules/comps/index.js delete mode 100644 test/integration/externalize-next-server/app/node_modules/comps/package.json delete mode 100644 test/integration/externalize-next-server/app/package.json delete mode 100644 test/integration/externalize-next-server/app/pages/index.js delete mode 100644 test/integration/externalize-next-server/test/index.test.js diff --git a/.eslintrc.json b/.eslintrc.json index 8c46d8c02736f..feb49b2ecbbff 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,10 @@ }, { "files": ["packages/**"], - "excludedFiles": ["packages/next/taskfile.js"], + "excludedFiles": [ + "packages/next/taskfile*.js", + "packages/next/webpack.config.js" + ], "rules": { "no-shadow": ["warn", { "builtinGlobals": false }], "import/no-extraneous-dependencies": [ diff --git a/bench/basic-app/app/api/app/route.js b/bench/basic-app/app/api/app/route.js new file mode 100644 index 0000000000000..944ba5a8e827f --- /dev/null +++ b/bench/basic-app/app/api/app/route.js @@ -0,0 +1,5 @@ +export function GET() { + return { name: 'John Doe' } +} + +export const dynamic = 'force-dynamic' diff --git a/bench/basic-app/app/layout.js b/bench/basic-app/app/layout.js new file mode 100644 index 0000000000000..8ebf54889577d --- /dev/null +++ b/bench/basic-app/app/layout.js @@ -0,0 +1,12 @@ +import React from 'react' + +export default function Layout({ children }) { + return ( + + + My App + + {children} + + ) +} diff --git a/bench/basic-app/app/page.js b/bench/basic-app/app/page.js new file mode 100644 index 0000000000000..83dc3aa56c9a0 --- /dev/null +++ b/bench/basic-app/app/page.js @@ -0,0 +1,7 @@ +import React from 'react' + +export default function Page() { + return

My Page

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

Hello {typeof window}

- - -) - -Page.getInitialProps = () => ({}) - -export default Page diff --git a/test/integration/externalize-next-server/test/index.test.js b/test/integration/externalize-next-server/test/index.test.js deleted file mode 100644 index bba968de16585..0000000000000 --- a/test/integration/externalize-next-server/test/index.test.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-env jest */ -import path from 'path' -import { nextBuild, readNextBuildServerPageFile } from 'next-test-utils' - -const appDir = path.join(__dirname, '../app') - -describe('externalize next/dist/shared', () => { - beforeAll(async () => { - await nextBuild(appDir) - }) - - it('Bundle next/dist/shared/lib/head.js but not next/dist/shared/lib/head-manager-context.js in _error', async () => { - const content = readNextBuildServerPageFile(appDir, '/_error') - expect(content).toContain( - `require("next/dist/shared/lib/head-manager-context.js")` - ) - expect(content).not.toContain(`require("next/dist/shared/lib/head.js")`) - }) -}) diff --git a/test/integration/jsconfig-baseurl/test/index.test.js b/test/integration/jsconfig-baseurl/test/index.test.js index 91f084ce1d55e..4ad014d0e8f03 100644 --- a/test/integration/jsconfig-baseurl/test/index.test.js +++ b/test/integration/jsconfig-baseurl/test/index.test.js @@ -72,12 +72,6 @@ describe('TypeScript Features', () => { const helloTrace = await fs.readJSON( join(appDir, '.next/server/pages/hello.js.nft.json') ) - const appTrace = await fs.readJSON( - join(appDir, '.next/server/pages/_app.js.nft.json') - ) - expect( - appTrace.files.some((file) => file.includes('node_modules/next')) - ).toBe(true) expect( helloTrace.files.some((file) => file.includes('components/world.js')) ).toBe(false) diff --git a/test/integration/jsconfig-paths/test/index.test.js b/test/integration/jsconfig-paths/test/index.test.js index 77c84e7edfbb4..bc3c635b9ce96 100644 --- a/test/integration/jsconfig-paths/test/index.test.js +++ b/test/integration/jsconfig-paths/test/index.test.js @@ -89,9 +89,6 @@ function runTests() { await nextBuild(appDir) }) it('should trace correctly', async () => { - const appTrace = await fs.readJSON( - join(appDir, '.next/server/pages/_app.js.nft.json') - ) const singleAliasTrace = await fs.readJSON( join(appDir, '.next/server/pages/single-alias.js.nft.json') ) @@ -107,9 +104,7 @@ function runTests() { const basicAliasTrace = await fs.readJSON( join(appDir, '.next/server/pages/basic-alias.js.nft.json') ) - expect( - appTrace.files.some((file) => file.includes('node_modules/next')) - ).toBe(true) + expect( singleAliasTrace.files.some((file) => file.includes('components/hello.js') From 3062462156f29a29718d0f4c760402f520c4e473 Mon Sep 17 00:00:00 2001 From: vercel-release-bot Date: Thu, 7 Sep 2023 16:06:41 +0000 Subject: [PATCH 06/16] 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 967e25d5906d3..9473ac4b6bb84 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 dd37943c6023c..cca799bdb09c9 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 c0705d75727c5..8e028a9ab6d75 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 78d707f7e4365..61d7b0aa5012f 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 b263ea34f81ef..590100e487387 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 6dee8d6b152fb..11e767e1d13e3 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 d81de7ff9d117..42ac260ab258e 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 f8736c746bded..dbac7a4a5d26e 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 9bbe796d6a80d..75739c7731f17 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 f05f95c9d7080..1af2a0530ad19 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 2dc3d9891c01b..f333bb9010080 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 9252f2f5ebbd7..c7b23dcd1e426 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 066239ce49c5c..87ac07c84398a 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 75b2e78962ffc..b80b6eb520902 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 934729edf8f1d..cc6d2a5b422a3 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 0db14f16bc121..20e4acbdcca74 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 65f33b9a2789b..7677d8733511c 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 c0bf75436a03f..8478bb10eb18c 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 07/16] 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 12ed8e5498879..295b57369a6d3 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 973b30ea7d548..9c7271ecdd66d 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 5b50aff1185cf..38bcb02a3cda6 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 738f5a4bf938f..4265f52139b4b 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 c09ccdc644c98..a3597079af6c0 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 0f4df5eb01f39..b72fc9af399ab 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 bde79e2c38b4d..fd99d25664678 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 45cf5470f40e1..e60c963e0ece0 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 7f40ca34fb94c..885ecdf4cd715 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 08/16] 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 d442b87fff858..270580f1f81a7 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 2faaf385fa393..cf17fb2d7d9e6 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 0000000000000..60418c58cba43 --- /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 0000000000000..a25d8e8adad2d --- /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 0000000000000..32a015bd37ca9 --- /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 0000000000000..3c8a96c601f40 --- /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 0000000000000..83ef2115f5326 --- /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 09/16] 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 2de2ef61939c7..3b28ea4bab515 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 5582290ae2d4c..21a75dfea9279 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 a10e864117372..f7c18306acf89 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 b80b6eb520902..30ee061047fad 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 0bdf3d36ef5d3..853a1a2db7873 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 18fa7158c5adc..86b09e3616a83 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 59e05adbe1d27..01235b6a1bf8f 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 8478bb10eb18c..68790eaa3a0aa 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 fdb648b46ee10..5102737198a85 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 10/16] 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 2c3c541e02b8e..a8de967825e16 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 4ed01fed08c83..fcde9b963e9fd 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 9ed54e8994d2e..bbf36b1ec538c 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 0540c02079cb1..9e7035dc7e819 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 defbb657c7c42..d9c641c4045b7 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 0000000000000..9a335e2d0995c --- /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 67beabf483fe3..b3e1cf19c4261 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 0000000000000..ad37357cb6dcf --- /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 11/16] 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 feb49b2ecbbff..8c46d8c02736f 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 944ba5a8e827f..0000000000000 --- 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 8ebf54889577d..0000000000000 --- 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 83dc3aa56c9a0..0000000000000 --- 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 0957c472383fa..0000000000000 --- 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 8f603094bd288..0000000000000 --- 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 e06229eee0637..0000000000000 --- 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 38f27b3c1c7a3..fdec9ffc360fe 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 25437fec08ef4..951f91570d92d 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 c4e1bce96aa5e..abdf23d0fd7d3 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 cecafb35f77a7..6182b2d7d4539 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 4265f52139b4b..cf35f3795f884 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 cfe9eebf19f94..54c7dd3331a63 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 05326de255203..de0bd2f2e7bf7 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 c530f3bcb9290..e05ac926c139f 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 1d242498b36df..e7485ff100ade 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 ea7f044c12cc8..013f87f9fd5b6 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 a5ad94c85fb0a..0000000000000 --- 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 c80db085946c2..0000000000000 --- 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 39e97b4ccc85e..0000000000000 --- 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 0000000000000..ce775cb8df7e8 --- /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 108b763da879c..e2c2f40a06dd7 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 0000000000000..72c048d7b6481 --- /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 0000000000000..72c048d7b6481 --- /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 a5ad94c85fb0a..0000000000000 --- 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 c80db085946c2..0000000000000 --- 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 2da1ee3c4029c..78fe148a8dc9b 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 6510748638097..2da980d8b0065 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 30ee061047fad..28fbd949a8972 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 b0e0870ca1392..8ad7407b4be45 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 f0d2ab692e2aa..c75509904c3a8 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 b4b8e5b0fe6cd..50a8b6165a747 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 eaeec836cb61e..a48822f9ed75a 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 b5def5d13c552..3f3527e6650d6 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 423beb27f6dca..867429c2ba901 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 d1b7487618fe2..f1f053d05eb9b 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 c2b1089df5b3f..2be12daecdbc6 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 47c0bd13f369e..8f82d244837e6 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 e9143c4117bd7..58b1b9f02ed05 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 a8de967825e16..db181118a674a 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 799398b5f300c..76356e05304a8 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 a0a27a184cbfe..d090264391e7c 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 1f0ffff7e2de8..3e410a93fb0ca 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 b3d69dcb065e8..bf6a56100080d 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 d37fce9851e91..4d92a279c3ed5 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 8d407fd6e9d6e..23e5493ae83fb 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 b9a2cfebd883f..10e72bc1ccbef 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 c1755cc5056bf..be486842c4f33 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 003d0a5cde9e4..e7a2f11a84f48 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 414b553c63249..a6052636ef256 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 94fdabb9b577a..7f7cca2003b0b 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 648069ea76986..28f8c3412ab3a 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 42df61a952af5..81df295dba302 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 ac888a3ede0ff..187f86a478751 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 7e9a93699fb65..5d48eaee9ef9f 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 1edbeffd7b3e9..606440a96f9c9 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 f558edfab2f1e..c5ddedd52351e 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 8c23c47d42d74..915f09cae0cae 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 d637d850b145a..ac343f8d79679 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 bdd819b0614d9..65ce9e42c05ee 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 1ec39ae9e35fd..820e5909bf031 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 2d4cdef348b1e..807374c855577 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 08dcefc65f2ce..f4d5e768b9808 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 fcde9b963e9fd..db40adfa3c5b1 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 e47c42b2aa60a..fe8a4a24c4b8b 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 bbbee6ff5f2a4..8055123367a94 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 bbf36b1ec538c..90ce7dc9423a3 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 bd6dfc4ef9047..cd87ef3802b00 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 36c978926517f..b11f39b141ccf 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 0c6caaba746ca..3b8fa6acb4013 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 9e7035dc7e819..db72a92fce743 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 d9c641c4045b7..284628dbf9098 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 4d35150664251..c5072218f035c 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 321b07ecd0a5f..3f2183c004b10 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 0f8a95c93746e..f7c163f020d31 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 07ec2e217c200..d1456477bacf6 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 7a15dee249e26..94226c8caa5c0 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 f43126691e5e5..342ecb623df74 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 f695e691c482d..a4b2c6dcd3184 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 b9fb7b9afd60a..31f58755996a4 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 d0939d9148b97..8e40472953f8f 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 46f85311a9f0a..fc94397c6aca7 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 f0a29cb15d1fc..a8526011c5d33 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 9f6059ec48f5a..6b8bc3228a005 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 1dd2eb8f7c639..30749ac66a83d 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 0cc53e0214d86..6bc5fd7e7ace1 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 764dc62792077..f044c24feaba3 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 1376ecfb197cd..50795855c53d4 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 d5adfc9de38b4..a28ac0e8ecb2a 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 fab999db35ddc..d826f2bdd4e04 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 839fa1b13c0e4..1eca11b7358cb 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 ddd6526e52f23..68932df7a36b0 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 6f70685df7e75..3283eb00a53f5 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 78601739acbe5..0000000000000 --- 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 daa0291a1c8b4..418e37420d7e9 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 e986c1bad3894..0000000000000 --- 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 f5909104bc772..0000000000000 --- 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 d1d36d31501e4..bf47738d35fea 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 e6139d5a69404..0000000000000 --- 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 ed74c41adb918..0000000000000 --- 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 976daeeec4a87..88dbda73b464c 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 a935b62abdcad..0000000000000 --- 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 e2730ef668901..dac8ae5546441 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 55cdfbdeca37c..0000000000000 --- 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 a8e5dd6c5945a..52188ed506dff 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 3032aa685300b..ae2b11b4dc00f 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 080361704b718..fb4d02c97b251 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 023cbde2d25d8..8e7bd60a8855d 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 1b45358bdf8ed..50eb4528c7dec 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 39da802ff39b1..d2b563493ebe6 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 c390e9180b3d9..0000000000000 --- 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 7abf3605541ad..087752225090f 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 2a8252e4fd143..b93dfce5fae21 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 fe36e793348e2..ac0989082aa9b 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 9b2e1526eec0e..1f53b3b479109 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 7bd6710cc4a80..6d135da26e939 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 f255fdd5412d4..e37ccca314812 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 5c6ad3e7ed5ab..1ea32a75956cd 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 d44ea986cad65..df3c369eef877 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 7c7bff8c2f784..8d7cd68bd3a9a 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 1e85b5c290971..aad3ed2baf20a 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 8edd21db9c299..04518b2389357 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 390410edda29e..cb497fc587d4f 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 42f95767bfa4c..3156e259f656f 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 82ba84182701f..1592d98551093 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 e47ce2174dd35..fa8e48f2fc088 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 29f92dda8dc08..ce68a8bec1e8b 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 4e8036a6894a2..f967be459022b 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 e3928f775f613..e242e19c9041f 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 04495d7b3621c..0000000000000 --- 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 321490234153b..f38289c3d4984 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 8e7c3063fa5ed..0000000000000 --- 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 68790eaa3a0aa..50f7630acfd82 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 08f9125a4f0cc..f4f30ac97ce25 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 3e5e6b82ea050..a61e222c3872e 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 da17edc01839d..4433c9c2ee84e 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 13e0fefea2787..ae798ec7c5c2d 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 34adb36ce5bd8..c23b1d2d05cd0 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 1ff677e583b15..f21b4224fe236 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 0000000000000..74c3153f1b835 --- /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 0000000000000..6e665b646a6ad --- /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 0000000000000..c5bd706a3a950 --- /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 0000000000000..9ceb7bee3db17 --- /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 0000000000000..bba968de16585 --- /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 4ad014d0e8f03..91f084ce1d55e 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 bc3c635b9ce96..77c84e7edfbb4 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 12/16] 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 d826f2bdd4e04..bacb1cc38e4ff 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 1ae559ea09db8..1e712b16379a4 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 087752225090f..0fe80339d1a0f 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 a25d8e8adad2d..1231f78f02670 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', () => { From 904d8eed3958be3a0c4167a858f06029aac42c65 Mon Sep 17 00:00:00 2001 From: vercel-release-bot Date: Thu, 7 Sep 2023 23:18:04 +0000 Subject: [PATCH 13/16] v13.4.20-canary.21 --- 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 9473ac4b6bb84..a589ef7b023c6 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "13.4.20-canary.20" + "version": "13.4.20-canary.21" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index cca799bdb09c9..93dd0606ab6e7 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.20", + "version": "13.4.20-canary.21", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 8e028a9ab6d75..d8032af352f66 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.20", + "version": "13.4.20-canary.21", "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.20", + "@next/eslint-plugin-next": "13.4.20-canary.21", "@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 61d7b0aa5012f..2d2c6da7a230c 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.20", + "version": "13.4.20-canary.21", "description": "ESLint plugin for NextJS.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index 590100e487387..20b38c3da5458 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "13.4.20-canary.20", + "version": "13.4.20-canary.21", "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 11e767e1d13e3..ba98bb5b04259 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.20", + "version": "13.4.20-canary.21", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 42ac260ab258e..60c2888bcfe50 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.20", + "version": "13.4.20-canary.21", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index dbac7a4a5d26e..38d1e9f40a0bd 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.20", + "version": "13.4.20-canary.21", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 75739c7731f17..01daaf6790025 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.20", + "version": "13.4.20-canary.21", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 1af2a0530ad19..fd6347ce0ca81 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.20", + "version": "13.4.20-canary.21", "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 f333bb9010080..d7bb163c02bc2 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.20", + "version": "13.4.20-canary.21", "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 c7b23dcd1e426..63889fce9a931 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.20", + "version": "13.4.20-canary.21", "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 87ac07c84398a..47906ad8c2f6c 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.20", + "version": "13.4.20-canary.21", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index 28fbd949a8972..058280c68b63f 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "13.4.20-canary.20", + "version": "13.4.20-canary.21", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -89,7 +89,7 @@ ] }, "dependencies": { - "@next/env": "13.4.20-canary.20", + "@next/env": "13.4.20-canary.21", "@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.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", + "@next/polyfill-module": "13.4.20-canary.21", + "@next/polyfill-nomodule": "13.4.20-canary.21", + "@next/react-dev-overlay": "13.4.20-canary.21", + "@next/react-refresh-utils": "13.4.20-canary.21", + "@next/swc": "13.4.20-canary.21", "@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 cc6d2a5b422a3..d760b0ff82191 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.20", + "version": "13.4.20-canary.21", "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 20e4acbdcca74..2db27f44e1e96 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.20", + "version": "13.4.20-canary.21", "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 7677d8733511c..4e69590dcd618 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.20", + "version": "13.4.20-canary.21", "private": true, "repository": { "url": "vercel/next.js", @@ -23,7 +23,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "13.4.20-canary.20", + "next": "13.4.20-canary.21", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50f7630acfd82..ecefb2db79ec1 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.20 + specifier: 13.4.20-canary.21 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.20 + specifier: 13.4.20-canary.21 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.20 + specifier: 13.4.20-canary.21 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 13.4.20-canary.20 + specifier: 13.4.20-canary.21 version: link:../next-polyfill-nomodule '@next/react-dev-overlay': - specifier: 13.4.20-canary.20 + specifier: 13.4.20-canary.21 version: link:../react-dev-overlay '@next/react-refresh-utils': - specifier: 13.4.20-canary.20 + specifier: 13.4.20-canary.21 version: link:../react-refresh-utils '@next/swc': - specifier: 13.4.20-canary.20 + specifier: 13.4.20-canary.21 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.20 + specifier: 13.4.20-canary.21 version: link:../next outdent: specifier: 0.8.0 From 8076d0c68a327869b088559587565708c75f7b71 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 8 Sep 2023 01:58:09 +0200 Subject: [PATCH 14/16] Add option to support log full request url in verbose mode (#55111) Fixes #52239 Closes NEXT-1604 Change the logging option to object for flexibility ```js experimental: { logging: { level: 'verbose', // control log level fullUrl: true, // console fetching url logging } } ``` ### After vs Before --- packages/next/src/server/config-schema.ts | 10 +++++- packages/next/src/server/config-shared.ts | 5 ++- packages/next/src/server/next-server.ts | 36 +++++++++++-------- .../app-static/app-fetch-logging.test.ts | 7 ++-- test/e2e/app-dir/app-static/next.config.js | 4 ++- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index 31cb19a7b2649..5b7b5ae9f1b58 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -501,7 +501,15 @@ const configSchema = { }, }, logging: { - type: 'string', + type: 'object', + properties: { + level: { + type: 'string', + }, + fullUrl: { + type: 'boolean', + }, + }, }, serverMinification: { type: 'boolean', diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 94ef73429f416..a44d576098143 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -149,7 +149,10 @@ export interface ExperimentalConfig { useDeploymentId?: boolean useDeploymentIdServerActions?: boolean deploymentId?: string - logging?: 'verbose' + logging?: { + level?: 'verbose' + fullUrl?: false + } appDocumentPreloading?: boolean strictNextHead?: boolean clientRouterFilter?: boolean diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 0fe80339d1a0f..d6ad6edaea41d 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -108,6 +108,12 @@ function writeStdoutLine(text: string) { process.stdout.write(' ' + text + '\n') } +function formatRequestUrl(url: string, maxLength: number | undefined) { + return maxLength !== undefined && url.length > maxLength + ? url.substring(0, maxLength) + '..' + : url +} + export interface NodeRequestHandler { ( req: IncomingMessage | BaseNextRequest, @@ -1010,7 +1016,9 @@ export default class NextNodeServer extends BaseServer { const normalizedRes = this.normalizeRes(res) const enabledVerboseLogging = - this.nextConfig.experimental.logging === 'verbose' + this.nextConfig.experimental.logging?.level === 'verbose' + const shouldTruncateUrl = !this.nextConfig.experimental.logging?.fullUrl + if (this.renderOpts.dev) { const _req = req as NodeNextRequest | IncomingMessage const _res = res as NodeNextResponse | ServerResponse @@ -1098,20 +1106,18 @@ export default class NextNodeServer extends BaseServer { if (url.length > 48) { const parsed = new URL(url) - const truncatedHost = - parsed.host.length > 16 - ? parsed.host.substring(0, 16) + '..' - : parsed.host - - const truncatedPath = - parsed.pathname.length > 24 - ? parsed.pathname.substring(0, 24) + '..' - : parsed.pathname - - const truncatedSearch = - parsed.search.length > 16 - ? parsed.search.substring(0, 16) + '..' - : parsed.search + const truncatedHost = formatRequestUrl( + parsed.host, + shouldTruncateUrl ? 16 : undefined + ) + const truncatedPath = formatRequestUrl( + parsed.pathname, + shouldTruncateUrl ? 24 : undefined + ) + const truncatedSearch = formatRequestUrl( + parsed.search, + shouldTruncateUrl ? 16 : undefined + ) url = parsed.protocol + diff --git a/test/e2e/app-dir/app-static/app-fetch-logging.test.ts b/test/e2e/app-dir/app-static/app-fetch-logging.test.ts index 7ce5f33e6732f..44b0888499df8 100644 --- a/test/e2e/app-dir/app-static/app-fetch-logging.test.ts +++ b/test/e2e/app-dir/app-static/app-fetch-logging.test.ts @@ -32,7 +32,6 @@ function parseLogsFromCli(cliOutput: string) { } parsedLogs.push(parsedLog) } - // console.log('parsedLogs', parsedLogs) return parsedLogs }, [] as any[]) } @@ -46,7 +45,7 @@ createNextDescribe( 'app/default-cache/page.js': new FileRef( path.join(__dirname, 'app/default-cache/page.js') ), - 'next.config.js': `module.exports = { experimental: { logging: 'verbose' } }`, + 'next.config.js': `module.exports = { experimental: { logging: { level: 'verbose', fullUrl: true } } }`, }, }, ({ next, isNextDev }) => { @@ -81,6 +80,9 @@ createNextDescribe( log.url.includes('api/random?no-cache') ) + // expend full url + expect(logs.every((log) => log.url.includes('..'))).toBe(false) + if (logEntry?.cache === 'cache: no-cache') { return 'success' } @@ -115,7 +117,6 @@ createNextDescribe( log.url.includes('api/random?auto-cache') ) - console.log('logEntry?.cache', logEntry?.cache) if (logEntry?.cache === 'cache-control: no-cache (hard refresh)') { return 'success' } diff --git a/test/e2e/app-dir/app-static/next.config.js b/test/e2e/app-dir/app-static/next.config.js index a575f6b793e2f..8a101145f40b9 100644 --- a/test/e2e/app-dir/app-static/next.config.js +++ b/test/e2e/app-dir/app-static/next.config.js @@ -1,7 +1,9 @@ /** @type {import('next').NextConfig} */ module.exports = { experimental: { - logging: 'verbose', + logging: { + level: 'verbose', + }, incrementalCacheHandlerPath: process.env.CUSTOM_CACHE_HANDLER, }, From 60d3ab3e47ac564d0f1d210afe6f06f6308ab5cd Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 8 Sep 2023 02:06:05 +0200 Subject: [PATCH 15/16] Filter out pages tree view for app dir only output (#55120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only output app routes if there's no pages routes in next build output For hello world app you'll only see app dir routes ``` Route (app) Size First Load JS + First Load JS shared by all 78.1 kB ├ chunks/main-app-e4c0616da69beffe.js 76.5 kB └ chunks/webpack-bf1a64d1eafd2816.js 1.61 kB ○ (Static) automatically rendered as static HTML (uses no initial props) ``` Also filter out `/favicon.ico` static route as it's confusing --- packages/next/src/build/utils.ts | 48 +++++++++++++------ .../e2e/app-dir/not-found/basic/index.test.ts | 2 +- .../app-dir/build-output/index.test.ts | 23 +++++++++ 3 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 test/production/app-dir/build-output/index.test.ts diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index 867429c2ba901..256ca7f6ab372 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -295,6 +295,31 @@ export function isInstrumentationHookFilename(file?: string) { ) } +const filterAndSortList = ( + list: ReadonlyArray, + routeType: ROUTER_TYPE, + hasCustomApp: boolean +) => { + let pages: string[] + if (routeType === 'app') { + // filter out static app route of /favicon.ico + pages = list.filter((e) => e !== '/favicon.ico') + } else { + // filter built-in pages + pages = list + .slice() + .filter( + (e) => + !( + e === '/_document' || + e === '/_error' || + (!hasCustomApp && e === '/_app') + ) + ) + } + return pages.sort((a, b) => a.localeCompare(b)) +} + export interface PageInfo { isHybridAmp?: boolean size: number @@ -367,21 +392,9 @@ export async function printTreeView( .replace(/(?:^|[.-])([0-9a-z]{6})[0-9a-z]{14}(?=\.)/, '.$1') // Check if we have a custom app. - const hasCustomApp = + const hasCustomApp = !!( pagesDir && (await findPageFile(pagesDir, '/_app', pageExtensions, false)) - - const filterAndSortList = (list: ReadonlyArray) => - list - .slice() - .filter( - (e) => - !( - e === '/_document' || - e === '/_error' || - (!hasCustomApp && e === '/_app') - ) - ) - .sort((a, b) => a.localeCompare(b)) + ) // Collect all the symbols we use so we can print the icons out. const usedSymbols = new Set() @@ -402,6 +415,11 @@ export async function printTreeView( list: ReadonlyArray routerType: ROUTER_TYPE }) => { + const filteredPages = filterAndSortList(list, routerType, hasCustomApp) + if (filteredPages.length === 0) { + return + } + messages.push( [ routerType === 'app' ? 'Route (app)' : 'Route (pages)', @@ -410,7 +428,7 @@ export async function printTreeView( ].map((entry) => chalk.underline(entry)) as [string, string, string] ) - filterAndSortList(list).forEach((item, i, arr) => { + filteredPages.forEach((item, i, arr) => { const border = i === 0 ? arr.length === 1 diff --git a/test/e2e/app-dir/not-found/basic/index.test.ts b/test/e2e/app-dir/not-found/basic/index.test.ts index e0ee2ff292dd3..3ede15a1cea9c 100644 --- a/test/e2e/app-dir/not-found/basic/index.test.ts +++ b/test/e2e/app-dir/not-found/basic/index.test.ts @@ -23,7 +23,7 @@ createNextDescribe( it('should not output /404 in tree view logs', async () => { const output = await next.cliOutput - expect(output).not.toContain('/404') + expect(output).not.toContain('○ /404') }) it('should use root not-found content for 404 html', async () => { diff --git a/test/production/app-dir/build-output/index.test.ts b/test/production/app-dir/build-output/index.test.ts new file mode 100644 index 0000000000000..f8d092c54172d --- /dev/null +++ b/test/production/app-dir/build-output/index.test.ts @@ -0,0 +1,23 @@ +import { createNextDescribe } from 'e2e-utils' + +createNextDescribe( + 'production - app dir - build output', + { + files: { + 'app/page.js': `export default function Page() { return null }`, + 'app/layout.js': `export default function Layout({ children }) { + return ( + {children} + ) + }`, + }, + }, + ({ next }) => { + it('should only log app routes', async () => { + const output = next.cliOutput + expect(output).toContain('Route (app)') + expect(output).not.toContain('Route (pages)') + expect(output).not.toContain('/favicon.ico') + }) + } +) From 56eeedff30500f59a7bece7c4400759064a77d3e Mon Sep 17 00:00:00 2001 From: Siddharth Gaikwad <48375212+sidgaikwad@users.noreply.github.com> Date: Fri, 8 Sep 2023 05:49:49 +0530 Subject: [PATCH 16/16] Update pull_request_template.md (#55098) just added the ")" to the documentation --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e7a876f2bdcb1..5d1d5a8db8e0c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -24,7 +24,7 @@ Choose the right checklist for the change(s) that you're making: - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` -- e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs +- e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md