Webpack in watch mode takes too long to build (~23s) after changes - javascript

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.

Related

Webpack shows white screen

After I've added webpack in my react app, once the webpack started, it shows the white screen, and the whole bundle gets downloaded at one time. Even though I've done code splitting in react.
my webpack config:
/* eslint-disable no-un`enter code here`def */
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlWebpackInjectPreload = require("#principalstudio/html-webpack-inject-preload");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: path.resolve(__dirname, './index.js'),
devtool: "inline-source-map",
output: {
path: path.resolve(__dirname, "build"),
filename: "bundle.js",
publicPath: '/'
},
devServer: {
contentBase: path.resolve(__dirname, 'build'),
port: 4000,
hot: true,
},
resolve: {
extensions: [".*",".js", ".jsx", ".css"],
fallback: {
stream: false
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
use: ["babel-loader"],
},
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.module\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: { importLoaders: 2, modules: true },
},
"postcss-loader",
],
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
use: [
{
loader: "url-loader",
options: {
limit: 8192,
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('./index.html')
}),
new CleanWebpackPlugin(),
new HtmlWebpackInjectPreload({
files: [
{
match: /.*\.woff2$/,
attributes: { as: "font", type: "font/woff2", crossorigin: true },
},
{
match: /\.[a-z-0-9]*.css$/,
attributes: { as: "style" },
},
],
}),
],
};
The attached bundle image also gets larger up to 19MB. What's going wrong I'm not able to figure out.

React + Webpack: "RuntimeError: memory access out of bounds"

I've recently developed a react + webpack application that is deployed using
AWS Amplify. I've been getting a strange error that is logged on Sentry, but can't
find a way to replicate the bug.
RuntimeError: memory access out of bounds
I suspect it has something to do with my webpack configuration, but I don't know whats wrong.
I never used wasm, but it seems to be related to it.
Here is my production level webpack configuration.
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const env = require('../environment/prod.env');
const commonPaths = require('./paths');
const webpack = require('webpack');
const SentryWebpackPlugin = require('#sentry/webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map',
output: {
filename: `${commonPaths.jsFolder}/[name].[hash].js`,
path: commonPaths.outputPath,
chunkFilename: `${commonPaths.jsFolder}/[name].[chunkhash].js`,
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin(),
],
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'initial',
},
async: {
test: /[\\/]node_modules[\\/]/,
name: 'async',
chunks: 'async',
minChunks: 4,
},
},
},
runtimeChunk: true,
},
module: {
rules: [
{
test: /\.scss$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': env,
}),
new MiniCssExtractPlugin({
filename: `${commonPaths.cssFolder}/[name].css`,
chunkFilename: `${commonPaths.cssFolder}/[name].css`,
}),
],
};
Here is also my common webpack configuration
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const commonPaths = require('./paths');
module.exports = {
context: commonPaths.srcPath,
entry: commonPaths.entryPath,
output: {
path: commonPaths.outputPath,
filename: 'js/[name].js',
},
resolve: {
extensions: ['.ts', '.js', '.html', '.vue'],
alias: {
'~': commonPaths.srcPath,
},
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: commonPaths.srcPath,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: ['react-hot-loader/babel'],
},
},
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/img/[name].[hash:8].[ext]',
publicPath: '/',
},
},
],
},
{
test: /\.(mp3)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/audio/[name].[hash:8].[ext]',
publicPath: '/',
},
},
],
},
{
test: /\.(ttc|ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[hash:8].[ext]',
publicPath: '/',
},
},
],
},
],
},
serve: {
content: commonPaths.entryPath,
dev: {
publicPath: commonPaths.outputPath,
},
open: true,
},
resolve: {
modules: ['src', 'node_modules', 'bower_components', 'shared', '/shared/vendor/modules'],
extensions: ['*', '.js', '.jsx'],
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({
favicon: './icon.png',
template: commonPaths.templatePath,
}),
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'async',
}),
],
};
Any help would really help. Thanks.
Try to restart your server and make sure you reinstall node modules.

Separate the config API with dev and prod webpack

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',
...
});

Duplicated css files using webpack MiniCssExtractPlugin and OptimizeCSSAssetsPlugin

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

