html-minifier-next 6.1.5 → 6.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
@@ -129,7 +129,7 @@ html-minifier-next --preset conservative --remove-empty-attributes input.html
129
129
 
130
130
  Most of the options are disabled by default. Experiment and find what works best for you and your project.
131
131
 
132
- Options can be used in config files (camelCase) or via CLI flags (kebab-case with `--` prefix). Options that default to `true` use `--no-` prefix in CLI to disable them.
132
+ Options can be used in config files (camelCase) or via CLI flags (kebab-case with `--` prefix). Boolean options generally support both `--option-name` to enable and `--no-option-name` to disable, so you can override a preset or config file from the command line. (Exception: Options whose name already starts with `no-`, such as `noNewlinesBeforeTagClose`, only expose the `--no-…` CLI flag.)
133
133
 
134
134
  | Option (config/CLI) | Description | Default |
135
135
  | --- | --- | --- |
@@ -142,7 +142,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
142
142
  | `collapseInlineTagWhitespace`<br>`--collapse-inline-tag-whitespace` | Collapse whitespace more aggressively between inline elements—use with `collapseWhitespace: true` | `false` |
143
143
  | `collapseWhitespace`<br>`--collapse-whitespace` | [Collapse whitespace that contributes to text nodes in a document tree](https://perfectionkills.com/experimenting-with-html-minifier/#collapse_whitespace) | `false` |
144
144
  | `conservativeCollapse`<br>`--conservative-collapse` | Always collapse to one space (never remove it entirely)—use with `collapseWhitespace: true` | `false` |
145
- | `continueOnMinifyError`<br>`--no-continue-on-minify-error` | Continue on minification errors; when `false`, minification errors throw and abort processing | `true` |
145
+ | `continueOnMinifyError`<br>`--continue-on-minify-error`<br>`--no-continue-on-minify-error` | Continue on minification errors; when `false`, minification errors throw and abort processing | `true` |
146
146
  | `continueOnParseError`<br>`--continue-on-parse-error` | [Handle parse errors](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors) instead of aborting | `false` |
147
147
  | `customAttrAssign`<br>`--custom-attr-assign` | Array of regexes that allow to support custom attribute assign expressions (e.g., `<div flex?="{{mode != cover}}"></div>`) | `[]` |
148
148
  | `customAttrCollapse`<br>`--custom-attr-collapse` | Regex that specifies custom attribute to strip newlines from (e.g., `/ng-class/`) | `undefined` |
package/cli.js CHANGED
@@ -34,12 +34,22 @@ import { pathToFileURL } from 'url';
34
34
  import os from 'os';
35
35
  import readline from 'readline';
36
36
  import { createRequire } from 'module';
37
- import { Command } from 'commander';
37
+ import { Command, Option } from 'commander';
38
38
 
39
39
  // Simple case conversion for CLI option names (ASCII-only, no Unicode needed)
40
40
  const paramCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
41
41
  const camelCase = (str) => paramCase(str).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
42
42
 
43
+ // Commander derives its internal option key by applying paramCase then camelCase to the flag name,
44
+ // stripping a leading `no-` first for negated flags (e.g., `--no-foo-bar` → `fooBar`);
45
+ // because option definition keys may differ from the result of that round-trip (e.g.,
46
+ // `minifyURLs` → Commander key `minifyUrls`, `noNewlinesBeforeTagClose` → `newlinesBeforeTagClose`),
47
+ // `commanderOptionKey` uses the same paramCase + camelCase path to compute the key Commander will use
48
+ const commanderOptionKey = (key) => {
49
+ const pc = paramCase(key);
50
+ return pc.startsWith('no-') ? camelCase(pc.slice(3)) : camelCase(pc);
51
+ };
52
+
43
53
  // Lazy-load HMN to reduce CLI cold-start overhead
44
54
  import { getPreset, getPresetNames } from './src/presets.js';
45
55
  import { parseRegExp } from './src/lib/utils.js';
@@ -140,20 +150,31 @@ const typeParsers = {
140
150
  // Configure command-line flags from shared option definitions
141
151
  const mainOptionKeys = Object.keys(optionDefinitions);
142
152
  mainOptionKeys.forEach(function (key) {
143
- const { description, type } = optionDefinitions[key];
153
+ const { description, descriptionAffirmative, type } = optionDefinitions[key];
154
+ const flag = paramCase(key);
144
155
  if (type === 'invertedBoolean') {
145
- program.option('--no-' + paramCase(key), description);
156
+ // The positive form (to re-enable after a preset/config disables it) is hidden from
157
+ // help—the footer note covers the convention; the negative form is the primary use case
158
+ program.addOption(new Option('--' + flag, descriptionAffirmative ?? 'Enable --' + flag).hideHelp());
159
+ program.option('--no-' + flag, description);
146
160
  } else if (type === 'boolean') {
147
- program.option('--' + paramCase(key), description);
161
+ program.option('--' + flag, description);
162
+ // The negation form is hidden from help—the footer note covers the convention;
163
+ // skip options whose flag already starts with `no-` (currently only
164
+ // `noNewlinesBeforeTagClose`), as `--no-no-X` is not usable
165
+ if (!flag.startsWith('no-')) {
166
+ program.addOption(new Option('--no-' + flag, 'Disable --' + flag).hideHelp());
167
+ }
148
168
  } else {
149
- const flag = '--' + paramCase(key) + (type === 'json' ? ' [value]' : ' <value>');
169
+ const cliFlag = '--' + flag + (type === 'json' ? ' [value]' : ' <value>');
150
170
  const parser = type === 'int' ? typeParsers.int(key) : typeParsers[type];
151
- program.option(flag, description, parser);
171
+ program.option(cliFlag, description, parser);
152
172
  }
153
173
  });
154
174
  program.option('-o --output <file>', 'Specify output file (reads from file arguments or STDIN; outputs to STDOUT if not specified)');
155
175
  program.option('-v --verbose', 'Show detailed processing information');
156
176
  program.option('-d --dry', 'Dry run: Process and report statistics without writing output');
177
+ program.addHelpText('after', '\nBoolean options support a `--no-<flag>` form to disable them, overriding a preset or config file (e.g., `--preset=comprehensive --no-collapse-whitespace`).');
157
178
 
158
179
  // Lazy import wrapper for HMN
159
180
  let minifyFnPromise;
@@ -398,9 +419,14 @@ program.helpOption('-h, --help', 'Display help for command');
398
419
 
399
420
  // 3. Apply CLI options (overrides config and preset)
400
421
  mainOptionKeys.forEach(function (key) {
401
- const param = programOptions[camelCase(key)];
402
- if (typeof param !== 'undefined') {
403
- options[key] = param;
422
+ const { type } = optionDefinitions[key];
423
+ const ck = commanderOptionKey(key);
424
+ if (program.getOptionValueSource(ck) === 'cli') {
425
+ const val = programOptions[ck];
426
+ // For boolean options whose param-case name starts with `no-`, Commander treats
427
+ // the flag as a negation and stores the inverted value under the stripped key;
428
+ // invert back so the option definition key gets the intended value
429
+ options[key] = (type === 'boolean' && paramCase(key).startsWith('no-')) ? !val : val;
404
430
  }
405
431
  });
406
432
 
@@ -413,7 +439,7 @@ program.helpOption('-h, --help', 'Display help for command');
413
439
  console.error(`Using preset: ${presetName}`);
414
440
  }
415
441
  const activeOptions = Object.entries(minifierOptions)
416
- .filter(([k]) => program.getOptionValueSource(camelCase(k)) === 'cli')
442
+ .filter(([k]) => program.getOptionValueSource(commanderOptionKey(k)) === 'cli')
417
443
  .map(([k, v]) => (typeof v === 'boolean' ? (v ? k : `no-${k}`) : k));
418
444
  if (activeOptions.length > 0) {
419
445
  console.error('CLI options: ' + activeOptions.join(', '));
@@ -36,6 +36,7 @@ export namespace optionDefinitions {
36
36
  namespace continueOnMinifyError {
37
37
  let description_6: string;
38
38
  export { description_6 as description };
39
+ export let descriptionAffirmative: string;
39
40
  let type_6: string;
40
41
  export { type_6 as type };
41
42
  }
package/package.json CHANGED
@@ -96,5 +96,5 @@
96
96
  },
97
97
  "type": "module",
98
98
  "types": "./dist/types/htmlminifier.d.ts",
99
- "version": "6.1.5"
99
+ "version": "6.2.0"
100
100
  }
@@ -27,6 +27,7 @@ const optionDefinitions = {
27
27
  },
28
28
  continueOnMinifyError: {
29
29
  description: 'Abort on minification errors',
30
+ descriptionAffirmative: 'Continue on minification errors',
30
31
  type: 'invertedBoolean'
31
32
  },
32
33
  continueOnParseError: {