html-validate 6.4.0 → 6.6.1
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 +105 -27
- package/dist/cjs/core.js +176 -81
- 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 +105 -27
- package/dist/es/core.js +176 -81
- 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 +26 -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.1";
|
|
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 {
|
|
@@ -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,40 +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.consumeConditional(token);
|
|
10488
|
-
break;
|
|
10489
|
-
case TokenType.COMMENT:
|
|
10490
|
-
this.consumeComment(token);
|
|
10491
|
-
break;
|
|
10492
|
-
case TokenType.DOCTYPE_OPEN:
|
|
10493
|
-
this.consumeDoctype(token, tokenStream);
|
|
10494
|
-
break;
|
|
10495
|
-
case TokenType.TEXT:
|
|
10496
|
-
case TokenType.TEMPLATING:
|
|
10497
|
-
this.appendText(token.data, token.location);
|
|
10498
|
-
break;
|
|
10499
|
-
case TokenType.EOF:
|
|
10500
|
-
this.closeTree(source, token.location);
|
|
10501
|
-
break;
|
|
10502
|
-
}
|
|
10516
|
+
this.consume(source, token, tokenStream);
|
|
10503
10517
|
it = this.next(tokenStream);
|
|
10504
10518
|
}
|
|
10505
10519
|
/* resolve any dynamic meta element properties */
|
|
@@ -10546,13 +10560,50 @@ class Parser {
|
|
|
10546
10560
|
return Boolean(active.parent && active.parent.is(tagName) && meta.includes(active.tagName));
|
|
10547
10561
|
}
|
|
10548
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
|
+
}
|
|
10549
10600
|
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10550
10601
|
consumeTag(source, startToken, tokenStream) {
|
|
10551
10602
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, startToken.location));
|
|
10552
10603
|
const endToken = tokens.slice(-1)[0];
|
|
10553
10604
|
const closeOptional = this.closeOptional(startToken);
|
|
10554
10605
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
10555
|
-
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable);
|
|
10606
|
+
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable, this.currentNamespace);
|
|
10556
10607
|
const isStartTag = !startToken.data[1];
|
|
10557
10608
|
const isClosing = !isStartTag || node.closed !== NodeClosed.Open;
|
|
10558
10609
|
const isForeign = node.meta && node.meta.foreign;
|
|
@@ -10657,6 +10708,15 @@ class Parser {
|
|
|
10657
10708
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_OPEN, errorLocation));
|
|
10658
10709
|
const [last] = tokens.slice(-1);
|
|
10659
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
|
+
}
|
|
10660
10720
|
/* keep going unless the new tag matches the foreign root element */
|
|
10661
10721
|
if (tagName !== foreignTagName) {
|
|
10662
10722
|
continue;
|
|
@@ -10689,15 +10749,15 @@ class Parser {
|
|
|
10689
10749
|
const keyLocation = this.getAttributeKeyLocation(token);
|
|
10690
10750
|
const valueLocation = this.getAttributeValueLocation(next);
|
|
10691
10751
|
const location = this.getAttributeLocation(token, next);
|
|
10692
|
-
const haveValue = next
|
|
10752
|
+
const haveValue = isAttrValueToken(next);
|
|
10693
10753
|
const attrData = {
|
|
10694
10754
|
key: token.data[1],
|
|
10695
10755
|
value: null,
|
|
10696
10756
|
quote: null,
|
|
10697
10757
|
};
|
|
10698
|
-
if (
|
|
10758
|
+
if (haveValue) {
|
|
10699
10759
|
const [, , value, quote] = next.data;
|
|
10700
|
-
attrData.value = value
|
|
10760
|
+
attrData.value = value;
|
|
10701
10761
|
attrData.quote = quote !== null && quote !== void 0 ? quote : null;
|
|
10702
10762
|
}
|
|
10703
10763
|
/* get callback to process attributes, default is to just return attribute
|
|
@@ -10776,12 +10836,16 @@ class Parser {
|
|
|
10776
10836
|
};
|
|
10777
10837
|
}
|
|
10778
10838
|
consumeDirective(token) {
|
|
10779
|
-
const directive = token.data
|
|
10780
|
-
|
|
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 */
|
|
10781
10845
|
if (!match) {
|
|
10782
|
-
throw new Error(`Failed to parse directive "${
|
|
10846
|
+
throw new Error(`Failed to parse directive "${text}"`);
|
|
10783
10847
|
}
|
|
10784
|
-
const [,
|
|
10848
|
+
const [, data, comment] = match;
|
|
10785
10849
|
this.trigger("directive", {
|
|
10786
10850
|
action,
|
|
10787
10851
|
data,
|
|
@@ -10824,7 +10888,8 @@ class Parser {
|
|
|
10824
10888
|
*/
|
|
10825
10889
|
consumeDoctype(startToken, tokenStream) {
|
|
10826
10890
|
const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.DOCTYPE_CLOSE, startToken.location));
|
|
10827
|
-
|
|
10891
|
+
/* first token is the doctype, second is the closing ">" */
|
|
10892
|
+
const doctype = tokens[0];
|
|
10828
10893
|
const value = doctype.data[0];
|
|
10829
10894
|
this.dom.doctype = value;
|
|
10830
10895
|
this.trigger("doctype", {
|
|
@@ -10850,6 +10915,35 @@ class Parser {
|
|
|
10850
10915
|
}
|
|
10851
10916
|
throw new ParserError(errorLocation, `stream ended before ${TokenType[search]} token was found`);
|
|
10852
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
|
+
}
|
|
10853
10947
|
next(tokenStream) {
|
|
10854
10948
|
const it = tokenStream.next();
|
|
10855
10949
|
if (!it.done) {
|
|
@@ -10857,7 +10951,7 @@ class Parser {
|
|
|
10857
10951
|
this.trigger("token", {
|
|
10858
10952
|
location: token.location,
|
|
10859
10953
|
type: token.type,
|
|
10860
|
-
data:
|
|
10954
|
+
data: Array.from(token.data),
|
|
10861
10955
|
});
|
|
10862
10956
|
}
|
|
10863
10957
|
return it;
|
|
@@ -11117,11 +11211,12 @@ class Engine {
|
|
|
11117
11211
|
return lines;
|
|
11118
11212
|
}
|
|
11119
11213
|
dumpTokens(source) {
|
|
11214
|
+
var _a;
|
|
11120
11215
|
const lexer = new Lexer();
|
|
11121
11216
|
const lines = [];
|
|
11122
11217
|
for (const src of source) {
|
|
11123
11218
|
for (const token of lexer.tokenize(src)) {
|
|
11124
|
-
const data =
|
|
11219
|
+
const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
|
|
11125
11220
|
lines.push({
|
|
11126
11221
|
token: TokenType[token.type],
|
|
11127
11222
|
data,
|