html-minifier-next 8.1.1 → 8.1.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/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts +6 -1
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +2 -1
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/types/lib/whitespace.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/htmlminifier.js +21 -8
- package/src/htmlparser.js +164 -72
- package/src/lib/constants.js +34 -1
- package/src/lib/utils.js +4 -3
- package/src/lib/whitespace.js +4 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAeG,YAWG,iBAAiB,GAXV;IACR,KAAK,CAAC,WAAW;IACjB,GAAG,CAAC,WAAW;IACf,KAAK,CAAC,WAAW;IACjB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IACnB,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,SAAS,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;CACxC,CAAmB;AA0CvB,eAAO,MAAM,MAAM,QAA8C,CAAC;AAoJlE,qBAAa,UAAU;IAMd,IAAI;IACJ,OAAO;IANd;;;OAGG;IACH,YAAY,IAAI,EAHL,MAGK,EAAE,OAAO,EAFd,iBAEc,EAGxB;IAEK,KAAK,kBAouBV;CACF"}
|
|
@@ -13,6 +13,8 @@ declare const RE_TRAILING_SEMICOLON: RegExp;
|
|
|
13
13
|
declare const RE_AMP_ENTITY: RegExp;
|
|
14
14
|
declare const RE_LEGACY_ENTITIES: RegExp;
|
|
15
15
|
declare const RE_ESCAPE_LT: RegExp;
|
|
16
|
+
declare const RE_ESCAPE_LT_RAW_TEXT: Record<string, RegExp>;
|
|
17
|
+
declare const RE_HTML_ENCODING: RegExp;
|
|
16
18
|
declare const RE_ATTR_WS_CHECK: RegExp;
|
|
17
19
|
declare const RE_ATTR_WS_COLLAPSE: RegExp;
|
|
18
20
|
declare const RE_ATTR_WS_TRIM: RegExp;
|
|
@@ -97,5 +99,8 @@ declare const looseElements: Set<string>;
|
|
|
97
99
|
declare const trailingElements: Set<string>;
|
|
98
100
|
declare const htmlElements: Set<string>;
|
|
99
101
|
declare const specialContentElements: Set<string>;
|
|
100
|
-
|
|
102
|
+
declare const genericRawTextElements: Set<string>;
|
|
103
|
+
declare const escapableRawTextElements: Set<string>;
|
|
104
|
+
declare const rawTextElements: Set<string>;
|
|
105
|
+
export { RE_WS_START, RE_WS_END, RE_WS_ONLY, RE_WS_NBSP_END, RE_ALL_WS_NBSP, RE_NBSP_LEADING_GROUP, RE_NBSP_LEAD_GROUP, RE_NBSP_TRAILING_GROUP, RE_NBSP_TRAILING_STRIP, RE_EVENT_ATTR_DEFAULT, RE_CAN_REMOVE_ATTR_QUOTES, RE_TRAILING_SEMICOLON, RE_AMP_ENTITY, RE_LEGACY_ENTITIES, RE_ESCAPE_LT, RE_ESCAPE_LT_RAW_TEXT, RE_HTML_ENCODING, RE_ATTR_WS_CHECK, RE_ATTR_WS_COLLAPSE, RE_ATTR_WS_TRIM, RE_STYLE_ELEMENT, RE_EMPTY_ATTRIBUTE, inlineElementsToKeepWhitespaceAround, inlineElementsToKeepWhitespaceWithin, inlineElementsToKeepWhitespace, formControlElements, generalDefaults, tagDefaults, executableScriptsMimetypes, keepScriptsMimetypes, jsonScriptTypes, isSimpleBoolean, isBooleanValue, collapsibleValues, srcsetElements, optionalStartTags, optionalEndTags, headerElements, descriptionElements, pBlockElements, pInlineElements, rubyEndTagOmission, rubyRtcEndTagOmission, optionElements, tableContentElements, tableSectionElements, cellElements, topLevelElements, compactElements, looseElements, trailingElements, htmlElements, specialContentElements, genericRawTextElements, escapableRawTextElements, rawTextElements };
|
|
101
106
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"AAEA,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;AACnC,QAAA,MAAM,aAAa,QAAwB,CAAC;AAC5C,QAAA,MAAM,kBAAkB,QAA+oB,CAAC;AACxqB,QAAA,MAAM,YAAY,QAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"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"}
|
|
@@ -89,8 +89,9 @@ declare function describeQuantifierRisk(pattern: RegExp | string): string | null
|
|
|
89
89
|
* over quoted attribute values (which may contain `>`).
|
|
90
90
|
* @param {string} html
|
|
91
91
|
* @param {number} pos - Start position (just after the tag name)
|
|
92
|
+
* @param {number} [limit] - Position to stop the search at (exclusive)
|
|
92
93
|
* @returns {number} Index of the closing `>`, or -1 if not found
|
|
93
94
|
*/
|
|
94
|
-
declare function findTagEnd(html: string, pos: number): number;
|
|
95
|
+
declare function findTagEnd(html: string, pos: number, limit?: number): number;
|
|
95
96
|
export { stableStringify, findTagEnd, LRU, MAX_CACHE_ENTRY_SIZE, hashContent, uniqueId, identity, isThenable, lowercase, replaceAsync, parseRegExp, embedSource, lostFlag, describeQuantifierRisk };
|
|
96
97
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,iBAAS,eAAe,CAAC,GAAG,EAHjB,OAGiB,GAFf,MAAM,CAYlB;AAID,cAAM,GAAG;IAEA,KAAK;IACL,IAAI;IACJ,IAAI;IACT,mCAAmC;IAC9B,GAAG,EADG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAJjC,YAAY,KAAK,SAAM,EAMtB;IACD,0BAA0B;IAC1B,GAAG,CAAC,GAAG,EADK,MACL,WAUN;IACD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAHI,MAGJ,EAAE,KAAK,EAFH,OAEG,QAOb;IACD,0BAA0B;IAC1B,MAAM,CAAC,GAAG,EADE,MACF,QAA2B;IACrC,6EAA6E;IAC7E,KAAK,IADS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAGxE;CACF;AAKD,QAAA,MAAM,oBAAoB,QAAc,CAAC;AAIzC,0BAA0B;AAC1B,iBAAS,WAAW,CAAC,GAAG,EADZ,MACY,UAOvB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAMtB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAEtB;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAHd,OAGc,GAFZ,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAID;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,GAAG,EALpB,MAKoB,EAAE,KAAK,EAJ3B,MAI2B,EAAE,OAAO,UAAA,GAFlC,OAAO,CAAC,MAAM,CAAC,CAe3B;AAID,qCAAqC;AACrC,iBAAS,WAAW,CAAC,KAAK,EADd,MAAM,GAAG,MACK,sBAUzB;AAyYD;;;;;;;;;GASG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAJd,MAIc,GAHZ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAqDlC;AAED;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,OAAO,EAHjB,MAGiB,GAFf,MAAM,CAOlB;AAGE,YAA6D,MAAM,GAAzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAA;CAAC,CAE7D;AAqKD;;;;;GAKG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAL5B,MAAM,GAAG,MAKmB,GAH1B,MAAM,GAAG,IAAI,CAqBzB;AAED
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,iBAAS,eAAe,CAAC,GAAG,EAHjB,OAGiB,GAFf,MAAM,CAYlB;AAID,cAAM,GAAG;IAEA,KAAK;IACL,IAAI;IACJ,IAAI;IACT,mCAAmC;IAC9B,GAAG,EADG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAJjC,YAAY,KAAK,SAAM,EAMtB;IACD,0BAA0B;IAC1B,GAAG,CAAC,GAAG,EADK,MACL,WAUN;IACD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAHI,MAGJ,EAAE,KAAK,EAFH,OAEG,QAOb;IACD,0BAA0B;IAC1B,MAAM,CAAC,GAAG,EADE,MACF,QAA2B;IACrC,6EAA6E;IAC7E,KAAK,IADS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAGxE;CACF;AAKD,QAAA,MAAM,oBAAoB,QAAc,CAAC;AAIzC,0BAA0B;AAC1B,iBAAS,WAAW,CAAC,GAAG,EADZ,MACY,UAOvB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAMtB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAEtB;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAHd,OAGc,GAFZ,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAID;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,GAAG,EALpB,MAKoB,EAAE,KAAK,EAJ3B,MAI2B,EAAE,OAAO,UAAA,GAFlC,OAAO,CAAC,MAAM,CAAC,CAe3B;AAID,qCAAqC;AACrC,iBAAS,WAAW,CAAC,KAAK,EADd,MAAM,GAAG,MACK,sBAUzB;AAyYD;;;;;;;;;GASG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAJd,MAIc,GAHZ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAqDlC;AAED;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,OAAO,EAHjB,MAGiB,GAFf,MAAM,CAOlB;AAGE,YAA6D,MAAM,GAAzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAA;CAAC,CAE7D;AAqKD;;;;;GAKG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAL5B,MAAM,GAAG,MAKmB,GAH1B,MAAM,GAAG,IAAI,CAqBzB;AAED;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,IAAI,EALb,MAKa,EAAE,GAAG,EAJlB,MAIkB,EAAE,KAAK,AAHjC,CACA,EADQ,MAGuC,GAFrC,MAAM,CAelB;AAID,OAAO,EACL,eAAe,EACf,UAAU,EACV,GAAG,EACH,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,QAAQ,EACR,sBAAsB,EACvB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whitespace.d.ts","sourceRoot":"","sources":["../../../src/lib/whitespace.js"],"names":[],"mappings":"AAeA,0BAA0B;AAC1B,QAAA,MAAM,cAAc,QADR,MAAM,WAQjB,CAAC;AAIF,0BAA0B;AAC1B,iBAAS,qBAAqB,CAAC,GAAG,EADtB,MACsB,UAgBjC;AAID;;;;;;GAMG;AACH,iBAAS,kBAAkB,CAAC,GAAG,EANpB,MAMoB,EAAE,OAAO,EAL7B;IAAC,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAKxD,EAAE,QAAQ,EAJvC,OAIuC,EAAE,SAAS,EAHlD,OAGkD,EAAE,WAAW,AAFvE,CAA8B,EAAtB,OAEuE,UAqFjF;AAoBD;;;;;;;;;GASG;AACH,iBAAS,uBAAuB,CAAC,GAAG,EATzB,MASyB,EAAE,OAAO,EARlC,MAQkC,EAAE,OAAO,EAP3C,MAO2C,EAAE,SAAS,EANtD,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAMkB,EAAE,SAAS,EALjE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAK6B,EAAE,OAAO,EAJ1E;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAAC,2BAA2B,CAAC,EAAE,OAAO,CAAA;CAI1B,EAAE,cAAc,EAH1F,GAAG,CAAC,MAAM,CAGgF,EAAE,aAAa,EAFzG,GAAG,CAAC,MAAM,CAE+F,UAiEnH;
|
|
1
|
+
{"version":3,"file":"whitespace.d.ts","sourceRoot":"","sources":["../../../src/lib/whitespace.js"],"names":[],"mappings":"AAeA,0BAA0B;AAC1B,QAAA,MAAM,cAAc,QADR,MAAM,WAQjB,CAAC;AAIF,0BAA0B;AAC1B,iBAAS,qBAAqB,CAAC,GAAG,EADtB,MACsB,UAgBjC;AAID;;;;;;GAMG;AACH,iBAAS,kBAAkB,CAAC,GAAG,EANpB,MAMoB,EAAE,OAAO,EAL7B;IAAC,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAKxD,EAAE,QAAQ,EAJvC,OAIuC,EAAE,SAAS,EAHlD,OAGkD,EAAE,WAAW,AAFvE,CAA8B,EAAtB,OAEuE,UAqFjF;AAoBD;;;;;;;;;GASG;AACH,iBAAS,uBAAuB,CAAC,GAAG,EATzB,MASyB,EAAE,OAAO,EARlC,MAQkC,EAAE,OAAO,EAP3C,MAO2C,EAAE,SAAS,EANtD,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAMkB,EAAE,SAAS,EALjE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAK6B,EAAE,OAAO,EAJ1E;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAAC,2BAA2B,CAAC,EAAE,OAAO,CAAA;CAI1B,EAAE,cAAc,EAH1F,GAAG,CAAC,MAAM,CAGgF,EAAE,aAAa,EAFzG,GAAG,CAAC,MAAM,CAE+F,UAiEnH;AASD,0BAA0B;AAC1B,iBAAS,qBAAqB,CAAC,GAAG,EADtB,MACsB,WAEjC;AAED,0BAA0B;AAC1B,iBAAS,iBAAiB,CAAC,GAAG,EADlB,MACkB,WAE7B;AAID,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EAClB,CAAC"}
|
package/package.json
CHANGED
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@commitlint/cli": "^21.2.2",
|
|
18
18
|
"@eslint/js": "^10.0.1",
|
|
19
|
-
"@swc/core": "^1.16.
|
|
19
|
+
"@swc/core": "^1.16.1",
|
|
20
20
|
"@types/node": "^26.2.0",
|
|
21
|
-
"eslint": "^10.
|
|
21
|
+
"eslint": "^10.9.1",
|
|
22
22
|
"globals": "^17.11.0",
|
|
23
23
|
"typescript": "^7.0.2",
|
|
24
24
|
"vite": "^8.2.1"
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
},
|
|
96
96
|
"type": "module",
|
|
97
97
|
"types": "dist/types/htmlminifier.d.ts",
|
|
98
|
-
"version": "8.1.
|
|
98
|
+
"version": "8.1.2"
|
|
99
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -8,6 +8,8 @@ import { collectUsedSymbols } from './lib/unused-css.js';
|
|
|
8
8
|
import {
|
|
9
9
|
RE_LEGACY_ENTITIES,
|
|
10
10
|
RE_ESCAPE_LT,
|
|
11
|
+
RE_ESCAPE_LT_RAW_TEXT,
|
|
12
|
+
RE_HTML_ENCODING,
|
|
11
13
|
RE_WS_START,
|
|
12
14
|
RE_WS_END,
|
|
13
15
|
RE_WS_ONLY,
|
|
@@ -16,6 +18,7 @@ import {
|
|
|
16
18
|
inlineElementsToKeepWhitespaceAround,
|
|
17
19
|
inlineElementsToKeepWhitespaceWithin,
|
|
18
20
|
specialContentElements,
|
|
21
|
+
rawTextElements,
|
|
19
22
|
htmlElements,
|
|
20
23
|
optionalStartTags,
|
|
21
24
|
optionalEndTags,
|
|
@@ -586,8 +589,6 @@ const RE_HTML_COMMENT_START = /^\s*<!--/;
|
|
|
586
589
|
const RE_CLOSING_TAG_START = /^\s*<\/([a-zA-Z][\w:-]*)/;
|
|
587
590
|
const RE_LAST_HTML_TAG = /[\s\S]*<(\/?[a-zA-Z][\w:-]*)/;
|
|
588
591
|
|
|
589
|
-
// HTML encoding types for annotation-xml (MathML)
|
|
590
|
-
const RE_HTML_ENCODING = /^(text\/html|application\/xhtml\+xml)$/i;
|
|
591
592
|
|
|
592
593
|
// Probe for SVG/MathML elements—when the input has none (the common case),
|
|
593
594
|
// the per-tag foreign-content machinery can be skipped entirely
|
|
@@ -1123,6 +1124,14 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1123
1124
|
buffer.length = Math.max(0, index);
|
|
1124
1125
|
}
|
|
1125
1126
|
|
|
1127
|
+
// Whether the element being parsed holds text rather than markup. The namespace decides
|
|
1128
|
+
// this, not the name alone: `script` and `style` hold text wherever they stand, the rest
|
|
1129
|
+
// only as HTML elements—an SVG or MathML `title` holds markup like any foreign element
|
|
1130
|
+
function holdsRawText() {
|
|
1131
|
+
return specialContentElements.has(currentTag) ||
|
|
1132
|
+
(rawTextElements.has(currentTag) && !options.insideForeignContent);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1126
1135
|
// Whether whitespace-only text between tags survives minification
|
|
1127
1136
|
function keepsWhitespace() {
|
|
1128
1137
|
return !options.collapseWhitespace || Boolean(options.conservativeCollapse) || Boolean(options.preserveLineBreaks);
|
|
@@ -1348,7 +1357,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1348
1357
|
}
|
|
1349
1358
|
}
|
|
1350
1359
|
charsPrevTag = /^\s*$/.test(text) ? textPrevTag : 'comment';
|
|
1351
|
-
if (options.decodeEntities && text && !
|
|
1360
|
+
if (options.decodeEntities && text && !holdsRawText()) {
|
|
1352
1361
|
// Escape any `&` symbols that start either:
|
|
1353
1362
|
// 1. a legacy-named character reference (i.e., one that doesn’t end with `;`)
|
|
1354
1363
|
// 2. or any other character reference (i.e., one that does end with `;`)
|
|
@@ -1358,7 +1367,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1358
1367
|
text = text.replace(RE_LEGACY_ENTITIES, '&$1');
|
|
1359
1368
|
}
|
|
1360
1369
|
if (text.indexOf('<') !== -1) {
|
|
1361
|
-
text
|
|
1370
|
+
// Escapable raw text ends at its own end tag alone, so only that one needs escaping;
|
|
1371
|
+
// in foreign content the same element holds markup, where every `<` does
|
|
1372
|
+
const escapeLt = (options.insideForeignContent ? undefined : RE_ESCAPE_LT_RAW_TEXT[currentTag]) ?? RE_ESCAPE_LT;
|
|
1373
|
+
text = text.replace(escapeLt, '<');
|
|
1362
1374
|
}
|
|
1363
1375
|
}
|
|
1364
1376
|
if (uidPattern && options.collapseWhitespace && stackNoTrimWhitespace.length) {
|
|
@@ -1531,10 +1543,11 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1531
1543
|
options.removeTagWhitespace = false;
|
|
1532
1544
|
}
|
|
1533
1545
|
// `foreignObject` in SVG and `annotation-xml` in MathML contain HTML content
|
|
1534
|
-
//
|
|
1546
|
+
// (the element itself is in SVG/MathML namespace, only its children are HTML);
|
|
1547
|
+
// a repeated attribute is dropped after the first, so the first `encoding` decides
|
|
1535
1548
|
if (options.insideForeignContent && (lowerTag === 'foreignobject' ||
|
|
1536
|
-
(lowerTag === 'annotation-xml' &&
|
|
1537
|
-
|
|
1549
|
+
(lowerTag === 'annotation-xml' && RE_HTML_ENCODING.test(
|
|
1550
|
+
attrs.find((/** @type {HTMLAttribute} */ a) => a.name.toLowerCase() === 'encoding')?.value ?? '')))) {
|
|
1538
1551
|
const nameParent = options.name;
|
|
1539
1552
|
options = Object.create(options);
|
|
1540
1553
|
options.caseSensitive = false;
|
|
@@ -1778,7 +1791,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1778
1791
|
textNextAttrs = nextAttrs || [];
|
|
1779
1792
|
|
|
1780
1793
|
// Detect whether any async work is actually needed for this text node
|
|
1781
|
-
const needsDecode = options.decodeEntities && text && !
|
|
1794
|
+
const needsDecode = options.decodeEntities && text && !holdsRawText() && text.indexOf('&') !== -1;
|
|
1782
1795
|
const needsProcessScript = specialContentElements.has(currentTag) && (options.processScripts || hasJsonScriptType(currentAttrs));
|
|
1783
1796
|
const needsMinifyJS = options.minifyJS !== identity && isExecutableScript(currentTag, currentAttrs);
|
|
1784
1797
|
const isModuleScript = needsMinifyJS && currentAttrs.some(
|
package/src/htmlparser.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { isThenable, embedSource } from './lib/utils.js';
|
|
8
|
+
import { isThenable, embedSource, findTagEnd } from './lib/utils.js';
|
|
9
|
+
import { escapableRawTextElements, genericRawTextElements, RE_HTML_ENCODING } from './lib/constants.js';
|
|
9
10
|
|
|
10
11
|
/** @import { HTMLAttribute } from './lib/attributes.js' */
|
|
11
12
|
|
|
@@ -64,6 +65,7 @@ const qnameCapture = (function () {
|
|
|
64
65
|
return '((?:' + ncname + '\\:)?' + ncname + ')';
|
|
65
66
|
})();
|
|
66
67
|
const startTagOpen = new RegExp('^<' + qnameCapture);
|
|
68
|
+
const endTagOpen = new RegExp('^</' + qnameCapture);
|
|
67
69
|
export const endTag = new RegExp('^</' + qnameCapture + '[^>]*>');
|
|
68
70
|
|
|
69
71
|
let IS_REGEX_CAPTURING_BROKEN = false;
|
|
@@ -84,45 +86,70 @@ const fillAttrs = new Set(['checked', 'compact', 'declare', 'defer', 'disabled',
|
|
|
84
86
|
// Special elements (can contain anything)
|
|
85
87
|
const special = new Set(['script', 'style']);
|
|
86
88
|
|
|
89
|
+
// Elements whose children are HTML rather than foreign content, each only in
|
|
90
|
+
// the namespace it belongs to (`annotation-xml` is one, too, but only for some
|
|
91
|
+
// `encoding` values, so it is checked separately)
|
|
92
|
+
// https://html.spec.whatwg.org/multipage/parsing.html#html-integration-point
|
|
93
|
+
const svgIntegrationPoints = new Set(['foreignobject', 'desc', 'title']);
|
|
94
|
+
const mathIntegrationPoints = new Set(['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
95
|
+
|
|
87
96
|
// HTML elements, https://html.spec.whatwg.org/multipage/indices.html#elements-3
|
|
88
97
|
// Phrasing content, https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
|
|
89
|
-
const nonPhrasing = new Set(['address', 'article', 'aside', 'base', 'blockquote', 'body', 'caption', 'col', 'colgroup', 'dd', 'details', 'dialog', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'legend', 'li', 'menuitem', 'meta', 'ol', 'optgroup', 'option', 'param', 'rp', 'rt', 'source', 'style', 'summary', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul']);
|
|
90
|
-
|
|
91
|
-
const reCache = {};
|
|
98
|
+
const nonPhrasing = new Set(['address', 'article', 'aside', 'base', 'blockquote', 'body', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dialog', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'legend', 'li', 'listing', 'main', 'menu', 'menuitem', 'meta', 'nav', 'ol', 'optgroup', 'option', 'param', 'plaintext', 'pre', 'rp', 'rt', 'search', 'section', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'xmp']);
|
|
92
99
|
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
const
|
|
96
|
-
'
|
|
97
|
-
'style': /([\s\S]*?)<\/style[^>]*>/i,
|
|
98
|
-
'noscript': /([\s\S]*?)<\/noscript[^>]*>/i
|
|
99
|
-
};
|
|
100
|
+
// A tag name ends at whitespace, a slash, or the closing bracket, so `</scriptx>` names a
|
|
101
|
+
// different element and does not end this one
|
|
102
|
+
const endsTagName = (/** @type {string} */ character) =>
|
|
103
|
+
character === '' || character === '/' || character === '>' || /\s/.test(character);
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
/**
|
|
106
|
+
* @param {string} text - The input the tag stands in
|
|
107
|
+
* @param {number} nameEnd - Position right after the tag name
|
|
108
|
+
* @returns {number} Position of the `>` that ends the tag, or -1 where none follows
|
|
109
|
+
*/
|
|
110
|
+
function findEndTagClose(text, nameEnd) {
|
|
111
|
+
const close = text.indexOf('>', nameEnd);
|
|
112
|
+
if (close === -1) {
|
|
113
|
+
return -1;
|
|
114
|
+
}
|
|
115
|
+
// A quoted attribute value can hold a `>` that does not end the tag it stands in, so a
|
|
116
|
+
// quote ahead of that bracket calls for a scan that knows about quoting. It stops at the
|
|
117
|
+
// next `<`, which no tag reaches past: The scans of consecutive tags then never overlap,
|
|
118
|
+
// and an unquoted value that spans one leaves the first bracket as the end, as before.
|
|
119
|
+
for (let index = nameEnd; index < close; index++) {
|
|
120
|
+
const character = text[index];
|
|
121
|
+
if (character === '"' || character === "'") {
|
|
122
|
+
const nextOpen = text.indexOf('<', nameEnd);
|
|
123
|
+
const quoted = findTagEnd(text, nameEnd, nextOpen === -1 ? text.length : nextOpen);
|
|
124
|
+
return quoted === -1 ? close : quoted;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return close;
|
|
128
|
+
}
|
|
103
129
|
|
|
104
|
-
// O(n) helper: Strip all occurrences of `open…close` delimiters, keeping inner content
|
|
105
|
-
// Used instead of a regex replace to avoid O(n²) behavior on adversarial inputs
|
|
106
130
|
/**
|
|
107
|
-
* @param {string}
|
|
108
|
-
* @param {string}
|
|
109
|
-
* @
|
|
131
|
+
* @param {string} text - The input from the start of the content on
|
|
132
|
+
* @param {string} tag - Lowercase element name
|
|
133
|
+
* @returns {{content: string, length: number} | null} What the element holds and how far it
|
|
134
|
+
* reaches, or `null` where no end tag of its own follows
|
|
110
135
|
*/
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
let
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
i = end + close.length;
|
|
136
|
+
function findRawTextEnd(text, tag) {
|
|
137
|
+
const nameEnd = tag.length + 2;
|
|
138
|
+
for (let candidate = text.indexOf('</'); candidate !== -1; candidate = text.indexOf('</', candidate + 2)) {
|
|
139
|
+
if (text.slice(candidate + 2, candidate + nameEnd).toLowerCase() !== tag || !endsTagName(text.charAt(candidate + nameEnd))) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
// What follows the name is not part of it, and the tag ends at the next bracket outside
|
|
143
|
+
// its attribute values—where none follows, no later end tag can be complete, either
|
|
144
|
+
const end = findEndTagClose(text, candidate + nameEnd);
|
|
145
|
+
return end === -1 ? null : { content: text.slice(0, candidate), length: end + 1 };
|
|
122
146
|
}
|
|
123
|
-
return
|
|
147
|
+
return null;
|
|
124
148
|
}
|
|
125
149
|
|
|
150
|
+
// Cache for compiled attribute regexes per handler configuration
|
|
151
|
+
const attrRegexCache = new WeakMap();
|
|
152
|
+
|
|
126
153
|
/** @param {HTMLParserHandler} handler */
|
|
127
154
|
function buildAttrRegex(handler) {
|
|
128
155
|
const unquotedValue = handler.continueOnParseError ? singleAttrValueLenientUnquoted : singleAttrValues[2];
|
|
@@ -202,7 +229,7 @@ export class HTMLParser {
|
|
|
202
229
|
const fullHtml = this.html;
|
|
203
230
|
const fullLength = fullHtml.length;
|
|
204
231
|
|
|
205
|
-
/** @type {Array<{tag: string, lowerTag: string, attrs: HTMLAttribute[]}>} */
|
|
232
|
+
/** @type {Array<{tag: string, lowerTag: string, attrs: HTMLAttribute[], namespace: string}>} */
|
|
206
233
|
const stack = [];
|
|
207
234
|
/** @type {string} */
|
|
208
235
|
let lastTag = '';
|
|
@@ -225,7 +252,7 @@ export class HTMLParser {
|
|
|
225
252
|
const startTagOpenY = new RegExp(startTagOpen.source.slice(1), 'y');
|
|
226
253
|
// `\s*` with sticky flag is O(n) at worst—no retry from different positions possible
|
|
227
254
|
const startTagCloseY = /\s*(\/?)>/y;
|
|
228
|
-
const
|
|
255
|
+
const endTagOpenY = new RegExp(endTagOpen.source.slice(1), 'y');
|
|
229
256
|
const doctypeY = /<!DOCTYPE[^<>]+>/iy;
|
|
230
257
|
const commentTestY = /<!--/y;
|
|
231
258
|
const conditionalTestY = /<!\[/y;
|
|
@@ -238,11 +265,11 @@ export class HTMLParser {
|
|
|
238
265
|
let pos = 0;
|
|
239
266
|
let lastPos;
|
|
240
267
|
|
|
241
|
-
// An end tag always ends at a `>`; without one, the
|
|
242
|
-
//
|
|
243
|
-
// (e.g., `</aaaa…` with no `>`). Track the next `>` monotonically
|
|
244
|
-
// advances, so the aggregate scan work stays O(n)—and skip the
|
|
245
|
-
// lies ahead, where
|
|
268
|
+
// An end tag always ends at a `>`; without one ahead, matching the name only to find
|
|
269
|
+
// no bracket behind it repeats the name run at every position, which costs O(n²) on a
|
|
270
|
+
// long unclosed name (e.g., `</aaaa…` with no `>`). Track the next `>` monotonically—
|
|
271
|
+
// `pos` only advances, so the aggregate scan work stays O(n)—and skip the match when
|
|
272
|
+
// none lies ahead, where the tag could not be complete anyway.
|
|
246
273
|
let nextGtPos = fullHtml.indexOf('>');
|
|
247
274
|
const hasCloseAtOrAfter = (/** @type {number} */ p) => {
|
|
248
275
|
if (nextGtPos !== -1 && nextGtPos < p) {
|
|
@@ -251,6 +278,13 @@ export class HTMLParser {
|
|
|
251
278
|
return nextGtPos !== -1;
|
|
252
279
|
};
|
|
253
280
|
|
|
281
|
+
// A `-->` that the opener at `pos` does not find is not there for any later one, either,
|
|
282
|
+
// and the same holds for the `]>` of a downlevel-revealed conditional comment. `pos`
|
|
283
|
+
// only advances, so remembering the search that came up empty keeps the aggregate work
|
|
284
|
+
// O(n), where searching again at every opener rescans the rest of the input each time.
|
|
285
|
+
let commentEndAhead = true;
|
|
286
|
+
let conditionalEndAhead = true;
|
|
287
|
+
|
|
254
288
|
// Helper to advance position
|
|
255
289
|
const advance = (/** @type {number} */ n) => { pos += n; };
|
|
256
290
|
|
|
@@ -274,11 +308,50 @@ export class HTMLParser {
|
|
|
274
308
|
return fullHtml.slice(startPos);
|
|
275
309
|
};
|
|
276
310
|
|
|
311
|
+
// `annotation-xml` holds HTML only where its `encoding` says so; with any other value,
|
|
312
|
+
// and with none, its content stays MathML
|
|
313
|
+
const annotationHoldsHTML = (/** @type {{attrs?: HTMLAttribute[]}} */ entry) => {
|
|
314
|
+
for (const attr of entry.attrs ?? []) {
|
|
315
|
+
if (attr.name.toLowerCase() === 'encoding') {
|
|
316
|
+
return RE_HTML_ENCODING.test(attr.value ?? '');
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// Escapable raw text is an HTML rule, so what decides it is the namespace the element sits
|
|
323
|
+
// in. Walking down from the root resolves that: `<svg>`/`<math>` lead out of HTML, an
|
|
324
|
+
// integration point leads back in, and a name counts only in the namespace it belongs to
|
|
325
|
+
// (`title` is one in SVG but not in MathML, `annotation-xml` one in MathML but not in SVG)
|
|
326
|
+
/** @param {{lowerTag: string, namespace: string, attrs?: HTMLAttribute[]} | undefined} entry */
|
|
327
|
+
const namespaceInside = (entry) => {
|
|
328
|
+
if (!entry) {
|
|
329
|
+
return '';
|
|
330
|
+
}
|
|
331
|
+
const { namespace, lowerTag } = entry;
|
|
332
|
+
if (!namespace) {
|
|
333
|
+
return lowerTag === 'svg' || lowerTag === 'math' ? lowerTag : '';
|
|
334
|
+
}
|
|
335
|
+
if (namespace === 'svg') {
|
|
336
|
+
return svgIntegrationPoints.has(lowerTag) ? '' : namespace;
|
|
337
|
+
}
|
|
338
|
+
return mathIntegrationPoints.has(lowerTag) || (lowerTag === 'annotation-xml' && annotationHoldsHTML(entry)) ? '' : namespace;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// The element itself never decides this: `<svg><title>` is SVG, what it holds is HTML
|
|
342
|
+
const inForeignContent = () => Boolean(stack[stack.length - 1]?.namespace);
|
|
343
|
+
|
|
344
|
+
// Whether the content of the element being parsed is read as text rather than markup
|
|
345
|
+
// (`script` and `style` hold text in every namespace; the rest do so as HTML elements only)
|
|
346
|
+
const holdsRawText = (/** @type {string} */ tag) =>
|
|
347
|
+
special.has(tag) ||
|
|
348
|
+
((genericRawTextElements.has(tag) || escapableRawTextElements.has(tag)) && !inForeignContent());
|
|
349
|
+
|
|
277
350
|
while (pos < fullLength) {
|
|
278
351
|
lastPos = pos;
|
|
279
352
|
|
|
280
|
-
// Make sure not to be in
|
|
281
|
-
if (!lastTagLower || !
|
|
353
|
+
// Make sure not to be in an element whose content is text, not markup
|
|
354
|
+
if (!lastTagLower || !holdsRawText(lastTagLower)) {
|
|
282
355
|
const textEnd = fullHtml.indexOf('<', pos);
|
|
283
356
|
|
|
284
357
|
if (textEnd === pos) {
|
|
@@ -298,9 +371,9 @@ export class HTMLParser {
|
|
|
298
371
|
const endTagMatch = cachedNextEndTag.match;
|
|
299
372
|
cachedNextStartTag = null;
|
|
300
373
|
cachedNextEndTag = null;
|
|
301
|
-
advance(
|
|
302
|
-
await parseEndTag(endTagMatch
|
|
303
|
-
prevTag = '/' +
|
|
374
|
+
advance(endTagMatch.text.length);
|
|
375
|
+
await parseEndTag(endTagMatch.text, endTagMatch.name);
|
|
376
|
+
prevTag = '/' + endTagMatch.name.toLowerCase();
|
|
304
377
|
prevAttrs = [];
|
|
305
378
|
continue;
|
|
306
379
|
}
|
|
@@ -310,7 +383,8 @@ export class HTMLParser {
|
|
|
310
383
|
// Comment
|
|
311
384
|
commentTestY.lastIndex = pos;
|
|
312
385
|
if (commentTestY.test(fullHtml)) {
|
|
313
|
-
const commentEnd = fullHtml.indexOf('-->', pos + 4);
|
|
386
|
+
const commentEnd = commentEndAhead ? fullHtml.indexOf('-->', pos + 4) : -1;
|
|
387
|
+
if (commentEnd === -1) commentEndAhead = false;
|
|
314
388
|
|
|
315
389
|
if (commentEnd >= 0) {
|
|
316
390
|
if (handler.comment) {
|
|
@@ -327,7 +401,8 @@ export class HTMLParser {
|
|
|
327
401
|
// https://web.archive.org/web/20241201212701/https://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
|
|
328
402
|
conditionalTestY.lastIndex = pos;
|
|
329
403
|
if (conditionalTestY.test(fullHtml)) {
|
|
330
|
-
const conditionalEnd = fullHtml.indexOf(']>', pos + 3);
|
|
404
|
+
const conditionalEnd = conditionalEndAhead ? fullHtml.indexOf(']>', pos + 3) : -1;
|
|
405
|
+
if (conditionalEnd === -1) conditionalEndAhead = false;
|
|
331
406
|
|
|
332
407
|
if (conditionalEnd >= 0) {
|
|
333
408
|
if (handler.comment) {
|
|
@@ -354,15 +429,14 @@ export class HTMLParser {
|
|
|
354
429
|
continue;
|
|
355
430
|
}
|
|
356
431
|
|
|
357
|
-
// End tag—skip when no `>` lies ahead: The
|
|
358
|
-
//
|
|
432
|
+
// End tag—skip when no `>` lies ahead: The tag cannot be complete, and matching
|
|
433
|
+
// its name at every position would cost O(n²) on a long unclosed name
|
|
359
434
|
if (hasCloseAtOrAfter(pos)) {
|
|
360
|
-
|
|
361
|
-
const endTagMatch = endTagY.exec(fullHtml);
|
|
435
|
+
const endTagMatch = matchEndTag(pos);
|
|
362
436
|
if (endTagMatch) {
|
|
363
|
-
advance(
|
|
364
|
-
await parseEndTag(endTagMatch
|
|
365
|
-
prevTag = '/' +
|
|
437
|
+
advance(endTagMatch.text.length);
|
|
438
|
+
await parseEndTag(endTagMatch.text, endTagMatch.name);
|
|
439
|
+
prevTag = '/' + endTagMatch.name.toLowerCase();
|
|
366
440
|
prevAttrs = [];
|
|
367
441
|
continue;
|
|
368
442
|
}
|
|
@@ -401,10 +475,9 @@ export class HTMLParser {
|
|
|
401
475
|
nextAttrs = extractAttrInfo(nextStartTagMatch.attrs);
|
|
402
476
|
cachedNextStartTag = { match: nextStartTagMatch, pos };
|
|
403
477
|
} else if (hasCloseAtOrAfter(pos)) {
|
|
404
|
-
|
|
405
|
-
const nextEndTagMatch = endTagY.exec(fullHtml);
|
|
478
|
+
const nextEndTagMatch = matchEndTag(pos);
|
|
406
479
|
if (nextEndTagMatch) {
|
|
407
|
-
nextTag = '/' + nextEndTagMatch
|
|
480
|
+
nextTag = '/' + nextEndTagMatch.name;
|
|
408
481
|
nextAttrs = [];
|
|
409
482
|
cachedNextEndTag = { match: nextEndTagMatch, pos };
|
|
410
483
|
} else {
|
|
@@ -425,32 +498,36 @@ export class HTMLParser {
|
|
|
425
498
|
prevAttrs = [];
|
|
426
499
|
} else {
|
|
427
500
|
const stackedTag = lastTagLower;
|
|
428
|
-
|
|
429
|
-
const reStackedTag = /** @type {Record<string, RegExp>} */ (preCompiledStackedTags)[stackedTag] || /** @type {Record<string, RegExp>} */ (reCache)[stackedTag] || (/** @type {Record<string, RegExp>} */ (reCache)[stackedTag] = new RegExp('([\\s\\S]*?)\\x3c/' + stackedTag + '[^>]*>', 'i'));
|
|
501
|
+
const isEscapableRawText = escapableRawTextElements.has(stackedTag);
|
|
430
502
|
|
|
431
503
|
const remaining = sliceFromPos(pos);
|
|
432
|
-
const
|
|
433
|
-
if (
|
|
434
|
-
|
|
435
|
-
if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
|
|
436
|
-
text = stripDelimited(stripDelimited(text, '<!--', '-->'), '<![CDATA[', ']]>');
|
|
437
|
-
}
|
|
504
|
+
const rawText = findRawTextEnd(remaining, stackedTag);
|
|
505
|
+
if (rawText) {
|
|
506
|
+
const text = rawText.content;
|
|
438
507
|
if (handler.chars) {
|
|
439
|
-
|
|
508
|
+
// Escapable raw text is a text node like any other, so the handler gets the tags
|
|
509
|
+
// around it—it collapses and trims whitespace by what stands next to the text
|
|
510
|
+
const result = isEscapableRawText
|
|
511
|
+
? handler.chars(text, stackedTag, '/' + stackedTag, prevAttrs, [])
|
|
512
|
+
: handler.chars(text);
|
|
440
513
|
if (isThenable(result)) await result;
|
|
441
514
|
}
|
|
442
515
|
// Advance HTML past the matched special tag content and its closing tag
|
|
443
|
-
advance(
|
|
516
|
+
advance(rawText.length);
|
|
444
517
|
await parseEndTag('</' + stackedTag + '>', stackedTag);
|
|
518
|
+
// What follows stands after that end tag, not after the start tag still named here—
|
|
519
|
+
// whitespace next to `textarea` stays verbatim only while the element is open
|
|
520
|
+
prevTag = '/' + stackedTag;
|
|
521
|
+
prevAttrs = [];
|
|
445
522
|
} else {
|
|
446
|
-
//
|
|
447
|
-
if (handler.
|
|
448
|
-
const result =
|
|
523
|
+
// Without an end tag of its own, the element holds the rest of the input as text
|
|
524
|
+
if (handler.chars && remaining) {
|
|
525
|
+
const result = isEscapableRawText
|
|
526
|
+
? handler.chars(remaining, stackedTag, '', prevAttrs, [])
|
|
527
|
+
: handler.chars(remaining);
|
|
449
528
|
if (isThenable(result)) await result;
|
|
450
|
-
advance(1);
|
|
451
|
-
} else {
|
|
452
|
-
break;
|
|
453
529
|
}
|
|
530
|
+
advance(remaining.length);
|
|
454
531
|
}
|
|
455
532
|
}
|
|
456
533
|
|
|
@@ -497,6 +574,21 @@ export class HTMLParser {
|
|
|
497
574
|
}).filter(attr => attr.name)); // Filter out invalid entries
|
|
498
575
|
}
|
|
499
576
|
|
|
577
|
+
/**
|
|
578
|
+
* @param {number} startPos
|
|
579
|
+
* @returns {{text: string, name: string} | null} The end tag standing there, or `null`
|
|
580
|
+
* where none is complete
|
|
581
|
+
*/
|
|
582
|
+
function matchEndTag(startPos) {
|
|
583
|
+
endTagOpenY.lastIndex = startPos;
|
|
584
|
+
const open = endTagOpenY.exec(fullHtml);
|
|
585
|
+
if (!open) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
const close = findEndTagClose(fullHtml, startPos + open[0].length);
|
|
589
|
+
return close === -1 ? null : { text: fullHtml.slice(startPos, close + 1), name: open[1] ?? '' };
|
|
590
|
+
}
|
|
591
|
+
|
|
500
592
|
function parseStartTag(/** @type {number} */ startPos) {
|
|
501
593
|
startTagOpenY.lastIndex = startPos;
|
|
502
594
|
const start = startTagOpenY.exec(fullHtml);
|
|
@@ -730,7 +822,7 @@ export class HTMLParser {
|
|
|
730
822
|
if (lowerTagName === 'col' && findTagInCurrentTable('colgroup') < 0) {
|
|
731
823
|
lastTag = 'colgroup';
|
|
732
824
|
lastTagLower = 'colgroup';
|
|
733
|
-
stack.push({ tag: lastTag, lowerTag: 'colgroup', attrs: [] });
|
|
825
|
+
stack.push({ tag: lastTag, lowerTag: 'colgroup', attrs: [], namespace: namespaceInside(stack[stack.length - 1]) });
|
|
734
826
|
if (handler.start) {
|
|
735
827
|
await handler.start(lastTag, [], false, '', true);
|
|
736
828
|
}
|
|
@@ -806,7 +898,7 @@ export class HTMLParser {
|
|
|
806
898
|
}));
|
|
807
899
|
|
|
808
900
|
if (!unary) {
|
|
809
|
-
stack.push({ tag: tagName, lowerTag: lowerTagName, attrs });
|
|
901
|
+
stack.push({ tag: tagName, lowerTag: lowerTagName, attrs, namespace: namespaceInside(stack[stack.length - 1]) });
|
|
810
902
|
lastTag = tagName;
|
|
811
903
|
lastTagLower = lowerTagName;
|
|
812
904
|
unarySlash = '';
|
package/src/lib/constants.js
CHANGED
|
@@ -15,6 +15,17 @@ const RE_TRAILING_SEMICOLON = /;$/;
|
|
|
15
15
|
const RE_AMP_ENTITY = /&(#?[0-9a-zA-Z]+;)/g;
|
|
16
16
|
const RE_LEGACY_ENTITIES = /&((?:Iacute|aacute|uacute|plusmn|Otilde|otilde|agrave|Agrave|Yacute|yacute|Oslash|oslash|atilde|Atilde|brvbar|ccedil|Ccedil|Ograve|curren|divide|eacute|Eacute|ograve|Oacute|egrave|Egrave|Ugrave|frac12|frac14|frac34|ugrave|oacute|iacute|Ntilde|ntilde|Uacute|middot|igrave|Igrave|iquest|Aacute|cedil|laquo|micro|iexcl|Icirc|icirc|acirc|Ucirc|Ecirc|ocirc|Ocirc|ecirc|ucirc|Aring|aring|AElig|aelig|acute|pound|raquo|Acirc|times|THORN|szlig|thorn|COPY|auml|ordf|ordm|Uuml|macr|uuml|Auml|ouml|Ouml|para|nbsp|euml|quot|QUOT|Euml|yuml|cent|sect|copy|sup1|sup2|sup3|iuml|Iuml|ETH|shy|reg|not|yen|amp|AMP|REG|uml|eth|deg|gt|GT|LT|lt)(?!;)|(?:#?[0-9a-zA-Z]+;))/g;
|
|
17
17
|
const RE_ESCAPE_LT = /</g;
|
|
18
|
+
// In escapable raw text only the element’s own end tag ends the element, so nothing else
|
|
19
|
+
// that `decodeEntities` decoded to a `<` needs escaping there
|
|
20
|
+
// (the record has no prototype, so a tag named after an inherited property—`constructor`,
|
|
21
|
+
// `__proto__`—finds nothing here and falls back to escaping every `<`)
|
|
22
|
+
const RE_ESCAPE_LT_RAW_TEXT = /** @type {Record<string, RegExp>} */ (Object.assign(Object.create(null), {
|
|
23
|
+
textarea: /<(?=\/textarea[\s/>])/gi,
|
|
24
|
+
title: /<(?=\/title[\s/>])/gi
|
|
25
|
+
}));
|
|
26
|
+
// Encodings that make an `annotation-xml` element hold HTML rather than MathML
|
|
27
|
+
// https://html.spec.whatwg.org/multipage/parsing.html#html-integration-point
|
|
28
|
+
const RE_HTML_ENCODING = /^(text\/html|application\/xhtml\+xml)$/i;
|
|
18
29
|
const RE_ATTR_WS_CHECK = /[ \n\r\t\f]/;
|
|
19
30
|
const RE_ATTR_WS_COLLAPSE = /[ \n\r\t\f]+/g;
|
|
20
31
|
const RE_ATTR_WS_TRIM = /^[ \n\r\t\f]+|[ \n\r\t\f]+$/g;
|
|
@@ -172,6 +183,23 @@ const htmlElements = new Set(['a', 'abbr', 'acronym', 'address', 'applet', 'area
|
|
|
172
183
|
|
|
173
184
|
const specialContentElements = new Set(['script', 'style']);
|
|
174
185
|
|
|
186
|
+
// Elements the parser reads as raw text as well, though only `script` and `style` are raw text
|
|
187
|
+
// elements by name—these are HTML’s alone, where `script` and `style` hold text in SVG, too
|
|
188
|
+
// https://html.spec.whatwg.org/multipage/parsing.html#generic-raw-text-element-parsing-algorithm
|
|
189
|
+
//
|
|
190
|
+
// (`noscript`, `noframes`, and `noembed` are raw text, too, but stay markup here: What they
|
|
191
|
+
// hold is markup to the UA that displays it—one without scripting or frames—so minifying it
|
|
192
|
+
// as markup keeps what that UA sees)
|
|
193
|
+
const genericRawTextElements = new Set(['iframe', 'xmp']);
|
|
194
|
+
|
|
195
|
+
// Escapable raw text elements, whose content is text rather than markup
|
|
196
|
+
// https://html.spec.whatwg.org/multipage/syntax.html#elements-2
|
|
197
|
+
const escapableRawTextElements = new Set(['textarea', 'title']);
|
|
198
|
+
|
|
199
|
+
// Raw text is read without resolving character references, so its text keeps them—unlike that
|
|
200
|
+
// of `textarea` and `title`, where they are resolved as they are anywhere else
|
|
201
|
+
const rawTextElements = new Set([...specialContentElements, ...genericRawTextElements]);
|
|
202
|
+
|
|
175
203
|
// Exports
|
|
176
204
|
|
|
177
205
|
export {
|
|
@@ -191,6 +219,8 @@ export {
|
|
|
191
219
|
RE_AMP_ENTITY,
|
|
192
220
|
RE_LEGACY_ENTITIES,
|
|
193
221
|
RE_ESCAPE_LT,
|
|
222
|
+
RE_ESCAPE_LT_RAW_TEXT,
|
|
223
|
+
RE_HTML_ENCODING,
|
|
194
224
|
RE_ATTR_WS_CHECK,
|
|
195
225
|
RE_ATTR_WS_COLLAPSE,
|
|
196
226
|
RE_ATTR_WS_TRIM,
|
|
@@ -240,5 +270,8 @@ export {
|
|
|
240
270
|
htmlElements,
|
|
241
271
|
|
|
242
272
|
// Special content elements
|
|
243
|
-
specialContentElements
|
|
273
|
+
specialContentElements,
|
|
274
|
+
genericRawTextElements,
|
|
275
|
+
escapableRawTextElements,
|
|
276
|
+
rawTextElements
|
|
244
277
|
};
|
package/src/lib/utils.js
CHANGED
|
@@ -814,17 +814,18 @@ function describeQuantifierRisk(pattern) {
|
|
|
814
814
|
* over quoted attribute values (which may contain `>`).
|
|
815
815
|
* @param {string} html
|
|
816
816
|
* @param {number} pos - Start position (just after the tag name)
|
|
817
|
+
* @param {number} [limit] - Position to stop the search at (exclusive)
|
|
817
818
|
* @returns {number} Index of the closing `>`, or -1 if not found
|
|
818
819
|
*/
|
|
819
|
-
function findTagEnd(html, pos) {
|
|
820
|
+
function findTagEnd(html, pos, limit = html.length) {
|
|
820
821
|
let i = pos;
|
|
821
|
-
while (i <
|
|
822
|
+
while (i < limit) {
|
|
822
823
|
const ch = html[i];
|
|
823
824
|
if (ch === '>') return i;
|
|
824
825
|
if (ch === '"' || ch === "'") {
|
|
825
826
|
const q = ch;
|
|
826
827
|
i++;
|
|
827
|
-
while (i <
|
|
828
|
+
while (i < limit && html[i] !== q) i++;
|
|
828
829
|
}
|
|
829
830
|
i++;
|
|
830
831
|
}
|
package/src/lib/whitespace.js
CHANGED
|
@@ -237,8 +237,10 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, op
|
|
|
237
237
|
|
|
238
238
|
// Collapse/trim whitespace for given tag
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
// `xmp`, `listing`, and `plaintext` render preformatted, as `pre` does
|
|
241
|
+
// https://html.spec.whatwg.org/multipage/rendering.html#the-page
|
|
242
|
+
const noCollapseWhitespaceTags = new Set(['script', 'style', 'pre', 'textarea', 'xmp', 'listing', 'plaintext']);
|
|
243
|
+
const noTrimWhitespaceTags = new Set(['pre', 'textarea', 'xmp', 'listing', 'plaintext']);
|
|
242
244
|
|
|
243
245
|
/** @param {string} tag */
|
|
244
246
|
function canCollapseWhitespace(tag) {
|