diff --git a/packages/nuxt3/src/config/config/build.ts b/packages/nuxt3/src/config/config/build.ts index 45a8122a2b..8ce0c9cbea 100644 --- a/packages/nuxt3/src/config/config/build.ts +++ b/packages/nuxt3/src/config/config/build.ts @@ -285,10 +285,10 @@ export default () => ({ vueStyle: {} } as Loaders, loadingScreen: {} as Record | false, + optimizeCSS: undefined as Record | false | undefined, optimization: { minimize: undefined as boolean | undefined, minimizer: undefined, - // cssMinimizer: undefined, splitChunks: { chunks: 'all', name: undefined, @@ -298,9 +298,7 @@ export default () => ({ } } } - } as WebpackConfiguration['optimization'] & { - // cssMinimizer: undefined | boolean | Record - }, + } as WebpackConfiguration['optimization'], /** * Enable [thread-loader](https://github.com/webpack-contrib/thread-loader#thread-loader) in webpack building * diff --git a/packages/nuxt3/src/config/options.ts b/packages/nuxt3/src/config/options.ts index 19575e25d1..5aac8ac6ae 100644 --- a/packages/nuxt3/src/config/options.ts +++ b/packages/nuxt3/src/config/options.ts @@ -345,10 +345,9 @@ function normalizeConfig (_options: CliConfiguration) { options.build.optimization.minimize = !options.dev } - // Enable cssMinimizer only when extractCSS is enabled - // if (options.build.optimization.cssMinimizer === undefined) { - // options.build.optimization.cssMinimizer = options.build.extractCSS ? {} : false - // } + if (options.build.optimizeCSS === undefined) { + options.build.optimizeCSS = options.build.extractCSS ? {} : false + } const { loaders } = options.build // const vueLoader = loaders.vue diff --git a/packages/nuxt3/src/webpack/plugins/vue/client.ts b/packages/nuxt3/src/webpack/plugins/vue/client.ts index 6f718747ed..4b04d00218 100644 --- a/packages/nuxt3/src/webpack/plugins/vue/client.ts +++ b/packages/nuxt3/src/webpack/plugins/vue/client.ts @@ -24,7 +24,7 @@ export default class VueSSRClientPlugin { const initialFiles = uniq(Object.keys(stats.entrypoints) .map(name => stats.entrypoints[name].assets) - .reduce((assets, all) => all.concat(assets), []) + .reduce((files, entryAssets) => files.concat(entryAssets.map(entryAsset => entryAsset.name)), []) .filter(file => isJS(file) || isCSS(file))) const asyncFiles = allFiles @@ -70,7 +70,7 @@ export default class VueSSRClientPlugin { const chunkGroup = namedChunkGroups[chunkName] if (chunkGroup) { for (const asset of chunkGroup.assets) { - filesSet.add(fileToIndex(asset)) + filesSet.add(fileToIndex(asset.name)) } } } diff --git a/packages/nuxt3/src/webpack/plugins/vue/server.ts b/packages/nuxt3/src/webpack/plugins/vue/server.ts index 045005402b..7db3952a3a 100644 --- a/packages/nuxt3/src/webpack/plugins/vue/server.ts +++ b/packages/nuxt3/src/webpack/plugins/vue/server.ts @@ -29,7 +29,7 @@ export default class VueSSRServerPlugin { return cb() } - const entryAssets = entryInfo.assets.filter(isJS) + const entryAssets = entryInfo.assets.filter(asset => isJS(asset.name)) if (entryAssets.length > 1) { throw new Error( @@ -39,14 +39,14 @@ export default class VueSSRServerPlugin { } const [entry] = entryAssets - if (!entry || typeof entry !== 'string') { + if (!entry || typeof entry.name !== 'string') { throw new Error( `Entry "${entryName}" not found. Did you specify the correct entry option?` ) } const bundle = { - entry, + entry: entry.name, files: {}, maps: {} } diff --git a/packages/nuxt3/src/webpack/presets/style.ts b/packages/nuxt3/src/webpack/presets/style.ts index 47e1d940d4..a44031c633 100644 --- a/packages/nuxt3/src/webpack/presets/style.ts +++ b/packages/nuxt3/src/webpack/presets/style.ts @@ -1,62 +1,136 @@ -// import MiniCssExtractPlugin from 'mini-css-extract-plugin' -// import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin' -// import StyleLoader from '../utils/style-loader' +import path from 'path' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' +import CssMinimizerPlugin from 'css-minimizer-webpack-plugin' +import { wrapArray } from 'src/utils' +import { fileName, WebpackConfigContext, applyPresets } from '../utils/config' -import { WebpackConfigContext } from '../utils/config' +export function style (ctx: WebpackConfigContext) { + applyPresets(ctx, [ + loaders, + extractCSS, + minimizer + ]) +} + +function minimizer (ctx: WebpackConfigContext) { + const { options, config } = ctx + + if (options.build.optimizeCSS && Array.isArray(config.optimization.minimizer)) { + config.optimization.minimizer.push(new CssMinimizerPlugin({ + ...options.build.optimizeCSS + })) + } +} + +function extractCSS (ctx: WebpackConfigContext) { + const { options, config } = ctx -export function style (_ctx: WebpackConfigContext) { - // // CSS extraction) - // if (options.build.extractCSS) { - // plugins.push(new MiniCssExtractPlugin(Object.assign({ - // filename: fileName(ctx, 'css'), - // chunkFilename: fileName(ctx, 'css') - // }, options.build.extractCSS))) - // } // CSS extraction - // if (options.build.extractCSS) { - // plugins.push(new MiniCssExtractPlugin(Object.assign({ - // filename: fileName(ctx, 'css'), - // chunkFilename: fileName(ctx, 'css'), - // // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132 - // reloadAll: true - // }, options.build.extractCSS))) - // } + if (options.build.extractCSS) { + config.plugins.push(new MiniCssExtractPlugin({ + filename: fileName(ctx, 'css'), + chunkFilename: fileName(ctx, 'css'), + ...(options.build.extractCSS as object) + })) + } +} + +function loaders (ctx: WebpackConfigContext) { + const { config, options } = ctx + + // CSS + config.module.rules.push(createdStyleRule('css', /\.css$/i, null, ctx)) + + // Postcss + config.module.rules.push(createdStyleRule('postcss', /\.p(ost)?css$/i, null, ctx)) + + // Less + const lessLoader = { loader: 'less-loader', options: options.build.loaders.less } + config.module.rules.push(createdStyleRule('less', /\.less$/i, lessLoader, ctx)) + + // Sass (TODO: optional dependency) + const sassLoader = { loader: 'sass-loader', options: options.build.loaders.sass } + config.module.rules.push(createdStyleRule('sass', /\.sass$/i, sassLoader, ctx)) + + const scssLoader = { loader: 'sass-loader', options: options.build.loaders.scss } + config.module.rules.push(createdStyleRule('scss', /\.scss$/i, scssLoader, ctx)) + + // Stylus + const stylusLoader = { loader: 'stylus-loader', options: options.build.loaders.stylus } + config.module.rules.push(createdStyleRule('stylus', /\.styl(us)?$/i, stylusLoader, ctx)) +} + +function createdStyleRule (lang: string, test: RegExp, processorLoader, ctx: WebpackConfigContext) { + const { options } = ctx + + const styleLoaders = [ + processorLoader, + createStyleResourcesLoaderRule(lang, options.build.styleResources, options.rootDir) + ].filter(Boolean) + + options.build.loaders.css.importLoaders = + options.build.loaders.cssModules.importLoaders = + styleLoaders.length + + const cssLoaders = createCssLoadersRule(ctx, options.build.loaders.css) + const cssModuleLoaders = createCssLoadersRule(ctx, options.build.loaders.cssModules) + + return { + test, + oneOf: [ + // This matches