html-validate 6.3.1 → 6.5.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/es/core.d.ts CHANGED
@@ -40,28 +40,95 @@ declare class EventHandler {
40
40
  declare enum TokenType {
41
41
  UNICODE_BOM = 1,
42
42
  WHITESPACE = 2,
43
- NEWLINE = 3,
44
- DOCTYPE_OPEN = 4,
45
- DOCTYPE_VALUE = 5,
46
- DOCTYPE_CLOSE = 6,
47
- TAG_OPEN = 7,
48
- TAG_CLOSE = 8,
49
- ATTR_NAME = 9,
50
- ATTR_VALUE = 10,
51
- TEXT = 11,
52
- TEMPLATING = 12,
53
- SCRIPT = 13,
54
- STYLE = 14,
55
- COMMENT = 15,
56
- CONDITIONAL = 16,
57
- DIRECTIVE = 17,
58
- EOF = 18
59
- }
60
- interface Token {
43
+ DOCTYPE_OPEN = 3,
44
+ DOCTYPE_VALUE = 4,
45
+ DOCTYPE_CLOSE = 5,
46
+ TAG_OPEN = 6,
47
+ TAG_CLOSE = 7,
48
+ ATTR_NAME = 8,
49
+ ATTR_VALUE = 9,
50
+ TEXT = 10,
51
+ TEMPLATING = 11,
52
+ SCRIPT = 12,
53
+ STYLE = 13,
54
+ COMMENT = 14,
55
+ CONDITIONAL = 15,
56
+ DIRECTIVE = 16,
57
+ EOF = 17
58
+ }
59
+ interface BaseToken {
61
60
  type: TokenType;
62
61
  location: Location;
63
- data?: any;
64
62
  }
63
+ interface UnicodeBOMToken extends BaseToken {
64
+ type: TokenType.UNICODE_BOM;
65
+ data: [bom: string];
66
+ }
67
+ interface WhitespaceToken extends BaseToken {
68
+ type: TokenType.WHITESPACE;
69
+ data: [text: string];
70
+ }
71
+ interface DoctypeOpenToken extends BaseToken {
72
+ type: TokenType.DOCTYPE_OPEN;
73
+ data: [text: string, tag: string];
74
+ }
75
+ interface DoctypeValueToken extends BaseToken {
76
+ type: TokenType.DOCTYPE_VALUE;
77
+ data: [text: string];
78
+ }
79
+ interface DoctypeCloseToken extends BaseToken {
80
+ type: TokenType.DOCTYPE_CLOSE;
81
+ data: [text: ">"];
82
+ }
83
+ interface TagOpenToken extends BaseToken {
84
+ type: TokenType.TAG_OPEN;
85
+ data: [text: string, close: "/" | "", tag: string];
86
+ }
87
+ interface TagCloseToken extends BaseToken {
88
+ type: TokenType.TAG_CLOSE;
89
+ data: [text: ">" | "/>"];
90
+ }
91
+ interface AttrNameToken extends BaseToken {
92
+ type: TokenType.ATTR_NAME;
93
+ data: [text: string, name: string];
94
+ }
95
+ interface AttrValueToken extends BaseToken {
96
+ type: TokenType.ATTR_VALUE;
97
+ data: [text: string, delimiter: string, value: string, quote?: '"' | "'"];
98
+ }
99
+ interface TextToken extends BaseToken {
100
+ type: TokenType.TEXT;
101
+ data: [text: string];
102
+ }
103
+ interface TemplatingToken extends BaseToken {
104
+ type: TokenType.TEMPLATING;
105
+ data: [text: string];
106
+ }
107
+ interface ScriptToken extends BaseToken {
108
+ type: TokenType.SCRIPT;
109
+ data: [text: string];
110
+ }
111
+ interface StyleToken extends BaseToken {
112
+ type: TokenType.STYLE;
113
+ data: [text: string];
114
+ }
115
+ interface CommentToken extends BaseToken {
116
+ type: TokenType.COMMENT;
117
+ data: [text: string, comment: string];
118
+ }
119
+ interface ConditionalToken extends BaseToken {
120
+ type: TokenType.CONDITIONAL;
121
+ data: [text: string, condition: string];
122
+ }
123
+ interface DirectiveToken extends BaseToken {
124
+ type: TokenType.DIRECTIVE;
125
+ data: [text: string, begin: "[", action: string, rest: string, end: "]" | ""];
126
+ }
127
+ interface EOFToken extends BaseToken {
128
+ type: TokenType.EOF;
129
+ data: [];
130
+ }
131
+ declare type Token = UnicodeBOMToken | WhitespaceToken | DoctypeOpenToken | DoctypeValueToken | DoctypeCloseToken | TagOpenToken | TagCloseToken | AttrNameToken | AttrValueToken | TextToken | TemplatingToken | ScriptToken | StyleToken | CommentToken | ConditionalToken | DirectiveToken | EOFToken;
65
132
 
66
133
  declare type TokenStream = IterableIterator<Token>;
67
134
 
@@ -502,6 +569,8 @@ interface ConditionalEvent extends Event {
502
569
  location: Location;
503
570
  /** Condition including markers. */
504
571
  condition: string;
572
+ /** The element containing the conditional, if any. */
573
+ parent: HtmlElement | null;
505
574
  }
506
575
  /**
507
576
  * Event emitted when html-validate directives `<!-- [html-validate-...] -->`
@@ -625,14 +694,14 @@ declare class Parser {
625
694
  * stack when is allowed to omit.
626
695
  */
627
696
  private closeOptional;
628
- protected consumeTag(source: Source, startToken: Token, tokenStream: TokenStream): void;
697
+ protected consumeTag(source: Source, startToken: TagOpenToken, tokenStream: TokenStream): void;
629
698
  protected closeElement(source: Source, node: HtmlElement | null, active: HtmlElement, location: Location): void;
630
699
  private processElement;
631
700
  /**
632
701
  * Discard tokens until the end tag for the foreign element is found.
633
702
  */
634
703
  protected discardForeignBody(source: Source, foreignTagName: string, tokenStream: TokenStream, errorLocation: Location): void;
635
- protected consumeAttribute(source: Source, node: HtmlElement, token: Token, next?: Token): void;
704
+ protected consumeAttribute(source: Source, node: HtmlElement, token: AttrNameToken, next?: Token): void;
636
705
  /**
637
706
  * Takes attribute key token an returns location.
638
707
  */
@@ -650,17 +719,24 @@ declare class Parser {
650
719
  * an aggregate location covering key, quotes if present and value.
651
720
  */
652
721
  private getAttributeLocation;
653
- protected consumeDirective(token: Token): void;
722
+ protected consumeDirective(token: DirectiveToken): void;
723
+ /**
724
+ * Consumes conditional comment in tag form.
725
+ *
726
+ * See also the related [[consumeCommend]] method.
727
+ */
728
+ protected consumeConditional(token: ConditionalToken): void;
654
729
  /**
655
730
  * Consumes comment token.
656
731
  *
657
- * Tries to find IE conditional comments and emits conditional token if found.
732
+ * Tries to find IE conditional comments and emits conditional token if
733
+ * found. See also the related [[consumeConditional]] method.
658
734
  */
659
- protected consumeComment(token: Token): void;
735
+ protected consumeComment(token: CommentToken): void;
660
736
  /**
661
737
  * Consumes doctype tokens. Emits doctype event.
662
738
  */
663
- protected consumeDoctype(startToken: Token, tokenStream: TokenStream): void;
739
+ protected consumeDoctype(startToken: DoctypeOpenToken, tokenStream: TokenStream): void;
664
740
  /**
665
741
  * Return a list of tokens found until the expected token was found.
666
742
  *
@@ -1145,7 +1221,7 @@ declare class HtmlElement extends DOMNode {
1145
1221
  /**
1146
1222
  * @internal
1147
1223
  */
1148
- static fromTokens(startToken: Token, endToken: Token, parent: HtmlElement | null, metaTable: MetaTable | null): HtmlElement;
1224
+ static fromTokens(startToken: TagOpenToken, endToken: TagCloseToken, parent: HtmlElement | null, metaTable: MetaTable | null): HtmlElement;
1149
1225
  /**
1150
1226
  * Returns annotated name if set or defaults to `<tagName>`.
1151
1227
  *
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 condensed = value.replace(/[\t\r\n ]+/g, " ");
1480
- const { tokens, locations } = parse(condensed, location);
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
  }
@@ -2945,7 +2945,7 @@ var TRANSFORMER_API;
2945
2945
  /** @public */
2946
2946
  const name = "html-validate";
2947
2947
  /** @public */
2948
- const version = "6.3.1";
2948
+ const version = "6.5.0";
2949
2949
  /** @public */
2950
2950
  const homepage = "https://html-validate.org";
2951
2951
  /** @public */
@@ -2984,6 +2984,42 @@ function parseSeverity(value) {
2984
2984
  }
2985
2985
  }
2986
2986
 
2987
+ function escape(value) {
2988
+ return value.replace(/'/g, "\\'");
2989
+ }
2990
+ function format(value, quote = false) {
2991
+ if (value === null) {
2992
+ return "null";
2993
+ }
2994
+ if (typeof value === "number") {
2995
+ return value.toString();
2996
+ }
2997
+ if (typeof value === "string") {
2998
+ return quote ? `'${escape(value)}'` : value;
2999
+ }
3000
+ if (Array.isArray(value)) {
3001
+ const content = value.map((it) => format(it, true)).join(", ");
3002
+ return `[ ${content} ]`;
3003
+ }
3004
+ if (typeof value === "object") {
3005
+ const content = Object.entries(value)
3006
+ .map(([key, nested]) => `${key}: ${format(nested, true)}`)
3007
+ .join(", ");
3008
+ return `{ ${content} }`;
3009
+ }
3010
+ return String(value);
3011
+ }
3012
+ /**
3013
+ * Replaces placeholder `{{ ... }}` with values from given object.
3014
+ *
3015
+ * @internal
3016
+ */
3017
+ function interpolate(text, data) {
3018
+ return text.replace(/{{\s*([^\s]+)\s*}}/g, (match, key) => {
3019
+ return typeof data[key] !== "undefined" ? format(data[key]) : match;
3020
+ });
3021
+ }
3022
+
2987
3023
  const remapEvents = {
2988
3024
  "tag:open": "tag:start",
2989
3025
  "tag:close": "tag:end",
@@ -3118,7 +3154,8 @@ class Rule {
3118
3154
  report(node, message, location, context) {
3119
3155
  if (this.isEnabled() && (!node || node.ruleEnabled(this.name))) {
3120
3156
  const where = this.findLocation({ node, location, event: this.event });
3121
- this.reporter.add(this, message, this.severity, node, where, context);
3157
+ const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
3158
+ this.reporter.add(this, interpolated, this.severity, node, where, context);
3122
3159
  }
3123
3160
  }
3124
3161
  findLocation(src) {
@@ -3225,7 +3262,8 @@ function ruleDocumentationUrl(filename) {
3225
3262
  const p = path.parse(filename);
3226
3263
  const root = path.join(distFolder, "rules");
3227
3264
  const rel = path.relative(root, path.join(p.dir, p.name));
3228
- return `${homepage}/rules/${rel}.html`;
3265
+ const normalized = rel.replace(/\\/g, "/");
3266
+ return `${homepage}/rules/${normalized}.html`;
3229
3267
  }
3230
3268
 
3231
3269
  const defaults$p = {
@@ -3642,22 +3680,21 @@ var TokenType;
3642
3680
  (function (TokenType) {
3643
3681
  TokenType[TokenType["UNICODE_BOM"] = 1] = "UNICODE_BOM";
3644
3682
  TokenType[TokenType["WHITESPACE"] = 2] = "WHITESPACE";
3645
- TokenType[TokenType["NEWLINE"] = 3] = "NEWLINE";
3646
- TokenType[TokenType["DOCTYPE_OPEN"] = 4] = "DOCTYPE_OPEN";
3647
- TokenType[TokenType["DOCTYPE_VALUE"] = 5] = "DOCTYPE_VALUE";
3648
- TokenType[TokenType["DOCTYPE_CLOSE"] = 6] = "DOCTYPE_CLOSE";
3649
- TokenType[TokenType["TAG_OPEN"] = 7] = "TAG_OPEN";
3650
- TokenType[TokenType["TAG_CLOSE"] = 8] = "TAG_CLOSE";
3651
- TokenType[TokenType["ATTR_NAME"] = 9] = "ATTR_NAME";
3652
- TokenType[TokenType["ATTR_VALUE"] = 10] = "ATTR_VALUE";
3653
- TokenType[TokenType["TEXT"] = 11] = "TEXT";
3654
- TokenType[TokenType["TEMPLATING"] = 12] = "TEMPLATING";
3655
- TokenType[TokenType["SCRIPT"] = 13] = "SCRIPT";
3656
- TokenType[TokenType["STYLE"] = 14] = "STYLE";
3657
- TokenType[TokenType["COMMENT"] = 15] = "COMMENT";
3658
- TokenType[TokenType["CONDITIONAL"] = 16] = "CONDITIONAL";
3659
- TokenType[TokenType["DIRECTIVE"] = 17] = "DIRECTIVE";
3660
- TokenType[TokenType["EOF"] = 18] = "EOF";
3683
+ TokenType[TokenType["DOCTYPE_OPEN"] = 3] = "DOCTYPE_OPEN";
3684
+ TokenType[TokenType["DOCTYPE_VALUE"] = 4] = "DOCTYPE_VALUE";
3685
+ TokenType[TokenType["DOCTYPE_CLOSE"] = 5] = "DOCTYPE_CLOSE";
3686
+ TokenType[TokenType["TAG_OPEN"] = 6] = "TAG_OPEN";
3687
+ TokenType[TokenType["TAG_CLOSE"] = 7] = "TAG_CLOSE";
3688
+ TokenType[TokenType["ATTR_NAME"] = 8] = "ATTR_NAME";
3689
+ TokenType[TokenType["ATTR_VALUE"] = 9] = "ATTR_VALUE";
3690
+ TokenType[TokenType["TEXT"] = 10] = "TEXT";
3691
+ TokenType[TokenType["TEMPLATING"] = 11] = "TEMPLATING";
3692
+ TokenType[TokenType["SCRIPT"] = 12] = "SCRIPT";
3693
+ TokenType[TokenType["STYLE"] = 13] = "STYLE";
3694
+ TokenType[TokenType["COMMENT"] = 14] = "COMMENT";
3695
+ TokenType[TokenType["CONDITIONAL"] = 15] = "CONDITIONAL";
3696
+ TokenType[TokenType["DIRECTIVE"] = 16] = "DIRECTIVE";
3697
+ TokenType[TokenType["EOF"] = 17] = "EOF";
3661
3698
  })(TokenType || (TokenType = {}));
3662
3699
 
3663
3700
  /* eslint-disable no-useless-escape */
@@ -3682,7 +3719,7 @@ const MATCH_SCRIPT_DATA = /^[^]*?(?=<\/script)/;
3682
3719
  const MATCH_SCRIPT_END = /^<(\/)(script)/;
3683
3720
  const MATCH_STYLE_DATA = /^[^]*?(?=<\/style)/;
3684
3721
  const MATCH_STYLE_END = /^<(\/)(style)/;
3685
- const MATCH_DIRECTIVE = /^<!--\s*\[html-validate-(.*?)]\s*-->/;
3722
+ const MATCH_DIRECTIVE = /^<!--\s*(\[)html-validate-([a-z0-9-]+)\s*(.*?)(]?)\s*-->/;
3686
3723
  const MATCH_COMMENT = /^<!--([^]*?)-->/;
3687
3724
  const MATCH_CONDITIONAL = /^<!\[([^\]]*?)\]>/;
3688
3725
  class InvalidTokenError extends Error {
@@ -3737,15 +3774,15 @@ class Lexer {
3737
3774
  previousState = context.state;
3738
3775
  previousLength = context.string.length;
3739
3776
  }
3740
- yield this.token(context, TokenType.EOF);
3777
+ yield this.token(context, TokenType.EOF, []);
3741
3778
  }
3742
3779
  token(context, type, data) {
3743
- const size = data ? data[0].length : 0;
3780
+ const size = data.length > 0 ? data[0].length : 0;
3744
3781
  const location = context.getLocation(size);
3745
3782
  return {
3746
3783
  type,
3747
3784
  location,
3748
- data: data ? Array.from(data) : null,
3785
+ data: Array.from(data),
3749
3786
  };
3750
3787
  }
3751
3788
  /* istanbul ignore next: used to provide a better error when an unhandled state happens */
@@ -3770,17 +3807,18 @@ class Lexer {
3770
3807
  }
3771
3808
  }
3772
3809
  *match(context, tests, error) {
3773
- let match = null;
3774
3810
  const n = tests.length;
3775
3811
  for (let i = 0; i < n; i++) {
3776
3812
  const [regex, nextState, tokenType] = tests[i];
3777
- if (regex === false || (match = context.string.match(regex))) {
3813
+ const match = regex ? context.string.match(regex) : [""];
3814
+ if (match) {
3778
3815
  let token = null;
3779
3816
  if (tokenType !== false) {
3780
- yield (token = this.token(context, tokenType, match));
3817
+ token = this.token(context, tokenType, match);
3818
+ yield token;
3781
3819
  }
3782
3820
  const state = this.evalNextState(nextState, token);
3783
- context.consume(match || 0, state);
3821
+ context.consume(match, state);
3784
3822
  this.enter(context, state, match);
3785
3823
  return;
3786
3824
  }
@@ -3827,18 +3865,19 @@ class Lexer {
3827
3865
  *tokenizeTag(context) {
3828
3866
  /* eslint-disable-next-line consistent-return -- exhaustive switch handled by typescript */
3829
3867
  function nextState(token) {
3868
+ const tagCloseToken = token;
3830
3869
  switch (context.contentModel) {
3831
3870
  case ContentModel.TEXT:
3832
3871
  return State.TEXT;
3833
3872
  case ContentModel.SCRIPT:
3834
- if (token && token.data[0][0] !== "/") {
3873
+ if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
3835
3874
  return State.SCRIPT;
3836
3875
  }
3837
3876
  else {
3838
3877
  return State.TEXT; /* <script/> (not legal but handle it anyway so the lexer doesn't choke on it) */
3839
3878
  }
3840
3879
  case ContentModel.STYLE:
3841
- if (token && token.data[0][0] !== "/") {
3880
+ if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
3842
3881
  return State.STYLE;
3843
3882
  }
3844
3883
  else {
@@ -5986,7 +6025,7 @@ class NoConditionalComment extends Rule {
5986
6025
  }
5987
6026
  setup() {
5988
6027
  this.on("conditional", (event) => {
5989
- this.report(null, "Use of conditional comments are deprecated", event.location);
6028
+ this.report(event.parent, "Use of conditional comments are deprecated", event.location);
5990
6029
  });
5991
6030
  }
5992
6031
  }
@@ -9759,7 +9798,9 @@ class ResolvedConfig {
9759
9798
  * @returns A list of transformed sources ready for validation.
9760
9799
  */
9761
9800
  transformFilename(filename) {
9762
- const data = fs.readFileSync(filename, { encoding: "utf8" });
9801
+ const stdin = 0;
9802
+ const src = filename !== "/dev/stdin" ? filename : stdin;
9803
+ const data = fs.readFileSync(src, { encoding: "utf8" });
9763
9804
  const source = {
9764
9805
  data,
9765
9806
  filename,
@@ -10410,6 +10451,9 @@ class ParserError extends Error {
10410
10451
  }
10411
10452
  }
10412
10453
 
10454
+ function isAttrValueToken(token) {
10455
+ return Boolean(token && token.type === TokenType.ATTR_VALUE);
10456
+ }
10413
10457
  /**
10414
10458
  * Parse HTML document into a DOM tree.
10415
10459
  *
@@ -10481,10 +10525,7 @@ class Parser {
10481
10525
  this.consumeDirective(token);
10482
10526
  break;
10483
10527
  case TokenType.CONDITIONAL:
10484
- this.trigger("conditional", {
10485
- condition: token.data[1],
10486
- location: token.location,
10487
- });
10528
+ this.consumeConditional(token);
10488
10529
  break;
10489
10530
  case TokenType.COMMENT:
10490
10531
  this.consumeComment(token);
@@ -10494,7 +10535,7 @@ class Parser {
10494
10535
  break;
10495
10536
  case TokenType.TEXT:
10496
10537
  case TokenType.TEMPLATING:
10497
- this.appendText(token.data, token.location);
10538
+ this.appendText(token.data[0], token.location);
10498
10539
  break;
10499
10540
  case TokenType.EOF:
10500
10541
  this.closeTree(source, token.location);
@@ -10689,15 +10730,15 @@ class Parser {
10689
10730
  const keyLocation = this.getAttributeKeyLocation(token);
10690
10731
  const valueLocation = this.getAttributeValueLocation(next);
10691
10732
  const location = this.getAttributeLocation(token, next);
10692
- const haveValue = next && next.type === TokenType.ATTR_VALUE;
10733
+ const haveValue = isAttrValueToken(next);
10693
10734
  const attrData = {
10694
10735
  key: token.data[1],
10695
10736
  value: null,
10696
10737
  quote: null,
10697
10738
  };
10698
- if (next && haveValue) {
10739
+ if (haveValue) {
10699
10740
  const [, , value, quote] = next.data;
10700
- attrData.value = value !== null && value !== void 0 ? value : null;
10741
+ attrData.value = value;
10701
10742
  attrData.quote = quote !== null && quote !== void 0 ? quote : null;
10702
10743
  }
10703
10744
  /* get callback to process attributes, default is to just return attribute
@@ -10776,12 +10817,16 @@ class Parser {
10776
10817
  };
10777
10818
  }
10778
10819
  consumeDirective(token) {
10779
- const directive = token.data[1];
10780
- const match = directive.match(/^([a-zA-Z0-9-]+)\s*(.*?)(?:\s*:\s*(.*))?$/);
10820
+ const [text, , action, directive, end] = token.data;
10821
+ if (end === "") {
10822
+ throw new Error(`Missing end bracket "]" on directive "${text}"`);
10823
+ }
10824
+ const match = directive.match(/^(.*?)(?:\s*(?:--|:)\s*(.*))?$/);
10825
+ /* istanbul ignore next: should not be possible, would be emitted as comment token */
10781
10826
  if (!match) {
10782
- throw new Error(`Failed to parse directive "${directive}"`);
10827
+ throw new Error(`Failed to parse directive "${text}"`);
10783
10828
  }
10784
- const [, action, data, comment] = match;
10829
+ const [, data, comment] = match;
10785
10830
  this.trigger("directive", {
10786
10831
  action,
10787
10832
  data,
@@ -10789,17 +10834,33 @@ class Parser {
10789
10834
  location: token.location,
10790
10835
  });
10791
10836
  }
10837
+ /**
10838
+ * Consumes conditional comment in tag form.
10839
+ *
10840
+ * See also the related [[consumeCommend]] method.
10841
+ */
10842
+ consumeConditional(token) {
10843
+ const element = this.dom.getActive();
10844
+ this.trigger("conditional", {
10845
+ condition: token.data[1],
10846
+ location: token.location,
10847
+ parent: element,
10848
+ });
10849
+ }
10792
10850
  /**
10793
10851
  * Consumes comment token.
10794
10852
  *
10795
- * Tries to find IE conditional comments and emits conditional token if found.
10853
+ * Tries to find IE conditional comments and emits conditional token if
10854
+ * found. See also the related [[consumeConditional]] method.
10796
10855
  */
10797
10856
  consumeComment(token) {
10798
10857
  const comment = token.data[0];
10858
+ const element = this.dom.getActive();
10799
10859
  for (const conditional of parseConditionalComment(comment, token.location)) {
10800
10860
  this.trigger("conditional", {
10801
10861
  condition: conditional.expression,
10802
10862
  location: conditional.location,
10863
+ parent: element,
10803
10864
  });
10804
10865
  }
10805
10866
  }
@@ -10808,7 +10869,8 @@ class Parser {
10808
10869
  */
10809
10870
  consumeDoctype(startToken, tokenStream) {
10810
10871
  const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.DOCTYPE_CLOSE, startToken.location));
10811
- const doctype = tokens[0]; /* first token is the doctype, second is the closing ">" */
10872
+ /* first token is the doctype, second is the closing ">" */
10873
+ const doctype = tokens[0];
10812
10874
  const value = doctype.data[0];
10813
10875
  this.dom.doctype = value;
10814
10876
  this.trigger("doctype", {
@@ -10841,7 +10903,7 @@ class Parser {
10841
10903
  this.trigger("token", {
10842
10904
  location: token.location,
10843
10905
  type: token.type,
10844
- data: token.data ? Array.from(token.data) : undefined,
10906
+ data: Array.from(token.data),
10845
10907
  });
10846
10908
  }
10847
10909
  return it;
@@ -11101,11 +11163,12 @@ class Engine {
11101
11163
  return lines;
11102
11164
  }
11103
11165
  dumpTokens(source) {
11166
+ var _a;
11104
11167
  const lexer = new Lexer();
11105
11168
  const lines = [];
11106
11169
  for (const src of source) {
11107
11170
  for (const token of lexer.tokenize(src)) {
11108
- const data = token.data ? token.data[0] : null;
11171
+ const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
11109
11172
  lines.push({
11110
11173
  token: TokenType[token.type],
11111
11174
  data,