Created
January 17, 2020 08:47
-
-
Save davidhellmann/6d807ad2d5c17b0f22de1a5dae540150 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// webpack.prod.js - production builds | |
const LEGACY_CONFIG = 'legacy'; | |
const MODERN_CONFIG = 'modern'; | |
// node modules | |
const git = require('git-rev-sync'); | |
const glob = require('glob-all'); | |
const merge = require('webpack-merge'); | |
const moment = require('moment'); | |
const path = require('path'); | |
const webpack = require('webpack'); | |
// webpack plugins | |
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') | |
.BundleAnalyzerPlugin; | |
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); | |
const CompressionPlugin = require('compression-webpack-plugin'); | |
const CreateSymlinkPlugin = require('create-symlink-webpack-plugin'); | |
const CriticalCssPlugin = require('critical-css-webpack-plugin'); | |
const HtmlWebpackPlugin = require('html-webpack-plugin'); | |
const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin'); | |
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); | |
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); | |
const PurgecssPlugin = require('purgecss-webpack-plugin'); | |
const SaveRemoteFilePlugin = require('save-remote-file-webpack-plugin'); | |
const TerserPlugin = require('terser-webpack-plugin'); | |
const WebappWebpackPlugin = require('webapp-webpack-plugin'); | |
const WhitelisterPlugin = require('purgecss-whitelister'); | |
const WorkboxPlugin = require('workbox-webpack-plugin'); | |
const zopfli = require('@gfx/zopfli'); | |
// config files | |
const common = require('./webpack.common.js'); | |
const pkg = require('./package.json'); | |
const settings = require('./webpack.settings.js'); | |
// Custom PurgeCSS extractor for Tailwind that allows special characters in | |
// class names. | |
// | |
// https://github.com/FullHuman/purgecss#extractor | |
class SpecialCharsExtractor { | |
static extract(content) { | |
return content.match(/[A-Za-z0-9-_:\/]+/g) || []; | |
} | |
} | |
// Configure file banner | |
const configureBanner = () => { | |
let commitHash = 'n/a'; | |
let branch = 'n/a'; | |
try { | |
let commitHash = git.long(); | |
let branch = git.branch(); | |
} catch (error) { | |
console.log('No git repository is associated with this project'); | |
} | |
return { | |
banner: [ | |
'/*!', | |
' * @project ' + settings.name, | |
' * @name ' + '[filebase]', | |
' * @author ' + pkg.author.name, | |
' * @build ' + moment().format('llll') + ' ET', | |
' * @release ' + commitHash + ' [' + branch + ']', | |
' * @copyright Copyright (c) ' + | |
moment().format('YYYY') + | |
' ' + | |
settings.copyright, | |
' *', | |
' */', | |
'', | |
].join('\n'), | |
raw: true, | |
}; | |
}; | |
// Configure Bundle Analyzer | |
const configureBundleAnalyzer = buildType => { | |
if (buildType === LEGACY_CONFIG) { | |
return { | |
analyzerMode: 'static', | |
reportFilename: 'report-legacy.html', | |
}; | |
} | |
if (buildType === MODERN_CONFIG) { | |
return { | |
analyzerMode: 'static', | |
reportFilename: 'report-modern.html', | |
}; | |
} | |
}; | |
// Configure Compression webpack plugin | |
const configureCompression = () => { | |
return { | |
filename: '[path].gz[query]', | |
test: /\.(js|css|html|svg)$/, | |
threshold: 10240, | |
minRatio: 0.8, | |
deleteOriginalAssets: false, | |
compressionOptions: { | |
numiterations: 15, | |
level: 9, | |
}, | |
algorithm(input, compressionOptions, callback) { | |
return zopfli.gzip(input, compressionOptions, callback); | |
}, | |
}; | |
}; | |
// Configure Critical CSS | |
const configureCriticalCss = () => { | |
return settings.criticalCssConfig.pages.map(row => { | |
const criticalSrc = settings.urls.critical + row.url; | |
const criticalDest = | |
settings.criticalCssConfig.base + | |
row.template + | |
settings.criticalCssConfig.suffix; | |
let criticalWidth = settings.criticalCssConfig.criticalWidth; | |
let criticalHeight = settings.criticalCssConfig.criticalHeight; | |
// Handle Google AMP templates | |
if (row.template.indexOf(settings.criticalCssConfig.ampPrefix) !== -1) { | |
criticalWidth = settings.criticalCssConfig.ampCriticalWidth; | |
criticalHeight = settings.criticalCssConfig.ampCriticalHeight; | |
} | |
console.log('source: ' + criticalSrc + ' dest: ' + criticalDest); | |
return new CriticalCssPlugin({ | |
base: './', | |
src: criticalSrc, | |
dest: criticalDest, | |
extract: false, | |
inline: false, | |
minify: true, | |
width: criticalWidth, | |
height: criticalHeight, | |
}); | |
}); | |
}; | |
// Configure Clean webpack | |
const configureCleanWebpack = () => { | |
return { | |
cleanOnceBeforeBuildPatterns: settings.paths.dist.clean, | |
verbose: true, | |
dry: false, | |
}; | |
}; | |
// Configure Html webpack | |
const configureHtml = () => { | |
return { | |
templateContent: '', | |
filename: 'webapp.html', | |
inject: false, | |
}; | |
}; | |
// Configure Image loader | |
const configureImageLoader = buildType => { | |
if (buildType === LEGACY_CONFIG) { | |
return { | |
test: /\.(png|jpe?g|gif|svg|webp)$/i, | |
use: [ | |
{ | |
loader: 'file-loader', | |
options: { | |
name: 'img/[name].[hash].[ext]', | |
}, | |
}, | |
], | |
}; | |
} | |
if (buildType === MODERN_CONFIG) { | |
return { | |
test: /\.(png|jpe?g|gif|svg|webp)$/i, | |
use: [ | |
{ | |
loader: 'file-loader', | |
options: { | |
name: 'img/[name].[hash].[ext]', | |
}, | |
}, | |
{ | |
loader: 'img-loader', | |
options: { | |
plugins: [ | |
require('imagemin-gifsicle')({ | |
interlaced: true, | |
}), | |
require('imagemin-mozjpeg')({ | |
progressive: true, | |
arithmetic: false, | |
}), | |
require('imagemin-optipng')({ | |
optimizationLevel: 5, | |
}), | |
require('imagemin-svgo')({ | |
plugins: [{ convertPathData: false }], | |
}), | |
], | |
}, | |
}, | |
], | |
}; | |
} | |
}; | |
// Configure optimization | |
const configureOptimization = buildType => { | |
if (buildType === LEGACY_CONFIG) { | |
return { | |
splitChunks: { | |
cacheGroups: { | |
default: false, | |
common: false, | |
styles: { | |
name: settings.vars.cssName, | |
test: /\.(pcss|scss|css|vue)$/, | |
chunks: 'all', | |
enforce: true, | |
}, | |
}, | |
}, | |
minimizer: [ | |
new TerserPlugin(configureTerser()), | |
new OptimizeCSSAssetsPlugin({ | |
cssProcessorOptions: { | |
map: { | |
inline: false, | |
annotation: true, | |
}, | |
safe: true, | |
discardComments: true, | |
}, | |
}), | |
], | |
}; | |
} | |
if (buildType === MODERN_CONFIG) { | |
return { | |
minimizer: [new TerserPlugin(configureTerser())], | |
}; | |
} | |
}; | |
// Configure Postcss loader | |
const configurePostcssLoader = buildType => { | |
if (buildType === LEGACY_CONFIG) { | |
return { | |
test: /\.(pcss|css|scss)$/, | |
use: [ | |
MiniCssExtractPlugin.loader, | |
{ | |
loader: 'css-loader', | |
options: { | |
importLoaders: 3, | |
sourceMap: true, | |
}, | |
}, | |
{ | |
loader: 'resolve-url-loader', | |
}, | |
{ | |
loader: 'postcss-loader', | |
options: { | |
sourceMap: true, | |
}, | |
}, | |
{ | |
loader: 'sass-loader', | |
options: { | |
// Prefer `dart-sass` | |
implementation: require('sass'), | |
sassOptions: { | |
includePaths: ['./node_modules'], | |
}, | |
sourceMap: true, | |
}, | |
}, | |
], | |
}; | |
} | |
// Don't generate CSS for the modern config in production | |
if (buildType === MODERN_CONFIG) { | |
return { | |
test: /\.(pcss|css|scss)$/, | |
loader: 'ignore-loader', | |
}; | |
} | |
}; | |
// Configure PurgeCSS | |
const configurePurgeCss = () => { | |
let paths = []; | |
// Configure whitelist paths | |
for (const [key, value] of Object.entries(settings.purgeCssConfig.paths)) { | |
paths.push(path.join(__dirname, value)); | |
} | |
return { | |
paths: glob.sync(paths), | |
whitelist: WhitelisterPlugin(settings.purgeCssConfig.whitelist), | |
whitelistPatterns: settings.purgeCssConfig.whitelistPatterns, | |
extractors: [ | |
{ | |
extractor: SpecialCharsExtractor, | |
extensions: settings.purgeCssConfig.extensions, | |
}, | |
], | |
}; | |
}; | |
// Configure terser | |
const configureTerser = () => { | |
return { | |
cache: true, | |
parallel: true, | |
sourceMap: true, | |
}; | |
}; | |
// Configure Webapp webpack | |
const configureWebapp = () => { | |
return { | |
logo: settings.webappConfig.logo, | |
prefix: settings.webappConfig.prefix, | |
cache: false, | |
inject: 'force', | |
favicons: { | |
appName: pkg.name, | |
appDescription: pkg.description, | |
developerName: pkg.author.name, | |
developerURL: pkg.author.url, | |
path: settings.paths.dist.base, | |
}, | |
}; | |
}; | |
// Configure Workbox service worker | |
const configureWorkbox = () => { | |
let config = settings.workboxConfig; | |
return config; | |
}; | |
// Production module exports | |
module.exports = [ | |
merge(common.legacyConfig, { | |
output: { | |
filename: path.join('./js', '[name]-legacy.[chunkhash].js'), | |
}, | |
mode: 'production', | |
devtool: 'source-map', | |
optimization: configureOptimization(LEGACY_CONFIG), | |
module: { | |
rules: [ | |
configurePostcssLoader(LEGACY_CONFIG), | |
configureImageLoader(LEGACY_CONFIG), | |
], | |
}, | |
plugins: [ | |
new MiniCssExtractPlugin({ | |
path: path.resolve(__dirname, settings.paths.dist.base), | |
filename: path.join('./css', '[name].[chunkhash].css'), | |
}), | |
new PurgecssPlugin(configurePurgeCss()), | |
new webpack.BannerPlugin(configureBanner()), | |
new HtmlWebpackPlugin(configureHtml()), | |
new WebappWebpackPlugin(configureWebapp()), | |
new CreateSymlinkPlugin(settings.createSymlinkConfig, true), | |
new SaveRemoteFilePlugin(settings.saveRemoteFileConfig), | |
new CompressionPlugin(configureCompression()), | |
// new BundleAnalyzerPlugin( | |
// configureBundleAnalyzer(LEGACY_CONFIG), | |
// ), | |
].concat(configureCriticalCss()), | |
}), | |
merge(common.modernConfig, { | |
output: { | |
filename: path.join('./js', '[name].[chunkhash].js'), | |
}, | |
mode: 'production', | |
devtool: 'source-map', | |
optimization: configureOptimization(MODERN_CONFIG), | |
module: { | |
rules: [ | |
configurePostcssLoader(MODERN_CONFIG), | |
configureImageLoader(MODERN_CONFIG), | |
], | |
}, | |
plugins: [ | |
new CleanWebpackPlugin(configureCleanWebpack()), | |
new webpack.BannerPlugin(configureBanner()), | |
new ImageminWebpWebpackPlugin(), | |
new WorkboxPlugin.GenerateSW(configureWorkbox()), | |
new CompressionPlugin(configureCompression()), | |
// new BundleAnalyzerPlugin( | |
// configureBundleAnalyzer(MODERN_CONFIG), | |
// ), | |
], | |
}), | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment