html-minifier-next 5.1.7 → 5.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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/cli.js +85 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,12 +14,14 @@ From npm for use as a command-line app:
14
14
  npm i -g html-minifier-next
15
15
  ```
16
16
 
17
- Directly with npx (no installation required):
17
+ Directly with npx:
18
18
 
19
19
  ```shell
20
20
  npx html-minifier-next --help
21
21
  ```
22
22
 
23
+ (For immediate, zero-config use in the current folder: `npx html-minifier-next --zero`)
24
+
23
25
  From npm for programmatic use:
24
26
 
25
27
  ```shell
@@ -34,6 +36,7 @@ Use `html-minifier-next --help` to check all available options:
34
36
 
35
37
  | Option | Description | Example |
36
38
  | --- | --- | --- |
39
+ | `--zero`, `-z` | Minify all HTML files in the current folder and its subfolders in place (except node_modules), using comprehensive settings (standalone—flag is ignored when combined with other options) | `html-minifier-next --zero` |
37
40
  | `--input-dir <dir>`, `-I <dir>` | Specify an input directory | `--input-dir=src` |
38
41
  | `--ignore-dir <patterns>`, `-X <patterns>` | Exclude directories—relative to input directory—from processing (comma-separated, overrides config file setting) | `--ignore-dir=libs`, `--ignore-dir=libs,vendor,node_modules` |
39
42
  | `--output-dir <dir>`, `-O <dir>` | Specify an output directory | `--output-dir=dist` |
package/cli.js CHANGED
@@ -32,6 +32,7 @@ import fs from 'fs';
32
32
  import path from 'path';
33
33
  import { pathToFileURL } from 'url';
34
34
  import os from 'os';
35
+ import readline from 'readline';
35
36
  import { createRequire } from 'module';
36
37
  import { Command } from 'commander';
37
38
 
@@ -49,11 +50,16 @@ const pkg = require('./package.json');
49
50
 
50
51
  const DEFAULT_FILE_EXTENSIONS = ['html', 'htm', 'shtml', 'shtm'];
51
52
 
53
+ const MARK_ERROR = process.stderr.isTTY ? '\x1b[31m' : '';
54
+ const MARK_SUCCESS = process.stderr.isTTY ? '\x1b[32m' : '';
55
+ const MARK_WARNING = process.stderr.isTTY ? '\x1b[33m' : '';
56
+ const MARK_RESET = process.stderr.isTTY ? '\x1b[0m' : '';
57
+
52
58
  const program = new Command();
53
59
  program.name(pkg.name);
54
60
 
55
61
  function fatal(message) {
56
- console.error(message);
62
+ console.error(`${MARK_ERROR}${message}${MARK_RESET}`);
57
63
  process.exit(1);
58
64
  }
59
65
 
@@ -237,6 +243,7 @@ function normalizeConfig(config) {
237
243
  }
238
244
 
239
245
  let config = {};
246
+ program.option('-z, --zero', 'Minify all HTML files in the current folder and its subfolders in place (except node_modules), using comprehensive settings (standalone—flag is ignored when combined with other options)');
240
247
  program.option('-I --input-dir <dir>', 'Specify an input directory');
241
248
  program.option('-X --ignore-dir <patterns>', 'Exclude directories—relative to input directory—from processing (comma-separated), e.g., “libs” or “libs,vendor,node_modules”');
242
249
  program.option('-O --output-dir <dir>', 'Specify an output directory');
