npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

metalsmith-optimize-html

v0.8.3

Published

A Metalsmith plugin to optimize HTML files

Readme

Metalsmith Optimize HTML

metalsmith: plugin npm: version license: MIT Coverage ESM/CommonJS Known Vulnerabilities

A Metalsmith plugin for optimizing and minifying HTML files

A modern, modular HTML optimizer for Metalsmith that reduces file sizes by removing unnecessary whitespace, comments, and redundant markup while preserving functionality.

Features

  • Modular Architecture: Enable only the optimizations you need
  • Dual Module Support: Works with both ESM and CommonJS imports
  • Safe Optimizations: Preserves functionality while reducing file size
  • Comprehensive Options: Fine-grained control over optimizations
  • Tag Exclusion: Exclude specific tags from optimization
  • Validation: Robust input validation with helpful error messages
  • Reliable Compatibility: Fixed in v0.5.3 to work correctly in CommonJS environments
  • ESM and CommonJS support:
    • ESM: import optimizeHTML from 'metalsmith-optimize-html';
    • CommonJS: const optimizeHTML = require('metalsmith-optimize-html';')`

Why this plugin?

Metalsmith HTML Minifier was historically the standard plugin for HTML optimization, but it became abandoned with security issues. This plugin is built from scratch with modern JavaScript practices, a functional approach, and minimal dependencies. It offers more granular control over optimizations while maintaining better security and compatibility with current projects.

Installation

npm install metalsmith-optimize-html

Usage

JavaScript API

Metalsmith(__dirname).use(
  optimizeHTML({
    // options
    removeComments: true,
    removeTagSpaces: true
  })
);

CLI Usage

In your metalsmith.json:

{
  "plugins": {
    "metalsmith-optimize-html": {
      "removeComments": true,
      "removeTagSpaces": true,
      "simplifyDoctype": true
    }
  }
}

Examples

Basic Usage

import Metalsmith from 'metalsmith';
import optimizeHTML from 'metalsmith-optimize-html';

Metalsmith(__dirname)
  .use(optimizeHTML({
    removeComments: true,
    removeTagSpaces: true
  }))
  .build((err) => {
    if (err) throw err;
    console.log('Build complete!');
  });

Aggressive Optimization

Enable all optimizations for maximum compression:

Metalsmith(__dirname)
  .use(optimizeHTML({
    aggressive: true
  }))
  .build();

Custom Configuration

Fine-tune specific optimizations:

Metalsmith(__dirname)
  .use(optimizeHTML({
    removeComments: true,
    removeTagSpaces: true,
    normalizeBooleanAttributes: true,
    cleanUrlAttributes: true,
    removeProtocols: true,
    removeDefaultAttributes: true,
    simplifyDoctype: true,
    safeRemoveAttributeQuotes: true,
    pattern: '**/*.html', // Only process HTML files
    excludeTags: ['pre', 'code'] // Preserve content in these tags
  }))
  .build();

With Site-wide Configuration

Use Metalsmith metadata for global settings:

Metalsmith(__dirname)
  .metadata({
    htmlOptimization: {
      removeComments: true,
      removeTagSpaces: true
    }
  })
  .use(optimizeHTML({
    // Local options will override metadata settings
    simplifyDoctype: true
  }))
  .build();

Production vs Development

const isProduction = process.env.NODE_ENV === 'production';

Metalsmith(__dirname)
  .use(optimizeHTML({
    aggressive: isProduction,
    removeComments: isProduction,
    // Keep readable formatting in development
    removeTagSpaces: isProduction
  }))
  .build();

Before and After

Input HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <!-- This is a comment -->
    <title>  My Website  </title>
    <script type="text/javascript" src="app.js"></script>
  </head>
  <body>
    <div   class="container"   id="main">
      <p>   Hello    world!   </p>
      <input type="checkbox" checked="checked" />
    </div>
  </body>
</html>

Output HTML (with aggressive optimization):

<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
<script src=app.js></script>
</head>
<body>
<div class=container id=main>
<p>Hello world!</p>
<input type=checkbox checked />
</div>
</body>
</html>

Options

The plugin validates all options for correct types and will throw detailed error messages if invalid options are provided. This helps catch configuration mistakes early.

Core Optimization (Always On)

Whitespace normalization

  • Collapses multiple whitespace to single space
  • Removes whitespace between HTML tags
  • Preserves whitespace in <pre>, <code>, <textarea>, <script>, <style> tags

Optional Optimizations

removeComments: boolean (default: false)

  • Removes all HTML comments
<!-- This comment will be removed -->

removeTagSpaces: boolean (default: false)

  • Removes extra spaces inside HTML tags
  • Normalizes spaces between attributes
<div class="example" id="test">
  <!-- becomes -->
  <div class="example" id="test"></div>
</div>

removeDefaultAttributes: boolean (default: false)

  • Removes common default attributes that browsers assume anyway
<script type="text/javascript" src="main.js">
<link type="text/css" rel="stylesheet">
<form method="get">
<input type="text">
<!-- becomes -->
<script src="main.js">
<link rel="stylesheet">
<form>
<input>

normalizeBooleanAttributes: boolean (default: false)

  • Normalizes boolean attributes to their shorter form
<input type="checkbox" checked="checked" disabled="disabled" />
<select multiple="multiple">
  <!-- becomes -->
  <input type="checkbox" checked disabled />
  <select multiple></select>
</select>

cleanUrlAttributes: boolean (default: false)

  • Cleans and normalizes URLs in href, src, action, srcset, and data attributes
  • Removes unnecessary whitespace in URLs
<a href="  https://example.com/page   ">
  <!-- becomes -->
  <a href="https://example.com/page"></a
></a>

cleanDataAttributes: boolean (default: false)

  • Normalizes whitespace in data-* attribute values
  • Maintains valid JSON in data attributes
<div data-config='{ "key" :  "value" }'>
  <!-- becomes -->
  <div data-config='{"key":"value"}'></div>
</div>

removeEmptyAttributes: boolean (default: false)

  • Removes attributes with empty values
<div id="" class="   " data-value="">
  <!-- becomes -->
  <div></div>
</div>

removeProtocols: boolean (default: false)

  • Converts URLs to protocol-relative URLs
  • Preserves protocols in links with rel="external"
<a href="https://example.com">
  <a href="http://example.com" rel="external">
    <!-- becomes -->
    <a href="//example.com"> <a href="http://example.com" rel="external"></a></a></a
></a>

simplifyDoctype: boolean (default: false)

  • Replaces any DOCTYPE declaration with HTML5 DOCTYPE
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<!-- becomes -->
<!DOCTYPE html>

safeRemoveAttributeQuotes: boolean (default: false)

  • Only removes quotes if value contains no special characters
  • Preserves quotes for values with spaces, brackets, etc.
<div class="example" id="test" data-value='{"key":"value"}'>
  <!-- becomes -->
  <div class="example" id="test" data-value='{"key":"value"}'></div>
</div>

aggressive: boolean (default: false)

  • Enables all optimizations with a single option:
    • removeComments
    • removeTagSpaces
    • removeDefaultAttributes
    • normalizeBooleanAttributes
    • cleanUrlAttributes
    • cleanDataAttributes
    • removeEmptyAttributes
    • removeProtocols
    • simplifyDoctype
    • safeRemoveAttributeQuotes
Metalsmith(__dirname).use(optimizeHTML({ aggressive: true }));

All individual option settings are ignored when aggressive is true, except when explicitly overridden:

Metalsmith(__dirname).use(
  optimizeHTML({
    aggressive: true,
    removeComments: false // This override will be respected
  })
);

Debugging

Debug messages can be enabled by setting the DEBUG environment variable.

metalsmith.env( 'DEBUG', 'metalsmith-optimize-html' );

Limitations and Edge Cases

Malformed HTML Comments

  • The plugin cannot safely handle nested or malformed HTML comments
  • If a comment is not properly closed, it might affect subsequent content

Preserved Content

  • Content within <pre>, <code>, <textarea>, <script>, and <style> tags is preserved
  • Whitespace and formatting within these tags remains untouched

URL Processing

  • Protocol removal only affects http:// and https:// protocols
  • Other protocols (ftp://, ws://, etc.) remain unchanged
  • Handles URLs in meta tags (og:url, twitter:url, canonical)
  • Processes SVG attributes (xmlns, xlink:href, href, src)

Data Attributes

  • JSON values in data attributes must be valid JSON
  • Invalid JSON structures are left unchanged

Test Coverage

This project maintains high statement and line coverage for the source code. Coverage is automatically verified and updated via GitHub Actions using the c8 coverage tool.

| Statements | Branches | Functions | Lines | | ---------- | -------- | --------- | ----- | | 95% | 87% | 89% | 95% |

Author

[email protected]

License

MIT