Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add template content #1401

Merged
merged 1 commit into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 66 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Allowed values are as follows
|**`title`**|`{String}`|`Webpack App`|The title to use for the generated HTML document|
|**`filename`**|`{String}`|`'index.html'`|The file to write the HTML to. Defaults to `index.html`. You can specify a subdirectory here too (eg: `assets/admin.html`)|
|**`template`**|`{String}`|``|`webpack` relative or absolute path to the template. By default it will use `src/index.ejs` if it exists. Please see the [docs](https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md) for details|
|**`templateContent`**|`{string\|Function|false}`|false| Can be used instead of `template` to provide an inline template - please read the [Writing Your Own Templates](https://github.com/jantimon/html-webpack-plugin#writing-your-own-templates) section |
|**`templateParameters`**|`{Boolean\|Object\|Function}`|``| Allows to overwrite the parameters used in the template - see [example](https://github.com/jantimon/html-webpack-plugin/tree/master/examples/template-parameters) |
|**`inject`**|`{Boolean\|String}`|`true`|`true \|\| 'head' \|\| 'body' \|\| false` Inject all assets into the given `template` or `templateContent`. When passing `true` or `'body'` all javascript resources will be placed at the bottom of the body element. `'head'` will place the scripts in the head element - see the [inject:false example](https://github.com/jantimon/html-webpack-plugin/tree/master/examples/custom-insertion-position)|
|**`scriptLoading`**|`{'blocking'\|'defer'}`|`'blocking'`| Modern browsers support non blocking javascript loading (`'defer'`) to improve the page startup performance. |
Expand Down Expand Up @@ -243,40 +244,40 @@ plugins: [

You can use the `lodash` syntax out of the box. If the `inject` feature doesn't fit your needs and you want full control over the asset placement use the [default template](https://github.com/jaketrent/html-webpack-template/blob/86f285d5c790a6c15263f5cc50fd666d51f974fd/index.html) of the [html-webpack-template project](https://github.com/jaketrent/html-webpack-template) as a starting point for writing your own.

The following variables are available in the template:
The following variables are available in the template by default (you can extend them using the `templateParameters` option):

- `htmlWebpackPlugin`: data specific to this plugin
- `htmlWebpackPlugin.files`: a massaged representation of the
`assetsByChunkName` attribute of webpack's [stats](https://github.com/webpack/docs/wiki/node.js-api#stats)
object. It contains a mapping from entry point name to the bundle filename, eg:
```json
"htmlWebpackPlugin": {
"files": {
"css": [ "main.css" ],
"js": [ "assets/head_bundle.js", "assets/main_bundle.js"],
"chunks": {
"head": {
"entry": "assets/head_bundle.js",
"css": [ "main.css" ]
},
"main": {
"entry": "assets/main_bundle.js",
"css": []
},
}
}
}
```
If you've set a publicPath in your webpack config this will be reflected
correctly in this assets hash.

- `htmlWebpackPlugin.options`: the options hash that was passed to
the plugin. In addition to the options actually used by this plugin,
you can use this hash to pass arbitrary data through to your template.

- `webpack`: the webpack [stats](https://github.com/webpack/docs/wiki/node.js-api#stats)
object. Note that this is the stats object as it was at the time the HTML template
was emitted and as such may not have the full set of stats that are available
after the webpack run is complete.
- `htmlWebpackPlugin.tags`: the prepared `headTags` and `bodyTags` Array to render the `<base>`, `<meta>`, `<script>` and `<link>` tags.
Can be used directly in templates and literals. For example:
```html
<html>
<head>
<%= htmlWebpackPlugin.tags.headTags %>
</head>
<body>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>
</html>
```

- `htmlWebpackPlugin.files`: direct access to the files used during the compilation.

```typescript
publicPath: string;
js: string[];
css: string[];
manifest?: string;
favicon?: string;
```

- `htmlWebpackPlugin.options`: the options hash that was passed to
the plugin. In addition to the options actually used by this plugin,
you can use this hash to pass arbitrary data through to your template.

- `webpackConfig`: the webpack configuration that was used for this compilation. This
can be used, for example, to get the `publicPath` (`webpackConfig.output.publicPath`).
Expand All @@ -287,6 +288,43 @@ The following variables are available in the template:
(see [the inline template example](examples/inline/template.pug)).


The template can also be directly inlined directly into the options object.
⚠️ **This approach does not allow to use weboack loaders for your template and will not update the template on changes**

**webpack.config.js**
```js
new HtmlWebpackPlugin({
templateContent: `
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
`
})
```

The `templateContent` can also access all `templateParameters` values.
⚠️ **This approach does not allow to use weboack loaders for your template and will not update the template on changes**

**webpack.config.js**
```js
new HtmlWebpackPlugin({
injext: false
templateContent: ({htmlWebpackPlugin}) => `
<html>
<head>
${htmlWebpackPlugin.tags.headTags}
</head>
<body>
<h1>Hello World</h1>
${htmlWebpackPlugin.tags.bodyTags}
</body>
</html>
`
})
```

### Filtering Chunks

To include only certain chunks you can limit the chunks being used
Expand Down
10 changes: 3 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const path = require('path');
const loaderUtils = require('loader-utils');
const { CachedChildCompilation } = require('./lib/cached-child-compiler');

const { createHtmlTagObject, htmlTagObjectToString } = require('./lib/html-tags');
const { createHtmlTagObject, htmlTagObjectToString, HtmlTagArray } = require('./lib/html-tags');

const prettyError = require('./lib/errors.js');
const chunkSorter = require('./lib/chunksorter.js');
Expand Down Expand Up @@ -822,17 +822,13 @@ class HtmlWebpackPlugin {
*/
prepareAssetTagGroupForRendering (assetTagGroup) {
const xhtml = this.options.xhtml;
const preparedTags = assetTagGroup.map((assetTag) => {
return HtmlTagArray.from(assetTagGroup.map((assetTag) => {
const copiedAssetTag = Object.assign({}, assetTag);
copiedAssetTag.toString = function () {
return htmlTagObjectToString(this, xhtml);
};
return copiedAssetTag;
});
preparedTags.toString = function () {
return this.join('');
};
return preparedTags;
}));
}

/**
Expand Down
24 changes: 24 additions & 0 deletions lib/html-tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,31 @@ function createHtmlTagObject (tagName, attributes, innerHTML) {
};
}

/**
* The `HtmlTagArray Array with a custom `.toString()` method.
*
* This allows the following:
* ```
* const tags = HtmlTagArray.from([tag1, tag2]);
* const scriptTags = tags.filter((tag) => tag.tagName === 'script');
* const html = scriptTags.toString();
* ```
*
* Or inside a string literal:
* ```
* const tags = HtmlTagArray.from([tag1, tag2]);
* const html = `<html><body>${tags.filter((tag) => tag.tagName === 'script')}</body></html>`;
* ```
*
*/
class HtmlTagArray extends Array {
toString () {
return this.join('');
}
}

module.exports = {
HtmlTagArray: HtmlTagArray,
createHtmlTagObject: createHtmlTagObject,
htmlTagObjectToString: htmlTagObjectToString
};
28 changes: 28 additions & 0 deletions spec/basic.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2342,4 +2342,32 @@ describe('HtmlWebpackPlugin', () => {
]
}, [/<selfclosed\/>/], null, done);
});

it('should allow to use headTags and bodyTags directly in string literals', done => {
testHtmlPlugin({
mode: 'production',
entry: path.join(__dirname, 'fixtures/theme.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
module: {
rules: [
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }
]
},
plugins: [
new MiniCssExtractPlugin({ filename: 'styles.css' }),
new HtmlWebpackPlugin({
inject: false,
templateContent: ({ htmlWebpackPlugin }) => `
<html>
<head>${htmlWebpackPlugin.tags.headTags}</head>
<body>${htmlWebpackPlugin.tags.bodyTags}</body>
</html>
`
})
]
}, ['<head><link href="styles.css" rel="stylesheet"></head>', '<script src="index_bundle.js"></script></body>'], null, done);
});
});
1 change: 1 addition & 0 deletions typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ declare namespace HtmlWebpackPlugin {
templateContent:
| false // Use the template option instead to load a file
| string
| ((templateParameters: { [option: string]: any }) => (string | Promise<string>))
| Promise<string>;
/**
* Allows to overwrite the parameters used in the template
Expand Down