From ed9fa664163697a0149b48552eff0471539a0ee3 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 27 Mar 2023 14:52:01 -0600 Subject: [PATCH 1/4] fix: handle case where absolute path contains a _ - Allows app files that have an underscore in their absolute path (but not their page path) allowing for the next dir to contain a `_` but not relative to the application root - Corrects beheviour around development mode and app routes --- .../next/src/server/dev/next-dev-server.ts | 2 +- .../normalizers/underscore-normalizer.ts | 10 ++++++++ .../app-page-route-matcher-provider.ts | 16 +++++++++++-- .../app-route-route-matcher-provider.ts | 17 ++++++++++--- .../dev-app-page-route-matcher-provider.ts | 22 +++++++++++------ .../dev-app-route-route-matcher-provider.ts | 5 ++++ .../_allow-underscored-root-directory.test.ts | 24 +++++++++++++++++++ .../app/%5Froutable-folder/route.js | 1 + .../app/_handlers/route.js | 7 ++++++ .../app/route.js | 1 + .../next.config.js | 8 +++++++ 11 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 packages/next/src/server/future/normalizers/underscore-normalizer.ts create mode 100644 test/e2e/app-dir/_allow-underscored-root-directory/_allow-underscored-root-directory.test.ts create mode 100644 test/e2e/app-dir/_allow-underscored-root-directory/app/%5Froutable-folder/route.js create mode 100644 test/e2e/app-dir/_allow-underscored-root-directory/app/_handlers/route.js create mode 100644 test/e2e/app-dir/_allow-underscored-root-directory/app/route.js create mode 100644 test/e2e/app-dir/_allow-underscored-root-directory/next.config.js diff --git a/packages/next/src/server/dev/next-dev-server.ts b/packages/next/src/server/dev/next-dev-server.ts index 1ef2effca288..a2552ed0c8d3 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -555,7 +555,7 @@ export default class DevServer extends Server { continue } // Ignore files/directories starting with `_` in the app directory - if (normalizePathSep(fileName).includes('/_')) { + if (normalizePathSep(pageName).includes('/_')) { continue } diff --git a/packages/next/src/server/future/normalizers/underscore-normalizer.ts b/packages/next/src/server/future/normalizers/underscore-normalizer.ts new file mode 100644 index 000000000000..843688976978 --- /dev/null +++ b/packages/next/src/server/future/normalizers/underscore-normalizer.ts @@ -0,0 +1,10 @@ +import { Normalizer } from './normalizer' + +/** + * UnderscoreNormalizer replaces all instances of %5F with _. + */ +export class UnderscoreNormalizer implements Normalizer { + public normalize(pathname: string): string { + return pathname.replace(/%5F/g, '_') + } +} diff --git a/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts index 8576a2484b9e..cd8f4910b6a2 100644 --- a/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts @@ -5,6 +5,10 @@ import { } from '../../../shared/lib/constants' import path from '../../../shared/lib/isomorphic/path' import { normalizeAppPath } from '../../../shared/lib/router/utils/app-paths' +import { Normalizers } from '../normalizers/normalizers' +import { PrefixingNormalizer } from '../normalizers/prefixing-normalizer' +import { UnderscoreNormalizer } from '../normalizers/underscore-normalizer' +import { wrapNormalizerFn } from '../normalizers/wrap-normalizer-fn' import { RouteKind } from '../route-kind' import { AppPageRouteMatcher } from '../route-matchers/app-page-route-matcher' import { @@ -14,6 +18,14 @@ import { import { ManifestRouteMatcherProvider } from './manifest-route-matcher-provider' export class AppPageRouteMatcherProvider extends ManifestRouteMatcherProvider { + private readonly normalizers = { + pathname: new Normalizers([ + wrapNormalizerFn(normalizeAppPath), + new UnderscoreNormalizer(), + ]), + bundlePath: new PrefixingNormalizer('app'), + } + constructor( private readonly distDir: string, manifestLoader: ManifestLoader @@ -31,7 +43,7 @@ export class AppPageRouteMatcherProvider extends ManifestRouteMatcherProvider = {} for (const page of pages) { - const pathname = normalizeAppPath(page).replace(/%5F/g, '_') + const pathname = this.normalizers.pathname.normalize(page) if (pathname in appPaths) appPaths[pathname].push(page) else appPaths[pathname] = [page] } @@ -43,7 +55,7 @@ export class AppPageRouteMatcherProvider extends ManifestRouteMatcherProvider { + private readonly normalizers = { + pathname: new Normalizers([ + wrapNormalizerFn(normalizeAppPath), + new UnderscoreNormalizer(), + ]), + bundlePath: new PrefixingNormalizer('app'), + } + constructor( private readonly distDir: string, manifestLoader: ManifestLoader @@ -25,15 +37,14 @@ export class AppRouteRouteMatcherProvider extends ManifestRouteMatcherProvider> { // This matcher only matches app routes. - const pages = Object.keys(manifest).filter((page) => isAppRouteRoute(page)) // Format the routes. const matchers: Array = [] for (const page of pages) { - const pathname = normalizeAppPath(page).replace(/%5F/g, '_') + const pathname = this.normalizers.pathname.normalize(page) const filename = path.join(this.distDir, SERVER_DIRECTORY, manifest[page]) - const bundlePath = path.join('app', page) + const bundlePath = this.normalizers.bundlePath.normalize(page) matchers.push( new AppRouteRouteMatcher({ diff --git a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts index 0e726d63cffd..e6b7fe8b54d9 100644 --- a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts @@ -8,6 +8,7 @@ import { normalizeAppPath } from '../../../../shared/lib/router/utils/app-paths' import { PrefixingNormalizer } from '../../normalizers/prefixing-normalizer' import { RouteKind } from '../../route-kind' import { FileCacheRouteMatcherProvider } from './file-cache-route-matcher-provider' +import { UnderscoreNormalizer } from '../../normalizers/underscore-normalizer' export class DevAppPageRouteMatcherProvider extends FileCacheRouteMatcherProvider { private readonly expression: RegExp @@ -41,6 +42,7 @@ export class DevAppPageRouteMatcherProvider extends FileCacheRouteMatcherProvide // The pathname to match should have the trailing `/page` and other route // group information stripped from it. wrapNormalizerFn(normalizeAppPath), + new UnderscoreNormalizer(), ]), bundlePath: new Normalizers([ pageNormalizer, @@ -59,12 +61,21 @@ export class DevAppPageRouteMatcherProvider extends FileCacheRouteMatcherProvide string, { page: string; pathname: string; bundlePath: string } >() + const routeFilenames = new Array() const appPaths: Record = {} for (const filename of files) { + // If the file isn't a match for this matcher, then skip it. + if (!this.expression.test(filename)) continue + const page = this.normalizers.page.normalize(filename) - const pathname = this.normalizers.pathname - .normalize(filename) - .replace(/%5F/g, '_') + + // Validate that this is not an ignored page. + if (page.includes('/_')) continue + + // This is a valid file that we want to create a matcher for. + routeFilenames.push(filename) + + const pathname = this.normalizers.pathname.normalize(filename) const bundlePath = this.normalizers.bundlePath.normalize(filename) // Save the normalization results. @@ -75,10 +86,7 @@ export class DevAppPageRouteMatcherProvider extends FileCacheRouteMatcherProvide } const matchers: Array = [] - for (const filename of files) { - // If the file isn't a match for this matcher, then skip it. - if (!this.expression.test(filename)) continue - + for (const filename of routeFilenames) { // Grab the cached values (and the appPaths). const cached = cache.get(filename) if (!cached) { diff --git a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts index 81a662bfea78..09fce3b53984 100644 --- a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts @@ -9,6 +9,7 @@ import { PrefixingNormalizer } from '../../normalizers/prefixing-normalizer' import { RouteKind } from '../../route-kind' import { FileCacheRouteMatcherProvider } from './file-cache-route-matcher-provider' import { isAppRouteRoute } from '../../../../lib/is-app-route-route' +import { UnderscoreNormalizer } from '../../normalizers/underscore-normalizer' export class DevAppRouteRouteMatcherProvider extends FileCacheRouteMatcherProvider { private readonly normalizers: { @@ -37,6 +38,7 @@ export class DevAppRouteRouteMatcherProvider extends FileCacheRouteMatcherProvid // The pathname to match should have the trailing `/route` and other route // group information stripped from it. wrapNormalizerFn(normalizeAppPath), + new UnderscoreNormalizer(), ]), bundlePath: new Normalizers([ pageNormalizer, @@ -56,6 +58,9 @@ export class DevAppRouteRouteMatcherProvider extends FileCacheRouteMatcherProvid // If the file isn't a match for this matcher, then skip it. if (!isAppRouteRoute(page)) continue + // Validate that this is not an ignored page. + if (page.includes('/_')) continue + const pathname = this.normalizers.pathname.normalize(filename) const bundlePath = this.normalizers.bundlePath.normalize(filename) diff --git a/test/e2e/app-dir/_allow-underscored-root-directory/_allow-underscored-root-directory.test.ts b/test/e2e/app-dir/_allow-underscored-root-directory/_allow-underscored-root-directory.test.ts new file mode 100644 index 000000000000..a61309275473 --- /dev/null +++ b/test/e2e/app-dir/_allow-underscored-root-directory/_allow-underscored-root-directory.test.ts @@ -0,0 +1,24 @@ +import { createNextDescribe } from 'e2e-utils' + +createNextDescribe( + '_allow-underscored-root-directory', + { + files: __dirname, + }, + ({ next }) => { + it('should not serve app path with underscore', async () => { + const res = await next.fetch('/_handlers') + expect(res.status).toBe(404) + }) + + it('should pages path with a underscore at the root', async () => { + const res = await next.fetch('/') + await expect(res.text()).resolves.toBe('Hello, world!') + }) + + it('should serve app path with %5F', async () => { + const res = await next.fetch('/_routable-folder') + await expect(res.text()).resolves.toBe('Hello, world!') + }) + } +) diff --git a/test/e2e/app-dir/_allow-underscored-root-directory/app/%5Froutable-folder/route.js b/test/e2e/app-dir/_allow-underscored-root-directory/app/%5Froutable-folder/route.js new file mode 100644 index 000000000000..5d790534e62e --- /dev/null +++ b/test/e2e/app-dir/_allow-underscored-root-directory/app/%5Froutable-folder/route.js @@ -0,0 +1 @@ +export { GET } from '../_handlers/route' diff --git a/test/e2e/app-dir/_allow-underscored-root-directory/app/_handlers/route.js b/test/e2e/app-dir/_allow-underscored-root-directory/app/_handlers/route.js new file mode 100644 index 000000000000..e3bee97e1c90 --- /dev/null +++ b/test/e2e/app-dir/_allow-underscored-root-directory/app/_handlers/route.js @@ -0,0 +1,7 @@ +export async function GET() { + return new Response('Hello, world!', { + headers: { + 'content-type': 'text/plain', + }, + }) +} diff --git a/test/e2e/app-dir/_allow-underscored-root-directory/app/route.js b/test/e2e/app-dir/_allow-underscored-root-directory/app/route.js new file mode 100644 index 000000000000..acfbeb553ad3 --- /dev/null +++ b/test/e2e/app-dir/_allow-underscored-root-directory/app/route.js @@ -0,0 +1 @@ +export { GET } from './_handlers/route' diff --git a/test/e2e/app-dir/_allow-underscored-root-directory/next.config.js b/test/e2e/app-dir/_allow-underscored-root-directory/next.config.js new file mode 100644 index 000000000000..bf49894afd40 --- /dev/null +++ b/test/e2e/app-dir/_allow-underscored-root-directory/next.config.js @@ -0,0 +1,8 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + experimental: { appDir: true }, +} + +module.exports = nextConfig From 152b0b57aa9cc4eb110a3a8b3f7511ecb4885dca Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 27 Mar 2023 15:09:33 -0600 Subject: [PATCH 2/4] fix: switch path to shared path module --- .../next/src/server/future/normalizers/prefixing-normalizer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/server/future/normalizers/prefixing-normalizer.ts b/packages/next/src/server/future/normalizers/prefixing-normalizer.ts index e4dade43429c..b67ebc43c266 100644 --- a/packages/next/src/server/future/normalizers/prefixing-normalizer.ts +++ b/packages/next/src/server/future/normalizers/prefixing-normalizer.ts @@ -1,4 +1,4 @@ -import path from 'path' +import path from '../../../shared/lib/isomorphic/path' import { Normalizer } from './normalizer' export class PrefixingNormalizer implements Normalizer { From fdf746c7079e7c18c7c0f6fab61dd0d4f7907d0d Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 27 Mar 2023 16:32:40 -0600 Subject: [PATCH 3/4] fix: cleanup for built normalizers --- .../absolute-filename-normalizer.ts | 4 +- .../built/app/app-bundle-path-normalizer.ts | 28 +++++++++++++ .../built/app/app-filename-normalizer.ts | 12 ++++++ .../built/app/app-page-normalizer.ts | 11 +++++ .../built/app/app-pathname-normalizer.ts | 36 ++++++++++++++++ .../future/normalizers/built/app/index.ts | 34 +++++++++++++++ .../future/normalizers/built/pages/index.ts | 33 +++++++++++++++ .../pages/pages-bundle-path-normalizer.ts | 36 ++++++++++++++++ .../built/pages/pages-filename-normalizer.ts | 12 ++++++ .../built/pages/pages-page-normalizer.ts | 7 ++++ .../built/pages/pages-pathname-normalizer.ts | 7 ++++ .../normalizers/prefixing-normalizer.ts | 6 ++- .../app-page-route-matcher-provider.ts | 41 ++++++------------- .../app-route-route-matcher-provider.ts | 29 ++++--------- .../dev-app-page-route-matcher-provider.ts | 38 ++--------------- .../dev-app-route-route-matcher-provider.ts | 29 +------------ .../dev-pages-api-route-matcher-provider.ts | 33 ++------------- .../dev/dev-pages-route-matcher-provider.ts | 31 ++------------ .../pages-api-route-matcher-provider.ts | 19 +++++---- .../pages-route-matcher-provider.ts | 23 +++++------ 20 files changed, 277 insertions(+), 192 deletions(-) create mode 100644 packages/next/src/server/future/normalizers/built/app/app-bundle-path-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/app/app-filename-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/app/app-page-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/app/app-pathname-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/app/index.ts create mode 100644 packages/next/src/server/future/normalizers/built/pages/index.ts create mode 100644 packages/next/src/server/future/normalizers/built/pages/pages-bundle-path-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/pages/pages-filename-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/pages/pages-page-normalizer.ts create mode 100644 packages/next/src/server/future/normalizers/built/pages/pages-pathname-normalizer.ts diff --git a/packages/next/src/server/future/normalizers/absolute-filename-normalizer.ts b/packages/next/src/server/future/normalizers/absolute-filename-normalizer.ts index f5a5f78d488b..b638e60f85eb 100644 --- a/packages/next/src/server/future/normalizers/absolute-filename-normalizer.ts +++ b/packages/next/src/server/future/normalizers/absolute-filename-normalizer.ts @@ -18,8 +18,8 @@ export class AbsoluteFilenameNormalizer implements Normalizer { private readonly pagesType: 'pages' | 'app' | 'root' ) {} - public normalize(pathname: string): string { - return absolutePathToPage(pathname, { + public normalize(filename: string): string { + return absolutePathToPage(filename, { extensions: this.extensions, keepIndex: false, dir: this.dir, diff --git a/packages/next/src/server/future/normalizers/built/app/app-bundle-path-normalizer.ts b/packages/next/src/server/future/normalizers/built/app/app-bundle-path-normalizer.ts new file mode 100644 index 000000000000..1ff9f1e24233 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/app/app-bundle-path-normalizer.ts @@ -0,0 +1,28 @@ +import { Normalizers } from '../../normalizers' +import { Normalizer } from '../../normalizer' +import { PrefixingNormalizer } from '../../prefixing-normalizer' + +export class AppBundlePathNormalizer extends PrefixingNormalizer { + constructor() { + super('app') + } + + public normalize(page: string): string { + return super.normalize(page) + } +} + +export class DevAppBundlePathNormalizer extends Normalizers { + constructor(pageNormalizer: Normalizer) { + super([ + // This should normalize the filename to a page. + pageNormalizer, + // Normalize the app page to a pathname. + new AppBundlePathNormalizer(), + ]) + } + + public normalize(filename: string): string { + return super.normalize(filename) + } +} diff --git a/packages/next/src/server/future/normalizers/built/app/app-filename-normalizer.ts b/packages/next/src/server/future/normalizers/built/app/app-filename-normalizer.ts new file mode 100644 index 000000000000..e42c88e72519 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/app/app-filename-normalizer.ts @@ -0,0 +1,12 @@ +import { SERVER_DIRECTORY } from '../../../../../shared/lib/constants' +import { PrefixingNormalizer } from '../../prefixing-normalizer' + +export class AppFilenameNormalizer extends PrefixingNormalizer { + constructor(distDir: string) { + super(distDir, SERVER_DIRECTORY) + } + + public normalize(manifestFilename: string): string { + return super.normalize(manifestFilename) + } +} diff --git a/packages/next/src/server/future/normalizers/built/app/app-page-normalizer.ts b/packages/next/src/server/future/normalizers/built/app/app-page-normalizer.ts new file mode 100644 index 000000000000..a18ea4ae049f --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/app/app-page-normalizer.ts @@ -0,0 +1,11 @@ +import { AbsoluteFilenameNormalizer } from '../../absolute-filename-normalizer' + +/** + * DevAppPageNormalizer is a normalizer that is used to normalize a pathname + * to a page in the `app` directory. + */ +export class DevAppPageNormalizer extends AbsoluteFilenameNormalizer { + constructor(appDir: string, extensions: ReadonlyArray) { + super(appDir, extensions, 'app') + } +} diff --git a/packages/next/src/server/future/normalizers/built/app/app-pathname-normalizer.ts b/packages/next/src/server/future/normalizers/built/app/app-pathname-normalizer.ts new file mode 100644 index 000000000000..7143e87138a5 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/app/app-pathname-normalizer.ts @@ -0,0 +1,36 @@ +import { normalizeAppPath } from '../../../../../shared/lib/router/utils/app-paths' +import { Normalizers } from '../../normalizers' +import { wrapNormalizerFn } from '../../wrap-normalizer-fn' +import { UnderscoreNormalizer } from '../../underscore-normalizer' +import { Normalizer } from '../../normalizer' + +export class AppPathnameNormalizer extends Normalizers { + constructor() { + super([ + // The pathname to match should have the trailing `/page` and other route + // group information stripped from it. + wrapNormalizerFn(normalizeAppPath), + // The page should have the `%5F` characters replaced with `_` characters. + new UnderscoreNormalizer(), + ]) + } + + public normalize(page: string): string { + return super.normalize(page) + } +} + +export class DevAppPathnameNormalizer extends Normalizers { + constructor(pageNormalizer: Normalizer) { + super([ + // This should normalize the filename to a page. + pageNormalizer, + // Normalize the app page to a pathname. + new AppPathnameNormalizer(), + ]) + } + + public normalize(filename: string): string { + return super.normalize(filename) + } +} diff --git a/packages/next/src/server/future/normalizers/built/app/index.ts b/packages/next/src/server/future/normalizers/built/app/index.ts new file mode 100644 index 000000000000..18d8ee898093 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/app/index.ts @@ -0,0 +1,34 @@ +import { + AppBundlePathNormalizer, + DevAppBundlePathNormalizer, +} from './app-bundle-path-normalizer' +import { AppFilenameNormalizer } from './app-filename-normalizer' +import { DevAppPageNormalizer } from './app-page-normalizer' +import { + AppPathnameNormalizer, + DevAppPathnameNormalizer, +} from './app-pathname-normalizer' + +export class AppNormalizers { + public readonly filename: AppFilenameNormalizer + public readonly pathname: AppPathnameNormalizer + public readonly bundlePath: AppBundlePathNormalizer + + constructor(distDir: string) { + this.filename = new AppFilenameNormalizer(distDir) + this.pathname = new AppPathnameNormalizer() + this.bundlePath = new AppBundlePathNormalizer() + } +} + +export class DevAppNormalizers { + public readonly page: DevAppPageNormalizer + public readonly pathname: DevAppPathnameNormalizer + public readonly bundlePath: DevAppBundlePathNormalizer + + constructor(appDir: string, extensions: ReadonlyArray) { + this.page = new DevAppPageNormalizer(appDir, extensions) + this.pathname = new DevAppPathnameNormalizer(this.page) + this.bundlePath = new DevAppBundlePathNormalizer(this.page) + } +} diff --git a/packages/next/src/server/future/normalizers/built/pages/index.ts b/packages/next/src/server/future/normalizers/built/pages/index.ts new file mode 100644 index 000000000000..3acff4e8f40f --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/pages/index.ts @@ -0,0 +1,33 @@ +import { + DevPagesBundlePathNormalizer, + PagesBundlePathNormalizer, +} from './pages-bundle-path-normalizer' +import { PagesFilenameNormalizer } from './pages-filename-normalizer' +import { DevPagesPageNormalizer } from './pages-page-normalizer' +import { DevPagesPathnameNormalizer } from './pages-pathname-normalizer' + +export class PagesNormalizers { + public readonly filename: PagesFilenameNormalizer + public readonly bundlePath: PagesBundlePathNormalizer + + constructor(distDir: string) { + this.filename = new PagesFilenameNormalizer(distDir) + this.bundlePath = new PagesBundlePathNormalizer() + + // You'd think that we'd require a `pathname` normalizer here, but for + // `/pages` we have to handle i18n routes, which means that we need to + // analyze the page path to determine the locale prefix and it's locale. + } +} + +export class DevPagesNormalizers { + public readonly page: DevPagesPageNormalizer + public readonly pathname: DevPagesPathnameNormalizer + public readonly bundlePath: DevPagesBundlePathNormalizer + + constructor(pagesDir: string, extensions: ReadonlyArray) { + this.page = new DevPagesPageNormalizer(pagesDir, extensions) + this.pathname = new DevPagesPathnameNormalizer(pagesDir, extensions) + this.bundlePath = new DevPagesBundlePathNormalizer(this.page) + } +} diff --git a/packages/next/src/server/future/normalizers/built/pages/pages-bundle-path-normalizer.ts b/packages/next/src/server/future/normalizers/built/pages/pages-bundle-path-normalizer.ts new file mode 100644 index 000000000000..79cfae76e599 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/pages/pages-bundle-path-normalizer.ts @@ -0,0 +1,36 @@ +import { normalizePagePath } from '../../../../../shared/lib/page-path/normalize-page-path' +import { Normalizer } from '../../normalizer' +import { Normalizers } from '../../normalizers' +import { PrefixingNormalizer } from '../../prefixing-normalizer' +import { wrapNormalizerFn } from '../../wrap-normalizer-fn' + +export class PagesBundlePathNormalizer extends Normalizers { + constructor() { + super([ + // The bundle path should have the trailing `/index` stripped from + // it. + wrapNormalizerFn(normalizePagePath), + // The page should prefixed with `pages/`. + new PrefixingNormalizer('pages'), + ]) + } + + public normalize(page: string): string { + return super.normalize(page) + } +} + +export class DevPagesBundlePathNormalizer extends Normalizers { + constructor(pagesNormalizer: Normalizer) { + super([ + // This should normalize the filename to a page. + pagesNormalizer, + // Normalize the app page to a pathname. + new PagesBundlePathNormalizer(), + ]) + } + + public normalize(filename: string): string { + return super.normalize(filename) + } +} diff --git a/packages/next/src/server/future/normalizers/built/pages/pages-filename-normalizer.ts b/packages/next/src/server/future/normalizers/built/pages/pages-filename-normalizer.ts new file mode 100644 index 000000000000..d3d0044b2e1e --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/pages/pages-filename-normalizer.ts @@ -0,0 +1,12 @@ +import { SERVER_DIRECTORY } from '../../../../../shared/lib/constants' +import { PrefixingNormalizer } from '../../prefixing-normalizer' + +export class PagesFilenameNormalizer extends PrefixingNormalizer { + constructor(distDir: string) { + super(distDir, SERVER_DIRECTORY) + } + + public normalize(manifestFilename: string): string { + return super.normalize(manifestFilename) + } +} diff --git a/packages/next/src/server/future/normalizers/built/pages/pages-page-normalizer.ts b/packages/next/src/server/future/normalizers/built/pages/pages-page-normalizer.ts new file mode 100644 index 000000000000..aa14514ba6e0 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/pages/pages-page-normalizer.ts @@ -0,0 +1,7 @@ +import { AbsoluteFilenameNormalizer } from '../../absolute-filename-normalizer' + +export class DevPagesPageNormalizer extends AbsoluteFilenameNormalizer { + constructor(pagesDir: string, extensions: ReadonlyArray) { + super(pagesDir, extensions, 'pages') + } +} diff --git a/packages/next/src/server/future/normalizers/built/pages/pages-pathname-normalizer.ts b/packages/next/src/server/future/normalizers/built/pages/pages-pathname-normalizer.ts new file mode 100644 index 000000000000..62294b77c9d2 --- /dev/null +++ b/packages/next/src/server/future/normalizers/built/pages/pages-pathname-normalizer.ts @@ -0,0 +1,7 @@ +import { AbsoluteFilenameNormalizer } from '../../absolute-filename-normalizer' + +export class DevPagesPathnameNormalizer extends AbsoluteFilenameNormalizer { + constructor(pagesDir: string, extensions: ReadonlyArray) { + super(pagesDir, extensions, 'pages') + } +} diff --git a/packages/next/src/server/future/normalizers/prefixing-normalizer.ts b/packages/next/src/server/future/normalizers/prefixing-normalizer.ts index b67ebc43c266..02bef440af0c 100644 --- a/packages/next/src/server/future/normalizers/prefixing-normalizer.ts +++ b/packages/next/src/server/future/normalizers/prefixing-normalizer.ts @@ -2,7 +2,11 @@ import path from '../../../shared/lib/isomorphic/path' import { Normalizer } from './normalizer' export class PrefixingNormalizer implements Normalizer { - constructor(private readonly prefix: string) {} + private readonly prefix: string + + constructor(...prefixes: ReadonlyArray) { + this.prefix = path.posix.join(...prefixes) + } public normalize(pathname: string): string { return path.posix.join(this.prefix, pathname) diff --git a/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts index cd8f4910b6a2..39234d44b6b3 100644 --- a/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/app-page-route-matcher-provider.ts @@ -1,14 +1,6 @@ import { isAppPageRoute } from '../../../lib/is-app-page-route' -import { - APP_PATHS_MANIFEST, - SERVER_DIRECTORY, -} from '../../../shared/lib/constants' -import path from '../../../shared/lib/isomorphic/path' -import { normalizeAppPath } from '../../../shared/lib/router/utils/app-paths' -import { Normalizers } from '../normalizers/normalizers' -import { PrefixingNormalizer } from '../normalizers/prefixing-normalizer' -import { UnderscoreNormalizer } from '../normalizers/underscore-normalizer' -import { wrapNormalizerFn } from '../normalizers/wrap-normalizer-fn' +import { APP_PATHS_MANIFEST } from '../../../shared/lib/constants' +import { AppNormalizers } from '../normalizers/built/app' import { RouteKind } from '../route-kind' import { AppPageRouteMatcher } from '../route-matchers/app-page-route-matcher' import { @@ -18,19 +10,12 @@ import { import { ManifestRouteMatcherProvider } from './manifest-route-matcher-provider' export class AppPageRouteMatcherProvider extends ManifestRouteMatcherProvider { - private readonly normalizers = { - pathname: new Normalizers([ - wrapNormalizerFn(normalizeAppPath), - new UnderscoreNormalizer(), - ]), - bundlePath: new PrefixingNormalizer('app'), - } + private readonly normalizers: AppNormalizers - constructor( - private readonly distDir: string, - manifestLoader: ManifestLoader - ) { + constructor(distDir: string, manifestLoader: ManifestLoader) { super(APP_PATHS_MANIFEST, manifestLoader) + + this.normalizers = new AppNormalizers(distDir) } protected async transform( @@ -41,20 +26,20 @@ export class AppPageRouteMatcherProvider extends ManifestRouteMatcherProvider = {} + const allAppPaths: Record = {} for (const page of pages) { const pathname = this.normalizers.pathname.normalize(page) - if (pathname in appPaths) appPaths[pathname].push(page) - else appPaths[pathname] = [page] + if (pathname in allAppPaths) allAppPaths[pathname].push(page) + else allAppPaths[pathname] = [page] } // Format the routes. const matchers: Array = [] - for (const [pathname, paths] of Object.entries(appPaths)) { + for (const [pathname, appPaths] of Object.entries(allAppPaths)) { // TODO-APP: (wyattjoh) this is a hack right now, should be more deterministic - const page = paths[0] + const page = appPaths[0] - const filename = path.join(this.distDir, SERVER_DIRECTORY, manifest[page]) + const filename = this.normalizers.filename.normalize(manifest[page]) const bundlePath = this.normalizers.bundlePath.normalize(page) matchers.push( @@ -64,7 +49,7 @@ export class AppPageRouteMatcherProvider extends ManifestRouteMatcherProvider { - private readonly normalizers = { - pathname: new Normalizers([ - wrapNormalizerFn(normalizeAppPath), - new UnderscoreNormalizer(), - ]), - bundlePath: new PrefixingNormalizer('app'), - } + private readonly normalizers: AppNormalizers - constructor( - private readonly distDir: string, - manifestLoader: ManifestLoader - ) { + constructor(distDir: string, manifestLoader: ManifestLoader) { super(APP_PATHS_MANIFEST, manifestLoader) + + this.normalizers = new AppNormalizers(distDir) } protected async transform( @@ -42,8 +27,8 @@ export class AppRouteRouteMatcherProvider extends ManifestRouteMatcherProvider = [] for (const page of pages) { + const filename = this.normalizers.filename.normalize(manifest[page]) const pathname = this.normalizers.pathname.normalize(page) - const filename = path.join(this.distDir, SERVER_DIRECTORY, manifest[page]) const bundlePath = this.normalizers.bundlePath.normalize(page) matchers.push( diff --git a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts index e6b7fe8b54d9..a88f72ebd4a2 100644 --- a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-page-route-matcher-provider.ts @@ -1,22 +1,12 @@ import { FileReader } from './helpers/file-reader/file-reader' import { AppPageRouteMatcher } from '../../route-matchers/app-page-route-matcher' -import { Normalizer } from '../../normalizers/normalizer' -import { AbsoluteFilenameNormalizer } from '../../normalizers/absolute-filename-normalizer' -import { Normalizers } from '../../normalizers/normalizers' -import { wrapNormalizerFn } from '../../normalizers/wrap-normalizer-fn' -import { normalizeAppPath } from '../../../../shared/lib/router/utils/app-paths' -import { PrefixingNormalizer } from '../../normalizers/prefixing-normalizer' import { RouteKind } from '../../route-kind' import { FileCacheRouteMatcherProvider } from './file-cache-route-matcher-provider' -import { UnderscoreNormalizer } from '../../normalizers/underscore-normalizer' +import { DevAppNormalizers } from '../../normalizers/built/app' export class DevAppPageRouteMatcherProvider extends FileCacheRouteMatcherProvider { private readonly expression: RegExp - private readonly normalizers: { - page: Normalizer - pathname: Normalizer - bundlePath: Normalizer - } + private readonly normalizers: DevAppNormalizers constructor( appDir: string, @@ -25,31 +15,11 @@ export class DevAppPageRouteMatcherProvider extends FileCacheRouteMatcherProvide ) { super(appDir, reader) + this.normalizers = new DevAppNormalizers(appDir, extensions) + // Match any page file that ends with `/page.${extension}` under the app // directory. this.expression = new RegExp(`[/\\\\]page\\.(?:${extensions.join('|')})$`) - - const pageNormalizer = new AbsoluteFilenameNormalizer( - appDir, - extensions, - 'app' - ) - - this.normalizers = { - page: pageNormalizer, - pathname: new Normalizers([ - pageNormalizer, - // The pathname to match should have the trailing `/page` and other route - // group information stripped from it. - wrapNormalizerFn(normalizeAppPath), - new UnderscoreNormalizer(), - ]), - bundlePath: new Normalizers([ - pageNormalizer, - // Prefix the bundle path with `app/`. - new PrefixingNormalizer('app'), - ]), - } } protected async transform( diff --git a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts index 09fce3b53984..d196d697436a 100644 --- a/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts @@ -1,15 +1,10 @@ import { FileReader } from './helpers/file-reader/file-reader' import { AppRouteRouteMatcher } from '../../route-matchers/app-route-route-matcher' import { Normalizer } from '../../normalizers/normalizer' -import { Normalizers } from '../../normalizers/normalizers' -import { AbsoluteFilenameNormalizer } from '../../normalizers/absolute-filename-normalizer' -import { wrapNormalizerFn } from '../../normalizers/wrap-normalizer-fn' -import { normalizeAppPath } from '../../../../shared/lib/router/utils/app-paths' -import { PrefixingNormalizer } from '../../normalizers/prefixing-normalizer' import { RouteKind } from '../../route-kind' import { FileCacheRouteMatcherProvider } from './file-cache-route-matcher-provider' import { isAppRouteRoute } from '../../../../lib/is-app-route-route' -import { UnderscoreNormalizer } from '../../normalizers/underscore-normalizer' +import { DevAppNormalizers } from '../../normalizers/built/app' export class DevAppRouteRouteMatcherProvider extends FileCacheRouteMatcherProvider { private readonly normalizers: { @@ -25,27 +20,7 @@ export class DevAppRouteRouteMatcherProvider extends FileCacheRouteMatcherProvid ) { super(appDir, reader) - const pageNormalizer = new AbsoluteFilenameNormalizer( - appDir, - extensions, - 'app' - ) - - this.normalizers = { - page: pageNormalizer, - pathname: new Normalizers([ - pageNormalizer, - // The pathname to match should have the trailing `/route` and other route - // group information stripped from it. - wrapNormalizerFn(normalizeAppPath), - new UnderscoreNormalizer(), - ]), - bundlePath: new Normalizers([ - pageNormalizer, - // Prefix the bundle path with `app/`. - new PrefixingNormalizer('app'), - ]), - } + this.normalizers = new DevAppNormalizers(appDir, extensions) } protected async transform( diff --git a/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-api-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-api-route-matcher-provider.ts index 1822d7a90be3..beea03ceb75f 100644 --- a/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-api-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-api-route-matcher-provider.ts @@ -1,26 +1,17 @@ -import { Normalizer } from '../../normalizers/normalizer' import { FileReader } from './helpers/file-reader/file-reader' import { PagesAPILocaleRouteMatcher, PagesAPIRouteMatcher, } from '../../route-matchers/pages-api-route-matcher' -import { AbsoluteFilenameNormalizer } from '../../normalizers/absolute-filename-normalizer' -import { Normalizers } from '../../normalizers/normalizers' -import { wrapNormalizerFn } from '../../normalizers/wrap-normalizer-fn' -import { normalizePagePath } from '../../../../shared/lib/page-path/normalize-page-path' -import { PrefixingNormalizer } from '../../normalizers/prefixing-normalizer' import { RouteKind } from '../../route-kind' import path from 'path' import { LocaleRouteNormalizer } from '../../normalizers/locale-route-normalizer' import { FileCacheRouteMatcherProvider } from './file-cache-route-matcher-provider' +import { DevPagesNormalizers } from '../../normalizers/built/pages' export class DevPagesAPIRouteMatcherProvider extends FileCacheRouteMatcherProvider { private readonly expression: RegExp - private readonly normalizers: { - page: Normalizer - pathname: Normalizer - bundlePath: Normalizer - } + private readonly normalizers: DevPagesNormalizers constructor( private readonly pagesDir: string, @@ -34,25 +25,7 @@ export class DevPagesAPIRouteMatcherProvider extends FileCacheRouteMatcherProvid // pages directory. this.expression = new RegExp(`\\.(?:${extensions.join('|')})$`) - const pageNormalizer = new AbsoluteFilenameNormalizer( - pagesDir, - extensions, - 'pages' - ) - - const bundlePathNormalizer = new Normalizers([ - pageNormalizer, - // If the bundle path would have ended in a `/`, add a `index` to it. - wrapNormalizerFn(normalizePagePath), - // Prefix the bundle path with `pages/`. - new PrefixingNormalizer('pages'), - ]) - - this.normalizers = { - page: pageNormalizer, - pathname: pageNormalizer, - bundlePath: bundlePathNormalizer, - } + this.normalizers = new DevPagesNormalizers(pagesDir, extensions) } private test(filename: string): boolean { diff --git a/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-route-matcher-provider.ts index 1d8066bef8fa..f391af162329 100644 --- a/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/dev/dev-pages-route-matcher-provider.ts @@ -1,26 +1,17 @@ -import { Normalizer } from '../../normalizers/normalizer' import { FileReader } from './helpers/file-reader/file-reader' import { PagesRouteMatcher, PagesLocaleRouteMatcher, } from '../../route-matchers/pages-route-matcher' -import { AbsoluteFilenameNormalizer } from '../../normalizers/absolute-filename-normalizer' -import { Normalizers } from '../../normalizers/normalizers' -import { wrapNormalizerFn } from '../../normalizers/wrap-normalizer-fn' -import { normalizePagePath } from '../../../../shared/lib/page-path/normalize-page-path' -import { PrefixingNormalizer } from '../../normalizers/prefixing-normalizer' import { RouteKind } from '../../route-kind' import path from 'path' import { LocaleRouteNormalizer } from '../../normalizers/locale-route-normalizer' import { FileCacheRouteMatcherProvider } from './file-cache-route-matcher-provider' +import { DevPagesNormalizers } from '../../normalizers/built/pages' export class DevPagesRouteMatcherProvider extends FileCacheRouteMatcherProvider { private readonly expression: RegExp - private readonly normalizers: { - page: Normalizer - pathname: Normalizer - bundlePath: Normalizer - } + private readonly normalizers: DevPagesNormalizers constructor( private readonly pagesDir: string, @@ -34,23 +25,7 @@ export class DevPagesRouteMatcherProvider extends FileCacheRouteMatcherProvider< // pages directory. this.expression = new RegExp(`\\.(?:${extensions.join('|')})$`) - const pageNormalizer = new AbsoluteFilenameNormalizer( - pagesDir, - extensions, - 'pages' - ) - - this.normalizers = { - page: pageNormalizer, - pathname: pageNormalizer, - bundlePath: new Normalizers([ - pageNormalizer, - // If the bundle path would have ended in a `/`, add a `index` to it. - wrapNormalizerFn(normalizePagePath), - // Prefix the bundle path with `pages/`. - new PrefixingNormalizer('pages'), - ]), - } + this.normalizers = new DevPagesNormalizers(pagesDir, extensions) } private test(filename: string): boolean { diff --git a/packages/next/src/server/future/route-matcher-providers/pages-api-route-matcher-provider.ts b/packages/next/src/server/future/route-matcher-providers/pages-api-route-matcher-provider.ts index fa6de9781623..eea01a39dbeb 100644 --- a/packages/next/src/server/future/route-matcher-providers/pages-api-route-matcher-provider.ts +++ b/packages/next/src/server/future/route-matcher-providers/pages-api-route-matcher-provider.ts @@ -1,7 +1,5 @@ -import path from '../../../shared/lib/isomorphic/path' import { isAPIRoute } from '../../../lib/is-api-route' -import { PAGES_MANIFEST, SERVER_DIRECTORY } from '../../../shared/lib/constants' -import { normalizePagePath } from '../../../shared/lib/page-path/normalize-page-path' +import { PAGES_MANIFEST } from '../../../shared/lib/constants' import { RouteKind } from '../route-kind' import { PagesAPILocaleRouteMatcher, @@ -13,14 +11,19 @@ import { } from './helpers/manifest-loaders/manifest-loader' import { ManifestRouteMatcherProvider } from './manifest-route-matcher-provider' import { I18NProvider } from '../helpers/i18n-provider' +import { PagesNormalizers } from '../normalizers/built/pages' export class PagesAPIRouteMatcherProvider extends ManifestRouteMatcherProvider { + private readonly normalizers: PagesNormalizers + constructor( - private readonly distDir: string, + distDir: string, manifestLoader: ManifestLoader, private readonly i18nProvider?: I18NProvider ) { super(PAGES_MANIFEST, manifestLoader) + + this.normalizers = new PagesNormalizers(distDir) } protected async transform( @@ -43,8 +46,8 @@ export class PagesAPIRouteMatcherProvider extends ManifestRouteMatcherProvider

