From eb0bd63af48ea9bec85670ad1bcbc455c5f879ec Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 15 Nov 2021 12:33:21 -0500 Subject: [PATCH 1/4] Fix basePath replacing server-side and `normalizeLocalePath()` when path is empty string (#30978) This fixes our `basePath` detection/replacing server-side as we were incorrectly considering `/docss` a match for a `basePath` of `/docs` which caused us to have an unexpected value in the `normalizeLocalePath` function. - Fixes #22429 - Regression introduced in #17757 - Fixes: https://github.com/vercel/next.js/issues/31423 Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> --- packages/next/server/dev/next-dev-server.ts | 6 ++--- packages/next/server/next-server.ts | 3 ++- packages/next/server/router.ts | 25 ++++++++++++++----- packages/next/server/web/next-url.ts | 3 ++- .../shared/lib/i18n/normalize-locale-path.ts | 5 +++- .../shared/lib/router/utils/parse-next-url.ts | 5 ++-- .../i18n-support-base-path/next.config.js | 2 +- .../i18n-support-base-path/test/index.test.js | 4 --- test/integration/i18n-support/test/shared.js | 21 ++++++++++++++++ 9 files changed, 55 insertions(+), 19 deletions(-) diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 339d5e183bfce..be90bcb2d0d55 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -34,7 +34,7 @@ import Server, { FindComponentsResult, } from '../next-server' import { normalizePagePath } from '../normalize-page-path' -import Router, { Params, route } from '../router' +import Router, { hasBasePath, Params, replaceBasePath, route } from '../router' import { eventCliSession } from '../../telemetry/events' import { Telemetry } from '../../telemetry/storage' import { setGlobal } from '../../trace' @@ -543,11 +543,11 @@ export default class DevServer extends Server { const { basePath } = this.nextConfig let originalPathname: string | null = null - if (basePath && parsedUrl.pathname?.startsWith(basePath)) { + if (basePath && hasBasePath(parsedUrl.pathname || '/', basePath)) { // strip basePath before handling dev bundles // If replace ends up replacing the full url it'll be `undefined`, meaning we have to default it to `/` originalPathname = parsedUrl.pathname - parsedUrl.pathname = parsedUrl.pathname!.slice(basePath.length) || '/' + parsedUrl.pathname = replaceBasePath(parsedUrl.pathname || '/', basePath) } const { pathname } = parsedUrl diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index c6ef07ff21683..24a226a18a9b8 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -67,6 +67,7 @@ import Router, { DynamicRoutes, PageChecker, Params, + replaceBasePath, route, Route, } from './router' @@ -369,7 +370,7 @@ export default class Server { }) if (url.basePath) { - req.url = req.url!.replace(this.nextConfig.basePath, '') || '/' + req.url = replaceBasePath(req.url!, this.nextConfig.basePath) addRequestMeta(req, '_nextHadBasePath', true) } diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index a9a9f6e4fb710..c4faf4767a4ed 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -44,9 +44,22 @@ export type PageChecker = (pathname: string) => Promise const customRouteTypes = new Set(['rewrite', 'redirect', 'header']) -function replaceBasePath(basePath: string, pathname: string) { - // If replace ends up replacing the full url it'll be `undefined`, meaning we have to default it to `/` - return pathname!.replace(basePath, '') || '/' +export function hasBasePath(pathname: string, basePath: string): boolean { + return ( + typeof pathname === 'string' && + (pathname === basePath || pathname.startsWith(basePath + '/')) + ) +} + +export function replaceBasePath(pathname: string, basePath: string): string { + // ensure basePath is only stripped if it matches exactly + // and doesn't contain extra chars e.g. basePath /docs + // should replace for /docs, /docs/, /docs/a but not /docsss + if (hasBasePath(pathname, basePath)) { + pathname = pathname.substr(basePath.length) + if (!pathname.startsWith('/')) pathname = `/${pathname}` + } + return pathname } export default class Router { @@ -142,7 +155,7 @@ export default class Router { const applyCheckTrue = async (checkParsedUrl: NextUrlWithParsedQuery) => { const originalFsPathname = checkParsedUrl.pathname - const fsPathname = replaceBasePath(this.basePath, originalFsPathname!) + const fsPathname = replaceBasePath(originalFsPathname!, this.basePath) for (const fsRoute of this.fsRoutes) { const fsParams = fsRoute.match(fsPathname) @@ -283,8 +296,8 @@ export default class Router { const keepLocale = isCustomRoute const currentPathnameNoBasePath = replaceBasePath( - this.basePath, - currentPathname + currentPathname, + this.basePath ) if (!keepBasePath) { diff --git a/packages/next/server/web/next-url.ts b/packages/next/server/web/next-url.ts index 4f30e80ed05bb..a82cff13c804b 100644 --- a/packages/next/server/web/next-url.ts +++ b/packages/next/server/web/next-url.ts @@ -2,6 +2,7 @@ import type { PathLocale } from '../../shared/lib/i18n/normalize-locale-path' import type { DomainLocale, I18NConfig } from '../config-shared' import { getLocaleMetadata } from '../../shared/lib/i18n/get-locale-metadata' import cookie from 'next/dist/compiled/cookie' +import { replaceBasePath } from '../router' /** * TODO @@ -48,7 +49,7 @@ export class NextURL extends URL { const { headers = {}, basePath, i18n } = this._options if (basePath && this._url.pathname.startsWith(basePath)) { - this._url.pathname = this._url.pathname.replace(basePath, '') || '/' + this._url.pathname = replaceBasePath(this._url.pathname, basePath) this._basePath = basePath } else { this._basePath = '' diff --git a/packages/next/shared/lib/i18n/normalize-locale-path.ts b/packages/next/shared/lib/i18n/normalize-locale-path.ts index ee21339dfc598..d687605e14a28 100644 --- a/packages/next/shared/lib/i18n/normalize-locale-path.ts +++ b/packages/next/shared/lib/i18n/normalize-locale-path.ts @@ -21,7 +21,10 @@ export function normalizeLocalePath( const pathnameParts = pathname.split('/') ;(locales || []).some((locale) => { - if (pathnameParts[1].toLowerCase() === locale.toLowerCase()) { + if ( + pathnameParts[1] && + pathnameParts[1].toLowerCase() === locale.toLowerCase() + ) { detectedLocale = locale pathnameParts.splice(1, 1) pathname = pathnameParts.join('/') || '/' diff --git a/packages/next/shared/lib/router/utils/parse-next-url.ts b/packages/next/shared/lib/router/utils/parse-next-url.ts index 1b86fb113236e..57985ecfaf0c5 100644 --- a/packages/next/shared/lib/router/utils/parse-next-url.ts +++ b/packages/next/shared/lib/router/utils/parse-next-url.ts @@ -4,6 +4,7 @@ import { parseUrl } from './parse-url' import type { NextConfig, DomainLocale } from '../../../../server/config-shared' import type { ParsedUrl } from './parse-url' import type { PathLocale } from '../../i18n/normalize-locale-path' +import { hasBasePath, replaceBasePath } from '../../../../server/router' interface Params { headers?: { [key: string]: string | string[] | undefined } @@ -15,8 +16,8 @@ export function parseNextUrl({ headers, nextConfig, url = '/' }: Params) { const urlParsed: ParsedNextUrl = parseUrl(url) const { basePath } = nextConfig - if (basePath && urlParsed.pathname.startsWith(basePath)) { - urlParsed.pathname = urlParsed.pathname.replace(basePath, '') || '/' + if (basePath && hasBasePath(urlParsed.pathname, basePath)) { + urlParsed.pathname = replaceBasePath(urlParsed.pathname, basePath) urlParsed.basePath = basePath } diff --git a/test/integration/i18n-support-base-path/next.config.js b/test/integration/i18n-support-base-path/next.config.js index 1c73f3a4211a3..b9ea287862296 100644 --- a/test/integration/i18n-support-base-path/next.config.js +++ b/test/integration/i18n-support-base-path/next.config.js @@ -1,6 +1,6 @@ module.exports = { // target: 'experimental-serverless-trace', - // basePath: '/docs', + basePath: '/docs', i18n: { // localeDetection: false, locales: [ diff --git a/test/integration/i18n-support-base-path/test/index.test.js b/test/integration/i18n-support-base-path/test/index.test.js index 48de91414112c..4ce30b352bc49 100644 --- a/test/integration/i18n-support-base-path/test/index.test.js +++ b/test/integration/i18n-support-base-path/test/index.test.js @@ -41,7 +41,6 @@ describe('i18n Support basePath', () => { isDev: true, } beforeAll(async () => { - nextConfig.replace('// basePath', 'basePath') nextConfig.replace(/__EXTERNAL_PORT__/g, ctx.externalPort) await fs.remove(join(appDir, '.next')) curCtx.appPort = await findPort() @@ -57,7 +56,6 @@ describe('i18n Support basePath', () => { describe('production mode', () => { beforeAll(async () => { - nextConfig.replace('// basePath', 'basePath') nextConfig.replace(/__EXTERNAL_PORT__/g, ctx.externalPort) await fs.remove(join(appDir, '.next')) await nextBuild(appDir) @@ -78,7 +76,6 @@ describe('i18n Support basePath', () => { beforeAll(async () => { await fs.remove(join(appDir, '.next')) nextConfig.replace('// target', 'target') - nextConfig.replace('// basePath', 'basePath') nextConfig.replace(/__EXTERNAL_PORT__/g, ctx.externalPort) await nextBuild(appDir) @@ -194,7 +191,6 @@ describe('i18n Support basePath', () => { describe('with localeDetection disabled', () => { beforeAll(async () => { await fs.remove(join(appDir, '.next')) - nextConfig.replace('// basePath', 'basePath') nextConfig.replace('// localeDetection', 'localeDetection') await nextBuild(appDir) diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index 1300d6e3c7663..b76143614412e 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -35,6 +35,27 @@ async function addDefaultLocaleCookie(browser) { } export function runTests(ctx) { + if (ctx.basePath) { + it.only('should handle basePath like pathname', async () => { + const { basePath } = ctx + + for (const pathname of [ + `${basePath}extra`, + `/en${basePath}`, + `${basePath}extra/en`, + `${basePath}en`, + `/en${basePath}`, + ]) { + console.error('checking', pathname) + const res = await fetchViaHTTP(ctx.appPort, pathname, undefined, { + redirect: 'manual', + }) + expect(res.status).toBe(404) + expect(await res.text()).toContain('This page could not be found') + } + }) + } + it('should redirect external domain correctly', async () => { const res = await fetchViaHTTP( ctx.appPort, From ef5795327a99ec8a3dfbe0b40fc1fdde3cdc58fb Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 15 Nov 2021 18:49:50 +0100 Subject: [PATCH 2/4] Close stream when fatal error occurs (#31164) * support custom 500 error in streaming * remove unused imports --- packages/next/build/entries.ts | 1 + .../next-middleware-ssr-loader/index.ts | 72 +++++++++++++------ packages/next/server/dev/hot-reloader.ts | 1 + .../app/pages/err.js | 5 ++ .../app/pages/err/render.js | 7 ++ .../app/pages/err/suspense.js | 20 ++++++ .../test/index.test.js | 13 ++++ 7 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 test/integration/react-streaming-and-server-components/app/pages/err.js create mode 100644 test/integration/react-streaming-and-server-components/app/pages/err/render.js create mode 100644 test/integration/react-streaming-and-server-components/app/pages/err/suspense.js diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index 3c4feca3e299a..a4b97bc00bd45 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -162,6 +162,7 @@ export function createEntrypoints( page, absoluteAppPath: pages['/_app'], absoluteDocumentPath: pages['/_document'], + absoluteErrorPath: pages['/_error'], absolutePagePath, isServerComponent: isFlight, buildId, 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 d056811f7ef58..bf2fd5bfe8b68 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 @@ -5,6 +5,7 @@ export default async function middlewareRSCLoader(this: any) { absolutePagePath, absoluteAppPath, absoluteDocumentPath, + absoluteErrorPath, basePath, isServerComponent: isServerComponentQuery, assetPrefix, @@ -14,23 +15,28 @@ export default async function middlewareRSCLoader(this: any) { const isServerComponent = isServerComponentQuery === 'true' const stringifiedAbsolutePagePath = stringifyRequest(this, absolutePagePath) const stringifiedAbsoluteAppPath = stringifyRequest(this, absoluteAppPath) + const stringifiedAbsoluteErrorPath = stringifyRequest(this, absoluteErrorPath) + const stringified500PagePath = stringifyRequest(this, './pages/500') const stringifiedAbsoluteDocumentPath = stringifyRequest( this, absoluteDocumentPath ) - let appDefinition = `const App = require(${stringifiedAbsoluteAppPath}).default` - let documentDefinition = `const Document = require(${stringifiedAbsoluteDocumentPath}).default` - const transformed = ` import { adapter } from 'next/dist/server/web/adapter' - import { RouterContext } from 'next/dist/shared/lib/router-context' import { renderToHTML } from 'next/dist/server/web/render' - ${appDefinition} - ${documentDefinition} - + import App from ${stringifiedAbsoluteAppPath} + import Document from ${stringifiedAbsoluteDocumentPath} + + let ErrorPage + try { + ErrorPage = require(${stringified500PagePath}).default + } catch (_) { + ErrorPage = require(${stringifiedAbsoluteErrorPath}).default + } + const { default: Page, config, @@ -66,6 +72,7 @@ export default async function middlewareRSCLoader(this: any) { } delete query.__flight__ + const req = { url: pathname } const renderOpts = { Component, pageConfig: config || {}, @@ -97,32 +104,51 @@ export default async function middlewareRSCLoader(this: any) { const transformStream = new TransformStream() const writer = transformStream.writable.getWriter() const encoder = new TextEncoder() - + let result + let renderError + let statusCode = 200 try { - const result = await renderToHTML( - { url: pathname }, + result = await renderToHTML( + req, {}, pathname, query, renderOpts ) - result.pipe({ - write: str => writer.write(encoder.encode(str)), - end: () => writer.close(), - // Not implemented: cork/uncork/on/removeListener - }) } catch (err) { - return new Response( - (err || 'An error occurred while rendering ' + pathname + '.').toString(), - { - status: 500, - headers: { 'x-middleware-ssr': '1' } - } - ) + renderError = err + statusCode = 500 + } + if (renderError) { + try { + const errorRes = { statusCode, err: renderError } + result = await renderToHTML( + req, + errorRes, + pathname, + query, + { ...renderOpts, Component: ErrorPage } + ) + } catch (err) { + return new Response( + (err || 'An error occurred while rendering ' + pathname + '.').toString(), + { + status: 500, + headers: { 'x-middleware-ssr': '1' } + } + ) + } } + result.pipe({ + write: str => writer.write(encoder.encode(str)), + end: () => writer.close(), + // Not implemented: cork/uncork/on/removeListener + }) + return new Response(transformStream.readable, { - headers: { 'x-middleware-ssr': '1' } + headers: { 'x-middleware-ssr': '1' }, + status: statusCode }) } diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index 7a0164864c2c5..3887dace73eda 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -528,6 +528,7 @@ export default class HotReloader { page, absoluteAppPath: this.pagesMapping['/_app'], absoluteDocumentPath: this.pagesMapping['/_document'], + absoluteErrorPath: this.pagesMapping['/_error'], absolutePagePath, isServerComponent, buildId: this.buildId, diff --git a/test/integration/react-streaming-and-server-components/app/pages/err.js b/test/integration/react-streaming-and-server-components/app/pages/err.js new file mode 100644 index 0000000000000..61c7136f7ba7e --- /dev/null +++ b/test/integration/react-streaming-and-server-components/app/pages/err.js @@ -0,0 +1,5 @@ +const page = () => 'page with err' +page.getInitialProps = () => { + throw new Error('oops') +} +export default page diff --git a/test/integration/react-streaming-and-server-components/app/pages/err/render.js b/test/integration/react-streaming-and-server-components/app/pages/err/render.js new file mode 100644 index 0000000000000..c05e49826ae77 --- /dev/null +++ b/test/integration/react-streaming-and-server-components/app/pages/err/render.js @@ -0,0 +1,7 @@ +let did = false +export default function Error() { + if (!did && typeof window === 'undefined') { + did = true + throw new Error('oops') + } +} diff --git a/test/integration/react-streaming-and-server-components/app/pages/err/suspense.js b/test/integration/react-streaming-and-server-components/app/pages/err/suspense.js new file mode 100644 index 0000000000000..645988d009dc3 --- /dev/null +++ b/test/integration/react-streaming-and-server-components/app/pages/err/suspense.js @@ -0,0 +1,20 @@ +import { Suspense } from 'react' + +let did = false +function Error() { + if (!did && typeof window === 'undefined') { + did = true + throw new Error('broken page') + } +} + +export default function page() { + return ( + <> +

Hey Error

+ + + + + ) +} diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index d0d26c4671a18..371953a331b90 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -24,6 +24,7 @@ const nativeModuleTestAppDir = join(__dirname, '../unsupported-native-module') const distDir = join(__dirname, '../app/.next') const documentPage = new File(join(appDir, 'pages/_document.jsx')) const appPage = new File(join(appDir, 'pages/_app.js')) +const error500Page = new File(join(appDir, 'pages/500.js')) const documentWithGip = ` import { Html, Head, Main, NextScript } from 'next/document' @@ -55,6 +56,12 @@ function App({ Component, pageProps }) { export default App ` +const page500 = ` +export default function Page500() { + return 'custom-500-page' +} +` + async function nextBuild(dir) { return await _nextBuild(dir, [], { stdout: true, @@ -100,11 +107,13 @@ describe('concurrentFeatures - prod', () => { const context = { appDir } beforeAll(async () => { + error500Page.write(page500) context.appPort = await findPort() await nextBuild(context.appDir) context.server = await nextStart(context.appDir, context.appPort) }) afterAll(async () => { + error500Page.delete() await killApp(context.server) }) @@ -155,10 +164,12 @@ describe('concurrentFeatures - dev', () => { const context = { appDir } beforeAll(async () => { + error500Page.write(page500) context.appPort = await findPort() context.server = await nextDev(context.appDir, context.appPort) }) afterAll(async () => { + error500Page.delete() await killApp(context.server) }) @@ -217,6 +228,7 @@ async function runBasicTests(context) { ) const path404HTML = await renderViaHTTP(context.appPort, '/404') + const path500HTML = await renderViaHTTP(context.appPort, '/err') const pathNotFoundHTML = await renderViaHTTP( context.appPort, '/this-is-not-found' @@ -230,6 +242,7 @@ async function runBasicTests(context) { expect(dynamicRouteHTML2).toContain('[pid]') expect(path404HTML).toContain('custom-404-page') + expect(path500HTML).toContain('custom-500-page') expect(pathNotFoundHTML).toContain('custom-404-page') }) From be03a1d17bf8aa16cae3c0658ad553ae93fd1ef6 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Mon, 15 Nov 2021 12:46:12 -0600 Subject: [PATCH 3/4] Remove .only and ensure jest lint rules apply for all tests (#31456) --- .eslintrc.json | 5 +- .../basic/styled-components-disabled.test.ts | 1 + test/integration/i18n-support/test/shared.js | 2 +- .../production-swcminify/test/security.js | 54 ------------------- test/integration/production/test/security.js | 54 ------------------- .../test/css.js | 14 ++--- .../required-server-files-i18n.test.ts | 2 +- 7 files changed, 13 insertions(+), 119 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 1cee5507a5901..26824c94ba667 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -30,14 +30,15 @@ }, "overrides": [ { - "files": ["test/**/*.test.js"], + "files": ["test/**/*.js", "test/**/*.ts", "**/*.test.ts"], "extends": ["plugin:jest/recommended"], "rules": { "jest/expect-expect": "off", "jest/no-disabled-tests": "off", "jest/no-conditional-expect": "off", "jest/valid-title": "off", - "jest/no-interpolation-in-snapshots": "off" + "jest/no-interpolation-in-snapshots": "off", + "jest/no-export": "off" } }, { "files": ["**/__tests__/**"], "env": { "jest": true } }, diff --git a/test/development/basic/styled-components-disabled.test.ts b/test/development/basic/styled-components-disabled.test.ts index af49dd5deb910..3e96e2081e5c5 100644 --- a/test/development/basic/styled-components-disabled.test.ts +++ b/test/development/basic/styled-components-disabled.test.ts @@ -54,6 +54,7 @@ describe('styled-components SWC transform', () => { throw new Error('did not find mismatch') } catch (err) { // Verify that it really has the logs + // eslint-disable-next-line jest/no-try-expect expect(await matchLogs$(browser)).toBe(true) } } finally { diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index b76143614412e..f24c92461b060 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -36,7 +36,7 @@ async function addDefaultLocaleCookie(browser) { export function runTests(ctx) { if (ctx.basePath) { - it.only('should handle basePath like pathname', async () => { + it('should handle basePath like pathname', async () => { const { basePath } = ctx for (const pathname of [ diff --git a/test/integration/production-swcminify/test/security.js b/test/integration/production-swcminify/test/security.js index fd674399249bf..5d1c2f880a589 100644 --- a/test/integration/production-swcminify/test/security.js +++ b/test/integration/production-swcminify/test/security.js @@ -291,60 +291,6 @@ module.exports = (context) => { expect(hostname).not.toBe('example.com') }) - it('should handle encoded value in the pathname correctly /', async () => { - const res = await fetchViaHTTP( - context.appPort, - '/redirect/me/to-about/%2fgoogle.com', - undefined, - { - redirect: 'manual', - } - ) - - const { pathname, hostname } = url.parse( - res.headers.get('location') || '' - ) - expect(res.status).toBe(307) - expect(pathname).toBe('/%2fgoogle.com/about') - expect(hostname).not.toBe('google.com') - }) - - it('should handle encoded value in the pathname to query correctly (/)', async () => { - const res = await fetchViaHTTP( - context.appPort, - '/redirect-query-test/%2Fgoogle.com', - undefined, - { - redirect: 'manual', - } - ) - - const { pathname, hostname, query } = url.parse( - res.headers.get('location') || '' - ) - expect(res.status).toBe(307) - expect(pathname).toBe('/about') - expect(query).toBe('foo=%2Fgoogle.com') - expect(hostname).not.toBe('google.com') - expect(hostname).not.toMatch(/google/) - }) - - it('should handle encoded / value for trailing slash correctly', async () => { - const res = await fetchViaHTTP( - context.appPort, - '/%2fexample.com/', - undefined, - { redirect: 'manual' } - ) - - const { pathname, hostname } = url.parse( - res.headers.get('location') || '' - ) - expect(res.status).toBe(308) - expect(pathname).toBe('/%2fexample.com') - expect(hostname).not.toBe('example.com') - }) - if (browserName !== 'internet explorer') { it('should not execute script embedded inside svg image', async () => { let browser diff --git a/test/integration/production/test/security.js b/test/integration/production/test/security.js index fd674399249bf..5d1c2f880a589 100644 --- a/test/integration/production/test/security.js +++ b/test/integration/production/test/security.js @@ -291,60 +291,6 @@ module.exports = (context) => { expect(hostname).not.toBe('example.com') }) - it('should handle encoded value in the pathname correctly /', async () => { - const res = await fetchViaHTTP( - context.appPort, - '/redirect/me/to-about/%2fgoogle.com', - undefined, - { - redirect: 'manual', - } - ) - - const { pathname, hostname } = url.parse( - res.headers.get('location') || '' - ) - expect(res.status).toBe(307) - expect(pathname).toBe('/%2fgoogle.com/about') - expect(hostname).not.toBe('google.com') - }) - - it('should handle encoded value in the pathname to query correctly (/)', async () => { - const res = await fetchViaHTTP( - context.appPort, - '/redirect-query-test/%2Fgoogle.com', - undefined, - { - redirect: 'manual', - } - ) - - const { pathname, hostname, query } = url.parse( - res.headers.get('location') || '' - ) - expect(res.status).toBe(307) - expect(pathname).toBe('/about') - expect(query).toBe('foo=%2Fgoogle.com') - expect(hostname).not.toBe('google.com') - expect(hostname).not.toMatch(/google/) - }) - - it('should handle encoded / value for trailing slash correctly', async () => { - const res = await fetchViaHTTP( - context.appPort, - '/%2fexample.com/', - undefined, - { redirect: 'manual' } - ) - - const { pathname, hostname } = url.parse( - res.headers.get('location') || '' - ) - expect(res.status).toBe(308) - expect(pathname).toBe('/%2fexample.com') - expect(hostname).not.toBe('example.com') - }) - if (browserName !== 'internet explorer') { it('should not execute script embedded inside svg image', async () => { let browser diff --git a/test/integration/react-streaming-and-server-components/test/css.js b/test/integration/react-streaming-and-server-components/test/css.js index 141e88a7a8328..f2cf95d9af075 100644 --- a/test/integration/react-streaming-and-server-components/test/css.js +++ b/test/integration/react-streaming-and-server-components/test/css.js @@ -17,11 +17,11 @@ export default function (context) { expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) }) // TODO: fix this test - // it.skip('should include css modules with `serverComponents: true`', async () => { - // const browser = await webdriver(context.appPort, '/css-modules') - // const currentColor = await browser.eval( - // `window.getComputedStyle(document.querySelector('h1')).color` - // ) - // expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) - // }) + it.skip('should include css modules with `serverComponents: true`', async () => { + const browser = await webdriver(context.appPort, '/css-modules') + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('h1')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + }) } diff --git a/test/production/required-server-files-i18n.test.ts b/test/production/required-server-files-i18n.test.ts index 6285ccfb69a75..b9b311108f825 100644 --- a/test/production/required-server-files-i18n.test.ts +++ b/test/production/required-server-files-i18n.test.ts @@ -701,7 +701,7 @@ describe('should set-up next', () => { expect(JSON.parse($('#router').text()).locale).toBe('en') }) - it('should have the correct asPath for fallback page', async () => { + it('should have the correct asPath for fallback page locale', async () => { const res = await fetchViaHTTP(appPort, '/fr/fallback/[slug]', undefined, { headers: { 'x-matched-path': '/fr/fallback/[slug]', From 66d9b4e14a2b59475480f24ad7c4a19d0f7d7eb7 Mon Sep 17 00:00:00 2001 From: "jj@jjsweb.site" Date: Mon, 15 Nov 2021 12:54:42 -0600 Subject: [PATCH 4/4] v12.0.4-canary.14 --- 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/package.json | 12 ++++++------ packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lerna.json b/lerna.json index 145a554a72f23..ec2c7bdc071f4 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "12.0.4-canary.13" + "version": "12.0.4-canary.14" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index aed0dc5f0d4df..47884fca1a19a 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.4-canary.13", + "version": "12.0.4-canary.14", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 0c6ffb2e404b1..08f1b21622111 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.4-canary.13", + "version": "12.0.4-canary.14", "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.4-canary.13", + "@next/eslint-plugin-next": "12.0.4-canary.14", "@rushstack/eslint-patch": "^1.0.6", "@typescript-eslint/parser": "^4.20.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 1e9e965ebb6db..257a78e4dbbbd 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.4-canary.13", + "version": "12.0.4-canary.14", "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 9f8867aea1367..5a5d7d9a4538a 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.4-canary.13", + "version": "12.0.4-canary.14", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index d614766a0db2e..ce5047de35e3a 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "12.0.4-canary.13", + "version": "12.0.4-canary.14", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 4336d5a031486..4267a827f1148 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "12.0.4-canary.13", + "version": "12.0.4-canary.14", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index de3355aec7177..2b05f27d9ecb6 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "12.0.4-canary.13", + "version": "12.0.4-canary.14", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 5bdfd6c483547..56b4dd8b2c941 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.4-canary.13", + "version": "12.0.4-canary.14", "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 3ef00be511b2d..4595c0765a498 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.4-canary.13", + "version": "12.0.4-canary.14", "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 f456574115655..7526b321a755b 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.4-canary.13", + "version": "12.0.4-canary.14", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index 86e3d17c40b9b..fe203120d03f7 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "12.0.4-canary.13", + "version": "12.0.4-canary.14", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -70,10 +70,10 @@ "@babel/runtime": "7.15.4", "@hapi/accept": "5.0.2", "@napi-rs/triples": "1.0.3", - "@next/env": "12.0.4-canary.13", - "@next/polyfill-module": "12.0.4-canary.13", - "@next/react-dev-overlay": "12.0.4-canary.13", - "@next/react-refresh-utils": "12.0.4-canary.13", + "@next/env": "12.0.4-canary.14", + "@next/polyfill-module": "12.0.4-canary.14", + "@next/react-dev-overlay": "12.0.4-canary.14", + "@next/react-refresh-utils": "12.0.4-canary.14", "acorn": "8.5.0", "assert": "2.0.0", "browserify-zlib": "0.2.0", @@ -156,7 +156,7 @@ "@babel/traverse": "7.15.0", "@babel/types": "7.15.0", "@napi-rs/cli": "1.2.1", - "@next/polyfill-nomodule": "12.0.4-canary.13", + "@next/polyfill-nomodule": "12.0.4-canary.14", "@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 af4bf3d160f86..59c3d7f20d695 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.4-canary.13", + "version": "12.0.4-canary.14", "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 b0124f71a99e8..f582454ca6277 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.4-canary.13", + "version": "12.0.4-canary.14", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js",