A webpack plugin that automates generation of the differential script loading with browserslist and browserslist-useragent-regexp.
- 🦔 Declare environments in
.browserslistrc
config like this:
defaults
[modern]
last 2 versions and last 1 year and not safari 12.1
[actual]
last 2 years and not last 2 versions
- 📝 Create
webpack.config.js
for multiple outputs:
function createWebpackConfig(env) {
return {
name: env,
/* ... */
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: 'usage',
corejs: 3
}]
],
plugins: [/* ... */]
}
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
inject: 'head'
})
]
};
}
- 🦄 Add
bdsl-webpack-plugin
:
const {
BdslWebpackPlugin,
getBrowserslistQueries,
getBrowserslistEnvList
} = require('bdsl-webpack-plugin');
function createWebpackConfig(env) {
return {
name: env,
/* ... */
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
['@babel/preset-env', {
/* ... */
targets: getBrowserslistQueries({ env })
}]
],
plugins: [/* ... */]
}
}]
},
plugins: [
new HtmlWebpackPlugin(/* ... */),
new BdslWebpackPlugin({ env })
]
};
}
module.exports = [
...getBrowserslistEnvList(),
undefined // to use default .browserslistrc queries
].map(createWebpackConfig);
- 🎉 Done! Now
index.html
will contain differential script loading:
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script>function dsl(a,s,c,l,i){c=dsld.createElement('script');c.async=a[0];c.src=s;l=a.length;for(i=1;i<l;i++)c.setAttribute(a[i][0],a[i][1]);dslf.appendChild(c)}var dsld=document,dslf=dsld.createDocumentFragment(),dslu=navigator.userAgent,dsla=[[]];if(/((CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(13[_\.]0|13[_\.]([1-9]|\d{2,})|(1[4-9]|[2-9]\d|\d{3,})[_\.]\d+)(?:[_\.]\d+)?)|(SamsungBrowser\/(9\.2|9\.([3-9]|\d{2,})|([1-9]\d|\d{3,})\.\d+|10\.1|10\.([2-9]|\d{2,})|(1[1-9]|[2-9]\d|\d{3,})\.\d+))|(Edge\/(79|([8-9]\d|\d{3,}))(?:\.\d+)?)|(HeadlessChrome((?:\/79\.\d+\.\d+)?|(?:\/([8-9]\d|\d{3,})\.\d+\.\d+)?))|((Chromium|Chrome)\/(79|([8-9]\d|\d{3,}))\.\d+(?:\.\d+)?)|(Version\/(13|(1[4-9]|[2-9]\d|\d{3,}))\.\d+(?:\.\d+)?.*Safari\/)|(Firefox\/(68|(69|[7-9]\d|\d{3,})|71|(7[2-9]|[8-9]\d|\d{3,}))\.\d+\.\d+)|(Firefox\/(68|(69|[7-9]\d|\d{3,})|71|(7[2-9]|[8-9]\d|\d{3,}))\.\d+(pre|[ab]\d+[a-z]*)?)/.test(dslu))dsl(dsla[0],"/index.modern.js")
else if(/((CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(11[_\.]3|11[_\.]([4-9]|\d{2,})|(1[2-9]|[2-9]\d|\d{3,})[_\.]\d+|12[_\.]0|12[_\.]([1-9]|\d{2,})|12[_\.]4|12[_\.]([5-9]|\d{2,})|(1[3-9]|[2-9]\d|\d{3,})[_\.]\d+)(?:[_\.]\d+)?)|(SamsungBrowser\/(7\.2|7\.([3-9]|\d{2,})|7\.4|7\.([5-9]|\d{2,})|([8-9]|\d{2,})\.\d+|8\.2|8\.([3-9]|\d{2,})|(9|\d{2,})\.\d+))|(Edge\/(17|(1[8-9]|[2-9]\d|\d{3,}))(?:\.\d+)?)|(HeadlessChrome((?:\/65\.\d+\.\d+)?|(?:\/(6[6-9]|[7-9]\d|\d{3,})\.\d+\.\d+)?))|((Chromium|Chrome)\/(65|(6[6-9]|[7-9]\d|\d{3,}))\.\d+(?:\.\d+)?([\d.]+$|.*Safari\/(?![\d.]+ Edge\/[\d.]+$)))|(Version\/(11\.1|11\.([2-9]|\d{2,})|(1[2-9]|[2-9]\d|\d{3,})\.\d+|12\.0|12\.([1-9]|\d{2,})|(1[3-9]|[2-9]\d|\d{3,})\.\d+)(?:\.\d+)?.*Safari\/)|(Firefox\/(59|([6-9]\d|\d{3,}))\.\d+\.\d+)|(Firefox\/(59|([6-9]\d|\d{3,}))\.\d+(pre|[ab]\d+[a-z]*)?)/.test(dslu))dsl(dsla[0],"/index.actual.js")
else dsl(dsla[0],"/index.legacy.js");dsld.all[1].appendChild(dslf)</script>
</head>
<body></body>
</html>
Here you can see complete webpack.config.js
example.
npm i -D bdsl-webpack-plugin
# or
yarn add -D bdsl-webpack-plugin
bdsl-webpack-plugin
captures scripts only from<head>
section, so withhtml-webpack-plugin
you should useinject: 'head'
option;- By default scripts are loaded asynchronously and executed in "as they defined" order. To execute script in "load-first" order you should add
async
attribute to<script>
tag. For that you can usescript-ext-html-webpack-plugin
; defer
scripts are not supported, so you can use libraries likewhen-dom-ready
to bootstrap code whenDOM
ready;- Webpack configs must be in modern to legacy browser order, e.g.
['modern', 'actual', 'legacy']
; bdsl-webpack-plugin
also definesprocess.env.BDSL_ENV
variable with bundle's environment.
There is a differential script loading with module/nomodule trick, for this you can use webpack-module-nomodule-plugin
. But browsers that support type=module
already have new JS-features with different level of support. For example: optional chaining operator (for comparison browsers with type=module
support).
Option | Type | Default | Description |
---|---|---|---|
isModule | boolean |
— | Use type=module support check instead of RegExp. Should be used only on certain build. |
browsers | string | string[] |
— | Manually provide a browserslist query (or an array of queries). It overrides the browserslist configuration specified in your project. |
env | string |
— | When multiple browserslist environments are specified, pick the config belonging to this environment. |
ignorePatch | boolean |
true |
Ignore the difference in patch browser numbers. |
ignoreMinor | boolean |
false |
Ignore the difference in minor browser versions. |
allowHigherVersions | boolean |
true |
For all browsers in the browserslist query, return a match if the useragent version is equal to or higher than the one specified in browserslist. |
allowZeroSubverions | boolean |
true |
Ignore match of patch or patch and minor, if they are 0. |
withStylesheets | boolean |
false |
Enable differential stylesheets loading. |
unsafeUseDocumentWrite | boolean |
false |
Use document.write() to inject <script> . This variant supports defer scripts, but some browsers can restrict document.write() calls. |
Read docs here.
- Basic
isModule
option with@babel/preset-modules
- Differential stylesheet loading
- Transpile dependencies
unsafeUseDocumentWrite
option- Differential serving with
SsrBdslWebpackPlugin
BdslBuilder
API with@loadable/server
You can get speed metrics from any site using devtool from this repo.
- Clone repo:
git clone git@github.com:TrigenSoftware/bdsl-webpack-plugin.git
- Install dependencies:
yarn
- Now you can run script:
yarn measure \
"https://nodsl.site.com/" \
"https://site.com/"
Output example
https://nodsl.site.com/
Average time: 1s 303ms
Fastest time: 1s 203ms
Slowest time: 2s 432ms
Encoded size: 292 kB
Decoded size: 1.08 MB
https://site.com/
Average time: 1s 274ms
Fastest time: 1s 140ms
Slowest time: 2s 284ms
Encoded size: 218 kB
Decoded size: 806 kB
Parameters
Environment variables:
MEASURE_TIMES - number of site measurements, 10 by default
Options:
--good3g - enable "Good 3G" network preset
--regular4g - enable "Regular 4G" network preset
--cache - enable resource caching