import ReactRefreshWebpackPlugin from 'next/dist/compiled/@next/react-refresh-utils/dist/ReactRefreshWebpackPlugin' import chalk from 'next/dist/compiled/chalk' import crypto from 'crypto' import { webpack } from 'next/dist/compiled/webpack/webpack' import type { webpack5 } from 'next/dist/compiled/webpack/webpack' import path, { dirname, join as pathJoin, relative as relativePath } from 'path' import { escapeStringRegexp } from '../shared/lib/escape-regexp' import { DOT_NEXT_ALIAS, NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_DIST_CLIENT, PAGES_DIR_ALIAS, ROOT_DIR_ALIAS, APP_DIR_ALIAS, SERVER_RUNTIME, } from '../lib/constants' import { fileExists } from '../lib/file-exists' import { CustomRoutes } from '../lib/load-custom-routes.js' import { CLIENT_STATIC_FILES_RUNTIME_AMP, CLIENT_STATIC_FILES_RUNTIME_MAIN, CLIENT_STATIC_FILES_RUNTIME_MAIN_ROOT, CLIENT_STATIC_FILES_RUNTIME_POLYFILLS_SYMBOL, CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH, CLIENT_STATIC_FILES_RUNTIME_WEBPACK, MIDDLEWARE_REACT_LOADABLE_MANIFEST, REACT_LOADABLE_MANIFEST, SERVERLESS_DIRECTORY, SERVER_DIRECTORY, MODERN_BROWSERSLIST_TARGET, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' import { finalizeEntrypoint } from './entries' import * as Log from './output/log' import { build as buildConfiguration } from './webpack/config' import MiddlewarePlugin, { handleWebpackExtenalForEdgeRuntime, } from './webpack/plugins/middleware-plugin' import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin' import { JsConfigPathsPlugin } from './webpack/plugins/jsconfig-paths-plugin' import { DropClientPage } from './webpack/plugins/next-drop-client-page-plugin' import PagesManifestPlugin from './webpack/plugins/pages-manifest-plugin' import { ProfilingPlugin } from './webpack/plugins/profiling-plugin' import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin' import { ServerlessPlugin } from './webpack/plugins/serverless-plugin' import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin' import { regexLikeCss } from './webpack/config/blocks/css' import { CopyFilePlugin } from './webpack/plugins/copy-file-plugin' import { FlightManifestPlugin } from './webpack/plugins/flight-manifest-plugin' import { ClientEntryPlugin } from './webpack/plugins/client-entry-plugin' import type { Feature, SWC_TARGET_TRIPLE, } from './webpack/plugins/telemetry-plugin' import type { Span } from '../trace' import { withoutRSCExtensions } from './utils' import browserslist from 'next/dist/compiled/browserslist' import loadJsConfig from './load-jsconfig' import { loadBindings } from './swc' import { clientComponentRegex } from './webpack/loaders/utils' const watchOptions = Object.freeze({ aggregateTimeout: 5, ignored: ['**/.git/**', '**/.next/**'], }) function getSupportedBrowsers( dir: string, isDevelopment: boolean, config: NextConfigComplete ): string[] | undefined { let browsers: any try { const browsersListConfig = browserslist.loadConfig({ path: dir, env: isDevelopment ? 'development' : 'production', }) // Running `browserslist` resolves `extends` and other config features into a list of browsers if (browsersListConfig && browsersListConfig.length > 0) { browsers = browserslist(browsersListConfig) } } catch {} // When user has browserslist use that target if (browsers && browsers.length > 0) { return browsers } // When user does not have browserslist use the default target // When `experimental.legacyBrowsers: false` the modern default is used return config.experimental.legacyBrowsers ? undefined : MODERN_BROWSERSLIST_TARGET } type ExcludesFalse = (x: T | false) => x is T const devtoolRevertWarning = execOnce( (devtool: webpack.Configuration['devtool']) => { console.warn( chalk.yellow.bold('Warning: ') + chalk.bold(`Reverting webpack devtool to '${devtool}'.\n`) + 'Changing the webpack devtool in development mode will cause severe performance regressions.\n' + 'Read more: https://nextjs.org/docs/messages/improper-devtool' ) } ) let loggedSwcDisabled = false let loggedIgnoredCompilerOptions = false function getOptimizedAliases(): { [pkg: string]: string } { const stubWindowFetch = path.join(__dirname, 'polyfills', 'fetch', 'index.js') const stubObjectAssign = path.join(__dirname, 'polyfills', 'object-assign.js') const shimAssign = path.join(__dirname, 'polyfills', 'object.assign') return Object.assign( {}, { unfetch$: stubWindowFetch, 'isomorphic-unfetch$': stubWindowFetch, 'whatwg-fetch$': path.join( __dirname, 'polyfills', 'fetch', 'whatwg-fetch.js' ), }, { 'object-assign$': stubObjectAssign, // Stub Package: object.assign 'object.assign/auto': path.join(shimAssign, 'auto.js'), 'object.assign/implementation': path.join( shimAssign, 'implementation.js' ), 'object.assign$': path.join(shimAssign, 'index.js'), 'object.assign/polyfill': path.join(shimAssign, 'polyfill.js'), 'object.assign/shim': path.join(shimAssign, 'shim.js'), // Replace: full URL polyfill with platform-based polyfill url: require.resolve('next/dist/compiled/native-url'), } ) } type ClientEntries = { [key: string]: string | string[] } export function attachReactRefresh( webpackConfig: webpack.Configuration, targetLoader: webpack.RuleSetUseItem ) { let injections = 0 const reactRefreshLoaderName = 'next/dist/compiled/@next/react-refresh-utils/dist/loader' const reactRefreshLoader = require.resolve(reactRefreshLoaderName) webpackConfig.module?.rules.forEach((rule) => { const curr = rule.use // When the user has configured `defaultLoaders.babel` for a input file: if (curr === targetLoader) { ++injections rule.use = [reactRefreshLoader, curr as webpack.RuleSetUseItem] } else if ( Array.isArray(curr) && curr.some((r) => r === targetLoader) && // Check if loader already exists: !curr.some( (r) => r === reactRefreshLoader || r === reactRefreshLoaderName ) ) { ++injections const idx = curr.findIndex((r) => r === targetLoader) // Clone to not mutate user input rule.use = [...curr] // inject / input: [other, babel] output: [other, refresh, babel]: rule.use.splice(idx, 0, reactRefreshLoader) } }) if (injections) { Log.info( `automatically enabled Fast Refresh for ${injections} custom loader${ injections > 1 ? 's' : '' }` ) } } export const NODE_RESOLVE_OPTIONS = { dependencyType: 'commonjs', modules: ['node_modules'], fallback: false, exportsFields: ['exports'], importsFields: ['imports'], conditionNames: ['node', 'require'], descriptionFiles: ['package.json'], extensions: ['.js', '.json', '.node'], enforceExtensions: false, symlinks: true, mainFields: ['main'], mainFiles: ['index'], roots: [], fullySpecified: false, preferRelative: false, preferAbsolute: false, restrictions: [], } export const NODE_BASE_RESOLVE_OPTIONS = { ...NODE_RESOLVE_OPTIONS, alias: false, } export const NODE_ESM_RESOLVE_OPTIONS = { ...NODE_RESOLVE_OPTIONS, alias: false, dependencyType: 'esm', conditionNames: ['node', 'import'], fullySpecified: true, } export const NODE_BASE_ESM_RESOLVE_OPTIONS = { ...NODE_ESM_RESOLVE_OPTIONS, alias: false, } export const nextImageLoaderRegex = /\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i export async function resolveExternal( appDir: string, esmExternalsConfig: NextConfigComplete['experimental']['esmExternals'], context: string, request: string, isEsmRequested: boolean, getResolve: ( options: any ) => ( resolveContext: string, resolveRequest: string ) => Promise<[string | null, boolean]>, isLocalCallback?: (res: string) => any, baseResolveCheck = true, esmResolveOptions: any = NODE_ESM_RESOLVE_OPTIONS, nodeResolveOptions: any = NODE_RESOLVE_OPTIONS, baseEsmResolveOptions: any = NODE_BASE_ESM_RESOLVE_OPTIONS, baseResolveOptions: any = NODE_BASE_RESOLVE_OPTIONS ) { const esmExternals = !!esmExternalsConfig const looseEsmExternals = esmExternalsConfig === 'loose' let res: string | null = null let isEsm: boolean = false let preferEsmOptions = esmExternals && isEsmRequested ? [true, false] : [false] for (const preferEsm of preferEsmOptions) { const resolve = getResolve( preferEsm ? esmResolveOptions : nodeResolveOptions ) // Resolve the import with the webpack provided context, this // ensures we're resolving the correct version when multiple // exist. try { ;[res, isEsm] = await resolve(context, request) } catch (err) { res = null } if (!res) { continue } // ESM externals can only be imported (and not required). // Make an exception in loose mode. if (!isEsmRequested && isEsm && !looseEsmExternals) { continue } if (isLocalCallback) { return { localRes: isLocalCallback(res) } } // Bundled Node.js code is relocated without its node_modules tree. // This means we need to make sure its request resolves to the same // package that'll be available at runtime. If it's not identical, // we need to bundle the code (even if it _should_ be external). if (baseResolveCheck) { let baseRes: string | null let baseIsEsm: boolean try { const baseResolve = getResolve( isEsm ? baseEsmResolveOptions : baseResolveOptions ) ;[baseRes, baseIsEsm] = await baseResolve(appDir, request) } catch (err) { baseRes = null baseIsEsm = false } // Same as above: if the package, when required from the root, // would be different from what the real resolution would use, we // cannot externalize it. // if request is pointing to a symlink it could point to the the same file, // the resolver will resolve symlinks so this is handled if (baseRes !== res || isEsm !== baseIsEsm) { res = null continue } } break } return { res, isEsm } } export default async function getBaseWebpackConfig( dir: string, { buildId, config, compilerType, dev = false, entrypoints, hasReactRoot, isDevFallback = false, pagesDir, reactProductionProfiling = false, rewrites, runWebpackSpan, target = 'server', appDir, middlewareRegex, }: { buildId: string config: NextConfigComplete compilerType: 'client' | 'server' | 'edge-server' dev?: boolean entrypoints: webpack5.EntryObject hasReactRoot: boolean isDevFallback?: boolean pagesDir: string reactProductionProfiling?: boolean rewrites: CustomRoutes['rewrites'] runWebpackSpan: Span target?: string appDir?: string middlewareRegex?: string } ): Promise { const isClient = compilerType === 'client' const isEdgeServer = compilerType === 'edge-server' const isNodeServer = compilerType === 'server' const { useTypeScript, jsConfig, resolvedBaseUrl } = await loadJsConfig( dir, config ) const supportedBrowsers = await getSupportedBrowsers(dir, dev, config) const hasRewrites = rewrites.beforeFiles.length > 0 || rewrites.afterFiles.length > 0 || rewrites.fallback.length > 0 if (isClient && !hasReactRoot) { if (config.experimental.runtime) { throw new Error( '`experimental.runtime` requires React 18 to be installed.' ) } if (config.experimental.serverComponents) { throw new Error( '`experimental.serverComponents` requires React 18 to be installed.' ) } } const hasConcurrentFeatures = hasReactRoot const hasServerComponents = hasConcurrentFeatures && !!config.experimental.serverComponents const disableOptimizedLoading = hasConcurrentFeatures ? true : config.experimental.disableOptimizedLoading if (isClient) { if (config.experimental.runtime === SERVER_RUNTIME.edge) { Log.warn( 'You are using the experimental Edge Runtime with `experimental.runtime`.' ) } if (config.experimental.runtime === 'nodejs') { Log.warn( 'You are using the experimental Node.js Runtime with `experimental.runtime`.' ) } if (hasServerComponents) { Log.warn( 'You have experimental React Server Components enabled. Continue at your own risk.' ) } } const babelConfigFile = await [ '.babelrc', '.babelrc.json', '.babelrc.js', '.babelrc.mjs', '.babelrc.cjs', 'babel.config.js', 'babel.config.json', 'babel.config.mjs', 'babel.config.cjs', ].reduce(async (memo: Promise, filename) => { const configFilePath = path.join(dir, filename) return ( (await memo) || ((await fileExists(configFilePath)) ? configFilePath : undefined) ) }, Promise.resolve(undefined)) const distDir = path.join(dir, config.distDir) let useSWCLoader = !babelConfigFile || config.experimental.forceSwcTransforms let SWCBinaryTarget: [Feature, boolean] | undefined = undefined if (useSWCLoader) { // TODO: we do not collect wasm target yet const binaryTarget = require('./swc')?.getBinaryMetadata?.() ?.target as SWC_TARGET_TRIPLE SWCBinaryTarget = binaryTarget ? [`swc/target/${binaryTarget}` as const, true] : undefined } if (!loggedSwcDisabled && !useSWCLoader && babelConfigFile) { Log.info( `Disabled SWC as replacement for Babel because of custom Babel configuration "${path.relative( dir, babelConfigFile )}" https://nextjs.org/docs/messages/swc-disabled` ) loggedSwcDisabled = true } // eagerly load swc bindings instead of waiting for transform calls if (!babelConfigFile && isClient) { await loadBindings() } if (!loggedIgnoredCompilerOptions && !useSWCLoader && config.compiler) { Log.info( '`compiler` options in `next.config.js` will be ignored while using Babel https://nextjs.org/docs/messages/ignored-compiler-options' ) loggedIgnoredCompilerOptions = true } const getBabelOrSwcLoader = () => { if (useSWCLoader && config?.experimental?.swcTraceProfiling) { // This will init subscribers once only in a single process lifecycle, // even though it can be called multiple times. // Subscriber need to be initialized _before_ any actual swc's call (transform, etcs) // to collect correct trace spans when they are called. require('./swc')?.initCustomTraceSubscriber?.( path.join(distDir, `swc-trace-profile-${Date.now()}.json`) ) } return useSWCLoader ? { loader: 'next-swc-loader', options: { isServer: isNodeServer || isEdgeServer, pagesDir, hasReactRefresh: dev && isClient, fileReading: config.experimental.swcFileReading, nextConfig: config, jsConfig, supportedBrowsers: config.experimental.browsersListForSwc ? supportedBrowsers : undefined, swcCacheDir: path.join( dir, config?.distDir ?? '.next', 'cache', 'swc' ), }, } : { loader: require.resolve('./babel/loader/index'), options: { configFile: babelConfigFile, isServer: isNodeServer || isEdgeServer, distDir, pagesDir, cwd: dir, development: dev, hasReactRefresh: dev && isClient, hasJsxRuntime: true, }, } } const defaultLoaders = { babel: getBabelOrSwcLoader(), } const rawPageExtensions = hasServerComponents ? withoutRSCExtensions(config.pageExtensions) : config.pageExtensions const serverComponentsRegex = new RegExp( `\\.server\\.(${rawPageExtensions.join('|')})$` ) const babelIncludeRegexes: RegExp[] = [ /next[\\/]dist[\\/]shared[\\/]lib/, /next[\\/]dist[\\/]client/, /next[\\/]dist[\\/]pages/, /[\\/](strip-ansi|ansi-regex)[\\/]/, ] // Support for NODE_PATH const nodePathList = (process.env.NODE_PATH || '') .split(process.platform === 'win32' ? ';' : ':') .filter((p) => !!p) // Intentionally not using isTargetLikeServerless helper const isLikeServerless = target === 'serverless' || target === 'experimental-serverless-trace' const outputPath = isNodeServer || isEdgeServer ? path.join( distDir, isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY ) : distDir const clientEntries = isClient ? ({ // Backwards compatibility 'main.js': [], ...(dev ? { [CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH]: require.resolve( `next/dist/compiled/@next/react-refresh-utils/dist/runtime` ), [CLIENT_STATIC_FILES_RUNTIME_AMP]: `./` + relativePath( dir, pathJoin(NEXT_PROJECT_ROOT_DIST_CLIENT, 'dev', 'amp-dev') ).replace(/\\/g, '/'), } : {}), [CLIENT_STATIC_FILES_RUNTIME_MAIN]: `./` + path .relative( dir, path.join( NEXT_PROJECT_ROOT_DIST_CLIENT, dev ? `next-dev.js` : 'next.js' ) ) .replace(/\\/g, '/'), ...(config.experimental.appDir ? { [CLIENT_STATIC_FILES_RUNTIME_MAIN_ROOT]: dev ? [ require.resolve( `next/dist/compiled/@next/react-refresh-utils/dist/runtime` ), `./` + path .relative( dir, path.join( NEXT_PROJECT_ROOT_DIST_CLIENT, 'app-next-dev.js' ) ) .replace(/\\/g, '/'), ] : `./` + path .relative( dir, path.join(NEXT_PROJECT_ROOT_DIST_CLIENT, 'app-next.js') ) .replace(/\\/g, '/'), } : {}), } as ClientEntries) : undefined function getReactProfilingInProduction() { if (reactProductionProfiling) { return { 'react-dom$': 'react-dom/profiling', 'scheduler/tracing': 'scheduler/tracing-profiling', } } } // tell webpack where to look for _app and _document // using aliases to allow falling back to the default // version when removed or not present const clientResolveRewrites = require.resolve( '../shared/lib/router/utils/resolve-rewrites' ) const customAppAliases: { [key: string]: string[] } = {} const customErrorAlias: { [key: string]: string[] } = {} const customDocumentAliases: { [key: string]: string[] } = {} const customRootAliases: { [key: string]: string[] } = {} if (dev) { customAppAliases[`${PAGES_DIR_ALIAS}/_app`] = [ ...rawPageExtensions.reduce((prev, ext) => { prev.push(path.join(pagesDir, `_app.${ext}`)) return prev }, [] as string[]), 'next/dist/pages/_app.js', ] customAppAliases[`${PAGES_DIR_ALIAS}/_error`] = [ ...rawPageExtensions.reduce((prev, ext) => { prev.push(path.join(pagesDir, `_error.${ext}`)) return prev }, [] as string[]), 'next/dist/pages/_error.js', ] customDocumentAliases[`${PAGES_DIR_ALIAS}/_document`] = [ ...rawPageExtensions.reduce((prev, ext) => { prev.push(path.join(pagesDir, `_document.${ext}`)) return prev }, [] as string[]), `next/dist/pages/_document.js`, ] } const reactDir = dirname(require.resolve('react/package.json')) const reactDomDir = dirname(require.resolve('react-dom/package.json')) const mainFieldsPerCompiler: Record = { server: ['main', 'module'], client: ['browser', 'module', 'main'], 'edge-server': ['browser', 'module', 'main'], } const resolveConfig = { // Disable .mjs for node_modules bundling extensions: isNodeServer ? [ '.js', '.mjs', ...(useTypeScript ? ['.tsx', '.ts'] : []), '.jsx', '.json', '.wasm', ] : [ '.mjs', '.js', ...(useTypeScript ? ['.tsx', '.ts'] : []), '.jsx', '.json', '.wasm', ], modules: [ 'node_modules', ...nodePathList, // Support for NODE_PATH environment variable ], alias: { next: NEXT_PROJECT_ROOT, react: `${reactDir}`, 'react-dom$': `${reactDomDir}`, 'react-dom/server$': `${reactDomDir}/server`, 'react-dom/server.browser$': `${reactDomDir}/server.browser`, 'react-dom/client$': `${reactDomDir}/client`, ...customAppAliases, ...customErrorAlias, ...customDocumentAliases, ...customRootAliases, [PAGES_DIR_ALIAS]: pagesDir, ...(appDir ? { [APP_DIR_ALIAS]: appDir, } : {}), [ROOT_DIR_ALIAS]: dir, [DOT_NEXT_ALIAS]: distDir, ...(isClient || isEdgeServer ? getOptimizedAliases() : {}), ...getReactProfilingInProduction(), ...(isClient || isEdgeServer ? { [clientResolveRewrites]: hasRewrites ? clientResolveRewrites : // With webpack 5 an alias can be pointed to false to noop false, } : {}), '@swc/helpers': path.dirname( require.resolve('@swc/helpers/package.json') ), setimmediate: 'next/dist/compiled/setimmediate', }, ...(isClient || isEdgeServer ? { fallback: { process: require.resolve('./polyfills/process'), }, } : undefined), mainFields: mainFieldsPerCompiler[compilerType], plugins: [], } const terserOptions: any = { parse: { ecma: 8, }, compress: { ecma: 5, warnings: false, // The following two options are known to break valid JavaScript code comparisons: false, inline: 2, // https://github.com/vercel/next.js/issues/7178#issuecomment-493048965 }, mangle: { safari10: true }, output: { ecma: 5, safari10: true, comments: false, // Fixes usage of Emoji and certain Regex ascii_only: true, }, } const isModuleCSS = (module: { type: string }): boolean => { return ( // mini-css-extract-plugin module.type === `css/mini-extract` || // extract-css-chunks-webpack-plugin (old) module.type === `css/extract-chunks` || // extract-css-chunks-webpack-plugin (new) module.type === `css/extract-css-chunks` ) } // Packages which will be split into the 'framework' chunk. // Only top-level packages are included, e.g. nested copies like // 'node_modules/meow/node_modules/object-assign' are not included. const topLevelFrameworkPaths: string[] = [] const visitedFrameworkPackages = new Set() // Adds package-paths of dependencies recursively const addPackagePath = (packageName: string, relativeToPath: string) => { try { if (visitedFrameworkPackages.has(packageName)) { return } visitedFrameworkPackages.add(packageName) const packageJsonPath = require.resolve(`${packageName}/package.json`, { paths: [relativeToPath], }) // Include a trailing slash so that a `.startsWith(packagePath)` check avoids false positives // when one package name starts with the full name of a different package. // For example: // "node_modules/react-slider".startsWith("node_modules/react") // true // "node_modules/react-slider".startsWith("node_modules/react/") // false const directory = path.join(packageJsonPath, '../') // Returning from the function in case the directory has already been added and traversed if (topLevelFrameworkPaths.includes(directory)) return topLevelFrameworkPaths.push(directory) const dependencies = require(packageJsonPath).dependencies || {} for (const name of Object.keys(dependencies)) { addPackagePath(name, directory) } } catch (_) { // don't error on failing to resolve framework packages } } for (const packageName of ['react', 'react-dom']) { addPackagePath(packageName, dir) } const crossOrigin = config.crossOrigin const looseEsmExternals = config.experimental?.esmExternals === 'loose' async function handleExternals( context: string, request: string, dependencyType: string, getResolve: ( options: any ) => ( resolveContext: string, resolveRequest: string ) => Promise<[string | null, boolean]> ) { // We need to externalize internal requests for files intended to // not be bundled. const isLocal: boolean = request.startsWith('.') || // Always check for unix-style path, as webpack sometimes // normalizes as posix. path.posix.isAbsolute(request) || // When on Windows, we also want to check for Windows-specific // absolute paths. (process.platform === 'win32' && path.win32.isAbsolute(request)) // make sure import "next" shows a warning when imported // in pages/components if (request === 'next') { return `commonjs next/dist/lib/import-next-warning` } // Relative requires don't need custom resolution, because they // are relative to requests we've already resolved here. // Absolute requires (require('/foo')) are extremely uncommon, but // also have no need for customization as they're already resolved. if (!isLocal) { if (/^(?:next$|react(?:$|\/))/.test(request)) { return `commonjs ${request}` } const notExternalModules = /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|future\/image|constants|dynamic|script)$)|string-hash$)/ if (notExternalModules.test(request)) { return } } // @swc/helpers should not be external as it would // require hoisting the package which we can't rely on if (request.includes('@swc/helpers')) { return } // When in esm externals mode, and using import, we resolve with // ESM resolving options. const isEsmRequested = dependencyType === 'esm' const isLocalCallback = (localRes: string) => { // Makes sure dist/shared and dist/server are not bundled // we need to process shared `router/router` and `dynamic`, // so that the DefinePlugin can inject process.env values const isNextExternal = /next[/\\]dist[/\\](shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic))/.test( localRes ) if (isNextExternal) { // Generate Next.js external import const externalRequest = path.posix.join( 'next', 'dist', path .relative( // Root of Next.js package: path.join(__dirname, '..'), localRes ) // Windows path normalization .replace(/\\/g, '/') ) return `commonjs ${externalRequest}` } else { // We don't want to retry local requests // with other preferEsm options return } } const resolveResult = await resolveExternal( dir, config.experimental.esmExternals, context, request, isEsmRequested, getResolve, isLocal ? isLocalCallback : undefined ) if ('localRes' in resolveResult) { return resolveResult.localRes } const { res, isEsm } = resolveResult // If the request cannot be resolved we need to have // webpack "bundle" it so it surfaces the not found error. if (!res) { return } // ESM externals can only be imported (and not required). // Make an exception in loose mode. if (!isEsmRequested && isEsm && !looseEsmExternals) { throw new Error( `ESM packages (${request}) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals` ) } const externalType = isEsm ? 'module' : 'commonjs' if ( res.match(/next[/\\]dist[/\\]shared[/\\](?!lib[/\\]router[/\\]router)/) || res.match(/next[/\\]dist[/\\]compiled[/\\].*\.[mc]?js$/) ) { return `${externalType} ${request}` } // Default pages have to be transpiled if ( res.match(/[/\\]next[/\\]dist[/\\]/) || // This is the @babel/plugin-transform-runtime "helpers: true" option res.match(/node_modules[/\\]@babel[/\\]runtime[/\\]/) ) { return } // Webpack itself has to be compiled because it doesn't always use module relative paths if ( res.match(/node_modules[/\\]webpack/) || res.match(/node_modules[/\\]css-loader/) ) { return } // Anything else that is standard JavaScript within `node_modules` // can be externalized. if (/node_modules[/\\].*\.[mc]?js$/.test(res)) { return `${externalType} ${request}` } // Default behavior: bundle the code! } const codeCondition = { test: /\.(tsx|ts|js|cjs|mjs|jsx)$/, ...(config.experimental.externalDir ? // Allowing importing TS/TSX files from outside of the root dir. {} : { include: [dir, ...babelIncludeRegexes] }), exclude: (excludePath: string) => { if (babelIncludeRegexes.some((r) => r.test(excludePath))) { return false } return /node_modules/.test(excludePath) }, } const serverComponentCodeCondition = { test: serverComponentsRegex, include: [dir, /next[\\/]dist[\\/]pages/], } const rscSharedRegex = /(node_modules\/react\/|\/shared\/lib\/(head-manager-context|router-context)\.js|node_modules\/styled-jsx\/)/ let webpackConfig: webpack.Configuration = { parallelism: Number(process.env.NEXT_WEBPACK_PARALLELISM) || undefined, // @ts-ignore externals: isClient || isEdgeServer ? // make sure importing "next" is handled gracefully for client // bundles in case a user imported types and it wasn't removed // TODO: should we warn/error for this instead? [ 'next', ...(isEdgeServer ? [ { '@builder.io/partytown': '{}', 'next/dist/compiled/etag': '{}', 'next/dist/compiled/chalk': '{}', 'react-dom': '{}', }, handleWebpackExtenalForEdgeRuntime, ] : []), ] : target !== 'serverless' ? [ ({ context, request, dependencyType, getResolve, }: { context: string request: string dependencyType: string getResolve: ( options: any ) => ( resolveContext: string, resolveRequest: string, callback: ( err?: Error, result?: string, resolveData?: { descriptionFileData?: { type?: any } } ) => void ) => void }) => handleExternals(context, request, dependencyType, (options) => { const resolveFunction = getResolve(options) return (resolveContext: string, requestToResolve: string) => new Promise((resolve, reject) => { resolveFunction( resolveContext, requestToResolve, (err, result, resolveData) => { if (err) return reject(err) if (!result) return resolve([null, false]) const isEsm = /\.js$/i.test(result) ? resolveData?.descriptionFileData?.type === 'module' : /\.mjs$/i.test(result) resolve([result, isEsm]) } ) }) }), ] : [ // When the 'serverless' target is used all node_modules will be compiled into the output bundles // So that the 'serverless' bundles have 0 runtime dependencies 'next/dist/compiled/@ampproject/toolbox-optimizer', // except this one // Mark this as external if not enabled so it doesn't cause a // webpack error from being missing ...(config.experimental.optimizeCss ? [] : ['critters']), ], optimization: { // @ts-ignore: TODO remove ts-ignore when webpack 4 is removed emitOnErrors: !dev, checkWasmTypes: false, nodeEnv: false, ...(hasServerComponents ? { // We have to use the names here instead of hashes to ensure the consistency between compilers. moduleIds: isClient ? 'deterministic' : 'named', } : {}), splitChunks: ((): webpack.Options.SplitChunksOptions | false => { // For the edge runtime, we have to bundle all dependencies inside without dynamic `require`s. // To make some dependencies like `react` to be shared between entrypoints, we use a special // cache group here even under dev mode. const edgeRSCCacheGroups = hasServerComponents ? { rscDeps: { enforce: true, name: 'rsc-runtime-deps', filename: 'rsc-runtime-deps.js', test: rscSharedRegex, }, } : undefined if (isEdgeServer && edgeRSCCacheGroups) { return { cacheGroups: edgeRSCCacheGroups, } } if (dev) { return false } if (isNodeServer) { return { // @ts-ignore filename: '[name].js', chunks: 'all', minSize: 1000, } } if (isEdgeServer) { return { // @ts-ignore filename: 'edge-chunks/[name].js', chunks: 'all', minChunks: 2, cacheGroups: edgeRSCCacheGroups, } } return { // Keep main and _app chunks unsplitted in webpack 5 // as we don't need a separate vendor chunk from that // and all other chunk depend on them so there is no // duplication that need to be pulled out. chunks: (chunk) => !/^(polyfills|main|pages\/_app)$/.test(chunk.name), cacheGroups: { framework: { chunks: 'all', name: 'framework', test(module) { const resource = module.nameForCondition?.() return resource ? topLevelFrameworkPaths.some((pkgPath) => resource.startsWith(pkgPath) ) : false }, priority: 40, // Don't let webpack eliminate this chunk (prevents this chunk from // becoming a part of the commons chunk) enforce: true, }, lib: { test(module: { size: Function nameForCondition: Function }): boolean { return ( module.size() > 160000 && /node_modules[/\\]/.test(module.nameForCondition() || '') ) }, name(module: { type: string libIdent?: Function updateHash: (hash: crypto.Hash) => void }): string { const hash = crypto.createHash('sha1') if (isModuleCSS(module)) { module.updateHash(hash) } else { if (!module.libIdent) { throw new Error( `Encountered unknown module type: ${module.type}. Please open an issue.` ) } hash.update(module.libIdent({ context: dir })) } return hash.digest('hex').substring(0, 8) }, priority: 30, minChunks: 1, reuseExistingChunk: true, }, }, maxInitialRequests: 25, minSize: 20000, } })(), runtimeChunk: isClient ? { name: CLIENT_STATIC_FILES_RUNTIME_WEBPACK } : undefined, minimize: !dev && isClient, minimizer: [ // Minify JavaScript (compiler: webpack.Compiler) => { // @ts-ignore No typings yet const { TerserPlugin, } = require('./webpack/plugins/terser-webpack-plugin/src/index.js') new TerserPlugin({ cacheDir: path.join(distDir, 'cache', 'next-minifier'), parallel: config.experimental.cpus, swcMinify: config.swcMinify, terserOptions: { ...terserOptions, compress: { ...terserOptions.compress, ...(config.experimental.swcMinifyDebugOptions?.compress ?? {}), }, mangle: { ...terserOptions.mangle, ...(config.experimental.swcMinifyDebugOptions?.mangle ?? {}), }, }, }).apply(compiler) }, // Minify CSS (compiler: webpack.Compiler) => { const { CssMinimizerPlugin, } = require('./webpack/plugins/css-minimizer-plugin') new CssMinimizerPlugin({ postcssOptions: { map: { // `inline: false` generates the source map in a separate file. // Otherwise, the CSS file is needlessly large. inline: false, // `annotation: false` skips appending the `sourceMappingURL` // to the end of the CSS file. Webpack already handles this. annotation: false, }, }, }).apply(compiler) }, ], }, context: dir, // Kept as function to be backwards compatible // @ts-ignore TODO webpack 5 typings needed entry: async () => { return { ...(clientEntries ? clientEntries : {}), ...entrypoints, } }, watchOptions, output: { // we must set publicPath to an empty value to override the default of // auto which doesn't work in IE11 publicPath: `${config.assetPrefix || ''}/_next/`, path: !dev && isNodeServer ? path.join(outputPath, 'chunks') : outputPath, // On the server we don't use hashes filename: isNodeServer || isEdgeServer ? dev || isEdgeServer ? `[name].js` : `../[name].js` : `static/chunks/${isDevFallback ? 'fallback/' : ''}[name]${ dev ? '' : appDir ? '-[chunkhash]' : '-[contenthash]' }.js`, library: isClient || isEdgeServer ? '_N_E' : undefined, libraryTarget: isClient || isEdgeServer ? 'assign' : 'commonjs2', hotUpdateChunkFilename: 'static/webpack/[id].[fullhash].hot-update.js', hotUpdateMainFilename: 'static/webpack/[fullhash].[runtime].hot-update.json', // This saves chunks with the name given via `import()` chunkFilename: isNodeServer || isEdgeServer ? '[name].js' : `static/chunks/${isDevFallback ? 'fallback/' : ''}${ dev ? '[name]' : '[name].[contenthash]' }.js`, strictModuleExceptionHandling: true, crossOriginLoading: crossOrigin, webassemblyModuleFilename: 'static/wasm/[modulehash].wasm', hashFunction: 'xxhash64', hashDigestLength: 16, }, performance: false, resolve: resolveConfig, resolveLoader: { // The loaders Next.js provides alias: [ 'error-loader', 'next-swc-loader', 'next-client-pages-loader', 'next-image-loader', 'next-serverless-loader', 'next-style-loader', 'next-flight-client-loader', 'next-flight-server-loader', 'next-flight-client-entry-loader', 'noop-loader', 'next-middleware-loader', 'next-edge-function-loader', 'next-edge-ssr-loader', 'next-middleware-asset-loader', 'next-middleware-wasm-loader', 'next-app-loader', ].reduce((alias, loader) => { // using multiple aliases to replace `resolveLoader.modules` alias[loader] = path.join(__dirname, 'webpack', 'loaders', loader) return alias }, {} as Record), modules: [ 'node_modules', ...nodePathList, // Support for NODE_PATH environment variable ], plugins: [], }, module: { rules: [ // TODO: FIXME: do NOT webpack 5 support with this // x-ref: https://github.com/webpack/webpack/issues/11467 ...(!config.experimental.fullySpecified ? [ { test: /\.m?js/, resolve: { fullySpecified: false, }, } as any, ] : []), ...(hasServerComponents ? isNodeServer || isEdgeServer ? [ // RSC server compilation loaders { ...serverComponentCodeCondition, issuerLayer: 'sc_server', use: { loader: 'next-flight-server-loader', }, }, { test: clientComponentRegex, issuerLayer: 'sc_server', use: { loader: 'next-flight-client-loader', }, }, // _app should be treated as a client component as well as all its dependencies. { test: new RegExp(`_app\\.(${rawPageExtensions.join('|')})$`), layer: 'sc_client', }, ] : [] : []), ...(hasServerComponents && isEdgeServer ? [ // Move shared dependencies from sc_server and sc_client into the // same layer. { test: rscSharedRegex, layer: 'rsc_shared_deps', }, ] : []), { test: /\.(js|cjs|mjs)$/, issuerLayer: 'api', parser: { // Switch back to normal URL handling url: true, }, }, { oneOf: [ { ...codeCondition, issuerLayer: 'api', parser: { // Switch back to normal URL handling url: true, }, use: defaultLoaders.babel, }, { ...codeCondition, issuerLayer: 'middleware', use: getBabelOrSwcLoader(), }, { ...codeCondition, use: dev && isClient ? [ require.resolve( 'next/dist/compiled/@next/react-refresh-utils/dist/loader' ), defaultLoaders.babel, ] : defaultLoaders.babel, }, ], }, ...(!config.images.disableStaticImages ? [ { test: nextImageLoaderRegex, loader: 'next-image-loader', issuer: { not: regexLikeCss }, dependency: { not: ['url'] }, options: { isServer: isNodeServer || isEdgeServer, isDev: dev, basePath: config.basePath, assetPrefix: config.assetPrefix, }, }, ] : []), ...(isEdgeServer || isClient ? [ { oneOf: [ { issuerLayer: 'middleware', resolve: { fallback: { process: require.resolve('./polyfills/process'), }, }, }, { resolve: { // Full list of old polyfills is accessible here: // https://github.com/webpack/webpack/blob/2a0536cf510768111a3a6dceeb14cb79b9f59273/lib/ModuleNotFoundError.js#L13-L42 fallback: { assert: require.resolve('next/dist/compiled/assert'), buffer: require.resolve('next/dist/compiled/buffer/'), constants: require.resolve( 'next/dist/compiled/constants-browserify' ), crypto: require.resolve( 'next/dist/compiled/crypto-browserify' ), domain: require.resolve( 'next/dist/compiled/domain-browser' ), http: require.resolve('next/dist/compiled/stream-http'), https: require.resolve( 'next/dist/compiled/https-browserify' ), os: require.resolve('next/dist/compiled/os-browserify'), path: require.resolve( 'next/dist/compiled/path-browserify' ), punycode: require.resolve( 'next/dist/compiled/punycode' ), process: require.resolve('./polyfills/process'), // Handled in separate alias querystring: require.resolve( 'next/dist/compiled/querystring-es3' ), stream: require.resolve( 'next/dist/compiled/stream-browserify' ), string_decoder: require.resolve( 'next/dist/compiled/string_decoder' ), sys: require.resolve('next/dist/compiled/util/'), timers: require.resolve( 'next/dist/compiled/timers-browserify' ), tty: require.resolve( 'next/dist/compiled/tty-browserify' ), // Handled in separate alias // url: require.resolve('url/'), util: require.resolve('next/dist/compiled/util/'), vm: require.resolve('next/dist/compiled/vm-browserify'), zlib: require.resolve( 'next/dist/compiled/browserify-zlib' ), events: require.resolve('next/dist/compiled/events/'), setImmediate: require.resolve( 'next/dist/compiled/setimmediate' ), }, }, }, ], }, ] : []), ].filter(Boolean), }, plugins: [ dev && isClient && new ReactRefreshWebpackPlugin(webpack), // Makes sure `Buffer` and `process` are polyfilled in client and flight bundles (same behavior as webpack 4) (isClient || isEdgeServer) && new webpack.ProvidePlugin({ // Buffer is used by getInlineScriptSource Buffer: [require.resolve('buffer'), 'Buffer'], // Avoid process being overridden when in web run time ...(isClient && { process: [require.resolve('process')] }), }), new webpack.DefinePlugin({ ...Object.keys(process.env).reduce( (prev: { [key: string]: string }, key: string) => { if (key.startsWith('NEXT_PUBLIC_')) { prev[`process.env.${key}`] = JSON.stringify(process.env[key]!) } return prev }, {} ), ...Object.keys(config.env).reduce((acc, key) => { errorIfEnvConflicted(config, key) return { ...acc, [`process.env.${key}`]: JSON.stringify(config.env[key]), } }, {}), ...(compilerType !== 'edge-server' ? {} : { EdgeRuntime: JSON.stringify( /** * Cloud providers can set this environment variable to allow users * and library authors to have different implementations based on * the runtime they are running with, if it's not using `edge-runtime` */ process.env.NEXT_EDGE_RUNTIME_PROVIDER || 'edge-runtime' ), }), // TODO: enforce `NODE_ENV` on `process.env`, and add a test: 'process.env.NODE_ENV': JSON.stringify( dev ? 'development' : 'production' ), ...((isNodeServer || isEdgeServer) && { 'process.env.NEXT_RUNTIME': JSON.stringify( isEdgeServer ? 'edge' : 'nodejs' ), }), 'process.env.__NEXT_MIDDLEWARE_REGEX': JSON.stringify( middlewareRegex || '' ), 'process.env.__NEXT_MANUAL_CLIENT_BASE_PATH': JSON.stringify( config.experimental.manualClientBasePath ), 'process.env.__NEXT_NEW_LINK_BEHAVIOR': JSON.stringify( config.experimental.newNextLinkBehavior ), 'process.env.__NEXT_OPTIMISTIC_CLIENT_CACHE': JSON.stringify( config.experimental.optimisticClientCache ), 'process.env.__NEXT_CROSS_ORIGIN': JSON.stringify(crossOrigin), 'process.browser': JSON.stringify(isClient), 'process.env.__NEXT_TEST_MODE': JSON.stringify( process.env.__NEXT_TEST_MODE ), // This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory ...(dev && (isClient || isEdgeServer) ? { 'process.env.__NEXT_DIST_DIR': JSON.stringify(distDir), } : {}), 'process.env.__NEXT_TRAILING_SLASH': JSON.stringify( config.trailingSlash ), 'process.env.__NEXT_BUILD_INDICATOR': JSON.stringify( config.devIndicators.buildActivity ), 'process.env.__NEXT_BUILD_INDICATOR_POSITION': JSON.stringify( config.devIndicators.buildActivityPosition ), 'process.env.__NEXT_STRICT_MODE': JSON.stringify( config.reactStrictMode ), 'process.env.__NEXT_REACT_ROOT': JSON.stringify(hasReactRoot), 'process.env.__NEXT_RSC': JSON.stringify(hasServerComponents), 'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify( config.optimizeFonts && !dev ), 'process.env.__NEXT_OPTIMIZE_CSS': JSON.stringify( config.experimental.optimizeCss && !dev ), 'process.env.__NEXT_SCRIPT_WORKERS': JSON.stringify( config.experimental.nextScriptWorkers && !dev ), 'process.env.__NEXT_SCROLL_RESTORATION': JSON.stringify( config.experimental.scrollRestoration ), 'process.env.__NEXT_IMAGE_OPTS': JSON.stringify({ deviceSizes: config.images.deviceSizes, imageSizes: config.images.imageSizes, path: config.images.path, loader: config.images.loader, dangerouslyAllowSVG: config.images.dangerouslyAllowSVG, experimentalUnoptimized: config?.experimental?.images?.unoptimized, experimentalFuture: config.experimental?.images?.allowFutureImage, ...(dev ? { // pass domains in development to allow validating on the client domains: config.images.domains, experimentalRemotePatterns: config.experimental?.images?.remotePatterns, } : {}), }), 'process.env.__NEXT_ROUTER_BASEPATH': JSON.stringify(config.basePath), 'process.env.__NEXT_HAS_REWRITES': JSON.stringify(hasRewrites), 'process.env.__NEXT_I18N_SUPPORT': JSON.stringify(!!config.i18n), 'process.env.__NEXT_I18N_DOMAINS': JSON.stringify(config.i18n?.domains), 'process.env.__NEXT_ANALYTICS_ID': JSON.stringify(config.analyticsId), ...(isNodeServer || isEdgeServer ? { // Fix bad-actors in the npm ecosystem (e.g. `node-formidable`) // This is typically found in unmaintained modules from the // pre-webpack era (common in server-side code) 'global.GENTLY': JSON.stringify(false), } : undefined), // stub process.env with proxy to warn a missing value is // being accessed in development mode ...(config.experimental.pageEnv && dev ? { 'process.env': ` new Proxy(${isNodeServer ? 'process.env' : '{}'}, { get(target, prop) { if (typeof target[prop] === 'undefined') { console.warn(\`An environment variable (\${prop}) that was not provided in the environment was accessed.\nSee more info here: https://nextjs.org/docs/messages/missing-env-value\`) } return target[prop] } }) `, } : {}), }), isClient && new ReactLoadablePlugin({ filename: REACT_LOADABLE_MANIFEST, pagesDir, runtimeAsset: hasConcurrentFeatures ? `server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js` : undefined, dev, }), (isClient || isEdgeServer) && new DropClientPage(), config.outputFileTracing && !isLikeServerless && (isNodeServer || isEdgeServer) && !dev && new (require('./webpack/plugins/next-trace-entrypoints-plugin').TraceEntryPointsPlugin)( { appDir: dir, esmExternals: config.experimental.esmExternals, staticImageImports: !config.images.disableStaticImages, outputFileTracingRoot: config.experimental.outputFileTracingRoot, } ), // Moment.js is an extremely popular library that bundles large locale files // by default due to how Webpack interprets its code. This is a practical // solution that requires the user to opt into importing specific locales. // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack config.excludeDefaultMomentLocales && new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/, }), ...(dev ? (() => { // Even though require.cache is server only we have to clear assets from both compilations // This is because the client compilation generates the build manifest that's used on the server side const { NextJsRequireCacheHotReloader, } = require('./webpack/plugins/nextjs-require-cache-hot-reloader') const devPlugins = [ new NextJsRequireCacheHotReloader({ hasServerComponents: config.experimental.serverComponents, }), ] if (isClient || isEdgeServer) { devPlugins.push(new webpack.HotModuleReplacementPlugin()) } return devPlugins })() : []), !dev && new webpack.IgnorePlugin({ resourceRegExp: /react-is/, contextRegExp: /next[\\/]dist[\\/]/, }), target === 'serverless' && (isNodeServer || isEdgeServer) && new ServerlessPlugin(), (isNodeServer || isEdgeServer) && new PagesManifestPlugin({ serverless: isLikeServerless, dev, isEdgeRuntime: isEdgeServer, appDirEnabled: !!config.experimental.appDir, }), // MiddlewarePlugin should be after DefinePlugin so NEXT_PUBLIC_* // replacement is done before its process.env.* handling isEdgeServer && new MiddlewarePlugin({ dev }), isClient && new BuildManifestPlugin({ buildId, rewrites, isDevFallback, exportRuntime: hasConcurrentFeatures, appDirEnabled: !!config.experimental.appDir, }), new ProfilingPlugin({ runWebpackSpan }), config.optimizeFonts && !dev && isNodeServer && (function () { const { FontStylesheetGatheringPlugin } = require('./webpack/plugins/font-stylesheet-gathering-plugin') as { FontStylesheetGatheringPlugin: typeof import('./webpack/plugins/font-stylesheet-gathering-plugin').FontStylesheetGatheringPlugin } return new FontStylesheetGatheringPlugin({ isLikeServerless, }) })(), new WellKnownErrorsPlugin(), isClient && 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, }, }), hasServerComponents && (isClient ? new FlightManifestPlugin({ dev, appDir: !!config.experimental.appDir, pageExtensions: rawPageExtensions, }) : new ClientEntryPlugin({ dev, isEdgeServer, })), !dev && isClient && new (require('./webpack/plugins/telemetry-plugin').TelemetryPlugin)( new Map( [ ['swcLoader', useSWCLoader], ['swcMinify', config.swcMinify], ['swcRelay', !!config.compiler?.relay], ['swcStyledComponents', !!config.compiler?.styledComponents], [ 'swcReactRemoveProperties', !!config.compiler?.reactRemoveProperties, ], [ 'swcExperimentalDecorators', !!jsConfig?.compilerOptions?.experimentalDecorators, ], ['swcRemoveConsole', !!config.compiler?.removeConsole], ['swcImportSource', !!jsConfig?.compilerOptions?.jsxImportSource], ['swcEmotion', !!config.compiler?.emotion], SWCBinaryTarget, ].filter<[Feature, boolean]>(Boolean as any) ) ), ].filter(Boolean as any as ExcludesFalse), } // Support tsconfig and jsconfig baseUrl if (resolvedBaseUrl) { webpackConfig.resolve?.modules?.push(resolvedBaseUrl) } if (jsConfig?.compilerOptions?.paths && resolvedBaseUrl) { webpackConfig.resolve?.plugins?.unshift( new JsConfigPathsPlugin(jsConfig.compilerOptions.paths, resolvedBaseUrl) ) } const webpack5Config = webpackConfig as webpack5.Configuration if (isEdgeServer) { webpack5Config.module?.rules?.unshift({ test: /\.wasm$/, loader: 'next-middleware-wasm-loader', type: 'javascript/auto', resourceQuery: /module/i, }) webpack5Config.module?.rules?.unshift({ dependency: 'url', loader: 'next-middleware-asset-loader', type: 'javascript/auto', layer: 'edge-asset', }) webpack5Config.module?.rules?.unshift({ issuerLayer: 'edge-asset', type: 'asset/source', }) } webpack5Config.experiments = { layers: true, cacheUnaffected: true, buildHttp: Array.isArray(config.experimental.urlImports) ? { allowedUris: config.experimental.urlImports, cacheLocation: path.join(dir, 'next.lock/data'), lockfileLocation: path.join(dir, 'next.lock/lock.json'), } : config.experimental.urlImports ? { cacheLocation: path.join(dir, 'next.lock/data'), lockfileLocation: path.join(dir, 'next.lock/lock.json'), ...config.experimental.urlImports, } : undefined, } webpack5Config.module!.parser = { javascript: { url: 'relative', }, } webpack5Config.module!.generator = { asset: { filename: 'static/media/[name].[hash:8][ext]', }, } if (!webpack5Config.output) { webpack5Config.output = {} } if (isClient) { webpack5Config.output.trustedTypes = 'nextjs#bundler' } if (isClient || isEdgeServer) { webpack5Config.output.enabledLibraryTypes = ['assign'] } if (dev) { // @ts-ignore unsafeCache exists webpack5Config.module.unsafeCache = (module) => !/[\\/]pages[\\/][^\\/]+(?:$|\?|#)/.test(module.resource) } // This enables managedPaths for all node_modules // and also for the unplugged folder when using yarn pnp // It also add the yarn cache to the immutable paths webpack5Config.snapshot = {} if (process.versions.pnp === '3') { webpack5Config.snapshot.managedPaths = [ /^(.+?(?:[\\/]\.yarn[\\/]unplugged[\\/][^\\/]+)?[\\/]node_modules[\\/])/, ] } else { webpack5Config.snapshot.managedPaths = [/^(.+?[\\/]node_modules[\\/])/] } if (process.versions.pnp === '3') { webpack5Config.snapshot.immutablePaths = [ /^(.+?[\\/]cache[\\/][^\\/]+\.zip[\\/]node_modules[\\/])/, ] } if (dev) { if (!webpack5Config.optimization) { webpack5Config.optimization = {} } // For Server Components, it's necessary to have provided exports collected // to generate the correct flight manifest. if (!hasServerComponents) { webpack5Config.optimization.providedExports = false } webpack5Config.optimization.usedExports = false } const configVars = JSON.stringify({ crossOrigin: config.crossOrigin, pageExtensions: rawPageExtensions, trailingSlash: config.trailingSlash, buildActivity: config.devIndicators.buildActivity, buildActivityPosition: config.devIndicators.buildActivityPosition, productionBrowserSourceMaps: !!config.productionBrowserSourceMaps, reactStrictMode: config.reactStrictMode, optimizeFonts: config.optimizeFonts, optimizeCss: config.experimental.optimizeCss, nextScriptWorkers: config.experimental.nextScriptWorkers, scrollRestoration: config.experimental.scrollRestoration, basePath: config.basePath, pageEnv: config.experimental.pageEnv, excludeDefaultMomentLocales: config.excludeDefaultMomentLocales, assetPrefix: config.assetPrefix, disableOptimizedLoading, target, isEdgeRuntime: isEdgeServer, reactProductionProfiling, webpack: !!config.webpack, hasRewrites, runtime: config.experimental.runtime, swcMinify: config.swcMinify, swcLoader: useSWCLoader, removeConsole: config.compiler?.removeConsole, reactRemoveProperties: config.compiler?.reactRemoveProperties, styledComponents: config.compiler?.styledComponents, relay: config.compiler?.relay, emotion: config.compiler?.emotion, modularizeImports: config.experimental?.modularizeImports, legacyBrowsers: config.experimental?.legacyBrowsers, }) const cache: any = { type: 'filesystem', // Includes: // - Next.js version // - next.config.js keys that affect compilation version: `${process.env.__NEXT_VERSION}|${configVars}`, cacheDirectory: path.join(distDir, 'cache', 'webpack'), } // Adds `next.config.js` as a buildDependency when custom webpack config is provided if (config.webpack && config.configFile) { cache.buildDependencies = { config: [config.configFile], } } webpack5Config.cache = cache if (process.env.NEXT_WEBPACK_LOGGING) { const infra = process.env.NEXT_WEBPACK_LOGGING.includes('infrastructure') const profileClient = process.env.NEXT_WEBPACK_LOGGING.includes('profile-client') const profileServer = process.env.NEXT_WEBPACK_LOGGING.includes('profile-server') const summaryClient = process.env.NEXT_WEBPACK_LOGGING.includes('summary-client') const summaryServer = process.env.NEXT_WEBPACK_LOGGING.includes('summary-server') const profile = (profileClient && isClient) || (profileServer && (isNodeServer || isEdgeServer)) const summary = (summaryClient && isClient) || (summaryServer && (isNodeServer || isEdgeServer)) const logDefault = !infra && !profile && !summary if (logDefault || infra) { webpack5Config.infrastructureLogging = { level: 'verbose', debug: /FileSystemInfo/, } } if (logDefault || profile) { webpack5Config.plugins!.push((compiler: webpack5.Compiler) => { compiler.hooks.done.tap('next-webpack-logging', (stats) => { console.log( stats.toString({ colors: true, logging: logDefault ? 'log' : 'verbose', }) ) }) }) } else if (summary) { webpack5Config.plugins!.push((compiler: webpack5.Compiler) => { compiler.hooks.done.tap('next-webpack-logging', (stats) => { console.log( stats.toString({ preset: 'summary', colors: true, timings: true, }) ) }) }) } if (profile) { const ProgressPlugin = webpack.ProgressPlugin as unknown as typeof webpack5.ProgressPlugin webpack5Config.plugins!.push( new ProgressPlugin({ profile: true, }) ) webpack5Config.profile = true } } webpackConfig = await buildConfiguration(webpackConfig, { supportedBrowsers, rootDirectory: dir, customAppFile: new RegExp(escapeStringRegexp(path.join(pagesDir, `_app`))), isDevelopment: dev, isServer: isNodeServer || isEdgeServer, isEdgeRuntime: isEdgeServer, targetWeb: isClient || isEdgeServer, assetPrefix: config.assetPrefix || '', sassOptions: config.sassOptions, productionBrowserSourceMaps: config.productionBrowserSourceMaps, future: config.future, experimental: config.experimental, disableStaticImages: config.images.disableStaticImages, }) // @ts-ignore Cache exists webpackConfig.cache.name = `${webpackConfig.name}-${webpackConfig.mode}${ isDevFallback ? '-fallback' : '' }` let originalDevtool = webpackConfig.devtool if (typeof config.webpack === 'function') { webpackConfig = config.webpack(webpackConfig, { dir, dev, isServer: isNodeServer || isEdgeServer, buildId, config, defaultLoaders, totalPages: Object.keys(entrypoints).length, webpack, ...(isNodeServer || isEdgeServer ? { nextRuntime: isEdgeServer ? 'edge' : 'nodejs', } : {}), }) if (!webpackConfig) { throw new Error( `Webpack config is undefined. You may have forgot to return properly from within the "webpack" method of your ${config.configFileName}.\n` + 'See more info here https://nextjs.org/docs/messages/undefined-webpack-config' ) } if (dev && originalDevtool !== webpackConfig.devtool) { webpackConfig.devtool = originalDevtool devtoolRevertWarning(originalDevtool) } // eslint-disable-next-line no-shadow const webpack5Config = webpackConfig as webpack5.Configuration // disable lazy compilation of entries as next.js has it's own method here if (webpack5Config.experiments?.lazyCompilation === true) { webpack5Config.experiments.lazyCompilation = { entries: false, } } else if ( typeof webpack5Config.experiments?.lazyCompilation === 'object' && webpack5Config.experiments.lazyCompilation.entries !== false ) { webpack5Config.experiments.lazyCompilation.entries = false } if (typeof (webpackConfig as any).then === 'function') { console.warn( '> Promise returned in next config. https://nextjs.org/docs/messages/promise-in-next-config' ) } } if (!config.images.disableStaticImages) { const rules = webpackConfig.module?.rules || [] const hasCustomSvg = rules.some( (rule) => rule.loader !== 'next-image-loader' && 'test' in rule && rule.test instanceof RegExp && rule.test.test('.svg') ) const nextImageRule = rules.find( (rule) => rule.loader === 'next-image-loader' ) if (hasCustomSvg && nextImageRule) { // Exclude svg if the user already defined it in custom // webpack config such as `@svgr/webpack` plugin or // the `babel-plugin-inline-react-svg` plugin. nextImageRule.test = /\.(png|jpg|jpeg|gif|webp|avif|ico|bmp)$/i } } if ( config.experimental.craCompat && webpackConfig.module?.rules && webpackConfig.plugins ) { // CRA allows importing non-webpack handled files with file-loader // these need to be the last rule to prevent catching other items // https://github.com/facebook/create-react-app/blob/fddce8a9e21bf68f37054586deb0c8636a45f50b/packages/react-scripts/config/webpack.config.js#L594 const fileLoaderExclude = [/\.(js|mjs|jsx|ts|tsx|json)$/] const fileLoader = { exclude: fileLoaderExclude, issuer: fileLoaderExclude, type: 'asset/resource', } const topRules = [] const innerRules = [] for (const rule of webpackConfig.module.rules) { if (rule.resolve) { topRules.push(rule) } else { if ( rule.oneOf && !(rule.test || rule.exclude || rule.resource || rule.issuer) ) { rule.oneOf.forEach((r) => innerRules.push(r)) } else { innerRules.push(rule) } } } webpackConfig.module.rules = [ ...(topRules as any), { oneOf: [...innerRules, fileLoader], }, ] } // Backwards compat with webpack-dev-middleware options object if (typeof config.webpackDevMiddleware === 'function') { const options = config.webpackDevMiddleware({ watchOptions: webpackConfig.watchOptions, }) if (options.watchOptions) { webpackConfig.watchOptions = options.watchOptions } } function canMatchCss(rule: webpack.RuleSetCondition | undefined): boolean { if (!rule) { return false } const fileNames = [ '/tmp/NEXTJS_CSS_DETECTION_FILE.css', '/tmp/NEXTJS_CSS_DETECTION_FILE.scss', '/tmp/NEXTJS_CSS_DETECTION_FILE.sass', '/tmp/NEXTJS_CSS_DETECTION_FILE.less', '/tmp/NEXTJS_CSS_DETECTION_FILE.styl', ] if (rule instanceof RegExp && fileNames.some((input) => rule.test(input))) { return true } if (typeof rule === 'function') { if ( fileNames.some((input) => { try { if (rule(input)) { return true } } catch (_) {} return false }) ) { return true } } if (Array.isArray(rule) && rule.some(canMatchCss)) { return true } return false } const hasUserCssConfig = webpackConfig.module?.rules.some( (rule) => canMatchCss(rule.test) || canMatchCss(rule.include) ) ?? false if (hasUserCssConfig) { // only show warning for one build if (isNodeServer || isEdgeServer) { console.warn( chalk.yellow.bold('Warning: ') + chalk.bold( 'Built-in CSS support is being disabled due to custom CSS configuration being detected.\n' ) + 'See here for more info: https://nextjs.org/docs/messages/built-in-css-disabled\n' ) } if (webpackConfig.module?.rules.length) { // Remove default CSS Loaders webpackConfig.module.rules.forEach((r) => { if (Array.isArray(r.oneOf)) { r.oneOf = r.oneOf.filter( (o) => (o as any)[Symbol.for('__next_css_remove')] !== true ) } }) } if (webpackConfig.plugins?.length) { // Disable CSS Extraction Plugin webpackConfig.plugins = webpackConfig.plugins.filter( (p) => (p as any).__next_css_remove !== true ) } if (webpackConfig.optimization?.minimizer?.length) { // Disable CSS Minifier webpackConfig.optimization.minimizer = webpackConfig.optimization.minimizer.filter( (e) => (e as any).__next_css_remove !== true ) } } // Inject missing React Refresh loaders so that development mode is fast: if (dev && isClient) { attachReactRefresh(webpackConfig, defaultLoaders.babel) } // check if using @zeit/next-typescript and show warning if ( (isNodeServer || isEdgeServer) && webpackConfig.module && Array.isArray(webpackConfig.module.rules) ) { let foundTsRule = false webpackConfig.module.rules = webpackConfig.module.rules.filter( (rule): boolean => { if (!(rule.test instanceof RegExp)) return true if ('noop.ts'.match(rule.test) && !'noop.js'.match(rule.test)) { // remove if it matches @zeit/next-typescript foundTsRule = rule.use === defaultLoaders.babel return !foundTsRule } return true } ) if (foundTsRule) { console.warn( `\n@zeit/next-typescript is no longer needed since Next.js has built-in support for TypeScript now. Please remove it from your ${config.configFileName} and your .babelrc\n` ) } } // Patch `@zeit/next-sass`, `@zeit/next-less`, `@zeit/next-stylus` for compatibility if (webpackConfig.module && Array.isArray(webpackConfig.module.rules)) { ;[].forEach.call( webpackConfig.module.rules, function (rule: webpack.RuleSetRule) { if (!(rule.test instanceof RegExp && Array.isArray(rule.use))) { return } const isSass = rule.test.source === '\\.scss$' || rule.test.source === '\\.sass$' const isLess = rule.test.source === '\\.less$' const isCss = rule.test.source === '\\.css$' const isStylus = rule.test.source === '\\.styl$' // Check if the rule we're iterating over applies to Sass, Less, or CSS if (!(isSass || isLess || isCss || isStylus)) { return } ;[].forEach.call(rule.use, function (use: webpack.RuleSetUseItem) { if ( !( use && typeof use === 'object' && // Identify use statements only pertaining to `css-loader` (use.loader === 'css-loader' || use.loader === 'css-loader/locals') && use.options && typeof use.options === 'object' && // The `minimize` property is a good heuristic that we need to // perform this hack. The `minimize` property was only valid on // old `css-loader` versions. Custom setups (that aren't next-sass, // next-less or next-stylus) likely have the newer version. // We still handle this gracefully below. (Object.prototype.hasOwnProperty.call(use.options, 'minimize') || Object.prototype.hasOwnProperty.call( use.options, 'exportOnlyLocals' )) ) ) { return } // Try to monkey patch within a try-catch. We shouldn't fail the build // if we cannot pull this off. // The user may not even be using the `next-sass` or `next-less` or // `next-stylus` plugins. // If it does work, great! try { // Resolve the version of `@zeit/next-css` as depended on by the Sass, // Less or Stylus plugin. const correctNextCss = require.resolve('@zeit/next-css', { paths: [ isCss ? // Resolve `@zeit/next-css` from the base directory dir : // Else, resolve it from the specific plugins require.resolve( isSass ? '@zeit/next-sass' : isLess ? '@zeit/next-less' : isStylus ? '@zeit/next-stylus' : 'next' ), ], }) // If we found `@zeit/next-css` ... if (correctNextCss) { // ... resolve the version of `css-loader` shipped with that // package instead of whichever was hoisted highest in your // `node_modules` tree. const correctCssLoader = require.resolve(use.loader, { paths: [correctNextCss], }) if (correctCssLoader) { // We saved the user from a failed build! use.loader = correctCssLoader } } } catch (_) { // The error is not required to be handled. } }) } ) } // Backwards compat for `main.js` entry key // and setup of dependencies between entries // we can't do that in the initial entry for // backward-compat reasons const originalEntry: any = webpackConfig.entry if (typeof originalEntry !== 'undefined') { const updatedEntry = async () => { const entry: webpack5.EntryObject = typeof originalEntry === 'function' ? await originalEntry() : originalEntry // Server compilation doesn't have main.js if ( clientEntries && Array.isArray(entry['main.js']) && entry['main.js'].length > 0 ) { const originalFile = clientEntries[ CLIENT_STATIC_FILES_RUNTIME_MAIN ] as string entry[CLIENT_STATIC_FILES_RUNTIME_MAIN] = [ ...entry['main.js'], originalFile, ] } delete entry['main.js'] for (const name of Object.keys(entry)) { entry[name] = finalizeEntrypoint({ value: entry[name], compilerType, name, appDir: config.experimental.appDir, }) } return entry } // @ts-ignore webpack 5 typings needed webpackConfig.entry = updatedEntry } if (!dev) { // entry is always a function webpackConfig.entry = await (webpackConfig.entry as webpack.EntryFunc)() } return webpackConfig } function errorIfEnvConflicted(config: NextConfigComplete, key: string) { const isPrivateKey = /^(?:NODE_.+)|^(?:__.+)$/i.test(key) const hasNextRuntimeKey = key === 'NEXT_RUNTIME' if (isPrivateKey || hasNextRuntimeKey) { throw new Error( `The key "${key}" under "env" in ${config.configFileName} is not allowed. https://nextjs.org/docs/messages/env-key-not-allowed` ) } }