html-minifier-next 1.1.5 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,7 +41,7 @@ const { minify } = require('html-minifier-next');
41
41
  const result = await minify('<p title="blah" id="moo">foo</p>', {
42
42
  removeAttributeQuotes: true,
43
43
  });
44
- result; // '<p title=blah id=moo>foo</p>'
44
+ result; // “<p title=blah id=moo>foo</p>”
45
45
  ```
46
46
 
47
47
  See [the original blog post](http://perfectionkills.com/experimenting-with-html-minifier) for details of [how it works](http://perfectionkills.com/experimenting-with-html-minifier#how_it_works), [description of each option](http://perfectionkills.com/experimenting-with-html-minifier#options), [testing results](http://perfectionkills.com/experimenting-with-html-minifier#field_testing), and [conclusions](http://perfectionkills.com/experimenting-with-html-minifier#cost_and_benefits).
@@ -54,21 +54,22 @@ How does HTMLMinifier compare to other solutions, like [minimize](https://github
54
54
 
55
55
  | Site | Original size (KB) | HTMLMinifier | minimize | htmlcompressor.com |
56
56
  | --- | --- | --- | --- | --- |
57
- | [Amazon](https://www.amazon.com/) | 695 | **625** | 682 | n/a |
58
- | [BBC](https://www.bbc.co.uk/) | 655 | **602** | 649 | n/a |
59
- | [ECMAScript](https://tc39.es/ecma262/) | 7197 | **6353** | 6573 | n/a |
60
- | [EFF](https://www.eff.org/) | 60 | **51** | 54 | n/a |
61
- | [Eloquent JavaScript](https://eloquentjavascript.net/) | 6 | **5** | 6 | n/a |
62
- | [FAZ](https://www.faz.net/aktuell/) | 1793 | **1667** | 1705 | n/a |
63
- | [Frontend Dogma](https://frontenddogma.com/) | 116 | **112** | 125 | n/a |
64
- | [Google](https://www.google.com/) | 51 | **46** | 51 | n/a |
65
- | [HTMLMinifier](https://github.com/kangax/html-minifier) | 366 | **245** | 343 | n/a |
66
- | [Mastodon](https://mastodon.social/explore) | 37 | **27** | 36 | n/a |
67
- | [NBC](https://www.nbc.com/) | 1022 | **932** | 1010 | n/a |
68
- | [New York Times](https://www.nytimes.com/) | 951 | **809** | 939 | n/a |
69
- | [United Nations](https://www.un.org/) | 9 | **7** | 8 | n/a |
70
- | [W3C](https://www.w3.org/) | 51 | **36** | 42 | n/a |
71
- | [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | 114 | **100** | 107 | n/a |
57
+ | [A List Apart](https://alistapart.com/) | 64 | **54** | 59 | 57 |
58
+ | [Amazon](https://www.amazon.com/) | 206 | **195** | 203 | 200 |
59
+ | [BBC](https://www.bbc.co.uk/) | 767 | **703** | 761 | n/a |
60
+ | [CSS-Tricks](https://css-tricks.com/) | 166 | **124** | 152 | 148 |
61
+ | [ECMAScript](https://tc39.es/ecma262/) | 7204 | **6361** | 6581 | n/a |
62
+ | [EFF](https://www.eff.org/) | 57 | **48** | 52 | 52 |
63
+ | [FAZ](https://www.faz.net/aktuell/) | 1767 | **1641** | 1679 | n/a |
64
+ | [Frontend Dogma](https://frontenddogma.com/) | 119 | **114** | 128 | 118 |
65
+ | [Google](https://www.google.com/) | 51 | **46** | 50 | 50 |
66
+ | [HTMLMinifier](https://github.com/kangax/html-minifier) | 373 | **250** | 349 | n/a |
67
+ | [Mastodon](https://mastodon.social/explore) | 37 | **28** | 36 | 36 |
68
+ | [NBC](https://www.nbc.com/) | 601 | **549** | 593 | n/a |
69
+ | [New York Times](https://www.nytimes.com/) | 822 | **701** | 811 | n/a |
70
+ | [United Nations](https://www.un.org/) | 9 | **7** | 8 | 8 |
71
+ | [W3C](https://www.w3.org/) | 50 | **36** | 41 | 39 |
72
+ | [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | 225 | **204** | 215 | 215 |
72
73
 
73
74
  ## Options quick reference
74
75
 
@@ -76,7 +77,7 @@ Most of the options are disabled by default.
76
77
 
77
78
  | Option | Description | Default |
78
79
  | --- | --- | --- |
79
- | `caseSensitive` | Treat attributes in case sensitive manner (useful for custom HTML tags) | `false` |
80
+ | `caseSensitive` | Treat attributes in case sensitive manner (useful for custom HTML elements) | `false` |
80
81
  | `collapseBooleanAttributes` | [Omit attribute values from boolean attributes](http://perfectionkills.com/experimenting-with-html-minifier#collapse_boolean_attributes) | `false` |
81
82
  | `customFragmentQuantifierLimit` | Set maximum quantifier limit for custom fragments to prevent ReDoS attacks | `200` |
82
83
  | `collapseInlineTagWhitespace` | Don’t leave any spaces between `display:inline;` elements when collapsing. Must be used in conjunction with `collapseWhitespace=true` | `false` |
@@ -91,7 +92,8 @@ Most of the options are disabled by default.
91
92
  | `html5` | Parse input according to HTML5 specifications | `true` |
92
93
  | `ignoreCustomComments` | Array of regexes that allow to ignore certain comments, when matched | `[ /^!/, /^\s*#/ ]` |
93
94
  | `ignoreCustomFragments` | Array of regexes that allow to ignore certain fragments, when matched (e.g. `<?php ... ?>`, `{{ ... }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` |
94
- | `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` |
95
+ | `includeAutoGeneratedTags` | Insert elements generated by HTML parser | `true` |
96
+ | `inlineCustomElements` | Array of names of custom elements which are inline | `[]` |
95
97
  | `keepClosingSlash` | Keep the trailing slash on singleton elements | `false` |
96
98
  | `maxInputLength` | Maximum input length to prevent ReDoS attacks (disabled by default) | `undefined` |
97
99
  | `maxLineLength` | Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points |
@@ -110,8 +112,8 @@ Most of the options are disabled by default.
110
112
  | `removeEmptyElements` | [Remove all elements with empty contents](http://perfectionkills.com/experimenting-with-html-minifier#remove_empty_elements) | `false` |
111
113
  | `removeOptionalTags` | [Remove optional tags](http://perfectionkills.com/experimenting-with-html-minifier#remove_optional_tags) | `false` |
112
114
  | `removeRedundantAttributes` | [Remove attributes when value matches default.](http://perfectionkills.com/experimenting-with-html-minifier#remove_redundant_attributes) | `false` |
113
- | `removeScriptTypeAttributes` | Remove `type="text/javascript"` from `script` tags. Other `type` attribute values are left intact | `false` |
114
- | `removeStyleLinkTypeAttributes`| Remove `type="text/css"` from `style` and `link` tags. Other `type` attribute values are left intact | `false` |
115
+ | `removeScriptTypeAttributes` | Remove `type="text/javascript"` from `script` elements. Other `type` attribute values are left intact | `false` |
116
+ | `removeStyleLinkTypeAttributes`| Remove `type="text/css"` from `style` and `link` elements. Other `type` attribute values are left intact | `false` |
115
117
  | `removeTagWhitespace` | Remove space between attributes whenever possible. **Note that this will result in invalid HTML!** | `false` |
116
118
  | `sortAttributes` | [Sort attributes by frequency](#sorting-attributes--style-classes) | `false` |
117
119
  | `sortClassName` | [Sort style classes by frequency](#sorting-attributes--style-classes) | `false` |
@@ -130,11 +132,11 @@ If you have chunks of markup you would like preserved, you can wrap them `<!-- h
130
132
 
131
133
  ### Minifying JSON-LD
132
134
 
133
- You can minify script tags with JSON-LD by setting the option `{ processScripts: ['application/ld+json'] }`. Note that this minification is very rudimentary, it is mainly useful for removing newlines and excessive whitespace.
135
+ You can minify script elements with JSON-LD by setting the option `{ processScripts: ['application/ld+json'] }`. Note that this minification is very rudimentary, it is mainly useful for removing newlines and excessive whitespace.
134
136
 
135
- ### Preserving SVG tags
137
+ ### Preserving SVG elements
136
138
 
137
- SVG tags are automatically recognized, and when they are minified, both case-sensitivity and closing-slashes are preserved, regardless of the minification settings used for the rest of the file.
139
+ SVG elements are automatically recognized, and when they are minified, both case-sensitivity and closing-slashes are preserved, regardless of the minification settings used for the rest of the file.
138
140
 
139
141
  ### Working with invalid markup
140
142
 
package/cli.js CHANGED
@@ -113,6 +113,7 @@ const mainOptions = {
113
113
  ignoreCustomComments: ['Array of regex\'es that allow to ignore certain comments, when matched', parseJSONRegExpArray],
114
114
  ignoreCustomFragments: ['Array of regex\'es that allow to ignore certain fragments, when matched (e.g. <?php ... ?>, {{ ... }})', parseJSONRegExpArray],
115
115
  includeAutoGeneratedTags: 'Insert tags generated by HTML parser',
116
+ inlineCustomElements: ['Array of names of custom elements which are inline', parseJSONArray],
116
117
  keepClosingSlash: 'Keep the trailing slash on singleton elements',
117
118
  maxInputLength: ['Maximum input length to prevent ReDoS attacks', parseInt],
118
119
  maxLineLength: ['Max line length', parseInt],
@@ -140,7 +141,7 @@ const mainOptions = {
140
141
  useShortDoctype: 'Replaces the doctype with the short (HTML5) doctype'
141
142
  };
142
143
 
143
- // configure commandline flags
144
+ // Configure command line flags
144
145
  const mainOptionKeys = Object.keys(mainOptions);
145
146
  mainOptionKeys.forEach(function (key) {
146
147
  const option = mainOptions[key];
@@ -304,4 +305,4 @@ if (inputDir || outputDir) {
304
305
  process.stdin.on('data', function (data) {
305
306
  content += data;
306
307
  }).on('end', writeMinify);
307
- }
308
+ }
@@ -496,9 +496,7 @@ class TokenChain {
496
496
  }
497
497
  }
498
498
 
499
- function trimWhitespace(str) {
500
- return str && str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
501
- }
499
+ const trimWhitespace = str => str && str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
502
500
 
503
501
  function collapseWhitespaceAll(str) {
504
502
  // Non-breaking space is specifically handled inside the replacer function here:
@@ -550,14 +548,14 @@ function collapseWhitespace(str, options, trimLeft, trimRight, collapseAll) {
550
548
  return lineBreakBefore + str + lineBreakAfter;
551
549
  }
552
550
 
553
- // non-empty tags that will maintain whitespace around them
554
- const inlineTags = new Set(['a', 'abbr', 'acronym', 'b', 'bdi', 'bdo', 'big', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'ins', 'kbd', 'label', 'mark', 'math', 'nobr', 'object', 'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'tt', 'u', 'var']);
555
- // non-empty tags that will maintain whitespace within them
551
+ // non-empty elements that will maintain whitespace around them
552
+ const inlineHtmlElements = ['a', 'abbr', 'acronym', 'b', 'bdi', 'bdo', 'big', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'mark', 'math', 'meter', 'nobr', 'object', 'output', 'progress', 'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'tt', 'u', 'var', 'wbr'];
553
+ // non-empty elements that will maintain whitespace within them
556
554
  const inlineTextTags = new Set(['a', 'abbr', 'acronym', 'b', 'big', 'del', 'em', 'font', 'i', 'ins', 'kbd', 'mark', 'nobr', 'rp', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'time', 'tt', 'u', 'var']);
557
- // self-closing tags that will maintain whitespace around them
555
+ // self-closing elements that will maintain whitespace around them
558
556
  const selfClosingInlineTags = new Set(['comment', 'img', 'input', 'wbr']);
559
557
 
560
- function collapseWhitespaceSmart(str, prevTag, nextTag, options) {
558
+ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineTags) {
561
559
  let trimLeft = prevTag && !selfClosingInlineTags.has(prevTag);
562
560
  if (trimLeft && !options.collapseInlineTagWhitespace) {
563
561
  trimLeft = prevTag.charAt(0) === '/' ? !inlineTags.has(prevTag.slice(1)) : !inlineTextTags.has(prevTag);
@@ -1357,6 +1355,7 @@ async function minifyHTML(value, options, partialMarkup) {
1357
1355
  let uidIgnore;
1358
1356
  let uidAttr;
1359
1357
  let uidPattern;
1358
+ let inlineTags = new Set([...inlineHtmlElements, ...(options.inlineCustomElements ?? [])]);
1360
1359
 
1361
1360
  // temporarily replace ignored chunks with comments,
1362
1361
  // so that we don't have to worry what's there.
@@ -1488,7 +1487,7 @@ async function minifyHTML(value, options, partialMarkup) {
1488
1487
  const match = str.match(/^<\/([\w:-]+)>$/);
1489
1488
  if (match) {
1490
1489
  endTag = match[1];
1491
- } else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options))) {
1490
+ } else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options, inlineTags))) {
1492
1491
  break;
1493
1492
  }
1494
1493
  }
@@ -1699,7 +1698,7 @@ async function minifyHTML(value, options, partialMarkup) {
1699
1698
  }
1700
1699
  }
1701
1700
  if (prevTag || nextTag) {
1702
- text = collapseWhitespaceSmart(text, prevTag, nextTag, options);
1701
+ text = collapseWhitespaceSmart(text, prevTag, nextTag, options, inlineTags);
1703
1702
  } else {
1704
1703
  text = collapseWhitespace(text, options, true, true);
1705
1704
  }