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/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
  }
@@ -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 tagName = startToken.data[2];
1897
- if (!tagName) {
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.3.2";
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
- this.reporter.add(this, message, this.severity, node, where, context);
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["NEWLINE"] = 3] = "NEWLINE";
3658
- TokenType[TokenType["DOCTYPE_OPEN"] = 4] = "DOCTYPE_OPEN";
3659
- TokenType[TokenType["DOCTYPE_VALUE"] = 5] = "DOCTYPE_VALUE";
3660
- TokenType[TokenType["DOCTYPE_CLOSE"] = 6] = "DOCTYPE_CLOSE";
3661
- TokenType[TokenType["TAG_OPEN"] = 7] = "TAG_OPEN";
3662
- TokenType[TokenType["TAG_CLOSE"] = 8] = "TAG_CLOSE";
3663
- TokenType[TokenType["ATTR_NAME"] = 9] = "ATTR_NAME";
3664
- TokenType[TokenType["ATTR_VALUE"] = 10] = "ATTR_VALUE";
3665
- TokenType[TokenType["TEXT"] = 11] = "TEXT";
3666
- TokenType[TokenType["TEMPLATING"] = 12] = "TEMPLATING";
3667
- TokenType[TokenType["SCRIPT"] = 13] = "SCRIPT";
3668
- TokenType[TokenType["STYLE"] = 14] = "STYLE";
3669
- TokenType[TokenType["COMMENT"] = 15] = "COMMENT";
3670
- TokenType[TokenType["CONDITIONAL"] = 16] = "CONDITIONAL";
3671
- TokenType[TokenType["DIRECTIVE"] = 17] = "DIRECTIVE";
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*\[html-validate-(.*?)]\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: data ? Array.from(data) : null,
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
- if (regex === false || (match = context.string.match(regex))) {
3827
+ const match = regex ? context.string.match(regex) : [""];
3828
+ if (match) {
3790
3829
  let token = null;
3791
3830
  if (tokenType !== false) {
3792
- yield (token = this.token(context, tokenType, match));
3831
+ token = this.token(context, tokenType, match);
3832
+ yield token;
3793
3833
  }
3794
3834
  const state = this.evalNextState(nextState, token);
3795
- context.consume(match || 0, state);
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 (token && token.data[0][0] !== "/") {
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 (token && token.data[0][0] !== "/") {
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(null, "Use of conditional comments are deprecated", event.location);
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": "error",
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": "error",
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
- switch (token.type) {
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 && next.type === exports.TokenType.ATTR_VALUE;
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 (next && haveValue) {
10769
+ if (haveValue) {
10713
10770
  const [, , value, quote] = next.data;
10714
- attrData.value = value !== null && value !== void 0 ? value : null;
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[1];
10794
- const match = directive.match(/^([a-zA-Z0-9-]+)\s*(.*?)(?:\s*:\s*(.*))?$/);
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 "${directive}"`);
10857
+ throw new Error(`Failed to parse directive "${text}"`);
10797
10858
  }
10798
- const [, action, data, comment] = match;
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 found.
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
- const doctype = tokens[0]; /* first token is the doctype, second is the closing ">" */
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: token.data ? Array.from(token.data) : undefined,
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 = token.data ? token.data[0] : null;
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,