html-minifier-next 4.5.0 → 4.6.0

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
@@ -72,29 +72,6 @@ html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir
72
72
  html-minifier-next --config-file=html-minifier.json --file-ext=xml --input-dir=src --output-dir=dist
73
73
  ```
74
74
 
75
- ### Presets
76
-
77
- HTML Minifier Next provides presets for common use cases. Presets are pre-configured option sets that can be used as a starting point:
78
-
79
- * `conservative`: Safe minification suitable for most projects. Includes whitespace collapsing, comment removal, and doctype normalization.
80
- * `comprehensive`: Aggressive minification for maximum file size reduction. Includes all conservative options plus attribute quote removal, optional tag removal, and more.
81
-
82
- **Using presets:**
83
-
84
- ```bash
85
- # Via CLI flag
86
- html-minifier-next --preset conservative input.html
87
-
88
- # Via config file
89
- html-minifier-next --config-file=config.json input.html
90
- # where config.json contains: { "preset": "conservative" }
91
-
92
- # Override preset options
93
- html-minifier-next --preset conservative --remove-empty-attributes input.html
94
- ```
95
-
96
- **Priority order:** Presets are applied first, then config file options, then CLI flags. This allows you to start with a preset and customize as needed.
97
-
98
75
  ### Node.js
99
76
 
100
77
  ESM with Node.js ≥16.14:
@@ -125,6 +102,29 @@ See [the original blog post](https://perfectionkills.com/experimenting-with-html
125
102
 
126
103
  For lint-like capabilities, take a look at [HTMLLint](https://github.com/kangax/html-lint).
127
104
 
105
+ ## Presets
106
+
107
+ HTML Minifier Next provides presets for common use cases. Presets are pre-configured option sets that can be used as a starting point:
108
+
109
+ * `conservative`: Safe minification suitable for most projects. Includes whitespace collapsing, comment removal, and doctype normalization.
110
+ * `comprehensive`: Aggressive minification for maximum file size reduction. Includes relevant conservative options plus attribute quote removal, optional tag removal, and more.
111
+
112
+ **Using presets:**
113
+
114
+ ```bash
115
+ # Via CLI flag
116
+ html-minifier-next --preset conservative input.html
117
+
118
+ # Via config file
119
+ html-minifier-next --config-file=html-minifier.json input.html
120
+ # where html-minifier.json contains: { "preset": "conservative" }
121
+
122
+ # Override preset options
123
+ html-minifier-next --preset conservative --remove-empty-attributes input.html
124
+ ```
125
+
126
+ **Priority order:** Presets are applied first, then config file options, then CLI flags. This allows you to start with a preset and customize as needed.
127
+
128
128
  ## Options quick reference
129
129
 
130
130
  Most of the options are disabled by default. Experiment and find what works best for you and your project.
@@ -569,7 +569,6 @@ const presets = {
569
569
  collapseBooleanAttributes: true,
570
570
  collapseInlineTagWhitespace: true,
571
571
  collapseWhitespace: true,
572
- conservativeCollapse: true,
573
572
  continueOnParseError: true,
574
573
  decodeEntities: true,
575
574
  minifyCSS: true,
@@ -1033,11 +1032,55 @@ async function cleanConditionalComment(comment, options) {
1033
1032
  : comment;
1034
1033
  }
1035
1034
 
1035
+ const jsonScriptTypes = new Set([
1036
+ 'application/json',
1037
+ 'application/ld+json',
1038
+ 'application/manifest+json',
1039
+ 'application/vnd.geo+json',
1040
+ 'importmap',
1041
+ 'speculationrules',
1042
+ ]);
1043
+
1044
+ function minifyJson(text, options) {
1045
+ try {
1046
+ return JSON.stringify(JSON.parse(text));
1047
+ }
1048
+ catch (err) {
1049
+ if (!options.continueOnMinifyError) {
1050
+ throw err;
1051
+ }
1052
+ options.log && options.log(err);
1053
+ return text;
1054
+ }
1055
+ }
1056
+
1057
+ function hasJsonScriptType(attrs) {
1058
+ for (let i = 0, len = attrs.length; i < len; i++) {
1059
+ const attrName = attrs[i].name.toLowerCase();
1060
+ if (attrName === 'type') {
1061
+ const attrValue = trimWhitespace((attrs[i].value || '').split(/;/, 2)[0]).toLowerCase();
1062
+ if (jsonScriptTypes.has(attrValue)) {
1063
+ return true;
1064
+ }
1065
+ }
1066
+ }
1067
+ return false;
1068
+ }
1069
+
1036
1070
  async function processScript(text, options, currentAttrs) {
1037
1071
  for (let i = 0, len = currentAttrs.length; i < len; i++) {
1038
- if (currentAttrs[i].name.toLowerCase() === 'type' &&
1039
- options.processScripts.indexOf(currentAttrs[i].value) > -1) {
1040
- return await minifyHTML(text, options);
1072
+ const attrName = currentAttrs[i].name.toLowerCase();
1073
+ if (attrName === 'type') {
1074
+ const rawValue = currentAttrs[i].value;
1075
+ const normalizedValue = trimWhitespace((rawValue || '').split(/;/, 2)[0]).toLowerCase();
1076
+ // Minify JSON script types automatically
1077
+ if (jsonScriptTypes.has(normalizedValue)) {
1078
+ return minifyJson(text, options);
1079
+ }
1080
+ // Process custom script types if specified
1081
+ if (options.processScripts && options.processScripts.indexOf(rawValue) > -1) {
1082
+ return await minifyHTML(text, options);
1083
+ }
1041
1084
  }
1042
1085
  }
1043
1086
  return text;
@@ -1917,7 +1960,7 @@ async function minifyHTML(value, options, partialMarkup) {
1917
1960
  text = collapseWhitespace(text, options, false, false, true);
1918
1961
  }
1919
1962
  }
1920
- if (options.processScripts && specialContentTags.has(currentTag)) {
1963
+ if (specialContentTags.has(currentTag) && (options.processScripts || hasJsonScriptType(currentAttrs))) {
1921
1964
  text = await processScript(text, options, currentAttrs);
1922
1965
  }
1923
1966
  if (isExecutableScript(currentTag, currentAttrs)) {
@@ -39622,7 +39622,6 @@ const presets = {
39622
39622
  collapseBooleanAttributes: true,
39623
39623
  collapseInlineTagWhitespace: true,
39624
39624
  collapseWhitespace: true,
39625
- conservativeCollapse: true,
39626
39625
  continueOnParseError: true,
39627
39626
  decodeEntities: true,
39628
39627
  minifyCSS: true,
@@ -40086,11 +40085,55 @@ async function cleanConditionalComment(comment, options) {
40086
40085
  : comment;
40087
40086
  }
40088
40087
 
40088
+ const jsonScriptTypes = new Set([
40089
+ 'application/json',
40090
+ 'application/ld+json',
40091
+ 'application/manifest+json',
40092
+ 'application/vnd.geo+json',
40093
+ 'importmap',
40094
+ 'speculationrules',
40095
+ ]);
40096
+
40097
+ function minifyJson(text, options) {
40098
+ try {
40099
+ return JSON.stringify(JSON.parse(text));
40100
+ }
40101
+ catch (err) {
40102
+ if (!options.continueOnMinifyError) {
40103
+ throw err;
40104
+ }
40105
+ options.log && options.log(err);
40106
+ return text;
40107
+ }
40108
+ }
40109
+
40110
+ function hasJsonScriptType(attrs) {
40111
+ for (let i = 0, len = attrs.length; i < len; i++) {
40112
+ const attrName = attrs[i].name.toLowerCase();
40113
+ if (attrName === 'type') {
40114
+ const attrValue = trimWhitespace((attrs[i].value || '').split(/;/, 2)[0]).toLowerCase();
40115
+ if (jsonScriptTypes.has(attrValue)) {
40116
+ return true;
40117
+ }
40118
+ }
40119
+ }
40120
+ return false;
40121
+ }
40122
+
40089
40123
  async function processScript(text, options, currentAttrs) {
40090
40124
  for (let i = 0, len = currentAttrs.length; i < len; i++) {
40091
- if (currentAttrs[i].name.toLowerCase() === 'type' &&
40092
- options.processScripts.indexOf(currentAttrs[i].value) > -1) {
40093
- return await minifyHTML(text, options);
40125
+ const attrName = currentAttrs[i].name.toLowerCase();
40126
+ if (attrName === 'type') {
40127
+ const rawValue = currentAttrs[i].value;
40128
+ const normalizedValue = trimWhitespace((rawValue || '').split(/;/, 2)[0]).toLowerCase();
40129
+ // Minify JSON script types automatically
40130
+ if (jsonScriptTypes.has(normalizedValue)) {
40131
+ return minifyJson(text, options);
40132
+ }
40133
+ // Process custom script types if specified
40134
+ if (options.processScripts && options.processScripts.indexOf(rawValue) > -1) {
40135
+ return await minifyHTML(text, options);
40136
+ }
40094
40137
  }
40095
40138
  }
40096
40139
  return text;
@@ -40970,7 +41013,7 @@ async function minifyHTML(value, options, partialMarkup) {
40970
41013
  text = collapseWhitespace(text, options, false, false, true);
40971
41014
  }
40972
41015
  }
40973
- if (options.processScripts && specialContentTags.has(currentTag)) {
41016
+ if (specialContentTags.has(currentTag) && (options.processScripts || hasJsonScriptType(currentAttrs))) {
40974
41017
  text = await processScript(text, options, currentAttrs);
40975
41018
  }
40976
41019
  if (isExecutableScript(currentTag, currentAttrs)) {
@@ -1 +1 @@
1
- {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAu8CO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAQ3B;;;;;;;;;;;;UAUS,MAAM;YACN,MAAM;YACN,MAAM;mBACN,MAAM;iBACN,MAAM;kBACN,MAAM;;;;;;;;;;;;;4BAQN,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;wBAMjG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;oBAMhH,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;;kCAOP,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;2BAOP,OAAO;;;;;;;;4BAOP,OAAO;;;;;;;2BAOP,OAAO;;;;;;;;uBAMP,MAAM,EAAE;;;;;;yBAOR,MAAM;;;;;;yBAKN,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;;;;;;;4BAKlB,MAAM,EAAE;;;;;;;oCAMR,MAAM;;;;;;;qBAMN,OAAO;;;;;;;YAMP,OAAO;;;;;;;;2BAMP,MAAM,EAAE;;;;;;;;;4BAOR,MAAM,EAAE;;;;;;;+BAQR,OAAO;;;;;;;2BAMP,SAAS,CAAC,MAAM,CAAC;;;;;;uBAMjB,OAAO;;;;;;;;UAKP,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;;;;;;;;qBAO1B,MAAM;;;;;;;oBAON,MAAM;;;;;;;;;;gBAMN,OAAO,GAAG,OAAO,CAAC,OAAO,cAAc,EAAE,gBAAgB,CAAC,OAAO,cAAc,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;eAS9J,OAAO,GAAG,OAAO,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;iBASzG,OAAO,GAAG,MAAM,GAAG,OAAO,WAAW,EAAE,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;WAS7F,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;;;;;+BAOxB,OAAO;;;;;;;;;;oBAMP,OAAO;;;;;;;;yBASP,OAAO;;;;;;;gCAOP,OAAO;;;;;;;;iCAMP,OAAO;;;;;;;;;;qBAOP,MAAM,EAAE;;;;;;;qBASR,IAAI,GAAG,GAAG;;;;;;;4BAMV,OAAO;;;;;;;;qBAMP,OAAO;;;;;;;;;4BAOP,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;0BAQtD,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;iCAOP,OAAO;;;;;;;oCAMP,OAAO;;;;;;;;;;0BAMP,OAAO;;;;;;;;;qBASP,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;;;;;;;;;oBAQzD,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;;;;;;;;0BAQrC,OAAO;;;;;;;sBAOP,OAAO;;wBAhyDkC,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
1
+ {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAm/CO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAQ3B;;;;;;;;;;;;UAUS,MAAM;YACN,MAAM;YACN,MAAM;mBACN,MAAM;iBACN,MAAM;kBACN,MAAM;;;;;;;;;;;;;4BAQN,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;wBAMjG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;oBAMhH,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;;kCAOP,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;2BAOP,OAAO;;;;;;;;4BAOP,OAAO;;;;;;;2BAOP,OAAO;;;;;;;;uBAMP,MAAM,EAAE;;;;;;yBAOR,MAAM;;;;;;yBAKN,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;;;;;;;4BAKlB,MAAM,EAAE;;;;;;;oCAMR,MAAM;;;;;;;qBAMN,OAAO;;;;;;;YAMP,OAAO;;;;;;;;2BAMP,MAAM,EAAE;;;;;;;;;4BAOR,MAAM,EAAE;;;;;;;+BAQR,OAAO;;;;;;;2BAMP,SAAS,CAAC,MAAM,CAAC;;;;;;uBAMjB,OAAO;;;;;;;;UAKP,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;;;;;;;;qBAO1B,MAAM;;;;;;;oBAON,MAAM;;;;;;;;;;gBAMN,OAAO,GAAG,OAAO,CAAC,OAAO,cAAc,EAAE,gBAAgB,CAAC,OAAO,cAAc,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;eAS9J,OAAO,GAAG,OAAO,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;iBASzG,OAAO,GAAG,MAAM,GAAG,OAAO,WAAW,EAAE,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;WAS7F,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;;;;;+BAOxB,OAAO;;;;;;;;;;oBAMP,OAAO;;;;;;;;yBASP,OAAO;;;;;;;gCAOP,OAAO;;;;;;;;iCAMP,OAAO;;;;;;;;;;qBAOP,MAAM,EAAE;;;;;;;qBASR,IAAI,GAAG,GAAG;;;;;;;4BAMV,OAAO;;;;;;;;qBAMP,OAAO;;;;;;;;;4BAOP,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;0BAQtD,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;iCAOP,OAAO;;;;;;;oCAMP,OAAO;;;;;;;;;;0BAMP,OAAO;;;;;;;;;qBASP,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;;;;;;;;;oBAQzD,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;;;;;;;;0BAQrC,OAAO;;;;;;;sBAOP,OAAO;;wBA50DkC,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
@@ -31,8 +31,6 @@ export namespace presets {
31
31
  export let collapseInlineTagWhitespace: boolean;
32
32
  let collapseWhitespace_1: boolean;
33
33
  export { collapseWhitespace_1 as collapseWhitespace };
34
- let conservativeCollapse_1: boolean;
35
- export { conservativeCollapse_1 as conservativeCollapse };
36
34
  let continueOnParseError_1: boolean;
37
35
  export { continueOnParseError_1 as continueOnParseError };
38
36
  let decodeEntities_1: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"AAiDA;;;;GAIG;AACH,gCAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAMvB;AAED;;;GAGG;AACH,kCAFa,MAAM,EAAE,CAIpB"}
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"AAgDA;;;;GAIG;AACH,gCAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAMvB;AAED;;;GAGG;AACH,kCAFa,MAAM,EAAE,CAIpB"}
package/package.json CHANGED
@@ -84,5 +84,5 @@
84
84
  "test:watch": "node --test --watch tests/*.spec.js"
85
85
  },
86
86
  "type": "module",
87
- "version": "4.5.0"
87
+ "version": "4.6.0"
88
88
  }
@@ -431,11 +431,55 @@ async function cleanConditionalComment(comment, options) {
431
431
  : comment;
432
432
  }
433
433
 
434
+ const jsonScriptTypes = new Set([
435
+ 'application/json',
436
+ 'application/ld+json',
437
+ 'application/manifest+json',
438
+ 'application/vnd.geo+json',
439
+ 'importmap',
440
+ 'speculationrules',
441
+ ]);
442
+
443
+ function minifyJson(text, options) {
444
+ try {
445
+ return JSON.stringify(JSON.parse(text));
446
+ }
447
+ catch (err) {
448
+ if (!options.continueOnMinifyError) {
449
+ throw err;
450
+ }
451
+ options.log && options.log(err);
452
+ return text;
453
+ }
454
+ }
455
+
456
+ function hasJsonScriptType(attrs) {
457
+ for (let i = 0, len = attrs.length; i < len; i++) {
458
+ const attrName = attrs[i].name.toLowerCase();
459
+ if (attrName === 'type') {
460
+ const attrValue = trimWhitespace((attrs[i].value || '').split(/;/, 2)[0]).toLowerCase();
461
+ if (jsonScriptTypes.has(attrValue)) {
462
+ return true;
463
+ }
464
+ }
465
+ }
466
+ return false;
467
+ }
468
+
434
469
  async function processScript(text, options, currentAttrs) {
435
470
  for (let i = 0, len = currentAttrs.length; i < len; i++) {
436
- if (currentAttrs[i].name.toLowerCase() === 'type' &&
437
- options.processScripts.indexOf(currentAttrs[i].value) > -1) {
438
- return await minifyHTML(text, options);
471
+ const attrName = currentAttrs[i].name.toLowerCase();
472
+ if (attrName === 'type') {
473
+ const rawValue = currentAttrs[i].value;
474
+ const normalizedValue = trimWhitespace((rawValue || '').split(/;/, 2)[0]).toLowerCase();
475
+ // Minify JSON script types automatically
476
+ if (jsonScriptTypes.has(normalizedValue)) {
477
+ return minifyJson(text, options);
478
+ }
479
+ // Process custom script types if specified
480
+ if (options.processScripts && options.processScripts.indexOf(rawValue) > -1) {
481
+ return await minifyHTML(text, options);
482
+ }
439
483
  }
440
484
  }
441
485
  return text;
@@ -1315,7 +1359,7 @@ async function minifyHTML(value, options, partialMarkup) {
1315
1359
  text = collapseWhitespace(text, options, false, false, true);
1316
1360
  }
1317
1361
  }
1318
- if (options.processScripts && specialContentTags.has(currentTag)) {
1362
+ if (specialContentTags.has(currentTag) && (options.processScripts || hasJsonScriptType(currentAttrs))) {
1319
1363
  text = await processScript(text, options, currentAttrs);
1320
1364
  }
1321
1365
  if (isExecutableScript(currentTag, currentAttrs)) {
package/src/presets.js CHANGED
@@ -26,7 +26,6 @@ export const presets = {
26
26
  collapseBooleanAttributes: true,
27
27
  collapseInlineTagWhitespace: true,
28
28
  collapseWhitespace: true,
29
- conservativeCollapse: true,
30
29
  continueOnParseError: true,
31
30
  decodeEntities: true,
32
31
  minifyCSS: true,
@@ -64,4 +63,4 @@ export function getPreset(name) {
64
63
  */
65
64
  export function getPresetNames() {
66
65
  return Object.keys(presets);
67
- }
66
+ }