{ + private readonly normalizers: PagesNormalizers + constructor( - private readonly distDir: string, + distDir: string, manifestLoader: ManifestLoader, private readonly i18nProvider?: I18NProvider ) { super(PAGES_MANIFEST, manifestLoader) + + this.normalizers = new PagesNormalizers(distDir) } protected async transform( @@ -57,8 +56,8 @@ export class PagesRouteMatcherProvider extends ManifestRouteMatcherProvider Date: Mon, 27 Mar 2023 16:59:53 -0600 Subject: [PATCH 4/4] fix: move metadata config --- .../webpack/loaders/metadata/discover.ts | 24 +------------------ .../src/lib/metadata/is-metadata-route.ts | 23 +++++++++++++++++- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/next/src/build/webpack/loaders/metadata/discover.ts b/packages/next/src/build/webpack/loaders/metadata/discover.ts index b86c81cebf1b..88c862aa11a2 100644 --- a/packages/next/src/build/webpack/loaders/metadata/discover.ts +++ b/packages/next/src/build/webpack/loaders/metadata/discover.ts @@ -5,34 +5,12 @@ import type { } from './types' import path from 'path' import { stringify } from 'querystring' +import { STATIC_METADATA_IMAGES } from '../../../../lib/metadata/is-metadata-route' const METADATA_TYPE = 'metadata' export const METADATA_RESOURCE_QUERY = '?__next_metadata' -export const STATIC_METADATA_IMAGES = { - icon: { - filename: 'icon', - extensions: ['ico', 'jpg', 'jpeg', 'png', 'svg'], - }, - apple: { - filename: 'apple-icon', - extensions: ['jpg', 'jpeg', 'png'], - }, - favicon: { - filename: 'favicon', - extensions: ['ico'], - }, - openGraph: { - filename: 'opengraph-image', - extensions: ['jpg', 'jpeg', 'png', 'gif'], - }, - twitter: { - filename: 'twitter-image', - extensions: ['jpg', 'jpeg', 'png', 'gif'], - }, -} as const - // Produce all compositions with filename (icon, apple-icon, etc.) with extensions (png, jpg, etc.) async function enumMetadataFiles( dir: string, diff --git a/packages/next/src/lib/metadata/is-metadata-route.ts b/packages/next/src/lib/metadata/is-metadata-route.ts index 06b26c7151d2..0023c4a29654 100644 --- a/packages/next/src/lib/metadata/is-metadata-route.ts +++ b/packages/next/src/lib/metadata/is-metadata-route.ts @@ -1,4 +1,25 @@ -import { STATIC_METADATA_IMAGES } from '../../build/webpack/loaders/metadata/discover' +export const STATIC_METADATA_IMAGES = { + icon: { + filename: 'icon', + extensions: ['ico', 'jpg', 'jpeg', 'png', 'svg'], + }, + apple: { + filename: 'apple-icon', + extensions: ['jpg', 'jpeg', 'png'], + }, + favicon: { + filename: 'favicon', + extensions: ['ico'], + }, + openGraph: { + filename: 'opengraph-image', + extensions: ['jpg', 'jpeg', 'png', 'gif'], + }, + twitter: { + filename: 'twitter-image', + extensions: ['jpg', 'jpeg', 'png', 'gif'], + }, +} as const // Match routes that are metadata routes, e.g. /sitemap.xml, /favicon., /., etc. // TODO-METADATA: support more metadata routes with more extensions