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/es/core.js CHANGED
@@ -1202,6 +1202,27 @@ class Attribute {
1202
1202
  }
1203
1203
  }
1204
1204
 
1205
+ function getCSSDeclarations(value) {
1206
+ return value
1207
+ .trim()
1208
+ .split(";")
1209
+ .filter(Boolean)
1210
+ .map((it) => {
1211
+ const [property, value] = it.split(":", 2);
1212
+ return [property.trim(), value ? value.trim() : ""];
1213
+ });
1214
+ }
1215
+ /**
1216
+ * @internal
1217
+ */
1218
+ function parseCssDeclaration(value) {
1219
+ if (!value || value instanceof DynamicValue) {
1220
+ return {};
1221
+ }
1222
+ const pairs = getCSSDeclarations(value);
1223
+ return Object.fromEntries(pairs);
1224
+ }
1225
+
1205
1226
  function sliceSize(size, begin, end) {
1206
1227
  if (typeof size !== "number") {
1207
1228
  return size;
@@ -1957,9 +1978,10 @@ class HtmlElement extends DOMNode {
1957
1978
  /* if a unique id is present, use it and short-circuit */
1958
1979
  if (cur.id) {
1959
1980
  const escaped = escapeSelectorComponent(cur.id);
1960
- const matches = root.querySelectorAll(`#${escaped}`);
1981
+ const selector = escaped.match(/^\d/) ? `[id="${escaped}"]` : `#${escaped}`;
1982
+ const matches = root.querySelectorAll(selector);
1961
1983
  if (matches.length === 1) {
1962
- parts.push(`#${escaped}`);
1984
+ parts.push(selector);
1963
1985
  break;
1964
1986
  }
1965
1987
  }
@@ -2150,6 +2172,10 @@ class HtmlElement extends DOMNode {
2150
2172
  get id() {
2151
2173
  return this.getAttributeValue("id");
2152
2174
  }
2175
+ get style() {
2176
+ const attr = this.getAttribute("style");
2177
+ return parseCssDeclaration(attr === null || attr === void 0 ? void 0 : attr.value);
2178
+ }
2153
2179
  /**
2154
2180
  * Returns the first child element or null if there are no child elements.
2155
2181
  */
@@ -2955,7 +2981,7 @@ var TRANSFORMER_API;
2955
2981
  /** @public */
2956
2982
  const name = "html-validate";
2957
2983
  /** @public */
2958
- const version = "6.7.0";
2984
+ const version = "6.9.0";
2959
2985
  /** @public */
2960
2986
  const homepage = "https://html-validate.org";
2961
2987
  /** @public */
@@ -3276,7 +3302,7 @@ function ruleDocumentationUrl(filename) {
3276
3302
  return `${homepage}/rules/${normalized}.html`;
3277
3303
  }
3278
3304
 
3279
- const defaults$q = {
3305
+ const defaults$r = {
3280
3306
  allowExternal: true,
3281
3307
  allowRelative: true,
3282
3308
  allowAbsolute: true,
@@ -3320,7 +3346,7 @@ function matchList(value, list) {
3320
3346
  }
3321
3347
  class AllowedLinks extends Rule {
3322
3348
  constructor(options) {
3323
- super({ ...defaults$q, ...options });
3349
+ super({ ...defaults$r, ...options });
3324
3350
  this.allowExternal = parseAllow(this.options.allowExternal);
3325
3351
  this.allowRelative = parseAllow(this.options.allowRelative);
3326
3352
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3621,13 +3647,13 @@ class CaseStyle {
3621
3647
  }
3622
3648
  }
3623
3649
 
3624
- const defaults$p = {
3650
+ const defaults$q = {
3625
3651
  style: "lowercase",
3626
3652
  ignoreForeign: true,
3627
3653
  };
3628
3654
  class AttrCase extends Rule {
3629
3655
  constructor(options) {
3630
- super({ ...defaults$p, ...options });
3656
+ super({ ...defaults$q, ...options });
3631
3657
  this.style = new CaseStyle(this.options.style, "attr-case");
3632
3658
  }
3633
3659
  static schema() {
@@ -3966,7 +3992,7 @@ class AttrDelimiter extends Rule {
3966
3992
  }
3967
3993
 
3968
3994
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
3969
- const defaults$o = {
3995
+ const defaults$p = {
3970
3996
  pattern: DEFAULT_PATTERN,
3971
3997
  ignoreForeign: true,
3972
3998
  };
@@ -4003,7 +4029,7 @@ function generateDescription(name, pattern) {
4003
4029
  }
4004
4030
  class AttrPattern extends Rule {
4005
4031
  constructor(options) {
4006
- super({ ...defaults$o, ...options });
4032
+ super({ ...defaults$p, ...options });
4007
4033
  this.pattern = generateRegexp(this.options.pattern);
4008
4034
  }
4009
4035
  static schema() {
@@ -4063,13 +4089,13 @@ var QuoteStyle;
4063
4089
  QuoteStyle["DOUBLE_QUOTE"] = "\"";
4064
4090
  QuoteStyle["AUTO_QUOTE"] = "auto";
4065
4091
  })(QuoteStyle || (QuoteStyle = {}));
4066
- const defaults$n = {
4092
+ const defaults$o = {
4067
4093
  style: "auto",
4068
4094
  unquoted: false,
4069
4095
  };
4070
4096
  class AttrQuotes extends Rule {
4071
4097
  constructor(options) {
4072
- super({ ...defaults$n, ...options });
4098
+ super({ ...defaults$o, ...options });
4073
4099
  this.style = parseStyle$4(this.options.style);
4074
4100
  }
4075
4101
  static schema() {
@@ -4234,12 +4260,12 @@ class AttributeAllowedValues extends Rule {
4234
4260
  }
4235
4261
  }
4236
4262
 
4237
- const defaults$m = {
4263
+ const defaults$n = {
4238
4264
  style: "omit",
4239
4265
  };
4240
4266
  class AttributeBooleanStyle extends Rule {
4241
4267
  constructor(options) {
4242
- super({ ...defaults$m, ...options });
4268
+ super({ ...defaults$n, ...options });
4243
4269
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4244
4270
  }
4245
4271
  static schema() {
@@ -4315,12 +4341,12 @@ function reportMessage$1(attr, style) {
4315
4341
  return "";
4316
4342
  }
4317
4343
 
4318
- const defaults$l = {
4344
+ const defaults$m = {
4319
4345
  style: "omit",
4320
4346
  };
4321
4347
  class AttributeEmptyStyle extends Rule {
4322
4348
  constructor(options) {
4323
- super({ ...defaults$l, ...options });
4349
+ super({ ...defaults$m, ...options });
4324
4350
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4325
4351
  }
4326
4352
  static schema() {
@@ -4428,12 +4454,12 @@ function describePattern(pattern) {
4428
4454
  }
4429
4455
  }
4430
4456
 
4431
- const defaults$k = {
4457
+ const defaults$l = {
4432
4458
  pattern: "kebabcase",
4433
4459
  };
4434
4460
  class ClassPattern extends Rule {
4435
4461
  constructor(options) {
4436
- super({ ...defaults$k, ...options });
4462
+ super({ ...defaults$l, ...options });
4437
4463
  this.pattern = parsePattern(this.options.pattern);
4438
4464
  }
4439
4465
  static schema() {
@@ -4542,13 +4568,13 @@ class CloseOrder extends Rule {
4542
4568
  }
4543
4569
  }
4544
4570
 
4545
- const defaults$j = {
4571
+ const defaults$k = {
4546
4572
  include: null,
4547
4573
  exclude: null,
4548
4574
  };
4549
4575
  class Deprecated extends Rule {
4550
4576
  constructor(options) {
4551
- super({ ...defaults$j, ...options });
4577
+ super({ ...defaults$k, ...options });
4552
4578
  }
4553
4579
  static schema() {
4554
4580
  return {
@@ -4711,12 +4737,12 @@ class NoStyleTag$1 extends Rule {
4711
4737
  }
4712
4738
  }
4713
4739
 
4714
- const defaults$i = {
4740
+ const defaults$j = {
4715
4741
  style: "uppercase",
4716
4742
  };
4717
4743
  class DoctypeStyle extends Rule {
4718
4744
  constructor(options) {
4719
- super({ ...defaults$i, ...options });
4745
+ super({ ...defaults$j, ...options });
4720
4746
  }
4721
4747
  static schema() {
4722
4748
  return {
@@ -4748,12 +4774,12 @@ class DoctypeStyle extends Rule {
4748
4774
  }
4749
4775
  }
4750
4776
 
4751
- const defaults$h = {
4777
+ const defaults$i = {
4752
4778
  style: "lowercase",
4753
4779
  };
4754
4780
  class ElementCase extends Rule {
4755
4781
  constructor(options) {
4756
- super({ ...defaults$h, ...options });
4782
+ super({ ...defaults$i, ...options });
4757
4783
  this.style = new CaseStyle(this.options.style, "element-case");
4758
4784
  }
4759
4785
  static schema() {
@@ -4819,14 +4845,14 @@ class ElementCase extends Rule {
4819
4845
  }
4820
4846
  }
4821
4847
 
4822
- const defaults$g = {
4848
+ const defaults$h = {
4823
4849
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
4824
4850
  whitelist: [],
4825
4851
  blacklist: [],
4826
4852
  };
4827
4853
  class ElementName extends Rule {
4828
4854
  constructor(options) {
4829
- super({ ...defaults$g, ...options });
4855
+ super({ ...defaults$h, ...options });
4830
4856
  // eslint-disable-next-line security/detect-non-literal-regexp
4831
4857
  this.pattern = new RegExp(this.options.pattern);
4832
4858
  }
@@ -4867,7 +4893,7 @@ class ElementName extends Rule {
4867
4893
  ...context.blacklist.map((cur) => `- ${cur}`),
4868
4894
  ];
4869
4895
  }
4870
- if (context.pattern !== defaults$g.pattern) {
4896
+ if (context.pattern !== defaults$h.pattern) {
4871
4897
  return [
4872
4898
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
4873
4899
  "",
@@ -5269,7 +5295,7 @@ class EmptyTitle extends Rule {
5269
5295
  }
5270
5296
  }
5271
5297
 
5272
- const defaults$f = {
5298
+ const defaults$g = {
5273
5299
  allowMultipleH1: false,
5274
5300
  minInitialRank: "h1",
5275
5301
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5300,7 +5326,7 @@ function parseMaxInitial(value) {
5300
5326
  }
5301
5327
  class HeadingLevel extends Rule {
5302
5328
  constructor(options) {
5303
- super({ ...defaults$f, ...options });
5329
+ super({ ...defaults$g, ...options });
5304
5330
  this.stack = [];
5305
5331
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5306
5332
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -5458,12 +5484,12 @@ class HeadingLevel extends Rule {
5458
5484
  }
5459
5485
  }
5460
5486
 
5461
- const defaults$e = {
5487
+ const defaults$f = {
5462
5488
  pattern: "kebabcase",
5463
5489
  };
5464
5490
  class IdPattern extends Rule {
5465
5491
  constructor(options) {
5466
- super({ ...defaults$e, ...options });
5492
+ super({ ...defaults$f, ...options });
5467
5493
  this.pattern = parsePattern(this.options.pattern);
5468
5494
  }
5469
5495
  static schema() {
@@ -5814,12 +5840,12 @@ function findLabelByParent(el) {
5814
5840
  return [];
5815
5841
  }
5816
5842
 
5817
- const defaults$d = {
5843
+ const defaults$e = {
5818
5844
  maxlength: 70,
5819
5845
  };
5820
5846
  class LongTitle extends Rule {
5821
5847
  constructor(options) {
5822
- super({ ...defaults$d, ...options });
5848
+ super({ ...defaults$e, ...options });
5823
5849
  this.maxlength = this.options.maxlength;
5824
5850
  }
5825
5851
  static schema() {
@@ -5966,13 +5992,13 @@ class MultipleLabeledControls extends Rule {
5966
5992
  }
5967
5993
  }
5968
5994
 
5969
- const defaults$c = {
5995
+ const defaults$d = {
5970
5996
  include: null,
5971
5997
  exclude: null,
5972
5998
  };
5973
5999
  class NoAutoplay extends Rule {
5974
6000
  constructor(options) {
5975
- super({ ...defaults$c, ...options });
6001
+ super({ ...defaults$d, ...options });
5976
6002
  }
5977
6003
  documentation(context) {
5978
6004
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6213,24 +6239,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6213
6239
  }
6214
6240
  }
6215
6241
 
6216
- const defaults$b = {
6242
+ const defaults$c = {
6217
6243
  include: null,
6218
6244
  exclude: null,
6219
6245
  allowedProperties: ["display"],
6220
6246
  };
6221
- function getCSSDeclarations(value) {
6222
- return value
6223
- .trim()
6224
- .split(";")
6225
- .filter(Boolean)
6226
- .map((it) => {
6227
- const [property, value] = it.split(":", 2);
6228
- return { property: property.trim(), value: value ? value.trim() : undefined };
6229
- });
6230
- }
6231
6247
  class NoInlineStyle extends Rule {
6232
6248
  constructor(options) {
6233
- super({ ...defaults$b, ...options });
6249
+ super({ ...defaults$c, ...options });
6234
6250
  }
6235
6251
  static schema() {
6236
6252
  return {
@@ -6308,18 +6324,15 @@ class NoInlineStyle extends Rule {
6308
6324
  return true;
6309
6325
  }
6310
6326
  allPropertiesAllowed(value) {
6311
- if (typeof value !== "string") {
6312
- return false;
6313
- }
6314
6327
  const allowProperties = this.options.allowedProperties;
6315
6328
  /* quick path: no properties are allowed, no need to check each one individually */
6316
6329
  if (allowProperties.length === 0) {
6317
6330
  return false;
6318
6331
  }
6319
- const declarations = getCSSDeclarations(value);
6332
+ const declarations = Object.keys(parseCssDeclaration(value));
6320
6333
  return (declarations.length > 0 &&
6321
6334
  declarations.every((it) => {
6322
- return allowProperties.includes(it.property);
6335
+ return allowProperties.includes(it);
6323
6336
  }));
6324
6337
  }
6325
6338
  }
@@ -6435,7 +6448,7 @@ class NoMultipleMain extends Rule {
6435
6448
  }
6436
6449
  }
6437
6450
 
6438
- const defaults$a = {
6451
+ const defaults$b = {
6439
6452
  relaxed: false,
6440
6453
  };
6441
6454
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6452,7 +6465,7 @@ const replacementTable = {
6452
6465
  };
6453
6466
  class NoRawCharacters extends Rule {
6454
6467
  constructor(options) {
6455
- super({ ...defaults$a, ...options });
6468
+ super({ ...defaults$b, ...options });
6456
6469
  this.relaxed = this.options.relaxed;
6457
6470
  }
6458
6471
  static schema() {
@@ -6630,13 +6643,13 @@ class NoRedundantRole extends Rule {
6630
6643
  }
6631
6644
 
6632
6645
  const xmlns = /^(.+):.+$/;
6633
- const defaults$9 = {
6646
+ const defaults$a = {
6634
6647
  ignoreForeign: true,
6635
6648
  ignoreXML: true,
6636
6649
  };
6637
6650
  class NoSelfClosing extends Rule {
6638
6651
  constructor(options) {
6639
- super({ ...defaults$9, ...options });
6652
+ super({ ...defaults$a, ...options });
6640
6653
  }
6641
6654
  static schema() {
6642
6655
  return {
@@ -6769,13 +6782,13 @@ const replacement = {
6769
6782
  reset: '<button type="reset">',
6770
6783
  image: '<button type="button">',
6771
6784
  };
6772
- const defaults$8 = {
6785
+ const defaults$9 = {
6773
6786
  include: null,
6774
6787
  exclude: null,
6775
6788
  };
6776
6789
  class PreferButton extends Rule {
6777
6790
  constructor(options) {
6778
- super({ ...defaults$8, ...options });
6791
+ super({ ...defaults$9, ...options });
6779
6792
  }
6780
6793
  static schema() {
6781
6794
  return {
@@ -6850,7 +6863,7 @@ class PreferButton extends Rule {
6850
6863
  }
6851
6864
  }
6852
6865
 
6853
- const defaults$7 = {
6866
+ const defaults$8 = {
6854
6867
  mapping: {
6855
6868
  article: "article",
6856
6869
  banner: "header",
@@ -6880,7 +6893,7 @@ const defaults$7 = {
6880
6893
  };
6881
6894
  class PreferNativeElement extends Rule {
6882
6895
  constructor(options) {
6883
- super({ ...defaults$7, ...options });
6896
+ super({ ...defaults$8, ...options });
6884
6897
  }
6885
6898
  static schema() {
6886
6899
  return {
@@ -7000,7 +7013,7 @@ class PreferTbody extends Rule {
7000
7013
  }
7001
7014
  }
7002
7015
 
7003
- const defaults$6 = {
7016
+ const defaults$7 = {
7004
7017
  target: "all",
7005
7018
  };
7006
7019
  const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
@@ -7010,7 +7023,7 @@ const supportSri = {
7010
7023
  };
7011
7024
  class RequireSri extends Rule {
7012
7025
  constructor(options) {
7013
- super({ ...defaults$6, ...options });
7026
+ super({ ...defaults$7, ...options });
7014
7027
  this.target = this.options.target;
7015
7028
  }
7016
7029
  static schema() {
@@ -7138,12 +7151,13 @@ class SvgFocusable extends Rule {
7138
7151
  }
7139
7152
  }
7140
7153
 
7141
- const defaults$5 = {
7154
+ const defaults$6 = {
7142
7155
  characters: [
7143
7156
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
7144
7157
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" },
7145
7158
  ],
7146
7159
  ignoreClasses: [],
7160
+ ignoreStyle: true,
7147
7161
  };
7148
7162
  function constructRegex(characters) {
7149
7163
  const disallowed = characters
@@ -7180,7 +7194,7 @@ function matchAll(text, regexp) {
7180
7194
  }
7181
7195
  class TelNonBreaking extends Rule {
7182
7196
  constructor(options) {
7183
- super({ ...defaults$5, ...options });
7197
+ super({ ...defaults$6, ...options });
7184
7198
  this.regex = constructRegex(this.options.characters);
7185
7199
  }
7186
7200
  static schema() {
@@ -7209,6 +7223,9 @@ class TelNonBreaking extends Rule {
7209
7223
  type: "string",
7210
7224
  },
7211
7225
  },
7226
+ ignoreStyle: {
7227
+ type: "boolean",
7228
+ },
7212
7229
  };
7213
7230
  }
7214
7231
  documentation(context) {
@@ -7235,13 +7252,10 @@ class TelNonBreaking extends Rule {
7235
7252
  setup() {
7236
7253
  this.on("element:ready", this.isRelevant, (event) => {
7237
7254
  const { target } = event;
7238
- const { ignoreClasses } = this.options;
7239
- /* skip if element has a class in the ignore list */
7240
- const isIgnored = ignoreClasses.some((it) => target.classList.contains(it));
7241
- if (isIgnored) {
7255
+ if (this.isIgnored(target)) {
7242
7256
  return;
7243
7257
  }
7244
- this.walk(target);
7258
+ this.walk(target, target);
7245
7259
  });
7246
7260
  }
7247
7261
  isRelevant(event) {
@@ -7257,17 +7271,36 @@ class TelNonBreaking extends Rule {
7257
7271
  }
7258
7272
  return true;
7259
7273
  }
7260
- walk(node) {
7274
+ isIgnoredClass(node) {
7275
+ const { ignoreClasses } = this.options;
7276
+ const { classList } = node;
7277
+ return ignoreClasses.some((it) => classList.contains(it));
7278
+ }
7279
+ isIgnoredStyle(node) {
7280
+ const { ignoreStyle } = this.options;
7281
+ const { style } = node;
7282
+ if (!ignoreStyle) {
7283
+ return false;
7284
+ }
7285
+ if (style["white-space"] === "nowrap" || style["white-space"] === "pre") {
7286
+ return true;
7287
+ }
7288
+ return false;
7289
+ }
7290
+ isIgnored(node) {
7291
+ return this.isIgnoredClass(node) || this.isIgnoredStyle(node);
7292
+ }
7293
+ walk(anchor, node) {
7261
7294
  for (const child of node.childNodes) {
7262
7295
  if (isTextNode(child)) {
7263
- this.detectDisallowed(child);
7296
+ this.detectDisallowed(anchor, child);
7264
7297
  }
7265
7298
  else if (isElementNode(child)) {
7266
- this.walk(child);
7299
+ this.walk(anchor, child);
7267
7300
  }
7268
7301
  }
7269
7302
  }
7270
- detectDisallowed(node) {
7303
+ detectDisallowed(anchor, node) {
7271
7304
  const [offset, text] = getText(node);
7272
7305
  const matches = matchAll(text, this.regex);
7273
7306
  for (const match of matches) {
@@ -7277,12 +7310,12 @@ class TelNonBreaking extends Rule {
7277
7310
  if (!entry) {
7278
7311
  throw new Error(`Failed to find entry for "${detected}" when searching text "${text}"`);
7279
7312
  }
7280
- const message = `"${detected}" should be replaced with "${entry.replacement}" in telephone number`;
7313
+ const message = `"${detected}" should be replaced with "${entry.replacement}" (${entry.description}) in telephone number`;
7281
7314
  const begin = offset + match.index;
7282
7315
  const end = begin + detected.length;
7283
7316
  const location = sliceLocation(node.location, begin, end);
7284
7317
  const context = entry;
7285
- this.report(node, message, location, context);
7318
+ this.report(anchor, message, location, context);
7286
7319
  }
7287
7320
  }
7288
7321
  }
@@ -9240,6 +9273,95 @@ class UnknownCharReference extends Rule {
9240
9273
  }
9241
9274
  }
9242
9275
 
9276
+ var RuleContext;
9277
+ (function (RuleContext) {
9278
+ RuleContext[RuleContext["EMPTY"] = 1] = "EMPTY";
9279
+ RuleContext[RuleContext["WHITESPACE"] = 2] = "WHITESPACE";
9280
+ RuleContext[RuleContext["LEADING_CHARACTER"] = 3] = "LEADING_CHARACTER";
9281
+ RuleContext[RuleContext["DISALLOWED_CHARACTER"] = 4] = "DISALLOWED_CHARACTER";
9282
+ })(RuleContext || (RuleContext = {}));
9283
+ const defaults$5 = {
9284
+ relaxed: false,
9285
+ };
9286
+ class ValidID extends Rule {
9287
+ constructor(options) {
9288
+ super({ ...defaults$5, ...options });
9289
+ }
9290
+ static schema() {
9291
+ return {
9292
+ relaxed: {
9293
+ type: "boolean",
9294
+ },
9295
+ };
9296
+ }
9297
+ documentation(context) {
9298
+ const { relaxed } = this.options;
9299
+ const message = context
9300
+ ? this.messages[context].replace("id", "ID").replace(/^(.)/, (m) => m.toUpperCase())
9301
+ : "Element ID is not valid";
9302
+ const relaxedDescription = relaxed
9303
+ ? []
9304
+ : [
9305
+ " - ID must begin with a letter",
9306
+ " - ID must only contain alphanumerical characters, `-` and `_`",
9307
+ ];
9308
+ return {
9309
+ description: [
9310
+ `${message}.`,
9311
+ "",
9312
+ "Under the current configuration the following rules are applied:",
9313
+ "",
9314
+ " - ID must not be empty",
9315
+ " - ID must not contain any whitespace characters",
9316
+ ...relaxedDescription,
9317
+ ].join("\n"),
9318
+ url: ruleDocumentationUrl("@/rules/valid-id.ts"),
9319
+ };
9320
+ }
9321
+ setup() {
9322
+ this.on("attr", this.isRelevant, (event) => {
9323
+ const { value } = event;
9324
+ if (value === null || value instanceof DynamicValue) {
9325
+ return;
9326
+ }
9327
+ if (value === "") {
9328
+ const context = RuleContext.EMPTY;
9329
+ this.report(event.target, this.messages[context], event.location, context);
9330
+ return;
9331
+ }
9332
+ if (value.match(/\s/)) {
9333
+ const context = RuleContext.WHITESPACE;
9334
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9335
+ return;
9336
+ }
9337
+ const { relaxed } = this.options;
9338
+ if (relaxed) {
9339
+ return;
9340
+ }
9341
+ if (value.match(/^[^a-zA-Z]/)) {
9342
+ const context = RuleContext.LEADING_CHARACTER;
9343
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9344
+ return;
9345
+ }
9346
+ if (value.match(/[^a-zA-Z0-9-_]/)) {
9347
+ const context = RuleContext.DISALLOWED_CHARACTER;
9348
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9349
+ }
9350
+ });
9351
+ }
9352
+ get messages() {
9353
+ return {
9354
+ [RuleContext.EMPTY]: "element id must not be empty",
9355
+ [RuleContext.WHITESPACE]: "element id must not contain whitespace",
9356
+ [RuleContext.LEADING_CHARACTER]: "element id must begin with a letter",
9357
+ [RuleContext.DISALLOWED_CHARACTER]: "element id must only contain alphanumerical, dash and underscore characters",
9358
+ };
9359
+ }
9360
+ isRelevant(event) {
9361
+ return event.key === "id";
9362
+ }
9363
+ }
9364
+
9243
9365
  var Style$1;
9244
9366
  (function (Style) {
9245
9367
  Style[Style["Any"] = 0] = "Any";
@@ -9751,6 +9873,7 @@ const bundledRules = {
9751
9873
  "tel-non-breaking": TelNonBreaking,
9752
9874
  "text-content": TextContent,
9753
9875
  "unrecognized-char-ref": UnknownCharReference,
9876
+ "valid-id": ValidID,
9754
9877
  void: Void,
9755
9878
  "void-content": VoidContent,
9756
9879
  "void-style": VoidStyle,
@@ -9848,6 +9971,7 @@ const config$1 = {
9848
9971
  "tel-non-breaking": "error",
9849
9972
  "text-content": "error",
9850
9973
  "unrecognized-char-ref": "error",
9974
+ "valid-id": ["error", { relaxed: false }],
9851
9975
  void: "off",
9852
9976
  "void-content": "error",
9853
9977
  "void-style": "error",
@@ -9883,6 +10007,7 @@ const config = {
9883
10007
  "no-raw-characters": ["error", { relaxed: true }],
9884
10008
  "script-element": "error",
9885
10009
  "unrecognized-char-ref": "error",
10010
+ "valid-id": ["error", { relaxed: true }],
9886
10011
  "void-content": "error",
9887
10012
  },
9888
10013
  };
@@ -10212,7 +10337,7 @@ class Config {
10212
10337
  }
10213
10338
  let filename;
10214
10339
  /* try searching builtin metadata */
10215
- filename = path.join(projectRoot, "elements", `${entry}.json`);
10340
+ filename = path.join(projectRoot, "elements", `${entry}.js`);
10216
10341
  if (fs.existsSync(filename)) {
10217
10342
  metaTable.loadFromFile(filename);
10218
10343
  continue;
@@ -11171,6 +11296,12 @@ class Parser {
11171
11296
  }
11172
11297
  }
11173
11298
 
11299
+ function freeze(src) {
11300
+ return {
11301
+ ...src,
11302
+ selector: src.selector(),
11303
+ };
11304
+ }
11174
11305
  /**
11175
11306
  * @internal
11176
11307
  */
@@ -11222,7 +11353,9 @@ class Reporter {
11222
11353
  line: location.line,
11223
11354
  column: location.column,
11224
11355
  size: location.size || 0,
11225
- selector: node ? node.generateSelector() : null,
11356
+ selector() {
11357
+ return node ? node.generateSelector() : null;
11358
+ },
11226
11359
  context,
11227
11360
  });
11228
11361
  }
@@ -11236,7 +11369,7 @@ class Reporter {
11236
11369
  const report = {
11237
11370
  valid: this.isValid(),
11238
11371
  results: Object.keys(this.result).map((filePath) => {
11239
- const messages = Array.from(this.result[filePath]).sort(messageSort);
11372
+ const messages = Array.from(this.result[filePath], freeze).sort(messageSort);
11240
11373
  const source = (sources || []).find((source) => { var _a; return filePath === ((_a = source.filename) !== null && _a !== void 0 ? _a : ""); });
11241
11374
  return {
11242
11375
  filePath,
@@ -11638,7 +11771,7 @@ class Engine {
11638
11771
  line: location.line,
11639
11772
  column: location.column,
11640
11773
  size: location.size || 0,
11641
- selector: null,
11774
+ selector: () => null,
11642
11775
  });
11643
11776
  }
11644
11777
  }
@@ -12160,6 +12293,8 @@ const formatter$4 = checkstyleFormatter;
12160
12293
 
12161
12294
  const defaults = {
12162
12295
  showLink: true,
12296
+ showSummary: true,
12297
+ showSelector: false,
12163
12298
  };
12164
12299
  /**
12165
12300
  * Codeframe formatter based on ESLint codeframe.
@@ -12215,6 +12350,7 @@ function getEndLocation(message, source) {
12215
12350
  * @returns The formatted output.
12216
12351
  */
12217
12352
  function formatMessage(message, parentResult, options) {
12353
+ var _a;
12218
12354
  const type = message.severity === 2 ? kleur.red("error") : kleur.yellow("warning");
12219
12355
  const msg = `${kleur.bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
12220
12356
  const ruleId = kleur.dim(`(${message.ruleId})`);
@@ -12237,6 +12373,9 @@ function formatMessage(message, parentResult, options) {
12237
12373
  end: getEndLocation(message, sourceCode),
12238
12374
  }, { highlightCode: false }));
12239
12375
  }
12376
+ if (options.showSelector) {
12377
+ result.push(`${kleur.bold("Selector:")} ${(_a = message.selector) !== null && _a !== void 0 ? _a : "-"}`);
12378
+ }
12240
12379
  if (options.showLink && message.ruleUrl) {
12241
12380
  result.push(`${kleur.bold("Details:")} ${message.ruleUrl}`);
12242
12381
  }
@@ -12274,9 +12413,11 @@ function codeframe(results, options) {
12274
12413
  return resultsOutput.concat(messages);
12275
12414
  }, [])
12276
12415
  .join("\n");
12277
- output += "\n";
12278
- output += formatSummary(errors, warnings);
12279
- output += "\n";
12416
+ if (merged.showSummary) {
12417
+ output += "\n";
12418
+ output += formatSummary(errors, warnings);
12419
+ output += "\n";
12420
+ }
12280
12421
  return errors + warnings > 0 ? output : "";
12281
12422
  }
12282
12423
  const formatter$3 = codeframe;
@@ -12357,5 +12498,5 @@ function getFormatter(name) {
12357
12498
  return (_a = availableFormatters[name]) !== null && _a !== void 0 ? _a : null;
12358
12499
  }
12359
12500
 
12360
- export { Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, MetaCopyableProperty as f, Reporter as g, TemplateExtractor as h, getFormatter as i, compatibilityCheck as j, TokenType as k, legacyRequire as l, bugs as m, name as n, presets as p, ruleExists as r, version as v };
12501
+ export { Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, MetaCopyableProperty as f, Reporter as g, TemplateExtractor as h, getFormatter as i, compatibilityCheck as j, codeframe as k, legacyRequire as l, bugs as m, name as n, presets as p, ruleExists as r, version as v };
12361
12502
  //# sourceMappingURL=core.js.map