html-minifier 5.0.0 → 5.0.2
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 +8 -6
- package/cli.js +2 -0
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/lib/attributes.d.ts +3 -1
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/fragments.d.ts +13 -1
- package/dist/types/lib/fragments.d.ts.map +1 -1
- package/package.json +5 -6
- package/src/htmlminifier.js +78 -51
- package/src/lib/attributes.js +31 -8
- package/src/lib/constants.js +3 -3
- package/src/lib/fragments.js +274 -1
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
Your web page optimization precision tool: HTML Minifier (HM) is a **highly effective, super-configurable, well-tested HTML minifier**, written in JavaScript, that also handles in-document CSS, JavaScript, and SVG minification.
|
|
6
6
|
|
|
7
|
-
HTML Minifier was originally created by [Juriy “kangax” Zaytsev](https://
|
|
7
|
+
HTML Minifier was originally created by [Juriy “kangax” Zaytsev](https://perfectionkills.com/). As of version 5, it’s actively maintained by [Jens Oliver Meiert](https://meiert.com)—richer in features, easier to use, and significantly faster ([engineering philosophy](https://meiert.com/blog/html-minifier-next-engineering-philosophy/)).
|
|
8
8
|
|
|
9
|
-
ℹ️ **Updating from HTML Minifier 4? Use
|
|
9
|
+
ℹ️ **Updating from HTML Minifier 4? [Use the HTML Minifier 4 to 5 upgrade guide.](https://meiert.com/blog/html-minifier-4-5/)** Switching from HTML Minifier Next? [Use the HTML Minifier Next to HTML Minifier migration guide.](https://meiert.com/blog/html-minifier-next-to-html-minifier/)
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -600,7 +600,7 @@ The CLI’s `--verbose` and `--dry` modes print the same information to STDERR a
|
|
|
600
600
|
|
|
601
601
|
## Minification comparison
|
|
602
602
|
|
|
603
|
-
Please see [**the Minifier Benchmarks
|
|
603
|
+
Please see [**the Minifier Benchmarks Project**](https://j9t.github.io/minifier-benchmarks/) for details on how HTML Minifier compares to other minifiers. (The benchmarks are currently maintained by the author of HTML Minifier. Contributions, including from other minifier authors, are welcome.)
|
|
604
604
|
|
|
605
605
|
## Examples
|
|
606
606
|
|
|
@@ -717,6 +717,8 @@ HTML reads CRLF and CR line breaks as line feeds, so HM writes them so, too. Mar
|
|
|
717
717
|
|
|
718
718
|
By default, `ignoreCustomFragments` preserves `<?php … ?>`, `<?= … ?>`, `<? … ?>`, and `<% … %>` blocks (as well as XML declarations), so PHP, ERB, JSP, and ASP templates can be minified as part of a build, with the markup, CSS, and JavaScript around those blocks being minified as usual. For includes and other partials, add `partialMarkup`.
|
|
719
719
|
|
|
720
|
+
CSS and JavaScript are only left as written where a template block starts a rule or statement (`<?php if ($a): ?>` between two statements, `<?= $rules ?>` after a rule, or `<?= $code ?>` after `case 1:`, `default:`, or a label), as it may render as anything or as nothing, or where it sits in an `@charset`, `@import`, or `@namespace` statement that would otherwise be dropped with it (`@import <?= $url ?>;`). Template blocks within values (`color: <?= $color ?>`) are safe—here, the code around them is minified, so a block in code that minification removes (such as an unused rule or dead code) goes with that code. This detection is best-effort, as HTML Minifier doesn’t parse template code. To exempt anything it misses, use [`canMinifyCSS`](#css-minification) or [`canMinifyJS`](#javascript-minification).
|
|
721
|
+
|
|
720
722
|
Whitespace next to template blocks is collapsed but kept, as it may matter once the template renders. `trimCustomFragments` removes it where HTML Minifier would remove whitespace anyway, as between block-level elements: `<ul> <?php foreach ($items as $item): ?> <li>` loses it, `Hello <?= $name ?>!` keeps it. Tags keep the spacing of their source, and whitespace next to template blocks in attribute values, CSS, and JavaScript is always kept.
|
|
721
723
|
|
|
722
724
|
Where pages are rendered ahead of serving, as with static exports or full-page caches, you can also minify the rendered HTML instead, which makes this a non-issue.
|
|
@@ -842,7 +844,7 @@ npm i;
|
|
|
842
844
|
npm run backtest
|
|
843
845
|
```
|
|
844
846
|
|
|
845
|
-
The backtest tool tracks minification performance—output size (raw, Gzip, and Brotli) and processing time—across Git history. Results are saved in the backtest folder (results.json).
|
|
847
|
+
The backtest tool tracks minification performance—output size (raw, Gzip, and Brotli) and processing time—across Git history. Results are saved in the backtest folder (results.json). The corpus (backtest/sites.json) is shared with [the Minifier Benchmarks Project](https://github.com/j9t/minifier-benchmarks)—changing sites requires changing them in both repositories.
|
|
846
848
|
|
|
847
849
|
Parameters:
|
|
848
850
|
|
|
@@ -890,7 +892,7 @@ This writes a .cpuprofile file to the working directory. Load it with `npx speed
|
|
|
890
892
|
|
|
891
893
|
## Acknowledgements
|
|
892
894
|
|
|
893
|
-
With many thanks to the previous authors of and contributors to HTML Minifier, especially [Juriy “kangax” Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, particularly [Daniel Ruf](https://github.com/DanielRuf), [Jonas Geiler](https://github.com/jonasgeiler), [Chris Morgan](https://github.com/chris-morgan), and [Andreas Borutta](https://github.com/fermiongithub)!
|
|
895
|
+
With many thanks to the previous authors of and contributors to HTML Minifier and HTML Minifier Next, especially [Juriy “kangax” Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, particularly [Daniel Ruf](https://github.com/DanielRuf), [Jonas Geiler](https://github.com/jonasgeiler), [Chris Morgan](https://github.com/chris-morgan), and [Andreas Borutta](https://github.com/fermiongithub)!
|
|
894
896
|
|
|
895
897
|
***
|
|
896
898
|
|
|
@@ -898,4 +900,4 @@ You might like some of my other work:
|
|
|
898
900
|
|
|
899
901
|
* Optimization tools: [hihtml](https://github.com/j9t/hihtml) · HTML Minifier · [ObsoHTML](https://github.com/j9t/obsohtml) · [CSS Dedup](https://github.com/j9t/css-dedup) · [Image Guard](https://github.com/j9t/image-guard) · [Compressor.js Next](https://github.com/j9t/compressorjs-next) · [.htaccess Punk](https://github.com/j9t/htaccess-punk)
|
|
900
902
|
* Defense tools: [IA Defensa](https://iadefensa.com/solutions/)
|
|
901
|
-
* Resources for quality web development: [Articles](https://meiert.com/topics/development/) · [Books](https://meiert.com/topics/books/) (
|
|
903
|
+
* Resources for quality web development: [Articles](https://meiert.com/topics/development/) · [Books](https://meiert.com/topics/books/) (e.g., [_On Web Development_](https://meiert.com/blog/on-web-development-2/)) · [News](https://dxsrc.org/) · [Terminology](https://webglossary.info/)
|
package/cli.js
CHANGED
|
@@ -1083,6 +1083,8 @@ program.helpOption('-h, --help', 'Display help for command');
|
|
|
1083
1083
|
await reportAndWrite(capturedFiles.join(', '), originals.join(''), outputs.join(''));
|
|
1084
1084
|
process.exit(0);
|
|
1085
1085
|
} else { // Minifying input coming from STDIN
|
|
1086
|
+
if (process.stdin.isTTY && process.argv.length === 2) program.help();
|
|
1087
|
+
|
|
1086
1088
|
content = '';
|
|
1087
1089
|
process.stdin.setEncoding('utf8');
|
|
1088
1090
|
process.stdin.on('data', function (/** @type {string} */ data) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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"}
|
|
@@ -73,15 +73,17 @@ declare function normalizeAttr(attr: HTMLAttribute, attrs: HTMLAttribute[], tag:
|
|
|
73
73
|
} | undefined;
|
|
74
74
|
/**
|
|
75
75
|
* @param {{name: string, value: string | undefined, attr: HTMLAttribute}} normalized
|
|
76
|
+
* @param {string} tag
|
|
76
77
|
* @param {string | boolean | undefined} hasUnarySlash
|
|
77
78
|
* @param {ProcessedOptions} options
|
|
78
79
|
* @param {boolean} isLast
|
|
79
80
|
* @param {string | undefined} uidAttr
|
|
81
|
+
* @param {string[]} markers - What custom fragment and `htmlmin:ignore` placeholders hold
|
|
80
82
|
*/
|
|
81
83
|
declare function buildAttr(normalized: {
|
|
82
84
|
name: string;
|
|
83
85
|
value: string | undefined;
|
|
84
86
|
attr: HTMLAttribute;
|
|
85
|
-
}, hasUnarySlash: string | boolean | undefined, options: ProcessedOptions, isLast: boolean, uidAttr: string | undefined): string;
|
|
87
|
+
}, tag: string, hasUnarySlash: string | boolean | undefined, options: ProcessedOptions, isLast: boolean, uidAttr: string | undefined, markers: string[]): string;
|
|
86
88
|
export { isIgnoredComment, isExecutableScript, isStyleElement, isMetaWithoutEffect, hasAttrName, normalizeAttr, buildAttr, deduplicateAttributes, replaceContentTypePragma };
|
|
87
89
|
//# sourceMappingURL=attributes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":"AAAA;;GAEG;AAoCC,OAAQ,KAAA,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGjD,YAA0I,aAAa,GAA7I;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAE1I;AA8BD;;;GAGG;AACH,iBAAS,gBAAgB,CAAC,IAAI,EAHnB,MAGmB,EAAE,OAAO,EAF5B,gBAE4B,WAOtC;AAuCD;;;;;;;GAOG;AACH,iBAAS,qBAAqB,CAAC,KAAK,EAJzB,aAAa,EAIY,EAAE,aAAa,EAHxC,OAGwC,GAFtC,aAAa,EAAE,CAqB3B;AA4JD;;;GAGG;AACH,iBAAS,kBAAkB,CAAC,GAAG,EAHpB,MAGoB,EAAE,KAAK,EAF3B,aAAa,EAEc,WAUrC;AAOD;;;GAGG;AACH,iBAAS,cAAc,CAAC,GAAG,EAHhB,MAGgB,EAAE,KAAK,EAFvB,aAAa,EAEU,WAUjC;
|
|
1
|
+
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":"AAAA;;GAEG;AAoCC,OAAQ,KAAA,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGjD,YAA0I,aAAa,GAA7I;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAE1I;AA8BD;;;GAGG;AACH,iBAAS,gBAAgB,CAAC,IAAI,EAHnB,MAGmB,EAAE,OAAO,EAF5B,gBAE4B,WAOtC;AAuCD;;;;;;;GAOG;AACH,iBAAS,qBAAqB,CAAC,KAAK,EAJzB,aAAa,EAIY,EAAE,aAAa,EAHxC,OAGwC,GAFtC,aAAa,EAAE,CAqB3B;AA4JD;;;GAGG;AACH,iBAAS,kBAAkB,CAAC,GAAG,EAHpB,MAGoB,EAAE,KAAK,EAF3B,aAAa,EAEc,WAUrC;AAOD;;;GAGG;AACH,iBAAS,cAAc,CAAC,GAAG,EAHhB,MAGgB,EAAE,KAAK,EAFvB,aAAa,EAEU,WAUjC;AAkLD;;;;;GAKG;AACH,iBAAS,mBAAmB,CAAC,KAAK,EAHvB,aAAa,EAGU,EAAE,OAAO,EAFhC,MAAM,EAE0B,WAkB1C;AA0BD;;;;;GAKG;AACH,iBAAS,wBAAwB,CAAC,KAAK,EAH5B,aAAa,EAGe,EAAE,OAAO,EAFrC,MAAM,EAE+B,QAe/C;AAmDD;;;GAGG;AACH,iBAAS,WAAW,CAAC,IAAI,EAHd,MAGc,EAAE,KAAK,EAFrB,aAAa,EAEQ,WAO/B;AAmUD;;;;;;;;GAQG;AACH,iBAAS,aAAa,CAAC,IAAI,EARhB,aAQgB,EAAE,KAAK,EAPvB,aAAa,EAOU,EAAE,GAAG,EAN5B,MAM4B,EAAE,MAAM,EALpC,MAKoC,EAAE,OAAO,EAJ7C,gBAI6C,EAAE,UAAU,UAAA,EAAE,OAAO,AAF1E,CACF,EADU,MAAM,EAE4D;;;;;;;;cAkB5E;AAsED;;;;;;;;GAQG;AACH,iBAAS,SAAS,CAAC,UAAU,EARlB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAQ3C,EAAE,GAAG,EAPvB,MAOuB,EAAE,aAAa,EANtC,MAAM,GAAG,OAAO,GAAG,SAMmB,EAAE,OAAO,EAL/C,gBAK+C,EAAE,MAAM,EAJvD,OAIuD,EAAE,OAAO,EAHhE,MAAM,GAAG,SAGuD,EAAE,OAAO,EAFzE,MAAM,EAEmE,UA0HnF;AAED,OAAO,EAEL,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,WAAW,EAGX,aAAa,EACb,SAAS,EACT,qBAAqB,EACrB,wBAAwB,EACzB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"AAAA;;GAEG;AAEH,QAAA,MAAM,WAAW,QAAkB,CAAC;AACpC,QAAA,MAAM,SAAS,QAAkB,CAAC;AAClC,QAAA,MAAM,UAAU,QAAmB,CAAC;AACpC,QAAA,MAAM,cAAc,QAAqB,CAAC;AAC1C,QAAA,MAAM,cAAc,QAAsB,CAAC;AAC3C,QAAA,MAAM,qBAAqB,QAAuB,CAAC;AACnD,QAAA,MAAM,kBAAkB,QAAqB,CAAC;AAC9C,QAAA,MAAM,sBAAsB,QAAqB,CAAC;AAClD,QAAA,MAAM,sBAAsB,QAAc,CAAC;AAC3C,QAAA,MAAM,qBAAqB,QAAkB,CAAC;AAC9C,QAAA,MAAM,yBAAyB,QAA0B,CAAC;AAC1D,QAAA,MAAM,qBAAqB,QAAO,CAAC;AAEnC,QAAA,MAAM,KAAK,QAAQ,CAAC;AACpB,QAAA,MAAM,gBAAgB,QAAW,CAAC;AAIlC,QAAA,MAAM,kBAAkB,QAAiG,CAAC;AAG1H,QAAA,MAAM,qBAAqB,QAA8G,CAAC;AAC1I,QAAA,MAAM,YAAY,QAAO,CAAC;AAG1B,QAAA,MAAM,eAAe,QAA4D,CAAC;AAGlF,QAAA,MAAM,kBAAkB,QAAgB,CAAC;AACzC,QAAA,MAAM,kBAAkB,QAAgB,CAAC;AAKzC,QAAA,MAAM,qBAAqB,EAAc,MAAM,CAAC,MAAM,EAAE,MAAM,CAG3D,CAAC;AAGJ,QAAA,MAAM,gBAAgB,QAA4C,CAAC;AAKnE,QAAA,MAAM,UAAU,QAA+K,CAAC;AAChM,QAAA,MAAM,gBAAgB,QAAgB,CAAC;AACvC,QAAA,MAAM,mBAAmB,QAAkB,CAAC;AAC5C,QAAA,MAAM,UAAU,QAAO,CAAC;AACxB,QAAA,MAAM,gBAAgB,QAAkB,CAAC;AACzC,QAAA,MAAM,kBAAkB,QAE6B,CAAC;AAKtD,QAAA,MAAM,oCAAoC,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"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"}
|
|
@@ -43,5 +43,17 @@ declare function toFragment(pattern: RegExp): DelimitedFragment | PatternFragmen
|
|
|
43
43
|
* @returns {string}
|
|
44
44
|
*/
|
|
45
45
|
declare function replaceCustomFragments(value: string, fragments: (DelimitedFragment | PatternFragment)[], replacer: (match: string) => string): string;
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Whether minified CSS or JavaScript can stand in for its source. A fragment starting a
|
|
48
|
+
* statement or rule can render as anything, or as nothing, while engines read it as an
|
|
49
|
+
* identifier or selector and join it with the code around it, or drop it; a fragment in
|
|
50
|
+
* an at-rule Lightning CSS cannot read would go with that at-rule
|
|
51
|
+
* @param {string} source
|
|
52
|
+
* @param {unknown} output - Anything but a string, from a minifier of one’s own, passes
|
|
53
|
+
* @param {RegExp} placeholder - Global pattern matching one fragment placeholder
|
|
54
|
+
* @param {boolean} [isJS] - Whether the code is JavaScript rather than CSS
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
declare function isMinifiedCodeSafe(source: string, output: unknown, placeholder: RegExp, isJS?: boolean): boolean;
|
|
58
|
+
export { toDelimitedFragment, toFragment, replaceCustomFragments, isMinifiedCodeSafe };
|
|
47
59
|
//# sourceMappingURL=fragments.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragments.d.ts","sourceRoot":"","sources":["../../../src/lib/fragments.js"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"fragments.d.ts","sourceRoot":"","sources":["../../../src/lib/fragments.js"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA0BA,YAA4F,iBAAiB,GAAnG;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,CAI1F;AAAA,YAAoE,eAAe,GAAzE;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,CAGpE;AAmCD;;;;;GAKG;AACH,iBAAS,mBAAmB,CAAC,OAAO,EAJzB,MAIyB,GAHvB,iBAAiB,GAAG,IAAI,CAwBpC;AA8CD;;;;GAIG;AACH,iBAAS,UAAU,CAAC,OAAO,EAHhB,MAGgB,GAFd,iBAAiB,GAAG,eAAe,CAa/C;AAED;;;;;;;GAOG;AACH,iBAAS,sBAAsB,CAAC,KAAK,EAN1B,MAM0B,EAAE,SAAS,EALrC,CAAC,iBAAiB,GAAG,eAAe,CAAC,EAKA,EAAE,QAAQ,EAH/C,CAAC,KAAK,EAAE,MAAM,KAAK,MAG4B,GAF7C,MAAM,CA2KlB;AA2OD;;;;;;;;;;GAUG;AACH,iBAAS,kBAAkB,CAAC,MAAM,EANvB,MAMuB,EAAE,MAAM,EAL/B,OAK+B,EAAE,WAAW,EAJ5C,MAI4C,EAAE,IAAI,AAH1D,CACA,EADQ,OAG0D,GAFxD,OAAO,CAenB;AAED,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,sBAAsB,EACtB,kBAAkB,EACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"bugs": "https://github.com/j9t/html-minifier/issues",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"commander": "^15.
|
|
9
|
+
"commander": "^15.1.0",
|
|
10
10
|
"entities": "^9.0.0",
|
|
11
11
|
"lightningcss": "^1.33.0",
|
|
12
12
|
"svgo": "^4.1.0",
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"description": "Highly effective, super-configurable, well-tested web page minifier",
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@eslint/js": "^10.0.1",
|
|
18
|
-
"@swc/core": "^1.16.
|
|
18
|
+
"@swc/core": "^1.16.22",
|
|
19
19
|
"@types/node": "^26.6.6",
|
|
20
20
|
"eslint": "^10.14.0",
|
|
21
21
|
"globals": "^17.14.0",
|
|
22
22
|
"typescript": "^7.0.2",
|
|
23
|
-
"vite": "^8.3.
|
|
23
|
+
"vite": "^8.3.6"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=22.13"
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"compressor",
|
|
47
47
|
"css",
|
|
48
48
|
"html",
|
|
49
|
+
"html-minifier",
|
|
49
50
|
"htmlmin",
|
|
50
51
|
"javascript",
|
|
51
52
|
"js",
|
|
@@ -55,8 +56,6 @@
|
|
|
55
56
|
"minify",
|
|
56
57
|
"optimize",
|
|
57
58
|
"optimizer",
|
|
58
|
-
"pack",
|
|
59
|
-
"packer",
|
|
60
59
|
"svg",
|
|
61
60
|
"svgo",
|
|
62
61
|
"swc",
|
|
@@ -94,5 +93,5 @@
|
|
|
94
93
|
},
|
|
95
94
|
"type": "module",
|
|
96
95
|
"types": "dist/types/htmlminifier.d.ts",
|
|
97
|
-
"version": "5.0.
|
|
96
|
+
"version": "5.0.2"
|
|
98
97
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
} from './lib/content.js';
|
|
79
79
|
|
|
80
80
|
import { processOptions } from './lib/options.js';
|
|
81
|
-
import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
81
|
+
import { toFragment, replaceCustomFragments, isMinifiedCodeSafe } from './lib/fragments.js';
|
|
82
82
|
|
|
83
83
|
/** @import { ProcessedOptions } from './lib/options.js' */
|
|
84
84
|
|
|
@@ -688,7 +688,22 @@ const RE_BLANKS_ONLY = /^[ \t\f]+$/;
|
|
|
688
688
|
const RE_HTML_COMMENT_START = /^\s*<!--/;
|
|
689
689
|
const RE_CLOSING_TAG_START = /^\s*<\/([a-zA-Z][\w:-]*)/;
|
|
690
690
|
const RE_FIRST_HTML_TAG = /^\s*<([a-zA-Z][\w:-]*)/;
|
|
691
|
-
const
|
|
691
|
+
const RE_HTML_TAG_AT = /<(\/?[a-zA-Z][\w:-]*)/y;
|
|
692
|
+
|
|
693
|
+
// Name of the last tag in `content` (prefixed with `/` for an end tag), scanned backward
|
|
694
|
+
// from each `<`, as a greedy `[\s\S]*<…` regex backtracks quadratically on tagless content
|
|
695
|
+
/**
|
|
696
|
+
* @param {string} content
|
|
697
|
+
* @returns {string | undefined}
|
|
698
|
+
*/
|
|
699
|
+
function findLastHtmlTag(content) {
|
|
700
|
+
for (let i = content.lastIndexOf('<'); i !== -1; i = i ? content.lastIndexOf('<', i - 1) : -1) {
|
|
701
|
+
RE_HTML_TAG_AT.lastIndex = i;
|
|
702
|
+
const match = RE_HTML_TAG_AT.exec(content);
|
|
703
|
+
if (match) return match[1];
|
|
704
|
+
}
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
692
707
|
|
|
693
708
|
|
|
694
709
|
// Probe for SVG/MathML elements—when the input has none (the common case),
|
|
@@ -1150,7 +1165,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1150
1165
|
let textVerbatim = false;
|
|
1151
1166
|
/** @type {string[]} */
|
|
1152
1167
|
const ignoredMarkupChunks = [];
|
|
1153
|
-
/** @type {Array<
|
|
1168
|
+
/** @type {Array<[string, string, string]>} */
|
|
1154
1169
|
const ignoredCustomMarkupChunks = [];
|
|
1155
1170
|
// Custom fragments whose surrounding whitespace is never trimmed: in tags, where it
|
|
1156
1171
|
// separates attributes, and in CSS and JavaScript, where minifiers can’t judge it
|
|
@@ -1248,35 +1263,40 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1248
1263
|
uidAttrLeadingPattern = new RegExp('^\\s*' + uidAttr + '(\\d+)' + uidAttr);
|
|
1249
1264
|
uidAttrExactPattern = new RegExp('^' + uidAttr + '(\\d+)' + uidAttr + '$');
|
|
1250
1265
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1266
|
+
const uidPlaceholderPattern = new RegExp(uidAttr + '\\d+' + uidAttr, 'g');
|
|
1267
|
+
|
|
1268
|
+
/**
|
|
1269
|
+
* @param {(text: string, ...args: any[]) => any} fn
|
|
1270
|
+
* @param {boolean} isJS
|
|
1271
|
+
* @returns {any}
|
|
1272
|
+
*/
|
|
1273
|
+
const wrapCodeMinifier = (fn, isJS) => (/** @type {string} */ text, /** @type {any[]} */ ...args) => {
|
|
1274
|
+
if (!text.includes(/** @type {string} */ (uidAttr))) return fn(text, ...args);
|
|
1275
|
+
|
|
1276
|
+
const source = text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
|
|
1277
|
+
const chunks = ignoredCustomMarkupChunks[+index];
|
|
1278
|
+
customMarkupInCode.add(+index);
|
|
1279
|
+
return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
|
|
1280
|
+
});
|
|
1281
|
+
const result = fn(source, ...args);
|
|
1282
|
+
const finish = (/** @type {string} */ output) => {
|
|
1283
|
+
if (isMinifiedCodeSafe(source, output, uidPlaceholderPattern, isJS)) return output;
|
|
1284
|
+
options.log('Keeping CSS or JavaScript as written, as minifying could break or lose a custom fragment');
|
|
1285
|
+
return source;
|
|
1286
|
+
};
|
|
1287
|
+
return isThenable(result) ? Promise.resolve(result).then(finish) : finish(result);
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
if (options.minifyCSS !== identity) options.minifyCSS = wrapCodeMinifier(options.minifyCSS, false);
|
|
1291
|
+
if (options.minifyJS !== identity) options.minifyJS = wrapCodeMinifier(options.minifyJS, true);
|
|
1276
1292
|
}
|
|
1277
1293
|
|
|
1278
1294
|
const token = uidAttr + ignoredCustomMarkupChunks.length + uidAttr;
|
|
1279
|
-
|
|
1295
|
+
// Split off leading and trailing whitespace without a regex, which would
|
|
1296
|
+
// backtrack quadratically over inner whitespace runs
|
|
1297
|
+
const leading = match.length - match.trimStart().length;
|
|
1298
|
+
const trailing = leading === match.length ? 0 : match.length - match.trimEnd().length;
|
|
1299
|
+
ignoredCustomMarkupChunks.push([match, match.slice(0, leading), match.slice(match.length - trailing)]);
|
|
1280
1300
|
return '\t' + token + '\t';
|
|
1281
1301
|
};
|
|
1282
1302
|
|
|
@@ -1323,7 +1343,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1323
1343
|
let joinsTag = false;
|
|
1324
1344
|
let writeIndex = 0;
|
|
1325
1345
|
// Padding of the previous attribute if that is a fragment without value
|
|
1326
|
-
/** @type {
|
|
1346
|
+
/** @type {[string, string, string] | null | undefined} */
|
|
1327
1347
|
let prevChunks = null;
|
|
1328
1348
|
for (const attr of attrs) {
|
|
1329
1349
|
const uidMatch = pattern.exec(attr.name);
|
|
@@ -1601,11 +1621,14 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1601
1621
|
// Nothing to the left of the text is in play here, so only its trailing run can
|
|
1602
1622
|
// change—unless preserved line breaks bring the leading run into play, too
|
|
1603
1623
|
const trailingOnly = !options.preserveLineBreaks;
|
|
1604
|
-
|
|
1624
|
+
let prevTag = '';
|
|
1625
|
+
let prevTagLower = '';
|
|
1626
|
+
for (; index > noTrimEndIndex && canTrimWhitespace(prevTagLower, prevTag, emptyAttrs); index--) {
|
|
1605
1627
|
const str = buffer[index] ?? '';
|
|
1606
1628
|
const endTagName = matchPlainEndTag(str);
|
|
1607
1629
|
if (endTagName !== null) {
|
|
1608
1630
|
prevTag = endTagName;
|
|
1631
|
+
prevTagLower = prevTag.toLowerCase();
|
|
1609
1632
|
continue;
|
|
1610
1633
|
}
|
|
1611
1634
|
// A tag ends the search, and so does text that survives collapsing. Text with no
|
|
@@ -1792,9 +1815,8 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1792
1815
|
const idx = +(match[1] ?? '');
|
|
1793
1816
|
if (idx < ignoredMarkupChunks.length) {
|
|
1794
1817
|
const content = ignoredMarkupChunks[idx];
|
|
1795
|
-
const
|
|
1796
|
-
if (
|
|
1797
|
-
const group = lastTagMatch[1] ?? '';
|
|
1818
|
+
const group = content && findLastHtmlTag(content);
|
|
1819
|
+
if (group) {
|
|
1798
1820
|
const isClose = group.charAt(0) === '/';
|
|
1799
1821
|
const tagName = resolveName(isClose ? group.slice(1) : group);
|
|
1800
1822
|
effectivePrevTag = isClose ? '/' + tagName : tagName;
|
|
@@ -2128,8 +2150,9 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2128
2150
|
* @param {string | boolean | undefined} hasUnarySlash
|
|
2129
2151
|
* @param {boolean | undefined} optional
|
|
2130
2152
|
* @param {ProcessedOptions | null} restoreOptions - Context a unary tag pushed, or null
|
|
2153
|
+
* @param {ProcessedOptions} optionsTag - Context the element’s own attributes are in
|
|
2131
2154
|
*/
|
|
2132
|
-
function emitStartTag(normalizedAttrs, tag, unary, autoGenerated, fragmentJoinsTag, hasUnarySlash, optional, restoreOptions) {
|
|
2155
|
+
function emitStartTag(normalizedAttrs, tag, unary, autoGenerated, fragmentJoinsTag, hasUnarySlash, optional, restoreOptions, optionsTag) {
|
|
2133
2156
|
// Find the last kept attribute, then emit in order, with no intermediate array
|
|
2134
2157
|
let lastKeptIndex = -1;
|
|
2135
2158
|
for (let i = normalizedAttrs.length - 1; i >= 0; i--) {
|
|
@@ -2151,7 +2174,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2151
2174
|
if (uidAttr && normalizedAttr.name.includes(uidAttr)) {
|
|
2152
2175
|
markTagFragments(normalizedAttr.name);
|
|
2153
2176
|
}
|
|
2154
|
-
tagText += buildAttr(normalizedAttr, hasUnarySlash,
|
|
2177
|
+
tagText += buildAttr(normalizedAttr, tag, hasUnarySlash, optionsTag, i === lastKeptIndex, uidAttr, markers);
|
|
2155
2178
|
}
|
|
2156
2179
|
}
|
|
2157
2180
|
} else {
|
|
@@ -2164,7 +2187,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2164
2187
|
if (uidAttr && normalizedAttr.name.includes(uidAttr)) {
|
|
2165
2188
|
markTagFragments(normalizedAttr.name);
|
|
2166
2189
|
}
|
|
2167
|
-
const builtAttr = buildAttr(normalizedAttr, hasUnarySlash,
|
|
2190
|
+
const builtAttr = buildAttr(normalizedAttr, tag, hasUnarySlash, optionsTag, i === lastKeptIndex, uidAttr, markers);
|
|
2168
2191
|
// One entry with the tag name, so that no line break can come between them
|
|
2169
2192
|
if (i === 0 && fragmentJoinsTag) {
|
|
2170
2193
|
buffer[buffer.length - 1] += builtAttr;
|
|
@@ -2320,7 +2343,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2320
2343
|
}
|
|
2321
2344
|
currentTag = tag;
|
|
2322
2345
|
charsPrevTag = tag;
|
|
2323
|
-
if (!inlineTextSet.has(tag)) {
|
|
2346
|
+
if (!inlineTextSet.has(lowerTag || tag)) {
|
|
2324
2347
|
currentChars = '';
|
|
2325
2348
|
}
|
|
2326
2349
|
hasChars = false;
|
|
@@ -2358,17 +2381,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2358
2381
|
// Set whitespace flags for nested tags (e.g., `<code>` within a `<pre>`); verbatim
|
|
2359
2382
|
// whitespace is tracked without `collapseWhitespace` too, as `removeWhitespaceLine` spares it
|
|
2360
2383
|
if (options.collapseWhitespace && !stackNoTrimWhitespace.length) {
|
|
2361
|
-
squashTrailingWhitespace(tag);
|
|
2384
|
+
squashTrailingWhitespace(lowerTag || tag);
|
|
2362
2385
|
}
|
|
2363
|
-
if (options.collapseWhitespace && !unary && inlineElementsTransparent.has(tag)) {
|
|
2386
|
+
if (options.collapseWhitespace && !unary && inlineElementsTransparent.has(lowerTag || tag)) {
|
|
2364
2387
|
transparentDepth++;
|
|
2365
2388
|
}
|
|
2366
2389
|
if (!unary) {
|
|
2367
|
-
|
|
2390
|
+
const tagLower = lowerTag || tag;
|
|
2391
|
+
if (!canTrimWhitespace(tagLower, tagOut, attrs) || stackNoTrimWhitespace.length) {
|
|
2368
2392
|
stackNoTrimWhitespace.push(tag);
|
|
2369
|
-
if (
|
|
2393
|
+
if (tagLower === 'pre' || tagLower === 'textarea') preTextareaDepth++;
|
|
2370
2394
|
}
|
|
2371
|
-
if (options.collapseWhitespace && (!canCollapseWhitespace(
|
|
2395
|
+
if (options.collapseWhitespace && (!canCollapseWhitespace(tagLower, tagOut, attrs) || stackNoCollapseWhitespace.length)) {
|
|
2372
2396
|
stackNoCollapseWhitespace.push(tag);
|
|
2373
2397
|
}
|
|
2374
2398
|
}
|
|
@@ -2385,7 +2409,9 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2385
2409
|
// SVGO reads the whole SVG block as XML, where an element closes itself or not, so
|
|
2386
2410
|
// every unary element in there is written with a slash
|
|
2387
2411
|
const needsXMLSlash = unary && Boolean(options.insideSVG) && Boolean(options.minifySVG);
|
|
2388
|
-
|
|
2412
|
+
// `foreignObject` and `annotation-xml` hold HTML, but are foreign elements themselves
|
|
2413
|
+
const optionsTag = useNameParentForTag ? optionsParent : options;
|
|
2414
|
+
const hasUnarySlash = needsXMLSlash || (unarySlash && optionsTag.keepClosingSlash);
|
|
2389
2415
|
|
|
2390
2416
|
bufferPush(openTag, true);
|
|
2391
2417
|
|
|
@@ -2432,10 +2458,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2432
2458
|
const restoreOptions = unary && pushedContext ? optionsParent : null;
|
|
2433
2459
|
if (anyThenable) {
|
|
2434
2460
|
return Promise.all(attrResults).then(resolved => {
|
|
2435
|
-
emitStartTag(resolved, tag, unary, autoGenerated, fragmentJoinsTag, hasUnarySlash, optional, restoreOptions);
|
|
2461
|
+
emitStartTag(resolved, tag, unary, autoGenerated, fragmentJoinsTag, hasUnarySlash, optional, restoreOptions, optionsTag);
|
|
2436
2462
|
});
|
|
2437
2463
|
}
|
|
2438
|
-
emitStartTag(/** @type {Array<{name: string, value: string | undefined, attr: HTMLAttribute} | undefined>} */ (attrResults), tag, unary, autoGenerated, fragmentJoinsTag, hasUnarySlash, optional, restoreOptions);
|
|
2464
|
+
emitStartTag(/** @type {Array<{name: string, value: string | undefined, attr: HTMLAttribute} | undefined>} */ (attrResults), tag, unary, autoGenerated, fragmentJoinsTag, hasUnarySlash, optional, restoreOptions, optionsTag);
|
|
2439
2465
|
return undefined;
|
|
2440
2466
|
},
|
|
2441
2467
|
end: function (/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs, /** @type {boolean} */ autoGenerated, /** @type {string | undefined} */ rest, /** @type {boolean | undefined} */ holdsTable) {
|
|
@@ -2488,14 +2514,15 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2488
2514
|
let closesNoTrim = false;
|
|
2489
2515
|
if (stackNoTrimWhitespace.length) {
|
|
2490
2516
|
if (tag === stackNoTrimWhitespace[stackNoTrimWhitespace.length - 1]) {
|
|
2491
|
-
|
|
2517
|
+
const tagLower = lowerTag || tag;
|
|
2518
|
+
if (tagLower === 'pre' || tagLower === 'textarea') preTextareaDepth--;
|
|
2492
2519
|
stackNoTrimWhitespace.pop();
|
|
2493
2520
|
closesNoTrim = !stackNoTrimWhitespace.length;
|
|
2494
2521
|
}
|
|
2495
2522
|
} else if (options.collapseWhitespace) {
|
|
2496
|
-
squashTrailingWhitespace('/' + tag);
|
|
2523
|
+
squashTrailingWhitespace('/' + (lowerTag || tag));
|
|
2497
2524
|
}
|
|
2498
|
-
if (transparentDepth && inlineElementsTransparent.has(tag)) {
|
|
2525
|
+
if (transparentDepth && inlineElementsTransparent.has(lowerTag || tag)) {
|
|
2499
2526
|
transparentDepth--;
|
|
2500
2527
|
}
|
|
2501
2528
|
let closesNoCollapse = false;
|
|
@@ -2566,7 +2593,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2566
2593
|
bufferPush(endTagText, true);
|
|
2567
2594
|
}
|
|
2568
2595
|
charsPrevTag = '/' + tag;
|
|
2569
|
-
if (!inlineElements.has(tag)) {
|
|
2596
|
+
if (!inlineElements.has(lowerTag || tag)) {
|
|
2570
2597
|
currentChars = '';
|
|
2571
2598
|
} else if (isElementEmpty) {
|
|
2572
2599
|
currentChars += '|';
|
|
@@ -2579,7 +2606,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2579
2606
|
bufferPush(endTagText, true);
|
|
2580
2607
|
}
|
|
2581
2608
|
charsPrevTag = '/' + tag;
|
|
2582
|
-
if (!inlineElements.has(tag)) {
|
|
2609
|
+
if (!inlineElements.has(lowerTag || tag)) {
|
|
2583
2610
|
currentChars = '';
|
|
2584
2611
|
} else if (isElementEmpty) {
|
|
2585
2612
|
currentChars += '|';
|
package/src/lib/attributes.js
CHANGED
|
@@ -342,21 +342,42 @@ function isStyleElement(tag, attrs) {
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
/**
|
|
345
|
-
* Whether an attribute collapses to its name;
|
|
346
|
-
*
|
|
345
|
+
* Whether an attribute collapses to its name; lower-cases the value only for the few names
|
|
346
|
+
* that depend on it
|
|
347
347
|
*
|
|
348
|
-
* @param {string}
|
|
348
|
+
* @param {string} name - In lowercase
|
|
349
349
|
* @param {string} attrValue
|
|
350
|
+
* @param {string} tag
|
|
351
|
+
* @param {string[]} markers - What custom fragment and `htmlmin:ignore` placeholders hold
|
|
350
352
|
*/
|
|
351
|
-
function collapsesToBooleanName(
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
353
|
+
function collapsesToBooleanName(name, attrValue, tag, markers) {
|
|
354
|
+
const simple = isSimpleBoolean.has(name);
|
|
355
|
+
if (!simple && name !== 'hidden' && name !== 'draggable' && !collapsibleValues.has(name)) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
// Custom elements define what their attributes mean, and `marquee` counts its loops
|
|
359
|
+
if (tag.includes('-') || (name === 'loop' && tag.toLowerCase() === 'marquee')) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
// Template code and ignored markup are not the minifier’s to remove
|
|
363
|
+
if (markers.some(marker => attrValue.indexOf(marker) !== -1)) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
if (simple) {
|
|
354
367
|
return true;
|
|
355
368
|
}
|
|
356
369
|
const values = collapsibleValues.get(name);
|
|
357
370
|
if (values) {
|
|
358
371
|
return values.has(attrValue.toLowerCase());
|
|
359
372
|
}
|
|
373
|
+
// What a reference decodes to, the value as written doesn’t show
|
|
374
|
+
if ((name === 'hidden' || name === 'draggable') && attrValue.indexOf('&') !== -1) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
// Any other value means the hidden state, which the bare attribute does, too
|
|
378
|
+
if (name === 'hidden') {
|
|
379
|
+
return attrValue.toLowerCase() !== 'until-found';
|
|
380
|
+
}
|
|
360
381
|
return name === 'draggable' && !isBooleanValue.has(attrValue.toLowerCase());
|
|
361
382
|
}
|
|
362
383
|
|
|
@@ -1051,12 +1072,14 @@ function normalizeAttrFinish(attrName, attrNameOut, attrValue, attr, tag, tagOut
|
|
|
1051
1072
|
|
|
1052
1073
|
/**
|
|
1053
1074
|
* @param {{name: string, value: string | undefined, attr: HTMLAttribute}} normalized
|
|
1075
|
+
* @param {string} tag
|
|
1054
1076
|
* @param {string | boolean | undefined} hasUnarySlash
|
|
1055
1077
|
* @param {ProcessedOptions} options
|
|
1056
1078
|
* @param {boolean} isLast
|
|
1057
1079
|
* @param {string | undefined} uidAttr
|
|
1080
|
+
* @param {string[]} markers - What custom fragment and `htmlmin:ignore` placeholders hold
|
|
1058
1081
|
*/
|
|
1059
|
-
function buildAttr(normalized, hasUnarySlash, options, isLast, uidAttr) {
|
|
1082
|
+
function buildAttr(normalized, tag, hasUnarySlash, options, isLast, uidAttr, markers) {
|
|
1060
1083
|
const attrName = normalized.name;
|
|
1061
1084
|
let attrValue = normalized.value;
|
|
1062
1085
|
const attr = normalized.attr;
|
|
@@ -1070,7 +1093,7 @@ function buildAttr(normalized, hasUnarySlash, options, isLast, uidAttr) {
|
|
|
1070
1093
|
|
|
1071
1094
|
// An attribute written as its name alone skips the quoting work below
|
|
1072
1095
|
if (typeof attrValue === 'undefined' ||
|
|
1073
|
-
(options.collapseBooleanAttributes && collapsesToBooleanName(attrName, attrValue ?? '')) ||
|
|
1096
|
+
(options.collapseBooleanAttributes && !options.insideForeignContent && collapsesToBooleanName(options.namesAsWritten ? attrName.toLowerCase() : attrName, attrValue ?? '', tag, markers)) ||
|
|
1074
1097
|
(options.collapseEmptyAttributes && attrValue === '' && (attr.customAssign ?? '=') === '=')) {
|
|
1075
1098
|
return attr.customOpen + attrName + (isLast ? '' : ' ') + attr.customClose;
|
|
1076
1099
|
}
|
package/src/lib/constants.js
CHANGED
|
@@ -59,10 +59,10 @@ const RE_EMPTY_ATTRIBUTE = new RegExp(
|
|
|
59
59
|
// Inline element sets for whitespace handling
|
|
60
60
|
|
|
61
61
|
// Non-empty elements that will maintain whitespace around them
|
|
62
|
-
const inlineElementsToKeepWhitespaceAround = new Set(['a', 'abbr', 'acronym', 'audio', 'b', 'bdi', 'bdo', 'big', 'button', 'canvas', 'cite', 'code', 'data', 'del', 'dfn', 'em', 'embed', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'math', 'meter', 'nobr', 'object', 'output', 'picture', 'progress', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'selectedcontent', 'slot', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'tt', 'u', 'var', 'video', 'wbr']);
|
|
62
|
+
const inlineElementsToKeepWhitespaceAround = new Set(['a', 'abbr', 'acronym', 'audio', 'b', 'bdi', 'bdo', 'big', 'button', 'canvas', 'cite', 'code', 'data', 'del', 'dfn', 'em', 'embed', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'math', 'meter', 'nobr', 'object', 'output', 'picture', 'progress', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'selectedcontent', 'slot', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'textpath', 'time', 'tspan', 'tt', 'u', 'var', 'video', 'wbr']);
|
|
63
63
|
|
|
64
64
|
// Non-empty elements that will maintain whitespace within them
|
|
65
|
-
const inlineElementsToKeepWhitespaceWithin = new Set(['a', 'abbr', 'acronym', 'b', 'big', 'data', 'del', 'em', 'font', 'i', 'ins', 'kbd', 'map', 'mark', 'nobr', 's', 'samp', 'slot', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'time', 'tt', 'u', 'var']);
|
|
65
|
+
const inlineElementsToKeepWhitespaceWithin = new Set(['a', 'abbr', 'acronym', 'b', 'big', 'data', 'del', 'em', 'font', 'i', 'ins', 'kbd', 'map', 'mark', 'nobr', 's', 'samp', 'slot', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'textpath', 'time', 'tspan', 'tt', 'u', 'var']);
|
|
66
66
|
|
|
67
67
|
// Text-level elements that keep whitespace between them and other elements under
|
|
68
68
|
// `collapseInlineTagWhitespace`
|
|
@@ -259,7 +259,7 @@ const keepScriptsMimetypes = new Set([
|
|
|
259
259
|
|
|
260
260
|
// Boolean attribute sets
|
|
261
261
|
|
|
262
|
-
const isSimpleBoolean = new Set(['allowfullscreen', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', 'default', '
|
|
262
|
+
const isSimpleBoolean = new Set(['allowfullscreen', 'alpha', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', 'default', 'defer', 'disabled', 'formnovalidate', 'headingreset', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'nomodule', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'selected', 'shadowrootclonable', 'shadowrootcustomelementregistry', 'shadowrootdelegatesfocus', 'shadowrootserializable', 'sortable', 'truespeed', 'typemustmatch']);
|
|
263
263
|
|
|
264
264
|
const isBooleanValue = new Set(['true', 'false']);
|
|
265
265
|
|
package/src/lib/fragments.js
CHANGED
|
@@ -11,6 +11,19 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const RE_WHITESPACE = /\s/;
|
|
14
|
+
const RE_NAME_CHAR = /[\w$-]/;
|
|
15
|
+
|
|
16
|
+
// Run on at most the eight characters ending in the word
|
|
17
|
+
const RE_BLOCK_KEYWORD = /(?:^|[^\w$.])(?:do|else|finally|try)$/;
|
|
18
|
+
|
|
19
|
+
// The last character before a `/` that starts a regular expression, not a division
|
|
20
|
+
const RE_BEFORE_REGEX = /[(,=:[!&|?{};+*%<>~^}-]/;
|
|
21
|
+
|
|
22
|
+
// Run on at most the eleven characters ending in the word
|
|
23
|
+
const RE_BEFORE_REGEX_KEYWORD = /(?:^|[^\w$.])(?:await|case|delete|do|else|in|instanceof|new|of|return|throw|typeof|void|yield)$/;
|
|
24
|
+
|
|
25
|
+
// Run on at most the eleven characters from the `@`
|
|
26
|
+
const RE_DROPPABLE_AT_RULE = /^@(?:charset|import|namespace)(?![\w-])/i;
|
|
14
27
|
|
|
15
28
|
// Any-character and negated-class bodies; without the `s` flag, `.` is itself a
|
|
16
29
|
// negated class, excluding line terminators
|
|
@@ -333,8 +346,268 @@ function replaceCustomFragments(value, fragments, replacer) {
|
|
|
333
346
|
return copied === 0 ? value : out + value.slice(copied);
|
|
334
347
|
}
|
|
335
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Index past the regular expression starting at the `/` at an index
|
|
351
|
+
* @param {string} source
|
|
352
|
+
* @param {number} index
|
|
353
|
+
* @returns {number} The index itself when the line ends before the expression does
|
|
354
|
+
*/
|
|
355
|
+
function skipRegex(source, index) {
|
|
356
|
+
let inClass = false;
|
|
357
|
+
for (let position = index + 1; position < source.length; position++) {
|
|
358
|
+
const current = source[position];
|
|
359
|
+
if (current === '\n' || current === '\r') return index;
|
|
360
|
+
if (current === '\\') position++;
|
|
361
|
+
else if (current === '[') inClass = true;
|
|
362
|
+
else if (current === ']') inClass = false;
|
|
363
|
+
else if (current === '/' && !inClass) return position + 1;
|
|
364
|
+
}
|
|
365
|
+
return index;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Index past the comment, string, or regular expression starting at an index
|
|
370
|
+
* @param {string} source
|
|
371
|
+
* @param {number} index
|
|
372
|
+
* @param {boolean} isJS
|
|
373
|
+
* @returns {number} The index itself when none starts there
|
|
374
|
+
*/
|
|
375
|
+
function skipCommentOrString(source, index, isJS) {
|
|
376
|
+
const char = source[index];
|
|
377
|
+
const next = source[index + 1];
|
|
378
|
+
|
|
379
|
+
if (char === '/' && next === '*') {
|
|
380
|
+
const end = source.indexOf('*/', index + 2);
|
|
381
|
+
return end === -1 ? source.length : end + 2;
|
|
382
|
+
}
|
|
383
|
+
if (isJS && char === '/' && next === '/') {
|
|
384
|
+
const end = source.indexOf('\n', index + 2);
|
|
385
|
+
return end === -1 ? source.length : end;
|
|
386
|
+
}
|
|
387
|
+
if (isJS && char === '/') return isRegexStart(source, index) ? skipRegex(source, index) : index;
|
|
388
|
+
if (char !== '"' && char !== '\'' && !(isJS && char === '`')) return index;
|
|
389
|
+
|
|
390
|
+
for (let position = index + 1; position < source.length; position++) {
|
|
391
|
+
const current = source[position];
|
|
392
|
+
if (current === '\\') position++;
|
|
393
|
+
else if (current === char) return position + 1;
|
|
394
|
+
else if (current === '\n' && char !== '`') return position;
|
|
395
|
+
}
|
|
396
|
+
return source.length;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Index of the last non-whitespace character before an index
|
|
401
|
+
* @param {string} source
|
|
402
|
+
* @param {number} index
|
|
403
|
+
* @returns {number} -1 when there is none
|
|
404
|
+
*/
|
|
405
|
+
function findPreviousToken(source, index) {
|
|
406
|
+
let position = index - 1;
|
|
407
|
+
while (position >= 0 && RE_WHITESPACE.test(source[position] ?? '')) position--;
|
|
408
|
+
return position;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Whether a `/` starts a regular expression rather than a division, going by what precedes
|
|
413
|
+
* it; after a `)`, which `if (a) /b/.test(c)` leaves open, it’s read as a division
|
|
414
|
+
* @param {string} source
|
|
415
|
+
* @param {number} slash
|
|
416
|
+
* @returns {boolean}
|
|
417
|
+
*/
|
|
418
|
+
function isRegexStart(source, slash) {
|
|
419
|
+
const before = findPreviousToken(source, slash);
|
|
420
|
+
if (before === -1) return true;
|
|
421
|
+
|
|
422
|
+
const char = source[before] ?? '';
|
|
423
|
+
// `++` and `--` can only end an operand here, as a literal cannot be incremented
|
|
424
|
+
if ((char === '+' || char === '-') && source[before - 1] === char) return false;
|
|
425
|
+
return RE_BEFORE_REGEX.test(char) || RE_BEFORE_REGEX_KEYWORD.test(source.slice(Math.max(before - 10, 0), before + 1));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Whether a block keyword ends at an index
|
|
430
|
+
* @param {string} source
|
|
431
|
+
* @param {number} index
|
|
432
|
+
* @returns {boolean}
|
|
433
|
+
*/
|
|
434
|
+
function isBlockKeywordEnd(source, index) {
|
|
435
|
+
return RE_BLOCK_KEYWORD.test(source.slice(Math.max(index - 7, 0), index + 1));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Whether a `{` opens a block rather than an object literal
|
|
440
|
+
* @param {string} source
|
|
441
|
+
* @param {number} brace
|
|
442
|
+
* @returns {boolean}
|
|
443
|
+
*/
|
|
444
|
+
function isBlockBrace(source, brace) {
|
|
445
|
+
const before = findPreviousToken(source, brace);
|
|
446
|
+
if (before === -1) return true;
|
|
447
|
+
|
|
448
|
+
const char = source[before] ?? '';
|
|
449
|
+
if (char === ';' || char === '{' || char === '}' || char === ')') return true;
|
|
450
|
+
if (char === '>') return source[before - 1] === '=';
|
|
451
|
+
return isBlockKeywordEnd(source, before);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Whether a colon ends a statement label or `default`, going by what precedes the name.
|
|
456
|
+
* Two expressions cannot follow each other, so a name after one of these tokens cannot
|
|
457
|
+
* be the middle of a ternary
|
|
458
|
+
* @param {string} source
|
|
459
|
+
* @param {number} colon
|
|
460
|
+
* @returns {boolean}
|
|
461
|
+
*/
|
|
462
|
+
function isLabelColon(source, colon) {
|
|
463
|
+
const end = findPreviousToken(source, colon);
|
|
464
|
+
let start = end;
|
|
465
|
+
while (start >= 0 && RE_NAME_CHAR.test(source[start] ?? '')) start--;
|
|
466
|
+
if (start === end) return false;
|
|
467
|
+
|
|
468
|
+
const before = findPreviousToken(source, start + 1);
|
|
469
|
+
const char = source[before];
|
|
470
|
+
// After `:`, any other name may be a nested ternary’s branch
|
|
471
|
+
if (char === ':') return source.slice(start + 1, end + 1) === 'default';
|
|
472
|
+
return before === -1 || char === ';' || char === '}' || char === ')' || (char === '{' && isBlockBrace(source, before));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Whether the `case` keyword starts at an index, rather than a name or property holding it
|
|
477
|
+
* @param {string} source
|
|
478
|
+
* @param {number} index
|
|
479
|
+
* @returns {boolean}
|
|
480
|
+
*/
|
|
481
|
+
function isCaseKeyword(source, index) {
|
|
482
|
+
if (!source.startsWith('case', index) || RE_NAME_CHAR.test(source[index + 4] ?? '')) return false;
|
|
483
|
+
|
|
484
|
+
const before = source[index - 1] ?? '';
|
|
485
|
+
if (before === '.' || RE_NAME_CHAR.test(before)) return false;
|
|
486
|
+
|
|
487
|
+
let after = index + 4;
|
|
488
|
+
while (RE_WHITESPACE.test(source[after] ?? '')) after++;
|
|
489
|
+
return source[after] !== ':';
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Indexes of the colons after which a JavaScript statement starts
|
|
494
|
+
* @param {string} source
|
|
495
|
+
* @returns {Set<number>}
|
|
496
|
+
*/
|
|
497
|
+
function findStatementColons(source) {
|
|
498
|
+
const colons = new Set();
|
|
499
|
+
// Unpaired `?` in the current `case` expression, -1 outside of one
|
|
500
|
+
let ternaries = -1;
|
|
501
|
+
|
|
502
|
+
for (let i = 0; i < source.length; i++) {
|
|
503
|
+
const end = skipCommentOrString(source, i, true);
|
|
504
|
+
if (end > i) {
|
|
505
|
+
i = end - 1;
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const char = source[i];
|
|
510
|
+
if (ternaries === -1) {
|
|
511
|
+
if (char === ':') {
|
|
512
|
+
if (isLabelColon(source, i)) colons.add(i);
|
|
513
|
+
} else if (char === 'c' && isCaseKeyword(source, i)) {
|
|
514
|
+
ternaries = 0;
|
|
515
|
+
i += 3;
|
|
516
|
+
}
|
|
517
|
+
} else if (char === '?') {
|
|
518
|
+
// `?.` and `??` have no `:` to pair with
|
|
519
|
+
if (source[i + 1] === '.' || source[i + 1] === '?') i++;
|
|
520
|
+
else ternaries++;
|
|
521
|
+
} else if (char === ':') {
|
|
522
|
+
if (ternaries === 0) colons.add(i);
|
|
523
|
+
ternaries--;
|
|
524
|
+
} else if (char === ';' || char === '{' || char === '}') {
|
|
525
|
+
ternaries = -1;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return colons;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Placeholders within `@charset`, `@import`, and `@namespace` statements, which
|
|
534
|
+
* Lightning CSS drops whole where it cannot read them, and `@charset` always
|
|
535
|
+
* @param {string} source
|
|
536
|
+
* @param {RegExp} placeholder
|
|
537
|
+
* @returns {string[]}
|
|
538
|
+
*/
|
|
539
|
+
function findDroppableAtRulePlaceholders(source, placeholder) {
|
|
540
|
+
const tokens = [];
|
|
541
|
+
let start = -1;
|
|
542
|
+
|
|
543
|
+
for (let i = 0; i <= source.length; i++) {
|
|
544
|
+
const end = skipCommentOrString(source, i, false);
|
|
545
|
+
if (end > i) {
|
|
546
|
+
i = end - 1;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const char = source[i];
|
|
551
|
+
if (start === -1) {
|
|
552
|
+
if (char === '@' && RE_DROPPABLE_AT_RULE.test(source.slice(i, i + 11))) start = i;
|
|
553
|
+
} else if (char === ';' || char === '{' || char === '}' || char === undefined) {
|
|
554
|
+
for (const match of source.slice(start, i).matchAll(placeholder)) tokens.push(match[0]);
|
|
555
|
+
start = -1;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return tokens;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Whether what comes before an index allows a statement or rule to start there
|
|
564
|
+
* @param {string} source
|
|
565
|
+
* @param {number} index
|
|
566
|
+
* @param {Set<number>} colons - Colons after which a statement starts
|
|
567
|
+
* @param {boolean} isJS
|
|
568
|
+
* @returns {boolean}
|
|
569
|
+
*/
|
|
570
|
+
function isCodeStart(source, index, colons, isJS) {
|
|
571
|
+
let position = index;
|
|
572
|
+
while (position > 0) {
|
|
573
|
+
const char = source[--position] ?? '';
|
|
574
|
+
if (char === '\n' || char === '\r' || char === ';' || char === '{' || char === '}') return true;
|
|
575
|
+
if (!RE_WHITESPACE.test(char)) {
|
|
576
|
+
return char === ')' || (char === '/' && source[position - 1] === '*') || colons.has(position) || (isJS && isBlockKeywordEnd(source, position));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Whether minified CSS or JavaScript can stand in for its source. A fragment starting a
|
|
584
|
+
* statement or rule can render as anything, or as nothing, while engines read it as an
|
|
585
|
+
* identifier or selector and join it with the code around it, or drop it; a fragment in
|
|
586
|
+
* an at-rule Lightning CSS cannot read would go with that at-rule
|
|
587
|
+
* @param {string} source
|
|
588
|
+
* @param {unknown} output - Anything but a string, from a minifier of one’s own, passes
|
|
589
|
+
* @param {RegExp} placeholder - Global pattern matching one fragment placeholder
|
|
590
|
+
* @param {boolean} [isJS] - Whether the code is JavaScript rather than CSS
|
|
591
|
+
* @returns {boolean}
|
|
592
|
+
*/
|
|
593
|
+
function isMinifiedCodeSafe(source, output, placeholder, isJS = false) {
|
|
594
|
+
if (typeof output !== 'string' || output === source) return true;
|
|
595
|
+
|
|
596
|
+
const matches = [...source.matchAll(placeholder)];
|
|
597
|
+
if (!matches.length) return true;
|
|
598
|
+
|
|
599
|
+
const colons = isJS ? findStatementColons(source) : new Set();
|
|
600
|
+
const starts = matches.filter(match => isCodeStart(source, match.index, colons, isJS));
|
|
601
|
+
const kept = new Set(Array.from(output.matchAll(placeholder), match => match[0]));
|
|
602
|
+
|
|
603
|
+
// Output of nothing but fragments only holds up where no label or `case` went with the code
|
|
604
|
+
if (starts.length && (colons.size || output.replace(placeholder, '').trim() || !starts.every(match => kept.has(match[0])))) return false;
|
|
605
|
+
return isJS || !source.includes('@') || findDroppableAtRulePlaceholders(source, placeholder).every(token => kept.has(token));
|
|
606
|
+
}
|
|
607
|
+
|
|
336
608
|
export {
|
|
337
609
|
toDelimitedFragment,
|
|
338
610
|
toFragment,
|
|
339
|
-
replaceCustomFragments
|
|
611
|
+
replaceCustomFragments,
|
|
612
|
+
isMinifiedCodeSafe
|
|
340
613
|
};
|