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/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.0";
|
|
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 {
|
|
@@ -5998,7 +6039,7 @@ class NoConditionalComment extends Rule {
|
|
|
5998
6039
|
}
|
|
5999
6040
|
setup() {
|
|
6000
6041
|
this.on("conditional", (event) => {
|
|
6001
|
-
this.report(
|
|
6042
|
+
this.report(event.parent, "Use of conditional comments are deprecated", event.location);
|
|
6002
6043
|
});
|
|
6003
6044
|
}
|
|
6004
6045
|
}
|
|
@@ -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,43 +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.trigger("conditional", {
|
|
10499
|
-
condition: token.data[1],
|
|
10500
|
-
location: token.location,
|
|
10501
|
-
});
|
|
10502
|
-
break;
|
|
10503
|
-
case exports.TokenType.COMMENT:
|
|
10504
|
-
this.consumeComment(token);
|
|
10505
|
-
break;
|
|
10506
|
-
case exports.TokenType.DOCTYPE_OPEN:
|
|
10507
|
-
this.consumeDoctype(token, tokenStream);
|
|
10508
|
-
break;
|
|
10509
|
-
case exports.TokenType.TEXT:
|
|
10510
|
-
case exports.TokenType.TEMPLATING:
|
|
10511
|
-
this.appendText(token.data, token.location);
|
|
10512
|
-
break;
|
|
10513
|
-
case exports.TokenType.EOF:
|
|
10514
|
-
this.closeTree(source, token.location);
|
|
10515
|
-
break;
|
|
10516
|
-
}
|
|
10527
|
+
this.consume(source, token, tokenStream);
|
|
10517
10528
|
it = this.next(tokenStream);
|
|
10518
10529
|
}
|
|
10519
10530
|
/* resolve any dynamic meta element properties */
|
|
@@ -10560,13 +10571,50 @@ class Parser {
|
|
|
10560
10571
|
return Boolean(active.parent && active.parent.is(tagName) && meta.includes(active.tagName));
|
|
10561
10572
|
}
|
|
10562
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
|
+
}
|
|
10563
10611
|
/* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
|
|
10564
10612
|
consumeTag(source, startToken, tokenStream) {
|
|
10565
10613
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_CLOSE, startToken.location));
|
|
10566
10614
|
const endToken = tokens.slice(-1)[0];
|
|
10567
10615
|
const closeOptional = this.closeOptional(startToken);
|
|
10568
10616
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
10569
|
-
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable);
|
|
10617
|
+
const node = HtmlElement.fromTokens(startToken, endToken, parent, this.metaTable, this.currentNamespace);
|
|
10570
10618
|
const isStartTag = !startToken.data[1];
|
|
10571
10619
|
const isClosing = !isStartTag || node.closed !== exports.NodeClosed.Open;
|
|
10572
10620
|
const isForeign = node.meta && node.meta.foreign;
|
|
@@ -10671,6 +10719,15 @@ class Parser {
|
|
|
10671
10719
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_OPEN, errorLocation));
|
|
10672
10720
|
const [last] = tokens.slice(-1);
|
|
10673
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
|
+
}
|
|
10674
10731
|
/* keep going unless the new tag matches the foreign root element */
|
|
10675
10732
|
if (tagName !== foreignTagName) {
|
|
10676
10733
|
continue;
|
|
@@ -10703,15 +10760,15 @@ class Parser {
|
|
|
10703
10760
|
const keyLocation = this.getAttributeKeyLocation(token);
|
|
10704
10761
|
const valueLocation = this.getAttributeValueLocation(next);
|
|
10705
10762
|
const location = this.getAttributeLocation(token, next);
|
|
10706
|
-
const haveValue = next
|
|
10763
|
+
const haveValue = isAttrValueToken(next);
|
|
10707
10764
|
const attrData = {
|
|
10708
10765
|
key: token.data[1],
|
|
10709
10766
|
value: null,
|
|
10710
10767
|
quote: null,
|
|
10711
10768
|
};
|
|
10712
|
-
if (
|
|
10769
|
+
if (haveValue) {
|
|
10713
10770
|
const [, , value, quote] = next.data;
|
|
10714
|
-
attrData.value = value
|
|
10771
|
+
attrData.value = value;
|
|
10715
10772
|
attrData.quote = quote !== null && quote !== void 0 ? quote : null;
|
|
10716
10773
|
}
|
|
10717
10774
|
/* get callback to process attributes, default is to just return attribute
|
|
@@ -10790,12 +10847,16 @@ class Parser {
|
|
|
10790
10847
|
};
|
|
10791
10848
|
}
|
|
10792
10849
|
consumeDirective(token) {
|
|
10793
|
-
const directive = token.data
|
|
10794
|
-
|
|
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 */
|
|
10795
10856
|
if (!match) {
|
|
10796
|
-
throw new Error(`Failed to parse directive "${
|
|
10857
|
+
throw new Error(`Failed to parse directive "${text}"`);
|
|
10797
10858
|
}
|
|
10798
|
-
const [,
|
|
10859
|
+
const [, data, comment] = match;
|
|
10799
10860
|
this.trigger("directive", {
|
|
10800
10861
|
action,
|
|
10801
10862
|
data,
|
|
@@ -10803,17 +10864,33 @@ class Parser {
|
|
|
10803
10864
|
location: token.location,
|
|
10804
10865
|
});
|
|
10805
10866
|
}
|
|
10867
|
+
/**
|
|
10868
|
+
* Consumes conditional comment in tag form.
|
|
10869
|
+
*
|
|
10870
|
+
* See also the related [[consumeCommend]] method.
|
|
10871
|
+
*/
|
|
10872
|
+
consumeConditional(token) {
|
|
10873
|
+
const element = this.dom.getActive();
|
|
10874
|
+
this.trigger("conditional", {
|
|
10875
|
+
condition: token.data[1],
|
|
10876
|
+
location: token.location,
|
|
10877
|
+
parent: element,
|
|
10878
|
+
});
|
|
10879
|
+
}
|
|
10806
10880
|
/**
|
|
10807
10881
|
* Consumes comment token.
|
|
10808
10882
|
*
|
|
10809
|
-
* Tries to find IE conditional comments and emits conditional token if
|
|
10883
|
+
* Tries to find IE conditional comments and emits conditional token if
|
|
10884
|
+
* found. See also the related [[consumeConditional]] method.
|
|
10810
10885
|
*/
|
|
10811
10886
|
consumeComment(token) {
|
|
10812
10887
|
const comment = token.data[0];
|
|
10888
|
+
const element = this.dom.getActive();
|
|
10813
10889
|
for (const conditional of parseConditionalComment(comment, token.location)) {
|
|
10814
10890
|
this.trigger("conditional", {
|
|
10815
10891
|
condition: conditional.expression,
|
|
10816
10892
|
location: conditional.location,
|
|
10893
|
+
parent: element,
|
|
10817
10894
|
});
|
|
10818
10895
|
}
|
|
10819
10896
|
}
|
|
@@ -10822,7 +10899,8 @@ class Parser {
|
|
|
10822
10899
|
*/
|
|
10823
10900
|
consumeDoctype(startToken, tokenStream) {
|
|
10824
10901
|
const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.DOCTYPE_CLOSE, startToken.location));
|
|
10825
|
-
|
|
10902
|
+
/* first token is the doctype, second is the closing ">" */
|
|
10903
|
+
const doctype = tokens[0];
|
|
10826
10904
|
const value = doctype.data[0];
|
|
10827
10905
|
this.dom.doctype = value;
|
|
10828
10906
|
this.trigger("doctype", {
|
|
@@ -10848,6 +10926,35 @@ class Parser {
|
|
|
10848
10926
|
}
|
|
10849
10927
|
throw new ParserError(errorLocation, `stream ended before ${exports.TokenType[search]} token was found`);
|
|
10850
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
|
+
}
|
|
10851
10958
|
next(tokenStream) {
|
|
10852
10959
|
const it = tokenStream.next();
|
|
10853
10960
|
if (!it.done) {
|
|
@@ -10855,7 +10962,7 @@ class Parser {
|
|
|
10855
10962
|
this.trigger("token", {
|
|
10856
10963
|
location: token.location,
|
|
10857
10964
|
type: token.type,
|
|
10858
|
-
data:
|
|
10965
|
+
data: Array.from(token.data),
|
|
10859
10966
|
});
|
|
10860
10967
|
}
|
|
10861
10968
|
return it;
|
|
@@ -11115,11 +11222,12 @@ class Engine {
|
|
|
11115
11222
|
return lines;
|
|
11116
11223
|
}
|
|
11117
11224
|
dumpTokens(source) {
|
|
11225
|
+
var _a;
|
|
11118
11226
|
const lexer = new Lexer();
|
|
11119
11227
|
const lines = [];
|
|
11120
11228
|
for (const src of source) {
|
|
11121
11229
|
for (const token of lexer.tokenize(src)) {
|
|
11122
|
-
const data =
|
|
11230
|
+
const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
|
|
11123
11231
|
lines.push({
|
|
11124
11232
|
token: exports.TokenType[token.type],
|
|
11125
11233
|
data,
|