html-validate 6.7.0 → 6.9.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
@@ -1233,6 +1233,27 @@ class Attribute {
1233
1233
  }
1234
1234
  }
1235
1235
 
1236
+ function getCSSDeclarations(value) {
1237
+ return value
1238
+ .trim()
1239
+ .split(";")
1240
+ .filter(Boolean)
1241
+ .map((it) => {
1242
+ const [property, value] = it.split(":", 2);
1243
+ return [property.trim(), value ? value.trim() : ""];
1244
+ });
1245
+ }
1246
+ /**
1247
+ * @internal
1248
+ */
1249
+ function parseCssDeclaration(value) {
1250
+ if (!value || value instanceof DynamicValue) {
1251
+ return {};
1252
+ }
1253
+ const pairs = getCSSDeclarations(value);
1254
+ return Object.fromEntries(pairs);
1255
+ }
1256
+
1236
1257
  function sliceSize(size, begin, end) {
1237
1258
  if (typeof size !== "number") {
1238
1259
  return size;
@@ -1988,9 +2009,10 @@ class HtmlElement extends DOMNode {
1988
2009
  /* if a unique id is present, use it and short-circuit */
1989
2010
  if (cur.id) {
1990
2011
  const escaped = escapeSelectorComponent(cur.id);
1991
- const matches = root.querySelectorAll(`#${escaped}`);
2012
+ const selector = escaped.match(/^\d/) ? `[id="${escaped}"]` : `#${escaped}`;
2013
+ const matches = root.querySelectorAll(selector);
1992
2014
  if (matches.length === 1) {
1993
- parts.push(`#${escaped}`);
2015
+ parts.push(selector);
1994
2016
  break;
1995
2017
  }
1996
2018
  }
@@ -2181,6 +2203,10 @@ class HtmlElement extends DOMNode {
2181
2203
  get id() {
2182
2204
  return this.getAttributeValue("id");
2183
2205
  }
2206
+ get style() {
2207
+ const attr = this.getAttribute("style");
2208
+ return parseCssDeclaration(attr === null || attr === void 0 ? void 0 : attr.value);
2209
+ }
2184
2210
  /**
2185
2211
  * Returns the first child element or null if there are no child elements.
2186
2212
  */
@@ -2986,7 +3012,7 @@ var TRANSFORMER_API;
2986
3012
  /** @public */
2987
3013
  const name = "html-validate";
2988
3014
  /** @public */
2989
- const version = "6.7.0";
3015
+ const version = "6.9.0";
2990
3016
  /** @public */
2991
3017
  const homepage = "https://html-validate.org";
2992
3018
  /** @public */
@@ -3307,7 +3333,7 @@ function ruleDocumentationUrl(filename) {
3307
3333
  return `${homepage}/rules/${normalized}.html`;
3308
3334
  }
3309
3335
 
3310
- const defaults$q = {
3336
+ const defaults$r = {
3311
3337
  allowExternal: true,
3312
3338
  allowRelative: true,
3313
3339
  allowAbsolute: true,
@@ -3351,7 +3377,7 @@ function matchList(value, list) {
3351
3377
  }
3352
3378
  class AllowedLinks extends Rule {
3353
3379
  constructor(options) {
3354
- super({ ...defaults$q, ...options });
3380
+ super({ ...defaults$r, ...options });
3355
3381
  this.allowExternal = parseAllow(this.options.allowExternal);
3356
3382
  this.allowRelative = parseAllow(this.options.allowRelative);
3357
3383
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3652,13 +3678,13 @@ class CaseStyle {
3652
3678
  }
3653
3679
  }
3654
3680
 
3655
- const defaults$p = {
3681
+ const defaults$q = {
3656
3682
  style: "lowercase",
3657
3683
  ignoreForeign: true,
3658
3684
  };
3659
3685
  class AttrCase extends Rule {
3660
3686
  constructor(options) {
3661
- super({ ...defaults$p, ...options });
3687
+ super({ ...defaults$q, ...options });
3662
3688
  this.style = new CaseStyle(this.options.style, "attr-case");
3663
3689
  }
3664
3690
  static schema() {
@@ -3720,7 +3746,7 @@ class AttrCase extends Rule {
3720
3746
  }
3721
3747
  }
3722
3748
 
3723
- exports.TokenType = void 0;
3749
+ var TokenType;
3724
3750
  (function (TokenType) {
3725
3751
  TokenType[TokenType["UNICODE_BOM"] = 1] = "UNICODE_BOM";
3726
3752
  TokenType[TokenType["WHITESPACE"] = 2] = "WHITESPACE";
@@ -3739,7 +3765,7 @@ exports.TokenType = void 0;
3739
3765
  TokenType[TokenType["CONDITIONAL"] = 15] = "CONDITIONAL";
3740
3766
  TokenType[TokenType["DIRECTIVE"] = 16] = "DIRECTIVE";
3741
3767
  TokenType[TokenType["EOF"] = 17] = "EOF";
3742
- })(exports.TokenType || (exports.TokenType = {}));
3768
+ })(TokenType || (TokenType = {}));
3743
3769
 
3744
3770
  /* eslint-disable no-useless-escape */
3745
3771
  const MATCH_UNICODE_BOM = /^\uFEFF/;
@@ -3818,7 +3844,7 @@ class Lexer {
3818
3844
  previousState = context.state;
3819
3845
  previousLength = context.string.length;
3820
3846
  }
3821
- yield this.token(context, exports.TokenType.EOF, []);
3847
+ yield this.token(context, TokenType.EOF, []);
3822
3848
  }
3823
3849
  token(context, type, data) {
3824
3850
  const size = data.length > 0 ? data[0].length : 0;
@@ -3889,21 +3915,21 @@ class Lexer {
3889
3915
  }
3890
3916
  *tokenizeInitial(context) {
3891
3917
  yield* this.match(context, [
3892
- [MATCH_UNICODE_BOM, State.INITIAL, exports.TokenType.UNICODE_BOM],
3918
+ [MATCH_UNICODE_BOM, State.INITIAL, TokenType.UNICODE_BOM],
3893
3919
  [MATCH_XML_TAG, State.INITIAL, false],
3894
- [MATCH_DOCTYPE_OPEN, State.DOCTYPE, exports.TokenType.DOCTYPE_OPEN],
3895
- [MATCH_WHITESPACE, State.INITIAL, exports.TokenType.WHITESPACE],
3896
- [MATCH_DIRECTIVE, State.INITIAL, exports.TokenType.DIRECTIVE],
3897
- [MATCH_CONDITIONAL, State.INITIAL, exports.TokenType.CONDITIONAL],
3898
- [MATCH_COMMENT, State.INITIAL, exports.TokenType.COMMENT],
3920
+ [MATCH_DOCTYPE_OPEN, State.DOCTYPE, TokenType.DOCTYPE_OPEN],
3921
+ [MATCH_WHITESPACE, State.INITIAL, TokenType.WHITESPACE],
3922
+ [MATCH_DIRECTIVE, State.INITIAL, TokenType.DIRECTIVE],
3923
+ [MATCH_CONDITIONAL, State.INITIAL, TokenType.CONDITIONAL],
3924
+ [MATCH_COMMENT, State.INITIAL, TokenType.COMMENT],
3899
3925
  [false, State.TEXT, false],
3900
3926
  ], "expected doctype");
3901
3927
  }
3902
3928
  *tokenizeDoctype(context) {
3903
3929
  yield* this.match(context, [
3904
- [MATCH_WHITESPACE, State.DOCTYPE, exports.TokenType.WHITESPACE],
3905
- [MATCH_DOCTYPE_VALUE, State.DOCTYPE, exports.TokenType.DOCTYPE_VALUE],
3906
- [MATCH_DOCTYPE_CLOSE, State.TEXT, exports.TokenType.DOCTYPE_CLOSE],
3930
+ [MATCH_WHITESPACE, State.DOCTYPE, TokenType.WHITESPACE],
3931
+ [MATCH_DOCTYPE_VALUE, State.DOCTYPE, TokenType.DOCTYPE_VALUE],
3932
+ [MATCH_DOCTYPE_CLOSE, State.TEXT, TokenType.DOCTYPE_CLOSE],
3907
3933
  ], "expected doctype name");
3908
3934
  }
3909
3935
  *tokenizeTag(context) {
@@ -3930,30 +3956,30 @@ class Lexer {
3930
3956
  }
3931
3957
  }
3932
3958
  yield* this.match(context, [
3933
- [MATCH_TAG_CLOSE, nextState, exports.TokenType.TAG_CLOSE],
3934
- [MATCH_ATTR_START, State.ATTR, exports.TokenType.ATTR_NAME],
3935
- [MATCH_WHITESPACE, State.TAG, exports.TokenType.WHITESPACE],
3959
+ [MATCH_TAG_CLOSE, nextState, TokenType.TAG_CLOSE],
3960
+ [MATCH_ATTR_START, State.ATTR, TokenType.ATTR_NAME],
3961
+ [MATCH_WHITESPACE, State.TAG, TokenType.WHITESPACE],
3936
3962
  ], 'expected attribute, ">" or "/>"');
3937
3963
  }
3938
3964
  *tokenizeAttr(context) {
3939
3965
  yield* this.match(context, [
3940
- [MATCH_ATTR_SINGLE, State.TAG, exports.TokenType.ATTR_VALUE],
3941
- [MATCH_ATTR_DOUBLE, State.TAG, exports.TokenType.ATTR_VALUE],
3942
- [MATCH_ATTR_UNQUOTED, State.TAG, exports.TokenType.ATTR_VALUE],
3966
+ [MATCH_ATTR_SINGLE, State.TAG, TokenType.ATTR_VALUE],
3967
+ [MATCH_ATTR_DOUBLE, State.TAG, TokenType.ATTR_VALUE],
3968
+ [MATCH_ATTR_UNQUOTED, State.TAG, TokenType.ATTR_VALUE],
3943
3969
  [false, State.TAG, false],
3944
3970
  ], 'expected attribute, ">" or "/>"');
3945
3971
  }
3946
3972
  *tokenizeText(context) {
3947
3973
  yield* this.match(context, [
3948
- [MATCH_WHITESPACE, State.TEXT, exports.TokenType.WHITESPACE],
3974
+ [MATCH_WHITESPACE, State.TEXT, TokenType.WHITESPACE],
3949
3975
  [MATCH_CDATA_BEGIN, State.CDATA, false],
3950
- [MATCH_DIRECTIVE, State.TEXT, exports.TokenType.DIRECTIVE],
3951
- [MATCH_CONDITIONAL, State.TEXT, exports.TokenType.CONDITIONAL],
3952
- [MATCH_COMMENT, State.TEXT, exports.TokenType.COMMENT],
3953
- [MATCH_TEMPLATING, State.TEXT, exports.TokenType.TEMPLATING],
3954
- [MATCH_TAG_OPEN, State.TAG, exports.TokenType.TAG_OPEN],
3955
- [MATCH_TEXT, State.TEXT, exports.TokenType.TEXT],
3956
- [MATCH_TAG_LOOKAHEAD, State.TEXT, exports.TokenType.TEXT],
3976
+ [MATCH_DIRECTIVE, State.TEXT, TokenType.DIRECTIVE],
3977
+ [MATCH_CONDITIONAL, State.TEXT, TokenType.CONDITIONAL],
3978
+ [MATCH_COMMENT, State.TEXT, TokenType.COMMENT],
3979
+ [MATCH_TEMPLATING, State.TEXT, TokenType.TEMPLATING],
3980
+ [MATCH_TAG_OPEN, State.TAG, TokenType.TAG_OPEN],
3981
+ [MATCH_TEXT, State.TEXT, TokenType.TEXT],
3982
+ [MATCH_TAG_LOOKAHEAD, State.TEXT, TokenType.TEXT],
3957
3983
  ], 'expected text or "<"');
3958
3984
  }
3959
3985
  *tokenizeCDATA(context) {
@@ -3961,21 +3987,21 @@ class Lexer {
3961
3987
  }
3962
3988
  *tokenizeScript(context) {
3963
3989
  yield* this.match(context, [
3964
- [MATCH_SCRIPT_END, State.TAG, exports.TokenType.TAG_OPEN],
3965
- [MATCH_SCRIPT_DATA, State.SCRIPT, exports.TokenType.SCRIPT],
3990
+ [MATCH_SCRIPT_END, State.TAG, TokenType.TAG_OPEN],
3991
+ [MATCH_SCRIPT_DATA, State.SCRIPT, TokenType.SCRIPT],
3966
3992
  ], "expected </script>");
3967
3993
  }
3968
3994
  *tokenizeStyle(context) {
3969
3995
  yield* this.match(context, [
3970
- [MATCH_STYLE_END, State.TAG, exports.TokenType.TAG_OPEN],
3971
- [MATCH_STYLE_DATA, State.STYLE, exports.TokenType.STYLE],
3996
+ [MATCH_STYLE_END, State.TAG, TokenType.TAG_OPEN],
3997
+ [MATCH_STYLE_DATA, State.STYLE, TokenType.STYLE],
3972
3998
  ], "expected </style>");
3973
3999
  }
3974
4000
  }
3975
4001
 
3976
4002
  const whitespace = /(\s+)/;
3977
4003
  function isRelevant$3(event) {
3978
- return event.type === exports.TokenType.ATTR_VALUE;
4004
+ return event.type === TokenType.ATTR_VALUE;
3979
4005
  }
3980
4006
  class AttrDelimiter extends Rule {
3981
4007
  documentation() {
@@ -3997,7 +4023,7 @@ class AttrDelimiter extends Rule {
3997
4023
  }
3998
4024
 
3999
4025
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4000
- const defaults$o = {
4026
+ const defaults$p = {
4001
4027
  pattern: DEFAULT_PATTERN,
4002
4028
  ignoreForeign: true,
4003
4029
  };
@@ -4034,7 +4060,7 @@ function generateDescription(name, pattern) {
4034
4060
  }
4035
4061
  class AttrPattern extends Rule {
4036
4062
  constructor(options) {
4037
- super({ ...defaults$o, ...options });
4063
+ super({ ...defaults$p, ...options });
4038
4064
  this.pattern = generateRegexp(this.options.pattern);
4039
4065
  }
4040
4066
  static schema() {
@@ -4094,13 +4120,13 @@ var QuoteStyle;
4094
4120
  QuoteStyle["DOUBLE_QUOTE"] = "\"";
4095
4121
  QuoteStyle["AUTO_QUOTE"] = "auto";
4096
4122
  })(QuoteStyle || (QuoteStyle = {}));
4097
- const defaults$n = {
4123
+ const defaults$o = {
4098
4124
  style: "auto",
4099
4125
  unquoted: false,
4100
4126
  };
4101
4127
  class AttrQuotes extends Rule {
4102
4128
  constructor(options) {
4103
- super({ ...defaults$n, ...options });
4129
+ super({ ...defaults$o, ...options });
4104
4130
  this.style = parseStyle$4(this.options.style);
4105
4131
  }
4106
4132
  static schema() {
@@ -4179,7 +4205,7 @@ class AttrSpacing extends Rule {
4179
4205
  setup() {
4180
4206
  let previousToken;
4181
4207
  this.on("token", (event) => {
4182
- if (event.type === exports.TokenType.ATTR_NAME && previousToken !== exports.TokenType.WHITESPACE) {
4208
+ if (event.type === TokenType.ATTR_NAME && previousToken !== TokenType.WHITESPACE) {
4183
4209
  this.report(null, "No space between attributes", event.location);
4184
4210
  }
4185
4211
  previousToken = event.type;
@@ -4265,12 +4291,12 @@ class AttributeAllowedValues extends Rule {
4265
4291
  }
4266
4292
  }
4267
4293
 
4268
- const defaults$m = {
4294
+ const defaults$n = {
4269
4295
  style: "omit",
4270
4296
  };
4271
4297
  class AttributeBooleanStyle extends Rule {
4272
4298
  constructor(options) {
4273
- super({ ...defaults$m, ...options });
4299
+ super({ ...defaults$n, ...options });
4274
4300
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4275
4301
  }
4276
4302
  static schema() {
@@ -4346,12 +4372,12 @@ function reportMessage$1(attr, style) {
4346
4372
  return "";
4347
4373
  }
4348
4374
 
4349
- const defaults$l = {
4375
+ const defaults$m = {
4350
4376
  style: "omit",
4351
4377
  };
4352
4378
  class AttributeEmptyStyle extends Rule {
4353
4379
  constructor(options) {
4354
- super({ ...defaults$l, ...options });
4380
+ super({ ...defaults$m, ...options });
4355
4381
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4356
4382
  }
4357
4383
  static schema() {
@@ -4459,12 +4485,12 @@ function describePattern(pattern) {
4459
4485
  }
4460
4486
  }
4461
4487
 
4462
- const defaults$k = {
4488
+ const defaults$l = {
4463
4489
  pattern: "kebabcase",
4464
4490
  };
4465
4491
  class ClassPattern extends Rule {
4466
4492
  constructor(options) {
4467
- super({ ...defaults$k, ...options });
4493
+ super({ ...defaults$l, ...options });
4468
4494
  this.pattern = parsePattern(this.options.pattern);
4469
4495
  }
4470
4496
  static schema() {
@@ -4573,13 +4599,13 @@ class CloseOrder extends Rule {
4573
4599
  }
4574
4600
  }
4575
4601
 
4576
- const defaults$j = {
4602
+ const defaults$k = {
4577
4603
  include: null,
4578
4604
  exclude: null,
4579
4605
  };
4580
4606
  class Deprecated extends Rule {
4581
4607
  constructor(options) {
4582
- super({ ...defaults$j, ...options });
4608
+ super({ ...defaults$k, ...options });
4583
4609
  }
4584
4610
  static schema() {
4585
4611
  return {
@@ -4742,12 +4768,12 @@ class NoStyleTag$1 extends Rule {
4742
4768
  }
4743
4769
  }
4744
4770
 
4745
- const defaults$i = {
4771
+ const defaults$j = {
4746
4772
  style: "uppercase",
4747
4773
  };
4748
4774
  class DoctypeStyle extends Rule {
4749
4775
  constructor(options) {
4750
- super({ ...defaults$i, ...options });
4776
+ super({ ...defaults$j, ...options });
4751
4777
  }
4752
4778
  static schema() {
4753
4779
  return {
@@ -4779,12 +4805,12 @@ class DoctypeStyle extends Rule {
4779
4805
  }
4780
4806
  }
4781
4807
 
4782
- const defaults$h = {
4808
+ const defaults$i = {
4783
4809
  style: "lowercase",
4784
4810
  };
4785
4811
  class ElementCase extends Rule {
4786
4812
  constructor(options) {
4787
- super({ ...defaults$h, ...options });
4813
+ super({ ...defaults$i, ...options });
4788
4814
  this.style = new CaseStyle(this.options.style, "element-case");
4789
4815
  }
4790
4816
  static schema() {
@@ -4850,14 +4876,14 @@ class ElementCase extends Rule {
4850
4876
  }
4851
4877
  }
4852
4878
 
4853
- const defaults$g = {
4879
+ const defaults$h = {
4854
4880
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
4855
4881
  whitelist: [],
4856
4882
  blacklist: [],
4857
4883
  };
4858
4884
  class ElementName extends Rule {
4859
4885
  constructor(options) {
4860
- super({ ...defaults$g, ...options });
4886
+ super({ ...defaults$h, ...options });
4861
4887
  // eslint-disable-next-line security/detect-non-literal-regexp
4862
4888
  this.pattern = new RegExp(this.options.pattern);
4863
4889
  }
@@ -4898,7 +4924,7 @@ class ElementName extends Rule {
4898
4924
  ...context.blacklist.map((cur) => `- ${cur}`),
4899
4925
  ];
4900
4926
  }
4901
- if (context.pattern !== defaults$g.pattern) {
4927
+ if (context.pattern !== defaults$h.pattern) {
4902
4928
  return [
4903
4929
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
4904
4930
  "",
@@ -5300,7 +5326,7 @@ class EmptyTitle extends Rule {
5300
5326
  }
5301
5327
  }
5302
5328
 
5303
- const defaults$f = {
5329
+ const defaults$g = {
5304
5330
  allowMultipleH1: false,
5305
5331
  minInitialRank: "h1",
5306
5332
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5331,7 +5357,7 @@ function parseMaxInitial(value) {
5331
5357
  }
5332
5358
  class HeadingLevel extends Rule {
5333
5359
  constructor(options) {
5334
- super({ ...defaults$f, ...options });
5360
+ super({ ...defaults$g, ...options });
5335
5361
  this.stack = [];
5336
5362
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5337
5363
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -5489,12 +5515,12 @@ class HeadingLevel extends Rule {
5489
5515
  }
5490
5516
  }
5491
5517
 
5492
- const defaults$e = {
5518
+ const defaults$f = {
5493
5519
  pattern: "kebabcase",
5494
5520
  };
5495
5521
  class IdPattern extends Rule {
5496
5522
  constructor(options) {
5497
- super({ ...defaults$e, ...options });
5523
+ super({ ...defaults$f, ...options });
5498
5524
  this.pattern = parsePattern(this.options.pattern);
5499
5525
  }
5500
5526
  static schema() {
@@ -5845,12 +5871,12 @@ function findLabelByParent(el) {
5845
5871
  return [];
5846
5872
  }
5847
5873
 
5848
- const defaults$d = {
5874
+ const defaults$e = {
5849
5875
  maxlength: 70,
5850
5876
  };
5851
5877
  class LongTitle extends Rule {
5852
5878
  constructor(options) {
5853
- super({ ...defaults$d, ...options });
5879
+ super({ ...defaults$e, ...options });
5854
5880
  this.maxlength = this.options.maxlength;
5855
5881
  }
5856
5882
  static schema() {
@@ -5997,13 +6023,13 @@ class MultipleLabeledControls extends Rule {
5997
6023
  }
5998
6024
  }
5999
6025
 
6000
- const defaults$c = {
6026
+ const defaults$d = {
6001
6027
  include: null,
6002
6028
  exclude: null,
6003
6029
  };
6004
6030
  class NoAutoplay extends Rule {
6005
6031
  constructor(options) {
6006
- super({ ...defaults$c, ...options });
6032
+ super({ ...defaults$d, ...options });
6007
6033
  }
6008
6034
  documentation(context) {
6009
6035
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6244,24 +6270,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6244
6270
  }
6245
6271
  }
6246
6272
 
6247
- const defaults$b = {
6273
+ const defaults$c = {
6248
6274
  include: null,
6249
6275
  exclude: null,
6250
6276
  allowedProperties: ["display"],
6251
6277
  };
6252
- function getCSSDeclarations(value) {
6253
- return value
6254
- .trim()
6255
- .split(";")
6256
- .filter(Boolean)
6257
- .map((it) => {
6258
- const [property, value] = it.split(":", 2);
6259
- return { property: property.trim(), value: value ? value.trim() : undefined };
6260
- });
6261
- }
6262
6278
  class NoInlineStyle extends Rule {
6263
6279
  constructor(options) {
6264
- super({ ...defaults$b, ...options });
6280
+ super({ ...defaults$c, ...options });
6265
6281
  }
6266
6282
  static schema() {
6267
6283
  return {
@@ -6339,18 +6355,15 @@ class NoInlineStyle extends Rule {
6339
6355
  return true;
6340
6356
  }
6341
6357
  allPropertiesAllowed(value) {
6342
- if (typeof value !== "string") {
6343
- return false;
6344
- }
6345
6358
  const allowProperties = this.options.allowedProperties;
6346
6359
  /* quick path: no properties are allowed, no need to check each one individually */
6347
6360
  if (allowProperties.length === 0) {
6348
6361
  return false;
6349
6362
  }
6350
- const declarations = getCSSDeclarations(value);
6363
+ const declarations = Object.keys(parseCssDeclaration(value));
6351
6364
  return (declarations.length > 0 &&
6352
6365
  declarations.every((it) => {
6353
- return allowProperties.includes(it.property);
6366
+ return allowProperties.includes(it);
6354
6367
  }));
6355
6368
  }
6356
6369
  }
@@ -6466,7 +6479,7 @@ class NoMultipleMain extends Rule {
6466
6479
  }
6467
6480
  }
6468
6481
 
6469
- const defaults$a = {
6482
+ const defaults$b = {
6470
6483
  relaxed: false,
6471
6484
  };
6472
6485
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6483,7 +6496,7 @@ const replacementTable = {
6483
6496
  };
6484
6497
  class NoRawCharacters extends Rule {
6485
6498
  constructor(options) {
6486
- super({ ...defaults$a, ...options });
6499
+ super({ ...defaults$b, ...options });
6487
6500
  this.relaxed = this.options.relaxed;
6488
6501
  }
6489
6502
  static schema() {
@@ -6661,13 +6674,13 @@ class NoRedundantRole extends Rule {
6661
6674
  }
6662
6675
 
6663
6676
  const xmlns = /^(.+):.+$/;
6664
- const defaults$9 = {
6677
+ const defaults$a = {
6665
6678
  ignoreForeign: true,
6666
6679
  ignoreXML: true,
6667
6680
  };
6668
6681
  class NoSelfClosing extends Rule {
6669
6682
  constructor(options) {
6670
- super({ ...defaults$9, ...options });
6683
+ super({ ...defaults$a, ...options });
6671
6684
  }
6672
6685
  static schema() {
6673
6686
  return {
@@ -6783,7 +6796,7 @@ class NoUtf8Bom extends Rule {
6783
6796
  }
6784
6797
  setup() {
6785
6798
  const unregister = this.on("token", (event) => {
6786
- if (event.type === exports.TokenType.UNICODE_BOM) {
6799
+ if (event.type === TokenType.UNICODE_BOM) {
6787
6800
  this.report(null, "File should be saved without UTF-8 BOM", event.location);
6788
6801
  }
6789
6802
  /* since the BOM must be the very first thing the rule can now be disabled for the rest of the run */
@@ -6800,13 +6813,13 @@ const replacement = {
6800
6813
  reset: '<button type="reset">',
6801
6814
  image: '<button type="button">',
6802
6815
  };
6803
- const defaults$8 = {
6816
+ const defaults$9 = {
6804
6817
  include: null,
6805
6818
  exclude: null,
6806
6819
  };
6807
6820
  class PreferButton extends Rule {
6808
6821
  constructor(options) {
6809
- super({ ...defaults$8, ...options });
6822
+ super({ ...defaults$9, ...options });
6810
6823
  }
6811
6824
  static schema() {
6812
6825
  return {
@@ -6881,7 +6894,7 @@ class PreferButton extends Rule {
6881
6894
  }
6882
6895
  }
6883
6896
 
6884
- const defaults$7 = {
6897
+ const defaults$8 = {
6885
6898
  mapping: {
6886
6899
  article: "article",
6887
6900
  banner: "header",
@@ -6911,7 +6924,7 @@ const defaults$7 = {
6911
6924
  };
6912
6925
  class PreferNativeElement extends Rule {
6913
6926
  constructor(options) {
6914
- super({ ...defaults$7, ...options });
6927
+ super({ ...defaults$8, ...options });
6915
6928
  }
6916
6929
  static schema() {
6917
6930
  return {
@@ -7031,7 +7044,7 @@ class PreferTbody extends Rule {
7031
7044
  }
7032
7045
  }
7033
7046
 
7034
- const defaults$6 = {
7047
+ const defaults$7 = {
7035
7048
  target: "all",
7036
7049
  };
7037
7050
  const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
@@ -7041,7 +7054,7 @@ const supportSri = {
7041
7054
  };
7042
7055
  class RequireSri extends Rule {
7043
7056
  constructor(options) {
7044
- super({ ...defaults$6, ...options });
7057
+ super({ ...defaults$7, ...options });
7045
7058
  this.target = this.options.target;
7046
7059
  }
7047
7060
  static schema() {
@@ -7169,12 +7182,13 @@ class SvgFocusable extends Rule {
7169
7182
  }
7170
7183
  }
7171
7184
 
7172
- const defaults$5 = {
7185
+ const defaults$6 = {
7173
7186
  characters: [
7174
7187
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
7175
7188
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" },
7176
7189
  ],
7177
7190
  ignoreClasses: [],
7191
+ ignoreStyle: true,
7178
7192
  };
7179
7193
  function constructRegex(characters) {
7180
7194
  const disallowed = characters
@@ -7211,7 +7225,7 @@ function matchAll(text, regexp) {
7211
7225
  }
7212
7226
  class TelNonBreaking extends Rule {
7213
7227
  constructor(options) {
7214
- super({ ...defaults$5, ...options });
7228
+ super({ ...defaults$6, ...options });
7215
7229
  this.regex = constructRegex(this.options.characters);
7216
7230
  }
7217
7231
  static schema() {
@@ -7240,6 +7254,9 @@ class TelNonBreaking extends Rule {
7240
7254
  type: "string",
7241
7255
  },
7242
7256
  },
7257
+ ignoreStyle: {
7258
+ type: "boolean",
7259
+ },
7243
7260
  };
7244
7261
  }
7245
7262
  documentation(context) {
@@ -7266,13 +7283,10 @@ class TelNonBreaking extends Rule {
7266
7283
  setup() {
7267
7284
  this.on("element:ready", this.isRelevant, (event) => {
7268
7285
  const { target } = event;
7269
- const { ignoreClasses } = this.options;
7270
- /* skip if element has a class in the ignore list */
7271
- const isIgnored = ignoreClasses.some((it) => target.classList.contains(it));
7272
- if (isIgnored) {
7286
+ if (this.isIgnored(target)) {
7273
7287
  return;
7274
7288
  }
7275
- this.walk(target);
7289
+ this.walk(target, target);
7276
7290
  });
7277
7291
  }
7278
7292
  isRelevant(event) {
@@ -7288,17 +7302,36 @@ class TelNonBreaking extends Rule {
7288
7302
  }
7289
7303
  return true;
7290
7304
  }
7291
- walk(node) {
7305
+ isIgnoredClass(node) {
7306
+ const { ignoreClasses } = this.options;
7307
+ const { classList } = node;
7308
+ return ignoreClasses.some((it) => classList.contains(it));
7309
+ }
7310
+ isIgnoredStyle(node) {
7311
+ const { ignoreStyle } = this.options;
7312
+ const { style } = node;
7313
+ if (!ignoreStyle) {
7314
+ return false;
7315
+ }
7316
+ if (style["white-space"] === "nowrap" || style["white-space"] === "pre") {
7317
+ return true;
7318
+ }
7319
+ return false;
7320
+ }
7321
+ isIgnored(node) {
7322
+ return this.isIgnoredClass(node) || this.isIgnoredStyle(node);
7323
+ }
7324
+ walk(anchor, node) {
7292
7325
  for (const child of node.childNodes) {
7293
7326
  if (isTextNode(child)) {
7294
- this.detectDisallowed(child);
7327
+ this.detectDisallowed(anchor, child);
7295
7328
  }
7296
7329
  else if (isElementNode(child)) {
7297
- this.walk(child);
7330
+ this.walk(anchor, child);
7298
7331
  }
7299
7332
  }
7300
7333
  }
7301
- detectDisallowed(node) {
7334
+ detectDisallowed(anchor, node) {
7302
7335
  const [offset, text] = getText(node);
7303
7336
  const matches = matchAll(text, this.regex);
7304
7337
  for (const match of matches) {
@@ -7308,12 +7341,12 @@ class TelNonBreaking extends Rule {
7308
7341
  if (!entry) {
7309
7342
  throw new Error(`Failed to find entry for "${detected}" when searching text "${text}"`);
7310
7343
  }
7311
- const message = `"${detected}" should be replaced with "${entry.replacement}" in telephone number`;
7344
+ const message = `"${detected}" should be replaced with "${entry.replacement}" (${entry.description}) in telephone number`;
7312
7345
  const begin = offset + match.index;
7313
7346
  const end = begin + detected.length;
7314
7347
  const location = sliceLocation(node.location, begin, end);
7315
7348
  const context = entry;
7316
- this.report(node, message, location, context);
7349
+ this.report(anchor, message, location, context);
7317
7350
  }
7318
7351
  }
7319
7352
  }
@@ -9271,6 +9304,95 @@ class UnknownCharReference extends Rule {
9271
9304
  }
9272
9305
  }
9273
9306
 
9307
+ var RuleContext;
9308
+ (function (RuleContext) {
9309
+ RuleContext[RuleContext["EMPTY"] = 1] = "EMPTY";
9310
+ RuleContext[RuleContext["WHITESPACE"] = 2] = "WHITESPACE";
9311
+ RuleContext[RuleContext["LEADING_CHARACTER"] = 3] = "LEADING_CHARACTER";
9312
+ RuleContext[RuleContext["DISALLOWED_CHARACTER"] = 4] = "DISALLOWED_CHARACTER";
9313
+ })(RuleContext || (RuleContext = {}));
9314
+ const defaults$5 = {
9315
+ relaxed: false,
9316
+ };
9317
+ class ValidID extends Rule {
9318
+ constructor(options) {
9319
+ super({ ...defaults$5, ...options });
9320
+ }
9321
+ static schema() {
9322
+ return {
9323
+ relaxed: {
9324
+ type: "boolean",
9325
+ },
9326
+ };
9327
+ }
9328
+ documentation(context) {
9329
+ const { relaxed } = this.options;
9330
+ const message = context
9331
+ ? this.messages[context].replace("id", "ID").replace(/^(.)/, (m) => m.toUpperCase())
9332
+ : "Element ID is not valid";
9333
+ const relaxedDescription = relaxed
9334
+ ? []
9335
+ : [
9336
+ " - ID must begin with a letter",
9337
+ " - ID must only contain alphanumerical characters, `-` and `_`",
9338
+ ];
9339
+ return {
9340
+ description: [
9341
+ `${message}.`,
9342
+ "",
9343
+ "Under the current configuration the following rules are applied:",
9344
+ "",
9345
+ " - ID must not be empty",
9346
+ " - ID must not contain any whitespace characters",
9347
+ ...relaxedDescription,
9348
+ ].join("\n"),
9349
+ url: ruleDocumentationUrl("@/rules/valid-id.ts"),
9350
+ };
9351
+ }
9352
+ setup() {
9353
+ this.on("attr", this.isRelevant, (event) => {
9354
+ const { value } = event;
9355
+ if (value === null || value instanceof DynamicValue) {
9356
+ return;
9357
+ }
9358
+ if (value === "") {
9359
+ const context = RuleContext.EMPTY;
9360
+ this.report(event.target, this.messages[context], event.location, context);
9361
+ return;
9362
+ }
9363
+ if (value.match(/\s/)) {
9364
+ const context = RuleContext.WHITESPACE;
9365
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9366
+ return;
9367
+ }
9368
+ const { relaxed } = this.options;
9369
+ if (relaxed) {
9370
+ return;
9371
+ }
9372
+ if (value.match(/^[^a-zA-Z]/)) {
9373
+ const context = RuleContext.LEADING_CHARACTER;
9374
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9375
+ return;
9376
+ }
9377
+ if (value.match(/[^a-zA-Z0-9-_]/)) {
9378
+ const context = RuleContext.DISALLOWED_CHARACTER;
9379
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9380
+ }
9381
+ });
9382
+ }
9383
+ get messages() {
9384
+ return {
9385
+ [RuleContext.EMPTY]: "element id must not be empty",
9386
+ [RuleContext.WHITESPACE]: "element id must not contain whitespace",
9387
+ [RuleContext.LEADING_CHARACTER]: "element id must begin with a letter",
9388
+ [RuleContext.DISALLOWED_CHARACTER]: "element id must only contain alphanumerical, dash and underscore characters",
9389
+ };
9390
+ }
9391
+ isRelevant(event) {
9392
+ return event.key === "id";
9393
+ }
9394
+ }
9395
+
9274
9396
  var Style$1;
9275
9397
  (function (Style) {
9276
9398
  Style[Style["Any"] = 0] = "Any";
@@ -9782,6 +9904,7 @@ const bundledRules = {
9782
9904
  "tel-non-breaking": TelNonBreaking,
9783
9905
  "text-content": TextContent,
9784
9906
  "unrecognized-char-ref": UnknownCharReference,
9907
+ "valid-id": ValidID,
9785
9908
  void: Void,
9786
9909
  "void-content": VoidContent,
9787
9910
  "void-style": VoidStyle,
@@ -9879,6 +10002,7 @@ const config$1 = {
9879
10002
  "tel-non-breaking": "error",
9880
10003
  "text-content": "error",
9881
10004
  "unrecognized-char-ref": "error",
10005
+ "valid-id": ["error", { relaxed: false }],
9882
10006
  void: "off",
9883
10007
  "void-content": "error",
9884
10008
  "void-style": "error",
@@ -9914,6 +10038,7 @@ const config = {
9914
10038
  "no-raw-characters": ["error", { relaxed: true }],
9915
10039
  "script-element": "error",
9916
10040
  "unrecognized-char-ref": "error",
10041
+ "valid-id": ["error", { relaxed: true }],
9917
10042
  "void-content": "error",
9918
10043
  },
9919
10044
  };
@@ -10243,7 +10368,7 @@ class Config {
10243
10368
  }
10244
10369
  let filename;
10245
10370
  /* try searching builtin metadata */
10246
- filename = path__default["default"].join(projectRoot, "elements", `${entry}.json`);
10371
+ filename = path__default["default"].join(projectRoot, "elements", `${entry}.js`);
10247
10372
  if (fs__default["default"].existsSync(filename)) {
10248
10373
  metaTable.loadFromFile(filename);
10249
10374
  continue;
@@ -10657,7 +10782,7 @@ class ParserError extends Error {
10657
10782
  }
10658
10783
 
10659
10784
  function isAttrValueToken(token) {
10660
- return Boolean(token && token.type === exports.TokenType.ATTR_VALUE);
10785
+ return Boolean(token && token.type === TokenType.ATTR_VALUE);
10661
10786
  }
10662
10787
  function svgShouldRetainTag(foreignTagName, tagName) {
10663
10788
  return foreignTagName === "svg" && ["title", "desc"].includes(tagName);
@@ -10765,43 +10890,43 @@ class Parser {
10765
10890
  /* eslint-disable-next-line complexity */
10766
10891
  consume(source, token, tokenStream) {
10767
10892
  switch (token.type) {
10768
- case exports.TokenType.UNICODE_BOM:
10893
+ case TokenType.UNICODE_BOM:
10769
10894
  /* ignore */
10770
10895
  break;
10771
- case exports.TokenType.TAG_OPEN:
10896
+ case TokenType.TAG_OPEN:
10772
10897
  this.consumeTag(source, token, tokenStream);
10773
10898
  break;
10774
- case exports.TokenType.WHITESPACE:
10899
+ case TokenType.WHITESPACE:
10775
10900
  this.trigger("whitespace", {
10776
10901
  text: token.data[0],
10777
10902
  location: token.location,
10778
10903
  });
10779
10904
  this.appendText(token.data[0], token.location);
10780
10905
  break;
10781
- case exports.TokenType.DIRECTIVE:
10906
+ case TokenType.DIRECTIVE:
10782
10907
  this.consumeDirective(token);
10783
10908
  break;
10784
- case exports.TokenType.CONDITIONAL:
10909
+ case TokenType.CONDITIONAL:
10785
10910
  this.consumeConditional(token);
10786
10911
  break;
10787
- case exports.TokenType.COMMENT:
10912
+ case TokenType.COMMENT:
10788
10913
  this.consumeComment(token);
10789
10914
  break;
10790
- case exports.TokenType.DOCTYPE_OPEN:
10915
+ case TokenType.DOCTYPE_OPEN:
10791
10916
  this.consumeDoctype(token, tokenStream);
10792
10917
  break;
10793
- case exports.TokenType.TEXT:
10794
- case exports.TokenType.TEMPLATING:
10918
+ case TokenType.TEXT:
10919
+ case TokenType.TEMPLATING:
10795
10920
  this.appendText(token.data[0], token.location);
10796
10921
  break;
10797
- case exports.TokenType.EOF:
10922
+ case TokenType.EOF:
10798
10923
  this.closeTree(source, token.location);
10799
10924
  break;
10800
10925
  }
10801
10926
  }
10802
10927
  /* eslint-disable-next-line complexity, sonarjs/cognitive-complexity */
10803
10928
  consumeTag(source, startToken, tokenStream) {
10804
- const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_CLOSE, startToken.location));
10929
+ const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, startToken.location));
10805
10930
  const endToken = tokens.slice(-1)[0];
10806
10931
  const closeOptional = this.closeOptional(startToken);
10807
10932
  const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
@@ -10827,9 +10952,9 @@ class Parser {
10827
10952
  for (let i = 0; i < tokens.length; i++) {
10828
10953
  const token = tokens[i];
10829
10954
  switch (token.type) {
10830
- case exports.TokenType.WHITESPACE:
10955
+ case TokenType.WHITESPACE:
10831
10956
  break;
10832
- case exports.TokenType.ATTR_NAME:
10957
+ case TokenType.ATTR_NAME:
10833
10958
  this.consumeAttribute(source, node, token, tokens[i + 1]);
10834
10959
  break;
10835
10960
  }
@@ -10907,7 +11032,7 @@ class Parser {
10907
11032
  let endToken;
10908
11033
  do {
10909
11034
  /* search for tags */
10910
- const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_OPEN, errorLocation));
11035
+ const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_OPEN, errorLocation));
10911
11036
  const [last] = tokens.slice(-1);
10912
11037
  const [, tagClosed, tagName] = last.data;
10913
11038
  /* special case: svg <title> and <desc> should be intact as it affects accessibility */
@@ -10924,7 +11049,7 @@ class Parser {
10924
11049
  continue;
10925
11050
  }
10926
11051
  /* locate end token and determine if this is a self-closed tag */
10927
- const endTokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.TAG_CLOSE, last.location));
11052
+ const endTokens = Array.from(this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, last.location));
10928
11053
  endToken = endTokens.slice(-1)[0];
10929
11054
  const selfClosed = endToken.data[0] === "/>";
10930
11055
  /* since foreign element may be nested keep a count for the number of
@@ -11010,7 +11135,7 @@ class Parser {
11010
11135
  * ^^^ ^^^ ^^^ (null) (null)
11011
11136
  */
11012
11137
  getAttributeValueLocation(token) {
11013
- if (!token || token.type !== exports.TokenType.ATTR_VALUE || token.data[2] === "") {
11138
+ if (!token || token.type !== TokenType.ATTR_VALUE || token.data[2] === "") {
11014
11139
  return null;
11015
11140
  }
11016
11141
  const quote = token.data[3];
@@ -11028,7 +11153,7 @@ class Parser {
11028
11153
  getAttributeLocation(key, value) {
11029
11154
  var _a;
11030
11155
  const begin = key.location;
11031
- const end = value && value.type === exports.TokenType.ATTR_VALUE ? value.location : undefined;
11156
+ const end = value && value.type === TokenType.ATTR_VALUE ? value.location : undefined;
11032
11157
  return {
11033
11158
  filename: begin.filename,
11034
11159
  line: begin.line,
@@ -11089,7 +11214,7 @@ class Parser {
11089
11214
  * Consumes doctype tokens. Emits doctype event.
11090
11215
  */
11091
11216
  consumeDoctype(startToken, tokenStream) {
11092
- const tokens = Array.from(this.consumeUntil(tokenStream, exports.TokenType.DOCTYPE_CLOSE, startToken.location));
11217
+ const tokens = Array.from(this.consumeUntil(tokenStream, TokenType.DOCTYPE_CLOSE, startToken.location));
11093
11218
  /* first token is the doctype, second is the closing ">" */
11094
11219
  const doctype = tokens[0];
11095
11220
  const value = doctype.data[0];
@@ -11115,7 +11240,7 @@ class Parser {
11115
11240
  return;
11116
11241
  it = this.next(tokenStream);
11117
11242
  }
11118
- throw new ParserError(errorLocation, `stream ended before ${exports.TokenType[search]} token was found`);
11243
+ throw new ParserError(errorLocation, `stream ended before ${TokenType[search]} token was found`);
11119
11244
  }
11120
11245
  /**
11121
11246
  * Consumes tokens until a matching close-tag is found. Tags are appended to
@@ -11129,7 +11254,7 @@ class Parser {
11129
11254
  while (!it.done) {
11130
11255
  const token = it.value;
11131
11256
  this.consume(source, token, tokenStream);
11132
- if (token.type === exports.TokenType.TAG_OPEN) {
11257
+ if (token.type === TokenType.TAG_OPEN) {
11133
11258
  const [, close, tagName] = token.data;
11134
11259
  if (tagName === searchTag) {
11135
11260
  if (close) {
@@ -11202,6 +11327,12 @@ class Parser {
11202
11327
  }
11203
11328
  }
11204
11329
 
11330
+ function freeze(src) {
11331
+ return {
11332
+ ...src,
11333
+ selector: src.selector(),
11334
+ };
11335
+ }
11205
11336
  /**
11206
11337
  * @internal
11207
11338
  */
@@ -11253,7 +11384,9 @@ class Reporter {
11253
11384
  line: location.line,
11254
11385
  column: location.column,
11255
11386
  size: location.size || 0,
11256
- selector: node ? node.generateSelector() : null,
11387
+ selector() {
11388
+ return node ? node.generateSelector() : null;
11389
+ },
11257
11390
  context,
11258
11391
  });
11259
11392
  }
@@ -11267,7 +11400,7 @@ class Reporter {
11267
11400
  const report = {
11268
11401
  valid: this.isValid(),
11269
11402
  results: Object.keys(this.result).map((filePath) => {
11270
- const messages = Array.from(this.result[filePath]).sort(messageSort);
11403
+ const messages = Array.from(this.result[filePath], freeze).sort(messageSort);
11271
11404
  const source = (sources || []).find((source) => { var _a; return filePath === ((_a = source.filename) !== null && _a !== void 0 ? _a : ""); });
11272
11405
  return {
11273
11406
  filePath,
@@ -11420,7 +11553,7 @@ class Engine {
11420
11553
  for (const token of lexer.tokenize(src)) {
11421
11554
  const data = (_a = token.data[0]) !== null && _a !== void 0 ? _a : "";
11422
11555
  lines.push({
11423
- token: exports.TokenType[token.type],
11556
+ token: TokenType[token.type],
11424
11557
  data,
11425
11558
  location: `${token.location.filename}:${token.location.line}:${token.location.column}`,
11426
11559
  });
@@ -11669,7 +11802,7 @@ class Engine {
11669
11802
  line: location.line,
11670
11803
  column: location.column,
11671
11804
  size: location.size || 0,
11672
- selector: null,
11805
+ selector: () => null,
11673
11806
  });
11674
11807
  }
11675
11808
  }
@@ -12191,6 +12324,8 @@ const formatter$4 = checkstyleFormatter;
12191
12324
 
12192
12325
  const defaults = {
12193
12326
  showLink: true,
12327
+ showSummary: true,
12328
+ showSelector: false,
12194
12329
  };
12195
12330
  /**
12196
12331
  * Codeframe formatter based on ESLint codeframe.
@@ -12246,6 +12381,7 @@ function getEndLocation(message, source) {
12246
12381
  * @returns The formatted output.
12247
12382
  */
12248
12383
  function formatMessage(message, parentResult, options) {
12384
+ var _a;
12249
12385
  const type = message.severity === 2 ? kleur__default["default"].red("error") : kleur__default["default"].yellow("warning");
12250
12386
  const msg = `${kleur__default["default"].bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
12251
12387
  const ruleId = kleur__default["default"].dim(`(${message.ruleId})`);
@@ -12268,6 +12404,9 @@ function formatMessage(message, parentResult, options) {
12268
12404
  end: getEndLocation(message, sourceCode),
12269
12405
  }, { highlightCode: false }));
12270
12406
  }
12407
+ if (options.showSelector) {
12408
+ result.push(`${kleur__default["default"].bold("Selector:")} ${(_a = message.selector) !== null && _a !== void 0 ? _a : "-"}`);
12409
+ }
12271
12410
  if (options.showLink && message.ruleUrl) {
12272
12411
  result.push(`${kleur__default["default"].bold("Details:")} ${message.ruleUrl}`);
12273
12412
  }
@@ -12305,9 +12444,11 @@ function codeframe(results, options) {
12305
12444
  return resultsOutput.concat(messages);
12306
12445
  }, [])
12307
12446
  .join("\n");
12308
- output += "\n";
12309
- output += formatSummary(errors, warnings);
12310
- output += "\n";
12447
+ if (merged.showSummary) {
12448
+ output += "\n";
12449
+ output += formatSummary(errors, warnings);
12450
+ output += "\n";
12451
+ }
12311
12452
  return errors + warnings > 0 ? output : "";
12312
12453
  }
12313
12454
  const formatter$3 = codeframe;
@@ -12407,6 +12548,7 @@ exports.TemplateExtractor = TemplateExtractor;
12407
12548
  exports.TextNode = TextNode;
12408
12549
  exports.UserError = UserError;
12409
12550
  exports.bugs = bugs;
12551
+ exports.codeframe = codeframe;
12410
12552
  exports.compatibilityCheck = compatibilityCheck;
12411
12553
  exports.getFormatter = getFormatter;
12412
12554
  exports.legacyRequire = legacyRequire;