Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including html-crush with all npm packages installed. Try it out:

var htmlCrush = require("html-crush")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

html-crush v1.8.1

Minifies HTML/CSS: valid or broken, pure or mixed with other languages

HTML Crush

Minifies HTML/CSS: valid or broken, pure or mixed with other languages

Minimum Node version required Repository is on GitLab Coverage View dependencies as 2D chart Downloads/Month Test in browser Code style: prettier MIT License

Online web app: htmlcrush.com

Table of Contents

Install

npm i html-crush

Consume via a require():

const { crush, defaults, version } = require("html-crush");

or as an ES Module:

import { crush, defaults, version } from "html-crush";

or for web pages, as a production-ready minified script file (so-called "UMD build"), straight from CDN:

<script src="https://cdn.jsdelivr.net/npm/html-crush/dist/html-crush.umd.js"></script>
// in which case you get a global variable "htmlCrush" which you consume like this:
const { crush, defaults, version } = htmlCrush;

This package has three builds in dist/ folder:

TypeKey in package.jsonPathSize
Main export - CommonJS version, transpiled to ES5, contains require and module.exportsmaindist/html-crush.cjs.js29 KB
ES module build that Webpack/Rollup understands. Untranspiled ES6 code with import/export.moduledist/html-crush.esm.js32 KB
UMD build for browsers, transpiled, minified, containing iife's and has all dependencies baked-inbrowserdist/html-crush.umd.js62 KB

⬆ back to top

Usage

const { crush } = require("html-crush");
const res = crush(
  `  <a>
     <b>
   c </b>
   </a>
     <a>
     <b>
   c </b>
   </a>
     <a>
     <b>
   c </b>
   </a>`,
  { removeLineBreaks: true, lineLengthLimit: 8 }
).result;
console.log("res:\n" + JSON.stringify(res, null, 4));
// => "res:
// <a><b> c
// </b></a>
// <a><b> c
// </b></a>
// <a><b> c
// </b></a>
// "

⬆ back to top

TLDR;

Let's minify any input (preferably but not necessarily HTML) without parsing, and do it according to how HTML interprets the whitespace.

We built this minifier so that we were able to minify email templates containing Nunjucks template literals, for example, {% if order.price < 50 %} (notice the bracket). Other tools on the market will fail upon first encounter of non-HTML code.

⬆ back to top

Features

This program:

  • Does not parse the input — input can be (X)HTML or whatever or mixed with whatever
  • Equally, the input can be with HTML errors, broken HTML, incomplete HTML or not-quite-HTML or whatever
  • Mailchimp, Responsys, Exact Target, Campaign Monitor tags in your HTML - all fine

As a side priority, this application also takes into consideration human-friendliness:

  1. Its API (this npm library) reports progress and its GUI front-end https://htmlcrush.com utilises it to allow a responsive UI
  2. We deliberately keep options count under 7
  3. GUI also considers white and dark interfaces, use of modern toggle switches, CSS hovers to react to any interaction
  4. API (this library) considers giving all possible JavaScript use choices: CommonJS transpiled to ES5, modern untranspiled ES Modules code in ES6, and UMD transpiled to ES5 with all dependencies baked-in, all published to npm and accessible via unpkg CDN
  5. Developer friendliness - source is fully set up with console.logs which report the line numbers and all actions as they happen. Production builds (dist/) strip all logging, of course. This means it's easy to come back later or the first time and debug the code

⬆ back to top

API - Input

This library exports a plain object where main function is under a key "crush". That's why you consume it like this:

import { crush, defaults, version } from "html-crush";

Once you get the function, crush, it's API is the following:

crush(str, [opts]) — in other words, two arguments:

  1. first argument — string,
  2. optional (marked with square brackets above) second argument — options — a plain object
Input argumentKey value's typeObligatory?Description
strStringyesThe input string of zero or more characters
optsPlain objectnoAn Optional Options Object. See below for its API.

If supplied input arguments are of any other types, an error will be thrown.

⬆ back to top

API - Output

The function exported under key crush will return a plain object where you'll find log data, result string and corresponding string ranges of all actions performed:

Key's nameKey value's typeDescription
logPlain objectFor example, { timeTakenInMiliseconds: 6, originalLength: 0, cleanedLength: 0, bytesSaved: 0, percentageReducedOfOriginal: 0 }
rangesArray of zero or more string range arraysFor example, if characters from index 0 to 5 and 30 to 35 were deleted, that would be [[0, 5], [30, 35]]
resultStringThe string version where all ranges were applied to it.

