html-minifier-next 8.4.3 → 8.4.4

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
@@ -83,6 +83,8 @@ export default {
83
83
  };
84
84
  ```
85
85
 
86
+ A module can also hold values JSON cannot express, like regular expressions (e.g., `ignoreCustomFragments: [/\{\{[\s\S]*?\}\}/]`) and functions (for `minifyCSS`, `minifyJS`, and `minifyURLs`, or in custom SVGO plugins for `minifySVG`). As functions cannot be passed to worker threads, a run using them minifies in-process.
87
+
86
88
  ### Node.js
87
89
 
88
90
  ```js
package/cli.js CHANGED
@@ -262,6 +262,28 @@ const CONFIG_KEYS_EXTRA = new Set(['$schema', 'preset', 'fileExt', 'ignoreDir'])
262
262
  // `--config-file` isn’t specified
263
263
  const CONFIG_FILES_DEFAULT = ['html-minifier-next.config.json', 'htmlminifier.config.json'];
264
264
 
265
+ /**
266
+ * Parse a config value—strings as on the command line, anything else as it is, since
267
+ * a JSON round trip would drop what only a module config can hold (functions, regexes)
268
+ * @param {string} key Option definition key
269
+ * @param {string} type Option definition type
270
+ * @param {unknown} value
271
+ */
272
+ function parseConfigValue(key, type, value) {
273
+ if (typeof value === 'string') return getParser(key, type)(value);
274
+ switch (type) {
275
+ case 'int':
276
+ return getParser(key, type)(String(value));
277
+ // A single value is wrapped as its string form is; `null` and `undefined` leave the option unset
278
+ case 'regexpArray':
279
+ return value == null ? undefined : (Array.isArray(value) ? value : [value]).map(parseRegExp);
280
+ case 'jsonArray':
281
+ return value == null ? undefined : (Array.isArray(value) ? value : [value]);
282
+ default:
283
+ return value;
284
+ }
285
+ }
286
+
265
287
  /**
266
288
  * Normalize and validate config object by applying parsers and transforming values.
267
289
  * @param {Record<string, any>} config - Raw config object
@@ -282,8 +304,7 @@ function normalizeConfig(config) {
282
304
  Object.entries(optionDefinitions).forEach(function ([key, { type }]) {
283
305
  if (key in normalized) {
284
306
  if (type !== 'boolean' && type !== 'invertedBoolean') {
285
- const value = normalized[key];
286
- normalized[key] = getParser(key, type)(typeof value === 'string' ? value : JSON.stringify(value));
307
+ normalized[key] = parseConfigValue(key, type, normalized[key]);
287
308
  }
288
309
  }
289
310
  });
@@ -554,7 +575,7 @@ program.helpOption('-h, --help', 'Display help for command');
554
575
 
555
576
  /**
556
577
  * A pool for this run, or `null` where one wouldn’t pay or wouldn’t work—in which case
557
- * the caller minifies in process exactly as before
578
+ * the caller minifies in-process exactly as before
558
579
  * @param {string[]} files
559
580
  */
560
581
  async function createPool(files) {
@@ -564,7 +585,7 @@ program.helpOption('-h, --help', 'Display help for command');
564
585
  // Whether the run went wide is worth saying, since the default weighs the file
565
586
  // count and the bytes there are to share out, neither of which the caller sees
566
587
  const inProcess = () => {
567
- if (isVerbose) console.error('Worker threads: none (minifying in process)');
588
+ if (isVerbose) console.error('Worker threads: none (minifying in-process)');
568
589
  return null;
569
590
  };
570
591
  // `availableParallelism` reports what this process may actually use, which under a
@@ -594,7 +615,7 @@ program.helpOption('-h, --help', 'Display help for command');
594
615
  }
595
616
 
596
617
  // `log` is a closure over this process’s console and is rebuilt inside the worker;
597
- // anything else unclonable (which the CLI itself never produces) stays in process
618
+ // anything else unclonable (such as a function from a module config) stays in-process
598
619
  const { log, ...options } = createOptions();
599
620
  try {
600
621
  structuredClone(options);
@@ -603,7 +624,7 @@ program.helpOption('-h, --help', 'Display help for command');
603
624
  }
604
625
 
605
626
  // A pool this small has nothing to share out and still pays for its workers, so an
