Skip to content

Commit cfb247a

Browse files
committed
feat: add attributes option
This allows developers to add custom attributes to the generated script tags. Useful when injecting polyfill scripts and adding a `nomodule` attribute to it.
1 parent 8cc5dc5 commit cfb247a

File tree

8 files changed

+158
-14
lines changed

8 files changed

+158
-14
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ lib/
22
coverage/
33
build/
44
dist/
5+
example/polyfill/polyfill.js

README.md

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,34 @@ Type: `string`, default: `js`
136136

137137
Can be set to `css` to create a `link`-tag instead of a `script`-tag.
138138

139-
## Examples
139+
#### `attributes`
140140

141-
### Add a DLL file from `webpack.DllPlugin`
141+
Type: `object`, default: `{}`
142142

143-
Note: Remember to build the DLL file in a separate build.
143+
Extra attributes to be added to the generated tag. Useful to for instance add
144+
`nomodule` to a polyfill script. The `attributes` object uses the key as the
145+
name of the attribute, and the value as the value of it. If value is simply
146+
`true` no value will be added.
144147

145-
See [example](example/dll/) for an example of how to set it up. You can run it
146-
by cloning this repo, running `yarn` followed by `yarn run example`.
148+
An example of this is included in the repository.
149+
150+
Currently only supports script tags.
151+
152+
## Examples
147153

148154
When adding assets, it's added to the start of the array, so when
149155
`html-webpack-plugin` injects the assets, it's before other assets. If you
150156
depend on some order for the assets beyond that, and ordering the plugins
151157
doesn't cut it, you'll have to create a custom template and add the tags
152158
yourself.
153159

160+
### Add a DLL file from `webpack.DllPlugin`
161+
162+
Note: Remember to build the DLL file in a separate build.
163+
164+
See [example](example/dll/) for an example of how to set it up. You can run it
165+
by cloning this repo, running `yarn` followed by `yarn run example`.
166+
154167
#### Webpack config
155168

156169
```js
@@ -201,6 +214,38 @@ const webpackConfig = {
201214
};
202215
```
203216

217+
### Add a polyfill file you have locally
218+
219+
See [example](example/polyfill/) for an example of how to use it. You can run it
220+
by cloning this repo, running `yarn` followed by `yarn run example`.
221+
222+
#### Webpack config
223+
224+
```js
225+
const path = require('path');
226+
const HtmlWebpackPlugin = require('html-webpack-plugin');
227+
const AddAssetHtmlPlugin = require('../../');
228+
229+
const webpackConfig = {
230+
entry: 'entry.js',
231+
devtool: '#source-map',
232+
mode: 'development',
233+
output: {
234+
path: 'dist',
235+
filename: 'index_bundle.js',
236+
},
237+
plugins: [
238+
new HtmlWebpackPlugin(),
239+
new AddAssetHtmlPlugin({
240+
filepath: path.resolve(__dirname, './polyfill.js'),
241+
attributes: {
242+
nomodule: true,
243+
},
244+
}),
245+
],
246+
};
247+
```
248+
204249
[npm-url]: https://npmjs.org/package/add-asset-html-webpack-plugin
205250
[npm-image]: https://img.shields.io/npm/v/add-asset-html-webpack-plugin.svg
206251
[travis-url]: https://travis-ci.org/SimenB/add-asset-html-webpack-plugin

example/polyfill/entry.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[].someWeirdFunction();

example/polyfill/polyfill.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// this file is a fake polyfill file
2+
3+
Array.prototype.someWeirdFunction = function() {
4+
console.log('hello!');
5+
};

