# htmlnano [![npm version](https://badge.fury.io/js/htmlnano.svg)](http://badge.fury.io/js/htmlnano) [![Build Status](https://travis-ci.org/posthtml/htmlnano.svg?branch=master)](https://travis-ci.org/posthtml/htmlnano) Modular HTML minifier, built on top of the [PostHTML](https://github.com/posthtml/posthtml). Inspired by [cssnano](http://cssnano.co/). > The author of htmlnano is available for hire as a full stack web developer: https://kirillmaltsev.net/services ## [Benchmark](https://github.com/maltsev/html-minifiers-benchmark/blob/master/README.md) [html-minifier@4.0.0]: https://www.npmjs.com/package/html-minifier [htmlnano@0.2.8]: https://www.npmjs.com/package/htmlnano | Website | Source (KB) | [html-minifier@4.0.0] | [htmlnano@0.2.8] | |---------|------------:|----------------:|-----------:| | [stackoverflow.blog](https://stackoverflow.blog/) | 78 | 72 | 66 | | [github.com](https://github.com/) | 215 | 187 | 177 | | [en.wikipedia.org](https://en.wikipedia.org/wiki/Main_Page) | 78 | 73 | 72 | | [npmjs.com](https://www.npmjs.com/features) | 29 | 25 | 25 | | **Avg. minify rate** | 0% | **10%** | **13%** | ## Usage ### Gulp ```bash npm install --save-dev gulp-htmlnano ``` ```js const gulp = require('gulp'); const htmlnano = require('gulp-htmlnano'); const options = { removeComments: false }; gulp.task('default', function() { return gulp .src('./index.html') .pipe(htmlnano(options)) .pipe(gulp.dest('./build')); }); ``` ### Javascript ```js const htmlnano = require('htmlnano'); const options = { removeEmptyAttributes: false, // Disable the module "removeEmptyAttributes" collapseWhitespace: 'conservative' // Pass options to the module "collapseWhitespace" }; // posthtml, posthtml-render, and posthtml-parse options const postHtmlOptions = { sync: true, // https://github.com/posthtml/posthtml#usage lowerCaseTags: true, // https://github.com/posthtml/posthtml-parser#options quoteAllAttributes: false, // https://github.com/posthtml/posthtml-render#options }; htmlnano // "preset" arg might be skipped (see "Presets" section below for more info) // "postHtmlOptions" arg might be skipped .process(html, options, preset, postHtmlOptions) .then(function (result) { // result.html is minified }) .catch(function (err) { console.error(err); }); ``` ### PostHTML Just add `htmlnano` as a final plugin: ```js const posthtml = require('posthtml'); const options = { removeComments: false, // Disable the module "removeComments" collapseWhitespace: 'conservative' // Pass options to the module "collapseWhitespace" }; const posthtmlPlugins = [ /* other PostHTML plugins */ require('htmlnano')(options) ]; const postHtmlOptions = { // See PostHTML docs }; posthtml(posthtmlPlugins) .process(html, posthtmlOptions) .then(function (result) { // result.html is minified }) .catch(function (err) { console.error(err); }); ``` ## Presets A preset is just an object with modules config. Currently the following presets are available: - [safe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.es6) — a default preset for minifying a regular HTML in a safe way (without breaking anything) - [ampSafe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/ampSafe.es6) - same as `safe` preset but for [AMP pages](https://www.ampproject.org/) - [max](https://github.com/posthtml/htmlnano/blob/master/lib/presets/max.es6) - maximal minification (might break some pages) You can use them the following way: ```js const htmlnano = require('htmlnano'); const ampSafePreset = require('htmlnano').presets.ampSafe; const options = { // Your options }; htmlnano .process(html, options, ampSafePreset) .then(function (result) { // result.html is minified }) .catch(function (err) { console.error(err); }); ``` If you skip `preset` argument [`safe`](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.es6) preset would be used by default. If you'd like to define your very own config without any presets pass an empty object as a preset: ```js const htmlnano = require('htmlnano'); const options = { // Your options }; htmlnano .process(html, options, {}) .then(function (result) { // result.html is minified }) .catch(function (err) { console.error(err); }); ``` You might create also your own presets: ```js const htmlnano = require('htmlnano'); // Preset for minifying email templates const emailPreset = { mergeStyles: true, minifyCss: { safe: true }, }; const options = { // Some specific options }; htmlnano .process(html, options, emailPreset) .then(function (result) { // result.html is minified }) .catch(function (err) { console.error(err); }); ``` Feel free [to submit a PR](https://github.com/posthtml/htmlnano/issues/new) with your preset if it might be useful for other developers as well. ## Modules By default the modules should only perform safe transforms, see the module documentation below for details. You can disable modules by passing `false` as option, and enable them by passing `true`. ### collapseAttributeWhitespace Collapse redundant white spaces in list-like attributes (`class`, `rel`, `ping`). ##### Example Source: ```html
``` Minified: ```html
``` ### collapseWhitespace Collapses redundant white spaces (including new lines). It doesn’t affect white spaces in the elements `
``` Minified (with `all`): ```html
hello world!answer
``` Minified (with `aggressive`): ```html
hello world! answer
``` Minified (with `conservative`): ```html
hello world! answer
``` ### deduplicateAttributeValues Remove duplicate values from list-like attributes (`class`, `rel`, `ping`). ##### Example Source: ```html ``` Minified: ```html ``` ### removeComments ##### Options - `safe` – removes all HTML comments except the conditional comments and [``](https://yandex.com/support/webmaster/controlling-robot/html.xml) (default) - `all` — removes all HTML comments ##### Example Source: ```html
``` Minified: ```html
``` ### removeEmptyAttributes Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.es6) attributes. ##### Side effects This module could break your styles or JS if you use selectors with attributes: ```CSS img[style=""] { margin: 10px; } ``` ##### Example Source: ```html ``` Minified: ```html ``` ### removeAttributeQuotes Remove quotes around attributes when possible, see [HTML Standard - 12.1.2.3 Attributes - Unquoted attribute value syntax](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2). ##### Example Source: ```html
``` Minified: ```html
``` ##### Notice The feature is implemented by [posthtml-render's `quoteAllAttributes`](https://github.com/posthtml/posthtml-render#options), which is one of the PostHTML's option. So `removeAttributeQuotes` could be overriden by other PostHTML's plugins and PostHTML's configuration. For example: ```js posthtml([ htmlnano({ removeAttributeQuotes: true }) ]).process(html, { quoteAllAttributes: true }) ``` `removeAttributeQuotes` will not work because PostHTML's `quoteAllAttributes` takes the priority. ### removeUnusedCss Removes unused CSS inside ` ``` Optimized: ```html
``` ### minifyCss Minifies CSS with [cssnano](http://cssnano.co/) inside ` ``` Minified: ```html
``` ### minifyJs Minifies JS using [Terser](https://github.com/fabiosantoscode/terser) inside ` ``` Minified: ```html
``` ### minifyJson Minifies JSON inside ``. ##### Example Source: ```html ``` Minified: ```html ``` ### minifySvg Minifies SVG inside `` tags using [SVGO](https://github.com/svg/svgo/). ##### Options See [the documentation of SVGO](https://github.com/svg/svgo/blob/master/README.md) for all supported options. SVGO options can be passed directly to the `minifySvg` module: ```js htmlnano.process(html, { minifySvg: { plugins: [ { collapseGroups: false }, ], }, }); ``` ##### Example Source: ```html SVG ` ``` Minified: ```html SVG ``` ### minifyConditionalComments Minify content inside conditional comments. ##### Example Source: ```html ``` Minified: ```html ``` ### removeRedundantAttributes Removes redundant attributes from tags if they contain default values: - `method="get"` from `
` - `type="text"` from `
click
``` Processed: ```html
click
``` **frequency** Source: ```html
``` Processed: ```html
``` ### sortAttributes Sort attributes inside elements. The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression. ##### Options - `alphabetical`: Default option. Sort attributes in alphabetical order. - `frequency`: Sort attributes by frequency. ##### Example **alphabetical** Source: ```html ``` Processed: ```html ``` **frequency** Source: ```html image ``` Processed: ```html image ``` ### minifyUrls Convert absolute URL to relative URL using [relateurl](https://www.npmjs.com/package/relateurl). ##### Options The base URL to resolve against. Support `String` & `URL`. ```js htmlnano.process(html, { minifyUrls: 'https://example.com' // Valid configuration }); ``` ```js htmlnano.process(html, { minifyUrls: new URL('https://example.com') // Valid configuration }); ``` ```js htmlnano.process(html, { minifyUrls: false // The module will be disabled }); ``` ```js htmlnano.process(html, { minifyUrls: true // Invalid configuration, the module will be disabled }); ``` ##### Example **Basic Usage** Configuration: ```js htmlnano.process(html, { minifyUrls: 'https://example.com' }); ``` Source: ```html bar ``` Minified: ```html bar ``` **With sub-directory** Configuration: ```js htmlnano.process(html, { minifyUrls: 'https://example.com/foo/baz/' }); ``` Source: ```html bar ``` Minified: ```html bar ``` ## removeOptionalTags Remove certain tags that can be omitted, see [HTML Standard - 13.1.2.4 Optional tags](https://html.spec.whatwg.org/multipage/syntax.html#optional-tags). ##### Example Source: ```html Title

Hi

``` Minified: ```html Title

Hi

``` ##### Notice Due to [the limitation of PostHTML](https://github.com/posthtml/htmlnano/issues/99), htmlnano can't remove only the start tag or the end tag of an element. Currently, htmlnano only supports removing the following optional tags, as htmlnano can remove their start tag and end tag at the same time: - `html` - `head` - `body` - `colgroup` - `tbody` ## Contribute Since the minifier is modular, it's very easy to add new modules: 1. Create a ES6-file inside `lib/modules/` with a function that does some minification. For example you can check [`lib/modules/example.es6`](https://github.com/posthtml/htmlnano/blob/master/lib/modules/example.es6). 2. Add the module's name into one of those [presets](https://github.com/posthtml/htmlnano/tree/master/lib/presets). You can choose either `ampSafe`, `max`, or `safe`. 3. Create a JS-file inside `test/modules/` with some unit-tests. 4. Describe your module in the section "[Modules](https://github.com/posthtml/htmlnano/blob/master/README.md#modules)". 5. Send me a pull request. Other types of contribution (bug fixes, documentation improves, etc) are also welcome! Would like to contribute, but don't have any ideas what to do? Check out [our issues](https://github.com/posthtml/htmlnano/labels/help%20wanted).