diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index dade8ac1d5d77..e0e8032c1b27f 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -97,10 +97,11 @@ export function createEntrypoints( Object.keys(pages).forEach((page) => { const absolutePagePath = pages[page] - const bundleFile = `${normalizePagePath(page)}.js` + const bundleFile = normalizePagePath(page) const isApiRoute = page.match(API_ROUTE) - const bundlePath = join('static', 'BUILD_ID', 'pages', bundleFile) + const clientBundlePath = join('static', 'pages', bundleFile) + const serverBundlePath = join('static', 'BUILD_ID', 'pages', bundleFile) const isLikeServerless = isTargetLikeServerless(target) @@ -114,7 +115,7 @@ export function createEntrypoints( serverlessLoaderOptions )}!` } else if (isApiRoute || target === 'server') { - server[bundlePath] = [absolutePagePath] + server[serverBundlePath] = [absolutePagePath] } else if (isLikeServerless && page !== '/_app' && page !== '/_document') { const serverlessLoaderOptions: ServerlessLoaderQuery = { page, @@ -143,7 +144,7 @@ export function createEntrypoints( // might cause the router to not be able to load causing hydration // to fail - client[bundlePath] = + client[clientBundlePath] = page === '/_app' ? [pageLoader, require.resolve('../client/router')] : pageLoader diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 48bc7ba82f4c8..861ec1710748d 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -415,7 +415,7 @@ export default async function build(dir: string, conf = null): Promise { error.indexOf('__next_polyfill__') > -1 ) { throw new Error( - '> webpack config.resolve.alias was incorrectly overriden. https://err.sh/vercel/next.js/invalid-resolve-alias' + '> webpack config.resolve.alias was incorrectly overridden. https://err.sh/vercel/next.js/invalid-resolve-alias' ) } throw new Error('> Build failed because of webpack errors') diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 3cf8cff351e31..a9cf8f87f43e6 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -679,26 +679,24 @@ export default async function getBaseWebpackConfig( }, output: { path: outputPath, - filename: ({ chunk }: { chunk: { name: string } }) => { - // Use `[name]-[contenthash].js` in production - if ( - !dev && - (chunk.name === CLIENT_STATIC_FILES_RUNTIME_MAIN || - chunk.name === CLIENT_STATIC_FILES_RUNTIME_WEBPACK || - chunk.name === CLIENT_STATIC_FILES_RUNTIME_POLYFILLS) - ) { - return chunk.name.replace(/\.js$/, '-[contenthash].js') - } - - if (chunk.name.includes('BUILD_ID')) { - return escapePathVariables(chunk.name).replace( - 'BUILD_ID', - isServer || dev ? buildId : '[contenthash]' - ) - } + filename: isServer + ? ({ chunk }: { chunk: { name: string } }) => { + // Use `[name]-[contenthash].js` in production + if (chunk.name.includes('BUILD_ID')) { + return ( + escapePathVariables(chunk.name).replace( + 'BUILD_ID', + isServer || dev ? buildId : '[contenthash]' + ) + '.js' + ) + } - return '[name]' - }, + return '[name].js' + } + : // Client compilation only + dev + ? '[name].js' + : '[name]-[chunkhash].js', libraryTarget: isServer ? 'commonjs2' : 'var', hotUpdateChunkFilename: isWebpack5 ? 'static/webpack/[id].[fullhash].hot-update.js' diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/build/webpack/plugins/build-manifest-plugin.ts index a5251c4d10228..b588e276d0df8 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.ts @@ -7,6 +7,7 @@ import { CLIENT_STATIC_FILES_RUNTIME_MAIN, CLIENT_STATIC_FILES_RUNTIME_POLYFILLS, CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, + CLIENT_STATIC_FILES_RUNTIME_AMP, } from '../../../next-server/lib/constants' import { BuildManifest } from '../../../next-server/server/get-page-files' import getRouteFromEntrypoint from '../../../next-server/server/get-route-from-entrypoint' @@ -62,6 +63,7 @@ export default class BuildManifestPlugin { const assetMap: BuildManifest = { polyfillFiles: [], devFiles: [], + ampDevFiles: [], lowPriorityFiles: [], pages: { '/_app': [] }, ampFirstPages: [], @@ -98,6 +100,19 @@ export default class BuildManifestPlugin { assetMap.devFiles = reactRefreshChunk?.files.filter(isJsFile) ?? [] for (const entrypoint of compilation.entrypoints.values()) { + const isAmpRuntime = + entrypoint.name === CLIENT_STATIC_FILES_RUNTIME_AMP + + if (isAmpRuntime) { + for (const file of entrypoint.getFiles()) { + if (!(isJsFile(file) || file.endsWith('.css'))) { + continue + } + + assetMap.ampDevFiles.push(file.replace(/\\/g, '/')) + } + continue + } const pagePath = getRouteFromEntrypoint(entrypoint.name) if (!pagePath) { diff --git a/packages/next/build/webpack/plugins/nextjs-ssr-module-cache.ts b/packages/next/build/webpack/plugins/nextjs-ssr-module-cache.ts index 2cab2c2fac62e..ec00655b23beb 100644 --- a/packages/next/build/webpack/plugins/nextjs-ssr-module-cache.ts +++ b/packages/next/build/webpack/plugins/nextjs-ssr-module-cache.ts @@ -1,8 +1,7 @@ import webpack from 'webpack' import { RawSource } from 'webpack-sources' import { join, relative, dirname } from 'path' -import { IS_BUNDLED_PAGE_REGEX } from '../../../next-server/lib/constants' - +import getRouteFromEntrypoint from '../../../next-server/server/get-route-from-entrypoint' const SSR_MODULE_CACHE_FILENAME = 'ssr-module-cache.js' // By default webpack keeps initialized modules per-module. @@ -44,7 +43,8 @@ export default class NextJsSsrImportPlugin { // If the chunk is not part of the pages directory we have to keep the original behavior, // otherwise webpack will error out when the file is used before the compilation finishes // this is the case with mini-css-extract-plugin - if (!IS_BUNDLED_PAGE_REGEX.exec(chunk.name)) { + + if (!getRouteFromEntrypoint(chunk.name)) { return originalFn(source, chunk) } const pagePath = join(outputPath, dirname(chunk.name)) diff --git a/packages/next/build/webpack/plugins/unlink-removed-pages-plugin.ts b/packages/next/build/webpack/plugins/unlink-removed-pages-plugin.ts index 283fd704ecf47..e9c3e9f7a6ebc 100644 --- a/packages/next/build/webpack/plugins/unlink-removed-pages-plugin.ts +++ b/packages/next/build/webpack/plugins/unlink-removed-pages-plugin.ts @@ -1,8 +1,7 @@ import { join } from 'path' import { promises } from 'fs' -import { IS_BUNDLED_PAGE_REGEX } from '../../../next-server/lib/constants' import { Compiler } from 'webpack' - +import getRouteFromEntrypoint from '../../../next-server/server/get-route-from-entrypoint' // Makes sure removed pages are removed from `.next` in development export class UnlinkRemovedPagesPlugin { prevAssets: any @@ -15,7 +14,7 @@ export class UnlinkRemovedPagesPlugin { 'NextJsUnlinkRemovedPages', (compilation, callback) => { const removed = Object.keys(this.prevAssets).filter( - (a) => IS_BUNDLED_PAGE_REGEX.test(a) && !compilation.assets[a] + (a) => getRouteFromEntrypoint(a) && !compilation.assets[a] ) this.prevAssets = compilation.assets diff --git a/packages/next/client/page-loader.js b/packages/next/client/page-loader.js index 969c315fa2232..9e0b93957da1c 100644 --- a/packages/next/client/page-loader.js +++ b/packages/next/client/page-loader.js @@ -248,9 +248,9 @@ export default class PageLoader { route = normalizeRoute(route) let scriptRoute = getAssetPath(route) - const url = `${this.assetPrefix}/_next/static/${encodeURIComponent( - this.buildId - )}/pages${encodeURI(scriptRoute)}.js` + const url = `${this.assetPrefix}/_next/static/pages${encodeURI( + scriptRoute + )}.js` this.loadScript(url, route) } } diff --git a/packages/next/next-server/lib/constants.ts b/packages/next/next-server/lib/constants.ts index 51e1ddd60ddc0..96b1a61c6d5b9 100644 --- a/packages/next/next-server/lib/constants.ts +++ b/packages/next/next-server/lib/constants.ts @@ -21,17 +21,15 @@ export const AMP_RENDER_TARGET = '__NEXT_AMP_RENDER_TARGET__' export const STRING_LITERAL_DROP_BUNDLE = '__NEXT_DROP_CLIENT_FILE__' export const CLIENT_STATIC_FILES_RUNTIME_PATH = `${CLIENT_STATIC_FILES_PATH}/${CLIENT_STATIC_FILES_RUNTIME}` // static/runtime/main.js -export const CLIENT_STATIC_FILES_RUNTIME_MAIN = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/main.js` +export const CLIENT_STATIC_FILES_RUNTIME_MAIN = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/main` // static/runtime/react-refresh.js -export const CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/react-refresh.js` +export const CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/react-refresh` // static/runtime/amp.js -export const CLIENT_STATIC_FILES_RUNTIME_AMP = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/amp.js` +export const CLIENT_STATIC_FILES_RUNTIME_AMP = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/amp` // static/runtime/webpack.js -export const CLIENT_STATIC_FILES_RUNTIME_WEBPACK = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/webpack.js` +export const CLIENT_STATIC_FILES_RUNTIME_WEBPACK = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/webpack` // static/runtime/polyfills.js -export const CLIENT_STATIC_FILES_RUNTIME_POLYFILLS = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/polyfills.js` -// matches static//pages/.js -export const IS_BUNDLED_PAGE_REGEX = /^static[/\\][^/\\]+[/\\]pages.*\.js$/ +export const CLIENT_STATIC_FILES_RUNTIME_POLYFILLS = `${CLIENT_STATIC_FILES_RUNTIME_PATH}/polyfills` export const TEMPORARY_REDIRECT_STATUS = 307 export const PERMANENT_REDIRECT_STATUS = 308 export const STATIC_PROPS_ID = '__N_SSG' diff --git a/packages/next/next-server/server/get-page-files.ts b/packages/next/next-server/server/get-page-files.ts index 0597d5a92c6ce..1e5543ee744da 100644 --- a/packages/next/next-server/server/get-page-files.ts +++ b/packages/next/next-server/server/get-page-files.ts @@ -2,6 +2,7 @@ import { normalizePagePath, denormalizePagePath } from './normalize-page-path' export type BuildManifest = { devFiles: string[] + ampDevFiles: string[] polyfillFiles: string[] lowPriorityFiles: string[] pages: { diff --git a/packages/next/next-server/server/get-route-from-entrypoint.ts b/packages/next/next-server/server/get-route-from-entrypoint.ts index fba96b5188ac8..65c4f75cd1293 100644 --- a/packages/next/next-server/server/get-route-from-entrypoint.ts +++ b/packages/next/next-server/server/get-route-from-entrypoint.ts @@ -1,27 +1,35 @@ import { denormalizePagePath } from './normalize-page-path' // matches static//pages/:page*.js -const ROUTE_NAME_REGEX = /^static[/\\][^/\\]+[/\\]pages[/\\](.*)\.js$/ -const SERVERLESS_ROUTE_NAME_REGEX = /^pages[/\\](.*)\.js$/ +const ROUTE_NAME_REGEX = /^static[/\\][^/\\]+[/\\]pages[/\\](.*)$/ +// matches pages/:page*.js +const SERVERLESS_ROUTE_NAME_REGEX = /^pages[/\\](.*)$/ +// matches static/pages/:page*.js +const BROWSER_ROUTE_NAME_REGEX = /^static[/\\]pages[/\\](.*)$/ -export default function getRouteFromEntrypoint( - entryFile: string, - isServerlessLike: boolean = false -): string | null { - const result = (isServerlessLike - ? SERVERLESS_ROUTE_NAME_REGEX - : ROUTE_NAME_REGEX - ).exec(entryFile) +function matchBundle(regex: RegExp, input: string): string | null { + const result = regex.exec(input) if (!result) { return null } - const pagePath = result[1] + return denormalizePagePath(`/${result[1]}`) +} - if (!pagePath) { - return null +export default function getRouteFromEntrypoint( + entryFile: string, + isServerlessLike: boolean = false +): string | null { + let pagePath = matchBundle( + isServerlessLike ? SERVERLESS_ROUTE_NAME_REGEX : ROUTE_NAME_REGEX, + entryFile + ) + + if (pagePath) { + return pagePath } - return denormalizePagePath(`/${pagePath}`) + // Potentially the passed item is a browser bundle so we try to match that also + return matchBundle(BROWSER_ROUTE_NAME_REGEX, entryFile) } diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 84749437f1576..4fd040cb5196b 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -372,6 +372,7 @@ export default class Server { params.path[0] === 'css' || params.path[0] === 'media' || params.path[0] === this.buildId || + params.path[0] === 'pages' || params.path[1] === 'pages' ) { this.setImmutableAssetCacheControl(res) diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 9215f923da2f6..69019a9bd792e 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -1,12 +1,7 @@ import PropTypes from 'prop-types' import React, { useContext, Component } from 'react' import flush from 'styled-jsx/server' -import { - AMP_RENDER_TARGET, - CLIENT_STATIC_FILES_RUNTIME_AMP, - CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, - CLIENT_STATIC_FILES_RUNTIME_WEBPACK, -} from '../next-server/lib/constants' +import { AMP_RENDER_TARGET } from '../next-server/lib/constants' import { DocumentContext as DocumentComponentContext } from '../next-server/lib/document-context' import { DocumentContext, @@ -619,10 +614,9 @@ export class NextScript extends Component { return null } - const AmpDevFiles = [ - CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, - CLIENT_STATIC_FILES_RUNTIME_AMP, - CLIENT_STATIC_FILES_RUNTIME_WEBPACK, + const ampDevFiles = [ + ...buildManifest.devFiles, + ...buildManifest.ampDevFiles, ] return ( @@ -643,19 +637,17 @@ export class NextScript extends Component { data-ampdevmode /> )} - {AmpDevFiles - ? AmpDevFiles.map((file) => ( -