From a803638da2483f0c468522d5da1037657af53e05 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Tue, 1 Feb 2022 22:46:07 +0100 Subject: [PATCH 1/7] fix problem with HMR when middleware and page reference the same node_module (#33873) fixes https://github.com/vercel/next.js/issues/31827 ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- packages/next/build/webpack-config.ts | 12 ------------ .../hmr/node_modules/shared-package/index.js | 1 + .../hmr/node_modules/shared-package/package.json | 3 +++ .../middleware/hmr/pages/about/_middleware.js | 2 ++ test/integration/middleware/hmr/pages/index.js | 3 +++ 5 files changed, 9 insertions(+), 12 deletions(-) create mode 100644 test/integration/middleware/hmr/node_modules/shared-package/index.js create mode 100644 test/integration/middleware/hmr/node_modules/shared-package/package.json diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 647194bfbef86..2c62c2d34243c 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -959,11 +959,6 @@ export default async function getBaseWebpackConfig( }, } - const nonUserCondition = { - include: /node_modules/, - exclude: babelIncludeRegexes, - } - let webpackConfig: webpack.Configuration = { parallelism: Number(process.env.NEXT_WEBPACK_PARALLELISM) || undefined, externals: targetWeb @@ -1256,13 +1251,6 @@ export default async function getBaseWebpackConfig( }, ], }, - { - ...nonUserCondition, - // Make all non-user modules to be compiled in a single layer - // This avoids compiling them mutliple times and avoids module id changes - issuerLayer: 'middleware', - layer: '', - }, ...(!config.images.disableStaticImages ? [ { diff --git a/test/integration/middleware/hmr/node_modules/shared-package/index.js b/test/integration/middleware/hmr/node_modules/shared-package/index.js new file mode 100644 index 0000000000000..a8653a9c9264c --- /dev/null +++ b/test/integration/middleware/hmr/node_modules/shared-package/index.js @@ -0,0 +1 @@ +module.exports = 42 diff --git a/test/integration/middleware/hmr/node_modules/shared-package/package.json b/test/integration/middleware/hmr/node_modules/shared-package/package.json new file mode 100644 index 0000000000000..1587a669681c9 --- /dev/null +++ b/test/integration/middleware/hmr/node_modules/shared-package/package.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} diff --git a/test/integration/middleware/hmr/pages/about/_middleware.js b/test/integration/middleware/hmr/pages/about/_middleware.js index 66902bd191d7f..3146e1d20277e 100644 --- a/test/integration/middleware/hmr/pages/about/_middleware.js +++ b/test/integration/middleware/hmr/pages/about/_middleware.js @@ -1,5 +1,7 @@ import { NextResponse } from 'next/server' +import magicValue from 'shared-package' export function middleware(request) { + if (magicValue !== 42) throw new Error('shared-package problem') return NextResponse.rewrite(new URL('/about/a', request.url)) } diff --git a/test/integration/middleware/hmr/pages/index.js b/test/integration/middleware/hmr/pages/index.js index 36f47d6337750..fa99cf9c2eb8b 100644 --- a/test/integration/middleware/hmr/pages/index.js +++ b/test/integration/middleware/hmr/pages/index.js @@ -1,3 +1,6 @@ +import magicValue from 'shared-package' +if (magicValue !== 42) throw new Error('shared-package problem') + export default function Home() { return (
From d432032bbfeebf38660aef61a3dac5cb88ca18c9 Mon Sep 17 00:00:00 2001 From: Jack Hanford Date: Tue, 1 Feb 2022 14:03:16 -0800 Subject: [PATCH 2/7] Correct pluralization in newly added Relay documentation (#33880) * Correct pluralization * Apply suggestions from code review Co-authored-by: JJ Kasper --- docs/advanced-features/compiler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-features/compiler.md b/docs/advanced-features/compiler.md index 7e192389688e6..9b59265a84ed4 100644 --- a/docs/advanced-features/compiler.md +++ b/docs/advanced-features/compiler.md @@ -112,7 +112,7 @@ module.exports = { } ``` -NOTE: In Next.js all JavaScripts files in `pages` directory are considered routes. So, for `relay-compiler` you'll need to specify `artifactDirectory` configuration settings outside of the `pages`, otherwise `relay-compiler` will generate files next to the source file in the `__generated__` directory, and this file will be considered a route, which will break production build. +NOTE: In Next.js all JavaScript files in `pages` directory are considered routes. So, for `relay-compiler` you'll need to specify `artifactDirectory` configuration settings outside of the `pages`, otherwise `relay-compiler` will generate files next to the source file in the `__generated__` directory, and this file will be considered a route, which will break production builds. ### Remove React Properties From 51f0359222e0decc6116e4fca90d416bf0640327 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Tue, 1 Feb 2022 23:36:47 +0100 Subject: [PATCH 3/7] Refactor page component getter in web server (#33759) Cleans up the code of the middleware SSR loader and the web server. Currently the page components and render options are provided to the server via `globalThis` which is not ideal. Instead we can inject `extendRenderOpts` and `loadComponent` to the web server. Since this is the the minimal mode and we'll need to handle `?flight` requests, we update the server render opts upon `updateRenderOpts` (In the future this should be changed to be passed to `requestHandler` to avoid race conditions). Currently, we can't fully get rid of the `__server_context` global as we call `getBuildId` in the base server constructor. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- .../next-middleware-ssr-loader/index.ts | 34 +++---- .../next-middleware-ssr-loader/render.ts | 91 ++++++++++++++++++- packages/next/server/base-server.ts | 4 + packages/next/server/load-components.ts | 2 +- packages/next/server/web-server.ts | 73 ++++++--------- 5 files changed, 128 insertions(+), 76 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts index 5341b620aba8b..d457fcaa72d36 100644 --- a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts @@ -44,37 +44,25 @@ export default async function middlewareSSRLoader(this: any) { } // Set server context - self.__current_route = ${JSON.stringify(page)} self.__server_context = { - Component: pageMod.default, - pageConfig: pageMod.config || {}, - buildManifest, - reactLoadableManifest, - Document, - App, - getStaticProps: pageMod.getStaticProps, - getServerSideProps: pageMod.getServerSideProps, - getStaticPaths: pageMod.getStaticPaths, - ComponentMod: undefined, - serverComponentManifest: ${isServerComponent} ? rscManifest : null, - - // components - errorMod, - error500Mod, - - // renderOpts + page: ${JSON.stringify(page)}, buildId: ${JSON.stringify(buildId)}, - dev: ${dev}, - env: process.env, - supportsDynamicHTML: true, - concurrentFeatures: true, - disableOptimizedLoading: true, } const render = getRender({ + dev: ${dev}, + page: ${JSON.stringify(page)}, + pageMod, + errorMod, + error500Mod, + App, Document, + buildManifest, + reactLoadableManifest, + serverComponentManifest: ${isServerComponent} ? rscManifest : null, isServerComponent: ${isServerComponent}, config: ${stringifiedConfig}, + buildId: ${JSON.stringify(buildId)}, }) export default function rscMiddleware(opts) { diff --git a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts index a56c33a3a7319..b74d8a3be093b 100644 --- a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts +++ b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts @@ -1,4 +1,7 @@ import type { NextConfig } from '../../../../server/config-shared' +import type { DocumentType, AppType } from '../../../../shared/lib/utils' +import type { BuildManifest } from '../../../../server/get-page-files' +import type { ReactLoadableManifest } from '../../../../server/load-components' import { NextRequest } from '../../../../server/web/spec-extension/request' import { toNodeHeaders } from '../../../../server/web/utils' @@ -20,20 +23,98 @@ function sendError(req: any, error: Error) { }) } +// Polyfilled for `path-browserify` inside the Web Server. +process.cwd = () => '' + export function getRender({ + dev, + page, + pageMod, + errorMod, + error500Mod, Document, + App, + buildManifest, + reactLoadableManifest, + serverComponentManifest, isServerComponent, config, + buildId, }: { - Document: any + dev: boolean + page: string + pageMod: any + errorMod: any + error500Mod: any + Document: DocumentType + App: AppType + buildManifest: BuildManifest + reactLoadableManifest: ReactLoadableManifest + serverComponentManifest: any | null isServerComponent: boolean config: NextConfig + buildId: string }) { - // Polyfilled for `path-browserify`. - process.cwd = () => '' + const baseLoadComponentResult = { + dev, + buildManifest, + reactLoadableManifest, + Document, + App, + } + const server = new WebServer({ conf: config, minimalMode: true, + webServerConfig: { + extendRenderOpts: { + buildId, + supportsDynamicHTML: true, + concurrentFeatures: true, + disableOptimizedLoading: true, + serverComponentManifest, + }, + loadComponent: async (pathname) => { + if (pathname === page) { + return { + ...baseLoadComponentResult, + Component: pageMod.default, + pageConfig: pageMod.config || {}, + getStaticProps: pageMod.getStaticProps, + getServerSideProps: pageMod.getServerSideProps, + getStaticPaths: pageMod.getStaticPaths, + ComponentMod: pageMod, + } + } + + // If there is a custom 500 page, we need to handle it separately. + if (pathname === '/500' && error500Mod) { + return { + ...baseLoadComponentResult, + Component: error500Mod.default, + pageConfig: error500Mod.config || {}, + getStaticProps: error500Mod.getStaticProps, + getServerSideProps: error500Mod.getServerSideProps, + getStaticPaths: error500Mod.getStaticPaths, + ComponentMod: error500Mod, + } + } + + if (pathname === '/_error') { + return { + ...baseLoadComponentResult, + Component: errorMod.default, + pageConfig: errorMod.config || {}, + getStaticProps: errorMod.getStaticProps, + getServerSideProps: errorMod.getServerSideProps, + getStaticPaths: errorMod.getStaticPaths, + ComponentMod: errorMod, + } + } + + return null + }, + }, }) const requestHandler = server.getRequestHandler() @@ -72,8 +153,8 @@ export function getRender({ ? JSON.parse(query.__props__) : undefined - // Extend the context. - Object.assign((self as any).__server_context, { + // Extend the render options. + server.updateRenderOpts({ renderServerComponentData, serverComponentProps, }) diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index f302d4517d84a..e327ba718af0e 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -158,6 +158,10 @@ export default abstract class Server { concurrentFeatures?: boolean serverComponents?: boolean crossOrigin?: string + supportsDynamicHTML?: boolean + serverComponentManifest?: any + renderServerComponentData?: boolean + serverComponentProps?: any } private incrementalCache: IncrementalCache private responseCache: ResponseCache diff --git a/packages/next/server/load-components.ts b/packages/next/server/load-components.ts index 5637e13258dfd..28d41a6d571d7 100644 --- a/packages/next/server/load-components.ts +++ b/packages/next/server/load-components.ts @@ -19,7 +19,7 @@ export type ManifestItem = { files: string[] } -type ReactLoadableManifest = { [moduleId: string]: ManifestItem } +export type ReactLoadableManifest = { [moduleId: string]: ManifestItem } export type LoadComponentsReturnType = { Component: React.ComponentType diff --git a/packages/next/server/web-server.ts b/packages/next/server/web-server.ts index acb68e9a999d0..5e61a18a52ca1 100644 --- a/packages/next/server/web-server.ts +++ b/packages/next/server/web-server.ts @@ -4,12 +4,24 @@ import type RenderResult from './render-result' import type { NextParsedUrlQuery } from './request-meta' import type { Params } from './router' import type { PayloadOptions } from './send-payload' +import type { LoadComponentsReturnType } from './load-components' -import BaseServer from './base-server' +import BaseServer, { Options } from './base-server' import { renderToHTML } from './render' -import { LoadComponentsReturnType } from './load-components' + +interface WebServerConfig { + loadComponent: (pathname: string) => Promise + extendRenderOpts?: Partial +} export default class NextWebServer extends BaseServer { + webServerConfig: WebServerConfig + + constructor(options: Options & { webServerConfig: WebServerConfig }) { + super(options) + this.webServerConfig = options.webServerConfig + Object.assign(this.renderOpts, options.webServerConfig.extendRenderOpts) + } protected generateRewrites() { // @TODO: assuming minimal mode right now return { @@ -78,7 +90,7 @@ export default class NextWebServer extends BaseServer { } protected getPagesManifest() { return { - [(globalThis as any).__current_route]: '', + [(globalThis as any).__server_context.page]: '', } } protected getFilesystemPaths() { @@ -158,52 +170,19 @@ export default class NextWebServer extends BaseServer { query?: NextParsedUrlQuery, params?: Params | null ) { - if (pathname === (globalThis as any).__current_route) { - return { - query: { - ...(query || {}), - ...(params || {}), - }, - components: (globalThis as any) - .__server_context as LoadComponentsReturnType, - } - } - - const { errorMod, error500Mod } = (globalThis as any).__server_context + const result = await this.webServerConfig.loadComponent(pathname) + if (!result) return null - // If there is a custom 500 page. - if (pathname === '/500' && error500Mod) { - return { - query: { - ...(query || {}), - ...(params || {}), - }, - components: { - ...(globalThis as any).__server_context, - Component: error500Mod.default, - getStaticProps: error500Mod.getStaticProps, - getServerSideProps: error500Mod.getServerSideProps, - getStaticPaths: error500Mod.getStaticPaths, - } as LoadComponentsReturnType, - } - } - - if (pathname === '/_error') { - return { - query: { - ...(query || {}), - ...(params || {}), - }, - components: { - ...(globalThis as any).__server_context, - Component: errorMod.default, - getStaticProps: errorMod.getStaticProps, - getServerSideProps: errorMod.getServerSideProps, - getStaticPaths: errorMod.getStaticPaths, - } as LoadComponentsReturnType, - } + return { + query: { + ...(query || {}), + ...(params || {}), + }, + components: result, } + } - return null + public updateRenderOpts(renderOpts: Partial) { + Object.assign(this.renderOpts, renderOpts) } } From 5d9310e3780006a245cec06f244729a1b23b20f7 Mon Sep 17 00:00:00 2001 From: Sean Kiefer Date: Tue, 1 Feb 2022 23:45:54 +0100 Subject: [PATCH 4/7] update NextResponse default redirect status to 307 to match docs (#33505) * fix: update default redirect status to 307 to match docs * fix: update default redirect status to 307 to match docs Co-authored-by: JJ Kasper --- packages/next/server/web/spec-compliant/response.ts | 2 +- packages/next/server/web/spec-extension/response.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/server/web/spec-compliant/response.ts b/packages/next/server/web/spec-compliant/response.ts index 3ea1a55650f6b..97334d4facd0e 100644 --- a/packages/next/server/web/spec-compliant/response.ts +++ b/packages/next/server/web/spec-compliant/response.ts @@ -39,7 +39,7 @@ class BaseResponse extends Body implements Response { } } - static redirect(url: string, status = 302) { + static redirect(url: string, status = 307) { if (!REDIRECTS.has(status)) { throw new RangeError( 'Failed to execute "redirect" on "response": Invalid status code' diff --git a/packages/next/server/web/spec-extension/response.ts b/packages/next/server/web/spec-extension/response.ts index 240096c3bd30d..e9993f74720a9 100644 --- a/packages/next/server/web/spec-extension/response.ts +++ b/packages/next/server/web/spec-extension/response.ts @@ -73,7 +73,7 @@ export class NextResponse extends Response { }) } - static redirect(url: string | NextURL | URL, status = 302) { + static redirect(url: string | NextURL | URL, status = 307) { if (!REDIRECTS.has(status)) { throw new RangeError( 'Failed to execute "redirect" on "response": Invalid status code' From d005631063bab341e1cee2859843670f483e3bf2 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 1 Feb 2022 16:52:26 -0600 Subject: [PATCH 5/7] Update npm comment in Docker example (#33881) --- examples/with-docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/with-docker/Dockerfile b/examples/with-docker/Dockerfile index 57635340bb16f..0182d790e8d00 100644 --- a/examples/with-docker/Dockerfile +++ b/examples/with-docker/Dockerfile @@ -7,8 +7,8 @@ COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile # If using npm with a `package-lock.json` comment out above and use below instead -# COPY package.json package-lock.json / -# RUN npm install +# COPY package.json package-lock.json ./ +# RUN npm ci # Rebuild the source code only when needed FROM node:16-alpine AS builder From 39d3210776d37e7dc11e77b54a65b33b5454c0ce Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 1 Feb 2022 17:01:29 -0600 Subject: [PATCH 6/7] v12.0.11-canary.1 --- 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/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 +- 15 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lerna.json b/lerna.json index b1652e9b30798..51ade9efb717a 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "12.0.11-canary.0" + "version": "12.0.11-canary.1" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 50162baf248e2..bf955b68605c5 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 13b16311f86b3..b6913b9660072 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "description": "ESLint configuration used by NextJS.", "main": "index.js", "license": "MIT", @@ -9,7 +9,7 @@ "directory": "packages/eslint-config-next" }, "dependencies": { - "@next/eslint-plugin-next": "12.0.11-canary.0", + "@next/eslint-plugin-next": "12.0.11-canary.1", "@rushstack/eslint-patch": "^1.0.8", "@typescript-eslint/parser": "^5.0.0", "eslint-import-resolver-node": "^0.3.4", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 87501fd37fbf7..2a8d02edb11b4 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": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "description": "ESLint plugin for NextJS.", "main": "lib/index.js", "license": "MIT", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index ddf14f974b718..af66c11d82d7b 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 92ff0ee26b07b..102649fc94013 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 82bf0e58c77ae..e41d531efe61d 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 9e41c9e737808..5d6dd1974716c 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 663cfa998663c..344adb05badd5 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "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 e780cec54e715..1e9f48b43e770 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "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 37e6f0e4801c5..81c9f88a966f2 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "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 79410dd58c093..d6be56e84911c 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "private": true, "scripts": { "build-native": "napi build --platform --cargo-name next_swc_napi native", diff --git a/packages/next/package.json b/packages/next/package.json index 1b31e1c960ec7..0926f7294762a 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -71,7 +71,7 @@ ] }, "dependencies": { - "@next/env": "12.0.11-canary.0", + "@next/env": "12.0.11-canary.1", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", "styled-jsx": "5.0.0", @@ -119,11 +119,11 @@ "@hapi/accept": "5.0.2", "@napi-rs/cli": "1.2.1", "@napi-rs/triples": "1.0.3", - "@next/polyfill-module": "12.0.11-canary.0", - "@next/polyfill-nomodule": "12.0.11-canary.0", - "@next/react-dev-overlay": "12.0.11-canary.0", - "@next/react-refresh-utils": "12.0.11-canary.0", - "@next/swc": "12.0.11-canary.0", + "@next/polyfill-module": "12.0.11-canary.1", + "@next/polyfill-nomodule": "12.0.11-canary.1", + "@next/react-dev-overlay": "12.0.11-canary.1", + "@next/react-refresh-utils": "12.0.11-canary.1", + "@next/swc": "12.0.11-canary.1", "@peculiar/webcrypto": "1.1.7", "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 828c2e1792df4..9d10133138308 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": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "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 01c9995a186e0..9744979568b25 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": "12.0.11-canary.0", + "version": "12.0.11-canary.1", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From 965d26eba373e760c5440054c76d77ea20d96fef Mon Sep 17 00:00:00 2001 From: Jim Fisher Date: Wed, 2 Feb 2022 02:57:04 +0000 Subject: [PATCH 7/7] Bug fix: dynamic page should not be interpreted as predefined page (#33808) Fixes: https://github.com/vercel/next.js/issues/17096 Fixes: https://github.com/vercel/next.js/issues/23824 Closes: https://github.com/vercel/next.js/pull/33765 ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added in https://github.com/vercel/next.js/pull/33765 - [x] Errors have helpful link attached (N/A) Co-authored-by: Tim Neutkens <6324199+timneutkens@users.noreply.github.com> Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> --- packages/next/server/base-server.ts | 22 ++++--- .../dynamic-route-interpolation/index.test.ts | 63 +++++++++++++++++++ 2 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 test/e2e/dynamic-route-interpolation/index.test.ts diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index e327ba718af0e..0d28b29ae6be5 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -881,7 +881,7 @@ export default abstract class Server { ): Promise { let page = pathname let params: Params | false = false - let pageFound = await this.hasPage(page) + let pageFound = !isDynamicRoute(page) && (await this.hasPage(page)) if (!pageFound && this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes) { @@ -1536,15 +1536,19 @@ export default abstract class Server { delete query._nextBubbleNoFallback try { - const result = await this.findPageComponents(pathname, query) - if (result) { - try { - return await this.renderToResponseWithComponents(ctx, result) - } catch (err) { - const isNoFallbackError = err instanceof NoFallbackError + // Ensure a request to the URL /accounts/[id] will be treated as a dynamic + // route correctly and not loaded immediately without parsing params. + if (!isDynamicRoute(pathname)) { + const result = await this.findPageComponents(pathname, query) + if (result) { + try { + return await this.renderToResponseWithComponents(ctx, result) + } catch (err) { + const isNoFallbackError = err instanceof NoFallbackError - if (!isNoFallbackError || (isNoFallbackError && bubbleNoFallback)) { - throw err + if (!isNoFallbackError || (isNoFallbackError && bubbleNoFallback)) { + throw err + } } } } diff --git a/test/e2e/dynamic-route-interpolation/index.test.ts b/test/e2e/dynamic-route-interpolation/index.test.ts new file mode 100644 index 0000000000000..441a5a5ac3bb8 --- /dev/null +++ b/test/e2e/dynamic-route-interpolation/index.test.ts @@ -0,0 +1,63 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP } from 'next-test-utils' +import cheerio from 'cheerio' + +describe('Dynamic Route Interpolation', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/blog/[slug].js': ` + export function getServerSideProps({ params }) { + return { props: { slug: params.slug } } + } + + export default function Page(props) { + return

{props.slug}

+ } + + `, + + 'pages/api/dynamic/[slug].js': ` + export default function Page(req, res) { + const { slug } = req.query + res.end('slug: ' + slug) + } + + `, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('should work', async () => { + const html = await renderViaHTTP(next.url, '/blog/a') + const $ = cheerio.load(html) + expect($('#slug').text()).toBe('a') + }) + + it('should work with parameter itself', async () => { + const html = await renderViaHTTP(next.url, '/blog/[slug]') + const $ = cheerio.load(html) + expect($('#slug').text()).toBe('[slug]') + }) + + it('should work with brackets', async () => { + const html = await renderViaHTTP(next.url, '/blog/[abc]') + const $ = cheerio.load(html) + expect($('#slug').text()).toBe('[abc]') + }) + + it('should work with parameter itself in API routes', async () => { + const text = await renderViaHTTP(next.url, '/api/dynamic/[slug]') + expect(text).toBe('slug: [slug]') + }) + + it('should work with brackets in API routes', async () => { + const text = await renderViaHTTP(next.url, '/api/dynamic/[abc]') + expect(text).toBe('slug: [abc]') + }) +})