html-validate 6.3.2 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/browser.d.ts +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +113 -26
- package/dist/cjs/core.js +194 -86
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/html-validate.js +48 -1
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/jest-lib.d.ts +1 -1
- package/dist/cjs/jest-lib.js.map +1 -1
- package/dist/cjs/jest.d.ts +2 -2
- package/dist/cjs/test-utils.d.ts +1 -1
- package/dist/cjs/test-utils.js.map +1 -1
- package/dist/es/browser.d.ts +1 -1
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +113 -26
- package/dist/es/core.js +194 -86
- package/dist/es/core.js.map +1 -1
- package/dist/es/html-validate.js +48 -1
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/index.d.ts +2 -2
- package/dist/es/jest-lib.d.ts +1 -1
- package/dist/es/jest-lib.js.map +1 -1
- package/dist/es/jest.d.ts +2 -2
- package/dist/es/test-utils.d.ts +1 -1
- package/dist/es/test-utils.js.map +1 -1
- package/package.json +25 -25
package/dist/es/core.js
CHANGED
|
@@ -1476,8 +1476,8 @@ class DOMTokenList extends Array {
|
|
|
1476
1476
|
constructor(value, location) {
|
|
1477
1477
|
if (value && typeof value === "string") {
|
|
1478
1478
|
/* replace all whitespace with a single space for easier parsing */
|
|
1479
|
-
const
|
|
1480
|
-
const { tokens, locations } = parse(
|
|
1479
|
+
const normalized = value.replace(/[\t\r\n]/g, " ");
|
|
1480
|
+
const { tokens, locations } = parse(normalized, location);
|
|
1481
1481
|
super(...tokens);
|
|
1482
1482
|
this.locations = locations;
|
|
1483
1483
|
}
|
|
@@ -1880,10 +1880,13 @@ class HtmlElement extends DOMNode {
|
|
|
1880
1880
|
}
|
|
1881
1881
|
/**
|
|
1882
1882
|
* @internal
|
|
1883
|
+
*
|
|
1884
|
+
* @param namespace - If given it is appended to the tagName.
|
|
1883
1885
|
*/
|
|
1884
|
-
static fromTokens(startToken, endToken, parent, metaTable) {
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1886
|
+
static fromTokens(startToken, endToken, parent, metaTable, namespace = "") {
|
|
1887
|
+
const name = startToken.data[2];
|
|
1888
|
+
const tagName = namespace ? `${namespace}:${name}` : name;
|
|
1889
|
+
if (!name) {
|
|
1887
1890
|
throw new Error("tagName cannot be empty");
|
|
1888
1891
|
}
|
|
1889
1892
|
const meta = metaTable ? metaTable.getMetaFor(tagName) : null;
|
|
@@ -2945,7 +2948,7 @@ var TRANSFORMER_API;
|
|
|
2945
2948
|
/** @public */
|
|
2946
2949
|
const name = "html-validate";
|
|
2947
2950
|
/** @public */
|
|
2948
|
-
const version = "6.
|
|
2951
|
+
const version = "6.6.0";
|
|
2949
2952
|
/** @public */
|
|
2950
2953
|
const homepage = "https://html-validate.org";
|
|
2951
2954
|
/** @public */
|
|
@@ -2984,6 +2987,42 @@ function parseSeverity(value) {
|
|
|
2984
2987
|
}
|
|
2985
2988
|
}
|
|
2986
2989
|
|
|
2990
|
+
function escape(value) {
|
|
2991
|
+
return value.replace(/'/g, "\\'");
|
|
2992
|
+
}
|
|
2993
|
+
function format(value, quote = false) {
|
|
2994
|
+
if (value === null) {
|
|
2995
|
+
return "null";
|
|
2996
|
+
}
|
|
2997
|
+
if (typeof value === "number") {
|
|
2998
|
+
return value.toString();
|
|
2999
|
+
}
|
|
3000
|
+
if (typeof value === "string") {
|
|
3001
|
+
return quote ? `'${escape(value)}'` : value;
|
|
3002
|
+
}
|
|
3003
|
+
if (Array.isArray(value)) {
|
|
3004
|
+
const content = value.map((it) => format(it, true)).join(", ");
|
|
3005
|
+
return `[ ${content} ]`;
|
|
3006
|
+
}
|
|
3007
|
+
if (typeof value === "object") {
|
|
3008
|
+
const content = Object.entries(value)
|
|
3009
|
+
.map(([key, nested]) => `${key}: ${format(nested, true)}`)
|
|
3010
|
+
.join(", ");
|
|
3011
|
+
return `{ ${content} }`;
|
|
3012
|
+
}
|
|
3013
|
+
return String(value);
|
|
3014
|
+
}
|
|
3015
|
+
/**
|
|
3016
|
+
* Replaces placeholder `{{ ... }}` with values from given object.
|
|
3017
|
+
*
|
|
3018
|
+
* @internal
|
|
3019
|
+
*/
|
|
3020
|
+
function interpolate(text, data) {
|
|
3021
|
+
return text.replace(/{{\s*([^\s]+)\s*}}/g, (match, key) => {
|
|
3022
|
+
return typeof data[key] !== "undefined" ? format(data[key]) : match;
|
|
3023
|
+
});
|
|
3024
|
+
}
|
|
3025
|
+
|
|
2987
3026
|
const remapEvents = {
|
|
2988
3027
|
"tag:open": "tag:start",
|
|
2989
3028
|
"tag:close": "tag:end",
|
|
@@ -3118,7 +3157,8 @@ class Rule {
|
|
|
3118
3157
|
report(node, message, location, context) {
|
|
3119
3158
|
if (this.isEnabled() && (!node || node.ruleEnabled(this.name))) {
|
|
3120
3159
|
const where = this.findLocation({ node, location, event: this.event });
|
|
3121
|
-
|
|
3160
|
+
const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
|
|
3161
|
+
this.reporter.add(this, interpolated, this.severity, node, where, context);
|
|
3122
3162
|
}
|
|
3123
3163
|
}
|
|
3124
3164
|
findLocation(src) {
|
|
@@ -3643,22 +3683,21 @@ var TokenType;
|
|
|
3643
3683
|
(function (TokenType) {
|
|
3644
3684
|
TokenType[TokenType["UNICODE_BOM"] = 1] = "UNICODE_BOM";
|
|
3645
3685
|
TokenType[TokenType["WHITESPACE"] = 2] = "WHITESPACE";
|
|
3646
|
-
TokenType[TokenType["
|
|
3647
|
-
TokenType[TokenType["
|
|
3648
|
-
TokenType[TokenType["
|
|
3649
|
-
TokenType[TokenType["
|
|
3650
|
-
TokenType[TokenType["
|
|
3651
|
-
TokenType[TokenType["
|
|
3652
|
-
TokenType[TokenType["
|
|
3653
|
-
TokenType[TokenType["
|
|
3654
|
-
TokenType[TokenType["
|
|
3655
|
-
TokenType[TokenType["
|
|
3656
|
-
TokenType[TokenType["
|
|
3657
|
-
TokenType[TokenType["
|
|
3658
|
-
TokenType[TokenType["
|
|
3659
|
-
TokenType[TokenType["
|
|
3660
|
-
TokenType[TokenType["
|
|
3661
|
-
TokenType[TokenType["EOF"] = 18] = "EOF";
|
|
3686
|
+
TokenType[TokenType["DOCTYPE_OPEN"] = 3] = "DOCTYPE_OPEN";
|
|
3687
|
+
TokenType[TokenType["DOCTYPE_VALUE"] = 4] = "DOCTYPE_VALUE";
|
|
3688
|
+
TokenType[TokenType["DOCTYPE_CLOSE"] = 5] = "DOCTYPE_CLOSE";
|
|
3689
|
+
TokenType[TokenType["TAG_OPEN"] = 6] = "TAG_OPEN";
|
|
3690
|
+
TokenType[TokenType["TAG_CLOSE"] = 7] = "TAG_CLOSE";
|
|
3691
|
+
TokenType[TokenType["ATTR_NAME"] = 8] = "ATTR_NAME";
|
|
3692
|
+
TokenType[TokenType["ATTR_VALUE"] = 9] = "ATTR_VALUE";
|
|
3693
|
+
TokenType[TokenType["TEXT"] = 10] = "TEXT";
|
|
3694
|
+
TokenType[TokenType["TEMPLATING"] = 11] = "TEMPLATING";
|
|
3695
|
+
TokenType[TokenType["SCRIPT"] = 12] = "SCRIPT";
|
|
3696
|
+
TokenType[TokenType["STYLE"] = 13] = "STYLE";
|
|
3697
|
+
TokenType[TokenType["COMMENT"] = 14] = "COMMENT";
|
|
3698
|
+
TokenType[TokenType["CONDITIONAL"] = 15] = "CONDITIONAL";
|
|
3699
|
+
TokenType[TokenType["DIRECTIVE"] = 16] = "DIRECTIVE";
|
|
3700
|
+
TokenType[TokenType["EOF"] = 17] = "EOF";
|
|
3662
3701
|
})(TokenType || (TokenType = {}));
|
|
3663
3702
|
|
|
3664
3703
|
/* eslint-disable no-useless-escape */
|
|
@@ -3683,7 +3722,7 @@ const MATCH_SCRIPT_DATA = /^[^]*?(?=<\/script)/;
|
|
|
3683
3722
|
const MATCH_SCRIPT_END = /^<(\/)(script)/;
|
|
3684
3723
|
const MATCH_STYLE_DATA = /^[^]*?(?=<\/style)/;
|
|
3685
3724
|
const MATCH_STYLE_END = /^<(\/)(style)/;
|
|
3686
|
-
const MATCH_DIRECTIVE = /^<!--\s
|
|
3725
|
+
const MATCH_DIRECTIVE = /^<!--\s*(\[)html-validate-([a-z0-9-]+)\s*(.*?)(]?)\s*-->/;
|
|
3687
3726
|
const MATCH_COMMENT = /^<!--([^]*?)-->/;
|
|
3688
3727
|
const MATCH_CONDITIONAL = /^<!\[([^\]]*?)\]>/;
|
|
3689
3728
|
class InvalidTokenError extends Error {
|
|
@@ -3738,15 +3777,15 @@ class Lexer {
|
|
|
3738
3777
|
previousState = context.state;
|
|
3739
3778
|
previousLength = context.string.length;
|
|
3740
3779
|
}
|
|
3741
|
-
yield this.token(context, TokenType.EOF);
|
|
3780
|
+
yield this.token(context, TokenType.EOF, []);
|
|
3742
3781
|
}
|
|
3743
3782
|
token(context, type, data) {
|
|
3744
|
-
const size = data ? data[0].length : 0;
|
|
3783
|
+
const size = data.length > 0 ? data[0].length : 0;
|
|
3745
3784
|
const location = context.getLocation(size);
|
|
3746
3785
|
return {
|
|
3747
3786
|
type,
|
|
3748
3787
|
location,
|
|
3749
|
-
data:
|
|
3788
|
+
data: Array.from(data),
|
|
3750
3789
|
};
|
|
3751
3790
|
}
|
|
3752
3791
|
/* istanbul ignore next: used to provide a better error when an unhandled state happens */
|
|
@@ -3771,17 +3810,18 @@ class Lexer {
|
|
|
3771
3810
|
}
|
|
3772
3811
|
}
|
|
3773
3812
|
*match(context, tests, error) {
|
|
3774
|
-
let match = null;
|
|
3775
3813
|
const n = tests.length;
|
|
3776
3814
|
for (let i = 0; i < n; i++) {
|
|
3777
3815
|
const [regex, nextState, tokenType] = tests[i];
|
|
3778
|
-
|
|
3816
|
+
const match = regex ? context.string.match(regex) : [""];
|
|
3817
|
+
if (match) {
|
|
3779
3818
|
let token = null;
|
|
3780
3819
|
if (tokenType !== false) {
|
|
3781
|
-
|
|
3820
|
+
token = this.token(context, tokenType, match);
|
|
3821
|
+
yield token;
|
|
3782
3822
|
}
|
|
3783
3823
|
const state = this.evalNextState(nextState, token);
|
|
3784
|
-
context.consume(match
|
|
3824
|
+
context.consume(match, state);
|
|
3785
3825
|
this.enter(context, state, match);
|
|
3786
3826
|
return;
|
|
3787
3827
|
}
|
|
@@ -3828,18 +3868,19 @@ class Lexer {
|
|
|
3828
3868
|
*tokenizeTag(context) {
|
|
3829
3869
|
/* eslint-disable-next-line consistent-return -- exhaustive switch handled by typescript */
|
|
3830
3870
|
function nextState(token) {
|
|
3871
|
+
const tagCloseToken = token;
|
|
3831
3872
|
switch (context.contentModel) {
|
|
3832
3873
|
case ContentModel.TEXT:
|
|
3833
3874
|
return State.TEXT;
|
|
3834
3875
|
case ContentModel.SCRIPT:
|
|
3835
|
-
if (
|
|
3876
|
+
if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
|
|
3836
3877
|
return State.SCRIPT;
|
|
3837
3878
|
}
|
|
3838
3879
|
else {
|
|
3839
3880
|
return State.TEXT; /* <script/> (not legal but handle it anyway so the lexer doesn't choke on it) */
|
|
3840
3881
|
}
|
|
3841
3882
|
case ContentModel.STYLE:
|
|
3842
|
-
if (
|
|
3883
|
+
if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
|
|
3843
3884
|
return State.STYLE;
|
|
3844
3885
|
}
|
|
3845
3886
|
else {
|
|
@@ -5987,7 +6028,7 @@ class NoConditionalComment extends Rule {
|
|
|
5987
6028
|
}
|
|
5988
6029
|
setup() {
|
|
5989
6030
|
this.on("conditional", (event) => {
|
|
5990
|
-
this.report(
|
|
6031
|
+
this.report(event.parent, "Use of conditional comments are deprecated", event.location);
|
|
5991
6032
|
});
|
|
5992
6033
|
}
|
|
5993
6034
|
}
|
|
@@ -9567,7 +9608,7 @@ const config$3 = {
|
|
|
9567
9608
|
"no-redundant-for": "error",
|
|
9568
9609
|
"no-redundant-role": "error",
|
|
9569
9610
|
"prefer-native-element": "error",
|
|
9570
|
-
"svg-focusable": "
|
|
9611
|
+
"svg-focusable": "off",
|
|
9571
9612
|
"text-content": "error",
|
|
9572
9613
|
"wcag/h30": "error",
|
|
9573
9614
|
"wcag/h32": "error",
|
|
@@ -9638,7 +9679,7 @@ const config$1 = {
|
|
|
9638
9679
|
"prefer-tbody": "error",
|
|
9639
9680
|
"script-element": "error",
|
|
9640
9681
|
"script-type": "error",
|
|
9641
|
-
"svg-focusable": "
|
|
9682
|
+
"svg-focusable": "off",
|
|
9642
9683
|
"text-content": "error",
|
|
9643
9684
|
"unrecognized-char-ref": "error",
|
|
9644
9685
|
void: "off",
|
|
@@ -10413,6 +10454,12 @@ class ParserError extends Error {
|
|
|
10413
10454
|
}
|
|
10414
10455
|
}
|
|
10415
10456
|
|
|
10457
|
+
function isAttrValueToken(token) {
|
|
10458
|
+
return Boolean(token && token.type === TokenType.ATTR_VALUE);
|
|
10459
|
+
}
|
|
10460
|
+
function svgShouldRetainTag(foreignTagName, tagName) {
|
|
10461
|
+
return foreignTagName === "svg" && ["title", "desc"].includes(tagName);
|
|
10462
|
+
}
|
|
10416
10463
|
/**
|
|
10417
10464
|
* Parse HTML document into a DOM tree.
|
|
10418
10465
|
*
|
|
@@ -10425,6 +10472,7 @@ class Parser {
|
|
|
10425
10472
|
* @param config - Configuration
|
|
10426
10473
|
*/
|
|
10427
10474
|
constructor(config) {
|
|
10475
|
+
this.currentNamespace = "";
|
|
10428
10476
|
this.event = new EventHandler();
|
|
10429
10477
|
this.dom = null;
|
|
10430
10478
|
this.metaTable = config.getMetaTable();
|
|
@@ -10435,7 +10483,6 @@ class Parser {
|
|
|
10435
10483
|
* @param source - HTML markup.
|
|
10436
10484
|
* @returns DOM tree representing the HTML markup.
|
|
10437
10485
|
*/
|
|
10438
|
-
// eslint-disable-next-line complexity
|
|
10439
10486
|
parseHtml(source) {
|
|
10440
10487
|
var _a, _b, _c, _d;
|
|
10441
10488
|
if (typeof source === "string") {
|
|
@@ -10466,43 +10513,7 @@ class Parser {
|
|
|
10466
10513
|
let it = this.next(tokenStream);
|
|
10467
10514
|
while (!it.done) {
|
|
10468
10515
|
const token = it.value;
|
|
10469
|
-
|
|
10470
|
-
case TokenType.UNICODE_BOM:
|
|
10471
|
-
/* ignore */
|
|
10472
|
-
break;
|
|
10473
|
-
case TokenType.TAG_OPEN:
|
|
10474
|
-
this.consumeTag(source, token, tokenStream);
|
|
10475
|
-
break;
|
|
10476
|
-
case TokenType.WHITESPACE:
|
|
10477
|
-
this.trigger("whitespace", {
|
|
10478
|
-
text: token.data[0],
|
|
10479
|
-
location: token.location,
|
|
10480
|
-
});
|
|
10481
|
-
this.appendText(token.data[0], token.location);
|
|
10482
|
-
break;
|
|
10483
|
-
case TokenType.DIRECTIVE:
|
|
10484
|
-
this.consumeDirective(token);
|
|
10485
|
-
break;
|
|
10486
|
-
case TokenType.CONDITIONAL:
|
|
10487
|
-
this.trigger("conditional", {
|
|
10488
|
-
condition: token.data[1],
|
|
10489
|
-
location: token.location,
|
|
10490
|
-
});
|
|
10491
|
-
break;
|
|
10492
|
-
case TokenType.COMMENT:
|
|
10493
|
-
this.consumeComment(token);
|
|
10494
|
-
break;
|
|
10495
|
-
case TokenType.DOCTYPE_OPEN:
|
|
10496
|
-
this.consumeDoctype(token, tokenStream);
|
|
10497
|
-
break;
|
|
10498
|
-
case TokenType.TEXT:
|
|
10499
|
-
case TokenType.TEMPLATING:
|
|
10500
|
-
this.appendText(token.data, token.location);
|
|
10501
|
-
break;
|
|
10502
|
-
case TokenType.EOF:
|
|
10503
|
-
this.closeTree(source, token.location);
|
|
10504
|
-
break;
|
|
10505
|
-
}
|
|
10516
|
+
this.consume(source, token, tokenStream);
|
|
10506
10517
|
it = this.next(tokenStream);
|
|
10507
10518
|
}
|
|
10508
10519
|
/* resolve any dynamic meta element properties */
|
|
@@ -10549,13 +10560,50 @@ class Parser {
|
|
|
10549
10560
|
return Boolean(active.parent && active.parent.is(tagName) && meta.includes(active.tagName));
|
|
10550
10561
|
}
|
|
10551
10562
|
}
|
|
10563
|
+
/* eslint-disable-next-line complexity */
|
|
10564
|
+
consume(source, token, tokenStream) {
|
|
10565
|
+
switch (token.type) {
|
|
10566
|
+
case TokenType.UNICODE_BOM:
|
|
10567
|
+
/* ignore */
|
|
10568
|
+
break;
|
|
10569
|
+
case TokenType.TAG_OPEN:
|
|
10570
|
+
this.consumeTag(source, token, tokenStream);
|
|
10571
|
+
break;
|
|
10572
|
+
case TokenType.WHITESPACE:
|
|
10573
|
+
this.trigger("whitespace", {
|
|
10574
|
+
text: token.data[0],
|
|
10575
|
+
location: token.location,
|
|
10576
|
+
});
|
|
10577
|
+
this.appendText(token.data[0], token.location);
|
|
10578
|
+
break;
|
|
10579
|
+
case TokenType.DIRECTIVE:
|
|
10580
|
+
this.consumeDirective(token);
|
|
10581
|
+
break;
|
|
10582
|
+
case TokenType.CONDITIONAL:
|
|
10583
|
+
this.consumeConditional(token);
|
|
10584
|
+
break;
|
|
10585
|
+
case TokenType.COMMENT:
|
|
10586
|
+
this.consumeComment(token);
|
|
10587
|
+
break;
|
|
10588
|
+
case TokenType.DOCTYPE_OPEN:
|
|
10589
|
+
this.consumeDoctype(token, tokenStream);
|
|
10590
|
+
break;
|
|
10591
|
+
case TokenType.TEXT:
|
|
10592
|
+
case TokenType.TEMPLATING:
|
|
10593
|
+
this.appendText(token.data[0], token.location);
|
|
10594
|
+
break;
|
|
10595
|
+
case TokenType.EOF:
|
|
10596
|
+
this.closeTree(source, token.location);
|
|
10597
|
+
break;
|
|
10598
|
+
}
|
|
10599
|
+
}
|
|
10552
10600
|
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10553
10601
|
consumeTag(source, startToken, tokenStream) {
|
|
10554
10602
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, startToken.location));
|
|
10555
10603
|
const endToken = tokens.slice(-1)[0];
|
|
10556
10604
|
const closeOptional = this.closeOptional(startToken);
|
|
10557
10605
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
10558
|
-
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable);
|
|
10606
|
+
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable, this.currentNamespace);
|
|
10559
10607
|
const isStartTag = !startToken.data[1];
|
|
10560
10608
|
const isClosing = !isStartTag || node.closed !== NodeClosed.Open;
|
|
10561
10609
|
const isForeign = node.meta && node.meta.foreign;
|
|
@@ -10660,6 +10708,15 @@ class Parser {
|
|
|
10660
10708
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_OPEN, errorLocation));
|
|
10661
10709
|
const [last] = tokens.slice(-1);
|
|
10662
10710
|
const [, tagClosed, tagName] = last.data;
|
|
10711
|
+
/* special case: svg <title> and <desc> should be intact as it affects accessibility */
|
|
10712
|
+
if (!tagClosed && svgShouldRetainTag(foreignTagName, tagName)) {
|
|
10713
|
+
const oldNamespace = this.currentNamespace;
|
|
10714
|
+
this.currentNamespace = "svg";
|
|
10715
|
+
this.consumeTag(source, last, tokenStream);
|
|
10716
|
+
this.consumeUntilMatchingTag(source, tokenStream, tagName);
|
|
10717
|
+
this.currentNamespace = oldNamespace;
|
|
10718
|
+
continue;
|
|
10719
|
+
}
|
|
10663
10720
|
/* keep going unless the new tag matches the foreign root element */
|
|
10664
10721
|
if (tagName !== foreignTagName) {
|
|
10665
10722
|
continue;
|
|
@@ -10692,15 +10749,15 @@ class Parser {
|
|
|
10692
10749
|
const keyLocation = this.getAttributeKeyLocation(token);
|
|
10693
10750
|
const valueLocation = this.getAttributeValueLocation(next);
|
|
10694
10751
|
const location = this.getAttributeLocation(token, next);
|
|
10695
|
-
const haveValue = next
|
|
10752
|
+
const haveValue = isAttrValueToken(next);
|
|
10696
10753
|
const attrData = {
|
|
10697
10754
|
key: token.data[1],
|
|
10698
10755
|
value: null,
|
|
10699
10756
|
quote: null,
|
|
10700
10757
|
};
|
|
10701
|
-
if (
|
|
10758
|
+
if (haveValue) {
|
|
10702
10759
|
const [, , value, quote] = next.data;
|
|
10703
|
-
attrData.value = value
|
|
10760
|
+
attrData.value = value;
|
|
10704
10761
|
attrData.quote = quote !== null && quote !== void 0 ? quote : null;
|
|
10705
10762
|
}
|
|
10706
10763
|
/* get callback to process attributes, default is to just return attribute
|
|
@@ -10779,12 +10836,16 @@ class Parser {
|
|
|
10779
10836
|
};
|
|
10780
10837
|
}
|
|
10781
10838
|
consumeDirective(token) {
|
|
10782
|
-
const directive = token.data
|
|
10783
|
-
|
|
10839
|
+
const [text, , action, directive, end] = token.data;
|
|
10840
|
+
if (end === "") {
|
|
10841
|
+
throw new Error(`Missing end bracket "]" on directive "${text}"`);
|
|
10842
|
+
}
|
|
10843
|
+
const match = directive.match(/^(.*?)(?:\s*(?:--|:)\s*(.*))?$/);
|
|
10844
|
+
/* istanbul ignore next: should not be possible, would be emitted as comment token */
|
|
10784
10845
|
if (!match) {
|
|
10785
|
-
throw new Error(`Failed to parse directive "${
|
|
10846
|
+
throw new Error(`Failed to parse directive "${text}"`);
|
|
10786
10847
|
}
|
|
10787
|
-
const [,
|
|
10848
|
+
const [, data, comment] = match;
|
|
10788
10849
|
this.trigger("directive", {
|
|
10789
10850
|
action,
|
|
10790
10851
|
data,
|
|
@@ -10792,17 +10853,33 @@ class Parser {
|
|
|
10792
10853
|
location: token.location,
|
|
10793
10854
|
});
|
|
10794
10855
|
}
|
|
10856
|
+
/**
|
|
10857
|
+
* Consumes conditional comment in tag form.
|
|
10858
|
+
*
|
|
10859
|
+
* See also the related [[consumeCommend]] method.
|
|
10860
|
+
*/
|
|
10861
|
+
consumeConditional(token) {
|
|
10862
|
+
const element = this.dom.getActive();
|
|
10863
|
+
this.trigger("conditional", {
|
|
10864
|
+
condition: token.data[1],
|
|
10865
|
+
location: token.location,
|
|
10866
|
+
parent: element,
|
|
10867
|
+
});
|
|
10868
|
+
}
|
|
10795
10869
|
/**
|
|
10796
10870
|
* Consumes comment token.
|
|
10797
10871
|
*
|
|
10798
|
-
* Tries to find IE conditional comments and emits conditional token if
|
|
10872
|
+
* Tries to find IE conditional comments and emits conditional token if
|
|
10873
|
+
* found. See also the related [[consumeConditional]] method.
|
|
10799
10874
|
*/
|
|
10800
10875
|
consumeComment(token) {
|
|
10801
10876
|
const comment = token.data[0];
|
|
10877
|
+
const element = this.dom.getActive();
|
|
10802
10878
|
for (const conditional of parseConditionalComment(comment, token.location)) {
|
|
10803
10879
|
this.trigger("conditional", {
|
|
10804
10880
|
condition: conditional.expression,
|
|
10805
10881
|
location: conditional.location,
|
|
10882
|
+
parent: element,
|
|
10806
10883
|
});
|
|
10807
10884
|
}
|
|
10808
10885
|
}
|
|
@@ -10811,7 +10888,8 @@ class Parser {
|
|
|
10811
10888
|
*/
|
|
10812
10889
|
consumeDoctype(startToken, tokenStream) {
|
|
10813
10890
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.DOCTYPE_CLOSE, startToken.location));
|
|
10814
|
-
|
|
10891
|
+
/* first token is the doctype, second is the closing ">" */
|
|
10892
|
+
const doctype = tokens[0];
|
|
10815
10893
|
const value = doctype.data[0];
|
|
10816
10894
|
this.dom.doctype = value;
|
|
10817
10895
|
this.trigger("doctype", {
|
|
@@ -10837,6 +10915,35 @@ class Parser {
|
|
|
10837
10915
|
}
|
|
10838
10916
|
throw new ParserError(errorLocation, `stream ended before ${TokenType[search]} token was found`);
|
|
10839
10917
|
}
|
|
10918
|
+
/**
|
|
10919
|
+
* Consumes tokens until a matching close-tag is found. Tags are appended to
|
|
10920
|
+
* the document.
|
|
10921
|
+
*
|
|
10922
|
+
* @internal
|
|
10923
|
+
*/
|
|
10924
|
+
consumeUntilMatchingTag(source, tokenStream, searchTag) {
|
|
10925
|
+
let numOpen = 1;
|
|
10926
|
+
let it = this.next(tokenStream);
|
|
10927
|
+
while (!it.done) {
|
|
10928
|
+
const token = it.value;
|
|
10929
|
+
this.consume(source, token, tokenStream);
|
|
10930
|
+
if (token.type === TokenType.TAG_OPEN) {
|
|
10931
|
+
const [, close, tagName] = token.data;
|
|
10932
|
+
if (tagName === searchTag) {
|
|
10933
|
+
if (close) {
|
|
10934
|
+
numOpen--;
|
|
10935
|
+
}
|
|
10936
|
+
else {
|
|
10937
|
+
numOpen++;
|
|
10938
|
+
}
|
|
10939
|
+
if (numOpen === 0) {
|
|
10940
|
+
return;
|
|
10941
|
+
}
|
|
10942
|
+
}
|
|
10943
|
+
}
|
|
10944
|
+
it = this.next(tokenStream);
|
|
10945
|
+
}
|
|
10946
|
+
}
|
|
10840
10947
|
next(tokenStream) {
|
|
10841
10948
|
const it = tokenStream.next();
|
|
10842
10949
|
if (!it.done) {
|
|
@@ -10844,7 +10951,7 @@ class Parser {
|
|
|
10844
10951
|
this.trigger("token", {
|
|
10845
10952
|
location: token.location,
|
|
10846
10953
|
type: token.type,
|
|
10847
|
-
data:
|
|
10954
|
+
data: Array.from(token.data),
|
|
10848
10955
|
});
|
|
10849
10956
|
}
|
|
10850
10957
|
return it;
|
|
@@ -11104,11 +11211,12 @@ class Engine {
|
|
|
11104
11211
|
return lines;
|
|
11105
11212
|
}
|
|
11106
11213
|
dumpTokens(source) {
|
|
11214
|
+
var _a;
|
|
11107
11215
|
const lexer = new Lexer();
|
|
11108
11216
|
const lines = [];
|
|
11109
11217
|
for (const src of source) {
|
|
11110
11218
|
for (const token of lexer.tokenize(src)) {
|
|
11111
|
-
const data =
|
|
11219
|
+
const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
|
|
11112
11220
|
lines.push({
|
|
11113
11221
|
token: TokenType[token.type],
|
|
11114
11222
|
data,
|