Skip to content

Commit

Permalink
feat: add attributes option
Browse files Browse the repository at this point in the history
This allows developers to add custom attributes to the generated script
tags. Useful when injecting polyfill scripts and adding a `nomodule`
attribute to it.
  • Loading branch information
SimenB committed Oct 13, 2018
1 parent 8cc5dc5 commit cfb247a
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 14 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ lib/
coverage/
build/
dist/
example/polyfill/polyfill.js
55 changes: 50 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,34 @@ Type: `string`, default: `js`

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

## Examples
#### `attributes`

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

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

See [example](example/dll/) for an example of how to set it up. You can run it
by cloning this repo, running `yarn` followed by `yarn run example`.
An example of this is included in the repository.

Currently only supports script tags.

## Examples

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

### Add a DLL file from `webpack.DllPlugin`

Note: Remember to build the DLL file in a separate build.

See [example](example/dll/) for an example of how to set it up. You can run it
by cloning this repo, running `yarn` followed by `yarn run example`.

#### Webpack config

```js
Expand Down Expand Up @@ -201,6 +214,38 @@ const webpackConfig = {
};
```

### Add a polyfill file you have locally

See [example](example/polyfill/) for an example of how to use it. You can run it
by cloning this repo, running `yarn` followed by `yarn run example`.

#### Webpack config

```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('../../');

const webpackConfig = {
entry: 'entry.js',
devtool: '#source-map',
mode: 'development',
output: {
path: 'dist',
filename: 'index_bundle.js',
},
plugins: [
new HtmlWebpackPlugin(),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, './polyfill.js'),
attributes: {
nomodule: true,
},
}),
],
};
```

[npm-url]: https://npmjs.org/package/add-asset-html-webpack-plugin
[npm-image]: https://img.shields.io/npm/v/add-asset-html-webpack-plugin.svg
[travis-url]: https://travis-ci.org/SimenB/add-asset-html-webpack-plugin
Expand Down
1 change: 1 addition & 0 deletions example/polyfill/entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[].someWeirdFunction();
5 changes: 5 additions & 0 deletions example/polyfill/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// this file is a fake polyfill file

Array.prototype.someWeirdFunction = function() {
console.log('hello!');
};
24 changes: 24 additions & 0 deletions example/polyfill/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('../../');

module.exports = {
// Normally CWD
context: __dirname,
entry: path.join(__dirname, 'entry.js'),
devtool: '#source-map',
mode: 'development',
output: {
path: path.join(__dirname, 'dist'),
filename: 'index_bundle.js',
},
plugins: [
new HtmlWebpackPlugin(),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, './polyfill.js'),
attributes: {
nomodule: true,
},
}),
],
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"postbuild": "prettier lib/* --write",
"cover": "jest --coverage",
"preexample": "npm run clean && npm run build",
"example": "npm run example:dll",
"example": "npm run example:dll && npm run example:polyfill",
"example:dll": "webpack --config example/dll/webpack.config.dll.js && webpack --config example/dll/webpack.config.js",
"example:polyfill": "webpack --config example/polyfill/webpack.config.js",
"lint": "eslint .",
"update-license": "licensor --width 72",
"build-and-update-license": "npm run build && npm run update-license",
Expand Down
49 changes: 41 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,44 @@ import {

export default class AddAssetHtmlPlugin {
constructor(assets = []) {
this.assets = Array.isArray(assets)
? assets.slice().reverse()
: [assets].filter(Boolean);
this.assets = Array.isArray(assets) ? assets.slice().reverse() : [assets];
this.addedAssets = [];
}

/* istanbul ignore next: this would be integration tests */
apply(compiler) {
compiler.hooks.compilation.tap('AddAssetHtmlPlugin', compilation => {
let hook;
let beforeGenerationHook;
let alterAssetTagsHook;

if (HtmlWebpackPlugin.version === 4) {
hook = HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration;
const hooks = HtmlWebpackPlugin.getHooks(compilation);

beforeGenerationHook = hooks.beforeAssetTagGeneration;
alterAssetTagsHook = hooks.alterAssetTags;
} else {
hook = compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration;
const { hooks } = compilation;

beforeGenerationHook = hooks.htmlWebpackPluginBeforeHtmlGeneration;
alterAssetTagsHook = hooks.htmlWebpackPluginAlterAssetTags;
}

hook.tapPromise('AddAssetHtmlPlugin', htmlPluginData =>
beforeGenerationHook.tapPromise('AddAssetHtmlPlugin', htmlPluginData =>
this.addAllAssetsToCompilation(compilation, htmlPluginData),
);

alterAssetTagsHook.tap('AddAssetHtmlPlugin', htmlPluginData => {
const { assetTags } = htmlPluginData;
if (assetTags) {
this.alterAssetsAttributes(assetTags);
} else {
this.alterAssetsAttributes({
scripts: htmlPluginData.body
.concat(htmlPluginData.head)
.filter(({ tagName }) => tagName === 'script'),
});
}
});
});
}

Expand All @@ -42,7 +61,19 @@ export default class AddAssetHtmlPlugin {
return htmlPluginData;
}

// eslint-disable-next-line class-methods-use-this
alterAssetsAttributes(assetTags) {
this.assets
.filter(
asset => asset.attributes && Object.keys(asset.attributes).length > 0,
)
.forEach(asset => {
assetTags.scripts
.map(({ attributes }) => attributes)
.filter(attrs => this.addedAssets.includes(attrs.src))
.forEach(attrs => Object.assign(attrs, asset.attributes));
});
}

async addFileToAssets(
compilation,
htmlPluginData,
Expand Down Expand Up @@ -96,6 +127,8 @@ export default class AddAssetHtmlPlugin {

resolveOutput(compilation, addedFilename, outputPath);

this.addedAssets.push(resolvedPath);

if (includeRelatedFiles) {
const relatedFiles = await globby(`${filepath}.*`);
await Promise.all(
Expand Down
34 changes: 34 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,40 @@ test('filter option should include some files with string option', async () => {
expect(pluginData.assets).toMatchSnapshot();
});

test('filter option should include some files with string option', async () => {
const pluginData = {
scripts: [
{
tagName: 'script',
voidTag: false,
attributes: { src: 'polyfill.js' },
},
{
tagName: 'script',
voidTag: false,
attributes: { src: 'index_bundle.js' },
},
],
styles: [],
meta: [],
};
const plugin = new AddAssetHtmlPlugin({
filepath: path.join(__dirname, 'my-file.js'),
files: 'index.*',
attributes: { nomodule: true },
});

plugin.addedAssets.push('polyfill.js');

await plugin.alterAssetsAttributes(pluginData);

expect(pluginData.scripts).toContainEqual({
tagName: 'script',
voidTag: false,
attributes: { src: 'polyfill.js', nomodule: true },
});
});

test('use globby to find multi file', async () => {
const assets = [{ filepath: './src/*.js' }];
const ret = await handleUrl(assets);
Expand Down

0 comments on commit cfb247a

Please sign in to comment.