diff --git a/packages/builder/src/context/build.js b/packages/builder/src/context/build.js index 3b60d116e4..8dcd1b39ad 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/builder/test/context/build.test.js b/packages/builder/test/context/build.test.js index 693d96ed23..a067add156 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/packages/webpack/src/builder.js b/packages/webpack/src/builder.js index 125c567bce..9c0b9d44cb 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 349a0e2648..ee079da564 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 68589eb44a..dc1c9078dc 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 8431de5820..27512b2a49 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 f2e34f1045..548267834b 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 7fff23f8c4..490d829297 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 0e82a78c25..0f32469485 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 be247b99ad..b6fb589f1c 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