606
- // explicit request for them lands in process as `--workers=1` does
627
+ // explicit request for them lands in-process as `--workers=1` does
607
628
  const poolSize = Math.min(size, fileCount);
608
629
  if (poolSize <= 1) {
609
630
  return inProcess();
@@ -2,6 +2,8 @@
2
2
  * General utility functions
3
3
  */
4
4
  /**
5
+ * Stringify with sorted keys, for cache keys—values that can behave differently must not
6
+ * stringify alike, so unquoted markers stand in for what JSON cannot express
5
7
  * @param {unknown} obj
6
8
  * @returns {string}
7
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,iBAAS,eAAe,CAAC,GAAG,EAHjB,OAGiB,GAFf,MAAM,CAYlB;AAID,cAAM,GAAG;IAEA,KAAK;IACL,IAAI;IACJ,IAAI;IACT,mCAAmC;IAC9B,GAAG,EADG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAJjC,YAAY,KAAK,SAAM,EAMtB;IACD,0BAA0B;IAC1B,GAAG,CAAC,GAAG,EADK,MACL,WAUN;IACD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAHI,MAGJ,EAAE,KAAK,EAFH,OAEG,QAOb;IACD,0BAA0B;IAC1B,MAAM,CAAC,GAAG,EADE,MACF,QAA2B;IACrC,6EAA6E;IAC7E,KAAK,IADS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAGxE;CACF;AAKD,QAAA,MAAM,oBAAoB,QAAc,CAAC;AAIzC,0BAA0B;AAC1B,iBAAS,WAAW,CAAC,GAAG,EADZ,MACY,UAOvB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAMtB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAEtB;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAHd,OAGc,GAFZ,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAGD,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAID;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,GAAG,EALpB,MAKoB,EAAE,KAAK,EAJ3B,MAI2B,EAAE,OAAO,UAAA,GAFlC,OAAO,CAAC,MAAM,CAAC,CAe3B;AAID,qCAAqC;AACrC,iBAAS,WAAW,CAAC,KAAK,EADd,MAAM,GAAG,MACK,sBAUzB;AAyYD;;;;;;;;;GASG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAJd,MAIc,GAHZ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAqDlC;AAED;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,OAAO,EAHjB,MAGiB,GAFf,MAAM,CAOlB;AAGE,YAA6D,MAAM,GAAzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAA;CAAC,CAE7D;AAqKD;;;;;GAKG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAL5B,MAAM,GAAG,MAKmB,GAH1B,MAAM,GAAG,IAAI,CAqBzB;AAED;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,IAAI,EALb,MAKa,EAAE,GAAG,EAJlB,MAIkB,EAAE,KAAK,AAHjC,CACA,EADQ,MAGuC,GAFrC,MAAM,CAelB;AAED;;;;;;;;;GASG;AACH,iBAAS,yBAAyB,CAAC,KAAK,EAL7B,MAK6B,EAAE,SAAS,EAJxC,MAIwC,EAAE,KAAK,EAH/C,OAG+C,GAF7C,MAAM,CAYlB;AAID,OAAO,EACL,eAAe,EACf,UAAU,EACV,GAAG,EACH,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,QAAQ,EACR,sBAAsB,EACtB,yBAAyB,EAC1B,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAAA;;GAEG;AAkBH;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,GAAG,EAHjB,OAGiB,GAFf,MAAM,CAgBlB;AAID,cAAM,GAAG;IAEA,KAAK;IACL,IAAI;IACJ,IAAI;IACT,mCAAmC;IAC9B,GAAG,EADG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAJjC,YAAY,KAAK,SAAM,EAMtB;IACD,0BAA0B;IAC1B,GAAG,CAAC,GAAG,EADK,MACL,WAUN;IACD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAHI,MAGJ,EAAE,KAAK,EAFH,OAEG,QAOb;IACD,0BAA0B;IAC1B,MAAM,CAAC,GAAG,EADE,MACF,QAA2B;IACrC,6EAA6E;IAC7E,KAAK,IADS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAGxE;CACF;AAKD,QAAA,MAAM,oBAAoB,QAAc,CAAC;AAIzC,0BAA0B;AAC1B,iBAAS,WAAW,CAAC,GAAG,EADZ,MACY,UAOvB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAMtB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAEtB;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAHd,OAGc,GAFZ,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAGD,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAID;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,GAAG,EALpB,MAKoB,EAAE,KAAK,EAJ3B,MAI2B,EAAE,OAAO,UAAA,GAFlC,OAAO,CAAC,MAAM,CAAC,CAe3B;AAID,qCAAqC;AACrC,iBAAS,WAAW,CAAC,KAAK,EADd,MAAM,GAAG,MACK,sBAUzB;AAyYD;;;;;;;;;GASG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAJd,MAIc,GAHZ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAqDlC;AAED;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,OAAO,EAHjB,MAGiB,GAFf,MAAM,CAOlB;AAGE,YAA6D,MAAM,GAAzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAA;CAAC,CAE7D;AAqKD;;;;;GAKG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAL5B,MAAM,GAAG,MAKmB,GAH1B,MAAM,GAAG,IAAI,CAqBzB;AAED;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,IAAI,EALb,MAKa,EAAE,GAAG,EAJlB,MAIkB,EAAE,KAAK,AAHjC,CACA,EADQ,MAGuC,GAFrC,MAAM,CAelB;AAED;;;;;;;;;GASG;AACH,iBAAS,yBAAyB,CAAC,KAAK,EAL7B,MAK6B,EAAE,SAAS,EAJxC,MAIwC,EAAE,KAAK,EAH/C,OAG+C,GAF7C,MAAM,CAYlB;AAID,OAAO,EACL,eAAe,EACf,UAAU,EACV,GAAG,EACH,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,QAAQ,EACR,sBAAsB,EACtB,yBAAyB,EAC1B,CAAC"}
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "eslint": "^10.10.0",
22
22
  "globals": "^17.12.0",
23
23
  "typescript": "^7.0.2",
24
- "vite": "^8.2.2"
24
+ "vite": "^8.3.0"
25
25
  },
26
26
  "engines": {
27
27
  "node": ">=22.13"
@@ -95,5 +95,5 @@
95
95
  },
96
96
  "type": "module",
97
97
  "types": "dist/types/htmlminifier.d.ts",
98
- "version": "8.4.3"
98
+ "version": "8.4.4"
99
99
  }
@@ -15,7 +15,7 @@
15
15
  * worker runs its own isolate and warms up its own JIT, so the useful worker count tops
16
16
  * out well short of the core count.
17
17
  *
18
- * Node-only: cli.js loads this module lazily and minifies in process wherever worker
18
+ * Node-only: cli.js loads this module lazily and minifies in-process wherever worker
19
19
  * threads are unavailable or the options can’t cross a structured clone.
20
20
  */
21
21
 
package/src/lib/utils.js CHANGED
@@ -2,13 +2,35 @@
2
2
  * General utility functions
3
3
  */
4
4
 
5
+ // Functions and other non-plain objects have no structure to compare (closures with the
6
+ // same source can behave differently), so they are told apart by identity
7
+ /** @type {WeakMap<object, number>} */
8
+ const objectIds = new WeakMap();
9
+ let objectIdLast = 0;
10
+
11
+ /** @param {object} obj */
12
+ function objectId(obj) {
13
+ let id = objectIds.get(obj);
14
+ if (id === undefined) {
15
+ id = ++objectIdLast;
16
+ objectIds.set(obj, id);
17
+ }
18
+ return id;
19
+ }
20
+
5
21
  /**
22
+ * Stringify with sorted keys, for cache keys—values that can behave differently must not
23
+ * stringify alike, so unquoted markers stand in for what JSON cannot express
6
24
  * @param {unknown} obj
7
25
  * @returns {string}
8
26
  */
9
27
  function stableStringify(obj) {
28
+ if (typeof obj === 'function') return 'function#' + objectId(obj);
10
29
  if (obj == null || typeof obj !== 'object') return JSON.stringify(obj);
30
+ if (obj instanceof RegExp) return String(obj);
11
31
  if (Array.isArray(obj)) return '[' + obj.map(stableStringify).join(',') + ']';
32
+ const proto = Object.getPrototypeOf(obj);
33
+ if (proto !== Object.prototype && proto !== null) return 'object#' + objectId(obj);
12
34
  const keys = Object.keys(obj).sort();
13
35
  let out = '{';
14
36
  for (let i = 0; i < keys.length; i++) {