html-minifier-next 4.3.1 → 4.5.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 +32 -7
- package/cli.js +29 -5
- package/dist/htmlminifier.cjs +87 -2
- package/dist/htmlminifier.esm.bundle.js +85 -3
- package/dist/types/htmlminifier.d.ts +17 -0
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/presets.d.ts +63 -0
- package/dist/types/presets.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/htmlminifier.js +14 -2
- package/src/htmlparser.js +5 -0
- package/src/presets.js +67 -0
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ Use `html-minifier-next --help` to check all available options:
|
|
|
33
33
|
| `--file-ext <extensions>` | Specify file extension(s) to process (overrides config file setting) | `--file-ext=html`, `--file-ext=html,htm,php`, `--file-ext="html, htm, php"` |
|
|
34
34
|
| `-o <file>`, `--output <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` |
|
|
35
35
|
| `-c <file>`, `--config-file <file>` | Use a configuration file | `--config-file=html-minifier.json` |
|
|
36
|
+
| `--preset <name>` | Use a preset configuration (conservative, comprehensive) | `--preset=conservative` |
|
|
36
37
|
| `-v`, `--verbose` | Show detailed processing information (active options, file statistics) | `html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace` |
|
|
37
38
|
| `-d`, `--dry` | Dry run: Process and report statistics without writing output | `html-minifier-next input.html --dry --collapse-whitespace` |
|
|
38
39
|
| `-V`, `--version` | Output the version number | `html-minifier-next --version` |
|
|
@@ -71,6 +72,29 @@ html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir
|
|
|
71
72
|
html-minifier-next --config-file=html-minifier.json --file-ext=xml --input-dir=src --output-dir=dist
|
|
72
73
|
```
|
|
73
74
|
|
|
75
|
+
### Presets
|
|
76
|
+
|
|
77
|
+
HTML Minifier Next provides presets for common use cases. Presets are pre-configured option sets that can be used as a starting point:
|
|
78
|
+
|
|
79
|
+
* `conservative`: Safe minification suitable for most projects. Includes whitespace collapsing, comment removal, and doctype normalization.
|
|
80
|
+
* `comprehensive`: Aggressive minification for maximum file size reduction. Includes all conservative options plus attribute quote removal, optional tag removal, and more.
|
|
81
|
+
|
|
82
|
+
**Using presets:**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Via CLI flag
|
|
86
|
+
html-minifier-next --preset conservative input.html
|
|
87
|
+
|
|
88
|
+
# Via config file
|
|
89
|
+
html-minifier-next --config-file=config.json input.html
|
|
90
|
+
# where config.json contains: { "preset": "conservative" }
|
|
91
|
+
|
|
92
|
+
# Override preset options
|
|
93
|
+
html-minifier-next --preset conservative --remove-empty-attributes input.html
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Priority order:** Presets are applied first, then config file options, then CLI flags. This allows you to start with a preset and customize as needed.
|
|
97
|
+
|
|
74
98
|
### Node.js
|
|
75
99
|
|
|
76
100
|
ESM with Node.js ≥16.14:
|
|
@@ -134,11 +158,12 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
134
158
|
| `minifyJS`<br>`--minify-js` | Minify JavaScript in `script` elements and event attributes (uses [Terser](https://github.com/terser/terser)) | `false` (could be `true`, `Object`, `Function(text, inline)`) |
|
|
135
159
|
| `minifyURLs`<br>`--minify-urls` | Minify URLs in various attributes (uses [relateurl](https://github.com/stevenvachon/relateurl)) | `false` (could be `String`, `Object`, `Function(text)`, `async Function(text)`) |
|
|
136
160
|
| `noNewlinesBeforeTagClose`<br>`--no-newlines-before-tag-close` | Never add a newline before a tag that closes an element | `false` |
|
|
161
|
+
| `partialMarkup`<br>`--partial-markup` | Treat input as a partial HTML fragment, preserving stray end tags (closing tags without opening tags) and preventing auto-closing of unclosed tags at end of input | `false` |
|
|
137
162
|
| `preserveLineBreaks`<br>`--preserve-line-breaks` | Always collapse to 1 line break (never remove it entirely) when whitespace between tags includes a line break—use with `collapseWhitespace: true` | `false` |
|
|
138
163
|
| `preventAttributesEscaping`<br>`--prevent-attributes-escaping` | Prevents the escaping of the values of attributes | `false` |
|
|
139
164
|
| `processConditionalComments`<br>`--process-conditional-comments` | Process contents of conditional comments through minifier | `false` |
|
|
140
165
|
| `processScripts`<br>`--process-scripts` | Array of strings corresponding to types of `script` elements to process through minifier (e.g., `text/ng-template`, `text/x-handlebars-template`, etc.) | `[]` |
|
|
141
|
-
| `quoteCharacter`<br>`--quote-character` | Type of quote to use for attribute values (`'` or `"`) | |
|
|
166
|
+
| `quoteCharacter`<br>`--quote-character` | Type of quote to use for attribute values (`'` or `"`) | Auto-detected (uses the quote requiring less escaping; defaults to `"` when equal) |
|
|
142
167
|
| `removeAttributeQuotes`<br>`--remove-attribute-quotes` | [Remove quotes around attributes when possible](https://perfectionkills.com/experimenting-with-html-minifier#remove_attribute_quotes) | `false` |
|
|
143
168
|
| `removeComments`<br>`--remove-comments` | [Strip HTML comments](https://perfectionkills.com/experimenting-with-html-minifier#remove_comments) | `false` |
|
|
144
169
|
| `removeEmptyAttributes`<br>`--remove-empty-attributes` | [Remove all attributes with whitespace-only values](https://perfectionkills.com/experimenting-with-html-minifier#remove_empty_or_blank_attributes) | `false` (could be `true`, `Function(attrName, tag)`) |
|
|
@@ -155,7 +180,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
155
180
|
|
|
156
181
|
### Sorting attributes and style classes
|
|
157
182
|
|
|
158
|
-
Minifier options like `sortAttributes` and `sortClassName` won
|
|
183
|
+
Minifier options like `sortAttributes` and `sortClassName` won’t impact the plain‑text size of the output. However, using these options for more consistent ordering improves the compression ratio for gzip and Brotli used over HTTP.
|
|
159
184
|
|
|
160
185
|
### CSS minification with Lightning CSS
|
|
161
186
|
|
|
@@ -288,21 +313,21 @@ If you have chunks of markup you would like preserved, you can wrap them with `<
|
|
|
288
313
|
|
|
289
314
|
### Minifying JSON-LD
|
|
290
315
|
|
|
291
|
-
You can minify `script` elements with JSON-LD by setting `{ processScripts: ['application/ld+json'] }`. Note that this minification is rudimentary; it’s mainly useful for removing newlines and excessive whitespace.
|
|
316
|
+
You can minify `script` elements with JSON-LD by setting `{ processScripts: ['application/ld+json'] }`. Note that this minification is rudimentary; it’s mainly useful for removing newlines and excessive whitespace.
|
|
292
317
|
|
|
293
318
|
### Preserving SVG elements
|
|
294
319
|
|
|
295
320
|
SVG elements are automatically recognized, and when they are minified, both case-sensitivity and closing-slashes are preserved, regardless of the minification settings used for the rest of the file.
|
|
296
321
|
|
|
297
|
-
### Working with invalid markup
|
|
322
|
+
### Working with invalid or partial markup
|
|
298
323
|
|
|
299
|
-
HTML Minifier Next
|
|
324
|
+
By default, HTML Minifier Next parses markup into a complete tree structure, then modifies it (removing anything that was specified for removal, ignoring anything that was specified to be ignored, etc.), then creates markup from that tree and returns it.
|
|
300
325
|
|
|
301
326
|
_Input markup (e.g., `<p id="">foo`) → Internal representation of markup in a form of tree (e.g., `{ tag: "p", attr: "id", children: ["foo"] }`) → Transformation of internal representation (e.g., removal of `id` attribute) → Output of resulting markup (e.g., `<p>foo</p>`)_
|
|
302
327
|
|
|
303
|
-
|
|
328
|
+
For partial HTML fragments (such as template includes, SSI fragments, or closing tags without opening tags), use the `partialMarkup: true` option. This preserves stray end tags (closing tags without corresponding opening tags) and prevents auto-closing of unclosed tags at the end of input. Note that normal HTML auto-closing rules still apply during parsing—for example, a closing parent tag will still auto-close its unclosed child elements.
|
|
304
329
|
|
|
305
|
-
To validate HTML markup, use [the W3C validator](https://validator.w3.org/) or one of [several validator packages](https://meiert.com/blog/html-validator-packages/).
|
|
330
|
+
To validate complete HTML markup, use [the W3C validator](https://validator.w3.org/) or one of [several validator packages](https://meiert.com/blog/html-validator-packages/).
|
|
306
331
|
|
|
307
332
|
## Security
|
|
308
333
|
|
package/cli.js
CHANGED
|
@@ -32,6 +32,7 @@ import { createRequire } from 'module';
|
|
|
32
32
|
import { camelCase, paramCase } from 'change-case';
|
|
33
33
|
import { Command } from 'commander';
|
|
34
34
|
import { minify } from './src/htmlminifier.js';
|
|
35
|
+
import { getPreset, getPresetNames } from './src/presets.js';
|
|
35
36
|
|
|
36
37
|
const require = createRequire(import.meta.url);
|
|
37
38
|
const pkg = require('./package.json');
|
|
@@ -140,7 +141,8 @@ const mainOptions = {
|
|
|
140
141
|
minifyJS: ['Minify JavaScript in “script” elements and event attributes (uses Terser)', parseJSON],
|
|
141
142
|
minifyURLs: ['Minify URLs in various attributes (uses relateurl)', parseJSON],
|
|
142
143
|
noNewlinesBeforeTagClose: 'Never add a newline before a tag that closes an element',
|
|
143
|
-
|
|
144
|
+
partialMarkup: 'Treat input as a partial HTML fragment, preserving stray end tags and unclosed tags',
|
|
145
|
+
preserveLineBreaks: 'Always collapse to 1 line break (never remove it entirely) when whitespace between tags includes a line break—use with "collapseWhitespace=true"',
|
|
144
146
|
preventAttributesEscaping: 'Prevents the escaping of the values of attributes',
|
|
145
147
|
processConditionalComments: 'Process contents of conditional comments through minifier',
|
|
146
148
|
processScripts: ['Array of strings corresponding to types of “script” elements to process through minifier (e.g., “text/ng-template”, “text/x-handlebars-template”, etc.)', parseJSONArray],
|
|
@@ -249,9 +251,10 @@ function normalizeConfig(config) {
|
|
|
249
251
|
|
|
250
252
|
let config = {};
|
|
251
253
|
program.option('-c --config-file <file>', 'Use config file');
|
|
254
|
+
program.option('--preset <name>', `Use a preset configuration (${getPresetNames().join(', ')})`);
|
|
252
255
|
program.option('--input-dir <dir>', 'Specify an input directory');
|
|
253
256
|
program.option('--output-dir <dir>', 'Specify an output directory');
|
|
254
|
-
program.option('--file-ext <extensions>', 'Specify file extension(s) to process (comma-separated), e.g.,
|
|
257
|
+
program.option('--file-ext <extensions>', 'Specify file extension(s) to process (comma-separated), e.g., "html" or "html,htm,php"');
|
|
255
258
|
|
|
256
259
|
(async () => {
|
|
257
260
|
let content;
|
|
@@ -270,19 +273,40 @@ program.option('--file-ext <extensions>', 'Specify file extension(s) to process
|
|
|
270
273
|
function createOptions() {
|
|
271
274
|
const options = {};
|
|
272
275
|
|
|
276
|
+
// Priority order: preset < config < CLI
|
|
277
|
+
// 1. Apply preset if specified (CLI `--preset` takes priority over config.preset)
|
|
278
|
+
const presetName = programOptions.preset || config.preset;
|
|
279
|
+
if (presetName) {
|
|
280
|
+
const preset = getPreset(presetName);
|
|
281
|
+
if (!preset) {
|
|
282
|
+
fatal(`Unknown preset "${presetName}". Available presets: ${getPresetNames().join(', ')}`);
|
|
283
|
+
}
|
|
284
|
+
Object.assign(options, preset);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 2. Apply config file options (overrides preset)
|
|
273
288
|
mainOptionKeys.forEach(function (key) {
|
|
274
|
-
|
|
289
|
+
if (key in config) {
|
|
290
|
+
options[key] = config[key];
|
|
291
|
+
}
|
|
292
|
+
});
|
|
275
293
|
|
|
294
|
+
// 3. Apply CLI options (overrides config and preset)
|
|
295
|
+
mainOptionKeys.forEach(function (key) {
|
|
296
|
+
const param = programOptions[key === 'minifyURLs' ? 'minifyUrls' : camelCase(key)];
|
|
276
297
|
if (typeof param !== 'undefined') {
|
|
277
298
|
options[key] = param;
|
|
278
|
-
} else if (key in config) {
|
|
279
|
-
options[key] = config[key];
|
|
280
299
|
}
|
|
281
300
|
});
|
|
301
|
+
|
|
282
302
|
return options;
|
|
283
303
|
}
|
|
284
304
|
|
|
285
305
|
function getActiveOptionsDisplay(minifierOptions) {
|
|
306
|
+
const presetName = programOptions.preset || config.preset;
|
|
307
|
+
if (presetName) {
|
|
308
|
+
console.error(`Using preset: ${presetName}`);
|
|
309
|
+
}
|
|
286
310
|
const activeOptions = Object.entries(minifierOptions)
|
|
287
311
|
.filter(([k]) => program.getOptionValueSource(k === 'minifyURLs' ? 'minifyUrls' : camelCase(k)) === 'cli')
|
|
288
312
|
.map(([k, v]) => (typeof v === 'boolean' ? (v ? k : `no-${k}`) : k));
|
package/dist/htmlminifier.cjs
CHANGED
|
@@ -453,6 +453,11 @@ class HTMLParser {
|
|
|
453
453
|
// Remove the open elements from the stack
|
|
454
454
|
stack.length = pos;
|
|
455
455
|
lastTag = pos && stack[pos - 1].tag;
|
|
456
|
+
} else if (handler.partialMarkup && tagName) {
|
|
457
|
+
// In partial markup mode, preserve stray end tags
|
|
458
|
+
if (handler.end) {
|
|
459
|
+
handler.end(tagName, [], false);
|
|
460
|
+
}
|
|
456
461
|
} else if (tagName.toLowerCase() === 'br') {
|
|
457
462
|
if (handler.start) {
|
|
458
463
|
await handler.start(tagName, [], true, '');
|
|
@@ -536,6 +541,74 @@ class TokenChain {
|
|
|
536
541
|
}
|
|
537
542
|
}
|
|
538
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Preset configurations for HTML Minifier Next
|
|
546
|
+
*
|
|
547
|
+
* Presets provide curated option sets for common use cases:
|
|
548
|
+
* - conservative: Safe minification suitable for most projects
|
|
549
|
+
* - comprehensive: Aggressive minification for maximum file size reduction
|
|
550
|
+
*/
|
|
551
|
+
|
|
552
|
+
const presets = {
|
|
553
|
+
conservative: {
|
|
554
|
+
collapseBooleanAttributes: true,
|
|
555
|
+
collapseWhitespace: true,
|
|
556
|
+
conservativeCollapse: true,
|
|
557
|
+
continueOnParseError: true,
|
|
558
|
+
decodeEntities: true,
|
|
559
|
+
minifyURLs: true,
|
|
560
|
+
noNewlinesBeforeTagClose: true,
|
|
561
|
+
preserveLineBreaks: true,
|
|
562
|
+
removeComments: true,
|
|
563
|
+
removeScriptTypeAttributes: true,
|
|
564
|
+
removeStyleLinkTypeAttributes: true,
|
|
565
|
+
useShortDoctype: true
|
|
566
|
+
},
|
|
567
|
+
comprehensive: {
|
|
568
|
+
caseSensitive: true,
|
|
569
|
+
collapseBooleanAttributes: true,
|
|
570
|
+
collapseInlineTagWhitespace: true,
|
|
571
|
+
collapseWhitespace: true,
|
|
572
|
+
conservativeCollapse: true,
|
|
573
|
+
continueOnParseError: true,
|
|
574
|
+
decodeEntities: true,
|
|
575
|
+
minifyCSS: true,
|
|
576
|
+
minifyJS: true,
|
|
577
|
+
minifyURLs: true,
|
|
578
|
+
noNewlinesBeforeTagClose: true,
|
|
579
|
+
processConditionalComments: true,
|
|
580
|
+
removeAttributeQuotes: true,
|
|
581
|
+
removeComments: true,
|
|
582
|
+
removeEmptyAttributes: true,
|
|
583
|
+
removeOptionalTags: true,
|
|
584
|
+
removeRedundantAttributes: true,
|
|
585
|
+
removeScriptTypeAttributes: true,
|
|
586
|
+
removeStyleLinkTypeAttributes: true,
|
|
587
|
+
sortAttributes: true,
|
|
588
|
+
sortClassName: true,
|
|
589
|
+
useShortDoctype: true
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Get preset configuration by name
|
|
595
|
+
* @param {string} name - Preset name ('conservative' or 'comprehensive')
|
|
596
|
+
* @returns {object|null} Preset options object or null if not found
|
|
597
|
+
*/
|
|
598
|
+
function getPreset(name) {
|
|
599
|
+
if (!name) return null;
|
|
600
|
+
const normalizedName = name.toLowerCase();
|
|
601
|
+
return presets[normalizedName] || null;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Get list of available preset names
|
|
606
|
+
* @returns {string[]} Array of preset names
|
|
607
|
+
*/
|
|
608
|
+
function getPresetNames() {
|
|
609
|
+
return Object.keys(presets);
|
|
610
|
+
}
|
|
611
|
+
|
|
539
612
|
const trimWhitespace = str => str && str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
|
|
540
613
|
|
|
541
614
|
function collapseWhitespaceAll(str) {
|
|
@@ -1642,7 +1715,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1642
1715
|
}
|
|
1643
1716
|
|
|
1644
1717
|
const parser = new HTMLParser(value, {
|
|
1645
|
-
partialMarkup,
|
|
1718
|
+
partialMarkup: partialMarkup ?? options.partialMarkup,
|
|
1646
1719
|
continueOnParseError: options.continueOnParseError,
|
|
1647
1720
|
customAttrAssign: options.customAttrAssign,
|
|
1648
1721
|
customAttrSurround: options.customAttrSurround,
|
|
@@ -2014,7 +2087,7 @@ const minify = async function (value, options) {
|
|
|
2014
2087
|
return result;
|
|
2015
2088
|
};
|
|
2016
2089
|
|
|
2017
|
-
var htmlminifier = { minify };
|
|
2090
|
+
var htmlminifier = { minify, presets, getPreset, getPresetNames };
|
|
2018
2091
|
|
|
2019
2092
|
/**
|
|
2020
2093
|
* @typedef {Object} HTMLAttribute
|
|
@@ -2226,6 +2299,15 @@ var htmlminifier = { minify };
|
|
|
2226
2299
|
*
|
|
2227
2300
|
* Default: `false`
|
|
2228
2301
|
*
|
|
2302
|
+
* @prop {boolean} [partialMarkup]
|
|
2303
|
+
* When true, treat input as a partial HTML fragment rather than a complete
|
|
2304
|
+
* document. This preserves stray end tags (closing tags without corresponding
|
|
2305
|
+
* opening tags) and prevents auto-closing of unclosed tags at the end of input.
|
|
2306
|
+
* Useful for minifying template fragments, SSI includes, or other partial HTML
|
|
2307
|
+
* that will be combined with other fragments.
|
|
2308
|
+
*
|
|
2309
|
+
* Default: `false`
|
|
2310
|
+
*
|
|
2229
2311
|
* @prop {boolean} [preserveLineBreaks]
|
|
2230
2312
|
* Preserve a single line break at the start/end of text nodes when
|
|
2231
2313
|
* collapsing/trimming whitespace.
|
|
@@ -2355,4 +2437,7 @@ var htmlminifier = { minify };
|
|
|
2355
2437
|
*/
|
|
2356
2438
|
|
|
2357
2439
|
exports.default = htmlminifier;
|
|
2440
|
+
exports.getPreset = getPreset;
|
|
2441
|
+
exports.getPresetNames = getPresetNames;
|
|
2358
2442
|
exports.minify = minify;
|
|
2443
|
+
exports.presets = presets;
|
|
@@ -39506,6 +39506,11 @@ class HTMLParser {
|
|
|
39506
39506
|
// Remove the open elements from the stack
|
|
39507
39507
|
stack.length = pos;
|
|
39508
39508
|
lastTag = pos && stack[pos - 1].tag;
|
|
39509
|
+
} else if (handler.partialMarkup && tagName) {
|
|
39510
|
+
// In partial markup mode, preserve stray end tags
|
|
39511
|
+
if (handler.end) {
|
|
39512
|
+
handler.end(tagName, [], false);
|
|
39513
|
+
}
|
|
39509
39514
|
} else if (tagName.toLowerCase() === 'br') {
|
|
39510
39515
|
if (handler.start) {
|
|
39511
39516
|
await handler.start(tagName, [], true, '');
|
|
@@ -39589,6 +39594,74 @@ class TokenChain {
|
|
|
39589
39594
|
}
|
|
39590
39595
|
}
|
|
39591
39596
|
|
|
39597
|
+
/**
|
|
39598
|
+
* Preset configurations for HTML Minifier Next
|
|
39599
|
+
*
|
|
39600
|
+
* Presets provide curated option sets for common use cases:
|
|
39601
|
+
* - conservative: Safe minification suitable for most projects
|
|
39602
|
+
* - comprehensive: Aggressive minification for maximum file size reduction
|
|
39603
|
+
*/
|
|
39604
|
+
|
|
39605
|
+
const presets = {
|
|
39606
|
+
conservative: {
|
|
39607
|
+
collapseBooleanAttributes: true,
|
|
39608
|
+
collapseWhitespace: true,
|
|
39609
|
+
conservativeCollapse: true,
|
|
39610
|
+
continueOnParseError: true,
|
|
39611
|
+
decodeEntities: true,
|
|
39612
|
+
minifyURLs: true,
|
|
39613
|
+
noNewlinesBeforeTagClose: true,
|
|
39614
|
+
preserveLineBreaks: true,
|
|
39615
|
+
removeComments: true,
|
|
39616
|
+
removeScriptTypeAttributes: true,
|
|
39617
|
+
removeStyleLinkTypeAttributes: true,
|
|
39618
|
+
useShortDoctype: true
|
|
39619
|
+
},
|
|
39620
|
+
comprehensive: {
|
|
39621
|
+
caseSensitive: true,
|
|
39622
|
+
collapseBooleanAttributes: true,
|
|
39623
|
+
collapseInlineTagWhitespace: true,
|
|
39624
|
+
collapseWhitespace: true,
|
|
39625
|
+
conservativeCollapse: true,
|
|
39626
|
+
continueOnParseError: true,
|
|
39627
|
+
decodeEntities: true,
|
|
39628
|
+
minifyCSS: true,
|
|
39629
|
+
minifyJS: true,
|
|
39630
|
+
minifyURLs: true,
|
|
39631
|
+
noNewlinesBeforeTagClose: true,
|
|
39632
|
+
processConditionalComments: true,
|
|
39633
|
+
removeAttributeQuotes: true,
|
|
39634
|
+
removeComments: true,
|
|
39635
|
+
removeEmptyAttributes: true,
|
|
39636
|
+
removeOptionalTags: true,
|
|
39637
|
+
removeRedundantAttributes: true,
|
|
39638
|
+
removeScriptTypeAttributes: true,
|
|
39639
|
+
removeStyleLinkTypeAttributes: true,
|
|
39640
|
+
sortAttributes: true,
|
|
39641
|
+
sortClassName: true,
|
|
39642
|
+
useShortDoctype: true
|
|
39643
|
+
}
|
|
39644
|
+
};
|
|
39645
|
+
|
|
39646
|
+
/**
|
|
39647
|
+
* Get preset configuration by name
|
|
39648
|
+
* @param {string} name - Preset name ('conservative' or 'comprehensive')
|
|
39649
|
+
* @returns {object|null} Preset options object or null if not found
|
|
39650
|
+
*/
|
|
39651
|
+
function getPreset(name) {
|
|
39652
|
+
if (!name) return null;
|
|
39653
|
+
const normalizedName = name.toLowerCase();
|
|
39654
|
+
return presets[normalizedName] || null;
|
|
39655
|
+
}
|
|
39656
|
+
|
|
39657
|
+
/**
|
|
39658
|
+
* Get list of available preset names
|
|
39659
|
+
* @returns {string[]} Array of preset names
|
|
39660
|
+
*/
|
|
39661
|
+
function getPresetNames() {
|
|
39662
|
+
return Object.keys(presets);
|
|
39663
|
+
}
|
|
39664
|
+
|
|
39592
39665
|
const trimWhitespace = str => str && str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
|
|
39593
39666
|
|
|
39594
39667
|
function collapseWhitespaceAll(str) {
|
|
@@ -40695,7 +40768,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
40695
40768
|
}
|
|
40696
40769
|
|
|
40697
40770
|
const parser = new HTMLParser(value, {
|
|
40698
|
-
partialMarkup,
|
|
40771
|
+
partialMarkup: partialMarkup ?? options.partialMarkup,
|
|
40699
40772
|
continueOnParseError: options.continueOnParseError,
|
|
40700
40773
|
customAttrAssign: options.customAttrAssign,
|
|
40701
40774
|
customAttrSurround: options.customAttrSurround,
|
|
@@ -41067,7 +41140,7 @@ const minify = async function (value, options) {
|
|
|
41067
41140
|
return result;
|
|
41068
41141
|
};
|
|
41069
41142
|
|
|
41070
|
-
var htmlminifier = { minify };
|
|
41143
|
+
var htmlminifier = { minify, presets, getPreset, getPresetNames };
|
|
41071
41144
|
|
|
41072
41145
|
/**
|
|
41073
41146
|
* @typedef {Object} HTMLAttribute
|
|
@@ -41279,6 +41352,15 @@ var htmlminifier = { minify };
|
|
|
41279
41352
|
*
|
|
41280
41353
|
* Default: `false`
|
|
41281
41354
|
*
|
|
41355
|
+
* @prop {boolean} [partialMarkup]
|
|
41356
|
+
* When true, treat input as a partial HTML fragment rather than a complete
|
|
41357
|
+
* document. This preserves stray end tags (closing tags without corresponding
|
|
41358
|
+
* opening tags) and prevents auto-closing of unclosed tags at the end of input.
|
|
41359
|
+
* Useful for minifying template fragments, SSI includes, or other partial HTML
|
|
41360
|
+
* that will be combined with other fragments.
|
|
41361
|
+
*
|
|
41362
|
+
* Default: `false`
|
|
41363
|
+
*
|
|
41282
41364
|
* @prop {boolean} [preserveLineBreaks]
|
|
41283
41365
|
* Preserve a single line break at the start/end of text nodes when
|
|
41284
41366
|
* collapsing/trimming whitespace.
|
|
@@ -41407,4 +41489,4 @@ var htmlminifier = { minify };
|
|
|
41407
41489
|
* Default: `false`
|
|
41408
41490
|
*/
|
|
41409
41491
|
|
|
41410
|
-
export { htmlminifier as default, minify };
|
|
41492
|
+
export { htmlminifier as default, getPreset, getPresetNames, minify, presets };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export function minify(value: string, options?: MinifierOptions): Promise<string>;
|
|
2
2
|
declare namespace _default {
|
|
3
3
|
export { minify };
|
|
4
|
+
export { presets };
|
|
5
|
+
export { getPreset };
|
|
6
|
+
export { getPresetNames };
|
|
4
7
|
}
|
|
5
8
|
export default _default;
|
|
6
9
|
/**
|
|
@@ -241,6 +244,16 @@ export type MinifierOptions = {
|
|
|
241
244
|
* Default: `false`
|
|
242
245
|
*/
|
|
243
246
|
noNewlinesBeforeTagClose?: boolean;
|
|
247
|
+
/**
|
|
248
|
+
* When true, treat input as a partial HTML fragment rather than a complete
|
|
249
|
+
* document. This preserves stray end tags (closing tags without corresponding
|
|
250
|
+
* opening tags) and prevents auto-closing of unclosed tags at the end of input.
|
|
251
|
+
* Useful for minifying template fragments, SSI includes, or other partial HTML
|
|
252
|
+
* that will be combined with other fragments.
|
|
253
|
+
*
|
|
254
|
+
* Default: `false`
|
|
255
|
+
*/
|
|
256
|
+
partialMarkup?: boolean;
|
|
244
257
|
/**
|
|
245
258
|
* Preserve a single line break at the start/end of text nodes when
|
|
246
259
|
* collapsing/trimming whitespace.
|
|
@@ -387,4 +400,8 @@ export type MinifierOptions = {
|
|
|
387
400
|
*/
|
|
388
401
|
useShortDoctype?: boolean;
|
|
389
402
|
};
|
|
403
|
+
import { presets } from './presets.js';
|
|
404
|
+
import { getPreset } from './presets.js';
|
|
405
|
+
import { getPresetNames } from './presets.js';
|
|
406
|
+
export { presets, getPreset, getPresetNames };
|
|
390
407
|
//# sourceMappingURL=htmlminifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAu8CO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAQ3B;;;;;;;;;;;;UAUS,MAAM;YACN,MAAM;YACN,MAAM;mBACN,MAAM;iBACN,MAAM;kBACN,MAAM;;;;;;;;;;;;;4BAQN,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;wBAMjG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;oBAMhH,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;;kCAOP,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;2BAOP,OAAO;;;;;;;;4BAOP,OAAO;;;;;;;2BAOP,OAAO;;;;;;;;uBAMP,MAAM,EAAE;;;;;;yBAOR,MAAM;;;;;;yBAKN,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;;;;;;;4BAKlB,MAAM,EAAE;;;;;;;oCAMR,MAAM;;;;;;;qBAMN,OAAO;;;;;;;YAMP,OAAO;;;;;;;;2BAMP,MAAM,EAAE;;;;;;;;;4BAOR,MAAM,EAAE;;;;;;;+BAQR,OAAO;;;;;;;2BAMP,SAAS,CAAC,MAAM,CAAC;;;;;;uBAMjB,OAAO;;;;;;;;UAKP,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;;;;;;;;qBAO1B,MAAM;;;;;;;oBAON,MAAM;;;;;;;;;;gBAMN,OAAO,GAAG,OAAO,CAAC,OAAO,cAAc,EAAE,gBAAgB,CAAC,OAAO,cAAc,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;eAS9J,OAAO,GAAG,OAAO,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;iBASzG,OAAO,GAAG,MAAM,GAAG,OAAO,WAAW,EAAE,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;WAS7F,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;;;;;+BAOxB,OAAO;;;;;;;;;;oBAMP,OAAO;;;;;;;;yBASP,OAAO;;;;;;;gCAOP,OAAO;;;;;;;;iCAMP,OAAO;;;;;;;;;;qBAOP,MAAM,EAAE;;;;;;;qBASR,IAAI,GAAG,GAAG;;;;;;;4BAMV,OAAO;;;;;;;;qBAMP,OAAO;;;;;;;;;4BAOP,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;0BAQtD,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;iCAOP,OAAO;;;;;;;oCAMP,OAAO;;;;;;;;;;0BAMP,OAAO;;;;;;;;;qBASP,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;;;;;;;;;oBAQzD,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;;;;;;;;0BAQrC,OAAO;;;;;;;sBAOP,OAAO;;wBAhyDkC,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAgDA,4BAAoE;AAyDpE;IACE,qCAGC;IAFC,UAAgB;IAChB,aAAsB;IAGxB,
|
|
1
|
+
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAgDA,4BAAoE;AAyDpE;IACE,qCAGC;IAFC,UAAgB;IAChB,aAAsB;IAGxB,uBAgWC;CACF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get preset configuration by name
|
|
3
|
+
* @param {string} name - Preset name ('conservative' or 'comprehensive')
|
|
4
|
+
* @returns {object|null} Preset options object or null if not found
|
|
5
|
+
*/
|
|
6
|
+
export function getPreset(name: string): object | null;
|
|
7
|
+
/**
|
|
8
|
+
* Get list of available preset names
|
|
9
|
+
* @returns {string[]} Array of preset names
|
|
10
|
+
*/
|
|
11
|
+
export function getPresetNames(): string[];
|
|
12
|
+
export namespace presets {
|
|
13
|
+
namespace conservative {
|
|
14
|
+
let collapseBooleanAttributes: boolean;
|
|
15
|
+
let collapseWhitespace: boolean;
|
|
16
|
+
let conservativeCollapse: boolean;
|
|
17
|
+
let continueOnParseError: boolean;
|
|
18
|
+
let decodeEntities: boolean;
|
|
19
|
+
let minifyURLs: boolean;
|
|
20
|
+
let noNewlinesBeforeTagClose: boolean;
|
|
21
|
+
let preserveLineBreaks: boolean;
|
|
22
|
+
let removeComments: boolean;
|
|
23
|
+
let removeScriptTypeAttributes: boolean;
|
|
24
|
+
let removeStyleLinkTypeAttributes: boolean;
|
|
25
|
+
let useShortDoctype: boolean;
|
|
26
|
+
}
|
|
27
|
+
namespace comprehensive {
|
|
28
|
+
export let caseSensitive: boolean;
|
|
29
|
+
let collapseBooleanAttributes_1: boolean;
|
|
30
|
+
export { collapseBooleanAttributes_1 as collapseBooleanAttributes };
|
|
31
|
+
export let collapseInlineTagWhitespace: boolean;
|
|
32
|
+
let collapseWhitespace_1: boolean;
|
|
33
|
+
export { collapseWhitespace_1 as collapseWhitespace };
|
|
34
|
+
let conservativeCollapse_1: boolean;
|
|
35
|
+
export { conservativeCollapse_1 as conservativeCollapse };
|
|
36
|
+
let continueOnParseError_1: boolean;
|
|
37
|
+
export { continueOnParseError_1 as continueOnParseError };
|
|
38
|
+
let decodeEntities_1: boolean;
|
|
39
|
+
export { decodeEntities_1 as decodeEntities };
|
|
40
|
+
export let minifyCSS: boolean;
|
|
41
|
+
export let minifyJS: boolean;
|
|
42
|
+
let minifyURLs_1: boolean;
|
|
43
|
+
export { minifyURLs_1 as minifyURLs };
|
|
44
|
+
let noNewlinesBeforeTagClose_1: boolean;
|
|
45
|
+
export { noNewlinesBeforeTagClose_1 as noNewlinesBeforeTagClose };
|
|
46
|
+
export let processConditionalComments: boolean;
|
|
47
|
+
export let removeAttributeQuotes: boolean;
|
|
48
|
+
let removeComments_1: boolean;
|
|
49
|
+
export { removeComments_1 as removeComments };
|
|
50
|
+
export let removeEmptyAttributes: boolean;
|
|
51
|
+
export let removeOptionalTags: boolean;
|
|
52
|
+
export let removeRedundantAttributes: boolean;
|
|
53
|
+
let removeScriptTypeAttributes_1: boolean;
|
|
54
|
+
export { removeScriptTypeAttributes_1 as removeScriptTypeAttributes };
|
|
55
|
+
let removeStyleLinkTypeAttributes_1: boolean;
|
|
56
|
+
export { removeStyleLinkTypeAttributes_1 as removeStyleLinkTypeAttributes };
|
|
57
|
+
export let sortAttributes: boolean;
|
|
58
|
+
export let sortClassName: boolean;
|
|
59
|
+
let useShortDoctype_1: boolean;
|
|
60
|
+
export { useShortDoctype_1 as useShortDoctype };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"AAiDA;;;;GAIG;AACH,gCAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAMvB;AAED;;;GAGG;AACH,kCAFa,MAAM,EAAE,CAIpB"}
|
package/package.json
CHANGED
package/src/htmlminifier.js
CHANGED
|
@@ -5,6 +5,7 @@ import { minify as terser } from 'terser';
|
|
|
5
5
|
import { HTMLParser, endTag } from './htmlparser.js';
|
|
6
6
|
import TokenChain from './tokenchain.js';
|
|
7
7
|
import { replaceAsync } from './utils.js';
|
|
8
|
+
import { presets, getPreset, getPresetNames } from './presets.js';
|
|
8
9
|
|
|
9
10
|
const trimWhitespace = str => str && str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
|
|
10
11
|
|
|
@@ -1112,7 +1113,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1112
1113
|
}
|
|
1113
1114
|
|
|
1114
1115
|
const parser = new HTMLParser(value, {
|
|
1115
|
-
partialMarkup,
|
|
1116
|
+
partialMarkup: partialMarkup ?? options.partialMarkup,
|
|
1116
1117
|
continueOnParseError: options.continueOnParseError,
|
|
1117
1118
|
customAttrAssign: options.customAttrAssign,
|
|
1118
1119
|
customAttrSurround: options.customAttrSurround,
|
|
@@ -1484,7 +1485,9 @@ export const minify = async function (value, options) {
|
|
|
1484
1485
|
return result;
|
|
1485
1486
|
};
|
|
1486
1487
|
|
|
1487
|
-
export
|
|
1488
|
+
export { presets, getPreset, getPresetNames };
|
|
1489
|
+
|
|
1490
|
+
export default { minify, presets, getPreset, getPresetNames };
|
|
1488
1491
|
|
|
1489
1492
|
/**
|
|
1490
1493
|
* @typedef {Object} HTMLAttribute
|
|
@@ -1696,6 +1699,15 @@ export default { minify };
|
|
|
1696
1699
|
*
|
|
1697
1700
|
* Default: `false`
|
|
1698
1701
|
*
|
|
1702
|
+
* @prop {boolean} [partialMarkup]
|
|
1703
|
+
* When true, treat input as a partial HTML fragment rather than a complete
|
|
1704
|
+
* document. This preserves stray end tags (closing tags without corresponding
|
|
1705
|
+
* opening tags) and prevents auto-closing of unclosed tags at the end of input.
|
|
1706
|
+
* Useful for minifying template fragments, SSI includes, or other partial HTML
|
|
1707
|
+
* that will be combined with other fragments.
|
|
1708
|
+
*
|
|
1709
|
+
* Default: `false`
|
|
1710
|
+
*
|
|
1699
1711
|
* @prop {boolean} [preserveLineBreaks]
|
|
1700
1712
|
* Preserve a single line break at the start/end of text nodes when
|
|
1701
1713
|
* collapsing/trimming whitespace.
|
package/src/htmlparser.js
CHANGED
|
@@ -443,6 +443,11 @@ export class HTMLParser {
|
|
|
443
443
|
// Remove the open elements from the stack
|
|
444
444
|
stack.length = pos;
|
|
445
445
|
lastTag = pos && stack[pos - 1].tag;
|
|
446
|
+
} else if (handler.partialMarkup && tagName) {
|
|
447
|
+
// In partial markup mode, preserve stray end tags
|
|
448
|
+
if (handler.end) {
|
|
449
|
+
handler.end(tagName, [], false);
|
|
450
|
+
}
|
|
446
451
|
} else if (tagName.toLowerCase() === 'br') {
|
|
447
452
|
if (handler.start) {
|
|
448
453
|
await handler.start(tagName, [], true, '');
|
package/src/presets.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset configurations for HTML Minifier Next
|
|
3
|
+
*
|
|
4
|
+
* Presets provide curated option sets for common use cases:
|
|
5
|
+
* - conservative: Safe minification suitable for most projects
|
|
6
|
+
* - comprehensive: Aggressive minification for maximum file size reduction
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const presets = {
|
|
10
|
+
conservative: {
|
|
11
|
+
collapseBooleanAttributes: true,
|
|
12
|
+
collapseWhitespace: true,
|
|
13
|
+
conservativeCollapse: true,
|
|
14
|
+
continueOnParseError: true,
|
|
15
|
+
decodeEntities: true,
|
|
16
|
+
minifyURLs: true,
|
|
17
|
+
noNewlinesBeforeTagClose: true,
|
|
18
|
+
preserveLineBreaks: true,
|
|
19
|
+
removeComments: true,
|
|
20
|
+
removeScriptTypeAttributes: true,
|
|
21
|
+
removeStyleLinkTypeAttributes: true,
|
|
22
|
+
useShortDoctype: true
|
|
23
|
+
},
|
|
24
|
+
comprehensive: {
|
|
25
|
+
caseSensitive: true,
|
|
26
|
+
collapseBooleanAttributes: true,
|
|
27
|
+
collapseInlineTagWhitespace: true,
|
|
28
|
+
collapseWhitespace: true,
|
|
29
|
+
conservativeCollapse: true,
|
|
30
|
+
continueOnParseError: true,
|
|
31
|
+
decodeEntities: true,
|
|
32
|
+
minifyCSS: true,
|
|
33
|
+
minifyJS: true,
|
|
34
|
+
minifyURLs: true,
|
|
35
|
+
noNewlinesBeforeTagClose: true,
|
|
36
|
+
processConditionalComments: true,
|
|
37
|
+
removeAttributeQuotes: true,
|
|
38
|
+
removeComments: true,
|
|
39
|
+
removeEmptyAttributes: true,
|
|
40
|
+
removeOptionalTags: true,
|
|
41
|
+
removeRedundantAttributes: true,
|
|
42
|
+
removeScriptTypeAttributes: true,
|
|
43
|
+
removeStyleLinkTypeAttributes: true,
|
|
44
|
+
sortAttributes: true,
|
|
45
|
+
sortClassName: true,
|
|
46
|
+
useShortDoctype: true
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get preset configuration by name
|
|
52
|
+
* @param {string} name - Preset name ('conservative' or 'comprehensive')
|
|
53
|
+
* @returns {object|null} Preset options object or null if not found
|
|
54
|
+
*/
|
|
55
|
+
export function getPreset(name) {
|
|
56
|
+
if (!name) return null;
|
|
57
|
+
const normalizedName = name.toLowerCase();
|
|
58
|
+
return presets[normalizedName] || null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get list of available preset names
|
|
63
|
+
* @returns {string[]} Array of preset names
|
|
64
|
+
*/
|
|
65
|
+
export function getPresetNames() {
|
|
66
|
+
return Object.keys(presets);
|
|
67
|
+
}
|