-
-
Notifications
You must be signed in to change notification settings - Fork 384
Support multiple instances of MiniCssExtractPlugin #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Please describe your use case and provide your config, this might not be needed anymore with |
Hi @michael-ciniawsky - thanks for the prompt response. Hopefully this will clarify the need.
When the build runs, a CSS bundle file for each theme is emitted. Each component imports the SASS file containing the theme variables
The above config runs happily emitting the css bundle I want to run the build once - emitting both light and dark theme bundles. |
Another use case is this:
In ETWP we'd do: -const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const ExtractTextPlugin = require('mini-css-extract-plugin')
-const GlobalExtractTextPlugin = new ExtractTextPlugin({
- filename: production ? 'styles-[name]-[contenthash].css' : 'styles-[name].css',
- ignoreOrder: false,
- allChunks: true,
-})
-
-const ModuleExtractTextPlugin = new ExtractTextPlugin({
- filename: production ? 'modules-[name]-[contenthash].css' : 'modules-[name].css',
- ignoreOrder: false,
- allChunks: true,
-}) {
test: /\.(css)$/,
include: [/stylesheets/, /node_modules/],
- use: extractCss
- ? GlobalExtractTextPlugin.extract({
- fallback: ['style-loader', 'css-loader'],
- use: ['css-loader'],
- })
- : ['style-loader', 'css-loader'],
+ use: extractCss ? [ExtractTextPlugin.loader, 'css-loader'] : ['style-loader', 'css-loader'],
}, {
test: /\.css$/,
exclude: [/stylesheets/, /node_modules/],
use: extractCss
+ ? [
- ? ModuleExtractTextPlugin.extract({
+ ExtractTextPlugin.loader,
- fallback: [
- 'style-loader',
- 'css-loader?sourceMap&modules,localIdentName=[local]-[hash:base64]',
- ],
+ ]
- use: ['css-loader?sourceMap&modules,localIdentName=[local]-[hash:base64]'],
+ 'css-loader?sourceMap&modules,localIdentName=[local]-[hash:base64]',
- })
: ['style-loader', 'css-loader?sourceMap&modules,localIdentName=[local]-[hash:base64]'], |
Here is an example repo of me trying to achieve theming with multiple instances of |
Does this functionality not overlap with |
@garygreen I can't see how that would work. In the end the SASS processor has to run twice (with different variables files each time) generating parallel sets of CSS. My understanding off chunks is that it's one set of output split into different bits - rather than two separate outputs. I'd happily be proved wrong tho ;-) |
@james-s-turner I'm thinking something along the lines of: module: {
rules: [
{
test: /\.scss$/,
include: path.resolve(__home, "src/light-theme"),
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
},
{
test: /\.scss$/,
include: path.resolve(__home, "src/dark-theme"),
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
}
]
}, With a splitChunks section etc. Or is that just a slightly different way to write what your trying to achieve? |
Interesting use case. The same could be supported for the mini-css-extract-plugin. In the same way as it's implemented in extract-text-webpack-plugin. Send a PR. |
@garygreen Unfortunately if you define two rules with the same test pattern - then only the first one is run |
@michael-ciniawsky the goal is just to be able to create more than one CSS file. Any suggestions? |
We also have something like this where we have our |
@sokra is there any guidance on how you would like it done? Should we : Or is there another way you wish this to be done? |
We use multiple instances of ETWP, one to handle css and one to handle scss so that the different formats end up in a different file.
|
Somebody can provide real use case for two instance? |
@evilebottnawi this is a real case I believe : I personally use the multiple extracts to separate the |
@lukepolo it is solved by code splitting, i.e |
Mind showing an example ? Or to some documentation, very little knowledge in that subject |
Forget about this plugins as plugins, it is PoC build-in css extract in future |
Ok so basically what your saying is all we need todo is :
|
Here's a (possibly unorthodox) use-case for multiple instances: I have been using an additional instance of ExtractTextWebpackPlugin to output critical CSS into a Twig template partial to be inlined into the head of my document. This file lives in a directory outside of my normal const extractCss = new ExtractTextPlugin('[name].css');
const extractCritical = new ExtractTextPlugin('../../src/FrontendBundle/Resources/views/common/critical-styles.css.twig'); {
test: /.scss$/,
exclude: path.resolve(srcPath, '../scss/critical.scss'),
use: extractCss.extract(['css-loader',
{
loader: 'postcss-loader',
options: { sourceMap: true }
},
'sass-loader'])
},
{
test: /.scss$/,
include: path.resolve(srcPath, '../scss/critical.scss'),
use: extractCritical.extract(['css-loader',
{
loader: 'postcss-loader',
options: { sourceMap: true }
},
'sass-loader'])
}, Will it be possible to continue with this approach using MiniExtractCSSPlugin? |
@andosteinmetz use Code splitting sometimes looks difficult, but it is part of your configuration which you should good understand. We have If you want have fast application, you should to understand how it works. With code splitting you can do all, really, what you can imagine. Who want two/three/etc instance just don't know how to use code splitting. My recommendation is using webpack config from popular boilerplate. Because you can decrease perf you app. |
@lukepolo yes, feel free to experimental with code splitting, if you can problems with this just ping me, also feel free to improve README to avoid question from other people 👍 |
I would recommend just using |
Thanks @evilebottnawi. The application is already doing some code splitting, but mulitple instances of ETWP was a quick hack to output to another directory. I will look at the documentation for |
@ccorcos |
@andosteinmetz multiple instance just the hack, also it is decrease you build time, code splitting is very fast and stable. |
@evilebottnawi I wonder if this use case for a multi entry app can also be resolved by code splitting? const extractSass = new ExtractTextPlugin('build/css/[name].css');
const extractPageLevelSass = new ExtractTextPlugin(
'Library.tx/build/css/[name].tx',
);
//…
{
test: /.*sass\/page_level\/.*\.scss$/,
loader: extractPageLevelSass.extract({
use: ['css-loader', parts.autoprefix(), parts.sassLoader()],
}),
},
{
test: /.*sass\/(?!page_level\/).*\.scss$/,
loader: extractSass.extract({
use: ['css-loader', parts.autoprefix(), parts.sassLoader()],
}),
},
//…
plugins: [
extractPageLevelSass,
extractSass,
//…
],
//… Almost there in webpack 4 with mini-css-extract-plugin, but it just outputs all extracted stylesheets to both locations: // parts
exports.extractCSS = ({
include, test, exclude, use = [], filename
}) => {
const plugin = new MiniCssExtractPlugin({
filename: filename,
});
return {
module: {
rules: [
{
test: test,
include,
exclude,
use: [MiniCssExtractPlugin.loader].concat(use),
},
],
},
plugins: [plugin],
};
};
// config
const extractPageLevelSass = parts.extractCSS({
test: /.*sass\/page_level\/.*\.scss$/,
filename: 'Library.tx/build/css/[name].tx',
use: ['css-loader', parts.autoprefix(), parts.sassLoader()],
});
const extractSass = parts.extractCSS({
test: /.*sass\/(?!page_level\/).*\.scss$/,
filename: 'build/css/[name].css',
use: ['css-loader', parts.autoprefix(), parts.sassLoader()],
});
module.exports = merge(config, extractPageLevelSass, extractSass); |
I am using multiple instances to separate my local style from my global styles. Until now I used it like this with extract-text-webpack-plugin. How would I achieve this with mini-css-extract?
|
I use multiple instances of ExtractTextWebpackPlugin to dynamically change the publichPath. Like: const myApp= new ExtractTextPlugin('stylesheets/app.css');
const dompdf= new ExtractTextPlugin('stylesheets/dompdf.css');
module.exports = {
module: {
rules: [
{
test: /app\.scss$/,
use: myApp.extract({ use: [...., 'sass-loader' ], publicPath: "/" })
},
{
test: /dompdf\.scss$/i,
use: dompdf.extract({ use: [...., 'sass-loader' ], publicPath: "https://app.domain.com/"})
},
]
},
plugins: [
myApp,
dompdf
]
}; And if someone can help me, i'll be very grateful I dont know if it is an issue. I have the next entry config: entry = {
app: ['src/assets/js/app.js', 'src/assets/scss/app.scss'],
dompdf: [ 'src/assets/scss/dompdf.scss'],
}; And i have the default config of mini-css-extract-plugin and for some reason What can be a posible solution? |
I was researching this one and realized the answer is actually listed in the Webpack docs. I've implemented this and it works great! https://webpack.js.org/plugins/mini-css-extract-plugin/#extracting-css-based-on-entry Though I ended up doing something a bit more dynamic where I have a JSON config file... {
"scss" : [
{
"path" : "assets/scss/styles-red.scss",
"name" : "styles-red",
"primary" : true
},
{
"path" : "assets/scss/styles-light-blue.scss",
"name" : "styles-light-blue",
"primary" : false
}
]
} Note: I have some environment-specific rules which are redacted here but that's the idea with Which I import: const config = require('./env/dev/config'); Setup default entry points: const entryPoints = {
app: [ 'whatwg-fetch', appPath ]
} Then loop through my config: config.scss.forEach(entry => {
entryPoints[entry.name] = path.join(rootPath, entry.path);
}); Setup the groups: const cacheGroups = {}
config.scss.forEach(entryPoint => {
cacheGroups[entryPoint.name] = {
name: entryPoint.name,
test: (m,c,entry = entryPoint.name) => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
chunks: 'all',
enforce: true
}
}); And add them in! const webpackConfig = {
entry: entryPoints,
// webpack stuff
optimization: {
splitChunks: {
cacheGroups: cacheGroups
}
}
// more webpack stuff
} Hope that helps someone! |
I'm interested in this functionality to be able to generate a light theme and dark theme based on 1 entry. |
@dwatts3624 feel free to send a PR to docs 👍 |
I've migrated webpack 2 to 4, I needed to migrate from ExtractTextPlugin to MiniCssExtractPlugin, and now I'm facing the same issue and I'm trying to implement the suggested examples, I'm struggling since 4 hours and I've reached a point where the console doesn't throw errors but my ReactJS webpage does not load at all, blank. Could someone bear a hand to me please? WebPack 3 Configurationconst webpack = require('webpack');
const resolve = require('path').resolve;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const extractAppStyles = new ExtractTextPlugin('css/app.[contenthash].css');
const extractVendorStyles = new ExtractTextPlugin('css/vendor.[contenthash].css');
// This contains shared configuration for dev and prod builds
module.exports = {
entry: {
// All App source files will be compiled into main
app: './src/index.js',
// All vendor files will be compiled into vendor.
// You should add new packages you install here.
vendor: [
'babel-polyfill',
'react',
'react-dom',
'react-router-dom',
'semantic-ui-react',
'dateformat',
'axios',
'react-waypoint',
//'@babel/preset-react'
]
},
devServer: {
historyApiFallback: true,
noInfo: true,
contentBase: './dist',
host: '0.0.0.0',
hot: true,
open: true,
historyApiFallback: true,
inline: true
},
module: {
rules: [
// Transpile all .js and .jsx files
{
test: /\.(js|jsx)?$/,
exclude: /(node_modules)/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/react',
'@babel/env',
],
plugins: [
"@babel/plugin-proposal-class-properties",
[
"@babel/plugin-proposal-decorators", { "legacy": true }
],
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
],
}
},
],
},
// Compile CSS files
{ test: /\.css$/, loader: "style-loader!css-loader" },
// Compile SCSS files
{
test: /\.scss$/,
// This compiles styles specific to this app
include: resolve(__dirname, './src/app/styles'),
use: extractAppStyles.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { minimize: true, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
]
}),
},
{
test: /\.scss$/,
// This compiles styles from Semantic-UI
include: resolve(__dirname, './src/assets'),
use: extractVendorStyles.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { minimize: true, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
]
}),
},
// Copy static assets over with file-loader
{
test: /\.(ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader', options: {name: '[name].[ext]'},
},
{
test: /\.(woff|woff2|eot|ttf|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader', options: {name: 'fonts/[name].[ext]'},
},
{
test: /\.(jpg|gif|png|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader', options: {name: 'images/[name].[ext]'},
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
// This pulls out webpack module IDs that changes every build to help with caching
new webpack.HashedModuleIdsPlugin(),
// This separates vendor-provided code into a separate chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
// This pulls out webpack boilerplate code that changes every build to help with caching
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
// Extract styles into a separate css files
extractAppStyles,
extractVendorStyles,
// Inject the build date as an environment variable
new webpack.DefinePlugin({
'process.env':{
'BUILD_DATE': JSON.stringify(new Date())
}
}),
// Inject the required assets into the template index file
new HTMLWebpackPlugin({
filename: 'index.html',
template: 'src/assets/index.html',
}),
// Copy public files into the dist folder
new CopyWebpackPlugin([
{ from: 'src/public' }
]),
],
output: {
path: __dirname + '/dist',
filename: 'js/[name].[chunkhash].js',
chunkFilename: 'js/[name].[chunkhash].js',
publicPath: '/',
},
}; WebPack 4 Configurationconst webpack = require('webpack');
const resolve = require('path').resolve;
//const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production'
//const extractAppStyles = new ExtractTextPlugin('css/app.[contenthash].css');
//const extractVendorStyles = new ExtractTextPlugin('css/vendor.[contenthash].css');
// This contains shared configuration for dev and prod builds
module.exports = {
entry: {
// All App source files will be compiled into main
app: './src/index.js',
// All vendor files will be compiled into vendor.
// You should add new packages you install here.
vendor: [
'babel-polyfill',
'react',
'react-dom',
'react-router-dom',
'semantic-ui-react',
'dateformat',
'axios',
'react-waypoint',
]
},
devServer: {
historyApiFallback: true,
noInfo: true,
contentBase: './dist',
host: '0.0.0.0',
hot: true,
open: true,
historyApiFallback: true,
inline: true
},
module: {
rules: [
// Transpile all .js and .jsx files
{
test: /\.(js|jsx)?$/,
exclude: /(node_modules)/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/react',
'@babel/env',
],
plugins: [
"@babel/plugin-proposal-class-properties",
[
"@babel/plugin-proposal-decorators", { "legacy": true }
],
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
],
}
},
],
},
// Compile CSS files
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] },
// Compile SCSS files
{
test: /\.scss$/,
// This compiles styles specific to this app
include: resolve(__dirname, './src/app/styles'),
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { minimize: true, sourceMap: true } },
{ loader: 'sass-loader', options: {
sourceMap: true,
includePaths: [resolve(__dirname, "css/app.css")]
} }
],
},
{
test: /\.scss$/,
// This compiles styles from Semantic-UI
include: resolve(__dirname, './src/assets'),
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { minimize: true, sourceMap: true } },
{ loader: 'sass-loader', options: {
sourceMap: true,
includePaths: [resolve(__dirname, "css/vendor.css")]
} }
]
},
// Copy static assets over with file-loader
{
test: /\.(ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader', options: {name: '[name].[ext]'},
},
{
test: /\.(woff|woff2|eot|ttf|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader', options: {name: 'fonts/[name].[ext]'},
},
{
test: /\.(jpg|gif|png|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader', options: {name: 'images/[name].[ext]'},
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
// This pulls out webpack module IDs that changes every build to help with caching
new webpack.HashedModuleIdsPlugin(),
// This separates vendor-provided code into a separate chunk
// new webpack.optimize.CommonsChunkPlugin({
// name: 'vendor'
// }),
// // This pulls out webpack boilerplate code that changes every build to help with caching
// new webpack.optimize.CommonsChunkPlugin({
// name: 'runtime'
// }),
// Extract styles into a separate css files
//extractAppStyles,
//extractVendorStyles,
// Inject the build date as an environment variable
new webpack.DefinePlugin({
'process.env':{
'BUILD_DATE': JSON.stringify(new Date())
}
}),
// Inject the required assets into the template index file
new HTMLWebpackPlugin({
filename: 'index.html',
template: 'src/assets/index.html',
}),
// Copy public files into the dist folder
new CopyWebpackPlugin([
{ from: 'src/public' }
]),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
}),
new MiniCssExtractPlugin({
filename: devMode ? 'app.css' : 'app.[hash].css',
}),
new MiniCssExtractPlugin({
filename: devMode ? 'vendor.css' : 'vendor.[hash].css',
})
],
optimization: {
splitChunks: {
chunks: 'all',
},
runtimeChunk: true,
},
output: {
path: __dirname + '/dist',
filename: 'js/[name].[chunkhash].js',
chunkFilename: 'js/[name].[chunkhash].js',
publicPath: '/',
},
}; I think that I'm not seeing it from a correct point of view. I don't see the way how I can simulate WebPack 3 configuration with mini-css-extract-plugin |
Any update about this? |
This helped a lot. Only issue is while it generates the correct css, it ALSO generates an extraneous JS per chunk. How can I stop this?
|
@ErikVeland You could try this: https://github.com/fqborges/webpack-fix-style-only-entries |
We have a use case for multiple instances of the plugin because we have two separate loader configurations, one for our legacy CSS that doesn't use CSS modules and one for our updated CSS that does use CSS modules. For this reason, we need to be able to run the plugin on each and extract them into the same output file. |
Any update about this? |
You should use |
@evilebottnawi |
…ract-plugin#45 before requiring different css
I use const glob = require('glob');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const entry = glob.sync('pages/**/*.js', { cwd: 'src/' }).reduce(function(entry, pathName) {
const pathNameWithoutExt = path.join(path.dirname(pathName), path.parse(pathName).name);
entry[pathNameWithoutExt] = './' + pathName; // Leading dot is crucial
return entry;
}, {});
const extractStyles = new ExtractTextPlugin('[name].css');
const extractHtml = new ExtractTextPlugin('[name].html');
module.exports = {
context: path.resolve('src/'),
entry,
output: {
path: path.resolve('public/'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.js$/,
include: path.resolve('src/'),
use: 'babel-loader',
},
{
test: /\.less$/,
include: path.resolve('src/'),
use: extractStyles.extract({ use: ['raw-loader', 'postcss-loader', 'less-loader'] }),
},
{
test: /\.html$/,
include: path.resolve('src/modules/'),
use: 'raw-loader',
},
{
test: /\.html$/,
include: path.resolve('src/pages/'),
use: extractHtml.extract({
use: [
{ loader: 'raw-loader' },
{ loader: 'nunjucks-html-loader', options: { searchPaths: ['src/'] } },
],
}),
},
],
},
plugins: [
extractStyles,
extractHtml,
],
}; |
Chunk splitting API really is the optimal solution here. I don't have it on hand but Google "the 100% correct way to code split" there's a medium article that comes up which shows some versitility of splitChunks. The webpack documentation site also has an updated section showing css splitting. Either under the code splitting, splitChunks, or mini css docs. I've been digging through the next.js webpack builds. The latest release has an impressive build and their use of splitChunks has some CSS config if I remember correctly. |
Is there a good solution for this use case I described above? We have two separate loader configurations for our CSS.. This is currently our sole blocker from being able to upgrade to Webpack 4.
|
Closed, this thread looks like a spam, it is hard to track something here, no need support multiple instances, you can split your css using Note: if you use |
I understand closing this issue as people were starting to add similar related issues but the original issue that @james-s-turner logged never seemed to be addressed/resolved. The conversation turned into people wondering how to code split CSS. @james-s-turner provided a repro with a test case: https://github.com/james-s-turner/webpack4-multi-theme/blob/master/webpack-config.js I'm trying to accomplish the same thing as @james-s-turner was trying to do, but haven't had much luck reading through docs on |
@bjankord please open new issue with reproducible test repo, i will write how to solve this and we update docs for this case, thanks |
Sounds good, I appreciate the feedback @evilebottnawi! 👍 |
…-webpack-plugin Can filename support function type ? panr#67 webpack-contrib/mini-css-extract-plugin#67 this not work: webpack-contrib/mini-css-extract-plugin#45 (comment) Extracting CSS based on entry also not work webpack-contrib/mini-css-extract-plugin#220
The extract-webpack-text-plugin allows you to define multiple instances of the plugin. Does MiniCssExtractPlugin support the same feature? My end goal is to generate multiple bundles for SASS themes.
The text was updated successfully, but these errors were encountered: