Repository: nuxt/nuxt.js
Pull request #5054 (2019-02-18 10:32:14)

refactor: unify context in webpack module
<!--- Provide a general summary of your changes in the title above -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (a non-breaking change which fixes an issue)
- [ ] New feature (a non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)


## Description
<!--- Describe your changes in detail -->
<!--- Why is this change required? What problem does it solve? -->
<!--- If it resolves an open issue, please link to the issue here. For example "Resolves: #1337" -->


## Checklist:
<!--- Put an `x` in all the boxes that apply. -->
<!--- If your change requires a documentation PR, please link it appropriately -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly. (PR: #)
- [ ] I have added tests to cover my changes (if not applicable, please state why)
- [ ] All new and existing tests are passing.



codecov-io: # [Codecov](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=h1) Report
> Merging [#5054](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=desc) into [dev](https://codecov.io/gh/nuxt/nuxt.js/commit/b9391d7ea07f11cd5ed6eb318111e490b9af4f8c?src=pr&el=desc) will **increase** coverage by `0.09%`.
> The diff coverage is `96.93%`.

[![Impacted file tree graph](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/graphs/tree.svg?width=650&token=nGD1PtMB3M&height=150&src=pr)](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##              dev    #5054      +/-   ##
==========================================
+ Coverage   94.98%   95.08%   +0.09%     
==========================================
  Files          72       72              
  Lines        2414     2421       +7     
  Branches      615      614       -1     
==========================================
+ Hits         2293     2302       +9     
+ Misses        101       99       -2     
  Partials       20       20
```


| [Impacted Files](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/webpack/src/config/client.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvY29uZmlnL2NsaWVudC5qcw==) | `96.29% <100%> (+0.46%)` | :arrow_up: |
| [packages/webpack/src/config/server.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvY29uZmlnL3NlcnZlci5qcw==) | `100% <100%> (ø)` | :arrow_up: |
| [packages/webpack/src/utils/postcss.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvdXRpbHMvcG9zdGNzcy5qcw==) | `91.83% <100%> (-0.48%)` | :arrow_down: |
| [packages/webpack/src/builder.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvYnVpbGRlci5qcw==) | `94.68% <100%> (ø)` | :arrow_up: |
| [packages/webpack/src/config/modern.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvY29uZmlnL21vZGVybi5qcw==) | `100% <100%> (ø)` | :arrow_up: |
| [packages/builder/src/context/build.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvYnVpbGRlci9zcmMvY29udGV4dC9idWlsZC5qcw==) | `100% <100%> (ø)` | :arrow_up: |
| [packages/webpack/src/utils/perf-loader.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvdXRpbHMvcGVyZi1sb2FkZXIuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/webpack/src/utils/style-loader.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvdXRpbHMvc3R5bGUtbG9hZGVyLmpz) | `94.11% <100%> (-0.17%)` | :arrow_down: |
| [packages/webpack/src/config/base.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvY29uZmlnL2Jhc2UuanM=) | `95.45% <89.65%> (ø)` | :arrow_up: |
| [packages/vue-renderer/src/renderer.js](https://codecov.io/gh/nuxt/nuxt.js/pull/5054/diff?src=pr&el=tree#diff-cGFja2FnZXMvdnVlLXJlbmRlcmVyL3NyYy9yZW5kZXJlci5qcw==) | `95.85% <0%> (+1.03%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=footer). Last update [b9391d7...07f95f8](https://codecov.io/gh/nuxt/nuxt.js/pull/5054?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packages/builder/src/context/build.js b/packages/builder/src/context/build.js
index 3b60d116e..8dcd1b39a 100644
--- a/packages/builder/src/context/build.js
+++ b/packages/builder/src/context/build.js
@@ -6,6 +6,10 @@ export default class BuildContext {
     this.isStatic = false
   }
 
+  get buildOptions() {
+    return this.options.build
+  }
+
   get plugins() {
     return this._builder.plugins
   }
diff --git a/packages/webpack/src/builder.js b/packages/webpack/src/builder.js
index 125c567bc..9c0b9d44c 100644
--- a/packages/webpack/src/builder.js
+++ b/packages/webpack/src/builder.js
@@ -19,8 +19,8 @@ import PerfLoader from './utils/perf-loader'
 const glob = pify(Glob)
 
 export class WebpackBundler {
-  constructor(context) {
-    this.context = context
+  constructor(buildContext) {
+    this.buildContext = buildContext
     // Fields that set on build
     this.compilers = []
     this.compilersWatching = []
@@ -28,7 +28,7 @@ export class WebpackBundler {
     this.hotMiddleware = {}
 
     // Initialize shared MFS for dev
-    if (this.context.options.dev) {
+    if (this.buildContext.options.dev) {
       this.mfs = new MFS()
 
       // TODO: Enable when async FS required
@@ -38,7 +38,7 @@ export class WebpackBundler {
   }
 
   async build() {
-    const { options } = this.context
+    const { options } = this.buildContext
 
     const compilersOptions = []
 
@@ -60,7 +60,7 @@ export class WebpackBundler {
       compilersOptions.push(serverConfig)
     }
 
-    for (const p of this.context.plugins) {
+    for (const p of this.buildContext.plugins) {
       // Client config
       if (!clientConfig.resolve.alias[p.name]) {
         clientConfig.resolve.alias[p.name] = p.mode === 'server' ? './empty.js' : p.src
@@ -78,7 +78,7 @@ export class WebpackBundler {
     }
 
     // Check styleResource existence
-    const { styleResources } = this.context.options.build
+    const { styleResources } = this.buildContext.options.build
     if (styleResources && Object.keys(styleResources).length) {
       consola.warn(
         'Using styleResources without the nuxt-style-resources-module is not suggested and can lead to severe performance issues.',
@@ -86,7 +86,7 @@ export class WebpackBundler {
       )
       for (const ext of Object.keys(styleResources)) {
         await Promise.all(wrapArray(styleResources[ext]).map(async (p) => {
-          const styleResourceFiles = await glob(path.resolve(this.context.options.rootDir, p))
+          const styleResourceFiles = await glob(path.resolve(this.buildContext.options.rootDir, p))
 
           if (!styleResourceFiles || styleResourceFiles.length === 0) {
             throw new Error(`Style Resource not found: ${p}`)
@@ -122,7 +122,7 @@ export class WebpackBundler {
 
   async webpackCompile(compiler) {
     const { name } = compiler.options
-    const { nuxt, options } = this.context
+    const { nuxt, options } = this.buildContext
 
     await nuxt.callHook('build:compile', { name, compiler })
 
@@ -179,7 +179,7 @@ export class WebpackBundler {
     consola.debug('Adding webpack middleware...')
 
     const { name } = compiler.options
-    const { nuxt: { server }, options } = this.context
+    const { nuxt: { server }, options } = this.buildContext
     const { client, ...hotMiddlewareOptions } = options.build.hotMiddleware || {}
 
     // Create webpack dev middleware
@@ -255,6 +255,6 @@ export class WebpackBundler {
   }
 
   forGenerate() {
-    this.context.isStatic = true
+    this.buildContext.isStatic = true
   }
 }
diff --git a/packages/webpack/src/config/base.js b/packages/webpack/src/config/base.js
index 349a0e264..ee079da56 100644
--- a/packages/webpack/src/config/base.js
+++ b/packages/webpack/src/config/base.js
@@ -20,16 +20,9 @@ import WarnFixPlugin from '../plugins/warnfix'
 import { reservedVueTags } from '../utils/reserved-tags'
 
 export default class WebpackBaseConfig {
-  constructor(builder, options) {
-    this.name = options.name
-    this.isServer = options.isServer
-    this.isModern = options.isModern
+  constructor(builder) {
     this.builder = builder
-    this.nuxt = builder.context.nuxt
-    this.isStatic = builder.context.isStatic
-    this.options = builder.context.options
-    this.loaders = this.options.build.loaders
-    this.buildMode = this.options.dev ? 'development' : 'production'
+    this.buildContext = builder.buildContext
     this.modulesToTranspile = this.normalizeTranspile()
   }
 
@@ -43,17 +36,29 @@ export default class WebpackBaseConfig {
 
   get nuxtEnv() {
     return {
-      isDev: this.options.dev,
+      isDev: this.dev,
       isServer: this.isServer,
       isClient: !this.isServer,
       isModern: !!this.isModern
     }
   }
 
+  get mode() {
+    return this.dev ? 'development' : 'production'
+  }
+
+  get dev() {
+    return this.buildContext.options.dev
+  }
+
+  get loaders() {
+    return this.buildContext.buildOptions.loaders
+  }
+
   normalizeTranspile() {
     // include SFCs in node_modules
     const items = [/\.vue\.js/i]
-    for (const pattern of this.options.build.transpile) {
+    for (const pattern of this.buildContext.buildOptions.transpile) {
       if (pattern instanceof RegExp) {
         items.push(pattern)
       } else {
@@ -65,7 +70,7 @@ export default class WebpackBaseConfig {
   }
 
   getBabelOptions() {
-    const options = clone(this.options.build.babel)
+    const options = clone(this.buildContext.buildOptions.babel)
 
     if (typeof options.presets === 'function') {
       options.presets = options.presets({ isServer: this.isServer })
@@ -86,11 +91,11 @@ export default class WebpackBaseConfig {
   }
 
   getFileName(key) {
-    let fileName = this.options.build.filenames[key]
+    let fileName = this.buildContext.buildOptions.filenames[key]
     if (typeof fileName === 'function') {
       fileName = fileName(this.nuxtEnv)
     }
-    if (this.options.dev) {
+    if (this.dev) {
       const hash = /\[(chunkhash|contenthash|hash)(?::(\d+))?]/.exec(fileName)
       if (hash) {
         consola.warn(`Notice: Please do not use ${hash[1]} in dev mode to prevent memory leak`)
@@ -105,11 +110,11 @@ export default class WebpackBaseConfig {
 
   env() {
     const env = {
-      'process.env.NODE_ENV': JSON.stringify(this.buildMode),
-      'process.mode': JSON.stringify(this.options.mode),
-      'process.static': this.isStatic
+      'process.env.NODE_ENV': JSON.stringify(this.mode),
+      'process.mode': JSON.stringify(this.mode),
+      'process.static': this.buildContext.isStatic
     }
-    Object.entries(this.options.env).forEach(([key, value]) => {
+    Object.entries(this.buildContext.options.env).forEach(([key, value]) => {
       env['process.env.' + key] =
         ['boolean', 'number'].includes(typeof value)
           ? value
@@ -119,19 +124,21 @@ export default class WebpackBaseConfig {
   }
 
   output() {
+    const {
+      options: { buildDir, router },
+      buildOptions: { publicPath }
+    } = this.buildContext
     return {
-      path: path.resolve(this.options.buildDir, 'dist', this.isServer ? 'server' : 'client'),
+      path: path.resolve(buildDir, 'dist', this.isServer ? 'server' : 'client'),
       filename: this.getFileName('app'),
       futureEmitAssets: true, // TODO: Remove when using webpack 5
       chunkFilename: this.getFileName('chunk'),
-      publicPath: isUrl(this.options.build.publicPath)
-        ? this.options.build.publicPath
-        : urlJoin(this.options.router.base, this.options.build.publicPath)
+      publicPath: isUrl(publicPath) ? publicPath : urlJoin(router.base, publicPath)
     }
   }
 
   optimization() {
-    const optimization = cloneDeep(this.options.build.optimization)
+    const optimization = cloneDeep(this.buildContext.buildOptions.optimization)
 
     if (optimization.minimize && optimization.minimizer === undefined) {
       optimization.minimizer = this.minimizer()
@@ -142,13 +149,14 @@ export default class WebpackBaseConfig {
 
   minimizer() {
     const minimizer = []
+    const { terser, cache } = this.buildContext.buildOptions
 
     // https://github.com/webpack-contrib/terser-webpack-plugin
-    if (this.options.build.terser) {
+    if (terser) {
       minimizer.push(
         new TerserWebpackPlugin(Object.assign({
           parallel: true,
-          cache: this.options.build.cache,
+          cache,
           sourceMap: this.devtool && /source-?map/.test(this.devtool),
           extractComments: {
             filename: 'LICENSES'
@@ -164,7 +172,7 @@ export default class WebpackBaseConfig {
               reserved: reservedVueTags
             }
           }
-        }, this.options.build.terser))
+        }, terser))
       )
     }
 
@@ -172,7 +180,7 @@ export default class WebpackBaseConfig {
   }
 
   alias() {
-    const { srcDir, rootDir, dir: { assets: assetsDir, static: staticDir } } = this.options
+    const { srcDir, rootDir, dir: { assets: assetsDir, static: staticDir } } = this.buildContext.options
 
     return {
       '~': path.join(srcDir),
@@ -185,10 +193,9 @@ export default class WebpackBaseConfig {
   }
 
   rules() {
-    const perfLoader = new PerfLoader(this)
+    const perfLoader = new PerfLoader(this.name, this.buildContext)
     const styleLoader = new StyleLoader(
-      this.options,
-      this.nuxt,
+      this.buildContext,
       { isServer: this.isServer, perfLoader }
     )
     const babelLoader = {
@@ -329,25 +336,26 @@ export default class WebpackBaseConfig {
 
   plugins() {
     const plugins = []
+    const { nuxt, buildOptions } = this.buildContext
 
     // Add timefix-plugin before others plugins
-    if (this.options.dev) {
+    if (this.dev) {
       plugins.push(new TimeFixPlugin())
     }
 
     // CSS extraction)
-    if (this.options.build.extractCSS) {
+    if (buildOptions.extractCSS) {
       plugins.push(new ExtractCssChunksPlugin(Object.assign({
         filename: this.getFileName('css'),
         chunkFilename: this.getFileName('css'),
         // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
         reloadAll: true
-      }, this.options.build.extractCSS)))
+      }, buildOptions.extractCSS)))
     }
 
     plugins.push(new VueLoader.VueLoaderPlugin())
 
-    plugins.push(...(this.options.build.plugins || []))
+    plugins.push(...(buildOptions.plugins || []))
 
     // Hide warnings about plugins without a default export (#1179)
     plugins.push(new WarnFixPlugin())
@@ -362,37 +370,38 @@ export default class WebpackBaseConfig {
         'profile',
         'stats'
       ],
-      basic: !this.options.build.quiet && env.minimalCLI,
-      fancy: !this.options.build.quiet && !env.minimalCLI,
-      profile: !this.options.build.quiet && this.options.build.profile,
-      stats: !this.options.build.quiet && !this.options.dev && this.options.build.stats,
+      basic: !buildOptions.quiet && env.minimalCLI,
+      fancy: !buildOptions.quiet && !env.minimalCLI,
+      profile: !buildOptions.quiet && buildOptions.profile,
+      stats: !buildOptions.quiet && !this.dev && buildOptions.stats,
       reporter: {
         change: (_, { shortPath }) => {
           if (!this.isServer) {
-            this.nuxt.callHook('bundler:change', shortPath)
+            nuxt.callHook('bundler:change', shortPath)
           }
         },
-        done: (context) => {
-          if (context.hasErrors) {
-            this.nuxt.callHook('bundler:error')
+        done: (buildContext) => {
+          if (buildContext.hasErrors) {
+            nuxt.callHook('bundler:error')
           }
         },
         allDone: () => {
-          this.nuxt.callHook('bundler:done')
+          nuxt.callHook('bundler:done')
         }
       }
     }))
 
-    if (this.options.build.hardSource) {
-      plugins.push(new HardSourcePlugin(Object.assign({}, this.options.build.hardSource)))
+    if (buildOptions.hardSource) {
+      plugins.push(new HardSourcePlugin(Object.assign({}, buildOptions.hardSource)))
     }
 
     return plugins
   }
 
   extendConfig(config) {
-    if (typeof this.options.build.extend === 'function') {
-      const extendedConfig = this.options.build.extend.call(
+    const { extend } = this.buildContext.buildOptions
+    if (typeof extend === 'function') {
+      const extendedConfig = extend.call(
         this.builder, config, { loaders: this.loaders, ...this.nuxtEnv }
       )
       // Only overwrite config when something is returned for backwards compatibility
@@ -405,17 +414,17 @@ export default class WebpackBaseConfig {
 
   config() {
     // Prioritize nested node_modules in webpack search path (#2558)
-    const webpackModulesDir = ['node_modules'].concat(this.options.modulesDir)
+    const webpackModulesDir = ['node_modules'].concat(this.buildContext.options.modulesDir)
 
     const config = {
       name: this.name,
-      mode: this.buildMode,
+      mode: this.mode,
       devtool: this.devtool,
       optimization: this.optimization(),
       output: this.output(),
       performance: {
         maxEntrypointSize: 1000 * 1024,
-        hints: this.options.dev ? false : 'warning'
+        hints: this.dev ? false : 'warning'
       },
       resolve: {
         extensions: ['.wasm', '.mjs', '.js', '.json', '.vue', '.jsx', '.ts', '.tsx'],
diff --git a/packages/webpack/src/config/client.js b/packages/webpack/src/config/client.js
index 68589eb44..dc1c9078d 100644
--- a/packages/webpack/src/config/client.js
+++ b/packages/webpack/src/config/client.js
@@ -14,12 +14,15 @@ import VueSSRClientPlugin from '../plugins/vue/client'
 import WebpackBaseConfig from './base'
 
 export default class WebpackClientConfig extends WebpackBaseConfig {
-  constructor(builder, options) {
-    super(builder, options || { name: 'client', isServer: false })
+  constructor(builder) {
+    super(builder)
+    this.name = 'client'
+    this.isServer = false
+    this.isModern = false
   }
 
   getFileName(...args) {
-    if (this.options.build.analyze) {
+    if (this.buildContext.buildOptions.analyze) {
       const [key] = args
       if (['app', 'chunk'].includes(key)) {
         return `${this.isModern ? 'modern-' : ''}[name].js`
@@ -44,7 +47,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
     // Small, known and common modules which are usually used project-wise
     // Sum of them may not be more than 244 KiB
     if (
-      this.options.build.splitChunks.commons === true &&
+      this.buildContext.buildOptions.splitChunks.commons === true &&
       optimization.splitChunks.cacheGroups.commons === undefined
     ) {
       optimization.splitChunks.cacheGroups.commons = {
@@ -60,14 +63,13 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
 
   minimizer() {
     const minimizer = super.minimizer()
+    const { optimizeCSS } = this.buildContext.buildOptions
 
     // https://github.com/NMFR/optimize-css-assets-webpack-plugin
     // https://github.com/webpack-contrib/mini-css-extract-plugin#minimizing-for-production
     // TODO: Remove OptimizeCSSAssetsPlugin when upgrading to webpack 5
-    if (this.options.build.optimizeCSS) {
-      minimizer.push(
-        new OptimizeCSSAssetsPlugin(Object.assign({}, this.options.build.optimizeCSS))
-      )
+    if (optimizeCSS) {
+      minimizer.push(new OptimizeCSSAssetsPlugin(Object.assign({}, optimizeCSS)))
     }
 
     return minimizer
@@ -75,14 +77,15 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
 
   plugins() {
     const plugins = super.plugins()
+    const { buildOptions, options: { appTemplatePath, buildDir, rootDir, modern } } = this.buildContext
 
     // Generate output HTML for SSR
-    if (this.options.build.ssr) {
+    if (buildOptions.ssr) {
       plugins.push(
         new HTMLPlugin({
           filename: '../server/index.ssr.html',
-          template: this.options.appTemplatePath,
-          minify: this.options.build.html.minify,
+          template: appTemplatePath,
+          minify: buildOptions.html.minify,
           inject: false // Resources will be injected using bundleRenderer
         })
       )
@@ -91,8 +94,8 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
     plugins.push(
       new HTMLPlugin({
         filename: '../server/index.spa.html',
-        template: this.options.appTemplatePath,
-        minify: this.options.build.html.minify,
+        template: appTemplatePath,
+        minify: buildOptions.html.minify,
         inject: true,
         chunksSortMode: 'dependency'
       }),
@@ -102,53 +105,53 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
       new webpack.DefinePlugin(this.env())
     )
 
-    if (this.options.dev) {
+    if (this.dev) {
       // TODO: webpackHotUpdate is not defined: https://github.com/webpack/webpack/issues/6693
       plugins.push(new webpack.HotModuleReplacementPlugin())
     }
 
     // Webpack Bundle Analyzer
     // https://github.com/webpack-contrib/webpack-bundle-analyzer
-    if (!this.options.dev && this.options.build.analyze) {
-      const statsDir = path.resolve(this.options.buildDir, 'stats')
+    if (!this.dev && buildOptions.analyze) {
+      const statsDir = path.resolve(buildDir, 'stats')
 
       plugins.push(new BundleAnalyzer.BundleAnalyzerPlugin(Object.assign({
         analyzerMode: 'static',
         defaultSizes: 'gzip',
         generateStatsFile: true,
-        openAnalyzer: !this.options.build.quiet,
+        openAnalyzer: !buildOptions.quiet,
         reportFilename: path.resolve(statsDir, `${this.name}.html`),
         statsFilename: path.resolve(statsDir, `${this.name}.json`)
-      }, this.options.build.analyze)))
+      }, buildOptions.analyze)))
     }
 
-    if (this.options.modern) {
+    if (modern) {
       plugins.push(new ModernModePlugin({
-        targetDir: path.resolve(this.options.buildDir, 'dist', 'client'),
+        targetDir: path.resolve(buildDir, 'dist', 'client'),
         isModernBuild: this.isModern
       }))
     }
 
-    if (this.options.build.crossorigin) {
+    if (buildOptions.crossorigin) {
       plugins.push(new CorsPlugin({
-        crossorigin: this.options.build.crossorigin
+        crossorigin: buildOptions.crossorigin
       }))
     }
 
     // TypeScript type checker
     // Only performs once per client compilation and only if `ts-loader` checker is not used (transpileOnly: true)
-    if (!this.isModern && this.loaders.ts.transpileOnly && this.options.build.useForkTsChecker) {
-      const forkTsCheckerResolvedPath = this.nuxt.resolver.resolveModule('fork-ts-checker-webpack-plugin')
+    if (!this.isModern && this.loaders.ts.transpileOnly && buildOptions.useForkTsChecker) {
+      const forkTsCheckerResolvedPath = this.buildContext.nuxt.resolver.resolveModule('fork-ts-checker-webpack-plugin')
       if (forkTsCheckerResolvedPath) {
         const ForkTsCheckerWebpackPlugin = require(forkTsCheckerResolvedPath)
         plugins.push(new ForkTsCheckerWebpackPlugin(Object.assign({
           vue: true,
-          tsconfig: path.resolve(this.options.rootDir, 'tsconfig.json'),
+          tsconfig: path.resolve(rootDir, 'tsconfig.json'),
           // https://github.com/Realytics/fork-ts-checker-webpack-plugin#options - tslint: boolean | string - So we set it false if file not found
-          tslint: (tslintPath => fs.existsSync(tslintPath) && tslintPath)(path.resolve(this.options.rootDir, 'tslint.json')),
+          tslint: (tslintPath => fs.existsSync(tslintPath) && tslintPath)(path.resolve(rootDir, 'tslint.json')),
           formatter: 'codeframe',
           logger: consola
-        }, this.options.build.useForkTsChecker)))
+        }, buildOptions.useForkTsChecker)))
       } else {
         consola.warn('You need to install `fork-ts-checker-webpack-plugin` as devDependency to enable TypeScript type checking !')
       }
@@ -159,8 +162,12 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
 
   config() {
     const config = super.config()
+    const {
+      options: { router, buildDir },
+      buildOptions: { hotMiddleware, quiet, friendlyErrors }
+    } = this.buildContext
 
-    const { client = {} } = this.options.build.hotMiddleware || {}
+    const { client = {} } = hotMiddleware || {}
     const { ansiColors, overlayStyles, ...options } = client
     const hotMiddlewareClientOptions = {
       reload: true,
@@ -170,17 +177,17 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
       ...options,
       name: this.name
     }
-    const clientPath = `${this.options.router.base}/__webpack_hmr/${this.name}`
+    const clientPath = `${router.base}/__webpack_hmr/${this.name}`
     const hotMiddlewareClientOptionsStr =
       `${querystring.stringify(hotMiddlewareClientOptions)}&path=${clientPath}`.replace(/\/\//g, '/')
 
     // Entry points
     config.entry = {
-      app: [path.resolve(this.options.buildDir, 'client.js')]
+      app: [path.resolve(buildDir, 'client.js')]
     }
 
     // Add HMR support
-    if (this.options.dev) {
+    if (this.dev) {
       config.entry.app.unshift(
         // https://github.com/webpack-contrib/webpack-hot-middleware/issues/53#issuecomment-162823945
         'eventsource-polyfill',
@@ -190,7 +197,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
     }
 
     // Add friendly error plugin
-    if (this.options.dev && !this.options.build.quiet && this.options.build.friendlyErrors) {
+    if (this.dev && !quiet && friendlyErrors) {
       config.plugins.push(
         new FriendlyErrorsWebpackPlugin({
           clearConsole: false,
diff --git a/packages/webpack/src/config/modern.js b/packages/webpack/src/config/modern.js
index 8431de582..27512b2a4 100644
--- a/packages/webpack/src/config/modern.js
+++ b/packages/webpack/src/config/modern.js
@@ -2,8 +2,10 @@ import clone from 'lodash/clone'
 import WebpackClientConfig from './client'
 
 export default class WebpackModernConfig extends WebpackClientConfig {
-  constructor(builder) {
-    super(builder, { name: 'modern', isServer: false, isModern: true })
+  constructor(...args) {
+    super(...args)
+    this.name = 'modern'
+    this.isModern = true
   }
 
   env() {
@@ -13,7 +15,7 @@ export default class WebpackModernConfig extends WebpackClientConfig {
   }
 
   getBabelOptions() {
-    const options = clone(this.options.build.babel)
+    const options = clone(this.buildContext.buildOptions.babel)
 
     options.presets = [
       [
diff --git a/packages/webpack/src/config/server.js b/packages/webpack/src/config/server.js
index f2e34f104..548267834 100644
--- a/packages/webpack/src/config/server.js
+++ b/packages/webpack/src/config/server.js
@@ -9,8 +9,10 @@ import VueSSRServerPlugin from '../plugins/vue/server'
 import WebpackBaseConfig from './base'
 
 export default class WebpackServerConfig extends WebpackBaseConfig {
-  constructor(builder) {
-    super(builder, { name: 'server', isServer: true })
+  constructor(...args) {
+    super(...args)
+    this.name = 'server'
+    this.isServer = true
     this.whitelist = this.normalizeWhitelist()
   }
 
@@ -18,7 +20,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
     const whitelist = [
       /\.(?!js(x|on)?$)/i
     ]
-    for (const pattern of this.options.build.transpile) {
+    for (const pattern of this.buildContext.buildOptions.transpile) {
       if (pattern instanceof RegExp) {
         whitelist.push(pattern)
       } else {
@@ -68,7 +70,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
       target: 'node',
       node: false,
       entry: {
-        app: [path.resolve(this.options.buildDir, 'server.js')]
+        app: [path.resolve(this.buildContext.options.buildDir, 'server.js')]
       },
       output: Object.assign({}, config.output, {
         filename: 'server.js',
@@ -85,8 +87,8 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
     // https://webpack.js.org/configuration/externals/#externals
     // https://github.com/liady/webpack-node-externals
     // https://vue-loader.vuejs.org/migrating.html#ssr-externals
-    if (!this.options.build.standalone) {
-      this.options.modulesDir.forEach((dir) => {
+    if (!this.buildContext.buildOptions.standalone) {
+      this.buildContext.options.modulesDir.forEach((dir) => {
         if (fs.existsSync(dir)) {
           config.externals.push(
             nodeExternals({
diff --git a/packages/webpack/src/utils/perf-loader.js b/packages/webpack/src/utils/perf-loader.js
index 7fff23f8c..490d82929 100644
--- a/packages/webpack/src/utils/perf-loader.js
+++ b/packages/webpack/src/utils/perf-loader.js
@@ -6,10 +6,10 @@ import { warmup } from 'thread-loader'
 // https://github.com/webpack-contrib/cache-loader
 
 export default class PerfLoader {
-  constructor(config) {
-    this.name = config.name
-    this.options = config.options
-    this.workerPools = PerfLoader.defaultPools(this.options)
+  constructor(name, buildContext) {
+    this.name = name
+    this.buildContext = buildContext
+    this.workerPools = PerfLoader.defaultPools({ dev: buildContext.options.dev })
     return new Proxy(this, {
       get(target, name) {
         return target[name] ? target[name] : target.use.bind(target, name)
@@ -25,13 +25,13 @@ export default class PerfLoader {
     }
   }
 
-  static warmupAll(options) {
-    options = PerfLoader.defaultPools(options)
-    PerfLoader.warmup(options.js, [
+  static warmupAll({ dev }) {
+    const pools = PerfLoader.defaultPools({ dev })
+    PerfLoader.warmup(pools.js, [
       require.resolve('babel-loader'),
       require.resolve('@babel/preset-env')
     ])
-    PerfLoader.warmup(options.css, ['css-loader'])
+    PerfLoader.warmup(pools.css, ['css-loader'])
   }
 
   static warmup(...args) {
@@ -41,7 +41,7 @@ export default class PerfLoader {
   use(poolName) {
     const loaders = []
 
-    if (this.options.build.cache) {
+    if (this.buildContext.buildOptions.cache) {
       loaders.push({
         loader: 'cache-loader',
         options: {
@@ -50,7 +50,7 @@ export default class PerfLoader {
       })
     }
 
-    if (this.options.build.parallel) {
+    if (this.buildContext.buildOptions.parallel) {
       const pool = this.workerPools[poolName]
       if (pool) {
         loaders.push({
diff --git a/packages/webpack/src/utils/postcss.js b/packages/webpack/src/utils/postcss.js
index 0e82a78c2..0f3246948 100644
--- a/packages/webpack/src/utils/postcss.js
+++ b/packages/webpack/src/utils/postcss.js
@@ -19,34 +19,29 @@ export const orderPresets = {
 }
 
 export default class PostcssConfig {
-  constructor(options, nuxt) {
-    this.nuxt = nuxt
-    this.dev = options.dev
-    this.postcss = options.build.postcss
-    this.srcDir = options.srcDir
-    this.rootDir = options.rootDir
-    this.cssSourceMap = options.build.cssSourceMap
-    this.modulesDir = options.modulesDir
+  constructor(buildContext) {
+    this.buildContext = buildContext
+  }
+
+  get postcssOptions() {
+    return this.buildContext.buildOptions.postcss
   }
 
   get defaultConfig() {
+    const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options
     return {
-      sourceMap: this.cssSourceMap,
+      sourceMap: this.buildContext.buildOptions.cssSourceMap,
       plugins: {
         // https://github.com/postcss/postcss-import
         'postcss-import': {
           resolve: createResolver({
             alias: {
-              '~': path.join(this.srcDir),
-              '~~': path.join(this.rootDir),
-              '@': path.join(this.srcDir),
-              '@@': path.join(this.rootDir)
+              '~': path.join(srcDir),
+              '~~': path.join(rootDir),
+              '@': path.join(srcDir),
+              '@@': path.join(rootDir)
             },
-            modules: [
-              this.srcDir,
-              this.rootDir,
-              ...this.modulesDir
-            ]
+            modules: [ srcDir, rootDir, ...modulesDir ]
           })
         },
 
@@ -55,7 +50,7 @@ export default class PostcssConfig {
 
         // https://github.com/csstools/postcss-preset-env
         'postcss-preset-env': this.preset || {},
-        'cssnano': this.dev ? false : { preset: 'default' }
+        'cssnano': dev ? false : { preset: 'default' }
       },
       // Array, String or Function
       order: 'cssnanoLast'
@@ -65,7 +60,8 @@ export default class PostcssConfig {
   searchConfigFile() {
     // Search for postCSS config file and use it if exists
     // https://github.com/michael-ciniawsky/postcss-load-config
-    for (const dir of [this.srcDir, this.rootDir]) {
+    const { srcDir, rootDir } = this.buildContext.options
+    for (const dir of [ srcDir, rootDir ]) {
       for (const file of [
         'postcss.config.js',
         '.postcssrc.js',
@@ -82,12 +78,12 @@ export default class PostcssConfig {
   }
 
   configFromFile() {
-    const loaderConfig = (this.postcss && this.postcss.config) || {}
+    const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {}
     loaderConfig.path = loaderConfig.path || this.searchConfigFile()
 
     if (loaderConfig.path) {
       return {
-        sourceMap: this.cssSourceMap,
+        sourceMap: this.buildContext.buildOptions.cssSourceMap,
         config: loaderConfig
       }
     }
@@ -117,7 +113,7 @@ export default class PostcssConfig {
       // Map postcss plugins into instances on object mode once
       config.plugins = this.sortPlugins(config)
         .map((p) => {
-          const plugin = this.nuxt.resolver.requireModule(p)
+          const plugin = this.buildContext.nuxt.resolver.requireModule(p)
           const opts = plugins[p]
           if (opts === false) {
             return // Disabled
@@ -130,7 +126,7 @@ export default class PostcssConfig {
 
   config() {
     /* istanbul ignore if */
-    if (!this.postcss) {
+    if (!this.postcssOptions) {
       return false
     }
 
@@ -139,7 +135,7 @@ export default class PostcssConfig {
       return config
     }
 
-    config = this.normalize(cloneDeep(this.postcss))
+    config = this.normalize(cloneDeep(this.postcssOptions))
 
     // Apply default plugins
     if (isPureObject(config)) {
diff --git a/packages/webpack/src/utils/style-loader.js b/packages/webpack/src/utils/style-loader.js
index be247b99a..b6fb589f1 100644
--- a/packages/webpack/src/utils/style-loader.js
+++ b/packages/webpack/src/utils/style-loader.js
@@ -6,24 +6,20 @@ import { wrapArray } from '@nuxt/utils'
 import PostcssConfig from './postcss'
 
 export default class StyleLoader {
-  constructor(options, nuxt, { isServer, perfLoader }) {
+  constructor(buildContext, { isServer, perfLoader }) {
+    this.buildContext = buildContext
     this.isServer = isServer
     this.perfLoader = perfLoader
-    this.rootDir = options.rootDir
-    this.loaders = {
-      vueStyle: options.build.loaders.vueStyle,
-      css: options.build.loaders.css,
-      cssModules: options.build.loaders.cssModules
-    }
-    this.extractCSS = options.build.extractCSS
-    this.resources = options.build.styleResources
-    this.sourceMap = Boolean(options.build.cssSourceMap)
 
-    if (options.build.postcss) {
-      this.postcssConfig = new PostcssConfig(options, nuxt)
+    if (buildContext.options.build.postcss) {
+      this.postcssConfig = new PostcssConfig(buildContext)
     }
   }
 
+  get extractCSS() {
+    return this.buildContext.buildOptions.extractCSS
+  }
+
   get exportOnlyLocals() {
     return Boolean(this.isServer && this.extractCSS)
   }
@@ -34,19 +30,20 @@ export default class StyleLoader {
   }
 
   styleResource(ext) {
-    const extResource = this.resources[ext]
+    const { buildOptions: { styleResources }, options: { rootDir } } = this.buildContext
+    const extResource = styleResources[ext]
     // style-resources-loader
     // https://github.com/yenshih/style-resources-loader
     if (!extResource) {
       return
     }
-    const patterns = wrapArray(extResource).map(p => path.resolve(this.rootDir, p))
+    const patterns = wrapArray(extResource).map(p => path.resolve(rootDir, p))
 
     return {
       loader: 'style-resources-loader',
       options: Object.assign(
         { patterns },
-        this.resources.options || {}
+        styleResources.options || {}
       )
     }
   }
@@ -66,7 +63,7 @@ export default class StyleLoader {
 
     return {
       loader: 'postcss-loader',
-      options: Object.assign({ sourceMap: this.sourceMap }, config)
+      options: Object.assign({ sourceMap: this.buildContext.buildOptions.cssSourceMap }, config)
     }
   }
 
@@ -94,32 +91,34 @@ export default class StyleLoader {
   styleLoader() {
     return this.extract() || {
       loader: 'vue-style-loader',
-      options: this.loaders.vueStyle
+      options: this.buildContext.buildOptions.loaders.vueStyle
     }
   }
 
   apply(ext, loaders = []) {
+    const { css, cssModules } = this.buildContext.buildOptions.loaders
+
     const customLoaders = [].concat(
       this.postcss(),
       this.normalize(loaders),
       this.styleResource(ext)
     ).filter(Boolean)
 
-    this.loaders.css.importLoaders = this.loaders.cssModules.importLoaders = customLoaders.length
+    css.importLoaders = cssModules.importLoaders = customLoaders.length
 
     return [
       // This matches <style module>
       {
         resourceQuery: /module/,
         use: this.perfLoader.css().concat(
-          this.cssModules(this.loaders.cssModules),
+          this.cssModules(cssModules),
           customLoaders
         )
       },
       // This matches plain <style> or <style scoped>
       {
         use: this.perfLoader.css().concat(
-          this.css(this.loaders.css),
+          this.css(css),
           customLoaders
         )
       }


Test patch:
diff --git a/packages/builder/test/context/build.test.js b/packages/builder/test/context/build.test.js
index 693d96ed2..a067add15 100644
--- a/packages/builder/test/context/build.test.js
+++ b/packages/builder/test/context/build.test.js
@@ -20,4 +20,14 @@ describe('builder: buildContext', () => {
     const context = new BuildContext(builder)
     expect(context.plugins).toEqual(builder.plugins)
   })
+
+  test('should return builder build options', () => {
+    const buildOptions = { id: 'test-build-options' }
+    const builder = {
+      plugins: [],
+      nuxt: { options: { build: buildOptions } }
+    }
+    const context = new BuildContext(builder)
+    expect(context.buildOptions).toEqual(buildOptions)
+  })
 })
diff --git a/test/unit/basic.generate.test.js b/test/unit/basic.generate.test.js
index 371165dd1..d391efe5e 100644
--- a/test/unit/basic.generate.test.js
+++ b/test/unit/basic.generate.test.js
@@ -46,7 +46,7 @@ describe('basic generate', () => {
   })
 
   test('Check builder', () => {
-    expect(builder.bundleBuilder.context.isStatic).toBe(true)
+    expect(builder.bundleBuilder.buildContext.isStatic).toBe(true)
     expect(builder.build).toHaveBeenCalledTimes(1)
   })
 
diff --git a/test/unit/wp.config.test.js b/test/unit/wp.config.test.js
index 500ec8528..04af2f43d 100644
--- a/test/unit/wp.config.test.js
+++ b/test/unit/wp.config.test.js
@@ -15,16 +15,18 @@ describe('webpack configuration', () => {
     ])
     expect(PerfLoader.warmup).toHaveBeenCalledWith(css, ['css-loader'])
 
-    const perfLoader = new PerfLoader({
-      name: 'test-perf',
-      options: {
-        dev: true,
-        build: {
+    const perfLoader = new PerfLoader(
+      'test-perf',
+      {
+        options: {
+          dev: true
+        },
+        buildOptions: {
           parallel: true,
           cache: true
         }
       }
-    })
+    )
     expect(perfLoader.workerPools).toMatchObject({ js, css })
     const loaders = perfLoader.use('js')
     const cacheDirectory = path.resolve('node_modules/.cache/cache-loader/test-perf')