From 60a9ac46e07467ad792eee17360f6284facb55cb Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 22 Jun 2022 16:07:48 -0500 Subject: [PATCH 1/3] Ensure resolvedUrl is correct with fallback rewrites --- packages/next/server/base-server.ts | 5 + .../app/next.config.js | 32 +++++ .../app/pages/blog/[slug].js | 32 +++++ .../app/pages/index.js | 25 ++++ .../app/pages/news.js | 25 ++++ .../minimal-mode-response-cache/index.test.ts | 129 ++++++++++++++++++ 6 files changed, 248 insertions(+) create mode 100644 test/production/minimal-mode-response-cache/app/next.config.js create mode 100644 test/production/minimal-mode-response-cache/app/pages/blog/[slug].js create mode 100644 test/production/minimal-mode-response-cache/app/pages/index.js create mode 100644 test/production/minimal-mode-response-cache/app/pages/news.js create mode 100644 test/production/minimal-mode-response-cache/index.test.ts diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index cccd5679389dc..c81c1175ed479 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -1341,6 +1341,11 @@ export default abstract class Server { let resolvedUrlPathname = getRequestMeta(req, '_nextRewroteUrl') || urlPathname + if (isSSG && this.minimalMode && req.headers['x-matched-path']) { + // the url value is already correct when the matched-path header is set + resolvedUrlPathname = urlPathname + } + urlPathname = removeTrailingSlash(urlPathname) resolvedUrlPathname = normalizeLocalePath( removeTrailingSlash(resolvedUrlPathname), diff --git a/test/production/minimal-mode-response-cache/app/next.config.js b/test/production/minimal-mode-response-cache/app/next.config.js new file mode 100644 index 0000000000000..e882c72b0b633 --- /dev/null +++ b/test/production/minimal-mode-response-cache/app/next.config.js @@ -0,0 +1,32 @@ +module.exports = { + experimental: { + outputStandalone: true, + }, + trailingSlash: true, + rewrites() { + return { + beforeFiles: [ + { + source: '/news/:path/', + destination: '/news/:path*/', + }, + ], + afterFiles: [ + { + source: '/somewhere', + destination: '/', + }, + ], + fallback: [ + { + source: '/:path*', + destination: '/:path*', + }, + { + source: '/(.*)', + destination: '/seo-redirects', + }, + ], + } + }, +} diff --git a/test/production/minimal-mode-response-cache/app/pages/blog/[slug].js b/test/production/minimal-mode-response-cache/app/pages/blog/[slug].js new file mode 100644 index 0000000000000..7744865fab407 --- /dev/null +++ b/test/production/minimal-mode-response-cache/app/pages/blog/[slug].js @@ -0,0 +1,32 @@ +import { useRouter } from 'next/router' + +export default function Page(props) { + const router = useRouter() + + return ( + <> +

/blog/[slug]

+

{JSON.stringify(props)}

+

{router.asPath}

+

{router.pathname}

+

{JSON.stringify(router.query)}

+ + ) +} + +export function getStaticProps({ params }) { + console.log('getStaticProps /blog/[slug]', params) + return { + props: { + params, + now: Date.now(), + }, + } +} + +export function getStaticPaths() { + return { + paths: ['/blog/first'], + fallback: 'blocking', + } +} diff --git a/test/production/minimal-mode-response-cache/app/pages/index.js b/test/production/minimal-mode-response-cache/app/pages/index.js new file mode 100644 index 0000000000000..0d7ef7f93f621 --- /dev/null +++ b/test/production/minimal-mode-response-cache/app/pages/index.js @@ -0,0 +1,25 @@ +import { useRouter } from 'next/router' + +export default function Page(props) { + const router = useRouter() + + return ( + <> +

/

+

{JSON.stringify(props)}

+

{router.asPath}

+

{router.pathname}

+

{JSON.stringify(router.query)}

+ + ) +} + +export function getStaticProps() { + console.log('getStaticProps /') + return { + props: { + index: true, + now: Date.now(), + }, + } +} diff --git a/test/production/minimal-mode-response-cache/app/pages/news.js b/test/production/minimal-mode-response-cache/app/pages/news.js new file mode 100644 index 0000000000000..74ce52a740af5 --- /dev/null +++ b/test/production/minimal-mode-response-cache/app/pages/news.js @@ -0,0 +1,25 @@ +import { useRouter } from 'next/router' + +export default function Page(props) { + const router = useRouter() + + return ( + <> +

/news

+

{JSON.stringify(props)}

+

{router.asPath}

+

{router.pathname}

+

{JSON.stringify(router.query)}

+ + ) +} + +export function getStaticProps() { + console.log('getStaticProps /news') + return { + props: { + news: true, + now: Date.now(), + }, + } +} diff --git a/test/production/minimal-mode-response-cache/index.test.ts b/test/production/minimal-mode-response-cache/index.test.ts new file mode 100644 index 0000000000000..138289e7d0175 --- /dev/null +++ b/test/production/minimal-mode-response-cache/index.test.ts @@ -0,0 +1,129 @@ +import glob from 'glob' +import fs from 'fs-extra' +import { join } from 'path' +import cheerio from 'cheerio' +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { + killApp, + findPort, + renderViaHTTP, + initNextServerScript, +} from 'next-test-utils' + +describe('minimal-mode-response-cache', () => { + let next: NextInstance + let server + let appPort + let output = '' + + beforeAll(async () => { + // test build against environment with next support + process.env.NOW_BUILDER = '1' + + next = await createNext({ + files: new FileRef(join(__dirname, 'app')), + }) + await next.stop() + + await fs.move( + join(next.testDir, '.next/standalone'), + join(next.testDir, 'standalone') + ) + for (const file of await fs.readdir(next.testDir)) { + if (file !== 'standalone') { + await fs.remove(join(next.testDir, file)) + console.log('removed', file) + } + } + const files = glob.sync('**/*', { + cwd: join(next.testDir, 'standalone/.next/server/pages'), + dot: true, + }) + + for (const file of files) { + if (file.endsWith('.json') || file.endsWith('.html')) { + await fs.remove(join(next.testDir, '.next/server', file)) + } + } + + const testServer = join(next.testDir, 'standalone/server.js') + await fs.writeFile( + testServer, + (await fs.readFile(testServer, 'utf8')) + .replace('console.error(err)', `console.error('top-level', err)`) + .replace('conf:', 'minimalMode: true,conf:') + ) + appPort = await findPort() + server = await initNextServerScript( + testServer, + /Listening on/, + { + ...process.env, + PORT: appPort, + }, + undefined, + { + cwd: next.testDir, + onStdout(msg) { + output += msg + }, + onStderr(msg) { + output += msg + }, + } + ) + }) + afterAll(async () => { + await next.destroy() + if (server) await killApp(server) + }) + + it('should have correct responses', async () => { + const html = await renderViaHTTP(appPort, '/') + expect(html.length).toBeTruthy() + + for (const { path, matchedPath, query, asPath, pathname } of [ + { path: '/', asPath: '/' }, + { path: '/', matchedPath: '/index', asPath: '/' }, + { path: '/', matchedPath: '/index/', asPath: '/' }, + { path: '/', matchedPath: '/', asPath: '/' }, + { + path: '/news/', + matchedPath: '/news/', + asPath: '/news/', + pathname: '/news', + }, + { + path: '/news/', + matchedPath: '/news', + asPath: '/news/', + pathname: '/news', + }, + { + path: '/blog/first/', + matchedPath: '/blog/first/', + pathname: '/blog/[slug]', + asPath: '/blog/first/', + query: { slug: 'first' }, + }, + { + path: '/blog/second/', + matchedPath: '/blog/[slug]/', + pathname: '/blog/[slug]', + asPath: '/blog/second/', + query: { slug: 'second' }, + }, + ]) { + const html = await renderViaHTTP(appPort, path, undefined, { + headers: { + 'x-matched-path': matchedPath || path, + }, + }) + const $ = cheerio.load(html) + expect($('#asPath').text()).toBe(asPath) + expect($('#pathname').text()).toBe(pathname || path) + expect(JSON.parse($('#query').text())).toEqual(query || {}) + } + }) +}) From 8b4143c41a47222923c7749dd416f2f0cee8d4fa Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 22 Jun 2022 16:17:28 -0500 Subject: [PATCH 2/3] fix-lint --- test/production/minimal-mode-response-cache/index.test.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/production/minimal-mode-response-cache/index.test.ts b/test/production/minimal-mode-response-cache/index.test.ts index 138289e7d0175..e311bfc8aaa4f 100644 --- a/test/production/minimal-mode-response-cache/index.test.ts +++ b/test/production/minimal-mode-response-cache/index.test.ts @@ -15,7 +15,6 @@ describe('minimal-mode-response-cache', () => { let next: NextInstance let server let appPort - let output = '' beforeAll(async () => { // test build against environment with next support @@ -65,12 +64,6 @@ describe('minimal-mode-response-cache', () => { undefined, { cwd: next.testDir, - onStdout(msg) { - output += msg - }, - onStderr(msg) { - output += msg - }, } ) }) From dac865abd18711ed0315eb8a8e73e8eaa85cb29b Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 22 Jun 2022 16:23:17 -0500 Subject: [PATCH 3/3] normalize slash --- .../build/webpack/loaders/next-serverless-loader/utils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts b/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts index dffb65244b408..1ab53f32acf8c 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts @@ -28,6 +28,7 @@ import { denormalizePagePath } from '../../../../shared/lib/page-path/denormaliz import cookie from 'next/dist/compiled/cookie' import { TEMPORARY_REDIRECT_STATUS } from '../../../../shared/lib/constants' import { addRequestMeta } from '../../../../server/request-meta' +import { removeTrailingSlash } from '../../../../shared/lib/router/utils/remove-trailing-slash' export const vercelHeader = 'x-vercel-id' @@ -98,7 +99,11 @@ export function getUtils({ let fsPathname = parsedUrl.pathname const matchesPage = () => { - return fsPathname === page || dynamicRouteMatcher?.(fsPathname) + const fsPathnameNoSlash = removeTrailingSlash(fsPathname || '') + return ( + fsPathnameNoSlash === removeTrailingSlash(page) || + dynamicRouteMatcher?.(fsPathnameNoSlash) + ) } const checkRewrite = (rewrite: Rewrite): boolean => {