Modular HTML minifier, built on top of the PostHTML
Clone or download
Permalink
Failed to load latest commit information.
lib Add preset support with following presets: (#64) Sep 14, 2018
test
.eslintignore Add example module Jan 16, 2016
.eslintrc Upgrade eslint and adjust its config Mar 13, 2018
.gitignore Add preset support with following presets: (#64) Sep 14, 2018
.npmignore Add preset support with following presets: (#64) Sep 14, 2018
.travis.yml Remove old node versions from Travis, disable leak checking in mocha. Sep 4, 2018
CHANGELOG.md Describe version 0.2.0 in CHANGELOG Sep 14, 2018
LICENSE.txt init Jan 12, 2016
README.md Update benchmark (html-minifier@3.5.20 and htmlnano@0.2.0) Sep 14, 2018
index.js Rewrite htmlnano Jan 16, 2016
package.json Bump to 0.2.0 Sep 14, 2018

README.md

htmlnano

npm version Build Status

Modular HTML minifier, built on top of the PostHTML. Inspired by cssnano.

Benchmark

Website Source (KB) html-minifier@3.5.20 htmlnano@0.2.0
stackoverflow.com 258 205 218
github.com 63 51 56
en.wikipedia.org 77 69 74
npmjs.com 32 29 30
Avg. minify rate 0% 15% 9%

Usage

Gulp

npm install --save-dev gulp-htmlnano
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

const htmlnano = require('htmlnano');
const options = {
    removeEmptyAttributes: false, // Disable the module "removeEmptyAttributes"
    collapseWhitespace: 'conservative' // Pass options to the module "collapseWhitespace"
};

htmlnano
    // "preset" arg might be skipped (see "Presets" section below for more info)
    .process(html, options, preset)
    .then(function (result) {
        // result.html is minified
    })
    .catch(function (err) {
        console.error(err);
    });

PostHTML

Just add htmlnano as a final plugin:

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)
];

// "preset" arg might be skipped (see "Presets" section below for more info)
posthtml(posthtmlPlugins, preset)
    .process(html)
    .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 — a default preset for minifying a regular HTML in a safe way (without breaking anything)
  • ampSafe - same as safe preset but for AMP pages
  • max - maximal minification (might break some pages)

You can use them the following way:

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 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:

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:

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 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:

<div class=" content  page  "></div>

Minified:

<div class="content page"></div>

collapseWhitespace

Collapses redundant white spaces (including new lines). It doesn’t affect white spaces in the elements <style>, <textarea>, <script> and <pre>.

Options
  • conservative — collapses all redundant white spaces to 1 space (default)
  • all — collapses all redundant white spaces
Side effects

<i>hello</i> <i>world</i> after minification will be rendered as helloworld. To prevent that use conservative option (this is the default option).

Example

Source:

<div>
    hello  world!
    <style>div  { color: red; }  </style>
</div>

Minified (with all):

<div>hello world!<style>div  { color: red; }  </style></div>

Minified (with conservative):

<div> hello world! <style>div  { color: red; }  </style> </div>

deduplicateAttributeValues

Remove duplicate values from list-like attributes (class, rel, ping).

Example

Source:

<div class="sidebar left sidebar"></div>

Minified:

<div class="sidebar left"></div>

removeComments

Options
  • safe – removes all HTML comments except the conditional comments and <!--noindex--><!--/noindex--> (default)
  • all — removes all HTML comments
Example

Source:

<div><!-- test --></div>

Minified:

<div></div>

removeEmptyAttributes

Removes empty safe-to-remove attributes.

Side effects

This module could break your styles or JS if you use selectors with attributes:

img[style=""] {
    margin: 10px;
}
Example

Source:

<img src="foo.jpg" alt="" style="">

Minified:

<img src="foo.jpg" alt="">

minifyCss

Minifies CSS with cssnano inside <style> tags and style attributes.

Options

See the documentation of cssnano for all supported optimizations. By default CSS is minified with preset default, which shouldn't have any side-effects.

To use another preset or disabled some optimizations pass options to minifyCss module:

htmlnano.process(html, {
    minifyCss: {
        preset: ['default', {
            discardComments: {
                removeAll: true,
            },
        }]
    }
});
Example

Source:

<div>
    <style>
        h1 {
            margin: 10px 10px 10px 10px;
            color: #ff0000;
        }
    </style>
</div>

Minified:

<div>
    <style>h1{margin:10px;color:red}</style>
</div>

minifyJs

Minifies JS with Terser inside <script> tags.

Options

See the API documentation of Terser

Example

Source:

<div>
    <script>
        /* comment */
        const foo = function () {

        };
    </script>
</div>

Minified:

<div>
    <script>const foo=function(){};</script>
</div>

minifyJson

Minifies JSON inside <script type="application/json"></script>.

Example

Source:

<script type="application/json">
{
    "user": "me"
}
</script>

Minified:

<script type="application/json">{"user":"me"}</script>

minifySvg

Minifies SVG inside <svg> tags with SVGO.

Options

See the documentation of SVGO

Example

Source:

<svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect width="100%" height="100%" fill="red" />

    <circle cx="150" cy="100" r="80" fill="green" />

    <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
</svg>`

Minified:

<svg baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="red"/><circle cx="150" cy="100" r="80" fill="green"/><text x="150" y="125" font-size="60" text-anchor="middle" fill="#fff">SVG</text></svg>

removeRedundantAttributes

Removes redundant attributes from tags if they contain default values:

  • method="get" from <form>
  • type="text" from <input>
  • type="submit" from <button>
  • language="javascript" and type="text/javascript" from <script>
  • charset from <script> if it's an external script
  • media="all" from <style> and <link>
Options

This module is disabled by default, change option to true to enable this module.

Side effects

This module could break your styles or JS if you use selectors with attributes:

form[method="get"] {
    color: red;
}
Example

Source:

<form method="get">
    <input type="text">
</form>

Minified:

<form>
    <input>
</form>

collapseBooleanAttributes

Collapses boolean attributes (like disabled) to the minimized form.

Options

If your document uses AMP, set the amphtml flag to collapse additonal, AMP-specific boolean attributes:

"collapseBooleanAttributes": {
    "amphtml": true
}
Side effects

This module could break your styles or JS if you use selectors with attributes:

button[disabled="disabled"] {
    color: red;
}
Example

Source:

<button disabled="disabled">click</button>
<script defer=""></script>

Minified:

<button disabled>click</button>
<script defer></script>

mergeStyles

Merges multiple <style> with the same media and type into one tag. <style scoped>...</style> are skipped.

Example

Source:

<style>h1 { color: red }</style>
<style media="print">div { color: blue }</style>

<style type="text/css" media="print">a {}</style>
<style>div { font-size: 20px }</style>

Minified:

<style>h1 { color: red } div { font-size: 20px }</style>
<style media="print">div { color: blue } a {}</style>

mergeScripts

Merge multiple <script> with the same attributes (id, class, type, async, defer) into one (last) tag.

Side effects

It could break your code if the tags with different attributes share the same variable scope. See the example below.

Example

Source:

<script>const foo = 'A:1';</script>
<script class="test">foo = 'B:1';</script>
<script type="text/javascript">foo = 'A:2';</script>
<script defer>foo = 'C:1';</script>
<script>foo = 'A:3';</script>
<script defer="defer">foo = 'C:2';</script>
<script class="test" type="text/javascript">foo = 'B:2';</script>

Minified:

<script>const foo = 'A:1'; foo = 'A:2'; foo = 'A:3';</script>
<script defer="defer">foo = 'C:1'; foo = 'C:2';</script>
<script class="test" type="text/javascript">foo = 'B:1'; foo = 'B:2';</script>

custom

It's also possible to pass custom modules in the minifier. As a function:

const options = {
    custom: function (tree, options) {
        // Some minification
        return tree;
    }
};

Or as a list of functions:

const options = {
    custom: [
        function (tree, options) {
            // Some minification
            return tree;
        },

        function (tree, options) {
            // Some other minification
            return tree;
        }
    ]
};

options is an object with all options that were passed to the plugin.

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.

  2. Add the module in the modules array. The modules are applied from top to bottom. So you can choose the order for your module.

  3. Create a JS-file inside test/modules/ with some unit-tests.

  4. Describe your module in the section "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.