Webpack 4: mini-css-extract-plugin + sass-loader + splitChunks - css

I've the follow example configuration to use mini-css-extract-plugin with Webpack 4:
entry: {
a: ['./js/a.js', './scss/a.scss'],
b: ['./js/b.js', './scss/b.scss']
},
module: {
rules: [
[...],
{
test: /\.(css|sass|scss)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('autoprefixer')
],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
js: {
test: /\.js$/,
name: "commons",
chunks: "all",
minChunks: 7,
},
css: {
test: /\.(css|sass|scss)$/,
name: "commons",
chunks: "all",
minChunks: 2,
}
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: "dist/[name].css",
}),
]
And the following sass files:
// a.scss
#import 'libA.scss';
#import 'libB.css';
[...]
// b.scss
#import 'libA.scss';
#import 'libB.css';
[...]
When I run webpack libB.css is inserted in in commons.css bundle while libA.scss not.
In general every import of .css file get processed by splitChunks option (only if extension css is specified in the name) while sass import not.
I have a project with multiple sass entry point and many #import of sass component and I'd like to create a common bundle with shared sass modules.

im doing someting like this in my Webpack 4 configuration.
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module: {
rules: [ {
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true,
sourceMap: true,
importLoader: 2
}
},
"sass-loader"
]}
]
},
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "style.css",
chunkFilename: "[name].css"
})
]
I also give output.publicPath -configuration object to point into my build dir, example ->
output: {
filename: "[name]-min.js",
path: path.resolve(__dirname, "dist"),
publicPath: "/static/react/dist/"
},
edit If you are interested in code splitting, Webpack 4 can do this "out of the box" for you. This will split .js and as well .css -files for you in case you use dynamic imports.
optimization: {
splitChunks: {
chunks: 'all'
}
}
If you other hand would like to merge only your .css into one file you could add as follow.
optimization: {
splitChunks: {
chunks: 'all'
cacheGroups: {
styles: {
name: 'styles',
test: /\.s?css$/,
chunks: 'all',
minChunks: 1,
reuseExistingChunk: true,
enforce: true,
},
}
},
}

Related

SVG in CSS is interpreted via webpack with "module.exports = 'data:...'" instead of valid svg

I use webpack to compile scss. The scss also contains url to svg files, which I need to process and link to css with correct url.
However, instead of a valid svg, I get a file (eebc108eb2b00c4b2784.svg) with the following content:
module.exports = "data:image/svg+xml,%3csvg width='30px' height='30px' xmlns=...'
which is linked to css like:
background-image: url(../eebc108eb2b00c4b2784.svg);
That is what i want basically, but svg obviously dont show. This SVG is not used anywhere in JS only in CSS.
My webpack config looks like this:
const webpack = require("webpack");
const path = require("path");
const AssetsPlugin = require("assets-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
let config = {
watch: true,
entry: {
appAdmHomepage: "./_devapp/AppAdmHomepage.js",
cssStyle: "../css/primary/style.scss",
cssAdm: "../css/primary/administrace.scss",
cssPrint: "../css/primary/print.scss",
cssObjProces: "../css/primary/objednavkovy_proces.scss",
cssDesign: "../css/primary/design.scss",
},
output: {
filename: "[name].bundle.js",
chunkFilename: "[name].[contenthash].js",
path: path.resolve(__dirname, "assets", "bundle")
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts", ".tsx", ".css", ".scss"]
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: path.resolve(__dirname, "node_modules"),
use: {
loader: "babel-loader",
options: {
presets: [
"#babel/preset-env",
["#babel/preset-react",{
"runtime": "automatic"
}]
],
plugins : [
["#babel/plugin-proposal-decorators", { version: "legacy" }],
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-proposal-class-properties",
"#babel/plugin-proposal-private-methods",
]
}
},
}, {
test: /\.svg$/,
use: [
{
loader: "svg-url-loader",
options: {
name: "./image/[name]--.[ext]"
},
},
],
}, {
test: /\.scss$/,
include: [
path.resolve(__dirname, "../css/primary/")
],
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
url: true,
import: true
}
},
"sass-loader"
]
}, {
test: /\.css$/, //pro datepicker
use: [
"style-loader",
{
loader: "css-loader",
options: {
url: true
}
}
]
}
],
},
plugins: [
new webpack.DefinePlugin({
"__DEV__" : JSON.stringify(true),
"__API_HOST__" : JSON.stringify("http://127.0.0.1:8080/www/eshop/www"),
"__PATH_HOST__" : JSON.stringify("/www/eshop/www"),
}),
new AssetsPlugin({
prettyPrint: true,
removeFullPathAutoPrefix: true,
fullPath: true
}),
new MiniCssExtractPlugin({
filename: "./css/[name].min.css"
}),
new CleanWebpackPlugin()
]
};
module.exports = config;
Is there a way to link correct form of SVG or am i making a mistake somewhere? Thanks for help

Compile sass files to hashed css using webpack

to compile my .scss files I use webpack.
What i want to achieve is to get for example
styles.b0eda78ad870282c8cb9fd58884415d7.css
instead of
styles.css
When I add to webpack option [hash] (now commented in source) - then every change in .scss files generate new .css files. For example 3 changes in .scss will generate 3 new files with different hashes:
themeA.b0eda78ad870282c8cb9fd58884415d7.css
themeA.2c0521243fe51696a6d08ae5443a6721.css
themeA.6ebd2d65f4d311fbac8441141826a9a8.css
I want only to update one css file with new hash and do not create new files.
My webpack configuration
const themeA = `${mainDirectory}/commons/themeA.scss`;
const themeB = `${mainDirectory}/commons/scss/shop/themeB.scss`;
const themeC = `${mainDirectory}/commons/themeC.scss`;
const themeD = `${mainDirectory}/commons/scss/shop/themeD.scss`;
let sassFiles;
const themeMap = new Map([
['a', themeA],
['b', themeB],
['c', themeC],
['d', themeD],
]);
if (theme === undefined) {
sassFiles = [...themeMap.values()];
} else {
sassFiles = [
`${mainDirectory}/commons/${theme}.scss`,
`${mainDirectory}/commons/scss/shop/${theme}_shop.scss`,
];
}
module.exports = {
entry: [...sassFiles],
mode: 'development',
output: {
path: path.resolve(mainDirectory, 'commons'),
filename: 'hot/sassBundle.js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.scss$/,
sideEffects: true,
use: [
{
loader: 'file-loader',
options: {
minimize: true,
name: '[name].css',
// hashed css
// name: '[name].[hash].css',
outputPath: '/css',
},
},
{
loader: 'extract-loader',
},
{
loader: 'css-loader',
options: {
url: false,
sourceMap: !buildMode,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: !buildMode,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: !buildMode,
convertToAbsoluteUrls: false,
},
},
],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
},
{
test: /\.(woff|woff2|eot|ttf)$/,
loader: 'url-loader?limit=100000',
},
],
},
plugins: [
new webpack.SourceMapDevToolPlugin({}),
new ReplaceInFileWebpackPlugin([
{
dir: `${mainDirectory}/commons/css/`,
test: [/\.css$/],
rules: [
{
search: /\/\*# sourceURL.+\\*/g,
replace: '',
},
],
},
]),
],
};

MiniCssExtractPlugin + SplitChunksPlugin doesn't put scoped css into bundle

I'm using a Webpack 4 setup, where I'm trying to get all my css into a single bundle, including my scoped css from .vue files. I'm using MiniCssExtractPlugin to extract the css from the files and SplitChunksPlugin to bundle all the css into a single file. The problem is that my scoped css from .vue files is being put into separate css files.
My webpack config:
optimization: {
splitChunks: {
cacheGroups: {
common: {
test: /[\\/]node_modules[\\/].*\.js$/,
name: 'common',
chunks: 'all'
},
styles: {
test: /\.css$/,
filename: '[name]-[contenthash].css',
chunks: 'all',
enforce: true
}
}
}
},
module: {
rules: [
{
enforce: 'pre',
test: /\.(js|vue)$/,
exclude: /node_modules/,
use: 'eslint-loader'
},
{
test: /\.vue$/,
use: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.(sa|sc|c)ss$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 3,
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
indentedSyntax: true
}
}
]
}
]
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
}),
new PurifyCSSPlugin({
paths: glob.sync([
path.join(__dirname, 'templates/**/*.html'),
path.join(__dirname, 'assets/js/**/*.js'),
path.join(__dirname, 'assets/js/**/*.vue')
]),
purifyOptions: {
info: true,
minify: true,
rejected: true
}
})
],
resolve: {
alias: {
jquery: 'jquery/src/jquery',
'vue': 'vue/dist/vue.js'
},
extensions: ['.js', '.vue', '.json', '.sass']
}
Output:
scoped-css-from-a-vue-file.03371a6565c9f56951dd.css // (172 bytes)
main-css-bundle-from-sass-files.af5152091d41a56d9bdd.css // (303 KiB)

