Skip to content

Instantly share code, notes, and snippets.

@moderndeveloperllc
Last active August 21, 2019 14:30
Show Gist options
  • Save moderndeveloperllc/a3774d2890fcff56d7b39b9883b0879f to your computer and use it in GitHub Desktop.
Save moderndeveloperllc/a3774d2890fcff56d7b39b9883b0879f to your computer and use it in GitHub Desktop.
webpack config - BabelMultiTargetPlugin and HtmlWebpackDeployPlugin
'use strict';
const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;
const HtmlWebpackDeployPlugin = require('html-webpack-deploy-plugin');
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
const {InjectManifest} = require('workbox-webpack-plugin');
const {resolve, join} = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const fs = require('fs');
const helperWhitelist = require('./utils/helper-whitelist');
const helperWhitelistModern = require('./utils/helper-whitelist-modern');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const crypto = require('crypto');
// const zlib = require('zlib');
const ENV = process.argv.find((arg) => arg.includes('production'))
? 'production'
: 'development';
const ANALYZE = process.argv.find((arg) => arg.includes('--analyze'));
const OUTPUT_PATH = ENV === 'production' ? resolve('dist') : resolve('src');
const INDEX_TEMPLATE = resolve('./src/index.html');
const key = () => {
try {
return fs.readFileSync('/usr/local/etc/nginx/ssl/star_wpm.key.pem');
} catch (e) {
console.log(e);
}
};
const cert = () => {
try {
return fs.readFileSync('/usr/local/etc/nginx/ssl/star_wpm.cert.pem');
} catch (e) {
console.log(e);
}
};
const ca = () => {
try {
return fs.readFileSync('/usr/local/etc/nginx/ssl/development-ca-chain.cert.pem');
} catch (e) {
console.log(e);
}
};
const helpers = [
{
from: resolve('./src/vendor/*.js'),
to: join(OUTPUT_PATH, 'vendor', '[name].[contenthash:8].[ext]'),
flatten: true,
},
];
const assets = [
{
from: resolve('./src/assets'),
to: join(OUTPUT_PATH, 'assets'),
},
{
from: resolve('./src/favicon.ico'),
to: OUTPUT_PATH,
},
{
from: resolve('./src/manifest.json'),
to: OUTPUT_PATH,
},
{
from: resolve('./src/styles/*.css'),
to: join(OUTPUT_PATH, 'styles'),
flatten: true,
},
];
const commonConfig = merge([
{
entry: './src/index.js',
output: {
path: OUTPUT_PATH,
filename: '[name].[chunkhash:8].js',
},
module: {
rules: [
{
test: /\.js$/,
use: [
BabelMultiTargetPlugin.loader(),
'uglify-template-string-loader',
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: INDEX_TEMPLATE,
minify: {
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true,
},
chunksSortMode: 'none',
}),
// Babel configuration for multiple output bundles targeting different sets
// of browsers
new BabelMultiTargetPlugin({
babel: {
plugins: [
[
require('@babel/plugin-external-helpers'),
{
whitelist: [...helperWhitelist, ...helperWhitelistModern],
},
],
// Minify HTML and CSS in tagged template literals
[
require('babel-plugin-template-html-minifier'),
{
modules: {
'@polymer/polymer/lib/utils/html-tag.js': ['html'],
'lit-html': ['html'],
'lit-element': [
'html',
{'name': 'css', 'encapsulation': 'style'},
],
},
htmlMinifier: {
collapseWhitespace: true,
minifyCSS: true,
removeComments: true,
sortAttributes: true,
sortClassName: true,
},
},
],
],
// @babel/preset-env options common for all bundles
presetOptions: {
// Don’t add polyfills, they are provided from webcomponents-loader.js
useBuiltIns: false,
debug: true,
},
},
// Modules excluded from targeting into different bundles
doNotTarget: [
// Array of RegExp patterns
],
// Modules that should not be transpiled
exclude: [
// Array of RegExp patterns
],
// Fix for `nomodule` attribute to work correctly in Safari 10.1
safari10NoModuleFix: false,
// Target browsers with and without ES modules support
targets: {
esm: {
browsers: [
'last 2 Chrome major versions',
'last 2 ChromeAndroid major versions',
'last 2 Edge major versions',
'last 2 Firefox major versions',
'last 2 Safari major versions',
'last 2 iOS major versions',
],
tagAssetsWithKey: false, // don’t append a suffix to the file name
esModule: true, // marks the bundle used with <script type="module">
},
es5: {
browsers: ['ie 11'],
tagAssetsWithKey: true, // append a suffix to the file name
noModule: true, // marks the bundle included without `type="module"`
},
},
}),
new HtmlWebpackDeployPlugin({
assets: {
copy: [...helpers, ...assets],
scripts: [
{
append: false,
path: './vendor/regenerator-runtime.min.js',
attributes: {noModule: true},
},
{
append: false,
path: './vendor/babel-helpers.min.js',
attributes: {noModule: true},
},
{
append: false,
path: './vendor/babel-helpers-modern.min.js'
}],
hash: (assetName, hash) => {
if (assetName) {
const copyFrom = assetName.replace('./vendor', './src/vendor');
const contentHash = crypto.createHash('md5').
update(fs.readFileSync(copyFrom, 'utf8')).
digest('hex').
slice(0, 8);
assetName = assetName.replace(/\.js$/, '.' + contentHash + '.js');
return assetName;
}
return assetName;
},
},
packages: {
'@webcomponents/webcomponentsjs': {
copy: [
{from: 'bundles', to: 'bundles/'},
{from: 'webcomponents-loader.js', to: 'webcomponents-loader.js'},
],
scripts: {
variableName: 'webcomponents',
append: false,
path: 'webcomponents-loader.js',
},
},
},
useAssetsPath: false,
usePublicPath: false,
}),
],
},
]);
const developmentConfig = merge([
{
devtool: 'cheap-module-source-map',
plugins: [],
devServer: {
contentBase: OUTPUT_PATH,
compress: true,
overlay: true,
port: 3000,
host: 'fms.wpm',
https: {
key: key(),
cert: cert(),
ca: ca(),
},
headers: {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-Frame-Options': 'SAMEORIGIN',
'X-Xss-Protection': '1; mode=block',
'X-Content-Type-Options': 'nosniff',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Content-Security-Policy': 'default-src \'self\'; script-src \'self\' storage.googleapis.com;' +
' style-src \'self\' \'unsafe-inline\'; font-src data:; connect-src \'self\' https://fms-engine.wpm:9443;',
},
historyApiFallback: true,
},
},
]);
const analyzeConfig = ANALYZE ? [new BundleAnalyzerPlugin()] : [];
const productionConfig = merge([
{
devtool: 'nosources-source-map',
optimization: {
minimizer: [
new TerserWebpackPlugin({
terserOptions: {
output: {
comments: false,
},
},
sourceMap: true,
parallel: true,
}),
],
},
plugins: [
new CleanWebpackPlugin({verbose: true}),
new InjectManifest({
swSrc: resolve('src', 'service-worker.js'),
swDest: resolve(OUTPUT_PATH, 'sw.js'),
exclude: [
/.*\.map$/,
/.*\/webcomponents-(?!loader).*\.js$/,
/.*\.es5\..*\.js$/,
],
}),
new CompressionPlugin({test: /\.(js(\.map)?|css|html|svg)$/i}),
/* new CompressionPlugin({
filename: '[path].br[query]',
algorithm: 'brotliCompress',
test: /\.(js(\.map)?|css|html|svg)$/i,
compressionOptions: {level: zlib.constants.BROTLI_MAX_QUALITY},
threshold: 200,
minRatio: 0.8,
deleteOriginalAssets: false,
}),*/
...analyzeConfig,
],
},
]);
module.exports = (mode) => {
if (mode === 'production') {
return merge(commonConfig, productionConfig, {mode});
}
return merge(commonConfig, developmentConfig, {mode});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment