I'm getting this problem of duplicated css files under dist/css
My webpack configuration is as follows:
webpack.base.config.js
/* eslint quote-props: ['off'] */
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isProduction = process.env.NODE_ENV === 'production';
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/entry.client.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'#': resolve('src'),
'$': resolve('node_modules'),
'i18n': resolve('i18n'),
'#fortawesome/fontawesome-free-solid$': '#fortawesome/fontawesome-free-solid/shakable.es.js',
'#fortawesome/fontawesome-free-regular$': '#fortawesome/fontawesome-free-regular/shakable.es.js',
'#fortawesome/fontawesome-free-brand$': '#fortawesome/fontawesome-free-brand/shakable.es.js',
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.s?[ac]ss$/,
use: [
!isProduction ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('i18n'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /^(?!.*-inline).*\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /.*(?:\.(gif|png|jpe?g)|(-inline\.svg))$/i,
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
},
},
}
]
},
{
test: /-inline\.svg$/,
loader: 'svg-inline-loader',
options: {
removeSVGTagAttrs: false,
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
And webpack.prod.config.js
const webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all"
},
},
},
concatenateModules: true,
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
dead_code: true,
drop_console: true,
},
output: {
comments: false,
},
ecma: 6,
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
new OptimizeCSSAssetsPlugin({}),
],
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new CleanPlugin([dist], {
root: path.resolve(__dirname, '..'),
}),
new VueLoaderPlugin(),
new Dotenv({
path: path.resolve('.env')
}),
new MiniCssExtractPlugin({
filename: utils.assetsPath('css/[name].[hash].css'),
chunkFilename: utils.assetsPath('css/[id].[hash].css')
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: process.env.NODE_ENV === 'testing'
? 'index.html'
: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode(a, b) {
return (a.names[0] < b.names[0]) ? 1 : -1;
},
}),
// Generates manifest to configure pwa options
new WebpackPwaManifest({
name: 'My Company',
short_name: 'MyCompany',
description: 'Vue + Vuetify progressive app with Loopback server',
theme_color: "#004d40",
start_url: "./",
background_color: "#fafafa",
icons: [
{
"src": path.resolve('src/assets/logo.png'),
"sizes": "200x200",
"type": "image/png"
},
{
"src": path.resolve('src/assets/logo-512px.png'),
"sizes": "512x512",
"type": "image/png"
},
]
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// copy custom static assets
new WorkboxPlugin.GenerateSW({
swDest: path.join(config.build.assetsRoot, 'sw.js'),
clientsClaim: true,
skipWaiting: true,
exclude: [/\.html$/],
}),
new HtmlCriticalPlugin({
base: path.resolve(__dirname, '../dist'),
src: 'index.html',
dest: 'index.html',
inline: true,
minify: true,
extract: true,
width: 375,
height: 565,
penthouse: {
blockJSRequests: false,
}
})
]
})
// This option is enabled by default, if nginx or another server already implement
// gzip you can disable it in config/index.js
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.productionBrotli) {
const BrotliWebpackPlugin = require('brotli-webpack-plugin');
webpackConfig.plugins.push(
new BrotliWebpackPlugin({
asset: '[path].br[query]',
test: new RegExp(
'\\.(' +
config.build.productionBrotliExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
I checked the contents of both files and one is compressed by OptimizeCSSAssetsPlugin and the other is not.
I also want to ask why this css chunks doesn't have the brotli and gziped version.
Thanks
Related
I would be grateful for any help or advice. I have compiled the configuration of the webpack, everything works in production mode, but the webpack-dev-server does not see static files and gives the error "Cannot get /". How can I solve this problem? Thank you in advance for the answer. Here is my webpack.config.js
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
// const autoprefixer = require("autoprefixer");
const isProd = process.env.NODE_ENV === "production";
const isDev = !isProd;
const optimization = () => {
const config = {
runtimeChunk: "single",
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
priority: -10,
chunks: "all",
},
},
},
};
if (isProd) {
config.minimizer = [
new CssMinimizerWebpackPlugin(),
new TerserWebpackPlugin(),
];
}
return config;
};
module.exports = {
entry: path.resolve(__dirname, "src", "index.js"),
mode: isProd ? "production" : "development",
output: {
path: path.join(__dirname, "build"),
filename: "[name].[contenthash:8].js",
chunkFilename: "[name].[contenthash:8].js",
publicPath: "./",
},
devtool: isDev ? "source-map" : false,
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.(eot|ttf|woff|woff2)(\?\S*)?$/,
loader: "file-loader",
options: {
name: "[name][contenthash:8].[ext]",
},
},
{
test: /\.(png|jpe?g|gif|webm|mp4|svg)$/,
loader: "file-loader",
options: {
name: "[name][contenthash:8].[ext]",
outputPath: "assets/img",
esModule: false,
},
},
{
test: /\.s?css$/,
use: [
"style-loader",
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: false,
},
},
"css-loader",
],
},
],
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[contenthash:8].css",
chunkFilename: "[name].[contenthash:8].css",
}),
new HTMLWebpackPlugin({
template: path.join(__dirname, "src", "public", "index.html"),
filename: "start-page.html",
alwaysWriteToDisk: true,
minify: {
collapseWhitespace: isProd,
},
}),
new CleanWebpackPlugin(),
],
resolve: {
alias: {
vue: "#vue/runtime-dom",
},
extensions: ["*", ".js", ".vue", ".json"],
},
optimization: optimization(),
devServer: {
compress: true,
port: 9000,
hot: true,
client: {
overlay: true,
},
},
};
Here is a project folders
According to the doc, you need to set the publicPath in the devServer.
Look at the documentation
module.exports = {
//...
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
},
};
After saving changes, webpack takes on average ~23 seconds to build. This is the output while it is building. It goes all the way up to 6695 modules before completing.
18% building 915/944 modules 29 active ...terial-ui/icons/esm/WallpaperTwoTone.js
How do I speed up the build time? Any help would be greatly appreciated.
node: 11.6
webpack: ^4.29.6
webpack.common.js:
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const ConcatPlugin = require('webpack-concat-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HappyPack = require('happypack');
// const HtmlWebpackPlugin = require('html-webpack-plugin')
const plugins = [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].css',
}),
new HappyPack({
id: 'html',
threads: 1,
loaders: ['html-loader']
}),
new HappyPack({
id: 'js',
threads: 1,
verbose: false,
debug: false,
loaders: ['babel-loader?cacheDirectory']
}),
new HappyPack({
id: 'css',
threads: 1,
loaders: ['css-loader']
}),
new HappyPack({
id: 'less',
threads: 1,
loaders: ['css-loader', 'less-loader']
}),
new ConcatPlugin({
sourceMap: false,
outputPath: 'css',
fileName: 'framework.css',
filesToConcat: [
'./node_modules/bootstrap/dist/css/bootstrap.css',
'./node_modules/material-design-icons-iconfont/dist/material-design-icons.css',
'./node_modules/ui-select/dist/select.min.css',
'./node_modules/angular-ui-bootstrap/dist/ui-bootstrap-csp.css',
'./node_modules/angular-loading-bar/build/loading-bar.css',
'./node_modules/typeface-roboto/index.css'
],
attributes: {
async: true
}
}),
new ConcatPlugin({
sourceMap: false,
outputPath: 'js',
fileName: 'angular.bundle.min.js',
filesToConcat: [
'./node_modules/jquery/dist/jquery.js',
'./node_modules/angular/angular.js',
'./node_modules/angular-route/angular-route.min.js',
'./node_modules/angular-cookies/angular-cookies.min.js',
'./node_modules/angular-resource/angular-resource.min.js',
'./node_modules/angular-sanitize/angular-sanitize.min.js',
'./node_modules/angular-animate/angular-animate.min.js',
'./node_modules/bootstrap/dist/js/bootstrap.js'
],
attributes: {
async: true
}
}),
new ConcatPlugin({
sourceMap: false,
outputPath: 'js',
fileName: 'lib.js',
filesToConcat: [
'./node_modules/ui-select/dist/select.min.js',
'./node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js',
'./node_modules/angular-ui-mask/dist/mask.min.js',
'./node_modules/angular-ui-indeterminate/dist/indeterminate.min.js',
'./node_modules/angular-ui-event/dist/event.min.js',
'./node_modules/angular-ui-validate/dist/validate.min.js',
'./node_modules/angular-ui-uploader/dist/uploader.min.js',
'./node_modules/angular-input-masks/releases/angular-input-masks-standalone.js',
'./node_modules/angular-file-saver/dist/angular-file-saver.bundle.js',
'./node_modules/angular-loading-bar/build/loading-bar.js',
'./node_modules/angular-pageslide-directive/dist/angular-pageslide-directive.js',
'./node_modules/ng-idle/angular-idle.js'
],
attributes: {
async: true
}
}),
new CopyPlugin([
{
from: './node_modules/bootstrap/dist/fonts/*',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'build', 'fonts'),
flatten: true
},
{
from: './node_modules/material-design-icons-iconfont/dist/fonts/*',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'build', 'css', 'fonts'),
flatten: true
},
{
from: './node_modules/typeface-roboto/files/*',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'build', 'css', 'files'),
flatten: true
},
{
from: './static_files/img/*.svg',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'build', 'img'),
flatten: true
},
{
from: './static_files/img/*.ico',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static'),
flatten: true
},
{
from: './static_files/pdf',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'pdf'),
flatten: true
},
{
from: './static_files/fonts',
to: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'fonts'),
flatten: true
}
])
];
const rules = [
{
test: /\.html$/,
loader: 'happypack/loader?id=html',
exclude: /node_modules/
},
{
test: /\.svg$/,
loader: 'svg-url-loader',
options: {
iesafe: true
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'happypack/loader?id=css']
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'happypack/loader?id=less']
},
{
test: /\.(png|woff|woff2|eot|ttf)$/,
loader: 'url-loader?limit=100000'
},
{
test: /\.(ts|tsx)?$/,
include: [
path.resolve(__dirname, 'js'),
path.resolve(__dirname, 'node_modules/array-move')
],
exclude: /e2e/,
use: [
{
loader: 'happypack/loader?id=js'
},
{
loader: 'ts-loader',
options: {
transpileOnly: true,
experimentalWatchApi: true,
}
},
{
loader: 'tslint-loader',
options: {
emitErrors: true,
formatter: 'codeFrame'
}
},
],
enforce: 'pre'
},
{
test: /\.m?jsx?$/,
include: [
path.resolve(__dirname, 'js'),
path.resolve(__dirname, 'node_modules/array-move')
],
exclude: /e2e/,
loader: 'happypack/loader?id=js'
}
];
module.exports = {
devtool: 'source-map',
cache: false,
parallelism: 30,
context: path.resolve(__dirname, ''),
entry: {
main: ['#babel/polyfill', './js/app.ts']
},
output: {
path: path.resolve(__dirname, '..', 'webapp', 'WEB-INF', 'static', 'build'),
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
sourceMapFilename: '[name].bundle.js.map'
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 1,
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "initial"
},
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true,
}
}
}
},
module: {
rules: rules
},
stats: {
colors: true
},
resolve: {
extensions: ['.tsx', '.ts', '.js', 'jsx', '.css', '.less']
},
externals: {
angular: 'angular'
},
performance: {
hints: false
},
plugins: plugins
};
wepback.dev.js:
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common');
const CleanWebpackPlugin = require('clean-webpack-plugin');
process.traceDeprecation = false; // set to true if you want to find dep that is deprecated.
module.exports = merge(commonConfig, () => {
return {
mode: 'development',
performance: {
hints: 'warning'
},
watchOptions: {
aggregateTimeout: 300,
poll: 1000,
ignored: /node_modules/
},
plugins: [
new CleanWebpackPlugin() // Only for watch!
]
}
});
Script we use to start the build:
"hot_build": "npm run clean:static && webpack -w --mode development --config webpack.dev.js --progress"
I have a question why practically the dev version is the same as in the prod.
Only the prod version should contain MiniCssExtractPlugin.loader version dev style-loader.
dev
{
test: /\.(css|sass|scss)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
},
},
...
prod
{
test: /\.(css|sass|scss)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 2
},
},
...
The same happens with the optimization section, you should only use it in the prod version.
Next issue #babel/polyfill is deprecated. Delete #babel/polyfill and add core-js instead and configure .babelrc accordingly.
The biggest problem is that every change you delete the built folders and everything is copied again by 'CopyPlugin'. Isn't it better to use webpack-dev-server for this?
And rebuild everything so that webpack.dev.js uses this.
Then the first time it will take a long time to build up but the changes you will make later will be very quick.
Please tell me, here is the config.js file in it, the exported variable with a reference to the API, but there is an API for the dev version, and there is a separate API for the prod version, as I understand it, you need to make 2 separate files, but how to make it in webpack so that when dev he used one file, and prod another?
Below added webpack configuration files, a project on react.js (if that makes any difference :) )
rules.js
const rules = [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.eot(\?v=\d+.\d+.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
name: '[name].[ext]',
},
},
],
},
{
test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream',
name: '[name].[ext]',
},
},
],
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
name: '[name].[ext]',
},
},
],
},
{
test: /\.(jpe?g|png|gif|ico)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
];
export default rules;
webpack.config.common.babel.js
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import Dotenv from 'dotenv-webpack';
import rules from './utils/rules';
const config = (env) => {
const PUBLIC_PATH = env.production ? '/' : '/';
return {
entry: {
index: './src/index.js'
},
output: {
publicPath: PUBLIC_PATH,
path: path.resolve(process.cwd(), 'dist'),
filename: env.production ? '[name].js' : '[name].bundle.js'
},
resolve: {
alias: {
'react-dom': '#hot-loader/react-dom',
app: path.resolve(process.cwd(), 'src/app/'),
store: path.resolve(process.cwd(), 'src/store/'),
ui: path.resolve(process.cwd(), 'src/ui/')
},
extensions: ['*', '.js', '.jsx', '.json', 'scss']
},
module: {
rules
},
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
plugins: [
new Dotenv({
systemvars: true
}),
new HtmlWebpackPlugin({
template: 'public/index.html',
favicon: 'public/favicon.ico',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
})
]
};
};
export default config;
webpack.config.dev.babel.js
import webpack from 'webpack';
import path from 'path';
import merge from 'webpack-merge';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import common from './webpack.config.common.babel';
const config = env => merge(common(env), {
devtool: 'cheap-module-eval-source-map',
target: 'web',
mode: 'development',
optimization: {
minimize: false
},
performance: {
hints: false
},
devServer: {
historyApiFallback: true,
contentBase: '../dist',
port: 3009,
hot: true,
clientLogLevel: 'none'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
{
test: /(\.css|\.scss|\.sass)$/,
use: [
{
loader: 'style-loader',
options: { singleton: true }
},
{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
/* eslint-disable global-require */
plugins: () => [require('autoprefixer')],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'src', 'scss')]
}
}
]
}
]
},
plugins: [
new webpack.NamedModulesPlugin(),
// new BundleAnalyzerPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.HotModuleReplacementPlugin()
]
});
export default config;
webpack.config.common.babel.js
import path from 'path';
import webpack from 'webpack';
import merge from 'webpack-merge';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import common from './webpack.config.common.babel';
const config = env => merge(common(env), {
devtool: 'source-map',
target: 'web',
mode: 'production',
optimization: {
minimize: true,
nodeEnv: 'production',
sideEffects: true,
concatenateModules: true,
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
module: {
rules: [
{
test: /(\.css|\.scss|\.sass)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
/* eslint-disable global-require */
plugins: () => [require('cssnano'), require('autoprefixer')],
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'src', 'scss')],
sourceMap: true,
},
},
],
},
],
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash].css',
})
],
});
export default config;
You can define two different npm scripts and use the --config flag for custom files (/file-paths):
"build:dev": "webpack --config ./webpack.client.dev.js",
"build:prod": "webpack --config ./webpack.client.prod.js"
For the common you have to use webpack-merge, for example:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
...
});
When i bundle split my webpack vendor ( node_modules ) my main.js file prints out the following message. When i disable bundle splitting, it works. But when I bundle split and i console.log the module (responsiveMenu) It complains about callbacks. I'm using the webpack bundle-loader to create my code splitted bundle.
my dist folder ususlly ends up like the following
main.js
bundle.1.js
bundle.2.js
bundle.x.js etc
below is my webpack file, the top of main.js and responsiveMenu.js, my babelrc.js is also present
babelrc.js
api.cache(true);
const presets = [['#babel/preset-env', {
"targets": {
"esmodules": true
}
}]];
const plugins = ['#babel/plugin-proposal-export-default-from', '#babel/plugin-transform-runtime'];
return {
presets,
plugins,
};
};
Webpack config
const webpack = require('webpack');
const minimatch = require('minimatch');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const TerserPlugin = require('terser-webpack-plugin');
const LiveReloadPlugin = require('webpack-livereload-plugin');
const globImporter = require('node-sass-glob-importer');
const CopyPlugin = require('copy-webpack-plugin');
const HappyPack = require('happypack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const PhpManifestPlugin = require('webpack-php-manifest');
const phpManifest = new PhpManifestPlugin();
const devMode = 'development' === process.env.NODE_ENV;
module.exports = {
resolve: {
alias: {
assets: path.resolve(__dirname, 'assets'),
fonts: path.resolve(__dirname, 'assets/fonts'),
modernizr$: path.resolve(__dirname, './.modernizrrc')
}
},
mode: process.env.NODE_ENV,
entry: {
'js/main': './assets/js/scripts/main.js',
'css/style': './assets/scss/style.scss'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
chunkFilename: 'js/bundle.[id].js',
publicPath: 'wp-content/themes/strafe-boilerplate/dist/'
},
devtool: devMode ? 'source-map' : 'cheap-eval-source-map',
performance: {
maxAssetSize: 1000000
},
optimization: {
minimize: !devMode,
removeAvailableModules: !devMode,
removeEmptyChunks: !devMode,
splitChunks: !devMode ? {
maxInitialRequests: Infinity,
minSize: 0,
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
name: 'vendors',
vendors: {
test: /node_modules/,
name(module, chunks, cacheGroupKey) {
const moduleFileName = module
.identifier()
.split('/')
.reduceRight(item => item);
const allChunksNames = chunks.map(item => item.name).join('');
let name = `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
return name;
}
},
}
} : false,
minimizer: !devMode ? [
new TerserPlugin({
sourceMap: !devMode,
cache: !devMode,
parallel: true
})
] : []
},
stats: {
assets: !devMode,
builtAt: !devMode,
children: false,
chunks: false,
colors: true,
entrypoints: !devMode,
env: false,
errors: !devMode,
errorDetails: false,
hash: false,
maxModules: 20,
modules: false,
performance: !devMode,
publicPath: false,
reasons: false,
source: false,
timings: !devMode,
version: false,
warnings: !devMode
},
module: {
rules: [
!devMode ?
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
include: path.resolve(__dirname, 'assets/js'),
use: ['happypack/loader', {
loader : 'bundle-loader'
}]
} : {
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
include: path.resolve(__dirname, 'assets/js'),
use: ['happypack/loader']
},
{
test: /\.(sa|sc|c)ss$/,
exclude: /(node_modules|bower_components)/,
include: path.resolve(__dirname, 'assets/scss'),
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
url: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
{
loader: 'resolve-url-loader',
options: {
engine: 'postcss',
root: path.resolve(__dirname, 'assets')
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
importer: globImporter(),
outputPath: 'css',
debug: true
}
}
]
},
{
test: /\.(png|jpg|gif)$/,
include: path.resolve(__dirname, 'assets/img'),
use: [
{
loader: 'file-loader',
options: {
outputPath: 'img',
name: '[folder]/[name].[ext]',
publicPath: '../img/',
useRelativePath: true
}
}
]
},
{
test: /\.(eot|woff|woff2|ttf)([\?]?.*)$/,
include: path.resolve(__dirname, 'assets/fonts'),
use: [
{
loader: 'file-loader',
options: {
outputPath: 'fonts',
name: '[folder]/[name].[ext]',
debug: true,
publicPath: '../fonts/',
useRelativePath: true
}
}
]
},
{
test: /\.(svg)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'img',
name: '[folder]/[name].[ext]',
publicPath: '../img/',
useRelativePath: true
}
},
{
loader: 'svgo-loader',
options: {
plugins: [
{ removeTitle: false },
{ convertColors: { shorthex: false } },
{ convertPathData: false },
{ removeViewBox: false }
]
}
}
]
},
{
test: /\.modernizrrc.js$/,
use: ['modernizr-loader']
},
{
test: /\.modernizrrc(\.json)?$/,
use: ['modernizr-loader', 'json-loader']
}
]
},
plugins: [
new HappyPack({
loaders: [
{
loader: 'babel-loader',
options: {
presets: [ '#babel/preset-react' ]
}
}
]
}),
new webpack.ProvidePlugin({
'Promise': 'bluebird'
}),
phpManifest,
devMode && new FriendlyErrorsPlugin({
clearConsole: false
}),
new CleanWebpackPlugin(['dist'], {
cleanStaleWebpackAssets: false,
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: !devMode ? 'css/bundle.[id].css' : false
}),
!devMode && new ImageminPlugin({
test: /\.(jpe?g|png|gif)$/i,
cacheFolder: './imgcache'
}),
devMode && new LiveReloadPlugin(),
new CopyPlugin([{ from: 'assets/img', to: 'img' }])
].filter(Boolean)
};
responsiveMenu.js
function responsiveMenu() {
// Responsive Menu
// Setup Doc, call out functions
function init() {
const toggleTrigger = $('.mobile-toggle');
const menuWrap = $('.menu-responsive');
menuPositionSetup(menuWrap);
triggerMenu(toggleTrigger, menuWrap);
childItemsHandler();
}
function menuPositionSetup(menu) {
let headerHeight = $('.header').outerHeight(true);
$(menu).css('top', headerHeight);
$(menu).css({ height: 'calc(100% - ' + headerHeight + 'px)' });
}
function triggerMenu(toggle, menu) {
$(toggle).on('click', function(e) {
mobileToggle($(this), menu);
});
}
function mobileToggle(trigger, menu) {
$(menu).toggleClass('is-active');
$(trigger).toggleClass('is-active');
$('body').toggleClass('is-overflow-h');
}
function childItemsHandler() {
$('.menu-item-has-children > a').on('click', function(e) {
if ($(this).hasClass('is-active')) {
// Go to the link
} else {
// Open the sub menu
$(this).addClass('is-active');
$(this)
.parent()
.find('.sub-menu')
.slideDown();
e.preventDefault();
}
});
}
};
export default responsiveMenu
main.js
import * as $ from 'jquery';
import responsiveMenu from './responsiveMenu';
console.log(responsiveMenu)
$(document).ready(function() {
init();
});
function init() {
responsiveMenu()
}
I've read a lot about this error, and I can't find the solution.
hot reloading is not working, and I have an error:
EventSource's response has a MIME type ("text/html") that is not
"text/event-stream". Aborting the connection.
"webpack": "^2.5.1",
"webpack-dev-middleware": "^1.8.4",
"webpack-hot-middleware": "^2.22.1",
I've found https://github.com/webpack-contrib/webpack-hot-middleware/issues/26 this but it didn't help me
Do you have any idea how to get rid of this error?
My webpack.config.js
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const project = require('../project.config')
const inProject = path.resolve.bind(path, project.basePath)
const inProjectSrc = (file) => inProject(project.srcDir, file)
const __DEV__ = project.env === 'development'
const __TEST__ = project.env === 'test'
const __PROD__ = project.env === 'production'
const config = {
entry: {
normalize: [
inProjectSrc('normalize'),
],
main: [
inProjectSrc(project.main),
],
},
devtool: project.sourcemaps ? 'source-map' : false,
output: {
path: inProject(project.outDir),
filename: __DEV__ ? '[name].js' : '[name].[chunkhash].js',
publicPath: project.publicPath,
},
resolve: {
modules: [
inProject(project.srcDir),
'node_modules',
],
extensions: ['*', '.js', '.jsx', '.json'],
},
externals: project.externals,
module: {
rules: [],
},
plugins: [
new webpack.DefinePlugin(Object.assign({
'process.env': { NODE_ENV: JSON.stringify(project.env) },
__DEV__,
__TEST__,
__PROD__,
}, project.globals))
],
}
// JavaScript
// ------------------------------------
config.module.rules.push({
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
query: {
cacheDirectory: true,
plugins: [
'babel-plugin-transform-class-properties',
'babel-plugin-syntax-dynamic-import',
[
'babel-plugin-transform-runtime',
{
helpers: true,
polyfill: false, // we polyfill needed features in src/normalize.js
regenerator: true,
},
],
[
'babel-plugin-transform-object-rest-spread',
{
useBuiltIns: true // we polyfill Object.assign in src/normalize.js
},
],
],
presets: [
'babel-preset-react',
['babel-preset-env', {
modules: false,
targets: {
ie9: true,
},
uglify: true,
}],
]
},
}],
})
// Styles
// ------------------------------------
const extractStyles = new ExtractTextPlugin({
filename: 'styles/[name].[contenthash].css',
allChunks: true,
disable: __DEV__,
})
config.module.rules.push({
test: /\.css$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader']
})
config.module.rules.push({
test: /\.(sass|scss)$/,
loader: extractStyles.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
sourceMap: project.sourcemaps,
minimize: {
autoprefixer: {
add: true,
remove: true,
browsers: ['last 2 versions'],
},
discardComments: {
removeAll : true,
},
discardUnused: false,
mergeIdents: false,
reduceIdents: false,
safe: true,
sourcemap: project.sourcemaps,
},
},
},
{
loader: 'sass-loader',
options: {
sourceMap: project.sourcemaps,
includePaths: [
inProjectSrc('styles'),
],
},
}
],
})
})
config.plugins.push(extractStyles)
// Images
// ------------------------------------
config.module.rules.push({
test : /\.(png|jpg|gif)$/,
loader : 'url-loader',
options : {
limit : 8192,
},
})
// Fonts
// ------------------------------------
;[
['woff', 'application/font-woff'],
['woff2', 'application/font-woff2'],
['otf', 'font/opentype'],
['ttf', 'application/octet-stream'],
['eot', 'application/vnd.ms-fontobject'],
['svg', 'image/svg+xml'],
].forEach((font) => {
const extension = font[0]
const mimetype = font[1]
config.module.rules.push({
test : new RegExp(`\\.${extension}$`),
loader : 'url-loader',
options : {
name : 'fonts/[name].[ext]',
limit : 10000,
mimetype,
},
})
})
// HTML Template
// ------------------------------------
config.plugins.push(new HtmlWebpackPlugin({
template: inProjectSrc('index.html'),
inject: true,
minify: {
collapseWhitespace: true,
},
}))
// Development Tools
// ------------------------------------
if (__DEV__) {
config.entry.main.push(
`webpack-hot-middleware/client.js?path=${config.output.publicPath}__webpack_hmr`
)
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
)
}
// Bundle Splitting
// ------------------------------------
if (!__TEST__) {
const bundles = ['normalize', 'manifest']
if (project.vendors && project.vendors.length) {
bundles.unshift('vendor')
config.entry.vendor = project.vendors
}
config.plugins.push(new webpack.optimize.CommonsChunkPlugin({ names: bundles }))
}
// Production Optimizations
// ------------------------------------
if (__PROD__) {
config.plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false,
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: !!config.devtool,
comments: false,
compress: {
warnings: false,
screw_ie8: true,
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
},
})
)
}
module.exports = config