Webpack babel config for both server and client javascript?

I'm trying to figure out how to have a single webpack config file that works for transforming both server (node.js) js and client js with the es2015 preset. Currently I have to specifically set "target: 'node'" for it to correctly process node-based files. If I don't, then webpack does the transformation based on the default "target: 'web'". It then reports errors because the 'mysql' module being imported clearly won't work for web.
How can I unify both into the same config file so that server and client js will be transformed separately? Or do I need separate configs entirely?
Sample webpack.config.js
'use strict';
var path = require('path');
var webpack = require('webpack');
module.exports = {
target: 'node',
resolve: {
root: path.resolve(__dirname),
extensions: ['', '.js']
},
entry: [
'babel-polyfill',
'./index.js',
],
output: {
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015']
}
}
]
}
};
Sample js code
import 'babel-polyfill';
import mysql from 'mysql';
class Test {
constructor () {
}
get read () {
}
};
You can pass multiple configs for Webpack to process at the same time. Simply return an array of configuration objects.
export default [
{
// config 1 here
},
{
// config 2 here
},
];
Extra tip: if you use .babel.js as extension for your config file, Webpack will run it through Babel for you, which allows you to use ES6 in your Webpack config.
Bonus: the snippet below
// Source: https://gist.github.com/Ambroos/f23d517a4261e52b4591224b4c8df826
import webpack from 'webpack';
import path from 'path';
import CleanPlugin from 'clean-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import AssetsPlugin from 'assets-webpack-plugin';
import CompressionPlugin from 'compression-webpack-plugin';
import autoprefixer from 'autoprefixer';
import rucksack from 'rucksack-css';
import cssnano from 'cssnano';
import moment from 'moment';
const sharedPlugins = [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /nl/),
new webpack.optimize.AggressiveMergingPlugin({}),
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.optimize.UglifyJsPlugin({
compress: {
drop_console: true,
screw_ie8: true,
sequences: true,
properties: true,
dead_code: true,
drop_debugger: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
if_return: true,
join_vars: true,
cascade: true,
negate_iife: true,
hoist_funs: true,
warnings: false,
},
mangle: {
screw_ie8: true,
},
output: {
screw_ie8: true,
preamble: '/* Website - ' + moment().format() + ' */',
},
}),
];
const sharedServerPlugins = [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /nl/),
new webpack.optimize.AggressiveMergingPlugin({}),
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.optimize.UglifyJsPlugin({
compress: {
drop_console: false,
screw_ie8: true,
sequences: true,
properties: true,
dead_code: true,
drop_debugger: false,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
if_return: true,
join_vars: true,
cascade: true,
negate_iife: true,
hoist_funs: true,
warnings: false,
},
mangle: {
screw_ie8: true,
},
output: {
screw_ie8: true,
preamble: '/* Website - ' + moment().format() + ' */',
},
}),
];
const PATHS = {
build: path.resolve(__dirname, '..', 'build'),
sourcemaps: path.resolve(__dirname, '..', 'build', 'sourcemaps'),
browserSource: path.resolve(__dirname, '..', 'src', 'browser', 'index.js'),
browserBuild: path.resolve(__dirname, '..', 'build', 'browser'),
serverSource: path.resolve(__dirname, '..', 'src', 'server', 'index.js'),
serverAssetsSource: path.resolve(__dirname, '..', 'src', 'server', 'assets', 'index.js'),
serverBuild: path.resolve(__dirname, '..', 'build', 'server'),
};
export default [
// Browser
{
entry: { browser: PATHS.browserSource },
output: {
path: PATHS.browserBuild,
filename: 's/[chunkhash].js',
chunkFilename: 's/async-[chunkhash].js',
publicPath: '/',
sourceMapFilename: '../sourcemaps/browser/[file].map',
},
devtool: 'hidden-source-map',
plugins: [
new AssetsPlugin({
prettyPrint: true,
path: path.resolve(PATHS.build, 'browserAssets'),
filename: 'index.js',
processOutput: assets => `module.exports = ${JSON.stringify(assets, null, ' ')};`,
}),
new CleanPlugin([PATHS.browserBuild, PATHS.sourcemaps], path.resolve(PATHS.build, 'browserAssets')),
new ExtractTextPlugin('s/[contenthash].css'),
new CompressionPlugin({
asset: '{file}.gz',
algorithm: 'gzip',
regExp: /\.js$|\.html$|\.css$|\.svg$|\.eot$|\.xml$/,
threshold: 1400,
minRation: 0.8,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK_ENV: JSON.stringify('browser'),
APP_ENV: (process.env.APP_ENV && JSON.stringify(process.env.APP_ENV)) || undefined,
},
}),
].concat(sharedPlugins),
externals: [
{ browserConfig: 'var websiteBrowserConfig' },
{ programs: 'var programs' },
],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.json$/,
loader: 'json',
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract(
'style',
[
'css?importLoaders=2&localIdentName=css-module-[hash:base64]',
'postcss',
'sass',
]
),
},
{
test: /\.(gif|png|jpe?g|svg|ico)$/i,
loaders: [
'url?limit=1400&name=s/i/[sha512:hash:base64:16].[ext]',
'image-webpack?bypassOnDebug',
],
},
{
test: /isotope\-|fizzy\-ui\-utils|desandro\-|masonry|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports?define=>false&this=>window',
},
{
test: /flickity/,
loader: 'imports?define=>false&this=>window',
},
{
test: /node_modules\/unipointer/,
loader: 'imports?define=>undefined',
},
],
},
postcss: () => {
return [rucksack, autoprefixer, cssnano];
},
},
// Server assets
{
entry: { assets: PATHS.serverAssetsSource },
target: 'node',
output: {
path: PATHS.browserBuild,
libraryTarget: 'commonjs',
filename: '../serverAssets/index.js',
publicPath: '/',
},
plugins: [
// assetsWriter,
new CompressionPlugin({
asset: '{file}.gz',
algorithm: 'gzip',
regExp: /\.js$|\.html$|\.css$|\.svg$|\.eot$|\.xml$/,
threshold: 1400,
minRation: 0.8,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK_ENV: JSON.stringify('assets'),
APP_ENV: (process.env.APP_ENV && JSON.stringify(process.env.APP_ENV)) || undefined,
},
}),
].concat(sharedPlugins),
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.json$/,
loader: 'json',
},
{
test: /\.(gif|png|jpe?g|svg|ico)$/i,
loaders: [
'url?limit=1400&name=s/i/[sha512:hash:base64:16].[ext]',
'image-webpack?bypassOnDebug',
],
},
],
},
},
// Server
{
entry: PATHS.serverSource,
target: 'node',
output: {
path: PATHS.build,
libraryTarget: 'commonjs',
filename: 'server/server.js',
publicPath: '/s/',
sourceMapFilename: 'sourcemaps/browser/[file].map',
},
externals: [
{ serverAssets: '../serverAssets/index.js' },
{ browserAssets: '../browserAssets/index.js' },
{ vrtConfig: '../../env_vars.js' },
/^(?!\.|\/).+/i,
/webpack-assets\.json/,
],
plugins: [
new CleanPlugin(PATHS.serverBuild),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK_ENV: JSON.stringify('server'),
APP_ENV: (process.env.APP_ENV && JSON.stringify(process.env.APP_ENV)) || undefined,
},
}),
].concat(sharedServerPlugins),
node: {
__dirname: false,
__filename: false,
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.json$/,
loader: 'json',
},
],
},
},
];
That is the config we use in one of our sites with a partially shared codebase, and partially shared assets. It consists of three Webpack builds in one:
Browser
Server assets (images/fonts/... that will be referenced in server-generated HTML)
Server code (Node)
The server code has a few special properties:
target: 'node' (Webpack needs this)
output.libraryTarget: 'commonjs' (makes Webpack use commonjs for unbundled libs)
externals: [ /^(?!\.|\/).+/i, ] (makes Webpack not bundle anything in node_modules, or anything that is not a relative path (starting with . or /)
This combination makes Webpack only process your own code, and access other modules and libraries through require. Which means your dependencies using native bindings won't break as they won't be bundled.

Categories

Resources