webpack-config.ts 43.7 KB
Newer Older
1
import { codeFrameColumns } from '@babel/code-frame'
2
import ReactRefreshWebpackPlugin from '@next/react-refresh-utils/ReactRefreshWebpackPlugin'
3
import crypto from 'crypto'
4 5 6
import { readFileSync } from 'fs'
import chalk from 'next/dist/compiled/chalk'
import TerserPlugin from 'next/dist/compiled/terser-webpack-plugin'
7
import path from 'path'
8
import webpack from 'webpack'
9
import type { Configuration } from 'webpack'
10
import {
11
  DOT_NEXT_ALIAS,
12 13 14 15
  NEXT_PROJECT_ROOT,
  NEXT_PROJECT_ROOT_DIST_CLIENT,
  PAGES_DIR_ALIAS,
} from '../lib/constants'
16
import { fileExists } from '../lib/file-exists'
17
import { resolveRequest } from '../lib/resolve-request'
18
import { getTypeScriptConfiguration } from '../lib/typescript/getTypeScriptConfiguration'
19 20
import {
  CLIENT_STATIC_FILES_RUNTIME_MAIN,
21
  CLIENT_STATIC_FILES_RUNTIME_POLYFILLS,
22 23 24
  CLIENT_STATIC_FILES_RUNTIME_WEBPACK,
  REACT_LOADABLE_MANIFEST,
  SERVERLESS_DIRECTORY,
25
  SERVER_DIRECTORY,
26
} from '../next-server/lib/constants'
27
import { execOnce } from '../next-server/lib/utils'
J
Joe Haddad 已提交
28
import { findPageFile } from '../server/lib/find-page-file'
29
import { WebpackEntrypoints } from './entries'
J
Joe Haddad 已提交
30 31 32 33 34
import {
  collectPlugins,
  PluginMetaData,
  VALID_MIDDLEWARE,
} from './plugins/collect-plugins'
35
import { build as buildConfiguration } from './webpack/config'
36
import { __overrideCssConfiguration } from './webpack/config/blocks/css/overrideCssConfiguration'
J
Joe Haddad 已提交
37
import { pluginLoaderOptions } from './webpack/loaders/next-plugin-loader'
38 39
import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin'
import ChunkNamesPlugin from './webpack/plugins/chunk-names-plugin'
J
Joe Haddad 已提交
40
import { CssMinimizerPlugin } from './webpack/plugins/css-minimizer-plugin'
41
import { JsConfigPathsPlugin } from './webpack/plugins/jsconfig-paths-plugin'
42
import { DropClientPage } from './webpack/plugins/next-drop-client-page-plugin'
43 44 45
import NextJsSsrImportPlugin from './webpack/plugins/nextjs-ssr-import'
import NextJsSSRModuleCachePlugin from './webpack/plugins/nextjs-ssr-module-cache'
import PagesManifestPlugin from './webpack/plugins/pages-manifest-plugin'
46
import { ProfilingPlugin } from './webpack/plugins/profiling-plugin'
47 48
import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin'
import { ServerlessPlugin } from './webpack/plugins/serverless-plugin'
49
import WebpackConformancePlugin, {
50
  DuplicatePolyfillsConformanceCheck,
51
  GranularChunksConformanceCheck,
52
  MinificationConformanceCheck,
53
  ReactSyncScriptsConformanceCheck,
54
} from './webpack/plugins/webpack-conformance-plugin'
J
Joe Haddad 已提交
55
import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin'
J
Joe Haddad 已提交
56

57
type ExcludesFalse = <T>(x: T | false) => x is T
58

59 60
const isWebpack5 = parseInt(webpack.version!) === 5

61 62 63 64 65 66
const escapePathVariables = (value: any) => {
  return typeof value === 'string'
    ? value.replace(/\[(\\*[\w:]+\\*)\]/gi, '[\\$1\\]')
    : value
}

67 68 69 70 71 72 73 74 75
const devtoolRevertWarning = execOnce((devtool: 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://err.sh/next.js/improper-devtool'
  )
})

76
function parseJsonFile(filePath: string) {
G
json5  
Guy Bedford 已提交
77
  const JSON5 = require('next/dist/compiled/json5')
78
  const contents = readFileSync(filePath, 'utf8')
79 80 81 82 83 84

  // Special case an empty file
  if (contents.trim() === '') {
    return {}
  }

85 86 87 88 89 90 91 92
  try {
    return JSON5.parse(contents)
  } catch (err) {
    const codeFrame = codeFrameColumns(
      String(contents),
      { start: { line: err.lineNumber, column: err.columnNumber } },
      { message: err.message, highlightCode: true }
    )
93
    throw new Error(`Failed to parse "${filePath}":\n${codeFrame}`)
94
  }
95 96
}

97
function getOptimizedAliases(isServer: boolean): { [pkg: string]: string } {
98 99 100 101
  if (isServer) {
    return {}
  }

102
  const stubWindowFetch = path.join(__dirname, 'polyfills', 'fetch', 'index.js')
103 104 105
  const stubObjectAssign = path.join(__dirname, 'polyfills', 'object-assign.js')

  const shimAssign = path.join(__dirname, 'polyfills', 'object.assign')
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
  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'),

131 132
      // Replace: full URL polyfill with platform-based polyfill
      url: require.resolve('native-url'),
133 134
    }
  )
135 136
}

137
type ClientEntries = {
138
  [key: string]: string | string[]
139 140
}

