Enhances html-webpack-plugin functionality with different deployment options for your scripts including:
async
attribute;defer
attribute;type="module"
attribute;- inlining;
preload
resource hint;prefetch
resource hint
This is an extension plugin for the webpack plugin html-webpack-plugin - a plugin that simplifies the creation of HTML files to serve your webpack bundles.
The raw html-webpack-plugin incorporates all webpack-generated javascipt as synchronous<script>
elements in the generated html. This plugin allows you to add attributes to these elements or even to inline the code in the element.
You must be running webpack on node 4+.
Install the plugin with npm:
$ npm install --save-dev script-ext-html-webpack-plugin
You may see an UNMET PEER DEPENDENCY
warning for webpack.
This is fine; in testing, we dynamically download multiple versions of webpack (via the dynavers module).
Add the plugin to your webpack config as follows:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin()
]
The above configuration will actually do nothing due to the configuration defaults. Some more useful scenarios:
All scripts set to async
:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'async'
})
]
All scripts set to async
except 'first.js' which is sync:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
sync: ['first.js'],
defaultAttribute: 'async'
})
]
Configuration offers much more complex options:
You must pass a hash of configuration options to the plugin to cause the addition of attributes:
inline
: array ofString
's and/orRegExp
's defining scripts that should be inlined in the html (default:[]
);sync
: array ofString
's and/orRegExp
's defining script names that should have no attribute (default:[]
);async
: array ofString
's and/orRegExp
's defining script names that should have anasync
attribute (default:[]
);defer
: array ofString
's and/orRegExp
's defining script names that should have adefer
attribute (default:[]
);defaultAttribute
:'sync' | 'async' | 'defer'
The default attribute to set -'sync'
actually results in no attribute (default:'sync'
);module
: array ofString
's and/orRegExp
's defining script names that should have atype="module"
attribute (default:[]
);preload
: array ofString
's and/orRegExp
's defining scripts that should have accompanying preload resource hints (default:[]
);prefetch
: array ofString
's and/orRegExp
's defining scripts that should have accompanying prefetch resource hints (default:[]
);
In the arrays a String
value matches if it is a substring of the script name.
In more complicated use cases it may prove difficult to ensure that the pattern matching for different attributes are mutually exclusive. To prevent confusion, the plugin operates a simple precedence model:
-
if a script name matches a
RegEx
orString
from theinline
option, it will be inlined; -
if a script name matches a
Regex
orString
from thesync
option, it will have no attribute, unless it matched condition 1; -
if a script name matches a
Regex
orString
from theasync
option, it will have theasync
attribute, unless it matched conditions 1 or 2; -
if a script name matches a
Regex
orString
from thedefer
option, it will have thedefer
attribute, unless it matched conditions 1, 2 or 3; -
if a script name does not match any of the previous conditions, it will have the `defaultAttribute' attribute.
The module
attribute is independent of conditions 2-5, but will be ignored if the script isinlined.
Some Examples:
All scripts with 'important' in their name are sync and all others set to defer
:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
sync: ['important'],
defaultAttribute: 'defer'
})
]
Alternatively, using a regular expression:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
sync: [/important/],
defaultAttribute: 'defer'
})
]
All scripts with 'mod' in their name are async and type 'module', all others are sync (no explicit setting for this as it is the default):
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
async: ['mod'],
module: ['mod']
})
]
Script 'startup.js' is inlined whilst all other scripts are async and preloaded:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
inline: ['startup'],
preload: [/\.js$/],
defaultAttribute: 'async'
})
]
And so on, to craziness:
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
inline: ['startup'],
sync: [/imp(1|2){1,3}}/, 'initial'],
defer: ['slow', /big.*andslow/],
module: [/^((?!sync).)*/, 'mod'],
prefetch: ['indirectly-referenced.js'],
defaultAttribute: 'async'
})
]
Any problems with real-world examples, just raise an issue.
In the above examples the actual script names are used to select the deployment option. You may not wish to couple asset names to your deployment like this. Instead you can use Webpack's entry configuration to create aliases that the plugin will then use for its pattern matching. Your webpack.config.js
will look something like this:
entry: {
a: path.join(__dirname, 'lib/myFunctions.js'),
b: path.join(__dirname, 'lib/otherFunctions.js'),
c: path.join(__dirname, 'lib/criticalFuntions.js')
},
output: {
...
filename: '[name].js'
}
plugins: [
new HtmlWebpackPlugin(),
new ScriptExtHtmlWebpackPlugin({
inline: ['c'],
defer: ['a', 'b']
})
]
Several notes and caveats apply:
- This feature is for
<script>
's only. If you wish to inline css please see the sister plugin style-ext-html-webpack-plugin. - Even the simplest script will be wrapped with webpack boilerplate; ensure you minify your javascript if you want your output html to be legible!
- Hot replacement of inlined scripts will only work if caching is switched off for html-webpack-plugin:
plugins: [
new HtmlWebpackPlugin({
cache: false
}),
new ScriptExtHtmlWebpackPlugin({
inline: ['myinlinedscript.js']
})
]
- An alternative approach, based on jade templates is illustrated in the HtmlWebpackPlugin inline example.
In most cases, modern browsers will intelligently preload referenced script assets.
However if you wish, this plugin can add resource hint elements to the <head>
element of the form:
<link rel="[preload|prefetch]" href="[scriptname]" as="script">
Use the preload
and prefetch
configuration options.
Where preload
and prefetch
patterns overlap, preload
takes precedence.
Notes:
- for more on resource hints, see the
w3c
definition; - for a more complete solution that allows the preloading\fetching of assets other than scripts, see the resource-hints-webpack-plugin.
v1.5.x
- added resource hints
- works with webpack 2.2.0
v1.4.x
- updated internal mechanism to use new(ish) HtmlWebpackPlugin event
- improved test mechanism and enhanced test coverage
- added support for
publicPath
for inline scripts (thanks @JustAboutJeff) - works with 'webpack -p' (thanks @brandongoode)
v1.3.x
- added
type="text/javascript"
by default, in response to Safari 9.1.1 bug - removed experimental status of inline option
- added weback 2.2.x beta support
v1.2.x
- added inline option
v1.1.x
- added
type="module"
option
v1.0.x
- initial release