webpack not getting seperate css file for extract text plugin

Hi everyone I am using web-pack to try to replace a gulp build that was overly complicated. but was running into some issues when it came to the Css. We are using sass. We also have a project structure that has the sass files right by each of the the angular component so that means for every class there is a separate folder. Currently our gulp magically goes into the folders and downloads the sass.
But I can't get the extract-text-webpack-plugin to output me a separate Css file the source I am using for trying to do this is.
https://itnext.io/sharing-sass-resources-with-sass-resources-loader-and-webpack-ca470cd11746
here is my code
const config = {
entry: {
'app': './app/app.js',
// 'vendor': './src/vendor.module.js'
},
devtool: 'source-map',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist/dev')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: [ 'ng-annotate-loader', 'babel-loader' ]
},
// for fixing of loading bootstrap icon files
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
loader: 'url-loader?limit=10000',
options: {
name: './fonts/[name].[ext]'
}
},
{
test: /\.(eot|ttf)$/,
loader: 'file-loader',
options: {
name: './fonts/[name].[ext]'
}
},
{
test: /\.html$/,
loader: 'html-loader',
options: {
attrs: [ 'attrs=false' ]
}
},
{
test: /\.scss$/,
use: ExtractTextWebpackPlugin.extract({
fallback: 'style-loader',
filename: path.resolve(__dirname, 'dist/dev') + 'app.css',
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
},
{
loader: 'sass-resources-loader',
options: {
resources: require(path.join(process.cwd(), './app/appscss.js'))
}
}
]
}),
},
/* ,
{
test: /\.(scss)$/,
use: ExtractTextWebpackPlugin.extract({
use: [
{
loader: "css-loader",
options: {
minimize: true
}
},
{
loader: "sass-loader"
}
]
})
} */
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
comments: false,
sourceMap: true,
}),
new ExtractTextWebpackPlugin('app.css', {
allChunks: true
}),
new webpack.DefinePlugin({
GULP_REPLACE_ENV_URL: JSON.stringify(environementUrl())
})
],
devServer: {
port: 5000,
contentBase: path.resolve(__dirname, 'dist/dev'),
historyApiFallback: true,
// needed since we set api to something other than host
// http://flummox-engineering.blogspot.com/2017/10/webpack-dev-server-invalid-host-header-host-0.0.0.0-not-working-npm-dev-server.html
disableHostCheck: true
}
};
module.exports = config;
Any help would be much appreciated.

React-filepond external CSS is not being applied. I suspect it's a webpack issue

This is beating my head in. I'm using react-filepond module in my react app, but the external CSS is not being applied. The module works but has no style. I suspect it's a loader issue in webpack but I'm still learning webpack and probably missed something. Thanks!
Here are the imports as per react-filepond:
import { FilePond } from 'react-filepond';
import 'filepond/dist/filepond.css'; // located in node_modules
Here's my webpack.config.js.
I'm using webpack 3.12.0
const path = require('path');
const autoprefixer = require('autoprefixer');
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
chunkFilename: '[id].js',
publicPath: '/'
},
resolve: {
extensions: ['.js', 'jsx', '.css']
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: ['react']
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
autoprefixer({
browsers: [
"> 1%",
"last 2 versions"
]
})
]
}
}
]
},
{
test: /\.(png|jpe?g|gif)$/,
loader: 'url-loader?limit=8000&name=images/[name].[ext]'
}
]
},
devServer: {
historyApiFallback: true
},
plugins: [
new htmlWebpackPlugin({
template: __dirname + '/src/index.html',
inject: 'body',
filename: 'index.html'
})
]
};
I found the solution for others wondering about this.
Issue: I am using CSS modules in react (note the line modules:true in the configuration of my css-loader in my webpack-config.js
The external react module I was trying to use does NOT use CSS modules.
Solution:
create a second rule for external CSS. Thus I have one for my CSS (as in the source above) and then I added this rule:
{
/* External CSS from node_modules */
test: /\.css$/,
include: /node_modules/,
loader: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
},
}
]
},
Most importantly, I did NOT turn on CSS Modules
And then, in my other CSS rule, I added:
exclude: /node_modules/,

Resources