From b93bf9ad8f9094b9202908a825b825a66730f7c5 Mon Sep 17 00:00:00 2001 From: Clark Du Date: Wed, 21 Mar 2018 18:16:03 +0800 Subject: [PATCH 1/2] feat: migrate to mini-css-extract-plugin --- lib/builder/webpack/base.config.mjs | 12 ++------ lib/builder/webpack/style-loader.js | 46 +++++++++++------------------ lib/builder/webpack/vue-loader.mjs | 1 - lib/common/nuxt.config.js | 4 ++- package.json | 2 +- yarn.lock | 18 +++++------ 6 files changed, 32 insertions(+), 51 deletions(-) diff --git a/lib/builder/webpack/base.config.mjs b/lib/builder/webpack/base.config.mjs index 52e827390e..15ec451d30 100644 --- a/lib/builder/webpack/base.config.mjs +++ b/lib/builder/webpack/base.config.mjs @@ -1,6 +1,6 @@ import path from 'path' -import ExtractTextPlugin from 'extract-text-webpack-plugin' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' import FriendlyErrorsWebpackPlugin from '@nuxtjs/friendly-errors-webpack-plugin' import TimeFixPlugin from 'time-fix-plugin' import webpack from 'webpack' @@ -164,17 +164,9 @@ export default function webpackBaseConfig({ name, isServer }) { // CSS extraction const extractCSS = this.options.build.extractCSS - // TODO: Temporary disabled in dev mode for fixing source maps - // (We need `source-map` devtool for *.css modules) if (extractCSS && !this.options.dev) { - config.plugins.push(new ExtractTextPlugin(Object.assign({ + config.plugins.push(new MiniCssExtractPlugin(Object.assign({ filename: this.getFileName('css') - - // When using optimization.splitChunks and there are - // extracted chunks in the commons chunk, - // allChunks *must* be set to true - // TODO: For nuxt this makes duplicate css assets! - // allChunks: true }, typeof extractCSS === 'object' ? extractCSS : {}))) } diff --git a/lib/builder/webpack/style-loader.js b/lib/builder/webpack/style-loader.js index 2bbfc771ed..4d78c1378f 100644 --- a/lib/builder/webpack/style-loader.js +++ b/lib/builder/webpack/style-loader.js @@ -1,6 +1,6 @@ import path from 'path' -import ExtractTextPlugin from 'extract-text-webpack-plugin' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' import postcssConfig from './postcss' @@ -15,13 +15,6 @@ export default function styleLoader(ext, loaders = [], isVueLoader = false) { ) ) - // Prepare vue-style-loader - // https://github.com/vuejs/vue-style-loader - const vueStyleLoader = { - loader: 'vue-style-loader', - options: { sourceMap } - } - // -- Configure additional loaders -- // style-resources-loader @@ -73,26 +66,23 @@ export default function styleLoader(ext, loaders = [], isVueLoader = false) { }) // -- With extractCSS -- - // TODO: Temporary disabled in dev mode for fixing source maps - // (We need `source-map` devtool for *.css modules) - if (this.options.build.extractCSS && !this.options.dev) { - // ExtractTextPlugin - // https://github.com/webpack-contrib/extract-text-webpack-plugin - const extractLoader = ExtractTextPlugin.extract({ - use: loaders, - fallback: vueStyleLoader - }) - - // css-hot-loader - // https://github.com/shepherdwind/css-hot-loader - const hotLoader = { - loader: 'css-hot-loader', - options: { sourceMap } + if (this.options.build.extractCSS) { + loaders.unshift(MiniCssExtractPlugin.loader) + if (this.options.dev) { + // css-hot-loader + // https://github.com/shepherdwind/css-hot-loader + loaders.unshift({ + loader: 'css-hot-loader', + options: { sourceMap } + }) } - - return this.options.dev ? [hotLoader].concat(extractLoader) : extractLoader + } else { + // Prepare vue-style-loader + // https://github.com/vuejs/vue-style-loader + loaders.unshift({ + loader: 'vue-style-loader', + options: { sourceMap } + }) } - - // -- Without extractCSS -- - return [vueStyleLoader].concat(loaders) + return loaders } diff --git a/lib/builder/webpack/vue-loader.mjs b/lib/builder/webpack/vue-loader.mjs index f426c7df17..de9a864bd8 100644 --- a/lib/builder/webpack/vue-loader.mjs +++ b/lib/builder/webpack/vue-loader.mjs @@ -5,7 +5,6 @@ export default function vueLoader({ isServer }) { // https://vue-loader.vuejs.org/en const config = { postcss: postcssConfig.call(this), - extractCSS: !!this.options.build.extractCSS, cssSourceMap: this.options.build.cssSourceMap, preserveWhitespace: false, loaders: { diff --git a/lib/common/nuxt.config.js b/lib/common/nuxt.config.js index d0643d0e45..65c935c4f0 100644 --- a/lib/common/nuxt.config.js +++ b/lib/common/nuxt.config.js @@ -37,7 +37,9 @@ export default { filenames: { app: '[name].[chunkhash].js', chunk: '[name].[chunkhash].js', - css: '[name].[contenthash].css' + // TODO: Use [name].[contenthash].css when webpack core supports [contenthash] + // https://github.com/webpack-contrib/mini-css-extract-plugin/pull/30#issuecomment-374700690 + css: '[name].[chunkhash].css' }, styleResources: {}, plugins: [], diff --git a/package.json b/package.json index d86117cafe..a9b77269f8 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "es6-promise": "^4.2.4", "esm": "^3.0.6", "etag": "^1.8.1", - "extract-text-webpack-plugin": "^4.0.0-beta.0", "file-loader": "^1.1.11", "fresh": "^0.5.2", "fs-extra": "^5.0.0", @@ -86,6 +85,7 @@ "lodash": "^4.17.5", "lru-cache": "^4.1.2", "memory-fs": "^0.4.1", + "mini-css-extract-plugin": "^0.2.0", "minimist": "^1.2.0", "opencollective": "^1.0.3", "ora": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index c85241ceef..ad82cd60ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -385,7 +385,7 @@ async@^1.4.0: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.1.4, async@^2.4.1: +async@^2.1.4: version "2.6.0" resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" dependencies: @@ -2686,15 +2686,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-text-webpack-plugin@^4.0.0-beta.0: - version "4.0.0-beta.0" - resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-4.0.0-beta.0.tgz#f7361d7ff430b42961f8d1321ba8c1757b5d4c42" - dependencies: - async "^2.4.1" - loader-utils "^1.1.0" - schema-utils "^0.4.5" - webpack-sources "^1.1.0" - extract-zip@^1.6.5: version "1.6.6" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" @@ -4619,6 +4610,13 @@ mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" +mini-css-extract-plugin@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.2.0.tgz#7a16b0e1096c86de8e4d1c3b063aa1aeae88d41d" + dependencies: + loader-utils "^1.1.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" From 4ec9317ad44cdfce23b9de80b308b3368f61199f Mon Sep 17 00:00:00 2001 From: Clark Du Date: Wed, 21 Mar 2018 19:34:16 +0800 Subject: [PATCH 2/2] refactor: move mini-css-extract-plugin to client --- lib/builder/webpack/base.config.mjs | 12 +-- lib/builder/webpack/client.config.mjs | 9 ++ lib/builder/webpack/style-loader.js | 132 +++++++++++++------------- lib/builder/webpack/vue-loader.mjs | 19 ++-- 4 files changed, 89 insertions(+), 83 deletions(-) diff --git a/lib/builder/webpack/base.config.mjs b/lib/builder/webpack/base.config.mjs index 15ec451d30..4d4d6c9ad5 100644 --- a/lib/builder/webpack/base.config.mjs +++ b/lib/builder/webpack/base.config.mjs @@ -1,6 +1,5 @@ import path from 'path' -import MiniCssExtractPlugin from 'mini-css-extract-plugin' import FriendlyErrorsWebpackPlugin from '@nuxtjs/friendly-errors-webpack-plugin' import TimeFixPlugin from 'time-fix-plugin' import webpack from 'webpack' @@ -11,7 +10,7 @@ import { isUrl, urlJoin } from '../../common/utils' import WarnFixPlugin from './plugins/warnfix' import ProgressPlugin from './plugins/progress' import vueLoader from './vue-loader' -import styleLoader from './style-loader' +import styleLoaderWrapper from './style-loader' /* |-------------------------------------------------------------------------- @@ -26,6 +25,7 @@ export default function webpackBaseConfig({ name, isServer }) { const webpackModulesDir = ['node_modules'].concat(this.options.modulesDir) const configAlias = {} + const styleLoader = styleLoaderWrapper({ isServer }) // Used by vue-loader so we can use in templates // with @@ -162,14 +162,6 @@ export default function webpackBaseConfig({ name, isServer }) { }) ) - // CSS extraction - const extractCSS = this.options.build.extractCSS - if (extractCSS && !this.options.dev) { - config.plugins.push(new MiniCssExtractPlugin(Object.assign({ - filename: this.getFileName('css') - }, typeof extractCSS === 'object' ? extractCSS : {}))) - } - // Clone deep avoid leaking config between Client and Server return _.cloneDeep(config) } diff --git a/lib/builder/webpack/client.config.mjs b/lib/builder/webpack/client.config.mjs index a94f22c9dc..cda2382016 100644 --- a/lib/builder/webpack/client.config.mjs +++ b/lib/builder/webpack/client.config.mjs @@ -6,6 +6,7 @@ import webpack from 'webpack' import HTMLPlugin from 'html-webpack-plugin' import StylishPlugin from 'webpack-stylish' import BundleAnalyzer from 'webpack-bundle-analyzer' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' import HtmlWebpackInlineSourcePlugin from 'html-webpack-inline-source-plugin' import Debug from 'debug' @@ -199,5 +200,13 @@ export default function webpackClientConfig() { } } + // CSS extraction + const extractCSS = this.options.build.extractCSS + if (extractCSS) { + config.plugins.push(new MiniCssExtractPlugin(Object.assign({ + filename: this.getFileName('css') + }, typeof extractCSS === 'object' ? extractCSS : {}))) + } + return config } diff --git a/lib/builder/webpack/style-loader.js b/lib/builder/webpack/style-loader.js index 4d78c1378f..023acc06d5 100644 --- a/lib/builder/webpack/style-loader.js +++ b/lib/builder/webpack/style-loader.js @@ -4,85 +4,87 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin' import postcssConfig from './postcss' -export default function styleLoader(ext, loaders = [], isVueLoader = false) { - const sourceMap = Boolean(this.options.build.cssSourceMap) +export default ({isVueLoader = false, isServer}) => { + return function styleLoader(ext, loaders = []) { + const sourceMap = Boolean(this.options.build.cssSourceMap) - // Normalize loaders - loaders = (Array.isArray(loaders) ? loaders : [loaders]).map(loader => - Object.assign( - { options: { sourceMap } }, - typeof loader === 'string' ? { loader } : loader - ) - ) - - // -- Configure additional loaders -- - - // style-resources-loader - // https://github.com/yenshih/style-resources-loader - if (this.options.build.styleResources[ext]) { - const patterns = Array.isArray(this.options.build.styleResources[ext]) - ? this.options.build.styleResources[ext] - : [this.options.build.styleResources[ext]] - const options = Object.assign( - {}, - this.options.build.styleResources.options || {}, - { patterns } + // Normalize loaders + loaders = (Array.isArray(loaders) ? loaders : [loaders]).map(loader => + Object.assign( + { options: { sourceMap } }, + typeof loader === 'string' ? { loader } : loader + ) ) - loaders.push({ - loader: 'style-resources-loader', - options - }) - } + // -- Configure additional loaders -- - // postcss-loader - // vue-loader already provides it's own - // https://github.com/postcss/postcss-loader - if (!isVueLoader) { - const _postcssConfig = postcssConfig.call(this) + // style-resources-loader + // https://github.com/yenshih/style-resources-loader + if (this.options.build.styleResources[ext]) { + const patterns = Array.isArray(this.options.build.styleResources[ext]) + ? this.options.build.styleResources[ext] + : [this.options.build.styleResources[ext]] + const options = Object.assign( + {}, + this.options.build.styleResources.options || {}, + { patterns } + ) - if (_postcssConfig) { - loaders.unshift({ - loader: 'postcss-loader', - options: Object.assign({ sourceMap }, _postcssConfig) + loaders.push({ + loader: 'style-resources-loader', + options }) } - } - // css-loader - // https://github.com/webpack-contrib/css-loader - const cssLoaderAlias = {} - cssLoaderAlias[`/${this.options.dir.assets}`] = path.join(this.options.srcDir, this.options.dir.assets) - cssLoaderAlias[`/${this.options.dir.static}`] = path.join(this.options.srcDir, this.options.dir.static) + // postcss-loader + // vue-loader already provides it's own + // https://github.com/postcss/postcss-loader + if (!isVueLoader) { + const _postcssConfig = postcssConfig.call(this) - loaders.unshift({ - loader: 'css-loader', - options: { - sourceMap, - minimize: !this.options.dev, - importLoaders: loaders.length, // Important! - alias: cssLoaderAlias + if (_postcssConfig) { + loaders.unshift({ + loader: 'postcss-loader', + options: Object.assign({ sourceMap }, _postcssConfig) + }) + } } - }) - // -- With extractCSS -- - if (this.options.build.extractCSS) { - loaders.unshift(MiniCssExtractPlugin.loader) - if (this.options.dev) { - // css-hot-loader - // https://github.com/shepherdwind/css-hot-loader + // css-loader + // https://github.com/webpack-contrib/css-loader + const cssLoaderAlias = {} + cssLoaderAlias[`/${this.options.dir.assets}`] = path.join(this.options.srcDir, this.options.dir.assets) + cssLoaderAlias[`/${this.options.dir.static}`] = path.join(this.options.srcDir, this.options.dir.static) + + loaders.unshift({ + loader: 'css-loader', + options: { + sourceMap, + minimize: !this.options.dev, + importLoaders: loaders.length, // Important! + alias: cssLoaderAlias + } + }) + + // -- With extractCSS -- + if (!isServer && this.options.build.extractCSS) { + loaders.unshift(MiniCssExtractPlugin.loader) + if (this.options.dev) { + // css-hot-loader + // https://github.com/shepherdwind/css-hot-loader + loaders.unshift({ + loader: 'css-hot-loader', + options: { sourceMap } + }) + } + } else { + // Prepare vue-style-loader + // https://github.com/vuejs/vue-style-loader loaders.unshift({ - loader: 'css-hot-loader', + loader: 'vue-style-loader', options: { sourceMap } }) } - } else { - // Prepare vue-style-loader - // https://github.com/vuejs/vue-style-loader - loaders.unshift({ - loader: 'vue-style-loader', - options: { sourceMap } - }) + return loaders } - return loaders } diff --git a/lib/builder/webpack/vue-loader.mjs b/lib/builder/webpack/vue-loader.mjs index de9a864bd8..488b605e88 100644 --- a/lib/builder/webpack/vue-loader.mjs +++ b/lib/builder/webpack/vue-loader.mjs @@ -1,8 +1,12 @@ import postcssConfig from './postcss' -import styleLoader from './style-loader' +import styleLoaderWrapper from './style-loader' export default function vueLoader({ isServer }) { // https://vue-loader.vuejs.org/en + const styleLoader = styleLoaderWrapper({ + isServer, + isVueLoader: true + }) const config = { postcss: postcssConfig.call(this), cssSourceMap: this.options.build.cssSourceMap, @@ -13,17 +17,16 @@ export default function vueLoader({ isServer }) { options: this.getBabelOptions({ isServer }) }, // Note: do not nest the `postcss` option under `loaders` - css: styleLoader.call(this, 'css', [], true), - less: styleLoader.call(this, 'less', 'less-loader', true), - scss: styleLoader.call(this, 'scss', 'sass-loader', true), + css: styleLoader.call(this, 'css', []), + less: styleLoader.call(this, 'less', 'less-loader'), + scss: styleLoader.call(this, 'scss', 'sass-loader'), sass: styleLoader.call( this, 'sass', - { loader: 'sass-loader', options: { indentedSyntax: true } }, - true + { loader: 'sass-loader', options: { indentedSyntax: true } } ), - stylus: styleLoader.call(this, 'stylus', 'stylus-loader', true), - styl: styleLoader.call(this, 'stylus', 'stylus-loader', true) + stylus: styleLoader.call(this, 'stylus', 'stylus-loader'), + styl: styleLoader.call(this, 'stylus', 'stylus-loader') }, template: { doctype: 'html' // For pug, see https://github.com/vuejs/vue-loader/issues/55