html-minifier-next 5.1.6 → 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.
- package/README.md +6 -3
- package/cli.js +88 -11
- 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
|
|
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,11 +36,12 @@ 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` |
|
|
40
43
|
| `--output <file>`, `-o <file>` | Specify output file (reads from file arguments or STDIN) | File to file: `html-minifier-next input.html -o output.html`<br>Pipe to file: `cat input.html \| html-minifier-next -o output.html`<br>File to STDOUT: `html-minifier-next input.html` |
|
|
41
|
-
| `--file-ext <extensions>`, `-f <extensions>` | Specify file extension(s) to process (comma-separated, overrides config file setting); defaults to `html,htm,
|
|
44
|
+
| `--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
45
|
| `--preset <name>`, `-p <name>` | Use a preset configuration (conservative or comprehensive) | `--preset=conservative` |
|
|
43
46
|
| `--config-file <file>`, `-c <file>` | Use a configuration file | `--config-file=html-minifier.json` |
|
|
44
47
|
| `--verbose`, `-v` | Show detailed processing information (active options, file statistics) | `html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace` |
|
|
@@ -400,7 +403,7 @@ npx html-minifier-next --input-dir=test --preset comprehensive --output-dir exam
|
|
|
400
403
|
**Process specific files and directories:**
|
|
401
404
|
|
|
402
405
|
```shell
|
|
403
|
-
# Process default extensions (html, htm,
|
|
406
|
+
# Process default extensions (html, htm, shtml, shtm)
|
|
404
407
|
html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist
|
|
405
408
|
|
|
406
409
|
# Process only specific extensions
|
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
|
|
|
@@ -47,13 +48,18 @@ import { optionDefinitions } from './src/lib/option-definitions.js';
|
|
|
47
48
|
const require = createRequire(import.meta.url);
|
|
48
49
|
const pkg = require('./package.json');
|
|
49
50
|
|
|
50
|
-
const DEFAULT_FILE_EXTENSIONS = ['html', 'htm', '
|
|
51
|
+
const DEFAULT_FILE_EXTENSIONS = ['html', 'htm', 'shtml', 'shtm'];
|
|
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' : '';
|
|
51
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,10 +243,11 @@ 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');
|
|
243
|
-
program.option('-f --file-ext <extensions>', 'Specify file extension(s) to process (comma-separated); defaults to “html,htm,
|
|
250
|
+
program.option('-f --file-ext <extensions>', 'Specify file extension(s) to process (comma-separated); defaults to “html,htm,shtml,shtm”; use “*” for all files');
|
|
244
251
|
program.option('-p --preset <name>', `Use a preset configuration (${getPresetNames().join(', ')})`);
|
|
245
252
|
program.option('-c --config-file <file>', 'Use config file');
|
|
246
253
|
program.option('--cache-css <size>', 'Set CSS minification cache size (number of entries, default: 500)', parseValidInt('cacheCSS'));
|
|
@@ -266,8 +273,8 @@ program.helpOption('-h, --help', 'Display help for command');
|
|
|
266
273
|
const jsonOptionKeys = ['minifyCss', 'minifyJs', 'minifyUrls'];
|
|
267
274
|
for (const key of jsonOptionKeys) {
|
|
268
275
|
const value = programOptions[key];
|
|
269
|
-
if (typeof value === 'string' && /\.(html?|php|xml|svg|
|
|
270
|
-
// The option consumed a filename
|
|
276
|
+
if (typeof value === 'string' && /\.(html?|shtml?|xhtml?|php|xml|svg|jsx|tsx|vue|ejs|hbs|mustache|twig)$/i.test(value)) {
|
|
277
|
+
// The option consumed a filename—inject 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(`
|
|
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(`
|
|
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 &&
|
|
645
|
-
//
|
|
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(
|
|
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(`
|
|
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