example/polyfill/webpack.config.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const path = require('path');
2+
const HtmlWebpackPlugin = require('html-webpack-plugin');
3+
const AddAssetHtmlPlugin = require('../../');
4+
5+
module.exports = {
6+
// Normally CWD
7+
context: __dirname,
8+
entry: path.join(__dirname, 'entry.js'),
9+
devtool: '#source-map',
10+
mode: 'development',
11+
output: {
12+
path: path.join(__dirname, 'dist'),
13+
filename: 'index_bundle.js',
14+
},
15+
plugins: [
16+
new HtmlWebpackPlugin(),
17+
new AddAssetHtmlPlugin({
18+
filepath: path.resolve(__dirname, './polyfill.js'),
19+
attributes: {
20+
nomodule: true,
21+
},
22+
}),
23+
],
24+
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
"postbuild": "prettier lib/* --write",
1414
"cover": "jest --coverage",
1515
"preexample": "npm run clean && npm run build",
16-
"example": "npm run example:dll",
16+
"example": "npm run example:dll && npm run example:polyfill",
1717
"example:dll": "webpack --config example/dll/webpack.config.dll.js && webpack --config example/dll/webpack.config.js",
18+
"example:polyfill": "webpack --config example/polyfill/webpack.config.js",
1819
"lint": "eslint .",
1920
"update-license": "licensor --width 72",
2021
"build-and-update-license": "npm run build && npm run update-license",

src/index.js

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,44 @@ import {
1212

1313
export default class AddAssetHtmlPlugin {
1414
constructor(assets = []) {
15-
this.assets = Array.isArray(assets)
16-
? assets.slice().reverse()
17-
: [assets].filter(Boolean);
15+
this.assets = Array.isArray(assets) ? assets.slice().reverse() : [assets];
16+
this.addedAssets = [];
1817
}
1918

2019
/* istanbul ignore next: this would be integration tests */
2120
apply(compiler) {
2221
compiler.hooks.compilation.tap('AddAssetHtmlPlugin', compilation => {
23-
let hook;
22+
let beforeGenerationHook;
23+
let alterAssetTagsHook;
2424

2525
if (HtmlWebpackPlugin.version === 4) {
26-
hook = HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration;
26+
const hooks = HtmlWebpackPlugin.getHooks(compilation);
27+
28+
beforeGenerationHook = hooks.beforeAssetTagGeneration;
29+
alterAssetTagsHook = hooks.alterAssetTags;
2730
} else {
28-
hook = compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration;
31+
const { hooks } = compilation;
32+
33+
beforeGenerationHook = hooks.htmlWebpackPluginBeforeHtmlGeneration;
34+
alterAssetTagsHook = hooks.htmlWebpackPluginAlterAssetTags;
2935
}
3036

31-
hook.tapPromise('AddAssetHtmlPlugin', htmlPluginData =>
37+
beforeGenerationHook.tapPromise('AddAssetHtmlPlugin', htmlPluginData =>
3238
this.addAllAssetsToCompilation(compilation, htmlPluginData),
3339
);
40+
41+
alterAssetTagsHook.tap('AddAssetHtmlPlugin', htmlPluginData => {
42+
const { assetTags } = htmlPluginData;
43+
if (assetTags) {
44+
this.alterAssetsAttributes(assetTags);
45+
} else {
46+
this.alterAssetsAttributes({
47+
scripts: htmlPluginData.body
48+
.concat(htmlPluginData.head)
49+
.filter(({ tagName }) => tagName === 'script'),
50+
});
51+
}
52+
});
3453
});
3554
}
3655

@@ -42,7 +61,19 @@ export default class AddAssetHtmlPlugin {
4261
return htmlPluginData;
4362
}
4463

45-
// eslint-disable-next-line class-methods-use-this
64+
alterAssetsAttributes(assetTags) {
65+
this.assets
66+
.filter(
67+
asset => asset.attributes && Object.keys(asset.attributes).length > 0,
68+
)
69+
.forEach(asset => {
70+
assetTags.scripts
71+
.map(({ attributes }) => attributes)
72+
.filter(attrs => this.addedAssets.includes(attrs.src))
73+
.forEach(attrs => Object.assign(attrs, asset.attributes));
74+
});
75+
}
76+
4677
async addFileToAssets(
4778
compilation,
4879
htmlPluginData,
@@ -96,6 +127,8 @@ export default class AddAssetHtmlPlugin {
96127

97128
resolveOutput(compilation, addedFilename, outputPath);
98129

130+
this.addedAssets.push(resolvedPath);
131+
99132
if (includeRelatedFiles) {
100133
const relatedFiles = await globby(`${filepath}.*`);
101134
await Promise.all(

test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,40 @@ test('filter option should include some files with string option', async () => {
241241
expect(pluginData.assets).toMatchSnapshot();
242242
});
243243

244+
test('filter option should include some files with string option', async () => {
245+
const pluginData = {
246+
scripts: [
247+
{
248+
tagName: 'script',
249+
voidTag: false,
250+
attributes: { src: 'polyfill.js' },
251+
},
252+
{
253+
tagName: 'script',
254+
voidTag: false,
255+
attributes: { src: 'index_bundle.js' },
256+
},
257+
],
258+
styles: [],
259+
meta: [],
260+
};
261+
const plugin = new AddAssetHtmlPlugin({
262+
filepath: path.join(__dirname, 'my-file.js'),
263+
files: 'index.*',
264+
attributes: { nomodule: true },
265+
});
266+
267+
plugin.addedAssets.push('polyfill.js');
268+
269+
await plugin.alterAssetsAttributes(pluginData);
270+
271+
expect(pluginData.scripts).toContainEqual({
272+
tagName: 'script',
273+
voidTag: false,
274+
attributes: { src: 'polyfill.js', nomodule: true },
275+
});
276+
});
277+
244278
test('use globby to find multi file', async () => {
245279
const assets = [{ filepath: './src/*.js' }];
246280
const ret = await handleUrl(assets);

0 commit comments

Comments
 (0)