141 142 143 144 145
export default async function getBaseWebpackConfig(
  dir: string,
  {
    buildId,
    config,
J
JJ Kasper 已提交
146 147 148 149
    dev = false,
    isServer = false,
    pagesDir,
    tracer,
150
    target = 'server',
151
    reactProductionProfiling = false,
152 153 154 155
    entrypoints,
  }: {
    buildId: string
    config: any
J
JJ Kasper 已提交
156 157 158
    dev?: boolean
    isServer?: boolean
    pagesDir: string
159
    target?: string
J
JJ Kasper 已提交
160
    tracer?: any
161
    reactProductionProfiling?: boolean
162 163 164
    entrypoints: WebpackEntrypoints
  }
): Promise<webpack.Configuration> {
165 166
  const productionBrowserSourceMaps =
    config.experimental.productionBrowserSourceMaps && !isServer
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
  let plugins: PluginMetaData[] = []
  let babelPresetPlugins: { dir: string; config: any }[] = []

  if (config.experimental.plugins) {
    plugins = await collectPlugins(dir, config.env, config.plugins)
    pluginLoaderOptions.plugins = plugins

    for (const plugin of plugins) {
      if (plugin.middleware.includes('babel-preset-build')) {
        babelPresetPlugins.push({
          dir: plugin.directory,
          config: plugin.config,
        })
      }
    }
  }
183

184
  const hasReactRefresh = dev && !isServer
185

186
  const distDir = path.join(dir, config.distDir)
T
Tim Neutkens 已提交
187 188
  const defaultLoaders = {
    babel: {
189
      loader: 'next-babel-loader',
190 191 192
      options: {
        isServer,
        distDir,
J
JJ Kasper 已提交
193
        pagesDir,
194
        cwd: dir,
195
        cache: true,
196 197
        babelPresetPlugins,
        hasModern: !!config.experimental.modern,
198
        development: dev,
199
        hasReactRefresh,
200
      },
201
    },
202
    // Backwards compat
203
    hotSelfAccept: {
204 205
      loader: 'noop-loader',
    },
T
Tim Neutkens 已提交
206 207
  }

208 209 210 211 212 213
  const babelIncludeRegexes: RegExp[] = [
    /next[\\/]dist[\\/]next-server[\\/]lib/,
    /next[\\/]dist[\\/]client/,
    /next[\\/]dist[\\/]pages/,
    /[\\/](strip-ansi|ansi-regex)[\\/]/,
    ...(config.experimental.plugins
J
Joe Haddad 已提交
214
      ? VALID_MIDDLEWARE.map((name) => new RegExp(`src(\\\\|/)${name}`))
215 216 217
      : []),
  ]

T
Tim Neutkens 已提交
218 219 220
  // Support for NODE_PATH
  const nodePathList = (process.env.NODE_PATH || '')
    .split(process.platform === 'win32' ? ';' : ':')
J
Joe Haddad 已提交
221
    .filter((p) => !!p)
T
Tim Neutkens 已提交
222

223 224 225 226 227 228
  const isServerless = target === 'serverless'
  const isServerlessTrace = target === 'experimental-serverless-trace'
  // Intentionally not using isTargetLikeServerless helper
  const isLikeServerless = isServerless || isServerlessTrace

  const outputDir = isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY
T
Tim Neutkens 已提交
229
  const outputPath = path.join(distDir, isServer ? outputDir : '')
230
  const totalPages = Object.keys(entrypoints).length
231
  const clientEntries = !isServer
232
    ? ({
233 234 235
        // Backwards compatibility
        'main.js': [],
        [CLIENT_STATIC_FILES_RUNTIME_MAIN]:
236 237 238 239 240 241 242 243
          `./` +
          path
            .relative(
              dir,
              path.join(
                NEXT_PROJECT_ROOT_DIST_CLIENT,
                dev ? `next-dev.js` : 'next.js'
              )
244
            )
245
            .replace(/\\/g, '/'),
246 247
        [CLIENT_STATIC_FILES_RUNTIME_POLYFILLS]: path.join(
          NEXT_PROJECT_ROOT_DIST_CLIENT,
248
          'polyfills.js'
249
        ),
250
      } as ClientEntries)
251
    : undefined
N
nkzawa 已提交
252

253 254
  let typeScriptPath
  try {
255
    typeScriptPath = resolveRequest('typescript', `${dir}/`)
256
  } catch (_) {}
257
  const tsConfigPath = path.join(dir, 'tsconfig.json')
258 259 260
  const useTypeScript = Boolean(
    typeScriptPath && (await fileExists(tsConfigPath))
  )
261

262 263 264
  let jsConfig
  // jsconfig is a subset of tsconfig
  if (useTypeScript) {
265 266 267
    const ts = (await import(typeScriptPath)) as typeof import('typescript')
    const tsConfig = await getTypeScriptConfiguration(ts, tsConfigPath)
    jsConfig = { compilerOptions: tsConfig.options }
268 269 270 271
  }

  const jsConfigPath = path.join(dir, 'jsconfig.json')
  if (!useTypeScript && (await fileExists(jsConfigPath))) {
272
    jsConfig = parseJsonFile(jsConfigPath)
273 274 275 276 277 278 279
  }

  let resolvedBaseUrl
  if (jsConfig?.compilerOptions?.baseUrl) {
    resolvedBaseUrl = path.resolve(dir, jsConfig.compilerOptions.baseUrl)
  }

280
  function getReactProfilingInProduction() {
281
    if (reactProductionProfiling) {
282 283 284 285 286 287 288
      return {
        'react-dom$': 'react-dom/profiling',
        'scheduler/tracing': 'scheduler/tracing-profiling',
      }
    }
  }

T
Tim Neutkens 已提交
289
  const resolveConfig = {
290
    // Disable .mjs for node_modules bundling
291
    extensions: isServer
292 293 294
      ? [
          '.js',
          '.mjs',
295
          ...(useTypeScript ? ['.tsx', '.ts'] : []),
296 297 298 299 300 301 302
          '.jsx',
          '.json',
          '.wasm',
        ]
      : [
          '.mjs',
          '.js',
303
          ...(useTypeScript ? ['.tsx', '.ts'] : []),
304 305 306 307
          '.jsx',
          '.json',
          '.wasm',
        ],
T
Tim Neutkens 已提交
308 309
    modules: [
      'node_modules',
310
      ...nodePathList, // Support for NODE_PATH environment variable
T
Tim Neutkens 已提交
311 312
    ],
    alias: {
313 314
      // These aliases make sure the wrapper module is not included in the bundles
      // Which makes bundles slightly smaller, but also skips parsing a module that we know will result in this alias
315
      'next/head': 'next/dist/next-server/lib/head.js',
316
      'next/router': 'next/dist/client/router.js',
317 318
      'next/config': 'next/dist/next-server/lib/runtime-config.js',
      'next/dynamic': 'next/dist/next-server/lib/dynamic.js',
T
Tim Neutkens 已提交
319
      next: NEXT_PROJECT_ROOT,
J
JJ Kasper 已提交
320
      [PAGES_DIR_ALIAS]: pagesDir,
321
      [DOT_NEXT_ALIAS]: distDir,
322
      ...getOptimizedAliases(isServer),
323
      ...getReactProfilingInProduction(),
324
    },
325
    mainFields: isServer ? ['main', 'module'] : ['browser', 'module', 'main'],
326 327 328
    plugins: isWebpack5
      ? // webpack 5+ has the PnP resolver built-in by default:
        []
329
      : [require('pnp-webpack-plugin')],
T
Tim Neutkens 已提交
330 331
  }

T
Tim Neutkens 已提交
332 333
  const webpackMode = dev ? 'development' : 'production'

334
  const terserOptions: any = {
335 336 337 338 339 340 341 342
    parse: {
      ecma: 8,
    },
    compress: {
      ecma: 5,
      warnings: false,
      // The following two options are known to break valid JavaScript code
      comparisons: false,
343
      inline: 2, // https://github.com/vercel/next.js/issues/7178#issuecomment-493048965
344 345 346 347 348 349 350 351 352 353 354
    },
    mangle: { safari10: true },
    output: {
      ecma: 5,
      safari10: true,
      comments: false,
      // Fixes usage of Emoji and certain Regex
      ascii_only: true,
    },
  }

355 356 357 358 359 360 361 362 363 364 365
  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`
    )
  }

366 367 368 369 370 371 372 373
  // Contains various versions of the Webpack SplitChunksPlugin used in different build types
  const splitChunksConfigs: {
    [propName: string]: webpack.Options.SplitChunksOptions
  } = {
    dev: {
      cacheGroups: {
        default: false,
        vendors: false,
374 375
        // In webpack 5 vendors was renamed to defaultVendors
        defaultVendors: false,
376 377
      },
    },
378
    prodGranular: {
379
      chunks: 'all',
380 381 382
      cacheGroups: {
        default: false,
        vendors: false,
383 384
        // In webpack 5 vendors was renamed to defaultVendors
        defaultVendors: false,
385
        framework: {
386
          chunks: 'all',
387
          name: 'framework',
A
Alex Castle 已提交
388
          // This regex ignores nested copies of framework libraries so they're
389
          // bundled with their issuer.
390
          // https://github.com/vercel/next.js/pull/9012
391
          test: /(?<!node_modules.*)[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types|use-subscription)[\\/]/,
392
          priority: 40,
J
Joe Haddad 已提交
393 394 395
          // Don't let webpack eliminate this chunk (prevents this chunk from
          // becoming a part of the commons chunk)
          enforce: true,
396 397 398 399 400 401 402 403
        },
        lib: {
          test(module: { size: Function; identifier: Function }): boolean {
            return (
              module.size() > 160000 &&
              /node_modules[/\\]/.test(module.identifier())
            )
          },
J
Joe Haddad 已提交
404 405 406 407 408 409
          name(module: {
            type: string
            libIdent?: Function
            updateHash: (hash: crypto.Hash) => void
          }): string {
            const hash = crypto.createHash('sha1')
410
            if (isModuleCSS(module)) {
J
Joe Haddad 已提交
411 412 413 414 415 416 417 418 419 420 421
              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 }))
            }

422
            return hash.digest('hex').substring(0, 8)
423 424 425 426 427 428
          },
          priority: 30,
          minChunks: 1,
          reuseExistingChunk: true,
        },
        commons: {
429
          name: 'commons',
430 431 432 433 434
          minChunks: totalPages,
          priority: 20,
        },
        shared: {
          name(module, chunks) {
435
            return (
436
              crypto
437 438 439 440 441 442 443 444
                .createHash('sha1')
                .update(
                  chunks.reduce(
                    (acc: string, chunk: webpack.compilation.Chunk) => {
                      return acc + chunk.name
                    },
                    ''
                  )
445
                )
446
                .digest('hex') + (isModuleCSS(module) ? '_CSS' : '')
447
            )
448 449 450 451 452 453
          },
          priority: 10,
          minChunks: 2,
          reuseExistingChunk: true,
        },
      },
454 455
      maxInitialRequests: 25,
      minSize: 20000,
456
    },
457 458 459 460 461 462 463
  }

  // Select appropriate SplitChunksPlugin config for this build
  let splitChunksConfig: webpack.Options.SplitChunksOptions
  if (dev) {
    splitChunksConfig = splitChunksConfigs.dev
  } else {
464
    splitChunksConfig = splitChunksConfigs.prodGranular
465 466
  }

467 468 469 470 471
  const crossOrigin =
    !config.crossOrigin && config.experimental.modern
      ? 'anonymous'
      : config.crossOrigin

472 473 474 475 476
  let customAppFile: string | null = await findPageFile(
    pagesDir,
    '/_app',
    config.pageExtensions
  )
J
Joe Haddad 已提交
477
  if (customAppFile) {
478
    customAppFile = path.resolve(path.join(pagesDir, customAppFile))
J
Joe Haddad 已提交
479 480
  }

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
  const conformanceConfig = Object.assign(
    {
      ReactSyncScriptsConformanceCheck: {
        enabled: true,
      },
      MinificationConformanceCheck: {
        enabled: true,
      },
      DuplicatePolyfillsConformanceCheck: {
        enabled: true,
        BlockedAPIToBePolyfilled: Object.assign(
          [],
          ['fetch'],
          config.conformance?.DuplicatePolyfillsConformanceCheck
            ?.BlockedAPIToBePolyfilled || []
        ),
      },
498 499 500
      GranularChunksConformanceCheck: {
        enabled: true,
      },
501 502 503
    },
    config.conformance
  )
504
  let webpackConfig: webpack.Configuration = {
505
    externals: !isServer
506 507 508 509
      ? // 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']
510
      : !isServerless
511 512
      ? [
          (context, request, callback) => {
513 514 515 516
            if (request === 'next') {
              return callback(undefined, `commonjs ${request}`)
            }

517 518 519 520 521 522 523 524
            const notExternalModules = [
              'next/app',
              'next/document',
              'next/link',
              'next/error',
              'string-hash',
              'next/constants',
            ]
525

526 527 528
            if (notExternalModules.indexOf(request) !== -1) {
              return callback()
            }
K
k-kawakami 已提交
529

530 531 532 533 534 535 536 537 538 539 540 541 542
            // 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))
            const isLikelyNextExternal =
              isLocal && /[/\\]next-server[/\\]/.test(request)
543

544 545 546 547
            // 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.
548
            if (isLocal && !isLikelyNextExternal) {
J
Joe Haddad 已提交
549
              return callback()
550 551
            }

552 553 554
            // Resolve the import with the webpack provided context, this
            // ensures we're resolving the correct version when multiple
            // exist.
555
            let res: string
556
            try {
557
              res = resolveRequest(request, `${context}/`)
558
            } catch (err) {
559 560 561
              // If the request cannot be resolved, we need to tell webpack to
              // "bundle" it so that webpack shows an error (that it cannot be
              // resolved).
562 563
              return callback()
            }
K
k-kawakami 已提交
564

565 566
            // Same as above, if the request cannot be resolved we need to have
            // webpack "bundle" it so it surfaces the not found error.
567 568 569
            if (!res) {
              return callback()
            }
K
k-kawakami 已提交
570

571 572
            let isNextExternal: boolean = false
            if (isLocal) {
573 574 575 576 577 578
              // we need to process next-server/lib/router/router so that
              // the DefinePlugin can inject process.env values
              isNextExternal = /next[/\\]dist[/\\]next-server[/\\](?!lib[/\\]router[/\\]router)/.test(
                res
              )

579 580 581 582
              if (!isNextExternal) {
                return callback()
              }
            }
583

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
            // `isNextExternal` special cases Next.js' internal requires that
            // should not be bundled. We need to skip the base resolve routine
            // to prevent it from being bundled (assumes Next.js version cannot
            // mismatch).
            if (!isNextExternal) {
              // 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).
              let baseRes: string | null
              try {
                baseRes = resolveRequest(request, `${dir}/`)
              } catch (err) {
                baseRes = null
              }

              // 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 (baseRes !== res) {
                return callback()
              }
606 607
            }

608 609 610
            // Default pages have to be transpiled
            if (
              !res.match(/next[/\\]dist[/\\]next-server[/\\]/) &&
611
              (res.match(/[/\\]next[/\\]dist[/\\]/) ||
612
                // This is the @babel/plugin-transform-runtime "helpers: true" option
613
                res.match(/node_modules[/\\]@babel[/\\]runtime[/\\]/))
614 615 616
            ) {
              return callback()
            }
K
k-kawakami 已提交
617

618 619 620 621 622 623 624
            // 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 callback()
            }
K
k-kawakami 已提交
625

626 627
            // Anything else that is standard JavaScript within `node_modules`
            // can be externalized.
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
            if (isNextExternal || res.match(/node_modules[/\\].*\.js$/)) {
              const externalRequest = isNextExternal
                ? // Generate Next.js external import
                  path.posix.join(
                    'next',
                    'dist',
                    path
                      .relative(
                        // Root of Next.js package:
                        path.join(__dirname, '..'),
                        res
                      )
                      // Windows path normalization
                      .replace(/\\/g, '/')
                  )
                : request

              return callback(undefined, `commonjs ${externalRequest}`)
646
            }
K
k-kawakami 已提交
647

648
            // Default behavior: bundle the code!
649
            callback()
650 651 652
          },
        ]
      : [
653 654
          // 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
655
          '@ampproject/toolbox-optimizer', // except this one
656
        ],
657
    optimization: {
658
      noEmitOnErrors: dev,
659 660 661 662
      checkWasmTypes: false,
      nodeEnv: false,
      splitChunks: isServer ? false : splitChunksConfig,
      runtimeChunk: isServer
663 664 665
        ? isWebpack5 && !isLikeServerless
          ? { name: 'webpack-runtime' }
          : undefined
666 667 668
        : { name: CLIENT_STATIC_FILES_RUNTIME_WEBPACK },
      minimize: !(dev || isServer),
      minimizer: [
J
Joe Haddad 已提交
669
        // Minify JavaScript
670
        new TerserPlugin({
J
Joe Haddad 已提交
671
          extractComments: false,
672 673
          cache: path.join(distDir, 'cache', 'next-minifier'),
          parallel: config.experimental.cpus || true,
674 675
          terserOptions,
        }),
J
Joe Haddad 已提交
676
        // Minify CSS
677 678 679 680 681 682 683 684 685
        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,
J
Joe Haddad 已提交
686
            },
687 688 689
          },
        }),
      ],
690
    },
N
nkzawa 已提交
691
    context: dir,
692 693 694
    node: {
      setImmediate: false,
    },
695
    // Kept as function to be backwards compatible
T
Tim Neutkens 已提交
696 697
    entry: async () => {
      return {
698 699
        ...(clientEntries ? clientEntries : {}),
        ...entrypoints,
700 701 702 703 704 705 706
        ...(isServer
          ? {
              'init-server.js': 'next-plugin-loader?middleware=on-init-server!',
              'on-error-server.js':
                'next-plugin-loader?middleware=on-error-server!',
            }
          : {}),
T
Tim Neutkens 已提交
707 708
      }
    },
709
    watchOptions: {
710
      ignored: ['**/.git/**', '**/node_modules/**', '**/.next/**'],
711
    },
N
nkzawa 已提交
712
    output: {
713
      path: outputPath,
714
      // On the server we don't use the chunkhash
715 716 717
      filename: isServer
        ? '[name].js'
        : `static/chunks/[name]${dev ? '' : '-[chunkhash]'}.js`,
718 719
      library: isServer ? undefined : '_N_E',
      libraryTarget: isServer ? 'commonjs2' : 'assign',
720 721 722 723 724 725
      hotUpdateChunkFilename: isWebpack5
        ? 'static/webpack/[id].[fullhash].hot-update.js'
        : 'static/webpack/[id].[hash].hot-update.js',
      hotUpdateMainFilename: isWebpack5
        ? 'static/webpack/[fullhash].hot-update.json'
        : 'static/webpack/[hash].hot-update.json',
726
      // This saves chunks with the name given via `import()`
727 728 729
      chunkFilename: isServer
        ? `${dev ? '[name]' : '[name].[contenthash]'}.js`
        : `static/chunks/${dev ? '[name]' : '[name].[contenthash]'}.js`,
A
Andy 已提交
730
      strictModuleExceptionHandling: true,
731
      crossOriginLoading: crossOrigin,
732
      futureEmitAssets: !dev,
733
      webassemblyModuleFilename: 'static/wasm/[modulehash].wasm',
N
nkzawa 已提交
734
    },
735
    performance: false,
T
Tim Neutkens 已提交
736
    resolve: resolveConfig,
N
nkzawa 已提交
737
    resolveLoader: {
738 739 740
      // The loaders Next.js provides
      alias: [
        'emit-file-loader',
741
        'error-loader',
742 743 744 745 746
        'next-babel-loader',
        'next-client-pages-loader',
        'next-data-loader',
        'next-serverless-loader',
        'noop-loader',
747
        'next-plugin-loader',
748 749 750
      ].reduce((alias, loader) => {
        // using multiple aliases to replace `resolveLoader.modules`
        alias[loader] = path.join(__dirname, 'webpack', 'loaders', loader)
751

752 753
        return alias
      }, {} as Record<string, string>),
N
Naoyuki Kanezawa 已提交
754
      modules: [
755
        'node_modules',
756 757
        ...nodePathList, // Support for NODE_PATH environment variable
      ],
758
      plugins: isWebpack5 ? [] : [require('pnp-webpack-plugin')],
N
nkzawa 已提交
759 760
    },
    module: {
761
      rules: [
T
Tim Neutkens 已提交
762
        {
763
          test: /\.(tsx|ts|js|mjs|jsx)$/,
764
          include: [dir, ...babelIncludeRegexes],
765 766
          exclude: (excludePath: string) => {
            if (babelIncludeRegexes.some((r) => r.test(excludePath))) {
767 768
              return false
            }
769
            return /node_modules/.test(excludePath)
770
          },
771 772 773 774 775
          use: config.experimental.babelMultiThread
            ? [
                // Move Babel transpilation into a thread pool (2 workers, unlimited batch size).
                // Applying a cache to the off-thread work avoids paying transfer costs for unchanged modules.
                {
G
Guy Bedford 已提交
776
                  loader: 'next/dist/compiled/cache-loader',
777 778 779 780 781 782 783 784 785
                  options: {
                    cacheContext: dir,
                    cacheDirectory: path.join(dir, '.next', 'cache', 'webpack'),
                    cacheIdentifier: `webpack${isServer ? '-server' : ''}${
                      config.experimental.modern ? '-hasmodern' : ''
                    }`,
                  },
                },
                {
G
Guy Bedford 已提交
786
                  loader: require.resolve('next/dist/compiled/thread-loader'),
787 788 789 790 791
                  options: {
                    workers: 2,
                    workerParallelJobs: Infinity,
                  },
                },
792 793 794
                hasReactRefresh
                  ? require.resolve('@next/react-refresh-utils/loader')
                  : '',
795
                defaultLoaders.babel,
796 797 798 799
              ].filter(Boolean)
            : hasReactRefresh
            ? [
                require.resolve('@next/react-refresh-utils/loader'),
800
                defaultLoaders.babel,
801 802
              ]
            : defaultLoaders.babel,
803
        },
804
      ].filter(Boolean),
N
nkzawa 已提交
805
    },
T
Tim Neutkens 已提交
806
    plugins: [
807
      hasReactRefresh && new ReactRefreshWebpackPlugin(),
808 809 810 811 812 813 814 815
      // Makes sure `Buffer` is polyfilled in client-side bundles (same behavior as webpack 4)
      isWebpack5 &&
        !isServer &&
        new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
      // Makes sure `process` is polyfilled in client-side bundles (same behavior as webpack 4)
      isWebpack5 &&
        !isServer &&
        new webpack.ProvidePlugin({ process: ['process'] }),
816
      // This plugin makes sure `output.filename` is used for entry chunks
T
Tim Neutkens 已提交
817
      !isWebpack5 && new ChunkNamesPlugin(),
818
      new webpack.DefinePlugin({
819 820 821 822 823 824 825 826 827
        ...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
          },
          {}
        ),
828
        ...Object.keys(config.env).reduce((acc, key) => {
829
          if (/^(?:NODE_.+)|^(?:__.+)$/i.test(key)) {
830
            throw new Error(
831
              `The key "${key}" under "env" in next.config.js is not allowed. https://err.sh/vercel/next.js/env-key-not-allowed`
832
            )
833 834 835
          }

          return {
836
            ...acc,
837
            [`process.env.${key}`]: JSON.stringify(config.env[key]),
838
          }
839
        }, {}),
840
        'process.env.NODE_ENV': JSON.stringify(webpackMode),
841
        'process.env.__NEXT_CROSS_ORIGIN': JSON.stringify(crossOrigin),
842
        'process.browser': JSON.stringify(!isServer),
J
JJ Kasper 已提交
843 844 845
        'process.env.__NEXT_TEST_MODE': JSON.stringify(
          process.env.__NEXT_TEST_MODE
        ),
846
        // This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory
847 848 849 850 851
        ...(dev && !isServer
          ? {
              'process.env.__NEXT_DIST_DIR': JSON.stringify(distDir),
            }
          : {}),
J
Jan Potoms 已提交
852
        'process.env.__NEXT_TRAILING_SLASH': JSON.stringify(
853
          config.trailingSlash
J
Jan Potoms 已提交
854
        ),
855
        'process.env.__NEXT_EXPORT_TRAILING_SLASH': JSON.stringify(
856
          config.exportTrailingSlash
857
        ),
858 859 860 861 862 863 864 865 866
        'process.env.__NEXT_MODERN_BUILD': JSON.stringify(
          config.experimental.modern && !dev
        ),
        'process.env.__NEXT_BUILD_INDICATOR': JSON.stringify(
          config.devIndicators.buildActivity
        ),
        'process.env.__NEXT_PRERENDER_INDICATOR': JSON.stringify(
          config.devIndicators.autoPrerender
        ),
867 868 869
        'process.env.__NEXT_PLUGINS': JSON.stringify(
          config.experimental.plugins
        ),
G
Gerald Monaco 已提交
870 871 872
        'process.env.__NEXT_STRICT_MODE': JSON.stringify(
          config.reactStrictMode
        ),
873 874 875
        'process.env.__NEXT_REACT_MODE': JSON.stringify(
          config.experimental.reactMode
        ),
P
Prateek Bhatnagar 已提交
876 877 878
        'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify(
          config.experimental.optimizeFonts
        ),
879 880 881
        'process.env.__NEXT_SCROLL_RESTORATION': JSON.stringify(
          config.experimental.scrollRestoration
        ),
882
        'process.env.__NEXT_ROUTER_BASEPATH': JSON.stringify(config.basePath),
883
        ...(isServer
884 885 886 887 888 889
          ? {
              // 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),
            }
890
          : undefined),
891
        // stub process.env with proxy to warn a missing value is
892 893
        // being accessed in development mode
        ...(config.experimental.pageEnv && process.env.NODE_ENV !== 'production'
894
          ? {
895
              'process.env': `
896 897 898 899 900 901 902 903 904 905 906
            new Proxy(${isServer ? '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://err.sh/next.js/missing-env-value\`)
                }
                return target[prop]
              }
            })
          `,
            }
          : {}),
907
      }),
908 909 910 911
      !isServer &&
        new ReactLoadablePlugin({
          filename: REACT_LOADABLE_MANIFEST,
        }),
912
      !isServer && new DropClientPage(),
913 914 915 916 917
      // 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.future.excludeDefaultMomentLocales &&
918 919 920 921
        new webpack.IgnorePlugin({
          resourceRegExp: /^\.\/locale$/,
          contextRegExp: /moment$/,
        }),
922 923 924 925 926 927 928
      ...(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')
929
            const devPlugins = [new NextJsRequireCacheHotReloader()]
930

931
            if (!isServer) {
932 933
              devPlugins.push(new webpack.HotModuleReplacementPlugin())
            }
934

935 936 937
            return devPlugins
          })()
        : []),
938 939
      // Webpack 5 no longer requires this plugin in production:
      !isWebpack5 && !dev && new webpack.HashedModuleIdsPlugin(),
940 941
      !dev &&
        new webpack.IgnorePlugin({
942 943
          resourceRegExp: /react-is/,
          contextRegExp: /(next-server|next)[\\/]dist[\\/]/,
944
        }),
J
Joe Haddad 已提交
945
      isServerless && isServer && new ServerlessPlugin(),
946
      isServer && new PagesManifestPlugin(isLikeServerless),
947 948
      !isWebpack5 &&
        target === 'server' &&
949 950
        isServer &&
        new NextJsSSRModuleCachePlugin({ outputPath }),
951
      isServer && new NextJsSsrImportPlugin(),
952 953 954 955 956
      !isServer &&
        new BuildManifestPlugin({
          buildId,
          modern: config.experimental.modern,
        }),
957 958 959
      tracer &&
        new ProfilingPlugin({
          tracer,
960
        }),
961 962
      !isWebpack5 &&
        config.experimental.modern &&
963 964
        !isServer &&
        !dev &&
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
        (() => {
          const { NextEsmPlugin } = require('./webpack/plugins/next-esm-plugin')
          return new NextEsmPlugin({
            filename: (getFileName: Function | string) => (...args: any[]) => {
              const name =
                typeof getFileName === 'function'
                  ? getFileName(...args)
                  : getFileName

              return name.includes('.js')
                ? name.replace(/\.js$/, '.module.js')
                : escapePathVariables(
                    args[0].chunk.name.replace(/\.js$/, '.module.js')
                  )
            },
            chunkFilename: (inputChunkName: string) =>
              inputChunkName.replace(/\.js$/, '.module.js'),
          })
        })(),
P
Prateek Bhatnagar 已提交
984 985 986
      config.experimental.optimizeFonts &&
        !dev &&
        isServer &&
J
Joe Haddad 已提交
987 988 989 990 991 992
        (function () {
          const {
            FontStylesheetGatheringPlugin,
          } = require('./webpack/plugins/font-stylesheet-gathering-plugin')
          return new FontStylesheetGatheringPlugin()
        })(),
993
      config.experimental.conformance &&
994
        !isWebpack5 &&
995 996
        !dev &&
        new WebpackConformancePlugin({
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
          tests: [
            !isServer &&
              conformanceConfig.MinificationConformanceCheck.enabled &&
              new MinificationConformanceCheck(),
            conformanceConfig.ReactSyncScriptsConformanceCheck.enabled &&
              new ReactSyncScriptsConformanceCheck({
                AllowedSources:
                  conformanceConfig.ReactSyncScriptsConformanceCheck
                    .allowedSources || [],
              }),
            !isServer &&
              conformanceConfig.DuplicatePolyfillsConformanceCheck.enabled &&
              new DuplicatePolyfillsConformanceCheck({
                BlockedAPIToBePolyfilled:
                  conformanceConfig.DuplicatePolyfillsConformanceCheck
                    .BlockedAPIToBePolyfilled,
              }),
1014 1015 1016 1017 1018
            !isServer &&
              conformanceConfig.GranularChunksConformanceCheck.enabled &&
              new GranularChunksConformanceCheck(
                splitChunksConfigs.prodGranular
              ),
1019
          ].filter(Boolean),
1020
        }),
J
Joe Haddad 已提交
1021
      new WellKnownErrorsPlugin(),
1022
    ].filter((Boolean as any) as ExcludesFalse),
1023
  }
1024

1025 1026 1027 1028 1029
  // Support tsconfig and jsconfig baseUrl
  if (resolvedBaseUrl) {
    webpackConfig.resolve?.modules?.push(resolvedBaseUrl)
  }

1030
  if (jsConfig?.compilerOptions?.paths && resolvedBaseUrl) {
1031
    webpackConfig.resolve?.plugins?.unshift(
1032 1033 1034 1035
      new JsConfigPathsPlugin(jsConfig.compilerOptions.paths, resolvedBaseUrl)
    )
  }

1036 1037 1038 1039 1040
  if (isWebpack5) {
    // On by default:
    delete webpackConfig.output?.futureEmitAssets
    // No longer polyfills Node.js modules:
    if (webpackConfig.node) delete webpackConfig.node.setImmediate
1041 1042 1043 1044 1045 1046 1047

    if (dev) {
      if (!webpackConfig.optimization) {
        webpackConfig.optimization = {}
      }
      webpackConfig.optimization.usedExports = false
    }
1048 1049
  }

1050 1051 1052 1053 1054
  webpackConfig = await buildConfiguration(webpackConfig, {
    rootDirectory: dir,
    customAppFile,
    isDevelopment: dev,
    isServer,
1055
    assetPrefix: config.assetPrefix || '',
1056
    sassOptions: config.sassOptions,
1057
    productionBrowserSourceMaps,
1058 1059
  })

1060
  let originalDevtool = webpackConfig.devtool
T
Tim Neutkens 已提交
1061
  if (typeof config.webpack === 'function') {
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
    webpackConfig = config.webpack(webpackConfig, {
      dir,
      dev,
      isServer,
      buildId,
      config,
      defaultLoaders,
      totalPages,
      webpack,
    })
1072

1073 1074 1075 1076 1077
    if (dev && originalDevtool !== webpackConfig.devtool) {
      webpackConfig.devtool = originalDevtool
      devtoolRevertWarning(originalDevtool)
    }

1078
    if (typeof (webpackConfig as any).then === 'function') {
1079
      console.warn(
1080
        '> Promise returned in next config. https://err.sh/vercel/next.js/promise-in-next-config'
1081
      )
1082
    }
1083
  }
T
Tim Neutkens 已提交
1084

1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
  // 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
    }
  }

1095 1096 1097 1098 1099
  function canMatchCss(rule: webpack.RuleSetCondition | undefined): boolean {
    if (!rule) {
      return false
    }

1100 1101 1102 1103 1104 1105 1106 1107
    const fileNames = [
      '/tmp/test.css',
      '/tmp/test.scss',
      '/tmp/test.sass',
      '/tmp/test.less',
      '/tmp/test.styl',
    ]

J
Joe Haddad 已提交
1108
    if (rule instanceof RegExp && fileNames.some((input) => rule.test(input))) {
1109 1110 1111 1112
      return true
    }

    if (typeof rule === 'function') {
1113
      if (
J
Joe Haddad 已提交
1114
        fileNames.some((input) => {
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
          try {
            if (rule(input)) {
              return true
            }
          } catch (_) {}
          return false
        })
      ) {
        return true
      }
1125 1126 1127 1128 1129 1130 1131 1132 1133
    }

    if (Array.isArray(rule) && rule.some(canMatchCss)) {
      return true
    }

    return false
  }

1134 1135
  const hasUserCssConfig =
    webpackConfig.module?.rules.some(
J
Joe Haddad 已提交
1136
      (rule) => canMatchCss(rule.test) || canMatchCss(rule.include)
1137
    ) ?? false
1138

1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
  if (hasUserCssConfig) {
    // only show warning for one build
    if (isServer) {
      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://err.sh/next.js/built-in-css-disabled\n'
      )
    }

    if (webpackConfig.module?.rules.length) {
      // Remove default CSS Loader
      webpackConfig.module.rules = webpackConfig.module.rules.filter(
J
Joe Haddad 已提交
1154
        (r) =>
1155 1156 1157 1158 1159
          !(
            typeof r.oneOf?.[0]?.options === 'object' &&
            r.oneOf[0].options.__next_css_remove === true
          )
      )
1160
    }
1161 1162 1163
    if (webpackConfig.plugins?.length) {
      // Disable CSS Extraction Plugin
      webpackConfig.plugins = webpackConfig.plugins.filter(
J
Joe Haddad 已提交
1164
        (p) => (p as any).__next_css_remove !== true
1165 1166 1167 1168 1169
      )
    }
    if (webpackConfig.optimization?.minimizer?.length) {
      // Disable CSS Minifier
      webpackConfig.optimization.minimizer = webpackConfig.optimization.minimizer.filter(
J
Joe Haddad 已提交
1170
        (e) => (e as any).__next_css_remove !== true
1171 1172 1173 1174
      )
    }
  } else {
    await __overrideCssConfiguration(dir, !dev, webpackConfig)
1175 1176
  }

1177
  // check if using @zeit/next-typescript and show warning
1178 1179 1180
  if (
    isServer &&
    webpackConfig.module &&
1181 1182 1183 1184
    Array.isArray(webpackConfig.module.rules)
  ) {
    let foundTsRule = false

1185 1186
    webpackConfig.module.rules = webpackConfig.module.rules.filter(
      (rule): boolean => {
1187
        if (!(rule.test instanceof RegExp)) return true
1188
        if ('noop.ts'.match(rule.test) && !'noop.js'.match(rule.test)) {
1189 1190 1191 1192 1193
          // remove if it matches @zeit/next-typescript
          foundTsRule = rule.use === defaultLoaders.babel
          return !foundTsRule
        }
        return true
1194 1195
      }
    )
1196 1197

    if (foundTsRule) {
1198
      console.warn(
1199
        '\n@zeit/next-typescript is no longer needed since Next.js has built-in support for TypeScript now. Please remove it from your next.config.js and your .babelrc\n'
1200
      )
1201 1202 1203
    }
  }

1204
  // Patch `@zeit/next-sass`, `@zeit/next-less`, `@zeit/next-stylus` for compatibility
1205
  if (webpackConfig.module && Array.isArray(webpackConfig.module.rules)) {
J
Joe Haddad 已提交
1206
    ;[].forEach.call(webpackConfig.module.rules, function (
1207 1208 1209 1210 1211 1212
      rule: webpack.RuleSetRule
    ) {
      if (!(rule.test instanceof RegExp && Array.isArray(rule.use))) {
        return
      }

1213 1214 1215 1216
      const isSass =
        rule.test.source === '\\.scss$' || rule.test.source === '\\.sass$'
      const isLess = rule.test.source === '\\.less$'
      const isCss = rule.test.source === '\\.css$'
1217
      const isStylus = rule.test.source === '\\.styl$'
1218 1219

      // Check if the rule we're iterating over applies to Sass, Less, or CSS
1220
      if (!(isSass || isLess || isCss || isStylus)) {
1221 1222 1223
        return
      }

J
Joe Haddad 已提交
1224
      ;[].forEach.call(rule.use, function (use: webpack.RuleSetUseItem) {
1225 1226 1227 1228 1229
        if (
          !(
            use &&
            typeof use === 'object' &&
            // Identify use statements only pertaining to `css-loader`
1230 1231
            (use.loader === 'css-loader' ||
              use.loader === 'css-loader/locals') &&
1232 1233 1234 1235
            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
1236 1237
            // old `css-loader` versions. Custom setups (that aren't next-sass,
            // next-less or next-stylus) likely have the newer version.
1238
            // We still handle this gracefully below.
1239 1240 1241 1242 1243
            (Object.prototype.hasOwnProperty.call(use.options, 'minimize') ||
              Object.prototype.hasOwnProperty.call(
                use.options,
                'exportOnlyLocals'
              ))
1244 1245 1246 1247 1248 1249 1250
          )
        ) {
          return
        }

        // Try to monkey patch within a try-catch. We shouldn't fail the build
        // if we cannot pull this off.
1251 1252
        // The user may not even be using the `next-sass` or `next-less` or
        // `next-stylus` plugins.
1253 1254
        // If it does work, great!
        try {
1255 1256
          // Resolve the version of `@zeit/next-css` as depended on by the Sass,
          // Less or Stylus plugin.
1257 1258
          const correctNextCss = resolveRequest(
            '@zeit/next-css',
1259 1260 1261 1262 1263 1264 1265 1266 1267
            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'
1268 1269
                    : isStylus
                    ? '@zeit/next-stylus'
1270 1271
                    : 'next'
                )
1272 1273 1274 1275 1276 1277 1278
          )

          // 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.
1279
            const correctCssLoader = resolveRequest(use.loader, correctNextCss)
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
            if (correctCssLoader) {
              // We saved the user from a failed build!
              use.loader = correctCssLoader
            }
          }
        } catch (_) {
          // The error is not required to be handled.
        }
      })
    })
  }

1292
  // Backwards compat for `main.js` entry key
1293
  const originalEntry: any = webpackConfig.entry
1294 1295
  if (typeof originalEntry !== 'undefined') {
    webpackConfig.entry = async () => {
1296 1297 1298 1299
      const entry: WebpackEntrypoints =
        typeof originalEntry === 'function'
          ? await originalEntry()
          : originalEntry
1300 1301
      // Server compilation doesn't have main.js
      if (clientEntries && entry['main.js'] && entry['main.js'].length > 0) {
1302 1303 1304
        const originalFile = clientEntries[
          CLIENT_STATIC_FILES_RUNTIME_MAIN
        ] as string
1305 1306 1307 1308
        entry[CLIENT_STATIC_FILES_RUNTIME_MAIN] = [
          ...entry['main.js'],
          originalFile,
        ]
1309
      }
1310
      delete entry['main.js']
1311

1312
      return entry
1313 1314 1315
    }
  }

1316
  if (!dev) {
1317 1318
    // entry is always a function
    webpackConfig.entry = await (webpackConfig.entry as webpack.EntryFunc)()
1319 1320
  }

T
Tim Neutkens 已提交
1321
  return webpackConfig
N
nkzawa 已提交
1322
}