html-minifier-next 7.0.0 → 7.2.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
@@ -40,7 +40,7 @@ Use `npx html-minifier-next --help` to check all available options:
40
40
  | `--output <file>`, `-o <file>` | Specify output file (reads from `--input` file argument or STDIN; outputs to STDOUT if not specified) | File to file: `npx html-minifier-next input.html -o output.html`<br>File to file (explicit): `npx html-minifier-next -i input.html -o output.html`<br>Pipe to file: `cat input.html \| npx html-minifier-next -o output.html`<br>File to STDOUT: `npx html-minifier-next input.html` |
41
41
  | `--file-ext <extensions>`, `-f <extensions>` | Specify file extension(s) to process (comma-separated, overrides config file setting); defaults to `html,htm,shtml,shtm`; use `*` for all files | `--file-ext=html,php`, `--file-ext='*'` |
42
42
  | `--preset <name>`, `-p <name>` | Use a preset configuration (conservative or comprehensive) | `--preset=conservative` |
43
- | `--config-file <file>`, `-c <file>` | Use a configuration file | `--config-file=html-minifier.json` |
43
+ | `--config-file <file>`, `-c <file>` | Use a configuration file | `--config-file=html-minifier-next.json` |
44
44
  | `--verbose`, `-v` | Show detailed processing information (active options, file statistics) | `npx html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace` |
45
45
  | `--dry`, `-d` | Dry run: Process and report statistics without writing output | `npx html-minifier-next input.html --dry --collapse-whitespace` |
46
46
 
@@ -59,6 +59,18 @@ You can use a configuration file to specify options. The file can be either in J
59
59
  }
60
60
  ```
61
61
 
62
+ For editor support (validation, autocomplete, and inline documentation) in JSON configuration files, reference the [JSON Schema](https://raw.githubusercontent.com/j9t/html-minifier-next/main/html-minifier-next.schema.json) that ships with the package:
63
+
64
+ ```json
65
+ {
66
+ "$schema": "https://raw.githubusercontent.com/j9t/html-minifier-next/main/html-minifier-next.schema.json",
67
+ "collapseWhitespace": true,
68
+ "removeComments": true
69
+ }
70
+ ```
71
+
72
+ (If HMN is installed locally, you can also use the path `./node_modules/html-minifier-next/html-minifier-next.schema.json` instead of the URL.)
73
+
62
74
  **JavaScript module configuration example** (requires `"type": "module"` in the project’s package.json, or use a .mjs extension):
63
75
 
64
76
  ```javascript
@@ -100,8 +112,8 @@ To review the specific options set, [presets.js](https://github.com/j9t/html-min
100
112
  npx html-minifier-next --preset conservative input.html
101
113
 
102
114
  # Via config file
103
- npx html-minifier-next --config-file=html-minifier.json input.html
104
- # where html-minifier.json contains: { "preset": "conservative" }
115
+ npx html-minifier-next --config-file=html-minifier-next.json input.html
116
+ # where html-minifier-next.json contains: { "preset": "conservative" }
105
117
 
106
118
  # Override preset options
107
119
  npx html-minifier-next --preset conservative --remove-empty-attributes input.html
@@ -360,13 +372,13 @@ npx html-minifier-next --minify-css --minify-js --minify-svg input.html
360
372
 
361
373
  * Single file processing: Default `500` is sufficient
362
374
  * Batch processing: Increase to `1000` or higher for better cache hit rates
363
- * Memory-constrained environments: Reduce to `200`–`300` to save memory
375
+ * Memory-constrained environments: Cache sizes can be lowered, though the savings are usually negligible—entries are typically kilobyte-scale, so even full caches only hold a few megabytes
364
376
  * Hundreds/thousands of files: Increase to `1000`–`2000` for optimal performance
365
377
 
366
378
  **Important:**
367
379
 
368
380
  * Cache locking: Caches are created on the first `minify()` call and persist for the process lifetime. Cache sizes are locked after first initialization—subsequent calls reuse the same caches even if different `cacheCSS`, `cacheJS`, or `cacheSVG` options are provided. The first call’s options determine the cache sizes.
369
- * Zero values: Explicit `0` values are coerced to `1` (minimum functional cache size) to avoid immediate eviction. If you want to minimize memory usage, use a small number like `10` or `50` instead of `0`.
381
+ * Zero values: Explicit `0` values are coerced to `1` (minimum functional cache size) to avoid immediate eviction. To keep the cache footprint as small as possible, use a small number like `10` or `50` instead of `0`.
370
382
 
371
383
  The caches persist across multiple `minify()` calls, making them particularly effective when processing many files in a batch operation.
372
384
 
@@ -396,7 +408,7 @@ npx html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist
396
408
  npx html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext=html,php
397
409
 
398
410
  # Using configuration file that sets `fileExt` (e.g., `"fileExt": "html,php"`)
399
- npx html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir=dist
411
+ npx html-minifier-next --config-file=html-minifier-next.json --input-dir=src --output-dir=dist
400
412
 
401
413
  # Process all files (explicit wildcard)
402
414
  npx html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext='*'
@@ -608,7 +620,7 @@ Parameters:
608
620
  * `--save`: Saves the run as the baseline (e.g., on `main` before switching to a branch)
609
621
  * `--core`: Disables the external minifiers (CSS, JS, SVG, URLs) to isolate HMN’s own processing time
610
622
  * `--iterations=N`: Sets the number of timed iterations (default 5; the median is reported)
611
- * `--config=PATH`: Uses an alternative options file (default `html-minifier.json`)
623
+ * `--config=PATH`: Uses an alternative options file (default `html-minifier-next.json`)
612
624
 
613
625
  To compare branches (A/B run), execute `npm run benchmark -- --save` on `main`, then `npm run benchmark` on the branch to see the deltas. Add `--core` on both ends when measuring changes to HMN’s own code rather than the bundled minifiers.
614
626
 
package/cli.js CHANGED
@@ -227,7 +227,7 @@ async function loadConfigFromPath(configPath) {
227
227
  }
228
228
 
229
229
  // Config keys the CLI handles itself, beyond the options in `optionDefinitions`
230
- const CONFIG_KEYS_EXTRA = new Set(['preset', 'fileExt', 'ignoreDir']);
230
+ const CONFIG_KEYS_EXTRA = new Set(['$schema', 'preset', 'fileExt', 'ignoreDir']);
231
231
 
232
232
  /**
233
233
  * Normalize and validate config object by applying parsers and transforming values.
@@ -283,9 +283,6 @@ program.option('-O --output-dir <dir>', 'Specify an output directory');
283
283
  program.option('-f --file-ext <extensions>', 'Specify file extension(s) to process (comma-separated); defaults to “html,htm,shtml,shtm”; use “*” for all files');
284
284
  program.option('-p --preset <name>', `Use a preset configuration (${getPresetNames().join(', ')})`);
285
285
  program.option('-c --config-file <file>', 'Use config file');
286
- program.option('--cache-css <size>', 'Set CSS minification cache size (number of entries, default: 500)', parseValidInt('cacheCSS'));
287
- program.option('--cache-js <size>', 'Set JavaScript minification cache size (number of entries, default: 500)', parseValidInt('cacheJS'));
288
- program.option('--cache-svg <size>', 'Set SVG minification cache size (number of entries, default: 500)', parseValidInt('cacheSVG'));
289
286
  program.version(pkg.version, '-V, --version', 'Output the version number');
290
287
  program.helpOption('-h, --help', 'Display help for command');
291
288
 
@@ -1 +1 @@
1
- {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":";;;4BA0Ba;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE;AAiB3I;;;GAGG;AACH,uCAHW,MAAM,WACN,gBAAgB,WAS1B;AAED;;;GAGG;AACH,2CAHW,MAAM,WACN;IAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAC,WAa5C;AAED,4BAA4B;AAC5B,gDADY,MAAM,WAIjB;AAED;;;GAGG;AACH,8CAHW,aAAa,EAAE,aACf,MAAM,WAShB;AA+BD;;;;;GAKG;AACH,0CALW,MAAM,YACN,MAAM,aACN,MAAM,SACN,aAAa,EAAE,WAyCzB;AAED,mEAGC;AAED,qEAGC;AAED;;;GAGG;AACH,wCAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED,sEAGC;AAED;;;GAGG;AACH,oCAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED;;;GAGG;AACH,6CAHW,MAAM,aACN,MAAM,uBAMhB;AAmBD;;;GAGG;AACH,6CAHW,MAAM,OACN,MAAM,WAKhB;AAgBD;;;GAGG;AACH,gDAHW,MAAM,OACN,MAAM,WAKhB;AAED;;;;GAIG;AACH,gCAJW,MAAM,SACN,aAAa,EAAE,SACf,MAAM,WAYhB;AAED;;;;GAIG;AACH,kCAJW,MAAM,SACN,aAAa,EAAE,YACf,MAAM,WAIhB;AAED;;;GAGG;AACH,mCAHW,MAAM,OACN,MAAM,WAIhB;AAED;;;GAGG;AACH,oCAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED;;;GAGG;AACH,6CAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED;;;;;GAKG;AACH,6CALW,MAAM,YACN,MAAM,aACN,MAAM,GAAG,SAAS,WAClB;IAAC,qBAAqB,CAAC,EAAE,OAAO,WAAW,CAAA;CAAC,OAWtD;AAED;;;GAGG;AACH,kCAHW,MAAM,SACN,aAAa,EAAE,WASzB;AAUD;;;;;;;GAOG;AACH,yCAPW,MAAM,YACN,MAAM,aACN,MAAM,WACN,gBAAgB,SAChB,aAAa,EAAE,iCAkMzB;AAwBD;;;;;;GAMG;AACH,oCANW,aAAa,SACb,aAAa,EAAE,OACf,MAAM,WACN,gBAAgB;;;;;;;;2BAe1B;AA0DD;;;;;;GAMG;AACH,sCANW;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAC,iBAC9D,MAAM,GAAG,OAAO,GAAG,SAAS,WAC5B,gBAAgB,UAChB,OAAO,WACP,MAAM,GAAG,SAAS,UA0H5B;AAzsBD;;;;;;;GAOG;AACH,6CAJW,aAAa,EAAE,iBACf,OAAO,GACL,aAAa,EAAE,CAqB3B;sCAlGqC,cAAc"}
1
+ {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":";;;4BA0Ba;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE;AAiB3I;;;GAGG;AACH,uCAHW,MAAM,WACN,gBAAgB,WAS1B;AAED;;;GAGG;AACH,2CAHW,MAAM,WACN;IAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAC,WAa5C;AAED,4BAA4B;AAC5B,gDADY,MAAM,WAIjB;AAED;;;GAGG;AACH,8CAHW,aAAa,EAAE,aACf,MAAM,WAShB;AA+BD;;;;;GAKG;AACH,0CALW,MAAM,YACN,MAAM,aACN,MAAM,SACN,aAAa,EAAE,WAyCzB;AAED,mEAGC;AAED,qEAGC;AAED;;;GAGG;AACH,wCAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED,sEAGC;AAED;;;GAGG;AACH,oCAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED;;;GAGG;AACH,6CAHW,MAAM,aACN,MAAM,uBAMhB;AAmBD;;;GAGG;AACH,6CAHW,MAAM,OACN,MAAM,WAKhB;AAgBD;;;GAGG;AACH,gDAHW,MAAM,OACN,MAAM,WAKhB;AAED;;;;GAIG;AACH,gCAJW,MAAM,SACN,aAAa,EAAE,SACf,MAAM,WAYhB;AAED;;;;GAIG;AACH,kCAJW,MAAM,SACN,aAAa,EAAE,YACf,MAAM,WAIhB;AAED;;;GAGG;AACH,mCAHW,MAAM,OACN,MAAM,WAKhB;AAED;;;GAGG;AACH,oCAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED;;;GAGG;AACH,6CAHW,MAAM,SACN,aAAa,EAAE,WAYzB;AAED;;;;;GAKG;AACH,6CALW,MAAM,YACN,MAAM,aACN,MAAM,GAAG,SAAS,WAClB;IAAC,qBAAqB,CAAC,EAAE,OAAO,WAAW,CAAA;CAAC,OAWtD;AAED;;;GAGG;AACH,kCAHW,MAAM,SACN,aAAa,EAAE,WASzB;AAUD;;;;;;;GAOG;AACH,yCAPW,MAAM,YACN,MAAM,aACN,MAAM,WACN,gBAAgB,SAChB,aAAa,EAAE,iCA4NzB;AAwBD;;;;;;GAMG;AACH,oCANW,aAAa,SACb,aAAa,EAAE,OACf,MAAM,WACN,gBAAgB;;;;;;;;2BAe1B;AA0DD;;;;;;GAMG;AACH,sCANW;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAC,iBAC9D,MAAM,GAAG,OAAO,GAAG,SAAS,WAC5B,gBAAgB,UAChB,OAAO,WACP,MAAM,GAAG,SAAS,UA0H5B;AApuBD;;;;;;;GAOG;AACH,6CAJW,aAAa,EAAE,iBACf,OAAO,GACL,aAAa,EAAE,CAqB3B;sCAlGqC,cAAc"}
@@ -1,273 +1,291 @@
1
1
  export namespace optionDefinitions {
2
- namespace caseSensitive {
2
+ namespace cacheCSS {
3
3
  let description: string;
4
4
  let type: string;
5
5
  }
6
- namespace collapseAttributeWhitespace {
6
+ namespace cacheJS {
7
7
  let description_1: string;
8
8
  export { description_1 as description };
9
9
  let type_1: string;
10
10
  export { type_1 as type };
11
11
  }
12
- namespace collapseBooleanAttributes {
12
+ namespace cacheSVG {
13
13
  let description_2: string;
14
14
  export { description_2 as description };
15
15
  let type_2: string;
16
16
  export { type_2 as type };
17
17
  }
18
- namespace collapseInlineTagWhitespace {
18
+ namespace caseSensitive {
19
19
  let description_3: string;
20
20
  export { description_3 as description };
21
21
  let type_3: string;
22
22
  export { type_3 as type };
23
23
  }
24
- namespace collapseWhitespace {
24
+ namespace collapseAttributeWhitespace {
25
25
  let description_4: string;
26
26
  export { description_4 as description };
27
27
  let type_4: string;
28
28
  export { type_4 as type };
29
29
  }
30
- namespace conservativeCollapse {
30
+ namespace collapseBooleanAttributes {
31
31
  let description_5: string;
32
32
  export { description_5 as description };
33
33
  let type_5: string;
34
34
  export { type_5 as type };
35
35
  }
36
- namespace continueOnMinifyError {
36
+ namespace collapseInlineTagWhitespace {
37
37
  let description_6: string;
38
38
  export { description_6 as description };
39
- export let descriptionAffirmative: string;
40
39
  let type_6: string;
41
40
  export { type_6 as type };
42
41
  }
43
- namespace continueOnParseError {
42
+ namespace collapseWhitespace {
44
43
  let description_7: string;
45
44
  export { description_7 as description };
46
45
  let type_7: string;
47
46
  export { type_7 as type };
48
47
  }
49
- namespace customAttrAssign {
48
+ namespace conservativeCollapse {
50
49
  let description_8: string;
51
50
  export { description_8 as description };
52
51
  let type_8: string;
53
52
  export { type_8 as type };
54
53
  }
55
- namespace customAttrCollapse {
54
+ namespace continueOnMinifyError {
56
55
  let description_9: string;
57
56
  export { description_9 as description };
57
+ export let descriptionAffirmative: string;
58
58
  let type_9: string;
59
59
  export { type_9 as type };
60
60
  }
61
- namespace customAttrSurround {
61
+ namespace continueOnParseError {
62
62
  let description_10: string;
63
63
  export { description_10 as description };
64
64
  let type_10: string;
65
65
  export { type_10 as type };
66
66
  }
67
- namespace customEventAttributes {
67
+ namespace customAttrAssign {
68
68
  let description_11: string;
69
69
  export { description_11 as description };
70
70
  let type_11: string;
71
71
  export { type_11 as type };
72
72
  }
73
- namespace customFragmentQuantifierLimit {
73
+ namespace customAttrCollapse {
74
74
  let description_12: string;
75
75
  export { description_12 as description };
76
76
  let type_12: string;
77
77
  export { type_12 as type };
78
78
  }
79
- namespace decodeEntities {
79
+ namespace customAttrSurround {
80
80
  let description_13: string;
81
81
  export { description_13 as description };
82
82
  let type_13: string;
83
83
  export { type_13 as type };
84
84
  }
85
- namespace ignoreCustomComments {
85
+ namespace customEventAttributes {
86
86
  let description_14: string;
87
87
  export { description_14 as description };
88
88
  let type_14: string;
89
89
  export { type_14 as type };
90
90
  }
91
- namespace ignoreCustomFragments {
91
+ namespace customFragmentQuantifierLimit {
92
92
  let description_15: string;
93
93
  export { description_15 as description };
94
94
  let type_15: string;
95
95
  export { type_15 as type };
96
96
  }
97
- namespace includeAutoGeneratedTags {
97
+ namespace decodeEntities {
98
98
  let description_16: string;
99
99
  export { description_16 as description };
100
100
  let type_16: string;
101
101
  export { type_16 as type };
102
102
  }
103
- namespace inlineCustomElements {
103
+ namespace ignoreCustomComments {
104
104
  let description_17: string;
105
105
  export { description_17 as description };
106
106
  let type_17: string;
107
107
  export { type_17 as type };
108
108
  }
109
- namespace keepClosingSlash {
109
+ namespace ignoreCustomFragments {
110
110
  let description_18: string;
111
111
  export { description_18 as description };
112
112
  let type_18: string;
113
113
  export { type_18 as type };
114
114
  }
115
- namespace maxInputLength {
115
+ namespace includeAutoGeneratedTags {
116
116
  let description_19: string;
117
117
  export { description_19 as description };
118
118
  let type_19: string;
119
119
  export { type_19 as type };
120
120
  }
121
- namespace maxLineLength {
121
+ namespace inlineCustomElements {
122
122
  let description_20: string;
123
123
  export { description_20 as description };
124
124
  let type_20: string;
125
125
  export { type_20 as type };
126
126
  }
127
- namespace mergeScripts {
127
+ namespace keepClosingSlash {
128
128
  let description_21: string;
129
129
  export { description_21 as description };
130
130
  let type_21: string;
131
131
  export { type_21 as type };
132
132
  }
133
- namespace minifyCSS {
133
+ namespace maxInputLength {
134
134
  let description_22: string;
135
135
  export { description_22 as description };
136
136
  let type_22: string;
137
137
  export { type_22 as type };
138
138
  }
139
- namespace minifyJS {
139
+ namespace maxLineLength {
140
140
  let description_23: string;
141
141
  export { description_23 as description };
142
142
  let type_23: string;
143
143
  export { type_23 as type };
144
144
  }
145
- namespace minifySVG {
145
+ namespace mergeScripts {
146
146
  let description_24: string;
147
147
  export { description_24 as description };
148
148
  let type_24: string;
149
149
  export { type_24 as type };
150
150
  }
151
- namespace minifyURLs {
151
+ namespace minifyCSS {
152
152
  let description_25: string;
153
153
  export { description_25 as description };
154
154
  let type_25: string;
155
155
  export { type_25 as type };
156
156
  }
157
- namespace noNewlinesBeforeTagClose {
157
+ namespace minifyJS {
158
158
  let description_26: string;
159
159
  export { description_26 as description };
160
160
  let type_26: string;
161
161
  export { type_26 as type };
162
162
  }
163
- namespace partialMarkup {
163
+ namespace minifySVG {
164
164
  let description_27: string;
165
165
  export { description_27 as description };
166
166
  let type_27: string;
167
167
  export { type_27 as type };
168
168
  }
169
- namespace preserveLineBreaks {
169
+ namespace minifyURLs {
170
170
  let description_28: string;
171
171
  export { description_28 as description };
172
172
  let type_28: string;
173
173
  export { type_28 as type };
174
174
  }
175
- namespace preventAttributesEscaping {
175
+ namespace noNewlinesBeforeTagClose {
176
176
  let description_29: string;
177
177
  export { description_29 as description };
178
178
  let type_29: string;
179
179
  export { type_29 as type };
180
180
  }
181
- namespace processScripts {
181
+ namespace partialMarkup {
182
182
  let description_30: string;
183
183
  export { description_30 as description };
184
184
  let type_30: string;
185
185
  export { type_30 as type };
186
186
  }
187
- namespace quoteCharacter {
187
+ namespace preserveLineBreaks {
188
188
  let description_31: string;
189
189
  export { description_31 as description };
190
190
  let type_31: string;
191
191
  export { type_31 as type };
192
192
  }
193
- namespace removeAttributeQuotes {
193
+ namespace preventAttributesEscaping {
194
194
  let description_32: string;
195
195
  export { description_32 as description };
196
196
  let type_32: string;
197
197
  export { type_32 as type };
198
198
  }
199
- namespace removeComments {
199
+ namespace processScripts {
200
200
  let description_33: string;
201
201
  export { description_33 as description };
202
202
  let type_33: string;
203
203
  export { type_33 as type };
204
204
  }
205
- namespace removeDefaultTypeAttributes {
205
+ namespace quoteCharacter {
206
206
  let description_34: string;
207
207
  export { description_34 as description };
208
208
  let type_34: string;
209
209
  export { type_34 as type };
210
210
  }
211
- namespace removeEmptyAttributes {
211
+ namespace removeAttributeQuotes {
212
212
  let description_35: string;
213
213
  export { description_35 as description };
214
214
  let type_35: string;
215
215
  export { type_35 as type };
216
216
  }
217
- namespace removeEmptyElements {
217
+ namespace removeComments {
218
218
  let description_36: string;
219
219
  export { description_36 as description };
220
220
  let type_36: string;
221
221
  export { type_36 as type };
222
222
  }
223
- namespace removeEmptyElementsExcept {
223
+ namespace removeDefaultTypeAttributes {
224
224
  let description_37: string;
225
225
  export { description_37 as description };
226
226
  let type_37: string;
227
227
  export { type_37 as type };
228
228
  }
229
- namespace removeOptionalTags {
229
+ namespace removeEmptyAttributes {
230
230
  let description_38: string;
231
231
  export { description_38 as description };
232
232
  let type_38: string;
233
233
  export { type_38 as type };
234
234
  }
235
- namespace removeRedundantAttributes {
235
+ namespace removeEmptyElements {
236
236
  let description_39: string;
237
237
  export { description_39 as description };
238
238
  let type_39: string;
239
239
  export { type_39 as type };
240
240
  }
241
- namespace removeTagWhitespace {
241
+ namespace removeEmptyElementsExcept {
242
242
  let description_40: string;
243
243
  export { description_40 as description };
244
244
  let type_40: string;
245
245
  export { type_40 as type };
246
246
  }
247
- namespace sortAttributes {
247
+ namespace removeOptionalTags {
248
248
  let description_41: string;
249
249
  export { description_41 as description };
250
250
  let type_41: string;
251
251
  export { type_41 as type };
252
252
  }
253
- namespace sortClassNames {
253
+ namespace removeRedundantAttributes {
254
254
  let description_42: string;
255
255
  export { description_42 as description };
256
256
  let type_42: string;
257
257
  export { type_42 as type };
258
258
  }
259
- namespace trimCustomFragments {
259
+ namespace removeTagWhitespace {
260
260
  let description_43: string;
261
261
  export { description_43 as description };
262
262
  let type_43: string;
263
263
  export { type_43 as type };
264
264
  }
265
- namespace useShortDoctype {
265
+ namespace sortAttributes {
266
266
  let description_44: string;
267
267
  export { description_44 as description };
268
268
  let type_44: string;
269
269
  export { type_44 as type };
270
270
  }
271
+ namespace sortClassNames {
272
+ let description_45: string;
273
+ export { description_45 as description };
274
+ let type_45: string;
275
+ export { type_45 as type };
276
+ }
277
+ namespace trimCustomFragments {
278
+ let description_46: string;
279
+ export { description_46 as description };
280
+ let type_46: string;
281
+ export { type_46 as type };
282
+ }
283
+ namespace useShortDoctype {
284
+ let description_47: string;
285
+ export { description_47 as description };
286
+ let type_47: string;
287
+ export { type_47 as type };
288
+ }
271
289
  }
272
290
  export namespace optionDefaults {
273
291
  let continueOnMinifyError_1: boolean;
@@ -0,0 +1,303 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://raw.githubusercontent.com/j9t/html-minifier-next/main/html-minifier-next.schema.json",
4
+ "title": "HTML Minifier Next configuration",
5
+ "description": "Configuration file for HTML Minifier Next (HMN), used via `--config-file`",
6
+ "type": "object",
7
+ "properties": {
8
+ "$schema": {
9
+ "description": "JSON Schema reference for this configuration file (enables editor validation and autocomplete)",
10
+ "type": "string"
11
+ },
12
+ "preset": {
13
+ "description": "Preset configuration to start from—config file options override preset options",
14
+ "enum": [
15
+ "conservative",
16
+ "comprehensive"
17
+ ]
18
+ },
19
+ "fileExt": {
20
+ "description": "File extension(s) to process, as comma-separated string or array (default: `html,htm,shtml,shtm`; use `*` for all files)",
21
+ "type": [
22
+ "string",
23
+ "array"
24
+ ],
25
+ "items": {
26
+ "type": "string"
27
+ }
28
+ },
29
+ "ignoreDir": {
30
+ "description": "Directories—relative to the input directory—to exclude from processing, as comma-separated string or array",
31
+ "type": [
32
+ "string",
33
+ "array"
34
+ ],
35
+ "items": {
36
+ "type": "string"
37
+ }
38
+ },
39
+ "cacheCSS": {
40
+ "description": "Set CSS minification cache size (number of entries, default: 500)",
41
+ "type": "integer",
42
+ "minimum": 0
43
+ },
44
+ "cacheJS": {
45
+ "description": "Set JavaScript minification cache size (number of entries, default: 500)",
46
+ "type": "integer",
47
+ "minimum": 0
48
+ },
49
+ "cacheSVG": {
50
+ "description": "Set SVG minification cache size (number of entries, default: 500)",
51
+ "type": "integer",
52
+ "minimum": 0
53
+ },
54
+ "caseSensitive": {
55
+ "description": "Treat attributes in case-sensitive manner (useful for custom HTML elements)",
56
+ "type": "boolean"
57
+ },
58
+ "collapseAttributeWhitespace": {
59
+ "description": "Trim and collapse whitespace characters within attribute values",
60
+ "type": "boolean"
61
+ },
62
+ "collapseBooleanAttributes": {
63
+ "description": "Omit attribute values from boolean attributes",
64
+ "type": "boolean"
65
+ },
66
+ "collapseInlineTagWhitespace": {
67
+ "description": "Collapse whitespace more aggressively between inline elements—use with `--collapse-whitespace`",
68
+ "type": "boolean"
69
+ },
70
+ "collapseWhitespace": {
71
+ "description": "Collapse whitespace that contributes to text nodes in a document tree",
72
+ "type": "boolean"
73
+ },
74
+ "conservativeCollapse": {
75
+ "description": "Always collapse to one space (never remove it entirely)—use with `--collapse-whitespace`",
76
+ "type": "boolean"
77
+ },
78
+ "continueOnMinifyError": {
79
+ "description": "Continue on minification errors",
80
+ "type": "boolean"
81
+ },
82
+ "continueOnParseError": {
83
+ "description": "Handle parse errors instead of aborting",
84
+ "type": "boolean"
85
+ },
86
+ "customAttrAssign": {
87
+ "description": "Array of regexes that allow to support custom attribute assign expressions (e.g., `<div flex?=\"{{mode != cover}}\"></div>`)",
88
+ "type": [
89
+ "string",
90
+ "array"
91
+ ],
92
+ "items": {
93
+ "type": "string"
94
+ }
95
+ },
96
+ "customAttrCollapse": {
97
+ "description": "Regex that specifies custom attribute to strip newlines from (e.g., `/ng-class/`)",
98
+ "type": "string"
99
+ },
100
+ "customAttrSurround": {
101
+ "description": "Array of regexes that allow to support custom attribute surround expressions (e.g., `<input {{#if value}}checked=\"checked\"{{/if}}>`)",
102
+ "type": [
103
+ "string",
104
+ "array"
105
+ ],
106
+ "items": {
107
+ "type": "string"
108
+ }
109
+ },
110
+ "customEventAttributes": {
111
+ "description": "Array of regexes that allow to support custom event attributes for minifyJS (e.g., `ng-click`)",
112
+ "type": [
113
+ "string",
114
+ "array"
115
+ ],
116
+ "items": {
117
+ "type": "string"
118
+ }
119
+ },
120
+ "customFragmentQuantifierLimit": {
121
+ "description": "Set maximum quantifier limit for custom fragments to prevent ReDoS attacks (default: 200)",
122
+ "type": "integer",
123
+ "minimum": 0
124
+ },
125
+ "decodeEntities": {
126
+ "description": "Use direct Unicode characters whenever possible",
127
+ "type": "boolean"
128
+ },
129
+ "ignoreCustomComments": {
130
+ "description": "Array of regexes that allow to ignore matching comments",
131
+ "type": [
132
+ "string",
133
+ "array"
134
+ ],
135
+ "items": {
136
+ "type": "string"
137
+ }
138
+ },
139
+ "ignoreCustomFragments": {
140
+ "description": "Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php … ?>`, `{{ … }}`)",
141
+ "type": [
142
+ "string",
143
+ "array"
144
+ ],
145
+ "items": {
146
+ "type": "string"
147
+ }
148
+ },
149
+ "includeAutoGeneratedTags": {
150
+ "description": "Insert elements generated by HTML parser",
151
+ "type": "boolean"
152
+ },
153
+ "inlineCustomElements": {
154
+ "description": "Array of names of custom elements which are inline, for whitespace handling",
155
+ "type": [
156
+ "string",
157
+ "array"
158
+ ],
159
+ "items": {
160
+ "type": "string"
161
+ }
162
+ },
163
+ "keepClosingSlash": {
164
+ "description": "Keep the trailing slash on void elements",
165
+ "type": "boolean"
166
+ },
167
+ "maxInputLength": {
168
+ "description": "Maximum input length to prevent ReDoS attacks",
169
+ "type": "integer",
170
+ "minimum": 0
171
+ },
172
+ "maxLineLength": {
173
+ "description": "Specify a maximum line length; compressed output will be split by newlines at valid HTML split-points",
174
+ "type": "integer",
175
+ "minimum": 0
176
+ },
177
+ "mergeScripts": {
178
+ "description": "Merge consecutive inline `script` elements into one",
179
+ "type": "boolean"
180
+ },
181
+ "minifyCSS": {
182
+ "description": "Minify CSS in `style` elements and attributes (uses Lightning CSS)",
183
+ "type": [
184
+ "boolean",
185
+ "string",
186
+ "object"
187
+ ]
188
+ },
189
+ "minifyJS": {
190
+ "description": "Minify JavaScript in `script` elements and event attributes (uses Terser or SWC; pass `{\"engine\": \"swc\"}` for SWC)",
191
+ "type": [
192
+ "boolean",
193
+ "string",
194
+ "object"
195
+ ]
196
+ },
197
+ "minifySVG": {
198
+ "description": "Minify SVG elements (uses SVGO)",
199
+ "type": [
200
+ "boolean",
201
+ "string",
202
+ "object"
203
+ ]
204
+ },
205
+ "minifyURLs": {
206
+ "description": "Minify URLs in various attributes",
207
+ "type": [
208
+ "boolean",
209
+ "string",
210
+ "object"
211
+ ]
212
+ },
213
+ "noNewlinesBeforeTagClose": {
214
+ "description": "Never add a newline before a tag that closes an element",
215
+ "type": "boolean"
216
+ },
217
+ "partialMarkup": {
218
+ "description": "Treat input as a partial HTML fragment, preserving stray end tags and unclosed tags",
219
+ "type": "boolean"
220
+ },
221
+ "preserveLineBreaks": {
222
+ "description": "Always collapse to one line break (never remove it entirely) when whitespace between tags includes a line break—use with `--collapse-whitespace`",
223
+ "type": "boolean"
224
+ },
225
+ "preventAttributesEscaping": {
226
+ "description": "Prevents the escaping of the values of attributes",
227
+ "type": "boolean"
228
+ },
229
+ "processScripts": {
230
+ "description": "Array of strings corresponding to types of `script` elements to process through minifier (e.g., `text/ng-template`, `text/x-handlebars-template`, etc.)",
231
+ "type": [
232
+ "string",
233
+ "array"
234
+ ],
235
+ "items": {
236
+ "type": "string"
237
+ }
238
+ },
239
+ "quoteCharacter": {
240
+ "description": "Type of quote to use for attribute values (`'` or `\"`)",
241
+ "type": "string"
242
+ },
243
+ "removeAttributeQuotes": {
244
+ "description": "Remove quotes around attributes when possible",
245
+ "type": "boolean"
246
+ },
247
+ "removeComments": {
248
+ "description": "Strip HTML comments",
249
+ "type": "boolean"
250
+ },
251
+ "removeDefaultTypeAttributes": {
252
+ "description": "Remove default `type` attributes from `style`/`link` (e.g., `type=\"text/css\"`) and `script` (e.g., `type=\"text/javascript\"`) elements; other `type` attribute values are left intact",
253
+ "type": "boolean"
254
+ },
255
+ "removeEmptyAttributes": {
256
+ "description": "Remove all attributes with whitespace-only values",
257
+ "type": "boolean"
258
+ },
259
+ "removeEmptyElements": {
260
+ "description": "Remove all elements with empty contents",
261
+ "type": "boolean"
262
+ },
263
+ "removeEmptyElementsExcept": {
264
+ "description": "Array of elements to preserve when `--remove-empty-elements` is enabled (e.g., `td`, `<span aria-hidden=\"true\">`)",
265
+ "type": [
266
+ "string",
267
+ "array"
268
+ ],
269
+ "items": {
270
+ "type": "string"
271
+ }
272
+ },
273
+ "removeOptionalTags": {
274
+ "description": "Remove optional tags",
275
+ "type": "boolean"
276
+ },
277
+ "removeRedundantAttributes": {
278
+ "description": "Remove attributes when value matches default",
279
+ "type": "boolean"
280
+ },
281
+ "removeTagWhitespace": {
282
+ "description": "Remove space between attributes whenever possible; note that this will result in invalid HTML",
283
+ "type": "boolean"
284
+ },
285
+ "sortAttributes": {
286
+ "description": "Sort attributes by frequency",
287
+ "type": "boolean"
288
+ },
289
+ "sortClassNames": {
290
+ "description": "Sort style classes by frequency",
291
+ "type": "boolean"
292
+ },
293
+ "trimCustomFragments": {
294
+ "description": "Trim whitespace around custom fragments (`--ignore-custom-fragments`)",
295
+ "type": "boolean"
296
+ },
297
+ "useShortDoctype": {
298
+ "description": "Replaces the doctype with the short HTML doctype",
299
+ "type": "boolean"
300
+ }
301
+ },
302
+ "additionalProperties": false
303
+ }
package/package.json CHANGED
@@ -14,13 +14,13 @@
14
14
  },
15
15
  "description": "Super-configurable and well-tested web page minifier (enhanced successor to HTML Minifier)",
16
16
  "devDependencies": {
17
- "@commitlint/cli": "^21.0.1",
17
+ "@commitlint/cli": "^21.2.0",
18
18
  "@eslint/js": "^10.0.1",
19
- "@swc/core": "^1.15.40",
20
- "@types/node": "^25.9.1",
21
- "eslint": "^10.4.0",
19
+ "@swc/core": "^1.15.41",
20
+ "@types/node": "^26.1.0",
21
+ "eslint": "^10.6.0",
22
22
  "typescript": "^6.0.3",
23
- "vite": "^8.0.14"
23
+ "vite": "^8.0.16"
24
24
  },
25
25
  "engines": {
26
26
  "node": ">=22"
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "files": [
36
36
  "cli.js",
37
+ "html-minifier-next.schema.json",
37
38
  "dist/types/",
38
39
  "src/"
39
40
  ],
@@ -78,8 +79,9 @@
78
79
  "url": "git+https://github.com/j9t/html-minifier-next.git"
79
80
  },
80
81
  "scripts": {
81
- "build": "tsc",
82
+ "build": "tsc && npm run build:schema",
82
83
  "build:docs": "vite build --base /html-minifier-next/",
84
+ "build:schema": "node scripts/build-schema.js",
83
85
  "deploy": "npm run build && npm run build:docs",
84
86
  "lint": "eslint .",
85
87
  "prebuild": "node --input-type=module --eval \"import {rmSync} from 'fs';rmSync('dist',{recursive:true,force:true})\"",
@@ -91,5 +93,5 @@
91
93
  "test:watch": "node --test --watch tests/*.spec.js"
92
94
  },
93
95
  "type": "module",
94
- "version": "7.0.0"
96
+ "version": "7.2.0"
95
97
  }
@@ -303,7 +303,8 @@ function isMediaQuery(tag, attrs, attrName) {
303
303
  * @param {string} tag
304
304
  */
305
305
  function isSrcset(attrName, tag) {
306
- return attrName === 'srcset' && srcsetElements.has(tag);
306
+ return (attrName === 'srcset' && srcsetElements.has(tag)) ||
307
+ (attrName === 'imagesrcset' && tag === 'link');
307
308
  }
308
309
 
309
310
  /**
@@ -492,8 +493,34 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTM
492
493
  }
493
494
 
494
495
  if (isSrcset(attrName, tag)) {
495
- // https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset
496
- const candidates = trimWhitespace(attrValue).split(/\s*,\s*/);
496
+ // Split into image candidate strings following the spec parsing algorithm
497
+ // (https://html.spec.whatwg.org/multipage/images.html#parsing-a-srcset-attribute);
498
+ // a naive split on commas would corrupt URLs that contain commas
499
+ const value = trimWhitespace(attrValue);
500
+ /** @type {string[]} */
501
+ const candidates = [];
502
+ let pos = 0;
503
+ while (pos < value.length) {
504
+ // Skip whitespace and separator commas
505
+ while (pos < value.length && /[\s,]/.test(value.charAt(pos))) pos++;
506
+ if (pos >= value.length) break;
507
+ const start = pos;
508
+ // URL: a run of non-whitespace characters (which may contain commas)
509
+ while (pos < value.length && !/\s/.test(value.charAt(pos))) pos++;
510
+ if (value.charAt(pos - 1) === ',') {
511
+ // Trailing comma(s) end the candidate—a URL without descriptor
512
+ candidates.push(value.slice(start, pos).replace(/,+$/, ''));
513
+ continue;
514
+ }
515
+ // Descriptor: everything up to the next comma outside parentheses
516
+ let inParens = false;
517
+ while (pos < value.length && (inParens || value.charAt(pos) !== ',')) {
518
+ if (value.charAt(pos) === '(') inParens = true;
519
+ else if (value.charAt(pos) === ')') inParens = false;
520
+ pos++;
521
+ }
522
+ candidates.push(trimWhitespace(value.slice(start, pos)));
523
+ }
497
524
  const processed = candidates.map(candidate => {
498
525
  let url = candidate;
499
526
  let descriptor = '';
@@ -1,6 +1,18 @@
1
1
  // Single source of truth for minifier option names, descriptions, types, and shared defaults
2
2
 
3
3
  const optionDefinitions = {
4
+ cacheCSS: {
5
+ description: 'Set CSS minification cache size (number of entries, default: 500)',
6
+ type: 'int'
7
+ },
8
+ cacheJS: {
9
+ description: 'Set JavaScript minification cache size (number of entries, default: 500)',
10
+ type: 'int'
11
+ },
12
+ cacheSVG: {
13
+ description: 'Set SVG minification cache size (number of entries, default: 500)',
14
+ type: 'int'
15
+ },
4
16
  caseSensitive: {
5
17
  description: 'Treat attributes in case-sensitive manner (useful for custom HTML elements)',
6
18
  type: 'boolean'
@@ -50,7 +50,7 @@ function shouldMinifyInnerHTML(options) {
50
50
  }
51
51
 
52
52
  // User-facing option keys that are valid but not listed in `optionDefinitions`
53
- const optionKeysExtra = new Set(['preset', 'log', 'canCollapseWhitespace', 'canTrimWhitespace', 'cacheCSS', 'cacheJS', 'cacheSVG']);
53
+ const optionKeysExtra = new Set(['preset', 'log', 'canCollapseWhitespace', 'canTrimWhitespace']);
54
54
 
55
55
  // Unknown option keys and preset names already warned about—warn once per
56
56
  // key per process, so repeated `minify` calls (e.g., batch runs) don’t flood STDERR