Permalink
Browse files

feat(webpack): Improvements in CLI generated Webpack config

Improvement of generated webpack config in relation to bundle splitting, long term cachability and other aspects.
Added new question for HTTP protocol to be used in webpack optimisation config
New question only accessible when following the 'custom' path
Choices are HTTP/1.1 or HTTP/2
Default is HTTP/1.1 when the question is not presented.

PR for feature: #969
  • Loading branch information...
chrisckc committed Dec 5, 2018
1 parent fca23b4 commit 2365331114289bc3f69e7948153461c88e12357a
@@ -54,6 +54,10 @@
"id": "webpack",
"displayName": "Webpack"
},
"httpProtocol": {
"id": "http1",
"displayName": "HTTP/1.1"
},
"build": {
"options": {
"server": "dev",
@@ -206,7 +210,7 @@
{
"id": 613,
"type": "state-assign",
"nextActivity": 621,
"nextActivity": 615,
"state": {
"loader": {
"id": "none",
@@ -240,6 +244,31 @@
}
}
},
{
"id": 615,
"type": "input-select",
"nextActivity": 621,
"question": "Which HTTP Protocol do you wish the outputted Webpack bundle to be optimised for?",
"stateProperty": "httpProtocol",
"options": [
{
"displayName": "HTTP/1.1",
"description": "The legacy HTTP/1.1 protocol, max 6 parallel requests/connections",
"value": {
"id": "http1",
"displayName": "HTTP/1.1"
}
},
{
"displayName": "HTTP/2",
"description": "The modern HTTP/2 Protocol, uses request multiplexing over a single connection",
"value": {
"id": "http2",
"displayName": "HTTP/2"
}
}
]
},
{
"id": 620,
"type": "input-select",
@@ -101,7 +101,7 @@
"url-loader": "^1.1.1",
"vinyl-fs": "^3.0.3",
"wait-on": "^3.1.0",
"webpack": "^4.20.2",
"webpack": "^4.27.0",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9",
"webpack-hot-middleware": "^2.24.3"
@@ -55,54 +55,130 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } =
chunkFilename: production ? '[name].[chunkhash].chunk.js' : '[name].[hash].chunk.js'
},
optimization: {
// Use splitChunks to breakdown the vendor bundle into smaller files
runtimeChunk: true, // separates the runtime chunk, required for long term cacheability
// moduleIds is the replacement for HashedModuleIdsPlugin and NamedModulesPlugin deprecated in https://github.com/webpack/webpack/releases/tag/v4.16.0
// changes module id's to use hashes be based on the relative path of the module, required for long term cacheability
moduleIds: 'hashed',
// Use splitChunks to breakdown the App/Aurelia bundle down into smaller chunks
// https://webpack.js.org/plugins/split-chunks-plugin/
splitChunks: {
hidePathInfo: true, // prevents the path from being used in the filename when using maxSize
chunks: "initial",
// sizes are compared against source before minification
// @if httpProtocol.id='http1'
maxSize: 200000, // splits chunks if bigger than 200k, adjust as required (maxSize added in webpack v4.15)
// @endif
// @if httpProtocol.id='http2'
maxInitialRequests: Infinity, // Default is 3, make this unlimited if using HTTP/2
maxAsyncRequests: Infinity, // Default is 5, make this unlimited if using HTTP/2
minSize: 10000, // chunk is only created if it would be bigger than minSize, adjust as required
maxSize: 40000, // splits chunks if bigger than 40k, adjust as required (maxSize added in webpack v4.15)
// @endif
cacheGroups: {
default: false, // Disable the built-in groups (default and vendors)
vendors: false,
bluebird: {
test: /[\\/]node_modules[\\/]bluebird[\\/]/,
name: "vendor.bluebird",
enforce: true,
priority: 100
},
// You can insert additional entries here for jQuery and bootstrap etc. if you need them
// Break the Aurelia bundle down into smaller chunks, binding and templating are the largest
aureliaBinding: {
test: /[\\/]node_modules[\\/]aurelia-binding[\\/]/,
name: "vendor.aurelia-binding",
enforce: true,
priority: 28
default: false, // Disable the built-in groups default & vendors (vendors is redefined below)
// You can insert additional cacheGroup entries here if you want to split out specific modules
// This is required in order to split out vendor css from the app css when using --extractCss
// For example to separate font-awesome and bootstrap:
// fontawesome: { // separates font-awesome css from the app css (font-awesome is only css/fonts)
// name: 'vendor.font-awesome',
// test: /[\\/]node_modules[\\/]font-awesome[\\/]/,
// priority: 100,
// enforce: true
// },
// bootstrap: { // separates bootstrap js from vendors and also bootstrap css from app css
// name: 'vendor.font-awesome',
// test: /[\\/]node_modules[\\/]bootstrap[\\/]/,
// priority: 90,
// enforce: true
// },

// @if httpProtocol.id='http1'
// This is the HTTP/1.1 optimised cacheGroup configuration
vendors: { // picks up everything from node_modules as long as the sum of node modules is larger than minSize
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 19,
enforce: true, // causes maxInitialRequests to be ignored, minSize still respected if specified in cacheGroup
minSize: 30000 // use the default minSize
},
aureliaTemplating: {
test: /[\\/]node_modules[\\/]aurelia-templating[\\/]/,
name: "vendor.aurelia-templating",
enforce: true,
priority: 26
vendorsAsync: { // vendors async chunk, remaining asynchronously used node modules as single chunk file
test: /[\\/]node_modules[\\/]/,
name: 'vendors.async',
chunks: 'async',
priority: 9,
reuseExistingChunk: true,
minSize: 10000 // use smaller minSize to avoid too much potential bundle bloat due to module duplication.
},
aurelia: {
test: /[\\/]node_modules[\\/]aurelia-.*[\\/]/,
name: "vendor.aurelia",
enforce: true,
commonsAsync: { // commons async chunk, remaining asynchronously used modules as single chunk file
name: 'commons.async',
minChunks: 2, // Minimum number of chunks that must share a module before splitting
chunks: 'async',
priority: 0,
reuseExistingChunk: true,
minSize: 10000 // use smaller minSize to avoid too much potential bundle bloat due to module duplication.
}
// @endif
// @if httpProtocol.id='http2'
// This is the HTTP/2 optimised cacheGroup configuration
// generic 'initial/sync' vendor node module splits: separates out larger modules
vendorSplit: { // each node module as separate chunk file if module is bigger than minSize
test: /[\\/]node_modules[\\/]/,
name(module) {
// Extract the name of the package from the path segment after node_modules
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `vendor.${packageName.replace('@', '')}`;
},
priority: 20
},
// This picks up everything else being used from node_modules
vendors: {
vendors: { // picks up everything else being used from node_modules that is less than minSize
test: /[\\/]node_modules[\\/]/,
name: "vendors",
priority: 19,
enforce: true // create chunk regardless of the size of the chunk
},
// generic 'async' vendor node module splits: separates out larger modules
vendorAsyncSplit: { // vendor async chunks, create each asynchronously used node module as separate chunk file if module is bigger than minSize
test: /[\\/]node_modules[\\/]/,
name(module) {
// Extract the name of the package from the path segment after node_modules
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `vendor.async.${packageName.replace('@', '')}`;
},
chunks: 'async',
priority: 10,
reuseExistingChunk: true,
minSize: 5000 // only create if 5k or larger
},
vendorsAsync: { // vendors async chunk, remaining asynchronously used node modules as single chunk file
test: /[\\/]node_modules[\\/]/,
name: "vendor",
enforce: true,
priority: 10
name: 'vendors.async',
chunks: 'async',
priority: 9,
reuseExistingChunk: true,
enforce: true // create chunk regardless of the size of the chunk
},
// generic 'async' common module splits: separates out larger modules
commonAsync: { // common async chunks, each asynchronously used module a separate chunk file if module is bigger than minSize
name(module) {
// Extract the name of the module from last path component. 'src/modulename/' results in 'modulename'
const moduleName = module.context.match(/[^\\/]+(?=\/$|$)/)[0];
return `common.async.${moduleName.replace('@', '')}`;
},
minChunks: 2, // Minimum number of chunks that must share a module before splitting
chunks: 'async',
priority: 1,
reuseExistingChunk: true,
minSize: 5000 // only create if 5k or larger
},
common: { // common chunk
name: 'common',
minChunks: 2, // Creates a new chunk if a module is shared between different chunks more than twice
commonsAsync: { // commons async chunk, remaining asynchronously used modules as single chunk file
name: 'commons.async',
minChunks: 2, // Minimum number of chunks that must share a module before splitting
chunks: 'async',
priority: 0,
reuseExistingChunk: true,
enforce: true
enforce: true // create chunk regardless of the size of the chunk
}
// @endif
}
}
},
@@ -232,12 +308,13 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } =
title, server, baseUrl
}
}),
...when(extractCss, new MiniCssExtractPlugin({
filename: production ? '[contenthash].css' : '[id].css',
allChunks: true
// ref: https://webpack.js.org/plugins/mini-css-extract-plugin/
...when(extractCss, new MiniCssExtractPlugin({ // updated to match the naming conventions for the js files
filename: production ? 'css/[name].[contenthash].bundle.css' : 'css/[name].[hash].bundle.css',
chunkFilename: production ? 'css/[name].[contenthash].chunk.css' : 'css/[name].[hash].chunk.css'
})),
...when(production || server, new CopyWebpackPlugin([
{ from: 'static', to: outDir }])),
{ from: 'static', to: outDir, ignore: ['.*'] }])), // ignore dot (hidden) files
...when(analyze, new BundleAnalyzerPlugin())
]
});
@@ -20,6 +20,7 @@ module.exports = class {
name: context.state.name,
type: context.state.type,
bundler: context.state.bundler,
httpProtocol: context.state.httpProtocol,
build: context.state.build,
platform: context.state.platform,
loader: context.state.loader,

0 comments on commit 2365331

Please sign in to comment.