webpack-config.ts 22.1 KB
Newer Older
1
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'
2 3
import {
  CLIENT_STATIC_FILES_RUNTIME_MAIN,
4 5 6
  CLIENT_STATIC_FILES_RUNTIME_WEBPACK,
  REACT_LOADABLE_MANIFEST,
  SERVER_DIRECTORY,
7
  SERVERLESS_DIRECTORY,
8
} from 'next-server/constants'
9 10
import resolve from 'next/dist/compiled/resolve/index.js'
import path from 'path'
11
import webpack from 'webpack'
12

13
import {
14
  DOT_NEXT_ALIAS,
15 16 17 18
  NEXT_PROJECT_ROOT,
  NEXT_PROJECT_ROOT_DIST_CLIENT,
  PAGES_DIR_ALIAS,
} from '../lib/constants'
19
import { fileExists } from '../lib/file-exists'
20
import { WebpackEntrypoints } from './entries'
21
import { AllModulesIdentifiedPlugin } from './webpack/plugins/all-modules-identified-plugin'
22
import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin'
23
import { ChunkGraphPlugin } from './webpack/plugins/chunk-graph-plugin'
24
import ChunkNamesPlugin from './webpack/plugins/chunk-names-plugin'
25
import { importAutoDllPlugin } from './webpack/plugins/dll-import'
26
import { HashedChunkIdsPlugin } from './webpack/plugins/hashed-chunk-ids-plugin'
27
import { DropClientPage } from './webpack/plugins/next-drop-client-page-plugin'
28
import NextEsmPlugin from './webpack/plugins/next-esm-plugin'
29 30 31 32 33 34 35 36
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'
import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin'
import { ServerlessPlugin } from './webpack/plugins/serverless-plugin'
import { SharedRuntimePlugin } from './webpack/plugins/shared-runtime-plugin'
import { TerserPlugin } from './webpack/plugins/terser-webpack-plugin/src/index'

37
type ExcludesFalse = <T>(x: T | false) => x is T
38

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
export default async function getBaseWebpackConfig(
  dir: string,
  {
    dev = false,
    isServer = false,
    buildId,
    config,
    target = 'server',
    entrypoints,
    selectivePageBuilding = false,
  }: {
    dev?: boolean
    isServer?: boolean
    buildId: string
    config: any
    target?: string
    entrypoints: WebpackEntrypoints
    selectivePageBuilding?: boolean
  }
): Promise<webpack.Configuration> {
59
  const distDir = path.join(dir, config.distDir)
T
Tim Neutkens 已提交
60 61
  const defaultLoaders = {
    babel: {
62
      loader: 'next-babel-loader',
63 64 65 66
      options: {
        isServer,
        distDir,
        cwd: dir,
67
        cache: !selectivePageBuilding,
68 69
        asyncToPromises: config.experimental.asyncToPromises,
      },
70
    },
71
    // Backwards compat
72
    hotSelfAccept: {
73 74
      loader: 'noop-loader',
    },
T
Tim Neutkens 已提交
75 76
  }

T
Tim Neutkens 已提交
77 78 79
  // Support for NODE_PATH
  const nodePathList = (process.env.NODE_PATH || '')
    .split(process.platform === 'win32' ? ';' : ':')
80
    .filter(p => !!p)
T
Tim Neutkens 已提交
81

82 83 84 85 86 87
  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 已提交
88
  const outputPath = path.join(distDir, isServer ? outputDir : '')
89
  const totalPages = Object.keys(entrypoints).length
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  const clientEntries = !isServer
    ? {
        // Backwards compatibility
        'main.js': [],
        [CLIENT_STATIC_FILES_RUNTIME_MAIN]:
          `.${path.sep}` +
          path.relative(
            dir,
            path.join(
              NEXT_PROJECT_ROOT_DIST_CLIENT,
              dev ? `next-dev.js` : 'next.js'
            )
          ),
      }
    : undefined
N
nkzawa 已提交
105

106 107 108 109
  let typeScriptPath
  try {
    typeScriptPath = resolve.sync('typescript', { basedir: dir })
  } catch (_) {}
110
  const tsConfigPath = path.join(dir, 'tsconfig.json')
111 112 113
  const useTypeScript = Boolean(
    typeScriptPath && (await fileExists(tsConfigPath))
  )
114

T
Tim Neutkens 已提交
115
  const resolveConfig = {
116
    // Disable .mjs for node_modules bundling
117
    extensions: isServer
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
      ? [
          ...(useTypeScript ? ['.tsx', '.ts'] : []),
          '.js',
          '.mjs',
          '.jsx',
          '.json',
          '.wasm',
        ]
      : [
          ...(useTypeScript ? ['.tsx', '.ts'] : []),
          '.mjs',
          '.js',
          '.jsx',
          '.json',
          '.wasm',
        ],
T
Tim Neutkens 已提交
134 135
    modules: [
      'node_modules',
136
      ...nodePathList, // Support for NODE_PATH environment variable
T
Tim Neutkens 已提交
137 138
    ],
    alias: {
139 140 141 142 143 144
      // 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
      'next/head': 'next-server/dist/lib/head.js',
      'next/router': 'next/dist/client/router.js',
      'next/config': 'next-server/dist/lib/runtime-config.js',
      'next/dynamic': 'next-server/dist/lib/dynamic.js',
T
Tim Neutkens 已提交
145
      next: NEXT_PROJECT_ROOT,
146
      [PAGES_DIR_ALIAS]: path.join(dir, 'pages'),
147
      [DOT_NEXT_ALIAS]: distDir,
148
    },
149
    mainFields: isServer ? ['main', 'module'] : ['browser', 'module', 'main'],
T
Tim Neutkens 已提交
150 151
  }

T
Tim Neutkens 已提交
152 153
  const webpackMode = dev ? 'development' : 'production'

154 155 156
  const terserPluginConfig = {
    parallel: true,
    sourceMap: false,
157
    cache: !selectivePageBuilding,
158
    cpus: config.experimental.cpus,
159
    distDir: distDir,
160
  }
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
  const terserOptions = {
    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/zeit/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,
    },
  }

J
Joe Haddad 已提交
182
  const devtool = dev ? 'cheap-module-source-map' : false
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  // 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,
      },
    },
    selective: {
      cacheGroups: {
        default: false,
        vendors: false,
        react: {
          name: 'commons',
          chunks: 'all',
201
          test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
        },
      },
    },
    prod: {
      chunks: 'all',
      cacheGroups: {
        default: false,
        vendors: false,
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
        },
        react: {
          name: 'commons',
          chunks: 'all',
218
          test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
        },
      },
    },
  }

  // Select appropriate SplitChunksPlugin config for this build
  let splitChunksConfig: webpack.Options.SplitChunksOptions
  if (dev) {
    splitChunksConfig = splitChunksConfigs.dev
  } else if (selectivePageBuilding) {
    splitChunksConfig = splitChunksConfigs.selective
  } else {
    splitChunksConfig = splitChunksConfigs.prod
  }

234 235 236 237 238
  const crossOrigin =
    !config.crossOrigin && config.experimental.modern
      ? 'anonymous'
      : config.crossOrigin

239
  let webpackConfig: webpack.Configuration = {
J
JJ Kasper 已提交
240
    devtool,
T
Tim Neutkens 已提交
241
    mode: webpackMode,
T
Tim Neutkens 已提交
242 243
    name: isServer ? 'server' : 'client',
    target: isServer ? 'node' : 'web',
244 245
    externals: !isServer
      ? undefined
246
      : !isServerless
247 248 249 250 251 252 253 254 255 256
      ? [
          (context, request, callback) => {
            const notExternalModules = [
              'next/app',
              'next/document',
              'next/link',
              'next/error',
              'string-hash',
              'next/constants',
            ]
257

258 259 260
            if (notExternalModules.indexOf(request) !== -1) {
              return callback()
            }
K
k-kawakami 已提交
261

262 263 264 265 266 267 268
            resolve(
              request,
              { basedir: dir, preserveSymlinks: true },
              (err, res) => {
                if (err) {
                  return callback()
                }
K
k-kawakami 已提交
269

270 271 272
                if (!res) {
                  return callback()
                }
K
k-kawakami 已提交
273

274 275 276 277 278 279 280 281
                // Default pages have to be transpiled
                if (
                  res.match(/next[/\\]dist[/\\]/) ||
                  res.match(/node_modules[/\\]@babel[/\\]runtime[/\\]/) ||
                  res.match(/node_modules[/\\]@babel[/\\]runtime-corejs2[/\\]/)
                ) {
                  return callback()
                }
K
k-kawakami 已提交
282

283 284 285 286 287 288 289
                // 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 已提交
290

291 292 293
                if (res.match(/node_modules[/\\].*\.js$/)) {
                  return callback(undefined, `commonjs ${request}`)
                }
K
k-kawakami 已提交
294

295 296 297 298 299 300
                callback()
              }
            )
          },
        ]
      : [
301 302
          // 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
303
          'amp-toolbox-optimizer', // except this one
304 305 306 307 308 309 310
          (context, request, callback) => {
            if (
              request === 'react-ssr-prepass' &&
              !config.experimental.ampBindInitData
            ) {
              // if it's the Next.js' require mark it as external
              // since it's not used
311 312 313
              if (
                context.replace(/\\/g, '/').includes('next-server/dist/server')
              ) {
314 315 316 317 318
                return callback(undefined, `commonjs ${request}`)
              }
            }
            return callback()
          },
319 320 321 322 323
        ],
    optimization: Object.assign(
      {
        checkWasmTypes: false,
        nodeEnv: false,
324
      },
325 326 327 328
      isServer
        ? {
            splitChunks: false,
            minimize: false,
J
Joe Haddad 已提交
329
          }
330 331 332 333 334 335
        : {
            runtimeChunk: selectivePageBuilding
              ? false
              : {
                  name: CLIENT_STATIC_FILES_RUNTIME_WEBPACK,
                },
336
            splitChunks: splitChunksConfig,
J
Joe Haddad 已提交
337 338
            minimize: !dev,
            minimizer: !dev
339 340 341 342
              ? [
                  new TerserPlugin({
                    ...terserPluginConfig,
                    terserOptions: {
343 344
                      ...terserOptions,
                      // Disable compress when using terser loader
345 346
                      ...(selectivePageBuilding ||
                      config.experimental.terserLoader
347
                        ? { compress: false }
348 349 350 351 352
                        : undefined),
                    },
                  }),
                ]
              : undefined,
353
          },
354 355 356 357 358
      selectivePageBuilding
        ? {
            providedExports: false,
            usedExports: false,
            concatenateModules: false,
359
          }
360 361 362 363 364
        : undefined
    ),
    recordsPath: selectivePageBuilding
      ? undefined
      : path.join(outputPath, 'records.json'),
N
nkzawa 已提交
365
    context: dir,
366
    // Kept as function to be backwards compatible
T
Tim Neutkens 已提交
367 368
    entry: async () => {
      return {
369 370
        ...(clientEntries ? clientEntries : {}),
        ...entrypoints,
T
Tim Neutkens 已提交
371 372
      }
    },
N
nkzawa 已提交
373
    output: {
374
      path: outputPath,
375
      filename: ({ chunk }: { chunk: { name: string } }) => {
376
        // Use `[name]-[contenthash].js` in production
377 378 379 380 381
        if (
          !dev &&
          (chunk.name === CLIENT_STATIC_FILES_RUNTIME_MAIN ||
            chunk.name === CLIENT_STATIC_FILES_RUNTIME_WEBPACK)
        ) {
382
          return chunk.name.replace(/\.js$/, '-[contenthash].js')
383 384 385
        }
        return '[name]'
      },
T
Tim Neutkens 已提交
386
      libraryTarget: isServer ? 'commonjs2' : 'var',
387 388 389
      hotUpdateChunkFilename: 'static/webpack/[id].[hash].hot-update.js',
      hotUpdateMainFilename: 'static/webpack/[hash].hot-update.json',
      // This saves chunks with the name given via `import()`
390 391 392
      chunkFilename: isServer
        ? `${dev ? '[name]' : '[name].[contenthash]'}.js`
        : `static/chunks/${dev ? '[name]' : '[name].[contenthash]'}.js`,
A
Andy 已提交
393
      strictModuleExceptionHandling: true,
394
      crossOriginLoading: crossOrigin,
395
      futureEmitAssets: !dev,
396
      webassemblyModuleFilename: 'static/wasm/[modulehash].wasm',
N
nkzawa 已提交
397
    },
398
    performance: false,
T
Tim Neutkens 已提交
399
    resolve: resolveConfig,
N
nkzawa 已提交
400
    resolveLoader: {
N
Naoyuki Kanezawa 已提交
401
      modules: [
402
        path.join(__dirname, 'webpack', 'loaders'), // The loaders Next.js provides
403
        'node_modules',
404 405
        ...nodePathList, // Support for NODE_PATH environment variable
      ],
N
nkzawa 已提交
406
    },
T
Tim Neutkens 已提交
407
    // @ts-ignore this is filtered
N
nkzawa 已提交
408
    module: {
409
      strictExportPresence: true,
410
      rules: [
411
        (selectivePageBuilding || config.experimental.terserLoader) &&
J
Joe Haddad 已提交
412
          !isServer && {
413 414 415 416 417 418
            test: /\.(js|mjs|jsx)$/,
            exclude: /\.min\.(js|mjs|jsx)$/,
            use: {
              loader: 'next-minify-loader',
              options: {
                terserOptions: {
419
                  ...terserOptions,
420 421 422 423 424 425 426 427 428 429 430
                  mangle: false,
                },
              },
            },
          },
        config.experimental.ampBindInitData &&
          !isServer && {
            test: /\.(tsx|ts|js|mjs|jsx)$/,
            include: [path.join(dir, 'data')],
            use: 'next-data-loader',
          },
T
Tim Neutkens 已提交
431
        {
432
          test: /\.(tsx|ts|js|mjs|jsx)$/,
433 434 435 436 437
          include: [
            dir,
            /next-server[\\/]dist[\\/]lib/,
            /next[\\/]dist[\\/]client/,
            /next[\\/]dist[\\/]pages/,
438
            /[\\/](strip-ansi|ansi-regex)[\\/]/,
439
          ],
440
          exclude: (path: string) => {
441 442 443
            if (
              /next-server[\\/]dist[\\/]lib/.test(path) ||
              /next[\\/]dist[\\/]client/.test(path) ||
444
              /next[\\/]dist[\\/]pages/.test(path) ||
445
              /[\\/](strip-ansi|ansi-regex)[\\/]/.test(path)
446
            ) {
447 448
              return false
            }
449

450
            return /node_modules/.test(path)
451
          },
452
          use: defaultLoaders.babel,
453
        },
454
      ].filter(Boolean),
N
nkzawa 已提交
455
    },
T
Tim Neutkens 已提交
456
    plugins: [
457 458
      // This plugin makes sure `output.filename` is used for entry chunks
      new ChunkNamesPlugin(),
459
      new webpack.DefinePlugin({
460
        ...Object.keys(config.env).reduce((acc, key) => {
461
          if (/^(?:NODE_.+)|^(?:__.+)$/i.test(key)) {
462 463 464
            throw new Error(
              `The key "${key}" under "env" in next.config.js is not allowed. https://err.sh/zeit/next.js/env-key-not-allowed`
            )
465 466 467
          }

          return {
468
            ...acc,
469
            [`process.env.${key}`]: JSON.stringify(config.env[key]),
470
          }
471
        }, {}),
472
        'process.env.NODE_ENV': JSON.stringify(webpackMode),
473
        'process.crossOrigin': JSON.stringify(crossOrigin),
474 475
        'process.browser': JSON.stringify(!isServer),
        // This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory
476 477 478 479 480 481
        ...(dev && !isServer
          ? {
              'process.env.__NEXT_DIST_DIR': JSON.stringify(distDir),
            }
          : {}),
        'process.env.__NEXT_EXPORT_TRAILING_SLASH': JSON.stringify(
482
          config.exportTrailingSlash
483
        ),
484
        'process.env.__NEXT_MODERN_BUILD': config.experimental.modern && !dev,
485
        ...(isServer
486 487 488 489 490 491 492 493 494 495 496 497
          ? {
              // Allow browser-only code to be eliminated
              'typeof window': JSON.stringify('undefined'),
              // 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),
            }
          : {
              // Allow server-only code to be eliminated
              'typeof window': JSON.stringify('object'),
            }),
498
      }),
499 500 501 502
      !isServer &&
        new ReactLoadablePlugin({
          filename: REACT_LOADABLE_MANIFEST,
        }),
503
      !isServer && new DropClientPage(),
J
JJ Kasper 已提交
504 505 506 507 508
      new ChunkGraphPlugin(buildId, {
        dir,
        distDir,
        isServer,
      }),
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
      ...(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 {
              UnlinkRemovedPagesPlugin,
            } = require('./webpack/plugins/unlink-removed-pages-plugin')
            const devPlugins = [
              new UnlinkRemovedPagesPlugin(),
              new webpack.NoEmitOnErrorsPlugin(),
              new NextJsRequireCacheHotReloader(),
            ]
524

525
            if (!isServer) {
526
              const AutoDllPlugin = importAutoDllPlugin({ distDir })
527 528 529 530 531 532 533 534 535
              devPlugins.push(
                new AutoDllPlugin({
                  filename: '[name]_[hash].js',
                  path: './static/development/dll',
                  context: dir,
                  entry: {
                    dll: ['react', 'react-dom'],
                  },
                  config: {
J
JJ Kasper 已提交
536
                    devtool,
537 538 539 540 541 542 543
                    mode: webpackMode,
                    resolve: resolveConfig,
                  },
                })
              )
              devPlugins.push(new webpack.HotModuleReplacementPlugin())
            }
544

545 546 547
            return devPlugins
          })()
        : []),
548
      !dev && new webpack.HashedModuleIdsPlugin(),
549 550
      // This must come after HashedModuleIdsPlugin (it sets any modules that
      // were missed by HashedModuleIdsPlugin)
551
      !dev && selectivePageBuilding && new AllModulesIdentifiedPlugin(dir),
552 553
      // This sets chunk ids to be hashed versions of their names to reduce
      // bundle churn
554
      !dev && selectivePageBuilding && new HashedChunkIdsPlugin(buildId),
J
Joe Haddad 已提交
555
      // On the client we want to share the same runtime cache
556
      !isServer && selectivePageBuilding && new SharedRuntimePlugin(),
557 558 559 560 561 562 563 564 565 566 567 568
      !dev &&
        new webpack.IgnorePlugin({
          checkResource: (resource: string) => {
            return /react-is/.test(resource)
          },
          checkContext: (context: string) => {
            return (
              /next-server[\\/]dist[\\/]/.test(context) ||
              /next[\\/]dist[\\/]/.test(context)
            )
          },
        }),
569 570 571 572 573 574 575 576
      isLikeServerless &&
        new ServerlessPlugin(buildId, {
          isServer,
          isFlyingShuttle: selectivePageBuilding,
          isTrace: isServerlessTrace,
        }),
      isServer && new PagesManifestPlugin(isLikeServerless),
      target === 'server' &&
577 578
        isServer &&
        new NextJsSSRModuleCachePlugin({ outputPath }),
579 580
      isServer && new NextJsSsrImportPlugin(),
      !isServer && new BuildManifestPlugin(),
581 582 583 584 585 586 587
      config.experimental.profiling &&
        new webpack.debug.ProfilingPlugin({
          outputPath: path.join(
            distDir,
            `profile-events-${isServer ? 'server' : 'client'}.json`
          ),
        }),
588 589
      !isServer &&
        useTypeScript &&
590
        new ForkTsCheckerWebpackPlugin({
591
          typescript: typeScriptPath,
592
          async: dev,
593 594 595 596 597 598 599 600
          useTypescriptIncrementalApi: true,
          checkSyntacticErrors: true,
          tsconfig: tsConfigPath,
          reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*'],
          compilerOptions: { isolatedModules: true, noEmit: true },
          silent: true,
          formatter: 'codeframe',
        }),
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
      config.experimental.modern &&
        !isServer &&
        !dev &&
        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')
              : args[0].chunk.name.replace(/\.js$/, '.module.js')
          },
          chunkFilename: (inputChunkName: string) =>
            inputChunkName.replace(/\.js$/, '.module.js'),
        }),
618
    ].filter((Boolean as any) as ExcludesFalse),
619
  }
620

T
Tim Neutkens 已提交
621
  if (typeof config.webpack === 'function') {
622 623 624 625 626 627 628 629 630 631
    webpackConfig = config.webpack(webpackConfig, {
      dir,
      dev,
      isServer,
      buildId,
      config,
      defaultLoaders,
      totalPages,
      webpack,
    })
632 633 634

    // @ts-ignore: Property 'then' does not exist on type 'Configuration'
    if (typeof webpackConfig.then === 'function') {
635 636 637
      console.warn(
        '> Promise returned in next config. https://err.sh/zeit/next.js/promise-in-next-config.md'
      )
638
    }
639
  }
T
Tim Neutkens 已提交
640

641
  // check if using @zeit/next-typescript and show warning
642 643 644
  if (
    isServer &&
    webpackConfig.module &&
645 646 647 648
    Array.isArray(webpackConfig.module.rules)
  ) {
    let foundTsRule = false

649 650
    webpackConfig.module.rules = webpackConfig.module.rules.filter(
      (rule): boolean => {
651
        if (!(rule.test instanceof RegExp)) return true
652
        if ('noop.ts'.match(rule.test) && !'noop.js'.match(rule.test)) {
653 654 655 656 657
          // remove if it matches @zeit/next-typescript
          foundTsRule = rule.use === defaultLoaders.babel
          return !foundTsRule
        }
        return true
658 659
      }
    )
660 661

    if (foundTsRule) {
662 663 664
      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 next.config.js\n'
      )
665 666 667
    }
  }

668
  // Backwards compat for `main.js` entry key
669
  const originalEntry: any = webpackConfig.entry
670 671
  if (typeof originalEntry !== 'undefined') {
    webpackConfig.entry = async () => {
672 673 674 675
      const entry: WebpackEntrypoints =
        typeof originalEntry === 'function'
          ? await originalEntry()
          : originalEntry
676 677 678 679 680
      // Server compilation doesn't have main.js
      if (clientEntries && entry['main.js'] && entry['main.js'].length > 0) {
        const originalFile = clientEntries[CLIENT_STATIC_FILES_RUNTIME_MAIN]
        entry[CLIENT_STATIC_FILES_RUNTIME_MAIN] = [
          ...entry['main.js'],
681
          originalFile,
682
        ]
683
      }
684
      delete entry['main.js']
685

686
      return entry
687 688 689
    }
  }

690
  if (!dev) {
691 692 693 694
    // @ts-ignore entry is always a function
    webpackConfig.entry = await webpackConfig.entry()
  }

T
Tim Neutkens 已提交
695
  return webpackConfig
N
nkzawa 已提交
696
}