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.
@@ -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/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 condensed = value.replace(/[\t\r\n ]+/g, " ");
1491
- const { tokens, locations } = parse(condensed, location);
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
  }
@@ -2956,7 +2956,7 @@ var TRANSFORMER_API;
2956
2956
  /** @public */
2957
2957
  const name = "html-validate";
2958
2958
  /** @public */
2959
- const version = "6.3.1";
2959
+ const version = "6.5.0";
2960
2960
  /** @public */
2961
2961
  const homepage = "https://html-validate.org";
2962
2962
  /** @public */
@@ -2995,6 +2995,42 @@ function parseSeverity(value) {
2995
2995
  }
2996
2996
  }
2997
2997
 
2998
+ function escape(value) {
2999
+ return value.replace(/'/g, "\\'");
3000
+ }
3001
+ function format(value, quote = false) {
3002
+ if (value === null) {
3003
+ return "null";
3004
+ }
3005
+ if (typeof value === "number") {
3006
+ return value.toString();
3007
+ }
3008
+ if (typeof value === "string") {
3009
+ return quote ? `'${escape(value)}'` : value;
3010
+ }
3011
+ if (Array.isArray(value)) {
3012
+ const content = value.map((it) => format(it, true)).join(", ");
3013
+ return `[ ${content} ]`;
3014
+ }
3015
+ if (typeof value === "object") {
3016
+ const content = Object.entries(value)
3017
+ .map(([key, nested]) => `${key}: ${format(nested, true)}`)
3018
+ .join(", ");
3019
+ return `{ ${content} }`;
3020
+ }
3021
+ return String(value);
3022
+ }
3023
+ /**
3024
+ * Replaces placeholder `{{ ... }}` with values from given object.
3025
+ *
3026
+ * @internal
3027
+ */
3028
+ function interpolate(text, data) {
3029
+ return text.replace(/{{\s*([^\s]+)\s*}}/g, (match, key) => {
3030
+ return typeof data[key] !== "undefined" ? format(data[key]) : match;
3031
+ });
3032
+ }
3033
+
2998
3034
  const remapEvents = {
2999
3035
  "tag:open": "tag:start",
3000
3036
  "tag:close": "tag:end",
@@ -3129,7 +3165,8 @@ class Rule {
3129
3165
  report(node, message, location, context) {
3130
3166
  if (this.isEnabled() && (!node || node.ruleEnabled(this.name))) {
3131
3167
  const where = this.findLocation({ node, location, event: this.event });
3132
- this.reporter.add(this, message, this.severity, node, where, context);
3168
+ const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
3169
+ this.reporter.add(this, interpolated, this.severity, node, where, context);
3133
3170
  }
3134
3171
  }
3135
3172
  findLocation(src) {
@@ -3236,7 +3273,8 @@ function ruleDocumentationUrl(filename) {
3236
3273
  const p = path__default["default"].parse(filename);
3237
3274
  const root = path__default["default"].join(distFolder, "rules");
3238
3275
  const rel = path__default["default"].relative(root, path__default["default"].join(p.dir, p.name));
3239
- return `${homepage}/rules/${rel}.html`;
3276
+ const normalized = rel.replace(/\\/g, "/");
3277
+ return `${homepage}/rules/${normalized}.html`;
3240
3278
  }
3241
3279
 
3242
3280
  const defaults$p = {
@@ -3653,22 +3691,21 @@ exports.TokenType = void 0;
3653
3691
  (function (TokenType) {
3654
3692
  TokenType[TokenType["UNICODE_BOM"] = 1] = "UNICODE_BOM";
3655
3693
  TokenType[TokenType["WHITESPACE"] = 2] = "WHITESPACE";
3656
- TokenType[TokenType["NEWLINE"] = 3] = "NEWLINE";
3657
- TokenType[TokenType["DOCTYPE_OPEN"] = 4] = "DOCTYPE_OPEN";
3658
- TokenType[TokenType["DOCTYPE_VALUE"] = 5] = "DOCTYPE_VALUE";
3659
- TokenType[TokenType["DOCTYPE_CLOSE"] = 6] = "DOCTYPE_CLOSE";
3660
- TokenType[TokenType["TAG_OPEN"] = 7] = "TAG_OPEN";
3661
- TokenType[TokenType["TAG_CLOSE"] = 8] = "TAG_CLOSE";
3662
- TokenType[TokenType["ATTR_NAME"] = 9] = "ATTR_NAME";
3663
- TokenType[TokenType["ATTR_VALUE"] = 10] = "ATTR_VALUE";
3664
- TokenType[TokenType["TEXT"] = 11] = "TEXT";
3665
- TokenType[TokenType["TEMPLATING"] = 12] = "TEMPLATING";
3666
- TokenType[TokenType["SCRIPT"] = 13] = "SCRIPT";
3667
- TokenType[TokenType["STYLE"] = 14] = "STYLE";
3668
- TokenType[TokenType["COMMENT"] = 15] = "COMMENT";
3669
- TokenType[TokenType["CONDITIONAL"] = 16] = "CONDITIONAL";
3670
- TokenType[TokenType["DIRECTIVE"] = 17] = "DIRECTIVE";
3671
- TokenType[TokenType["EOF"] = 18] = "EOF";
3694
+ TokenType[TokenType["DOCTYPE_OPEN"] = 3] = "DOCTYPE_OPEN";
3695
+ TokenType[TokenType["DOCTYPE_VALUE"] = 4] = "DOCTYPE_VALUE";
3696
+ TokenType[TokenType["DOCTYPE_CLOSE"] = 5] = "DOCTYPE_CLOSE";
3697
+ TokenType[TokenType["TAG_OPEN"] = 6] = "TAG_OPEN";
3698
+ TokenType[TokenType["TAG_CLOSE"] = 7] = "TAG_CLOSE";
3699
+ TokenType[TokenType["ATTR_NAME"] = 8] = "ATTR_NAME";
3700
+ TokenType[TokenType["ATTR_VALUE"] = 9] = "ATTR_VALUE";
3701
+ TokenType[TokenType["TEXT"] = 10] = "TEXT";
3702
+ TokenType[TokenType["TEMPLATING"] = 11] = "TEMPLATING";
3703
+ TokenType[TokenType["SCRIPT"] = 12] = "SCRIPT";
3704
+ TokenType[TokenType["STYLE"] = 13] = "STYLE";
3705
+ TokenType[TokenType["COMMENT"] = 14] = "COMMENT";
3706
+ TokenType[TokenType["CONDITIONAL"] = 15] = "CONDITIONAL";
3707
+ TokenType[TokenType["DIRECTIVE"] = 16] = "DIRECTIVE";
3708
+ TokenType[TokenType["EOF"] = 17] = "EOF";
3672
3709
  })(exports.TokenType || (exports.TokenType = {}));
3673
3710
 
3674
3711
  /* eslint-disable no-useless-escape */
@@ -3693,7 +3730,7 @@ const MATCH_SCRIPT_DATA = /^[^]*?(?=<\/script)/;
3693
3730
  const MATCH_SCRIPT_END = /^<(\/)(script)/;
3694
3731
  const MATCH_STYLE_DATA = /^[^]*?(?=<\/style)/;
3695
3732
  const MATCH_STYLE_END = /^<(\/)(style)/;
3696
- const MATCH_DIRECTIVE = /^<!--\s*\[html-validate-(.*?)]\s*-->/;
3733
+ const MATCH_DIRECTIVE = /^<!--\s*(\[)html-validate-([a-z0-9-]+)\s*(.*?)(]?)\s*-->/;
3697
3734
  const MATCH_COMMENT = /^<!--([^]*?)-->/;
3698
3735
  const MATCH_CONDITIONAL = /^<!\[([^\]]*?)\]>/;
3699
3736
  class InvalidTokenError extends Error {
@@ -3748,15 +3785,15 @@ class Lexer {
3748
3785
  previousState = context.state;
3749
3786
  previousLength = context.string.length;
3750
3787
  }
3751
- yield this.token(context, exports.TokenType.EOF);
3788
+ yield this.token(context, exports.TokenType.EOF, []);
3752
3789
  }
3753
3790
  token(context, type, data) {
3754
- const size = data ? data[0].length : 0;
3791
+ const size = data.length > 0 ? data[0].length : 0;
3755
3792
  const location = context.getLocation(size);
3756
3793
  return {
3757
3794
  type,
3758
3795
  location,
3759
- data: data ? Array.from(data) : null,
3796
+ data: Array.from(data),
3760
3797
  };
3761
3798
  }
3762
3799
  /* istanbul ignore next: used to provide a better error when an unhandled state happens */
@@ -3781,17 +3818,18 @@ class Lexer {
3781
3818
  }
3782
3819
  }
3783
3820
  *match(context, tests, error) {
3784
- let match = null;
3785
3821
  const n = tests.length;
3786
3822
  for (let i = 0; i < n; i++) {
3787
3823
  const [regex, nextState, tokenType] = tests[i];
3788
- if (regex === false || (match = context.string.match(regex))) {
3824
+ const match = regex ? context.string.match(regex) : [""];
3825
+ if (match) {
3789
3826
  let token = null;
3790
3827
  if (tokenType !== false) {
3791
- yield (token = this.token(context, tokenType, match));
3828
+ token = this.token(context, tokenType, match);
3829
+ yield token;
3792
3830
  }
3793
3831
  const state = this.evalNextState(nextState, token);
3794
- context.consume(match || 0, state);
3832
+ context.consume(match, state);
3795
3833
  this.enter(context, state, match);
3796
3834
  return;
3797
3835
  }
@@ -3838,18 +3876,19 @@ class Lexer {
3838
3876
  *tokenizeTag(context) {
3839
3877
  /* eslint-disable-next-line consistent-return -- exhaustive switch handled by typescript */
3840
3878
  function nextState(token) {
3879
+ const tagCloseToken = token;
3841
3880
  switch (context.contentModel) {
3842
3881
  case ContentModel.TEXT:
3843
3882
  return State.TEXT;
3844
3883
  case ContentModel.SCRIPT:
3845
- if (token && token.data[0][0] !== "/") {
3884
+ if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
3846
3885
  return State.SCRIPT;
3847
3886
  }
3848
3887
  else {
3849
3888
  return State.TEXT; /* <script/> (not legal but handle it anyway so the lexer doesn't choke on it) */
3850
3889
  }
3851
3890
  case ContentModel.STYLE:
3852
- if (token && token.data[0][0] !== "/") {
3891
+ if (tagCloseToken && tagCloseToken.data[0][0] !== "/") {
3853
3892
  return State.STYLE;
3854
3893
  }
3855
3894
  else {
@@ -5997,7 +6036,7 @@ class NoConditionalComment extends Rule {
5997
6036
  }
5998
6037
  setup() {
5999
6038
  this.on("conditional", (event) => {
6000
- this.report(null, "Use of conditional comments are deprecated", event.location);
6039
+ this.report(event.parent, "Use of conditional comments are deprecated", event.location);
6001
6040
  });
6002
6041
  }
6003
6042
  }
@@ -9770,7 +9809,9 @@ class ResolvedConfig {
9770
9809
  * @returns A list of transformed sources ready for validation.
9771
9810
  */
9772
9811
  transformFilename(filename) {
9773
- const data = fs__default["default"].readFileSync(filename, { encoding: "utf8" });
9812
+ const stdin = 0;
9813
+ const src = filename !== "/dev/stdin" ? filename : stdin;
9814
+ const data = fs__default["default"].readFileSync(src, { encoding: "utf8" });
9774
9815
  const source = {
9775
9816
  data,
9776
9817
  filename,
@@ -10421,6 +10462,9 @@ class ParserError extends Error {
10421
10462
  }
10422
10463
  }
10423
10464
 
10465
+ function isAttrValueToken(token) {
10466
+ return Boolean(token && token.type === exports.TokenType.ATTR_VALUE);
10467
+ }
10424
10468
  /**
10425
10469
  * Parse HTML document into a DOM tree.
10426
10470
  *
@@ -10492,10 +10536,7 @@ class Parser {
10492
10536
  this.consumeDirective(token);
10493
10537
  break;
10494
10538
  case exports.TokenType.CONDITIONAL:
10495
- this.trigger("conditional", {
10496
- condition: token.data[1],
10497
- location: token.location,
10498
- });
10539
+ this.consumeConditional(token);
10499
10540
  break;
10500
10541
  case exports.TokenType.COMMENT:
10501
10542
  this.consumeComment(token);
@@ -10505,7 +10546,7 @@ class Parser {
10505
10546
  break;
10506
10547
  case exports.TokenType.TEXT:
10507
10548
  case exports.TokenType.TEMPLATING:
10508
- this.appendText(token.data, token.location);
10549
+ this.appendText(token.data[0], token.location);
10509
10550
  break;
10510
10551
  case exports.TokenType.EOF:
10511
10552
  this.closeTree(source, token.location);
@@ -10700,15 +10741,15 @@ class Parser {
10700
10741
  const keyLocation = this.getAttributeKeyLocation(token);
10701
10742
  const valueLocation = this.getAttributeValueLocation(next);
10702
10743
  const location = this.getAttributeLocation(token, next);
10703
- const haveValue = next && next.type === exports.TokenType.ATTR_VALUE;
10744
+ const haveValue = isAttrValueToken(next);
10704
10745
  const attrData = {
10705
10746
  key: token.data[1],
10706
10747
  value: null,
10707
10748
  quote: null,
10708
10749
  };
10709
- if (next && haveValue) {
10750
+ if (haveValue) {
10710
10751
  const [, , value, quote] = next.data;
10711
- attrData.value = value !== null && value !== void 0 ? value : null;
10752
+ attrData.value = value;
10712
10753
  attrData.quote = quote !== null && quote !== void 0 ? quote : null;
10713
10754
  }
10714
10755
  /* get callback to process attributes, default is to just return attribute
@@ -10787,12 +10828,16 @@ class Parser {
10787
10828
  };
10788
10829
  }
10789
10830
  consumeDirective(token) {
10790
- const directive = token.data[1];
10791
- const match = directive.match(/^([a-zA-Z0-9-]+)\s*(.*?)(?:\s*:\s*(.*))?$/);
10831
+ const [text, , action, directive, end] = token.data;
10832
+ if (end === "") {
10833
+ throw new Error(`Missing end bracket "]" on directive "${text}"`);
10834
+ }
10835
+ const match = directive.match(/^(.*?)(?:\s*(?:--|:)\s*(.*))?$/);
10836
+ /* istanbul ignore next: should not be possible, would be emitted as comment token */
10792
10837
  if (!match) {
10793
- throw new Error(`Failed to parse directive "${directive}"`);
10838
+ throw new Error(`Failed to parse directive "${text}"`);
10794
10839
  }
10795
- const [, action, data, comment] = match;
10840
+ const [, data, comment] = match;
10796
10841
  this.trigger("directive", {
10797
10842
  action,
10798
10843
  data,
@@ -10800,17 +10845,33 @@ class Parser {
10800
10845
  location: token.location,
10801
10846
  });
10802
10847
  }
10848
+ /**
10849
+ * Consumes conditional comment in tag form.
10850
+ *
10851
+ * See also the related [[consumeCommend]] method.
10852
+ */
10853
+ consumeConditional(token) {
10854
+ const element = this.dom.getActive();
10855
+ this.trigger("conditional", {
10856
+ condition: token.data[1],
10857
+ location: token.location,
10858
+ parent: element,
10859
+ });
10860
+ }
10803
10861
  /**
10804
10862
  * Consumes comment token.
10805
10863
  *
10806
- * Tries to find IE conditional comments and emits conditional token if found.
10864
+ * Tries to find IE conditional comments and emits conditional token if
10865
+ * found. See also the related [[consumeConditional]] method.
10807
10866
  */
10808
10867
  consumeComment(token) {
10809
10868
  const comment = token.data[0];
10869
+ const element = this.dom.getActive();
10810
10870
  for (const conditional of parseConditionalComment(comment, token.location)) {
10811
10871
  this.trigger("conditional", {
10812
10872
  condition: conditional.expression,
10813
10873
  location: conditional.location,
10874
+ parent: element,
10814
10875
  });
10815
10876
  }
10816
10877
  }
@@ -10819,7 +10880,8 @@ class Parser {
10819
10880
  */
10820
10881
  consumeDoctype(startToken, tokenStream) {
10821
10882
  const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.DOCTYPE_CLOSE, startToken.location));
10822
- const doctype = tokens[0]; /* first token is the doctype, second is the closing ">" */
10883
+ /* first token is the doctype, second is the closing ">" */
10884
+ const doctype = tokens[0];
10823
10885
  const value = doctype.data[0];
10824
10886
  this.dom.doctype = value;
10825
10887
  this.trigger("doctype", {
@@ -10852,7 +10914,7 @@ class Parser {
10852
10914
  this.trigger("token", {
10853
10915
  location: token.location,
10854
10916
  type: token.type,
10855
- data: token.data ? Array.from(token.data) : undefined,
10917
+ data: Array.from(token.data),
10856
10918
  });
10857
10919
  }
10858
10920
  return it;
@@ -11112,11 +11174,12 @@ class Engine {
11112
11174
  return lines;
11113
11175
  }
11114
11176
  dumpTokens(source) {
11177
+ var _a;
11115
11178
  const lexer = new Lexer();
11116
11179
  const lines = [];
11117
11180
  for (const src of source) {
11118
11181
  for (const token of lexer.tokenize(src)) {
11119
- const data = token.data ? token.data[0] : null;
11182
+ const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
11120
11183
  lines.push({
11121
11184
  token: exports.TokenType[token.type],
11122
11185
  data,