html-bundler-webpack-plugin
Advanced tools
Comparing version 0.6.0 to 0.7.0
# Change log | ||
## 0.7.0 (2023-01-29) | ||
- feat: add `postprocess` plugin option | ||
- fix: parsing srcset attribute containing a query as JSON5, e.g. `srcset="image.png?{sizes: [100,200,300], format: 'jpg'}"` | ||
- test: add tests for options, responsive images, exceptions | ||
- docs: update readme | ||
## 0.6.0 (2023-01-28) | ||
- feat: add `sources` options to define custom tags and attributes for resolving source files | ||
- feat: add `extractComments` option to enable/disable saving comments in *.LICENSE.txt file | ||
- feat: add `sources` loader option to define custom tags and attributes for resolving source files | ||
- feat: add `extractComments` plugin option to enable/disable saving comments in *.LICENSE.txt file | ||
- feat: add to default resolving the `data` attribute of `object` tag | ||
- feat: add supports the `responsive-loader` | ||
- fix: resolves excact attribute name w/o leading wildcard | ||
- fix: resolves exact attribute name w/o leading wildcard | ||
- fix: resolves mutiline attributes | ||
@@ -20,4 +26,4 @@ - fix: resolves mutiline values in srcset attribute | ||
## 0.5.0 (2023-01-22) | ||
- feat: add plugin option `test` to process entry files that pass test assertion | ||
- feat: add loader option `preprocessor` to allows pre-processing of content before handling | ||
- feat: add `test` plugin option to process entry files that pass test assertion | ||
- feat: add `preprocessor` loader option to allow pre-processing of content before handling | ||
- test: add test for usage `Handlebars` template engine | ||
@@ -54,8 +60,9 @@ - docs: update readme with new features | ||
- feat: resolving the Webpack alias in the source file name | ||
- feat: option `js` to extract JavaScript files from source scripts loaded in HTML via a `<script>` tag and generates a separate file for it | ||
- feat: option `css` to extract CSS files from source styles loaded in HTML via a `<link>` tag and generates a separate file for it | ||
- feat: add `js` plugin option to extract JavaScript files from source scripts loaded in HTML via a `<script>` tag and generates a separate file for it | ||
- feat: add `css` plugin option to extract CSS files from source styles loaded in HTML via a `<link>` tag and generates a separate file for it | ||
- feat: processes the images, fonts from sources loaded via `<link>`, `<img>` or `<source>` tags and generates a separate file for it | ||
- feat: resolves and extracts images from sources loaded via `url()` in a style (css, scss) | ||
- feat: support auto `publicPath` | ||
## 0.0.1-beta.0 (2023-01-07) | ||
- docs: announcement of the plugin |
{ | ||
"name": "html-bundler-webpack-plugin", | ||
"version": "0.6.0", | ||
"description": "HTML bundler plugin for webpack handels HTML files as entrypoint and extracts CSS, JS, images from their sources loaded in HTML.", | ||
"version": "0.7.0", | ||
"description": "HTML bundler plugin for webpack handels HTML templates as entry point and extracts CSS, JS, images from their sources loaded in HTML.", | ||
"keywords": [ | ||
@@ -12,3 +12,4 @@ "webpack", | ||
"entry", | ||
"entrypoint", | ||
"asset", | ||
"resource", | ||
"scss", | ||
@@ -23,3 +24,2 @@ "css", | ||
"svg", | ||
"png", | ||
"image", | ||
@@ -26,0 +26,0 @@ "extract", |
232
README.md
@@ -9,3 +9,3 @@ <div align="center"> | ||
</h1> | ||
<div>HTML Bundler Plugin is the right way to bundle all resources with your HTML files</div> | ||
<div>HTML Bundler Plugin is the right way to bundle all resources with your HTML templates</div> | ||
</div> | ||
@@ -20,3 +20,3 @@ | ||
This is a new powerful plugin that does exactly what you want, automatically extracts JS, CSS, images, fonts | ||
This is a new powerful plugin that does exactly what you want: automatically extracts JS, CSS, images, fonts | ||
from their sources loaded directly in HTML. | ||
@@ -29,15 +29,13 @@ The generated HTML contains output hashed filenames of processed source files. | ||
💡 **Highlights**: | ||
💡 **Highlights** | ||
- Define your HTML templates in Webpack entry. | ||
- The HTML template is the entry point for all source scripts and styles. | ||
- The HTML template is the entry point. | ||
- Source scripts and styles can be loaded directly in HTML using `<script>` and `<link>` tags. | ||
- All JS and CSS files will be extracted from their sources loaded in HTML tags. | ||
- You can easily inline JS, CSS, SVG, images **without additional plugins and loaders**. | ||
- You can easily use a template engine, e.g. [Nunjucks](https://mozilla.github.io/nunjucks/), [Handlebars](https://handlebarsjs.com) and others **without template loaders** | ||
- All JS and CSS files will be extracted from their sources loaded in HTML. | ||
- You can inline JS, CSS, SVG, images **without additional plugins and loaders**. | ||
- You can use a template engine, e.g. [Nunjucks](https://mozilla.github.io/nunjucks/), [Handlebars](https://handlebarsjs.com) and others **without template loaders** | ||
- This plugin works like the [pug-plugin](https://github.com/webdiscus/pug-plugin) but the entry point is a `HTML` template. | ||
This plugin works like the [pug-plugin](https://github.com/webdiscus/pug-plugin) but the entry point is a `HTML` template. | ||
How to easily build a multipage website with this plugin, see the [Webpack boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate) used the `html-bundler-webpack-plugin`. | ||
How to easily build a multipage website with this plugin, see the [Webpack boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate) used the `html-bundler-webpack-plugin`; | ||
### Simple usage example | ||
@@ -120,2 +118,3 @@ | ||
- [How to use source images in HTML](#recipe-use-images-in-html) | ||
- [How to resize and generate responsive images](#recipe-responsive-images) | ||
- [How to preload source fonts in HTML](#recipe-preload-fonts) | ||
@@ -147,2 +146,3 @@ - [How to inline CSS in HTML](#recipe-inline-css) | ||
- support the `auto` publicPath | ||
- enable/disable extraction of comments to `*.LICENSE.txt` file | ||
@@ -366,2 +366,44 @@ Just one HTML bundler plugin replaces the most used functionality of the plugins and loaders: | ||
### `postprocess` | ||
Type: | ||
```ts | ||
type postprocess = ( | ||
content: string, | ||
info: ResourceInfo, | ||
compilation: Compilation, | ||
) => string|null; | ||
type ResourceInfo = { | ||
verbose: boolean, | ||
isEntry: boolean, | ||
filename: | ||
| string | ||
| ((pathData: PathData) => string), | ||
sourceFile: string, | ||
outputPath: string, | ||
assetFile: string, | ||
}; | ||
``` | ||
Default: `null`<br> | ||
Called after the Webpack compilation. | ||
The `postprocess` have the following arguments: | ||
- `content: string` - a content of processed file | ||
- `info: ResourceInfo` - an info about current file | ||
- `compilation: Compilation` - the Webpack [compilation object](https://webpack.js.org/api/compilation-object/) | ||
If return `null` then the content processed via Webpack is ignored and will be saved a result from the loader. | ||
The `ResourceInfo` have the following properties: | ||
- `verbose: boolean` - whether information should be displayed | ||
- `isEntry: boolean` - if is `true`, the resource is the entry point, otherwise is a resource loaded in the entry point | ||
- `filename: string|function` - a filename of the resource, see [filename](https://webpack.js.org/configuration/output/#outputfilename) | ||
- `sourceFile: string` - a full path of the source file | ||
- `outputPath: string` - a full path of the output directory | ||
- `assetFile: string` - an output asset file relative by outputPath | ||
--- | ||
@@ -392,3 +434,3 @@ | ||
By default, resolves source files in the following attributes: | ||
By default, resolves source files in the following tags and attributes: | ||
@@ -407,12 +449,12 @@ | Tag | Attributes | | ||
To disable the processing of all attributes, set the `source` option as `false`. | ||
To disable the processing of all attributes, set the `sources` option as `false`. | ||
> **Note** | ||
> | ||
> Automatically are processed only attributes containing a relative path or Webpack alias, e.g.: | ||
> - `src="./image.png"` - relative path to local directory | ||
> - `src="../../assets/image.png"` - relative path to parent directory | ||
> - `src="@images/image.png"` - image directory as Webpack alias | ||
> Automatically are processed only attributes containing a relative path or Webpack alias: | ||
> - `src="./image.png"` - a relative path to local directory | ||
> - `src="../../assets/image.png"` - a relative path to parent directory | ||
> - `src="@images/image.png"` - an image directory as Webpack alias | ||
> | ||
> Url values like following are not processed, e.g.: | ||
> Url values are not processed: | ||
> - `src="https://example.com/img/image.png"` | ||
@@ -422,8 +464,33 @@ > - `src="//example.com/img/image.png"` | ||
> | ||
> Other not file values are ignored, e.g.: | ||
> Others not file values are ignored, e.g.: | ||
> - `src="data:image/png; ..."` | ||
> - `src="javascript: ..."` | ||
The default tags can be extended with new attributes or add new tags and attributes. | ||
The `filter` is called for all attributes of the tag defined as defaults and in `sources` option. | ||
The argument is an object containing the properties: | ||
- `tag: string` - a name of the HTML tag | ||
- `attribute: string` - a name of the HTML attribute | ||
- `value: string` - a value of the HTML attribute | ||
- `attributes: string` - all attributes of the tag | ||
- `resourcePath: string` - a path of the HTML template | ||
The processing of an attribute can be ignored by returning `false`. | ||
Examples of using argument properties: | ||
```js | ||
{ | ||
tag: 'img', | ||
// use the destructuring of variables from the object argument | ||
filter: ({ tag, attribute, value, attributes, resourcePath }) => { | ||
if (attribute === 'src') return false; | ||
if (value.endsWith('.webp')) return false; | ||
if ('srcset' in attributes && attributes['srcset'] === '') return false; | ||
if (resourcePath.indexOf('example')) return false; | ||
// otherwise return 'true' or nothing to allow processing | ||
}, | ||
} | ||
``` | ||
The default sources can be extended with new tags and attributes. | ||
For example, add the processing of the `data-src` and `data-srcset` attributes to the `img` tag: | ||
@@ -453,5 +520,5 @@ | ||
You can use the `filter` function to allow processing for specific attributes. | ||
You can use the `filter` function to allow the processing only specific attributes. | ||
For example, use `filter` to allow processing for images in `meta` tag by `content` attribute: | ||
For example, allow processing only for images in `content` attribute of the `meta` tag: | ||
@@ -488,4 +555,3 @@ ```html | ||
attributes: ['content'], | ||
// note: use the destruction of used variables from the object argument | ||
filter: ({ tag, attribute, value, attributes, resourcePath }) => { | ||
filter: ({ attributes }) => { | ||
const allowedNames = ['twitter:image', 'logo']; | ||
@@ -505,13 +571,4 @@ if ('name' in attributes && allowedNames.indexOf(attributes.name) < 0) { | ||
The `filter` parameter is an object containing the properties: | ||
- `tag` the name of the HTML tag | ||
- `attribute` the name of the HTML attribute | ||
- `value` the value of the HTML attribute | ||
- `attributes` all attributes of the tag | ||
- `resourcePath` the path of the HTML file | ||
The filter can disable an attribute of a tag. | ||
The attribute can be ignored by returning `false`, otherwise the function should return nothing. | ||
Filter can disable an attribute of a tag. | ||
For example, disable the processing of default attribute `srcset` of the `img` tag: | ||
@@ -552,7 +609,8 @@ | ||
The `content` argument is raw content of a file.\ | ||
The `loaderContext` argument is an object contained useful properties, e.g.: | ||
- `mode` - the Webpack mode: `production`, `development`, `none` | ||
- `rootContext` - the path to Webpack context | ||
- `resource` - the template file | ||
The `content` argument is the raw content of a file.\ | ||
The `loaderContext` argument is an object contained useful properties: | ||
- `mode: string` - a Webpack mode: `production`, `development`, `none` | ||
- `rootContext: string` - a path to Webpack context | ||
- `resource: string` - a template file, including query | ||
- `resourcePath: string` - a template file | ||
@@ -562,3 +620,4 @@ Complete API see by the [Loader Context](https://webpack.js.org/api/loaders/#the-loader-context). | ||
The preprocessor is called before handling of the content. | ||
This function can be used to replace a placeholder with a variable or compile the content with a template engine, e.g. [Handlebars](https://handlebarsjs.com). | ||
This function can be used to replace a placeholder with a variable or compile the content with a template engine, | ||
such as [Handlebars](https://handlebarsjs.com), [Nunjucks](https://mozilla.github.io/nunjucks). | ||
@@ -570,3 +629,3 @@ For example, set variable in the template | ||
<head> | ||
<title>{{title}}</title> | ||
<title>{{ title }}</title> | ||
</head> | ||
@@ -600,3 +659,3 @@ <body> | ||
options: { | ||
preprocessor: (content, loaderContext) => content.replace('{{title}}', 'Homepage'), | ||
preprocessor: (content, loaderContext) => content.replace('{{ title }}', 'Homepage'), | ||
}, | ||
@@ -615,3 +674,3 @@ }, | ||
> The `preprocessor` will be called for each entry file. | ||
> For multipage configuration, you can use the `loaderContext.resource` property to differentiate data for diverse pages. | ||
> For multipage configuration, you can use the `loaderContext.resourcePath` property to differentiate data for diverse pages. | ||
> See the [usage example](#recipe-pass-data-multipage). | ||
@@ -669,2 +728,74 @@ | ||
<a id="recipe-responsive-images" name="recipe-responsive-images" href="#recipe-responsive-images"></a> | ||
## How to resize and generate responsive images | ||
To resize or generate responsive images is recommended to use the [responsive-loader](https://github.com/dazuaz/responsive-loader). | ||
Install additional packages: | ||
``` | ||
npm i -D responsive-loader sharp | ||
``` | ||
To resize an image use the query parameter `size`: | ||
```html | ||
<!-- resize source image to max. 640px --> | ||
<img src="./image.png?size=640"> | ||
``` | ||
To generate responsible images use in `srcset` attribute the query parameter `sizes` als `JSON5` to avoid parsing error, | ||
because many images must be separated by commas `,` but we use the comma to separate sizes for one image: | ||
```html | ||
<!-- responsible images with different sizes: 320px, 480px, 640px --> | ||
<img src="./image.png?size=480" | ||
srcset="./image.png?{sizes:[320,480,640]}"> | ||
``` | ||
You can convert source image to other output format. | ||
For example, we have original image 2000px width as PNG and want to resize to 640px and save as WEBP: | ||
```html | ||
<img src="./image.png?size=640&format=webp"> | ||
``` | ||
You can create a small inline image placeholder. To do this, use the following query parameters: | ||
- `placeholder=true` - enable to generate the placeholder | ||
- `placeholderSize=35` - the size of the generating placeholder | ||
- `prop=placeholder` - the plugin-specific `prop` parameter retrieves the property from the object generated by `responsive-loader` | ||
```html | ||
<img src="./image.png?placeholder=true&placeholderSize=35&prop=placeholder" | ||
srcset="./image.png?{sizes:[320,480,640]}"> | ||
``` | ||
The generated HTML: | ||
```html | ||
<img src="data:image/png;base64,iVBORw0K ..." | ||
srcset="/img/image-320w.png 320w,/img/image-480w.png 480w,/img/image-640w.png 640w"> | ||
``` | ||
Add to Webpack config the rule for responsive images: | ||
```js | ||
module.exports = { | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.(png|jpe?g|webp)$/i, | ||
type: 'asset/resource', | ||
use: { | ||
loader: 'responsive-loader', | ||
options: { | ||
// output filename of images, e.g. dist/assets/img/image-640w.png | ||
name: 'assets/img/[name]-[width]w.[ext]', | ||
sizes: [640], // max. image size, if 'size' query is not used | ||
}, | ||
}, | ||
}, | ||
// ... other loaders | ||
], | ||
}, | ||
}; | ||
``` | ||
<a id="recipe-preload-fonts" name="recipe-preload-fonts" href="#recipe-preload-fonts"></a> | ||
@@ -861,8 +992,8 @@ ## How to preload source fonts in HTML | ||
<head> | ||
<title>{{title}}</title> | ||
<title>{{ title }}</title> | ||
</head> | ||
<body> | ||
<h1>{{headline}}</h1> | ||
<h1>{{ headline }}</h1> | ||
<div> | ||
<p>{{firstname}} {{lastname}}</p> | ||
<p>{{ firstname }} {{ lastname }}</p> | ||
</div> | ||
@@ -927,3 +1058,3 @@ </body> | ||
For multipage configuration, better to use the [Nunjucks](https://mozilla.github.io/nunjucks/) templating engine maintained by Mozilla. | ||
For multipage configuration, better to use the [Nunjucks](https://mozilla.github.io/nunjucks) templating engine maintained by Mozilla. | ||
@@ -936,3 +1067,3 @@ For example, you have several pages with variables.\ | ||
<head> | ||
<title>{{title}}</title> | ||
<title>{{ title }}</title> | ||
<!-- block for specific page styles --> | ||
@@ -1068,6 +1199,5 @@ {% block styles %}{% endblock %} | ||
options: { | ||
// the deconstucted 'resource' argument is the template file | ||
preprocessor: (content, { resource }) => { | ||
preprocessor: (content, { resourcePath }) => { | ||
// get template variables by template filename | ||
const data = findData(resource, entryData); | ||
const data = findData(resourcePath, entryData); | ||
// render template with specific variables | ||
@@ -1074,0 +1204,0 @@ return Nunjucks.renderString(content, data); |
@@ -97,3 +97,3 @@ const vm = require('vm'); | ||
* @property {boolean} isEntry True if is the asset from entry, false if asset is required from template. | ||
* @property {boolean} [verbose = false] Whether information should be displayed. | ||
* @property {boolean} verbose Whether information should be displayed. | ||
* @property {string|(function(PathData, AssetInfo): string)} filename The filename template or function. | ||
@@ -477,2 +477,3 @@ * @property {string} sourceFile The absolute path to source file. | ||
* Called before a module build has started. | ||
* | ||
* @param {Object} module | ||
@@ -578,3 +579,3 @@ */ | ||
entryAsset: null, | ||
// postprocessInfo | ||
// resourceInfo | ||
isEntry: true, | ||
@@ -663,3 +664,3 @@ verbose: entry.verbose, | ||
entryAsset: entry.filename, | ||
// postprocessInfo | ||
// resourceInfo | ||
isEntry: false, | ||
@@ -817,3 +818,3 @@ verbose: moduleVerbose, | ||
if (pluginModule.postprocess) { | ||
const postprocessInfo = { | ||
const resourceInfo = { | ||
isEntry, | ||
@@ -827,5 +828,5 @@ verbose, | ||
try { | ||
content = pluginModule.postprocess(content, postprocessInfo, this.compilation); | ||
content = pluginModule.postprocess(content, resourceInfo, this.compilation); | ||
} catch (error) { | ||
postprocessException(error, postprocessInfo); | ||
postprocessException(error, resourceInfo); | ||
} | ||
@@ -900,5 +901,2 @@ } | ||
break; | ||
case 'asset/source': | ||
// reserved | ||
break; | ||
// no default | ||
@@ -905,0 +903,0 @@ } |
const Loader = require('./Loader'); | ||
const { isWin, isInline, pathToPosix } = require('./Utils'); | ||
const { indexOf } = require('nunjucks/src/lib'); | ||
@@ -38,2 +39,24 @@ const spaceChars = [' ', '\t', '\n', '\r', '\f']; | ||
/** | ||
* Returns the first index of the searched char if none of the excluded chars was found before it. | ||
* | ||
* @param {string} search The search char. | ||
* @param {string} content Where to search for a char. | ||
* @param {number} startPos The offset in content. | ||
* @param {string} except A string containing chars that should not be before the searched character. | ||
* @return {number} TThe index of the found char, otherwise -1. | ||
*/ | ||
const indexOfChar = (search, content, startPos = 0, except = '') => { | ||
let offset = 0; | ||
let char; | ||
while ((char = content.charAt(startPos))) { | ||
if (char === search) return startPos; | ||
if (offset++ > 0 && except.indexOf(char) > -1) return -1; | ||
startPos++; | ||
} | ||
return -1; | ||
}; | ||
/** | ||
* Whether link tag load a style or other assets. | ||
@@ -102,2 +125,3 @@ * | ||
* Resolve alias: href="@styles/basic.css", href="~Styles/basic.css", href="Styles/basic.css" | ||
* Resolve file with query: srcset="image.png?{sizes: [100,200,300], format: 'jpg'}" | ||
* | ||
@@ -121,23 +145,16 @@ * Ignore: | ||
if (/^(?:\/{1,2})/.test(file) || file.indexOf(':') > -1 || file.startsWith('#')) { | ||
if (/^(?:\/{1,2})/.test(file) || file.startsWith('#') || (file.indexOf(':') > 0 && file.indexOf('?{') < 0)) { | ||
return false; | ||
} | ||
let resolvedFile; | ||
switch (type) { | ||
case 'style': | ||
case 'inline/style': | ||
resolvedFile = Loader.compiler.loaderRequireStyle(file, issuer); | ||
break; | ||
return Loader.compiler.loaderRequireStyle(file, issuer); | ||
case 'script': | ||
case 'inline/script': | ||
resolvedFile = Loader.compiler.loaderRequireScript(file, issuer); | ||
break; | ||
default: | ||
resolvedFile = Loader.compiler.loaderRequire(file, issuer); | ||
break; | ||
return Loader.compiler.loaderRequireScript(file, issuer); | ||
} | ||
return resolvedFile; | ||
return Loader.compiler.loaderRequire(file, issuer); | ||
} | ||
@@ -148,4 +165,2 @@ | ||
* | ||
* TODO: optimize the parsing to save data directly in flat structure. | ||
* | ||
* @param {Array<{}>} tags | ||
@@ -240,8 +255,10 @@ * @return {Array<{}>} | ||
let attrValues = []; | ||
endPos = content.indexOf(close, startPos) + close.length; | ||
endPos = indexOfChar(close, content, startPos, '<'); | ||
if (endPos < 1) { | ||
throw new Error(`The '${tag}' tag hasn't the closing '>' char.`); | ||
throw new Error(`The '${tag}' tag starting at ${startPos} position is missing the closing '>' char.`); | ||
} | ||
endPos += close.length; | ||
const source = content.slice(startPos, endPos); | ||
@@ -379,2 +396,7 @@ let type = 'asset'; | ||
// support for 'responsive-loader' value, e.g.: image.png?{sizes: [100,200,300], format: 'jpg'} | ||
if (srcsetValue.indexOf('?{') > 0) { | ||
return [{ value: srcsetValue, startPos, endPos: srcsetValue.length }]; | ||
} | ||
do { | ||
@@ -381,0 +403,0 @@ currentPos = srcsetValue.indexOf(',', startPos); |
@@ -96,3 +96,2 @@ const { merge } = require('webpack-merge'); | ||
compileResult = loaderOptions.sources === false ? content : HtmlBundler.compile(content, filename, sources); | ||
//console.log('>> ', filename, '\n', compileResult); | ||
@@ -99,0 +98,0 @@ // Note: don't use compileResult.dependencies because it is not available by compile error. |
198074
4300
1290