webpack-config.ts 20.8 KB
Newer Older
1
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'
2
import fs from 'fs'
3 4
import {
  CLIENT_STATIC_FILES_RUNTIME_MAIN,
5 6 7
  CLIENT_STATIC_FILES_RUNTIME_WEBPACK,
  REACT_LOADABLE_MANIFEST,
  SERVER_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 29 30 31 32 33 34 35
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'

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

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
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> {
58
  const distDir = path.join(dir, config.distDir)
T
Tim Neutkens 已提交
59 60
  const defaultLoaders = {
    babel: {
61
      loader: 'next-babel-loader',
62 63 64 65
      options: {
        isServer,
        distDir,
        cwd: dir,
66
        cache: !selectivePageBuilding,
67 68
        asyncToPromises: config.experimental.asyncToPromises,
      },
69
    },
70
    // Backwards compat
71
    hotSelfAccept: {
72 73
      loader: 'noop-loader',
    },
T
Tim Neutkens 已提交
74 75
  }

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

T
Tim Neutkens 已提交
81 82
  const outputDir = target === 'serverless' ? 'serverless' : SERVER_DIRECTORY
  const outputPath = path.join(distDir, isServer ? outputDir : '')
83
  const totalPages = Object.keys(entrypoints).length
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
  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 已提交
99

100 101 102 103
  let typeScriptPath
  try {
    typeScriptPath = resolve.sync('typescript', { basedir: dir })
  } catch (_) {}
104
  const tsConfigPath = path.join(dir, 'tsconfig.json')
105 106 107
  const useTypeScript = Boolean(
    typeScriptPath && (await fileExists(tsConfigPath))
  )
108

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

T
Tim Neutkens 已提交
146 147
  const webpackMode = dev ? 'development' : 'production'

148 149 150
  const terserPluginConfig = {
    parallel: true,
    sourceMap: false,
151
    cache: !selectivePageBuilding,
152
    cpus: config.experimental.cpus,
153
    distDir: distDir,
154
  }
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
  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 已提交
176
  const devtool = dev ? 'cheap-module-source-map' : false
177 178

  let webpackConfig: webpack.Configuration = {
J
JJ Kasper 已提交
179
    devtool,
T
Tim Neutkens 已提交
180
    mode: webpackMode,
T
Tim Neutkens 已提交
181 182
    name: isServer ? 'server' : 'client',
    target: isServer ? 'node' : 'web',
183 184 185 186 187 188 189 190 191 192 193 194 195
    externals: !isServer
      ? undefined
      : target !== 'serverless'
      ? [
          (context, request, callback) => {
            const notExternalModules = [
              'next/app',
              'next/document',
              'next/link',
              'next/error',
              'string-hash',
              'next/constants',
            ]
196

197 198 199
            if (notExternalModules.indexOf(request) !== -1) {
              return callback()
            }
K
k-kawakami 已提交
200

201 202 203 204 205 206 207
            resolve(
              request,
              { basedir: dir, preserveSymlinks: true },
              (err, res) => {
                if (err) {
                  return callback()
                }
K
k-kawakami 已提交
208

209 210 211
                if (!res) {
                  return callback()
                }
K
k-kawakami 已提交
212

213 214 215 216 217 218 219 220
                // 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 已提交
221

222 223 224 225 226 227 228
                // 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 已提交
229

230 231 232
                if (res.match(/node_modules[/\\].*\.js$/)) {
                  return callback(undefined, `commonjs ${request}`)
                }
K
k-kawakami 已提交
233

234 235 236 237 238 239 240 241 242
                callback()
              }
            )
          },
        ]
      : [
          // 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
          'amp-toolbox-optimizer', // except this one
243 244 245 246 247 248 249
          (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
250 251 252
              if (
                context.replace(/\\/g, '/').includes('next-server/dist/server')
              ) {
253 254 255 256 257
                return callback(undefined, `commonjs ${request}`)
              }
            }
            return callback()
          },
258 259 260 261 262
        ],
    optimization: Object.assign(
      {
        checkWasmTypes: false,
        nodeEnv: false,
263
      },
264 265 266 267
      isServer
        ? {
            splitChunks: false,
            minimize: false,
J
Joe Haddad 已提交
268
          }
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
        : {
            runtimeChunk: selectivePageBuilding
              ? false
              : {
                  name: CLIENT_STATIC_FILES_RUNTIME_WEBPACK,
                },
            splitChunks: dev
              ? {
                  cacheGroups: {
                    default: false,
                    vendors: false,
                  },
                }
              : selectivePageBuilding
              ? {
                  cacheGroups: {
                    default: false,
                    vendors: false,
                    react: {
                      name: 'commons',
                      chunks: 'all',
                      test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
                    },
                  },
                }
              : {
                  chunks: 'all',
                  cacheGroups: {
                    default: false,
                    vendors: false,
                    commons: {
                      name: 'commons',
                      chunks: 'all',
                      minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
                    },
                    react: {
                      name: 'commons',
                      chunks: 'all',
                      test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
                    },
                  },
                },
J
Joe Haddad 已提交
311 312
            minimize: !dev,
            minimizer: !dev
313 314 315 316
              ? [
                  new TerserPlugin({
                    ...terserPluginConfig,
                    terserOptions: {
317 318
                      ...terserOptions,
                      // Disable compress when using terser loader
319 320
                      ...(selectivePageBuilding ||
                      config.experimental.terserLoader
321
                        ? { compress: false }
322 323 324 325 326
                        : undefined),
                    },
                  }),
                ]
              : undefined,
327
          },
328 329 330 331 332
      selectivePageBuilding
        ? {
            providedExports: false,
            usedExports: false,
            concatenateModules: false,
333
          }
334 335 336 337 338
        : undefined
    ),
    recordsPath: selectivePageBuilding
      ? undefined
      : path.join(outputPath, 'records.json'),
N
nkzawa 已提交
339
    context: dir,
340
    // Kept as function to be backwards compatible
T
Tim Neutkens 已提交
341 342
    entry: async () => {
      return {
343 344
        ...(clientEntries ? clientEntries : {}),
        ...entrypoints,
T
Tim Neutkens 已提交
345 346
      }
    },
N
nkzawa 已提交
347
    output: {
348
      path: outputPath,
349
      filename: ({ chunk }: { chunk: { name: string } }) => {
350
        // Use `[name]-[contenthash].js` in production
351 352 353 354 355
        if (
          !dev &&
          (chunk.name === CLIENT_STATIC_FILES_RUNTIME_MAIN ||
            chunk.name === CLIENT_STATIC_FILES_RUNTIME_WEBPACK)
        ) {
356
          return chunk.name.replace(/\.js$/, '-[contenthash].js')
357 358 359
        }
        return '[name]'
      },
T
Tim Neutkens 已提交
360
      libraryTarget: isServer ? 'commonjs2' : 'var',
361 362 363
      hotUpdateChunkFilename: 'static/webpack/[id].[hash].hot-update.js',
      hotUpdateMainFilename: 'static/webpack/[hash].hot-update.json',
      // This saves chunks with the name given via `import()`
364 365 366
      chunkFilename: isServer
        ? `${dev ? '[name]' : '[name].[contenthash]'}.js`
        : `static/chunks/${dev ? '[name]' : '[name].[contenthash]'}.js`,
A
Andy 已提交
367
      strictModuleExceptionHandling: true,
368
      crossOriginLoading: config.crossOrigin,
369
      futureEmitAssets: !dev,
370
      webassemblyModuleFilename: 'static/wasm/[modulehash].wasm',
N
nkzawa 已提交
371
    },
372
    performance: false,
T
Tim Neutkens 已提交
373
    resolve: resolveConfig,
N
nkzawa 已提交
374
    resolveLoader: {
N
Naoyuki Kanezawa 已提交
375
      modules: [
376
        path.join(__dirname, 'webpack', 'loaders'), // The loaders Next.js provides
377
        'node_modules',
378 379
        ...nodePathList, // Support for NODE_PATH environment variable
      ],
N
nkzawa 已提交
380
    },
T
Tim Neutkens 已提交
381
    // @ts-ignore this is filtered
N
nkzawa 已提交
382
    module: {
383
      strictExportPresence: true,
384
      rules: [
385
        (selectivePageBuilding || config.experimental.terserLoader) &&
J
Joe Haddad 已提交
386
          !isServer && {
387 388 389 390 391 392
            test: /\.(js|mjs|jsx)$/,
            exclude: /\.min\.(js|mjs|jsx)$/,
            use: {
              loader: 'next-minify-loader',
              options: {
                terserOptions: {
393
                  ...terserOptions,
394 395 396 397 398 399 400 401 402 403 404
                  mangle: false,
                },
              },
            },
          },
        config.experimental.ampBindInitData &&
          !isServer && {
            test: /\.(tsx|ts|js|mjs|jsx)$/,
            include: [path.join(dir, 'data')],
            use: 'next-data-loader',
          },
T
Tim Neutkens 已提交
405
        {
406
          test: /\.(tsx|ts|js|mjs|jsx)$/,
407 408 409 410 411
          include: [
            dir,
            /next-server[\\/]dist[\\/]lib/,
            /next[\\/]dist[\\/]client/,
            /next[\\/]dist[\\/]pages/,
412
            /[\\/](strip-ansi|ansi-regex)[\\/]/,
413
          ],
414
          exclude: (path: string) => {
415 416 417
            if (
              /next-server[\\/]dist[\\/]lib/.test(path) ||
              /next[\\/]dist[\\/]client/.test(path) ||
418
              /next[\\/]dist[\\/]pages/.test(path) ||
419
              /[\\/](strip-ansi|ansi-regex)[\\/]/.test(path)
420
            ) {
421 422
              return false
            }
423

424
            return /node_modules/.test(path)
425
          },
426
          use: defaultLoaders.babel,
427
        },
428
      ].filter(Boolean),
N
nkzawa 已提交
429
    },
T
Tim Neutkens 已提交
430
    plugins: [
431 432
      // This plugin makes sure `output.filename` is used for entry chunks
      new ChunkNamesPlugin(),
433
      new webpack.DefinePlugin({
434
        ...Object.keys(config.env).reduce((acc, key) => {
435
          if (/^(?:NODE_.+)|(?:__.+)$/i.test(key)) {
436 437 438
            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`
            )
439 440 441
          }

          return {
442
            ...acc,
443
            [`process.env.${key}`]: JSON.stringify(config.env[key]),
444
          }
445
        }, {}),
446
        'process.env.NODE_ENV': JSON.stringify(webpackMode),
447
        'process.crossOrigin': JSON.stringify(config.crossOrigin),
448 449
        'process.browser': JSON.stringify(!isServer),
        // This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory
450 451 452 453 454 455
        ...(dev && !isServer
          ? {
              'process.env.__NEXT_DIST_DIR': JSON.stringify(distDir),
            }
          : {}),
        'process.env.__NEXT_EXPORT_TRAILING_SLASH': JSON.stringify(
456
          config.exportTrailingSlash
457
        ),
458
        ...(isServer
459 460 461 462 463 464 465 466 467 468 469 470
          ? {
              // 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'),
            }),
471
      }),
472 473 474 475
      !isServer &&
        new ReactLoadablePlugin({
          filename: REACT_LOADABLE_MANIFEST,
        }),
476
      !isServer && new DropClientPage(),
J
JJ Kasper 已提交
477 478 479 480 481
      new ChunkGraphPlugin(buildId, {
        dir,
        distDir,
        isServer,
      }),
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
      ...(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(),
            ]
497

498
            if (!isServer) {
499
              const AutoDllPlugin = importAutoDllPlugin({ distDir })
500 501 502 503 504 505 506 507 508
              devPlugins.push(
                new AutoDllPlugin({
                  filename: '[name]_[hash].js',
                  path: './static/development/dll',
                  context: dir,
                  entry: {
                    dll: ['react', 'react-dom'],
                  },
                  config: {
J
JJ Kasper 已提交
509
                    devtool,
510 511 512 513 514 515 516
                    mode: webpackMode,
                    resolve: resolveConfig,
                  },
                })
              )
              devPlugins.push(new webpack.HotModuleReplacementPlugin())
            }
517

518 519 520
            return devPlugins
          })()
        : []),
521
      !dev && new webpack.HashedModuleIdsPlugin(),
522 523
      // This must come after HashedModuleIdsPlugin (it sets any modules that
      // were missed by HashedModuleIdsPlugin)
524
      !dev && selectivePageBuilding && new AllModulesIdentifiedPlugin(dir),
525 526
      // This sets chunk ids to be hashed versions of their names to reduce
      // bundle churn
527
      !dev && selectivePageBuilding && new HashedChunkIdsPlugin(buildId),
J
Joe Haddad 已提交
528
      // On the client we want to share the same runtime cache
529
      !isServer && selectivePageBuilding && new SharedRuntimePlugin(),
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
      !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)
            )
          },
        }),
      target === 'serverless' &&
        (isServer || selectivePageBuilding) &&
        new ServerlessPlugin(buildId, { isServer }),
J
JJ Kasper 已提交
545
      isServer && new PagesManifestPlugin(target === 'serverless'),
546 547 548
      target !== 'serverless' &&
        isServer &&
        new NextJsSSRModuleCachePlugin({ outputPath }),
549 550
      isServer && new NextJsSsrImportPlugin(),
      !isServer && new BuildManifestPlugin(),
551 552 553 554 555 556 557
      config.experimental.profiling &&
        new webpack.debug.ProfilingPlugin({
          outputPath: path.join(
            distDir,
            `profile-events-${isServer ? 'server' : 'client'}.json`
          ),
        }),
558 559
      !isServer &&
        useTypeScript &&
560
        new ForkTsCheckerWebpackPlugin({
561
          typescript: typeScriptPath,
562
          async: dev,
563 564 565 566 567 568 569 570
          useTypescriptIncrementalApi: true,
          checkSyntacticErrors: true,
          tsconfig: tsConfigPath,
          reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*'],
          compilerOptions: { isolatedModules: true, noEmit: true },
          silent: true,
          formatter: 'codeframe',
        }),
571
    ].filter((Boolean as any) as ExcludesFalse),
572
  }
573

T
Tim Neutkens 已提交
574
  if (typeof config.webpack === 'function') {
575 576 577 578 579 580 581 582 583 584
    webpackConfig = config.webpack(webpackConfig, {
      dir,
      dev,
      isServer,
      buildId,
      config,
      defaultLoaders,
      totalPages,
      webpack,
    })
585 586 587

    // @ts-ignore: Property 'then' does not exist on type 'Configuration'
    if (typeof webpackConfig.then === 'function') {
588 589 590
      console.warn(
        '> Promise returned in next config. https://err.sh/zeit/next.js/promise-in-next-config.md'
      )
591
    }
592
  }
T
Tim Neutkens 已提交
593

594
  // check if using @zeit/next-typescript and show warning
595 596 597
  if (
    isServer &&
    webpackConfig.module &&
598 599 600 601
    Array.isArray(webpackConfig.module.rules)
  ) {
    let foundTsRule = false

602 603
    webpackConfig.module.rules = webpackConfig.module.rules.filter(
      (rule): boolean => {
604
        if (!(rule.test instanceof RegExp)) return true
605
        if ('noop.ts'.match(rule.test) && !'noop.js'.match(rule.test)) {
606 607 608 609 610
          // remove if it matches @zeit/next-typescript
          foundTsRule = rule.use === defaultLoaders.babel
          return !foundTsRule
        }
        return true
611 612
      }
    )
613 614

    if (foundTsRule) {
615 616 617
      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'
      )
618 619 620
    }
  }

621
  // Backwards compat for `main.js` entry key
622
  const originalEntry: any = webpackConfig.entry
623 624
  if (typeof originalEntry !== 'undefined') {
    webpackConfig.entry = async () => {
625 626 627 628
      const entry: WebpackEntrypoints =
        typeof originalEntry === 'function'
          ? await originalEntry()
          : originalEntry
629 630 631 632 633
      // 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'],
634
          originalFile,
635
        ]
636
      }
637
      delete entry['main.js']
638

639
      return entry
640 641 642
    }
  }

643
  if (!dev) {
644 645 646 647
    // @ts-ignore entry is always a function
    webpackConfig.entry = await webpackConfig.entry()
  }

T
Tim Neutkens 已提交
648
  return webpackConfig
N
nkzawa 已提交
649
}