@@ -267,7 +274,7 @@ program.helpOption('-h, --help', 'Display help for command');
267
274
  for (const key of jsonOptionKeys) {
268
275
  const value = programOptions[key];
269
276
  if (typeof value === 'string' && /\.(html?|shtml?|xhtml?|php|xml|svg|jsx|tsx|vue|ejs|hbs|mustache|twig)$/i.test(value)) {
270
- // The option consumed a filename - inject it back
277
+ // The option consumed a filenameinject it back
271
278
  programOptions[key] = true;
272
279
  capturedFiles.push(value);
273
280
  filesProvided = true;
@@ -276,6 +283,76 @@ program.helpOption('-h, --help', 'Display help for command');
276
283
 
277
284
  // Defer reading files—multi-file mode will process per-file later
278
285
 
286
+ // Handle zero config mode (standalone in-place minification of the current folder)
287
+ if (programOptions.zero) {
288
+ const hasOtherArgs = process.argv.slice(2).some(arg => arg !== '--zero' && arg !== '-z');
289
+ if (hasOtherArgs) {
290
+ console.error('Note: `--zero` was ignored—it can only be used on its own, to minify the current folder at comprehensive settings.');
291
+ } else {
292
+ const cwd = process.cwd();
293
+ const commandName = process.env.npm_command === 'exec'
294
+ ? 'npx html-minifier-next'
295
+ : process.argv[1].endsWith('.js')
296
+ ? `${path.basename(process.argv[0])} ${process.argv[1]}`
297
+ : path.basename(process.argv[1]);
298
+
299
+ process.stderr.write(
300
+ `${MARK_WARNING}Zero-config mode minifies all HTML files in the current folder and its subfolders (${cwd}) in place, using comprehensive settings. If you want to compare results and be able to revert, do this under version control.${MARK_RESET}\n` +
301
+ `Equivalent to: ${commandName} --input-dir=. --output-dir=. --ignore-dir=node_modules --preset=comprehensive\n\n` +
302
+ `Do you want to continue? [y/N] `
303
+ );
304
+
305
+ const answer = await new Promise((resolve) => {
306
+ const rl = readline.createInterface({ input: process.stdin, output: null });
307
+ rl.once('line', (line) => {
308
+ resolve(line.trim().toLowerCase());
309
+ rl.close();
310
+ });
311
+ rl.once('close', () => resolve(''));
312
+ });
313
+
314
+ if (answer !== 'y') {
315
+ process.stderr.write(`${MARK_ERROR}In-place minification aborted.${MARK_RESET}\n`);
316
+ process.exit(0);
317
+ }
318
+
319
+ // Apply comprehensive preset for all processing
320
+ programOptions.preset = 'comprehensive';
321
+
322
+ const inputDirResolved = await fs.promises.realpath(cwd).catch(() => cwd);
323
+ const extensions = DEFAULT_FILE_EXTENSIONS;
324
+ const ignorePatterns = ['node_modules'];
325
+
326
+ const showProgress = process.stderr.isTTY;
327
+ let progress = null;
328
+ if (showProgress) {
329
+ progress = { current: 0, total: null };
330
+ }
331
+
332
+ const allFiles = await collectFiles(cwd, extensions, undefined, ignorePatterns, inputDirResolved);
333
+ const concurrency = Math.max(1, Math.min(os.cpus().length || 4, 8));
334
+
335
+ if (progress) {
336
+ progress.total = allFiles.length;
337
+ }
338
+
339
+ await runWithConcurrency(allFiles, concurrency, async (file) => {
340
+ await processFile(file, file, false, false);
341
+ if (progress) {
342
+ progress.current++;
343
+ updateProgress(progress.current, progress.total);
344
+ }
345
+ });
346
+
347
+ if (progress) {
348
+ clearProgress();
349
+ }
350
+ console.error(`${MARK_SUCCESS}Processed ${allFiles.length.toLocaleString()} file${allFiles.length === 1 ? '' : 's'}.${MARK_RESET}`);
351
+
352
+ process.exit(0);
353
+ }
354
+ }
355
+
279
356
  // Load and normalize config if `--config-file` was specified
280
357
  if (programOptions.configFile) {
281
358
  config = await loadConfigFromPath(programOptions.configFile);
@@ -353,7 +430,7 @@ program.helpOption('-h, --help', 'Display help for command');
353
430
 
354
431
  // Show stats if dry run or verbose mode
355
432
  if (isDryRun || isVerbose) {
356
- console.error(` ${path.relative(process.cwd(), inputFile)}: ${stats.originalSize.toLocaleString()} → ${stats.minifiedSize.toLocaleString()} bytes (${stats.sign}${Math.abs(stats.saved).toLocaleString()}, ${stats.percentage}%)`);
433
+ console.error(` ${MARK_SUCCESS}✓${MARK_RESET} ${path.relative(process.cwd(), inputFile)}: ${stats.originalSize.toLocaleString()} → ${stats.minifiedSize.toLocaleString()} bytes (${stats.sign}${Math.abs(stats.saved).toLocaleString()}, ${stats.percentage}%)`);
357
434
  }
358
435
 
359
436
  if (isDryRun) {
@@ -588,7 +665,7 @@ program.helpOption('-h, --help', 'Display help for command');
588
665
  // Show stats if verbose
589
666
  if (programOptions.verbose) {
590
667
  const inputSource = program.args.length > 0 ? program.args.join(', ') : 'STDIN';
591
- console.error(` ${inputSource}: ${stats.originalSize.toLocaleString()} → ${stats.minifiedSize.toLocaleString()} bytes (${stats.sign}${Math.abs(stats.saved).toLocaleString()}, ${stats.percentage}%)`);
668
+ console.error(` ${MARK_SUCCESS}✓${MARK_RESET} ${inputSource}: ${stats.originalSize.toLocaleString()} → ${stats.minifiedSize.toLocaleString()} bytes (${stats.sign}${Math.abs(stats.saved).toLocaleString()}, ${stats.percentage}%)`);
592
669
  }
593
670
 
594
671
  if (programOptions.output) {
@@ -641,8 +718,8 @@ program.helpOption('-h, --help', 'Display help for command');
641
718
  outputReal = path.resolve(outputDir);
642
719
  }
643
720
  let skipRootAbs;
644
- if (inputReal && outputReal && (outputReal === inputReal || outputReal.startsWith(inputReal + path.sep))) {
645
- // Instead of aborting, skip traversing into the output directory
721
+ if (inputReal && outputReal && outputReal !== inputReal && outputReal.startsWith(inputReal + path.sep)) {
722
+ // Skip traversing into the output directory when it is nested inside the input directory
646
723
  skipRootAbs = outputReal;
647
724
  }
648
725
 
@@ -694,7 +771,7 @@ program.helpOption('-h, --help', 'Display help for command');
694
771
  // Show completion message and clear progress indicator
695
772
  if (progress) {
696
773
  clearProgress();
697
- console.error(`Processed ${progress.current.toLocaleString()} file${progress.current === 1 ? '' : 's'}`);
774
+ console.error(`${MARK_SUCCESS}Processed ${progress.current.toLocaleString()} file${progress.current === 1 ? '' : 's'}.${MARK_RESET}`);
698
775
  }
699
776
 
700
777
  if (isVerbose && stats && stats.length > 0) {
@@ -753,7 +830,7 @@ program.helpOption('-h, --help', 'Display help for command');
753
830
 
754
831
  if (programOptions.verbose) {
755
832
  const inputSource = capturedFiles.join(', ');
756
- console.error(` ${inputSource}: ${stats.originalSize.toLocaleString()} → ${stats.minifiedSize.toLocaleString()} bytes (${stats.sign}${Math.abs(stats.saved).toLocaleString()}, ${stats.percentage}%)`);
833
+ console.error(` ${MARK_SUCCESS}✓${MARK_RESET} ${inputSource}: ${stats.originalSize.toLocaleString()} → ${stats.minifiedSize.toLocaleString()} bytes (${stats.sign}${Math.abs(stats.saved).toLocaleString()}, ${stats.percentage}%)`);
757
834
  }
758
835
 
759
836
  if (programOptions.output) {
package/package.json CHANGED
@@ -95,5 +95,5 @@
95
95
  },
96
96
  "type": "module",
97
97
  "types": "./dist/types/htmlminifier.d.ts",
98
- "version": "5.1.7"
98
+ "version": "5.2.0"
99
99
  }