html-minifier-next 1.2.0 → 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).
@@ -77,7 +77,7 @@ Most of the options are disabled by default.
77
77
 
78
78
  | Option | Description | Default |
79
79
  | --- | --- | --- |
80
- | `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` |
81
81
  | `collapseBooleanAttributes` | [Omit attribute values from boolean attributes](http://perfectionkills.com/experimenting-with-html-minifier#collapse_boolean_attributes) | `false` |
82
82
  | `customFragmentQuantifierLimit` | Set maximum quantifier limit for custom fragments to prevent ReDoS attacks | `200` |
83
83
  | `collapseInlineTagWhitespace` | Don’t leave any spaces between `display:inline;` elements when collapsing. Must be used in conjunction with `collapseWhitespace=true` | `false` |
@@ -92,7 +92,7 @@ Most of the options are disabled by default.
92
92
  | `html5` | Parse input according to HTML5 specifications | `true` |
93
93
  | `ignoreCustomComments` | Array of regexes that allow to ignore certain comments, when matched | `[ /^!/, /^\s*#/ ]` |
94
94
  | `ignoreCustomFragments` | Array of regexes that allow to ignore certain fragments, when matched (e.g. `<?php ... ?>`, `{{ ... }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` |
95
- | `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` |
95
+ | `includeAutoGeneratedTags` | Insert elements generated by HTML parser | `true` |
96
96
  | `inlineCustomElements` | Array of names of custom elements which are inline | `[]` |
97
97
  | `keepClosingSlash` | Keep the trailing slash on singleton elements | `false` |
98
98
  | `maxInputLength` | Maximum input length to prevent ReDoS attacks (disabled by default) | `undefined` |
@@ -112,8 +112,8 @@ Most of the options are disabled by default.
112
112
  | `removeEmptyElements` | [Remove all elements with empty contents](http://perfectionkills.com/experimenting-with-html-minifier#remove_empty_elements) | `false` |
113
113
  | `removeOptionalTags` | [Remove optional tags](http://perfectionkills.com/experimenting-with-html-minifier#remove_optional_tags) | `false` |
114
114
  | `removeRedundantAttributes` | [Remove attributes when value matches default.](http://perfectionkills.com/experimenting-with-html-minifier#remove_redundant_attributes) | `false` |
115
- | `removeScriptTypeAttributes` | Remove `type="text/javascript"` from `script` tags. Other `type` attribute values are left intact | `false` |
116
- | `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` |
117
117
  | `removeTagWhitespace` | Remove space between attributes whenever possible. **Note that this will result in invalid HTML!** | `false` |
118
118
  | `sortAttributes` | [Sort attributes by frequency](#sorting-attributes--style-classes) | `false` |
119
119
  | `sortClassName` | [Sort style classes by frequency](#sorting-attributes--style-classes) | `false` |
@@ -132,11 +132,11 @@ If you have chunks of markup you would like preserved, you can wrap them `<!-- h
132
132
 
133
133
  ### Minifying JSON-LD
134
134
 
135
- 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.
136
136
 
137
- ### Preserving SVG tags
137
+ ### Preserving SVG elements
138
138
 
139
- 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.
140
140
 
141
141
  ### Working with invalid markup
142
142
 
@@ -233,4 +233,4 @@ npm run serve
233
233
 
234
234
  ## Acknowledgements
235
235
 
236
- With many thanks to all the previous authors of HTML Minifier, especially [Juriy Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, like [Daniel Ruf](https://github.com/DanielRuf).
236
+ With many thanks to all the previous authors of HTML Minifier, especially [Juriy Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, like [Daniel Ruf](https://github.com/DanielRuf).
package/cli.js CHANGED
@@ -141,7 +141,7 @@ const mainOptions = {
141
141
  useShortDoctype: 'Replaces the doctype with the short (HTML5) doctype'
142
142
  };
143
143
 
144
- // configure commandline flags
144
+ // Configure command line flags
145
145
  const mainOptionKeys = Object.keys(mainOptions);
146
146
  mainOptionKeys.forEach(function (key) {
147
147
  const option = mainOptions[key];
@@ -305,4 +305,4 @@ if (inputDir || outputDir) {
305
305
  process.stdin.on('data', function (data) {
306
306
  content += data;
307
307
  }).on('end', writeMinify);
308
- }
308
+ }
package/package.json CHANGED
@@ -71,6 +71,11 @@
71
71
  "main": "./dist/htmlminifier.cjs",
72
72
  "module": "./src/htmlminifier.js",
73
73
  "name": "html-minifier-next",
74
+ "overrides": {
75
+ "glob": "^10.0.0",
76
+ "inflight": "npm:@nodelib/fs.stat@^3.0.0"
77
+ },
78
+ "overrides_comment": "@@ Remove when Jest fixes deprecated glob@7.2.3 and inflight dependencies",
74
79
  "repository": "https://github.com/j9t/html-minifier-next.git",
75
80
  "scripts": {
76
81
  "build": "rollup -c",
@@ -83,5 +88,5 @@
83
88
  "test:watch": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --watch"
84
89
  },
85
90
  "type": "module",
86
- "version": "1.2.0"
91
+ "version": "1.2.1"
87
92
  }
@@ -52,28 +52,28 @@ function collapseWhitespace(str, options, trimLeft, trimRight, collapseAll) {
52
52
  }
53
53
 
54
54
  if (collapseAll) {
55
- // strip non space whitespace then compress spaces to one
55
+ // Strip non-space whitespace then compress spaces to one
56
56
  str = collapseWhitespaceAll(str);
57
57
  }
58
58
 
59
59
  return lineBreakBefore + str + lineBreakAfter;
60
60
  }
61
61
 
62
- // non-empty elements that will maintain whitespace around them
63
- 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'];
64
- // non-empty elements that will maintain whitespace within them
65
- 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']);
66
- // self-closing elements that will maintain whitespace around them
67
- const selfClosingInlineTags = new Set(['comment', 'img', 'input', 'wbr']);
62
+ // Non-empty elements that will maintain whitespace around them
63
+ const inlineElementsToKeepWhitespaceAround = ['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'];
64
+ // Non-empty elements that will maintain whitespace within them
65
+ const inlineElementsToKeepWhitespaceWithin = 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']);
66
+ // Elements that will always maintain whitespace around them
67
+ const inlineElementsToKeepWhitespace = new Set(['comment', 'img', 'input', 'wbr']);
68
68
 
69
- function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineTags) {
70
- let trimLeft = prevTag && !selfClosingInlineTags.has(prevTag);
69
+ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements, inlineTextSet) {
70
+ let trimLeft = prevTag && !inlineElementsToKeepWhitespace.has(prevTag);
71
71
  if (trimLeft && !options.collapseInlineTagWhitespace) {
72
- trimLeft = prevTag.charAt(0) === '/' ? !inlineTags.has(prevTag.slice(1)) : !inlineTextTags.has(prevTag);
72
+ trimLeft = prevTag.charAt(0) === '/' ? !inlineElements.has(prevTag.slice(1)) : !inlineTextSet.has(prevTag);
73
73
  }
74
- let trimRight = nextTag && !selfClosingInlineTags.has(nextTag);
74
+ let trimRight = nextTag && !inlineElementsToKeepWhitespace.has(nextTag);
75
75
  if (trimRight && !options.collapseInlineTagWhitespace) {
76
- trimRight = nextTag.charAt(0) === '/' ? !inlineTextTags.has(nextTag.slice(1)) : !inlineTags.has(nextTag);
76
+ trimRight = nextTag.charAt(0) === '/' ? !inlineTextSet.has(nextTag.slice(1)) : !inlineElements.has(nextTag);
77
77
  }
78
78
  return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag);
79
79
  }
@@ -395,7 +395,7 @@ async function processScript(text, options, currentAttrs) {
395
395
  // Tag omission rules from https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
396
396
  // with the following deviations:
397
397
  // - retain <body> if followed by <noscript>
398
- // - </rb>, </rt>, </rtc>, </rp> & </tfoot> follow https://www.w3.org/TR/html5/syntax.html#optional-tags
398
+ // - </rb>, </rt>, </rtc>, </rp>, and </tfoot> follow https://www.w3.org/TR/html5/syntax.html#optional-tags
399
399
  // - retain all tags which are adjacent to non-standard HTML tags
400
400
  const optionalStartTags = new Set(['html', 'head', 'body', 'colgroup', 'tbody']);
401
401
  const optionalEndTags = new Set(['html', 'head', 'body', 'li', 'dt', 'dd', 'p', 'rb', 'rt', 'rtc', 'rp', 'optgroup', 'option', 'colgroup', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th']);
@@ -605,7 +605,7 @@ function buildAttr(normalized, hasUnarySlash, options, isLast, uidAttr) {
605
605
  emittedAttrValue += ' ';
606
606
  }
607
607
  } else if (isLast && !hasUnarySlash && !/\/$/.test(attrValue)) {
608
- // make sure trailing slash is not interpreted as HTML self-closing tag
608
+ // Make sure trailing slash is not interpreted as HTML self-closing tag
609
609
  emittedAttrValue = attrValue;
610
610
  } else {
611
611
  emittedAttrValue = attrValue + ' ';
@@ -866,14 +866,16 @@ async function minifyHTML(value, options, partialMarkup) {
866
866
  let uidIgnore;
867
867
  let uidAttr;
868
868
  let uidPattern;
869
- // Create inline tags set with backward compatibility for inlineCustomTags
870
- const customElements = options.inlineCustomElements ?? options.inlineCustomTags ?? [];
871
- const normalizedCustomElements = customElements.map(name => options.name(name));
872
- let inlineTags = new Set([...inlineHtmlElements, ...normalizedCustomElements]);
873
-
874
- // temporarily replace ignored chunks with comments,
875
- // so that we don't have to worry what's there.
876
- // for all we care there might be
869
+ // Create inline tags/text sets with custom elements
870
+ const customElementsInput = options.inlineCustomElements ?? [];
871
+ const customElementsArr = Array.isArray(customElementsInput) ? customElementsInput : Array.from(customElementsInput);
872
+ const normalizedCustomElements = customElementsArr.map(name => options.name(name));
873
+ const inlineTextSet = new Set([...inlineElementsToKeepWhitespaceWithin, ...normalizedCustomElements]);
874
+ const inlineElements = new Set([...inlineElementsToKeepWhitespaceAround, ...normalizedCustomElements]);
875
+
876
+ // Temporarily replace ignored chunks with comments,
877
+ // so that we don’t have to worry what’s there.
878
+ // For all we care there might be
877
879
  // completely-horribly-broken-alien-non-html-emoj-cthulhu-filled content
878
880
  value = value.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g, function (match, group1) {
879
881
  if (!uidIgnore) {
@@ -994,20 +996,20 @@ async function minifyHTML(value, options, partialMarkup) {
994
996
  buffer.length = Math.max(0, index);
995
997
  }
996
998
 
997
- // look for trailing whitespaces, bypass any inline tags
999
+ // Look for trailing whitespaces, bypass any inline tags
998
1000
  function trimTrailingWhitespace(index, nextTag) {
999
1001
  for (let endTag = null; index >= 0 && _canTrimWhitespace(endTag); index--) {
1000
1002
  const str = buffer[index];
1001
1003
  const match = str.match(/^<\/([\w:-]+)>$/);
1002
1004
  if (match) {
1003
1005
  endTag = match[1];
1004
- } else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options, inlineTags))) {
1006
+ } else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options, inlineElements, inlineTextSet))) {
1005
1007
  break;
1006
1008
  }
1007
1009
  }
1008
1010
  }
1009
1011
 
1010
- // look for trailing whitespaces from previously processed text
1012
+ // Look for trailing whitespaces from previously processed text
1011
1013
  // which may not be trimmed due to a following comment or an empty
1012
1014
  // element which has now been removed
1013
1015
  function squashTrailingWhitespace(nextTag) {
@@ -1038,7 +1040,7 @@ async function minifyHTML(value, options, partialMarkup) {
1038
1040
  tag = options.name(tag);
1039
1041
  currentTag = tag;
1040
1042
  charsPrevTag = tag;
1041
- if (!inlineTextTags.has(tag)) {
1043
+ if (!inlineTextSet.has(tag)) {
1042
1044
  currentChars = '';
1043
1045
  }
1044
1046
  hasChars = false;
@@ -1056,7 +1058,7 @@ async function minifyHTML(value, options, partialMarkup) {
1056
1058
  removeStartTag();
1057
1059
  }
1058
1060
  optionalStartTag = '';
1059
- // end-tag-followed-by-start-tag omission rules
1061
+ // End-tag-followed-by-start-tag omission rules
1060
1062
  if (htmlTag && canRemovePrecedingTag(optionalEndTag, tag)) {
1061
1063
  removeEndTag();
1062
1064
  // <colgroup> cannot be omitted if preceding </colgroup> is omitted
@@ -1066,7 +1068,7 @@ async function minifyHTML(value, options, partialMarkup) {
1066
1068
  optionalEndTag = '';
1067
1069
  }
1068
1070
 
1069
- // set whitespace flags for nested tags (eg. <code> within a <pre>)
1071
+ // Set whitespace flags for nested tags (eg. <code> within a <pre>)
1070
1072
  if (options.collapseWhitespace) {
1071
1073
  if (!stackNoTrimWhitespace.length) {
1072
1074
  squashTrailingWhitespace(tag);
@@ -1102,7 +1104,7 @@ async function minifyHTML(value, options, partialMarkup) {
1102
1104
  buffer.push(' ');
1103
1105
  buffer.push.apply(buffer, parts);
1104
1106
  } else if (optional && optionalStartTags.has(tag)) {
1105
- // start tag must never be omitted if it has any attributes
1107
+ // Start tag must never be omitted if it has any attributes
1106
1108
  optionalStartTag = tag;
1107
1109
  }
1108
1110
 
@@ -1119,7 +1121,7 @@ async function minifyHTML(value, options, partialMarkup) {
1119
1121
  }
1120
1122
  tag = options.name(tag);
1121
1123
 
1122
- // check if current tag is in a whitespace stack
1124
+ // Check if current tag is in a whitespace stack
1123
1125
  if (options.collapseWhitespace) {
1124
1126
  if (stackNoTrimWhitespace.length) {
1125
1127
  if (tag === stackNoTrimWhitespace[stackNoTrimWhitespace.length - 1]) {
@@ -1157,7 +1159,7 @@ async function minifyHTML(value, options, partialMarkup) {
1157
1159
  }
1158
1160
 
1159
1161
  if (options.removeEmptyElements && isElementEmpty && canRemoveElement(tag, attrs)) {
1160
- // remove last "element" from buffer
1162
+ // Remove last element from buffer
1161
1163
  removeStartTag();
1162
1164
  optionalStartTag = '';
1163
1165
  optionalEndTag = '';
@@ -1168,7 +1170,7 @@ async function minifyHTML(value, options, partialMarkup) {
1168
1170
  buffer.push('</' + tag + '>');
1169
1171
  }
1170
1172
  charsPrevTag = '/' + tag;
1171
- if (!inlineTags.has(tag)) {
1173
+ if (!inlineElements.has(tag)) {
1172
1174
  currentChars = '';
1173
1175
  } else if (isElementEmpty) {
1174
1176
  currentChars += '|';
@@ -1207,12 +1209,12 @@ async function minifyHTML(value, options, partialMarkup) {
1207
1209
  }
1208
1210
  trimTrailingWhitespace(tagIndex - 1, 'br');
1209
1211
  }
1210
- } else if (inlineTextTags.has(prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag)) {
1212
+ } else if (inlineTextSet.has(prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag)) {
1211
1213
  text = collapseWhitespace(text, options, /(?:^|\s)$/.test(currentChars));
1212
1214
  }
1213
1215
  }
1214
1216
  if (prevTag || nextTag) {
1215
- text = collapseWhitespaceSmart(text, prevTag, nextTag, options, inlineTags);
1217
+ text = collapseWhitespaceSmart(text, prevTag, nextTag, options, inlineElements, inlineTextSet);
1216
1218
  } else {
1217
1219
  text = collapseWhitespace(text, options, true, true);
1218
1220
  }
@@ -1282,7 +1284,7 @@ async function minifyHTML(value, options, partialMarkup) {
1282
1284
  text = prefix + text + suffix;
1283
1285
  }
1284
1286
  if (options.removeOptionalTags && text) {
1285
- // preceding comments suppress tag omissions
1287
+ // Preceding comments suppress tag omissions
1286
1288
  optionalStartTag = '';
1287
1289
  optionalEndTag = '';
1288
1290
  }
@@ -1386,4 +1388,4 @@ export const minify = async function (value, options) {
1386
1388
  return result;
1387
1389
  };
1388
1390
 
1389
- export default { minify };
1391
+ export default { minify };
package/src/htmlparser.js CHANGED
@@ -131,7 +131,7 @@ export class HTMLParser {
131
131
  let last, prevTag, nextTag;
132
132
  while (html) {
133
133
  last = html;
134
- // Make sure we're not in a script or style element
134
+ // Make sure were not in a `script` or `style` element
135
135
  if (!lastTag || !special.has(lastTag)) {
136
136
  let textEnd = html.indexOf('<');
137
137
  if (textEnd === 0) {
@@ -207,7 +207,7 @@ export class HTMLParser {
207
207
  html = '';
208
208
  }
209
209
 
210
- // next tag
210
+ // Next tag
211
211
  let nextTagMatch = parseStartTag(html);
212
212
  if (nextTagMatch) {
213
213
  nextTag = nextTagMatch.tagName;
@@ -320,9 +320,9 @@ export class HTMLParser {
320
320
 
321
321
  const attrs = match.attrs.map(function (args) {
322
322
  let name, value, customOpen, customClose, customAssign, quote;
323
- const ncp = 7; // number of captured parts, scalar
323
+ const ncp = 7; // Number of captured parts, scalar
324
324
 
325
- // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
325
+ // Hackish workaround for FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
326
326
  if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {
327
327
  if (args[3] === '') { delete args[3]; }
328
328
  if (args[4] === '') { delete args[4]; }
@@ -512,13 +512,13 @@ export const HTMLtoDOM = (html, doc) => {
512
512
  }
513
513
  }
514
514
 
515
- // If we're working with a document, inject contents into
516
- // the body element
515
+ // If were working with a document, inject contents into
516
+ // the `body` element
517
517
  let curParentNode = one.body;
518
518
 
519
519
  const parser = new HTMLParser(html, {
520
520
  start: function (tagName, attrs, unary) {
521
- // If it's a pre-built element, then we can ignore
521
+ // If its a pre-built element, then we can ignore
522
522
  // its construction
523
523
  if (one[tagName]) {
524
524
  curParentNode = one[tagName];
@@ -552,7 +552,7 @@ export const HTMLtoDOM = (html, doc) => {
552
552
  curParentNode.appendChild(doc.createTextNode(text));
553
553
  },
554
554
  comment: function (/* text */) {
555
- // create comment node
555
+ // Create comment node
556
556
  },
557
557
  ignore: function (/* text */) {
558
558
  // What to do here?
@@ -562,4 +562,4 @@ export const HTMLtoDOM = (html, doc) => {
562
562
  parser.parse();
563
563
 
564
564
  return doc;
565
- };
565
+ };