⬆ back to top

Optional Options Object

Options Object's keyThe type of its valueDefaultDescription
{
lineLengthLimitnumber500When removing line breaks, what is the maximum line length to keep. Relevant only when opts.removeLineBreaks is on
removeIndentationsBooleantrueShould we remove indentations? The default is, yes.
removeLineBreaksBooleanfalseShould we remove the line breaks? The default answer is, no. Enabling it automatically enables opts.removeIndentations.
reportProgressFuncnull or Boolean false or functionnullIf you supply a function here, it will be called, and an argument will be given to it, a natural number, which means percentage complete at that moment. Values will range from 1 to 99, and finally, the main function will return the result's plain object.
reportProgressFuncFromNatural number0Default is zero percent but you can squeeze reporting percentages to start from a different number
reportProgressFuncToNatural number100Default is 100 percent but you can squeeze reporting percentages to go up to a different number
breakToTheLeftOfarray of zero or more stringssee belowWhen any of given strings are encountered AND removeLineBreaks option is on, current line will be terminated. This setting is not active if removeLineBreaks is turned off. If you want to disable a default set, either set this key to false or null or to an empty array.
}

Here it is, in one place, in case you want to copy-paste it somewhere:

{
  lineLengthLimit: 500,
  removeIndentations: true,
  removeLineBreaks: false,
  reportProgressFunc: null,
  breakToTheLeftOf: [
    "</td",
    "<html",
    "<head",
    "<meta",
    "<table",
    "<script",
    "</script",
    "<!DOCTYPE",
    "<style",
    "</style",
    "<title",
    "<body",
    "@media",
    "</html",
    "</body",
    "<!--[if",
    "<!--<![endif"
  ]
}

⬆ back to top

opts.reportProgressFunc

This feature is used in a web worker setup. Basically, you pass the web worker the input (source, options) and it passes you one or more messages back. That can be one message, final result, but it can equally be many messages, for example, a sequence of natural numbers, each meaning progress percentage done so far, AND THEN, finally, full result.

This latter case is exactly what is happening on our front-end GUI, https://emailcrush.com

If you set the optional options object's key's reportProgressFunc value to anything else than a function, an error will be thrown. If you set it to a function, that function will be fed a natural number string, meaning percentage done so far, from 1 to 100.

Now, it's up to you how to distinguish "in progress" results and the final result. I use a random string, which is unlikely to happen in the input and I append that secret random string in front of the percentage being passed. Then, front-end checks did result that came through have a secret random string in front or not. If so, it's progress. If not, it's a final result.

Non-deterministic unit tests

This library has usual unit tests written for ava, with coverage tracked by nyc.

Yawn.

We also have non-deterministic unit tests, where the input is random.

The idea is, even if we generate the random input and apply random settings, the result with all whitespace characters wiped should be equal to the original untouched random input with all whitespace characters wiped. In other words, we ensure that no non-whitespace characters were affected by this application. Which is true! This application, at least so far, is not meant to delete any non-whitespace characters and under no settings combination.

Personally, I think this is cool, and I haven't seen it anywhere else.

Here's how to fire it up:

ava test_alt/nondeterministic.js -- --time=3s

Call the file nondeterministic.js located in folder test_alt in ava, pass the duration in seconds you want to generate and run tests. More time, more random tests. Just number, for example -- --time=3 means 3 milliseconds. "s" appended means seconds, for example, -- --time=3s. Three minutes would be -- --time=3m.

⬆ back to top

Contributing

  • If you see an error, raise an issue.
  • If you want a new feature but can't code it up yourself, also raise an issue. Let's discuss it.
  • If you tried to use this package, but something didn't work out, also raise an issue. We'll try to help.
  • If you want to contribute some code, fork the monorepo via GitLab, then write code, then file a pull request on GitLab. We'll merge it in and release.

In monorepo, npm libraries are located in packages/ folder. Inside, the source code is located either in src/ folder (normal npm library) or in the root, cli.js (if it's a command-line application).

The npm script "dev", the "dev": "rollup -c --dev --silent" builds the development version retaining all console.logs with row numbers. It's handy to have js-row-num-cli installed globally so you can automatically update the row numbers on all console.logs.

⬆ back to top

Licence

MIT License

Copyright (c) 2015-2019 Roy Revelt and other contributors

RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free