diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index 946fa76986781..7181d6f8c9718 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -15,11 +15,11 @@ import { EDGE_RUNTIME_WEBPACK } from '../shared/lib/constants' import { MIDDLEWARE_ROUTE } from '../lib/constants' import { __ApiPreviewProps } from '../server/api-utils' import { isTargetLikeServerless } from '../server/utils' -import { normalizePagePath } from '../server/page-path-utils' -import { normalizePathSep } from '../server/page-path-utils' import { warn } from './output/log' import { parse } from '../build/swc' import { isFlightPage, withoutRSCExtensions } from './utils' +import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' type ObjectValue = T extends { [key: string]: infer V } ? V : never diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index d2a6a04954c39..e096a315d1906 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -63,7 +63,7 @@ import { __ApiPreviewProps } from '../server/api-utils' import loadConfig from '../server/config' import { isTargetLikeServerless } from '../server/utils' import { BuildManifest } from '../server/get-page-files' -import { normalizePagePath } from '../server/page-path-utils' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { getPagePath } from '../server/require' import * as ciEnvironment from '../telemetry/ci-info' import { diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index cd71a50301d47..74e29050550be 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -26,10 +26,6 @@ import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import escapePathDelimiters from '../shared/lib/router/utils/escape-path-delimiters' import { findPageFile } from '../server/lib/find-page-file' import { GetStaticPaths, PageConfig } from 'next/types' -import { - denormalizePagePath, - normalizePagePath, -} from '../server/page-path-utils' import { BuildManifest } from '../server/get-page-files' import { removePathTrailingSlash } from '../client/normalize-trailing-slash' import { UnwrapPromise } from '../lib/coalesced-function' @@ -42,6 +38,8 @@ import isError from '../lib/is-error' import { recursiveDelete } from '../lib/recursive-delete' import { Sema } from 'next/dist/compiled/async-sema' import { MiddlewareManifest } from './webpack/plugins/middleware-plugin' +import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' const { builtinModules } = require('module') const RESERVED_PAGE = /^\/(_app|_error|_document|api(\/|$))/ diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts b/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts index 7041477bd200e..806299a229f48 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts @@ -6,7 +6,7 @@ import { getUtils, vercelHeader, ServerlessHandlerCtx } from './utils' import { renderToHTML } from '../../../../server/render' import { tryGetPreviewData } from '../../../../server/api-utils/node' -import { denormalizePagePath } from '../../../../server/page-path-utils' +import { denormalizePagePath } from '../../../../shared/lib/page-path/denormalize-page-path' import { setLazyProp, getCookieParser } from '../../../../server/api-utils' import { getRedirectStatus } from '../../../../lib/load-custom-routes' import getRouteNoAssetPath from '../../../../shared/lib/router/utils/get-route-from-asset-path' 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 e28741a073826..3aedda04071b1 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts @@ -23,7 +23,7 @@ import { __ApiPreviewProps } from '../../../../server/api-utils' import { acceptLanguage } from '../../../../server/accept-header' import { detectLocaleCookie } from '../../../../shared/lib/i18n/detect-locale-cookie' import { detectDomainLocale } from '../../../../shared/lib/i18n/detect-domain-locale' -import { denormalizePagePath } from '../../../../server/page-path-utils' +import { denormalizePagePath } from '../../../../shared/lib/page-path/denormalize-page-path' import cookie from 'next/dist/compiled/cookie' import { TEMPORARY_REDIRECT_STATUS } from '../../../../shared/lib/constants' import { addRequestMeta } from '../../../../server/request-meta' diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index ddeda3b155e81..8191dd75e7f34 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -34,10 +34,8 @@ import { NextConfigComplete } from '../server/config-shared' import { eventCliSession } from '../telemetry/events' import { hasNextSupport } from '../telemetry/ci-info' import { Telemetry } from '../telemetry/storage' -import { - normalizePagePath, - denormalizePagePath, -} from '../server/page-path-utils' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' +import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import { loadEnvConfig } from '@next/env' import { PrerenderManifest } from '../build' import { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin' diff --git a/packages/next/export/worker.ts b/packages/next/export/worker.ts index 83d4181e1964a..e68d20456d088 100644 --- a/packages/next/export/worker.ts +++ b/packages/next/export/worker.ts @@ -13,7 +13,7 @@ import { loadComponents } from '../server/load-components' import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import { getRouteMatcher } from '../shared/lib/router/utils/route-matcher' import { getRouteRegex } from '../shared/lib/router/utils/route-regex' -import { normalizePagePath } from '../server/page-path-utils' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { SERVER_PROPS_EXPORT_ERROR } from '../lib/constants' import '../server/node-polyfill-fetch' import { requireFontManifest } from '../server/require' diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 9af94b082f63e..f47d30f94a0c8 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -50,7 +50,7 @@ import { isBlockedPage, isBot } from './utils' import RenderResult from './render-result' import { removePathTrailingSlash } from '../client/normalize-trailing-slash' import getRouteFromAssetPath from '../shared/lib/router/utils/get-route-from-asset-path' -import { denormalizePagePath } from './page-path-utils' +import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' import * as Log from '../build/output/log' import { detectDomainLocale } from '../shared/lib/i18n/detect-domain-locale' diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index 798920d3e4317..d17049d479664 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -26,7 +26,8 @@ import { entries, onDemandEntryHandler, } from './on-demand-entry-handler' -import { denormalizePagePath, normalizePathSep } from '../page-path-utils' +import { denormalizePagePath } from '../../shared/lib/page-path/denormalize-page-path' +import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep' import getRouteFromEntrypoint from '../get-route-from-entrypoint' import { fileExists } from '../../lib/file-exists' import { difference } from '../../build/utils' diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 61462cf9dd98c..d23353c1ceeef 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -40,7 +40,8 @@ import { isDynamicRoute, } from '../../shared/lib/router/utils' import Server, { WrappedBuildError } from '../next-server' -import { absolutePathToPage, normalizePagePath } from '../page-path-utils' +import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path' +import { absolutePathToPage } from '../../shared/lib/page-path/absolute-path-to-page' import Router from '../router' import { getPathMatch } from '../../shared/lib/router/utils/path-match' import { hasBasePath, replaceBasePath } from '../router-utils' diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index ef7c8cbf141d0..08eabdff3a532 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -5,8 +5,10 @@ import { EventEmitter } from 'events' import { findPageFile } from '../lib/find-page-file' import { getPageRuntime, runDependingOnPageType } from '../../build/entries' import { join, posix } from 'path' -import { normalizePagePath, normalizePathSep } from '../page-path-utils' -import { ensureLeadingSlash, removePagePathTail } from '../page-path-utils' +import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep' +import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path' +import { ensureLeadingSlash } from '../../shared/lib/page-path/ensure-leading-slash' +import { removePagePathTail } from '../../shared/lib/page-path/remove-page-path-tail' import { pageNotFoundError } from '../require' import { reportTrigger } from '../../build/output' import getRouteFromEntrypoint from '../get-route-from-entrypoint' diff --git a/packages/next/server/get-page-files.ts b/packages/next/server/get-page-files.ts index 9a099951b9c9f..5e6945cbdf53b 100644 --- a/packages/next/server/get-page-files.ts +++ b/packages/next/server/get-page-files.ts @@ -1,4 +1,5 @@ -import { normalizePagePath, denormalizePagePath } from './page-path-utils' +import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' export type BuildManifest = { devFiles: readonly string[] diff --git a/packages/next/server/incremental-cache.ts b/packages/next/server/incremental-cache.ts index 109572a3e98cc..bb309aed2a11b 100644 --- a/packages/next/server/incremental-cache.ts +++ b/packages/next/server/incremental-cache.ts @@ -3,7 +3,7 @@ import type { CacheFs } from '../shared/lib/utils' import LRUCache from 'next/dist/compiled/lru-cache' import path from '../shared/lib/isomorphic/path' import { PrerenderManifest } from '../build' -import { normalizePagePath } from './page-path-utils' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { IncrementalCacheValue, IncrementalCacheEntry } from './response-cache' function toRoute(pathname: string): string { diff --git a/packages/next/server/lib/find-page-file.ts b/packages/next/server/lib/find-page-file.ts index aeab2d1653d18..b7e60c2b7dd56 100644 --- a/packages/next/server/lib/find-page-file.ts +++ b/packages/next/server/lib/find-page-file.ts @@ -1,5 +1,5 @@ import { fileExists } from '../../lib/file-exists' -import { getPagePaths } from '../page-path-utils' +import { getPagePaths } from '../../shared/lib/page-path/get-page-paths' import { nonNullable } from '../../lib/non-nullable' import { join, sep, normalize } from 'path' import { promises } from 'fs' diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 2cef57c694376..c0a5e91ccef76 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -55,7 +55,7 @@ import BaseServer, { stringifyQuery, } from './base-server' import { getMiddlewareInfo, getPagePath, requireFontManifest } from './require' -import { normalizePagePath } from './page-path-utils' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { loadComponents } from './load-components' import isError, { getProperError } from '../lib/is-error' import { FontManifest } from './font-utils' diff --git a/packages/next/server/page-path-utils.ts b/packages/next/server/page-path-utils.ts deleted file mode 100644 index e41a3476faf3d..0000000000000 --- a/packages/next/server/page-path-utils.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { isDynamicRoute } from '../shared/lib/router/utils' -import { join, posix, relative } from '../shared/lib/isomorphic/path' -import { flatten } from '../shared/lib/flatten' - -/** - * For a given page path, this function ensures that there is a leading slash. - * If there is not a leading slash, one is added, otherwise it is noop. - */ -export function ensureLeadingSlash(path: string) { - return path.startsWith('/') ? path : `/${path}` -} - -/** - * For a given page path, this function ensures that there is no backslash - * escaping slashes in the path. Example: - * - `foo\/bar\/baz` -> `foo/bar/baz` - */ -export function normalizePathSep(path: string): string { - return path.replace(/\\/g, '/') -} - -/** - * Removes the file extension for a page and the trailing `index` if it exists - * making sure to not return an empty string. The page head is not touched - * and returned as it is passed. Examples: - * - `/foo/bar/baz/index.js` -> `/foo/bar/baz` - * - `/foo/bar/baz.js` -> `/foo/bar/baz` - * - * @param pagePath A page to a page file (absolute or relative) - * @param extensions Extensions allowed for the page. - */ -export function removePagePathTail(pagePath: string, extensions: string[]) { - return ( - normalizePathSep(pagePath) - .replace(new RegExp(`\\.+(?:${extensions.join('|')})$`), '') - .replace(/\/index$/, '') || '/' - ) -} - -/** - * Takes a page and transforms it into its file counterpart ensuring that the - * output is normalized. Note this function is not idempotent because a page - * `/index` can be referencing `/index/index.js` and `/index/index` could be - * referencing `/index/index/index.js`. Examples: - * - `/` -> `/index` - * - `/index/foo` -> `/index/index/foo` - * - `/index` -> `/index/index` - */ -export function normalizePagePath(page: string): string { - const normalized = ensureLeadingSlash( - /^\/index(\/|$)/.test(page) && !isDynamicRoute(page) - ? `/index${page}` - : page === '/' - ? '/index' - : page - ) - - const resolvedPage = posix.normalize(normalized) - if (resolvedPage !== normalized) { - throw new Error( - `Requested and resolved page mismatch: ${normalized} ${resolvedPage}` - ) - } - - return normalized -} - -/** - * Performs the opposite transformation of `normalizePagePath`. Note that - * this function is not idempotent either in cases where there are multiple - * leading `/index` for the page. Examples: - * - `/index` -> `/` - * - `/index/foo` -> `/foo` - * - `/index/index` -> `/index` - */ -export function denormalizePagePath(page: string) { - let _page = normalizePathSep(page) - return _page.startsWith('/index/') && !isDynamicRoute(_page) - ? _page.slice(6) - : _page !== '/index' - ? _page - : '/' -} - -/** - * Calculate all possible pagePaths for a given normalized pagePath along with - * allowed extensions. This can be used to check which one of the files exists - * and to debug inspected locations. - * - * @param normalizedPagePath Normalized page path (it will denormalize). - * @param extensions Allowed extensions. - */ -export function getPagePaths(normalizedPagePath: string, extensions: string[]) { - const page = denormalizePagePath(normalizedPagePath) - return flatten( - extensions.map((extension) => { - return !normalizedPagePath.endsWith('/index') - ? [`${page}.${extension}`, join(page, `index.${extension}`)] - : [join(page, `index.${extension}`)] - }) - ) -} - -/** - * Given the absolute path to the pages folder, an absolute file path for a - * page and the page extensions, this function will return the page path - * relative to the pages folder. It doesn't consider index tail. Example: - * - `/Users/rick/my-project/pages/foo/bar/baz.js` -> `/foo/bar/baz` - * - * @param pagesDir Absolute path to the pages folder. - * @param filepath Absolute path to the page. - * @param extensions Extensions allowed for the page. - */ -export function absolutePathToPage( - pagesDir: string, - pagePath: string, - extensions: string[] -) { - return removePagePathTail( - normalizePathSep(ensureLeadingSlash(relative(pagesDir, pagePath))), - extensions - ) -} diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 12d481ddaaac4..cd3af5d6d4fdb 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -55,7 +55,8 @@ import { loadGetInitialProps, } from '../shared/lib/utils' import { HtmlContext } from '../shared/lib/html-context' -import { normalizePagePath, denormalizePagePath } from './page-path-utils' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' +import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import { getRequestMeta, NextParsedUrlQuery } from './request-meta' import { allowedStatusCodes, diff --git a/packages/next/server/require.ts b/packages/next/server/require.ts index ce3169afb7da3..ee436ab9b64e9 100644 --- a/packages/next/server/require.ts +++ b/packages/next/server/require.ts @@ -7,8 +7,9 @@ import { SERVER_DIRECTORY, SERVERLESS_DIRECTORY, } from '../shared/lib/constants' -import { normalizePagePath, denormalizePagePath } from './page-path-utils' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' +import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' +import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin' import type { MiddlewareManifest } from '../build/webpack/plugins/middleware-plugin' import type { WasmBinding } from '../build/webpack/loaders/get-module-build-info' diff --git a/packages/next/shared/lib/i18n/get-locale-metadata.ts b/packages/next/shared/lib/i18n/get-locale-metadata.ts index 3847a35edd273..f62273bd6758a 100644 --- a/packages/next/shared/lib/i18n/get-locale-metadata.ts +++ b/packages/next/shared/lib/i18n/get-locale-metadata.ts @@ -1,5 +1,5 @@ import { acceptLanguage } from '../../../server/accept-header' -import { denormalizePagePath } from '../../../server/page-path-utils' +import { denormalizePagePath } from '../page-path/denormalize-page-path' import { detectDomainLocale } from './detect-domain-locale' import { formatUrl } from '../router/utils/format-url' import { normalizeLocalePath } from './normalize-locale-path' diff --git a/packages/next/shared/lib/page-path/absolute-path-to-page.ts b/packages/next/shared/lib/page-path/absolute-path-to-page.ts new file mode 100644 index 0000000000000..6b2cd91a6e3df --- /dev/null +++ b/packages/next/shared/lib/page-path/absolute-path-to-page.ts @@ -0,0 +1,25 @@ +import { ensureLeadingSlash } from './ensure-leading-slash' +import { normalizePathSep } from './normalize-path-sep' +import { relative } from '../isomorphic/path' +import { removePagePathTail } from './remove-page-path-tail' + +/** + * Given the absolute path to the pages folder, an absolute file path for a + * page and the page extensions, this function will return the page path + * relative to the pages folder. It doesn't consider index tail. Example: + * - `/Users/rick/my-project/pages/foo/bar/baz.js` -> `/foo/bar/baz` + * + * @param pagesDir Absolute path to the pages folder. + * @param filepath Absolute path to the page. + * @param extensions Extensions allowed for the page. + */ +export function absolutePathToPage( + pagesDir: string, + pagePath: string, + extensions: string[] +) { + return removePagePathTail( + normalizePathSep(ensureLeadingSlash(relative(pagesDir, pagePath))), + extensions + ) +} diff --git a/packages/next/shared/lib/page-path/denormalize-page-path.ts b/packages/next/shared/lib/page-path/denormalize-page-path.ts new file mode 100644 index 0000000000000..adbc2ad8c44ae --- /dev/null +++ b/packages/next/shared/lib/page-path/denormalize-page-path.ts @@ -0,0 +1,19 @@ +import { isDynamicRoute } from '../router/utils' +import { normalizePathSep } from './normalize-path-sep' + +/** + * Performs the opposite transformation of `normalizePagePath`. Note that + * this function is not idempotent either in cases where there are multiple + * leading `/index` for the page. Examples: + * - `/index` -> `/` + * - `/index/foo` -> `/foo` + * - `/index/index` -> `/index` + */ +export function denormalizePagePath(page: string) { + let _page = normalizePathSep(page) + return _page.startsWith('/index/') && !isDynamicRoute(_page) + ? _page.slice(6) + : _page !== '/index' + ? _page + : '/' +} diff --git a/packages/next/shared/lib/page-path/ensure-leading-slash.ts b/packages/next/shared/lib/page-path/ensure-leading-slash.ts new file mode 100644 index 0000000000000..d23a6d30e84dc --- /dev/null +++ b/packages/next/shared/lib/page-path/ensure-leading-slash.ts @@ -0,0 +1,7 @@ +/** + * For a given page path, this function ensures that there is a leading slash. + * If there is not a leading slash, one is added, otherwise it is noop. + */ +export function ensureLeadingSlash(path: string) { + return path.startsWith('/') ? path : `/${path}` +} diff --git a/packages/next/shared/lib/page-path/get-page-paths.ts b/packages/next/shared/lib/page-path/get-page-paths.ts new file mode 100644 index 0000000000000..bdf9db2124202 --- /dev/null +++ b/packages/next/shared/lib/page-path/get-page-paths.ts @@ -0,0 +1,22 @@ +import { denormalizePagePath } from './denormalize-page-path' +import { flatten } from '../flatten' +import { join } from '../isomorphic/path' + +/** + * Calculate all possible pagePaths for a given normalized pagePath along with + * allowed extensions. This can be used to check which one of the files exists + * and to debug inspected locations. + * + * @param normalizedPagePath Normalized page path (it will denormalize). + * @param extensions Allowed extensions. + */ +export function getPagePaths(normalizedPagePath: string, extensions: string[]) { + const page = denormalizePagePath(normalizedPagePath) + return flatten( + extensions.map((extension) => { + return !normalizedPagePath.endsWith('/index') + ? [`${page}.${extension}`, join(page, `index.${extension}`)] + : [join(page, `index.${extension}`)] + }) + ) +} diff --git a/packages/next/shared/lib/page-path/normalize-page-path.ts b/packages/next/shared/lib/page-path/normalize-page-path.ts new file mode 100644 index 0000000000000..1df8cef5856ad --- /dev/null +++ b/packages/next/shared/lib/page-path/normalize-page-path.ts @@ -0,0 +1,31 @@ +import { ensureLeadingSlash } from './ensure-leading-slash' +import { isDynamicRoute } from '../router/utils' +import { posix } from '../isomorphic/path' + +/** + * Takes a page and transforms it into its file counterpart ensuring that the + * output is normalized. Note this function is not idempotent because a page + * `/index` can be referencing `/index/index.js` and `/index/index` could be + * referencing `/index/index/index.js`. Examples: + * - `/` -> `/index` + * - `/index/foo` -> `/index/index/foo` + * - `/index` -> `/index/index` + */ +export function normalizePagePath(page: string): string { + const normalized = ensureLeadingSlash( + /^\/index(\/|$)/.test(page) && !isDynamicRoute(page) + ? `/index${page}` + : page === '/' + ? '/index' + : page + ) + + const resolvedPage = posix.normalize(normalized) + if (resolvedPage !== normalized) { + throw new Error( + `Requested and resolved page mismatch: ${normalized} ${resolvedPage}` + ) + } + + return normalized +} diff --git a/packages/next/shared/lib/page-path/normalize-path-sep.ts b/packages/next/shared/lib/page-path/normalize-path-sep.ts new file mode 100644 index 0000000000000..a3df0473e4528 --- /dev/null +++ b/packages/next/shared/lib/page-path/normalize-path-sep.ts @@ -0,0 +1,8 @@ +/** + * For a given page path, this function ensures that there is no backslash + * escaping slashes in the path. Example: + * - `foo\/bar\/baz` -> `foo/bar/baz` + */ +export function normalizePathSep(path: string): string { + return path.replace(/\\/g, '/') +} diff --git a/packages/next/shared/lib/page-path/remove-page-path-tail.ts b/packages/next/shared/lib/page-path/remove-page-path-tail.ts new file mode 100644 index 0000000000000..3b0d94df46a19 --- /dev/null +++ b/packages/next/shared/lib/page-path/remove-page-path-tail.ts @@ -0,0 +1,19 @@ +import { normalizePathSep } from './normalize-path-sep' + +/** + * Removes the file extension for a page and the trailing `index` if it exists + * making sure to not return an empty string. The page head is not touched + * and returned as it is passed. Examples: + * - `/foo/bar/baz/index.js` -> `/foo/bar/baz` + * - `/foo/bar/baz.js` -> `/foo/bar/baz` + * + * @param pagePath A page to a page file (absolute or relative) + * @param extensions Extensions allowed for the page. + */ +export function removePagePathTail(pagePath: string, extensions: string[]) { + return ( + normalizePathSep(pagePath) + .replace(new RegExp(`\\.+(?:${extensions.join('|')})$`), '') + .replace(/\/index$/, '') || '/' + ) +} diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index 316c4408eae12..d36d38a9ff64d 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -18,7 +18,7 @@ import { } from '../../../client/route-loader' import { handleClientScriptLoad } from '../../../client/script' import isError, { getProperError } from '../../../lib/is-error' -import { denormalizePagePath } from '../../../server/page-path-utils' +import { denormalizePagePath } from '../page-path/denormalize-page-path' import { normalizeLocalePath } from '../i18n/normalize-locale-path' import mitt from '../mitt' import { diff --git a/test/integration/production-swcminify/test/index.test.js b/test/integration/production-swcminify/test/index.test.js index 348394d03181a..cd4697912c1cb 100644 --- a/test/integration/production-swcminify/test/index.test.js +++ b/test/integration/production-swcminify/test/index.test.js @@ -75,12 +75,12 @@ describe.skip('Production Usage with swcMinify', () => { expect(serverTrace.version).toBe(1) expect( serverTrace.files.some((file) => - file.includes('next/dist/server/send-payload/index.js') + file.includes('next/dist/shared/lib/page-path/normalize-page-path.js') ) ).toBe(true) expect( serverTrace.files.some((file) => - file.includes('next/dist/server/page-path-utils.js') + file.includes('next/dist/shared/lib/page-path/normalize-page-path.js') ) ).toBe(true) expect( diff --git a/test/integration/production/test/index.test.js b/test/integration/production/test/index.test.js index e74639e042889..33029d2b957de 100644 --- a/test/integration/production/test/index.test.js +++ b/test/integration/production/test/index.test.js @@ -105,7 +105,7 @@ describe('Production Usage', () => { ).toBe(true) expect( serverTrace.files.some((file) => - file.includes('next/dist/server/page-path-utils.js') + file.includes('next/dist/shared/lib/page-path/normalize-page-path.js') ) ).toBe(true) expect( diff --git a/test/unit/find-page-file.test.ts b/test/unit/find-page-file.test.ts index b51b39055bbc6..1b468854fe109 100644 --- a/test/unit/find-page-file.test.ts +++ b/test/unit/find-page-file.test.ts @@ -1,6 +1,6 @@ /* eslint-env jest */ import { findPageFile } from 'next/dist/server/lib/find-page-file' -import { normalizePagePath } from 'next/dist/server/page-path-utils' +import { normalizePagePath } from 'packages/next/dist/shared/lib/page-path/normalize-page-path' import { join } from 'path' diff --git a/test/unit/isolated/require-page.test.ts b/test/unit/isolated/require-page.test.ts index 11b0cac9b8a2c..9bb314c25dc67 100644 --- a/test/unit/isolated/require-page.test.ts +++ b/test/unit/isolated/require-page.test.ts @@ -1,8 +1,8 @@ /* eslint-env jest */ import { join } from 'path' -import { normalizePagePath } from 'next/dist/server/page-path-utils' import { SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH } from 'next/constants' +import { normalizePagePath } from 'next/dist/shared/lib/page-path/normalize-page-path' import { requirePage, getPagePath,