Separate the config API with dev and prod webpack - javascript

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

Related

How i can solve the devServer problem in webpack?

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,
},
};

Webpack 4 won't load files after renaming to jsx extension

I'm trying to import jsx files without using the file extensions, like import LandingPage from './components/LandingPage'; and as long as the original file is named LandingPage.js this works fine, but if I rename it to LandingPage.jsx it doesn't work anymore. I already added resolve.extensions: ['.js', '.jsx'] to my webpack config file and nothing changed. If I try to import a jsx file, I get this error:
ERROR in ./src/App.js
Module not found: Error: Can't resolve './components/LandingPage' in 'C:\Users\myself\Documents\Projects\react-app-starter\src'
webpack.config.js:
/* eslint-disable import/no-extraneous-dependencies */
const path = require('path');
const BrotliPlugin = require('brotli-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
require('dotenv-defaults').config({
path: `${__dirname}/.env`,
encoding: 'utf8',
defaults: `${__dirname}/.env.sample`,
});
const commonConfig = {
entry: './src/index.js',
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(js|jsx)?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.html$/,
exclude: /template\.html$/,
use: {
loader: 'html-loader',
options: {
minimize: true,
removeComments: false,
collapseWhitespace: true,
},
},
},
{
test: /\.(png|jpg|gif|ico)$/,
use: 'file-loader',
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
},
},
},
{
test: /\.geojson$/,
loader: 'json-loader',
},
],
},
plugins: [
new Dotenv({
defaults: `${__dirname}/.env.sample`,
path: `${__dirname}/.env`,
}),
new HtmlWebPackPlugin({
template: './src/template.html',
title: process.env.APP_TITLE,
filename: 'index.html',
favicon: './src/favicon.ico',
}),
],
};
if (process.env.BABEL_USE_MATERIAL_UI_ES_MODULES) {
commonConfig.resolve = {
alias: {
'#material-ui/core': '#material-ui/core/es',
},
};
}
module.exports = (env, argv = { mode: 'development' }) => {
switch (argv.mode) {
default:
case 'development': {
return {
...commonConfig,
devServer: {
compress: process.env.WEBPACK_DEV_SERVER_COMPRESS === 'true',
host: process.env.WEBPACK_DEV_SERVER_HOST,
open: process.env.WEBPACK_DEV_SERVER_OPEN === 'true',
port: process.env.WEBPACK_DEV_SERVER_PORT,
historyApiFallback: true,
},
devtool: 'eval-source-map',
};
}
case 'production': {
return {
...commonConfig,
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[contenthash].bundle.js',
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
...commonConfig.plugins,
new BrotliPlugin({
asset: '[path].br[query]',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
],
};
}
}
};
babel.config.js:
module.exports = (api) => {
api.cache(false);
const conditionalPresets = [];
const presets = [
'#babel/preset-react',
'#babel/preset-env',
...conditionalPresets,
];
const conditionalPlugins = (process.env.BABEL_USE_MATERIAL_UI_ES_MODULES === 'true') ?
[
[
'import', {
libraryName: '#material-ui/icons',
libraryDirectory: '',
camel2DashComponentName: false,
},
],
]
: [
[
'import',
{
libraryName: '#material-ui/core',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core',
],
[
'import',
{
libraryName: '#material-ui/core/colors',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core/colors',
],
[
'import',
{
libraryName: '#material-ui/core/styles',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core/styles',
],
[
'import',
{
libraryName: '#material-ui/icons',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/icons',
],
];
const plugins = [
'#babel/proposal-class-properties',
'#babel/syntax-dynamic-import',
'#babel/transform-runtime',
'#babel/plugin-transform-react-jsx',
...conditionalPlugins,
];
return {
presets,
plugins,
};
};
My build script: "dev": "webpack-dev-server --mode development --progress"

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.

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