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/cjs/core.js
CHANGED
|
@@ -1487,8 +1487,8 @@ class DOMTokenList extends Array {
|
|
|
1487
1487
|
constructor(value, location) {
|
|
1488
1488
|
if (value && typeof value === "string") {
|
|
1489
1489
|
/* replace all whitespace with a single space for easier parsing */
|
|
1490
|
-
const
|
|
1491
|
-
const { tokens, locations } = parse(
|
|
1490
|
+
const normalized = value.replace(/[\t\r\n]/g, " ");
|
|
1491
|
+
const { tokens, locations } = parse(normalized, location);
|
|
1492
1492
|
super(...tokens);
|
|
1493
1493
|
this.locations = locations;
|
|
1494
1494
|
}
|
|
@@ -1891,10 +1891,13 @@ class HtmlElement extends DOMNode {
|
|
|
1891
1891
|
}
|
|
1892
1892
|
/**
|
|
1893
1893
|
* @internal
|
|
1894
|
+
*
|
|
1895
|
+
* @param namespace - If given it is appended to the tagName.
|
|
1894
1896
|
*/
|
|
1895
|
-
static fromTokens(startToken, endToken, parent, metaTable) {
|
|
1896
|
-
const
|
|
1897
|
-
|
|
1897
|
+
static fromTokens(startToken, endToken, parent, metaTable, namespace = "") {
|
|
1898
|
+
const name = startToken.data[2];
|
|
1899
|
+
const tagName = namespace ? `${namespace}:${name}` : name;
|
|
1900
|
+
if (!name) {
|
|
1898
1901
|
throw new Error("tagName cannot be empty");
|
|
1899
1902
|
}
|
|
1900
1903
|
const meta = metaTable ? metaTable.getMetaFor(tagName) : null;
|
|
@@ -2956,7 +2959,7 @@ var TRANSFORMER_API;
|
|
|
2956
2959
|
/** @public */
|
|
2957
2960
|
const name = "html-validate";
|
|
2958
2961
|
/** @public */
|
|
2959
|
-
const version = "6.
|
|
2962
|
+
const version = "6.6.1";
|
|
2960
2963
|
/** @public */
|
|
2961
2964
|
const homepage = "https://html-validate.org";
|
|
2962
2965
|
/** @public */
|
|
@@ -2995,6 +2998,42 @@ function parseSeverity(value) {
|
|
|
2995
2998
|
}
|
|
2996
2999
|
}
|
|
2997
3000
|
|
|
3001
|
+
function escape(value) {
|
|
3002
|
+
return value.replace(/'/g, "\\'");
|
|
3003
|
+
}
|
|
3004
|
+
function format(value, quote = false) {
|
|
3005
|
+
if (value === null) {
|
|
3006
|
+
return "null";
|
|
3007
|
+
}
|
|
3008
|
+
if (typeof value === "number") {
|
|
3009
|
+
return value.toString();
|
|
3010
|
+
}
|
|
3011
|
+
if (typeof value === "string") {
|
|
3012
|
+
return quote ? `'${escape(value)}'` : value;
|
|
3013
|
+
}
|
|
3014
|
+
if (Array.isArray(value)) {
|
|
3015
|
+
const content = value.map((it) => format(it, true)).join(", ");
|
|
3016
|
+
return `[ ${content} ]`;
|
|
3017
|
+
}
|
|
3018
|
+
if (typeof value === "object") {
|
|
3019
|
+
const content = Object.entries(value)
|
|
3020
|
+
.map(([key, nested]) => `${key}: ${format(nested, true)}`)
|
|
3021
|
+
.join(", ");
|
|
3022
|
+
return `{ ${content} }`;
|
|
3023
|
+
}
|
|
3024
|
+
return String(value);
|
|
3025
|
+
}
|
|
3026
|
+
/**
|
|
3027
|
+
* Replaces placeholder `{{ ... }}` with values from given object.
|
|
3028
|
+
*
|
|
3029
|
+
* @internal
|
|
3030
|
+
*/
|
|
3031
|
+
function interpolate(text, data) {
|
|
3032
|
+
return text.replace(/{{\s*([^\s]+)\s*}}/g, (match, key) => {
|
|
3033
|
+
return typeof data[key] !== "undefined" ? format(data[key]) : match;
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
|
|
2998
3037
|
const remapEvents = {
|
|
2999
3038
|
"tag:open": "tag:start",
|
|
3000
3039
|
"tag:close": "tag:end",
|
|
@@ -3129,7 +3168,8 @@ class Rule {
|
|
|
3129
3168
|
report(node, message, location, context) {
|
|
3130
3169
|
if (this.isEnabled() && (!node || node.ruleEnabled(this.name))) {
|
|
3131
3170
|
const where = this.findLocation({ node, location, event: this.event });
|
|
3132
|
-
|
|
3171
|
+
const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
|
|
3172
|
+
this.reporter.add(this, interpolated, this.severity, node, where, context);
|
|
3133
3173
|
}
|
|
3134
3174
|
}
|
|
3135
3175
|
findLocation(src) {
|
|
@@ -3654,22 +3694,21 @@ exports.TokenType = void 0;
|
|
|
3654
3694
|
(function (TokenType) {
|
|
3655
3695
|
TokenType[TokenType["UNICODE_BOM"] = 1] = "UNICODE_BOM";
|
|
3656
3696
|
TokenType[TokenType["WHITESPACE"] = 2] = "WHITESPACE";
|
|
3657
|
-
TokenType[TokenType["
|
|
3658
|
-
TokenType[TokenType["
|
|
3659
|
-
TokenType[TokenType["
|
|
3660
|
-
TokenType[TokenType["
|
|
3661
|
-
TokenType[TokenType["
|
|
3662
|
-
TokenType[TokenType["
|
|
3663
|
-
TokenType[TokenType["
|
|
3664
|
-
TokenType[TokenType["
|
|
3665
|
-
TokenType[TokenType["
|
|
3666
|
-
TokenType[TokenType["
|
|
3667
|
-
TokenType[TokenType["
|
|
3668
|
-
TokenType[TokenType["
|
|
3669
|
-
TokenType[TokenType["
|
|
3670
|
-
TokenType[TokenType["
|
|
3671
|
-
TokenType[TokenType["
|
|
3672
|
-
TokenType[TokenType["EOF"] = 18] = "EOF";
|
|
3697
|
+
TokenType[TokenType["DOCTYPE_OPEN"] = 3] = "DOCTYPE_OPEN";
|
|
3698
|
+
TokenType[TokenType["DOCTYPE_VALUE"] = 4] = "DOCTYPE_VALUE";
|
|
3699
|
+
TokenType[TokenType["DOCTYPE_CLOSE"] = 5] = "DOCTYPE_CLOSE";
|
|
3700
|
+
TokenType[TokenType["TAG_OPEN"] = 6] = "TAG_OPEN";
|
|
3701
|
+
TokenType[TokenType["TAG_CLOSE"] = 7] = "TAG_CLOSE";
|
|
3702
|
+
TokenType[TokenType["ATTR_NAME"] = 8] = "ATTR_NAME";
|
|
3703
|
+
TokenType[TokenType["ATTR_VALUE"] = 9] = "ATTR_VALUE";
|
|
3704
|
+
TokenType[TokenType["TEXT"] = 10] = "TEXT";
|
|
3705
|
+
TokenType[TokenType["TEMPLATING"] = 11] = "TEMPLATING";
|
|
3706
|
+
TokenType[TokenType["SCRIPT"] = 12] = "SCRIPT";
|
|
3707
|
+
TokenType[TokenType["STYLE"] = 13] = "STYLE";
|
|
3708
|
+
TokenType[TokenType["COMMENT"] = 14] = "COMMENT";
|
|
3709
|
+
TokenType[TokenType["CONDITIONAL"] = 15] = "CONDITIONAL";
|
|
3710
|
+
TokenType[TokenType["DIRECTIVE"] = 16] = "DIRECTIVE";
|
|
3711
|
+
TokenType[TokenType["EOF"] = 17] = "EOF";
|
|
3673
3712
|
})(exports.TokenType || (exports.TokenType = {}));
|
|
3674
3713
|
|
|
3675
3714
|
/* eslint-disable no-useless-escape */
|
|
@@ -3694,7 +3733,7 @@ const MATCH_SCRIPT_DATA = /^[^]*?(?=<\/script)/;
|
|
|
3694
3733
|
const MATCH_SCRIPT_END = /^<(\/)(script)/;
|
|
3695
3734
|
const MATCH_STYLE_DATA = /^[^]*?(?=<\/style)/;
|
|
3696
3735
|
const MATCH_STYLE_END = /^<(\/)(style)/;
|
|
3697
|
-
const MATCH_DIRECTIVE = /^<!--\s
|
|
3736
|
+
const MATCH_DIRECTIVE = /^<!--\s*(\[)html-validate-([a-z0-9-]+)\s*(.*?)(]?)\s*-->/;
|
|
3698
3737
|
const MATCH_COMMENT = /^<!--([^]*?)-->/;
|
|
3699
3738
|
const MATCH_CONDITIONAL = /^<!\[([^\]]*?)\]>/;
|
|
3700
3739
|
class InvalidTokenError extends Error {
|
|
@@ -3749,15 +3788,15 @@ class Lexer {
|
|
|
3749
3788
|
previousState = context.state;
|
|
3750
3789
|
previousLength = context.string.length;
|
|
3751
3790
|
}
|
|
3752
|
-
yield this.token(context, exports.TokenType.EOF);
|
|
3791
|
+
yield this.token(context, exports.TokenType.EOF, []);
|
|
3753
3792
|
}
|
|
3754
3793
|
token(context, type, data) {
|
|
3755
|
-
const size = data ? data[0].length : 0;
|
|
3794
|
+
const size = data.length > 0 ? data[0].length : 0;
|
|
3756
3795
|
const location = context.getLocation(size);
|
|
3757
3796
|
return {
|
|
3758
3797
|
type,
|
|
3759
3798
|
location,
|
|
3760
|
-
data:
|
|
3799
|
+
data: Array.from(data),
|
|
3761
3800
|
};
|
|
3762
3801
|
}
|
|
3763
3802
|
/* istanbul ignore next: used to provide a better error when an unhandled state happens */
|
|
@@ -3782,17 +3821,18 @@ class Lexer {
|
|
|
3782
3821
|
}
|
|
3783
3822
|
}
|
|
3784
3823
|
*match(context, tests, error) {
|
|
3785
|
-
let match = null;
|
|
3786
3824
|
const n = tests.length;
|
|
3787
3825
|
for (let i = 0; i < n; i++) {
|
|
3788
3826
|
const [regex, nextState, tokenType] = tests[i];
|
|
3789
|
-
|
|
3827
|
+
const match = regex ? context.string.match(regex) : [""];
|
|
3828
|
+
if (match) {
|
|
3790
3829
|
let token = null;
|
|
3791
3830
|
if (tokenType !== false) {
|
|
3792
|
-
|
|
3831
|
+
token = this.token(context, tokenType, match);
|
|
3832
|
+
yield token;
|
|
3793
3833
|
}
|
|
3794
3834
|
const state = this.evalNextState(nextState, token);
|
|
3795
|
-
context.consume(match
|
|
3835
|
+
context.consume(match, state);
|
|
3796
3836
|
this.enter(context, state, match);
|
|
3797
3837
|
return;
|
|
3798
3838
|
}
|
|
@@ -3839,18 +3879,19 @@ class Lexer {
|
|
|
3839
3879
|
*tokenizeTag(context) {
|
|
3840
3880
|
/* eslint-disable-next-line consistent-return -- exhaustive switch handled by typescript */
|
|
3841
3881
|
function nextState(token) {
|
|
3882
|
+
const tagCloseToken = token;
|
|
3842
3883
|
switch (context.contentModel) {
|
|
3843
3884
|
case ContentModel.TEXT:
|
|
3844
3885
|
return State.TEXT;
|
|
3845
3886
|
case ContentModel.SCRIPT:
|
|
3846
|
-
if (
|
|
3887
|
+
if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
|
|
3847
3888
|
return State.SCRIPT;
|
|
3848
3889
|
}
|
|
3849
3890
|
else {
|
|
3850
3891
|
return State.TEXT; /* <script/> (not legal but handle it anyway so the lexer doesn't choke on it) */
|
|
3851
3892
|
}
|
|
3852
3893
|
case ContentModel.STYLE:
|
|
3853
|
-
if (
|
|
3894
|
+
if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
|
|
3854
3895
|
return State.STYLE;
|
|
3855
3896
|
}
|
|
3856
3897
|
else {
|
|
@@ -9578,7 +9619,7 @@ const config$3 = {
|
|
|
9578
9619
|
"no-redundant-for": "error",
|
|
9579
9620
|
"no-redundant-role": "error",
|
|
9580
9621
|
"prefer-native-element": "error",
|
|
9581
|
-
"svg-focusable": "
|
|
9622
|
+
"svg-focusable": "off",
|
|
9582
9623
|
"text-content": "error",
|
|
9583
9624
|
"wcag/h30": "error",
|
|
9584
9625
|
"wcag/h32": "error",
|
|
@@ -9649,7 +9690,7 @@ const config$1 = {
|
|
|
9649
9690
|
"prefer-tbody": "error",
|
|
9650
9691
|
"script-element": "error",
|
|
9651
9692
|
"script-type": "error",
|
|
9652
|
-
"svg-focusable": "
|
|
9693
|
+
"svg-focusable": "off",
|
|
9653
9694
|
"text-content": "error",
|
|
9654
9695
|
"unrecognized-char-ref": "error",
|
|
9655
9696
|
void: "off",
|
|
@@ -10424,6 +10465,12 @@ class ParserError extends Error {
|
|
|
10424
10465
|
}
|
|
10425
10466
|
}
|
|
10426
10467
|
|
|
10468
|
+
function isAttrValueToken(token) {
|
|
10469
|
+
return Boolean(token && token.type === exports.TokenType.ATTR_VALUE);
|
|
10470
|
+
}
|
|
10471
|
+
function svgShouldRetainTag(foreignTagName, tagName) {
|
|
10472
|
+
return foreignTagName === "svg" && ["title", "desc"].includes(tagName);
|
|
10473
|
+
}
|
|
10427
10474
|
/**
|
|
10428
10475
|
* Parse HTML document into a DOM tree.
|
|
10429
10476
|
*
|
|
@@ -10436,6 +10483,7 @@ class Parser {
|
|
|
10436
10483
|
* @param config - Configuration
|
|
10437
10484
|
*/
|
|
10438
10485
|
constructor(config) {
|
|
10486
|
+
this.currentNamespace = "";
|
|
10439
10487
|
this.event = new EventHandler();
|
|
10440
10488
|
this.dom = null;
|
|
10441
10489
|
this.metaTable = config.getMetaTable();
|
|
@@ -10446,7 +10494,6 @@ class Parser {
|
|
|
10446
10494
|
* @param source - HTML markup.
|
|
10447
10495
|
* @returns DOM tree representing the HTML markup.
|
|
10448
10496
|
*/
|
|
10449
|
-
// eslint-disable-next-line complexity
|
|
10450
10497
|
parseHtml(source) {
|
|
10451
10498
|
var _a, _b, _c, _d;
|
|
10452
10499
|
if (typeof source === "string") {
|
|
@@ -10477,40 +10524,7 @@ class Parser {
|
|
|
10477
10524
|
let it = this.next(tokenStream);
|
|
10478
10525
|
while (!it.done) {
|
|
10479
10526
|
const token = it.value;
|
|
10480
|
-
|
|
10481
|
-
case exports.TokenType.UNICODE_BOM:
|
|
10482
|
-
/* ignore */
|
|
10483
|
-
break;
|
|
10484
|
-
case exports.TokenType.TAG_OPEN:
|
|
10485
|
-
this.consumeTag(source, token, tokenStream);
|
|
10486
|
-
break;
|
|
10487
|
-
case exports.TokenType.WHITESPACE:
|
|
10488
|
-
this.trigger("whitespace", {
|
|
10489
|
-
text: token.data[0],
|
|
10490
|
-
location: token.location,
|
|
10491
|
-
});
|
|
10492
|
-
this.appendText(token.data[0], token.location);
|
|
10493
|
-
break;
|
|
10494
|
-
case exports.TokenType.DIRECTIVE:
|
|
10495
|
-
this.consumeDirective(token);
|
|
10496
|
-
break;
|
|
10497
|
-
case exports.TokenType.CONDITIONAL:
|
|
10498
|
-
this.consumeConditional(token);
|
|
10499
|
-
break;
|
|
10500
|
-
case exports.TokenType.COMMENT:
|
|
10501
|
-
this.consumeComment(token);
|
|
10502
|
-
break;
|
|
10503
|
-
case exports.TokenType.DOCTYPE_OPEN:
|
|
10504
|
-
this.consumeDoctype(token, tokenStream);
|
|
10505
|
-
break;
|
|
10506
|
-
case exports.TokenType.TEXT:
|
|
10507
|
-
case exports.TokenType.TEMPLATING:
|
|
10508
|
-
this.appendText(token.data, token.location);
|
|
10509
|
-
break;
|
|
10510
|
-
case exports.TokenType.EOF:
|
|
10511
|
-
this.closeTree(source, token.location);
|
|
10512
|
-
break;
|
|
10513
|
-
}
|
|
10527
|
+
this.consume(source, token, tokenStream);
|
|
10514
10528
|
it = this.next(tokenStream);
|
|
10515
10529
|
}
|
|
10516
10530
|
/* resolve any dynamic meta element properties */
|
|
@@ -10557,13 +10571,50 @@ class Parser {
|
|
|
10557
10571
|
return Boolean(active.parent && active.parent.is(tagName) && meta.includes(active.tagName));
|
|
10558
10572
|
}
|
|
10559
10573
|
}
|
|
10574
|
+
/* eslint-disable-next-line complexity */
|
|
10575
|
+
consume(source, token, tokenStream) {
|
|
10576
|
+
switch (token.type) {
|
|
10577
|
+
case exports.TokenType.UNICODE_BOM:
|
|
10578
|
+
/* ignore */
|
|
10579
|
+
break;
|
|
10580
|
+
case exports.TokenType.TAG_OPEN:
|
|
10581
|
+
this.consumeTag(source, token, tokenStream);
|
|
10582
|
+
break;
|
|
10583
|
+
case exports.TokenType.WHITESPACE:
|
|
10584
|
+
this.trigger("whitespace", {
|
|
10585
|
+
text: token.data[0],
|
|
10586
|
+
location: token.location,
|
|
10587
|
+
});
|
|
10588
|
+
this.appendText(token.data[0], token.location);
|
|
10589
|
+
break;
|
|
10590
|
+
case exports.TokenType.DIRECTIVE:
|
|
10591
|
+
this.consumeDirective(token);
|
|
10592
|
+
break;
|
|
10593
|
+
case exports.TokenType.CONDITIONAL:
|
|
10594
|
+
this.consumeConditional(token);
|
|
10595
|
+
break;
|
|
10596
|
+
case exports.TokenType.COMMENT:
|
|
10597
|
+
this.consumeComment(token);
|
|
10598
|
+
break;
|
|
10599
|
+
case exports.TokenType.DOCTYPE_OPEN:
|
|
10600
|
+
this.consumeDoctype(token, tokenStream);
|
|
10601
|
+
break;
|
|
10602
|
+
case exports.TokenType.TEXT:
|
|
10603
|
+
case exports.TokenType.TEMPLATING:
|
|
10604
|
+
this.appendText(token.data[0], token.location);
|
|
10605
|
+
break;
|
|
10606
|
+
case exports.TokenType.EOF:
|
|
10607
|
+
this.closeTree(source, token.location);
|
|
10608
|
+
break;
|
|
10609
|
+
}
|
|
10610
|
+
}
|
|
10560
10611
|
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10561
10612
|
consumeTag(source, startToken, tokenStream) {
|
|
10562
10613
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_CLOSE, startToken.location));
|
|
10563
10614
|
const endToken = tokens.slice(-1)[0];
|
|
10564
10615
|
const closeOptional = this.closeOptional(startToken);
|
|
10565
10616
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
10566
|
-
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable);
|
|
10617
|
+
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable, this.currentNamespace);
|
|
10567
10618
|
const isStartTag = !startToken.data[1];
|
|
10568
10619
|
const isClosing = !isStartTag || node.closed !== exports.NodeClosed.Open;
|
|
10569
10620
|
const isForeign = node.meta && node.meta.foreign;
|
|
@@ -10668,6 +10719,15 @@ class Parser {
|
|
|
10668
10719
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_OPEN, errorLocation));
|
|
10669
10720
|
const [last] = tokens.slice(-1);
|
|
10670
10721
|
const [, tagClosed, tagName] = last.data;
|
|
10722
|
+
/* special case: svg <title> and <desc> should be intact as it affects accessibility */
|
|
10723
|
+
if (!tagClosed && svgShouldRetainTag(foreignTagName, tagName)) {
|
|
10724
|
+
const oldNamespace = this.currentNamespace;
|
|
10725
|
+
this.currentNamespace = "svg";
|
|
10726
|
+
this.consumeTag(source, last, tokenStream);
|
|
10727
|
+
this.consumeUntilMatchingTag(source, tokenStream, tagName);
|
|
10728
|
+
this.currentNamespace = oldNamespace;
|
|
10729
|
+
continue;
|
|
10730
|
+
}
|
|
10671
10731
|
/* keep going unless the new tag matches the foreign root element */
|
|
10672
10732
|
if (tagName !== foreignTagName) {
|
|
10673
10733
|
continue;
|
|
@@ -10700,15 +10760,15 @@ class Parser {
|
|
|
10700
10760
|
const keyLocation = this.getAttributeKeyLocation(token);
|
|
10701
10761
|
const valueLocation = this.getAttributeValueLocation(next);
|
|
10702
10762
|
const location = this.getAttributeLocation(token, next);
|
|
10703
|
-
const haveValue = next
|
|
10763
|
+
const haveValue = isAttrValueToken(next);
|
|
10704
10764
|
const attrData = {
|
|
10705
10765
|
key: token.data[1],
|
|
10706
10766
|
value: null,
|
|
10707
10767
|
quote: null,
|
|
10708
10768
|
};
|
|
10709
|
-
if (
|
|
10769
|
+
if (haveValue) {
|
|
10710
10770
|
const [, , value, quote] = next.data;
|
|
10711
|
-
attrData.value = value
|
|
10771
|
+
attrData.value = value;
|
|
10712
10772
|
attrData.quote = quote !== null && quote !== void 0 ? quote : null;
|
|
10713
10773
|
}
|
|
10714
10774
|
/* get callback to process attributes, default is to just return attribute
|
|
@@ -10787,12 +10847,16 @@ class Parser {
|
|
|
10787
10847
|
};
|
|
10788
10848
|
}
|
|
10789
10849
|
consumeDirective(token) {
|
|
10790
|
-
const directive = token.data
|
|
10791
|
-
|
|
10850
|
+
const [text, , action, directive, end] = token.data;
|
|
10851
|
+
if (end === "") {
|
|
10852
|
+
throw new Error(`Missing end bracket "]" on directive "${text}"`);
|
|
10853
|
+
}
|
|
10854
|
+
const match = directive.match(/^(.*?)(?:\s*(?:--|:)\s*(.*))?$/);
|
|
10855
|
+
/* istanbul ignore next: should not be possible, would be emitted as comment token */
|
|
10792
10856
|
if (!match) {
|
|
10793
|
-
throw new Error(`Failed to parse directive "${
|
|
10857
|
+
throw new Error(`Failed to parse directive "${text}"`);
|
|
10794
10858
|
}
|
|
10795
|
-
const [,
|
|
10859
|
+
const [, data, comment] = match;
|
|
10796
10860
|
this.trigger("directive", {
|
|
10797
10861
|
action,
|
|
10798
10862
|
data,
|
|
@@ -10835,7 +10899,8 @@ class Parser {
|
|
|
10835
10899
|
*/
|
|
10836
10900
|
consumeDoctype(startToken, tokenStream) {
|
|
10837
10901
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.DOCTYPE_CLOSE, startToken.location));
|
|
10838
|
-
|
|
10902
|
+
/* first token is the doctype, second is the closing ">" */
|
|
10903
|
+
const doctype = tokens[0];
|
|
10839
10904
|
const value = doctype.data[0];
|
|
10840
10905
|
this.dom.doctype = value;
|
|
10841
10906
|
this.trigger("doctype", {
|
|
@@ -10861,6 +10926,35 @@ class Parser {
|
|
|
10861
10926
|
}
|
|
10862
10927
|
throw new ParserError(errorLocation, `stream ended before ${exports.TokenType[search]} token was found`);
|
|
10863
10928
|
}
|
|
10929
|
+
/**
|
|
10930
|
+
* Consumes tokens until a matching close-tag is found. Tags are appended to
|
|
10931
|
+
* the document.
|
|
10932
|
+
*
|
|
10933
|
+
* @internal
|
|
10934
|
+
*/
|
|
10935
|
+
consumeUntilMatchingTag(source, tokenStream, searchTag) {
|
|
10936
|
+
let numOpen = 1;
|
|
10937
|
+
let it = this.next(tokenStream);
|
|
10938
|
+
while (!it.done) {
|
|
10939
|
+
const token = it.value;
|
|
10940
|
+
this.consume(source, token, tokenStream);
|
|
10941
|
+
if (token.type === exports.TokenType.TAG_OPEN) {
|
|
10942
|
+
const [, close, tagName] = token.data;
|
|
10943
|
+
if (tagName === searchTag) {
|
|
10944
|
+
if (close) {
|
|
10945
|
+
numOpen--;
|
|
10946
|
+
}
|
|
10947
|
+
else {
|
|
10948
|
+
numOpen++;
|
|
10949
|
+
}
|
|
10950
|
+
if (numOpen === 0) {
|
|
10951
|
+
return;
|
|
10952
|
+
}
|
|
10953
|
+
}
|
|
10954
|
+
}
|
|
10955
|
+
it = this.next(tokenStream);
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10864
10958
|
next(tokenStream) {
|
|
10865
10959
|
const it = tokenStream.next();
|
|
10866
10960
|
if (!it.done) {
|
|
@@ -10868,7 +10962,7 @@ class Parser {
|
|
|
10868
10962
|
this.trigger("token", {
|
|
10869
10963
|
location: token.location,
|
|
10870
10964
|
type: token.type,
|
|
10871
|
-
data:
|
|
10965
|
+
data: Array.from(token.data),
|
|
10872
10966
|
});
|
|
10873
10967
|
}
|
|
10874
10968
|
return it;
|
|
@@ -11128,11 +11222,12 @@ class Engine {
|
|
|
11128
11222
|
return lines;
|
|
11129
11223
|
}
|
|
11130
11224
|
dumpTokens(source) {
|
|
11225
|
+
var _a;
|
|
11131
11226
|
const lexer = new Lexer();
|
|
11132
11227
|
const lines = [];
|
|
11133
11228
|
for (const src of source) {
|
|
11134
11229
|
for (const token of lexer.tokenize(src)) {
|
|
11135
|
-
const data =
|
|
11230
|
+
const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
|
|
11136
11231
|
lines.push({
|
|
11137
11232
|
token: exports.TokenType[token.type],
|
|
11138
11233
|
data,
|