diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 82b54a3181dd8dd..6ca7fe421a2c5ea 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -19,7 +19,7 @@ import { getTypeScriptConfiguration } from '../lib/typescript/getTypeScriptConfi import { CLIENT_STATIC_FILES_RUNTIME_AMP, CLIENT_STATIC_FILES_RUNTIME_MAIN, - CLIENT_STATIC_FILES_RUNTIME_POLYFILLS, + CLIENT_STATIC_FILES_RUNTIME_POLYFILLS_SYMBOL, CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, CLIENT_STATIC_FILES_RUNTIME_WEBPACK, REACT_LOADABLE_MANIFEST, @@ -52,6 +52,7 @@ import WebpackConformancePlugin, { } from './webpack/plugins/webpack-conformance-plugin' import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin' import { regexLikeCss } from './webpack/config/blocks/css' +import { CopyFilePlugin } from './webpack/plugins/copy-file-plugin' type ExcludesFalse = (x: T | false) => x is T @@ -378,10 +379,6 @@ export default async function getBaseWebpackConfig( ) ) .replace(/\\/g, '/'), - [CLIENT_STATIC_FILES_RUNTIME_POLYFILLS]: path.join( - NEXT_PROJECT_ROOT_DIST_CLIENT, - 'polyfills.js' - ), } as ClientEntries) : undefined @@ -1343,6 +1340,18 @@ export default async function getBaseWebpackConfig( ].filter(Boolean), }), new WellKnownErrorsPlugin(), + !isServer && + new CopyFilePlugin({ + filePath: require.resolve('./polyfills/polyfill-nomodule'), + cacheKey: process.env.__NEXT_VERSION as string, + name: `static/chunks/polyfills${dev ? '' : '-[hash]'}.js`, + minimize: false, + info: { + [CLIENT_STATIC_FILES_RUNTIME_POLYFILLS_SYMBOL]: 1, + // This file is already minified + minimized: true, + }, + }), ].filter((Boolean as any) as ExcludesFalse), } diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/build/webpack/plugins/build-manifest-plugin.ts index 2f5cb4d23f62431..a6aa31c6c66a1f3 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.ts @@ -8,7 +8,7 @@ import { BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, CLIENT_STATIC_FILES_RUNTIME_MAIN, - CLIENT_STATIC_FILES_RUNTIME_POLYFILLS, + CLIENT_STATIC_FILES_RUNTIME_POLYFILLS_SYMBOL, CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, CLIENT_STATIC_FILES_RUNTIME_AMP, } from '../../../shared/lib/constants' @@ -146,9 +146,24 @@ export default class BuildManifestPlugin { getEntrypointFiles(entrypoints.get(CLIENT_STATIC_FILES_RUNTIME_MAIN)) ) - assetMap.polyfillFiles = getEntrypointFiles( - entrypoints.get(CLIENT_STATIC_FILES_RUNTIME_POLYFILLS) - ).filter((file) => !mainFiles.has(file)) + const compilationAssets: { + name: string + source: typeof sources.RawSource + info: object + }[] = compilation.getAssets() + + assetMap.polyfillFiles = compilationAssets + .filter((p) => { + // Ensure only .js files are passed through + if (!p.name.endsWith('.js')) { + return false + } + + return ( + p.info && CLIENT_STATIC_FILES_RUNTIME_POLYFILLS_SYMBOL in p.info + ) + }) + .map((v) => v.name) assetMap.devFiles = getEntrypointFiles( entrypoints.get(CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH) @@ -160,7 +175,6 @@ export default class BuildManifestPlugin { const systemEntrypoints = new Set([ CLIENT_STATIC_FILES_RUNTIME_MAIN, - CLIENT_STATIC_FILES_RUNTIME_POLYFILLS, CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, CLIENT_STATIC_FILES_RUNTIME_AMP, ]) diff --git a/packages/next/build/webpack/plugins/copy-file-plugin.ts b/packages/next/build/webpack/plugins/copy-file-plugin.ts new file mode 100644 index 000000000000000..ffa40a34af9241c --- /dev/null +++ b/packages/next/build/webpack/plugins/copy-file-plugin.ts @@ -0,0 +1,89 @@ +import { promises as fs } from 'fs' +import loaderUtils from 'next/dist/compiled/loader-utils' +import { + isWebpack5, + sources, + webpack, +} from 'next/dist/compiled/webpack/webpack' + +const PLUGIN_NAME = 'CopyFilePlugin' + +export class CopyFilePlugin { + private filePath: string + private name: string + private cacheKey: string + private info?: object + + constructor({ + filePath, + cacheKey, + name, + info, + }: { + filePath: string + cacheKey: string + name: string + minimize: boolean + info?: object + }) { + this.filePath = filePath + this.cacheKey = cacheKey + this.name = name + this.info = info + } + + apply(compiler: webpack.Compiler) { + compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation: any) => { + const cache = isWebpack5 ? compilation.getCache('CopyFilePlugin') : null + const hook = isWebpack5 + ? // @ts-ignore + compilation.hooks.processAssets + : compilation.hooks.additionalAssets + hook.tapPromise( + isWebpack5 + ? { + name: PLUGIN_NAME, + // @ts-ignore TODO: Remove ignore when webpack 5 is stable + stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, + } + : PLUGIN_NAME, + async () => { + if (cache) { + const cachedResult = await cache.getPromise( + this.filePath, + this.cacheKey + ) + if (cachedResult) { + const { file, source } = cachedResult + compilation.emitAsset(file, source, { + ...this.info, + }) + return + } + } + const content = await fs.readFile(this.filePath, 'utf8') + + const file = loaderUtils.interpolateName( + { resourcePath: this.filePath }, + this.name, + { content, context: compiler.context } + ) + + const source = new sources.RawSource(content) + + if (cache) { + await cache.storePromise(this.filePath, this.cacheKey, { + file, + source, + }) + } + + // @ts-ignore + compilation.emitAsset(file, source, { + ...this.info, + }) + } + ) + }) + } +} diff --git a/packages/next/client/polyfills.js b/packages/next/client/polyfills.js deleted file mode 100644 index 6b8b07f6926307d..000000000000000 --- a/packages/next/client/polyfills.js +++ /dev/null @@ -1 +0,0 @@ -import 'next/dist/build/polyfills/polyfill-nomodule' diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index 3b8e639a378e8ee..d12c9d69e124ddb 100644 --- a/packages/next/shared/lib/constants.ts +++ b/packages/next/shared/lib/constants.ts @@ -32,7 +32,7 @@ export const CLIENT_STATIC_FILES_RUNTIME_AMP = `amp` // static/runtime/webpack.js export const CLIENT_STATIC_FILES_RUNTIME_WEBPACK = `webpack` // static/runtime/polyfills.js -export const CLIENT_STATIC_FILES_RUNTIME_POLYFILLS = `polyfills` +export const CLIENT_STATIC_FILES_RUNTIME_POLYFILLS_SYMBOL = Symbol(`polyfills`) export const TEMPORARY_REDIRECT_STATUS = 307 export const PERMANENT_REDIRECT_STATUS = 308 export const STATIC_PROPS_ID = '__N_SSG'