Is it possible to have multiple JS chunks but a single css output file?
My current setup is:
optimization: {
minimize: true,
minimizer: [
new UglifyJSPlugin({
uglifyOptions: {
compress: {
passes: 2,
},
output: {
comments: false,
},
},
}),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
filename: 'vendorbundle.js',
test: m => /node_modules/.test(m.context),
},
},
},
},
...
...
new MiniCssExtractPlugin({
filename: 'styles.css',
}),
This generates 2 JS files, and 2 css files (styles.css, and 1.styles.css)
What I would like to have is a single css file, is that doable?
I've been using something like that for the same use-case with success:
optimization: {
splitChunks: {
cacheGroups: {
default: false,
// Custom common chunk
bundle: {
name: 'commons',
chunks: 'all',
minChunks: 3,
reuseExistingChunk: false,
},
// Customer vendor
vendors: {
chunks: 'initial',
name: 'vendors',
test: 'vendors',
},
// Merge all the CSS into one file
styles: {
name: 'styles',
test: /\.s?css$/,
chunks: 'all',
minChunks: 1,
reuseExistingChunk: true,
enforce: true,
},
},
},
},
Doesnt seem to work for me

@Akkenar can you share all of the config file please?
@AvraamMavridis it seems that I was missing an enforce: true on the rule.
You'll find an example project there: https://github.com/Akkenar/CSS-Single-File-Example
Webpack config:
import webpack from 'webpack';
import path from 'path';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
const vendors = ['react', 'react-dom'];
const isProduction = process.env.NODE_ENV === 'production';
export default {
name: 'client',
mode: isProduction ? 'production' : 'development',
target: 'web',
entry: {
bundle: './entry.js',
vendors,
},
context: path.join(__dirname, 'src'),
stats: {
// Disable the verbose output on build
children: false,
},
devtool: isProduction ? false : 'inline-cheap-module-eval-source-map',
output: {
path: path.join(__dirname, 'target', 'build'),
filename: '[name].js',
chunkFilename: '[name].js',
},
resolve: {
extensions: ['.js', '.jsx', '.scss'],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader?cacheDirectory'],
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
],
},
optimization: {
splitChunks: {
cacheGroups: {
default: false,
// Custom common chunk
bundle: {
name: 'commons',
chunks: 'all',
minChunks: 3,
reuseExistingChunk: false,
},
// Customer vendor
vendors: {
chunks: 'initial',
name: 'vendors',
test: 'vendors',
},
// Merge all the CSS into one file
styles: {
name: 'styles',
test: /\.s?css$/,
chunks: 'all',
enforce: true,
},
},
},
},
profile: false,
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].css',
}),
new OptimizeCSSAssetsPlugin(),
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true,
preserveLineBreaks: true,
removeComments: true,
},
filename: 'index.html',
template: path.join(__dirname, 'src', 'index.html'),
}),
],
};
@Akkenar It doesnt really work:

That is my setup:
const path = require('path');
const loaders = require('./loaders');
const plugins = require('./plugins');
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const frontend = require('../package.json');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const DIRECTORY = `${__dirname}/../`;
module.exports = {
mode: 'production',
devtool: 'source-map',
entry: {
app: ['babel-polyfill', path.join(__dirname, './../src/App.js')],
},
optimization: {
minimize: true,
minimizer: [
new UglifyJSPlugin({
uglifyOptions: {
compress: {
passes: 2,
},
output: {
comments: false,
},
},
}),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
default: false,
// Custom common chunk
bundle: {
name: 'commons',
chunks: 'all',
minChunks: 3,
reuseExistingChunk: false,
},
// Customer vendor
vendor: {
filename: 'vendorbundle.js',
test: m => /node_modules/.test(m.context),
},
// Merge all the CSS into one file
styles: {
name: 'styles',
test: /\.s?css$/,
chunks: 'all',
minChunks: 1,
reuseExistingChunk: true,
enforce: true,
},
},
},
},
output: {
filename: 'bundle.js',
path: path.join(DIRECTORY, './artifacts/'),
chunkFilename: 'chunk-[name]-[hash].js',
publicPath: '/savingglobal/assets/dist/js/',
},
plugins: plugins.concat([
new ProgressBarPlugin(),
/* Css specific plugins */
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].css',
}),
new OptimizeCssAssetsPlugin({
cssProcessor: require('cssnano'),
cssProcessorOptions: {
discardComments: { removeAll: true },
zindex: false,
},
canPrint: true,
}),
/* Define the enviroment */
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env': {
NODE_ENV: JSON.stringify('production'),
frontend_version: JSON.stringify(frontend.version),
},
}),
new webpack.optimize.ModuleConcatenationPlugin(),
]),
module: {
rules: loaders.concat([
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
]),
},
resolve: {
alias: {
'raisin-frontend': path.resolve(__dirname, '../../'),
'ui-lib': '@raisin/ui-lib',
assets: path.resolve(__dirname, '../assets'),
},
extensions: ['.js', '.jsx', '.json', '.scss'],
},
};
It seems that the vendors are still generated for the CSS.
You could try:
vendors: false, in cacheGroups, see if this disables the vendor~ generationcacheGroups, see if you can have the style one being more greedy.Do you want to send a PR adding this to the documentation?
minChunks: 1, and reuseExistingChunk: true, are not needed.
Thanks for your feedback @sokra .
I'll send a PR whenever I find time.
how to prevent styles.js? it's a redundant file.
how to prevent styles.js? it's a redundant file.
This need to be solved on webpack side
Is this supposed to work at the moment ?
@aboeglin no, please create issue in webpack repo with minimum reproducible test repo. thanks
I'm still getting a CSS file for every chunk with
styles: {
name: 'styles',
test: /\.(css|less)$/, // not sure if .less is necessary or its already been compiled at this point
chunks: 'all',
enforce: true
}
Which seems to be breaking my CSS CSS break was my own dumb fault. cssnano was shrinking my z-indexes. I still haven't managed to produce a single output CSS file though. Probably something else I messed up...
I also cannot make it work, whats happens for me is that I have multiple entries (multi real web pages SPA), and after any combination suggested here, I end up with unnecessary styles.js chunk, and, whats worse, without vendor and commons chunks which are generated otherwise.
For now I migrated back to extract-text-plugin@next, which works fine.
It should be jsx? unless you're using jsx and jx extensions, which would be
weird.
On Tue, Aug 7, 2018, 7:59 AM Iuriy Budnikov notifications@github.com
wrote:
@mnpenner https://github.com/mnpenner,
Try to set extension test: /.js?x$/ for js files.
It works for me.optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendor',
chunks: 'all',
test: /.js?x$/,
},
styles: {
name: 'styles',
test: /.s?css$/,
chunks: 'all',
enforce: true,
},
},
}
},—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/52#issuecomment-411087149,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABD7ptewB1If4CefyHgNadruHYV2wx71ks5uOatOgaJpZM4S4WPG
.
@mnpenner , please ignore my message. It doesn't resolve the issue.
@sokra
Why the issue has closed? This is not fixed. At least point people to a direction.
Non of the above proposed solutions worked for me.
I solved the issue by using underscore for scss files in components and then importing it in style.scss the usual way.
components
-- About
--- _about.scss
--- About.vue
--- About.ts
scss
-- style.scss
@import '../components/About/about';
If someone found an answer please post it here.
Give the explanation here a shot: https://github.com/webpack-contrib/mini-css-extract-plugin/issues/257#issuecomment-421625285
I seemed to be having a similar problem and this fixed it.
Can this please be re-opened until a solution is in place?
@rnicholus solution of what? use splitChunks and configure chunks how you want (one, two, one hundred)
I've followed that guidance, and it didn't seem to have any effect. This seems to have been a problem for other posters in this issue as well.
This works for me, change the test regular expression to only match .js (and not something like .css):
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/].*\.js$/,
name: 'vendors',
chunks: 'all',
},
},
},
},
@2-5 After adding that to my webpack config, I'm still seeing about 50 CSS chunks.
To Create a Single css file and create chunk of vendor.js file, can use the following configuration.
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/](.(?!.*\.css$))*$/,
name: 'vendors',
chunks: 'all'
}
},
}
}
the regex is different from one mentioned in above comments : test: /[\\/]node_modules[\\/].*\.js$/,
in a way that it takes all files apart from .css files in vendors.js...
can use either one based on file types used in project.
this is compatible with Webpack version > 4 at the time.
Stumbled over the same problem but I go one step further because my script imports also .scss and .less files from vendors:
splitChunks: {
vendor: {
// [...]
test: /node_modules.*(?<!\.css)(?<!\.scss)(?<!\.less)$/
// [...]
}
}
Very bad bug.
It's impossible to fetch all styles to one file.
Found the reason - it was due to my custom webpack config, where I was combining chunks by issuer file.
But for one chunk the issuer was completely separate - that's why some styles gone to separate bundle.
@StNekroman, have you tried the formula suggested in the PR #60 , which is already merged?
It works for me, combining in one file shared styles among different entry points dependency graphs, no matter if are vendors or local app ones.
@temple nevermind, it was my issue - comment updated.
Most helpful comment
This works for me, change the
testregular expression to only match.js(and not something like.css):