html-validate 7.8.0 → 7.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
@@ -3533,7 +3533,7 @@ class Rule {
3533
3533
  }
3534
3534
  }
3535
3535
 
3536
- const defaults$t = {
3536
+ const defaults$u = {
3537
3537
  allowExternal: true,
3538
3538
  allowRelative: true,
3539
3539
  allowAbsolute: true,
@@ -3577,7 +3577,7 @@ function matchList(value, list) {
3577
3577
  }
3578
3578
  class AllowedLinks extends Rule {
3579
3579
  constructor(options) {
3580
- super({ ...defaults$t, ...options });
3580
+ super({ ...defaults$u, ...options });
3581
3581
  this.allowExternal = parseAllow(this.options.allowExternal);
3582
3582
  this.allowRelative = parseAllow(this.options.allowRelative);
3583
3583
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3725,7 +3725,7 @@ var RuleContext$1;
3725
3725
  RuleContext["MISSING_ALT"] = "missing-alt";
3726
3726
  RuleContext["MISSING_HREF"] = "missing-href";
3727
3727
  })(RuleContext$1 || (RuleContext$1 = {}));
3728
- const defaults$s = {
3728
+ const defaults$t = {
3729
3729
  accessible: true,
3730
3730
  };
3731
3731
  function findByTarget(target, siblings) {
@@ -3734,7 +3734,7 @@ function findByTarget(target, siblings) {
3734
3734
  function getAltText(node) {
3735
3735
  return node.getAttributeValue("alt");
3736
3736
  }
3737
- function getDescription(context) {
3737
+ function getDescription$1(context) {
3738
3738
  switch (context) {
3739
3739
  case RuleContext$1.MISSING_ALT:
3740
3740
  return [
@@ -3763,7 +3763,7 @@ function getDescription(context) {
3763
3763
  }
3764
3764
  class AreaAlt extends Rule {
3765
3765
  constructor(options) {
3766
- super({ ...defaults$s, ...options });
3766
+ super({ ...defaults$t, ...options });
3767
3767
  }
3768
3768
  static schema() {
3769
3769
  return {
@@ -3774,7 +3774,7 @@ class AreaAlt extends Rule {
3774
3774
  }
3775
3775
  documentation(context) {
3776
3776
  return {
3777
- description: getDescription(context).join("\n"),
3777
+ description: getDescription$1(context).join("\n"),
3778
3778
  url: "https://html-validate.org/rules/area-alt.html",
3779
3779
  };
3780
3780
  }
@@ -3927,13 +3927,13 @@ class AriaLabelMisuse extends Rule {
3927
3927
  class ConfigError extends UserError {
3928
3928
  }
3929
3929
 
3930
- const defaults$r = {
3930
+ const defaults$s = {
3931
3931
  style: "lowercase",
3932
3932
  ignoreForeign: true,
3933
3933
  };
3934
3934
  class AttrCase extends Rule {
3935
3935
  constructor(options) {
3936
- super({ ...defaults$r, ...options });
3936
+ super({ ...defaults$s, ...options });
3937
3937
  this.style = new CaseStyle(this.options.style, "attr-case");
3938
3938
  }
3939
3939
  static schema() {
@@ -4278,7 +4278,7 @@ class AttrDelimiter extends Rule {
4278
4278
  }
4279
4279
 
4280
4280
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4281
- const defaults$q = {
4281
+ const defaults$r = {
4282
4282
  pattern: DEFAULT_PATTERN,
4283
4283
  ignoreForeign: true,
4284
4284
  };
@@ -4315,7 +4315,7 @@ function generateDescription(name, pattern) {
4315
4315
  }
4316
4316
  class AttrPattern extends Rule {
4317
4317
  constructor(options) {
4318
- super({ ...defaults$q, ...options });
4318
+ super({ ...defaults$r, ...options });
4319
4319
  this.pattern = generateRegexp(this.options.pattern);
4320
4320
  }
4321
4321
  static schema() {
@@ -4376,7 +4376,7 @@ var QuoteStyle;
4376
4376
  QuoteStyle["AUTO_QUOTE"] = "auto";
4377
4377
  QuoteStyle["ANY_QUOTE"] = "any";
4378
4378
  })(QuoteStyle || (QuoteStyle = {}));
4379
- const defaults$p = {
4379
+ const defaults$q = {
4380
4380
  style: "auto",
4381
4381
  unquoted: false,
4382
4382
  };
@@ -4417,7 +4417,7 @@ function describeStyle(style, unquoted) {
4417
4417
  }
4418
4418
  class AttrQuotes extends Rule {
4419
4419
  constructor(options) {
4420
- super({ ...defaults$p, ...options });
4420
+ super({ ...defaults$q, ...options });
4421
4421
  this.style = parseStyle$4(this.options.style);
4422
4422
  }
4423
4423
  static schema() {
@@ -4613,12 +4613,12 @@ class AttributeAllowedValues extends Rule {
4613
4613
  }
4614
4614
  }
4615
4615
 
4616
- const defaults$o = {
4616
+ const defaults$p = {
4617
4617
  style: "omit",
4618
4618
  };
4619
4619
  class AttributeBooleanStyle extends Rule {
4620
4620
  constructor(options) {
4621
- super({ ...defaults$o, ...options });
4621
+ super({ ...defaults$p, ...options });
4622
4622
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4623
4623
  }
4624
4624
  static schema() {
@@ -4694,12 +4694,12 @@ function reportMessage$1(attr, style) {
4694
4694
  return "";
4695
4695
  }
4696
4696
 
4697
- const defaults$n = {
4697
+ const defaults$o = {
4698
4698
  style: "omit",
4699
4699
  };
4700
4700
  class AttributeEmptyStyle extends Rule {
4701
4701
  constructor(options) {
4702
- super({ ...defaults$n, ...options });
4702
+ super({ ...defaults$o, ...options });
4703
4703
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4704
4704
  }
4705
4705
  static schema() {
@@ -4855,12 +4855,12 @@ function describePattern(pattern) {
4855
4855
  }
4856
4856
  }
4857
4857
 
4858
- const defaults$m = {
4858
+ const defaults$n = {
4859
4859
  pattern: "kebabcase",
4860
4860
  };
4861
4861
  class ClassPattern extends Rule {
4862
4862
  constructor(options) {
4863
- super({ ...defaults$m, ...options });
4863
+ super({ ...defaults$n, ...options });
4864
4864
  this.pattern = parsePattern(this.options.pattern);
4865
4865
  }
4866
4866
  static schema() {
@@ -4969,13 +4969,13 @@ class CloseOrder extends Rule {
4969
4969
  }
4970
4970
  }
4971
4971
 
4972
- const defaults$l = {
4972
+ const defaults$m = {
4973
4973
  include: null,
4974
4974
  exclude: null,
4975
4975
  };
4976
4976
  class Deprecated extends Rule {
4977
4977
  constructor(options) {
4978
- super({ ...defaults$l, ...options });
4978
+ super({ ...defaults$m, ...options });
4979
4979
  }
4980
4980
  static schema() {
4981
4981
  return {
@@ -5138,12 +5138,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5138
5138
  }
5139
5139
  };
5140
5140
 
5141
- const defaults$k = {
5141
+ const defaults$l = {
5142
5142
  style: "uppercase",
5143
5143
  };
5144
5144
  class DoctypeStyle extends Rule {
5145
5145
  constructor(options) {
5146
- super({ ...defaults$k, ...options });
5146
+ super({ ...defaults$l, ...options });
5147
5147
  }
5148
5148
  static schema() {
5149
5149
  return {
@@ -5175,12 +5175,12 @@ class DoctypeStyle extends Rule {
5175
5175
  }
5176
5176
  }
5177
5177
 
5178
- const defaults$j = {
5178
+ const defaults$k = {
5179
5179
  style: "lowercase",
5180
5180
  };
5181
5181
  class ElementCase extends Rule {
5182
5182
  constructor(options) {
5183
- super({ ...defaults$j, ...options });
5183
+ super({ ...defaults$k, ...options });
5184
5184
  this.style = new CaseStyle(this.options.style, "element-case");
5185
5185
  }
5186
5186
  static schema() {
@@ -5246,14 +5246,14 @@ class ElementCase extends Rule {
5246
5246
  }
5247
5247
  }
5248
5248
 
5249
- const defaults$i = {
5249
+ const defaults$j = {
5250
5250
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5251
5251
  whitelist: [],
5252
5252
  blacklist: [],
5253
5253
  };
5254
5254
  class ElementName extends Rule {
5255
5255
  constructor(options) {
5256
- super({ ...defaults$i, ...options });
5256
+ super({ ...defaults$j, ...options });
5257
5257
  // eslint-disable-next-line security/detect-non-literal-regexp
5258
5258
  this.pattern = new RegExp(this.options.pattern);
5259
5259
  }
@@ -5294,7 +5294,7 @@ class ElementName extends Rule {
5294
5294
  ...context.blacklist.map((cur) => `- ${cur}`),
5295
5295
  ];
5296
5296
  }
5297
- if (context.pattern !== defaults$i.pattern) {
5297
+ if (context.pattern !== defaults$j.pattern) {
5298
5298
  return [
5299
5299
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5300
5300
  "",
@@ -5842,7 +5842,7 @@ class EmptyTitle extends Rule {
5842
5842
  }
5843
5843
  }
5844
5844
 
5845
- const defaults$h = {
5845
+ const defaults$i = {
5846
5846
  allowMultipleH1: false,
5847
5847
  minInitialRank: "h1",
5848
5848
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5873,7 +5873,7 @@ function parseMaxInitial(value) {
5873
5873
  }
5874
5874
  class HeadingLevel extends Rule {
5875
5875
  constructor(options) {
5876
- super({ ...defaults$h, ...options });
5876
+ super({ ...defaults$i, ...options });
5877
5877
  this.stack = [];
5878
5878
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5879
5879
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -6031,12 +6031,12 @@ class HeadingLevel extends Rule {
6031
6031
  }
6032
6032
  }
6033
6033
 
6034
- const defaults$g = {
6034
+ const defaults$h = {
6035
6035
  pattern: "kebabcase",
6036
6036
  };
6037
6037
  class IdPattern extends Rule {
6038
6038
  constructor(options) {
6039
- super({ ...defaults$g, ...options });
6039
+ super({ ...defaults$h, ...options });
6040
6040
  this.pattern = parsePattern(this.options.pattern);
6041
6041
  }
6042
6042
  static schema() {
@@ -6318,12 +6318,12 @@ function findLabelByParent(el) {
6318
6318
  return [];
6319
6319
  }
6320
6320
 
6321
- const defaults$f = {
6321
+ const defaults$g = {
6322
6322
  maxlength: 70,
6323
6323
  };
6324
6324
  class LongTitle extends Rule {
6325
6325
  constructor(options) {
6326
- super({ ...defaults$f, ...options });
6326
+ super({ ...defaults$g, ...options });
6327
6327
  this.maxlength = this.options.maxlength;
6328
6328
  }
6329
6329
  static schema() {
@@ -6406,6 +6406,49 @@ function parseContent(text) {
6406
6406
  }
6407
6407
  }
6408
6408
 
6409
+ function getName(attr) {
6410
+ const name = attr.value;
6411
+ if (!name || name instanceof DynamicValue) {
6412
+ return null;
6413
+ }
6414
+ return name;
6415
+ }
6416
+ class MapDupName extends Rule {
6417
+ documentation() {
6418
+ return {
6419
+ description: "`<map>` must have a unique name, it cannot be the same name as another `<map>` element",
6420
+ url: "https://html-validate.org/rules/map-dup-name.html",
6421
+ };
6422
+ }
6423
+ setup() {
6424
+ this.on("dom:ready", (event) => {
6425
+ const { document } = event;
6426
+ const maps = document.querySelectorAll("map[name]");
6427
+ const names = new Set();
6428
+ for (const map of maps) {
6429
+ const attr = map.getAttribute("name");
6430
+ /* istanbul ignore next -- should not happen as querySelector matches
6431
+ * only the elements with the name attribute */
6432
+ if (!attr) {
6433
+ continue;
6434
+ }
6435
+ const name = getName(attr);
6436
+ if (!name) {
6437
+ continue;
6438
+ }
6439
+ if (names.has(name)) {
6440
+ this.report({
6441
+ node: map,
6442
+ message: `<map> name must be unique`,
6443
+ location: attr.keyLocation,
6444
+ });
6445
+ }
6446
+ names.add(name);
6447
+ }
6448
+ });
6449
+ }
6450
+ }
6451
+
6409
6452
  class MissingDoctype extends Rule {
6410
6453
  documentation() {
6411
6454
  return {
@@ -6470,13 +6513,13 @@ class MultipleLabeledControls extends Rule {
6470
6513
  }
6471
6514
  }
6472
6515
 
6473
- const defaults$e = {
6516
+ const defaults$f = {
6474
6517
  include: null,
6475
6518
  exclude: null,
6476
6519
  };
6477
6520
  class NoAutoplay extends Rule {
6478
6521
  constructor(options) {
6479
- super({ ...defaults$e, ...options });
6522
+ super({ ...defaults$f, ...options });
6480
6523
  }
6481
6524
  documentation(context) {
6482
6525
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6717,14 +6760,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6717
6760
  }
6718
6761
  }
6719
6762
 
6720
- const defaults$d = {
6763
+ const defaults$e = {
6721
6764
  include: null,
6722
6765
  exclude: null,
6723
6766
  allowedProperties: ["display"],
6724
6767
  };
6725
6768
  class NoInlineStyle extends Rule {
6726
6769
  constructor(options) {
6727
- super({ ...defaults$d, ...options });
6770
+ super({ ...defaults$e, ...options });
6728
6771
  }
6729
6772
  static schema() {
6730
6773
  return {
@@ -6926,7 +6969,7 @@ class NoMultipleMain extends Rule {
6926
6969
  }
6927
6970
  }
6928
6971
 
6929
- const defaults$c = {
6972
+ const defaults$d = {
6930
6973
  relaxed: false,
6931
6974
  };
6932
6975
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6943,7 +6986,7 @@ const replacementTable = {
6943
6986
  };
6944
6987
  class NoRawCharacters extends Rule {
6945
6988
  constructor(options) {
6946
- super({ ...defaults$c, ...options });
6989
+ super({ ...defaults$d, ...options });
6947
6990
  this.relaxed = this.options.relaxed;
6948
6991
  }
6949
6992
  static schema() {
@@ -7121,13 +7164,13 @@ class NoRedundantRole extends Rule {
7121
7164
  }
7122
7165
 
7123
7166
  const xmlns = /^(.+):.+$/;
7124
- const defaults$b = {
7167
+ const defaults$c = {
7125
7168
  ignoreForeign: true,
7126
7169
  ignoreXML: true,
7127
7170
  };
7128
7171
  class NoSelfClosing extends Rule {
7129
7172
  constructor(options) {
7130
- super({ ...defaults$b, ...options });
7173
+ super({ ...defaults$c, ...options });
7131
7174
  }
7132
7175
  static schema() {
7133
7176
  return {
@@ -7260,13 +7303,13 @@ const replacement = {
7260
7303
  reset: '<button type="reset">',
7261
7304
  image: '<button type="button">',
7262
7305
  };
7263
- const defaults$a = {
7306
+ const defaults$b = {
7264
7307
  include: null,
7265
7308
  exclude: null,
7266
7309
  };
7267
7310
  class PreferButton extends Rule {
7268
7311
  constructor(options) {
7269
- super({ ...defaults$a, ...options });
7312
+ super({ ...defaults$b, ...options });
7270
7313
  }
7271
7314
  static schema() {
7272
7315
  return {
@@ -7341,7 +7384,7 @@ class PreferButton extends Rule {
7341
7384
  }
7342
7385
  }
7343
7386
 
7344
- const defaults$9 = {
7387
+ const defaults$a = {
7345
7388
  mapping: {
7346
7389
  article: "article",
7347
7390
  banner: "header",
@@ -7371,7 +7414,7 @@ const defaults$9 = {
7371
7414
  };
7372
7415
  class PreferNativeElement extends Rule {
7373
7416
  constructor(options) {
7374
- super({ ...defaults$9, ...options });
7417
+ super({ ...defaults$a, ...options });
7375
7418
  }
7376
7419
  static schema() {
7377
7420
  return {
@@ -7491,12 +7534,12 @@ class PreferTbody extends Rule {
7491
7534
  }
7492
7535
  }
7493
7536
 
7494
- const defaults$8 = {
7537
+ const defaults$9 = {
7495
7538
  tags: ["script", "style"],
7496
7539
  };
7497
7540
  class RequireCSPNonce extends Rule {
7498
7541
  constructor(options) {
7499
- super({ ...defaults$8, ...options });
7542
+ super({ ...defaults$9, ...options });
7500
7543
  }
7501
7544
  static schema() {
7502
7545
  return {
@@ -7547,7 +7590,7 @@ class RequireCSPNonce extends Rule {
7547
7590
  }
7548
7591
  }
7549
7592
 
7550
- const defaults$7 = {
7593
+ const defaults$8 = {
7551
7594
  target: "all",
7552
7595
  include: null,
7553
7596
  exclude: null,
@@ -7559,7 +7602,7 @@ const supportSri = {
7559
7602
  };
7560
7603
  class RequireSri extends Rule {
7561
7604
  constructor(options) {
7562
- super({ ...defaults$7, ...options });
7605
+ super({ ...defaults$8, ...options });
7563
7606
  this.target = this.options.target;
7564
7607
  }
7565
7608
  static schema() {
@@ -7721,7 +7764,7 @@ class SvgFocusable extends Rule {
7721
7764
  }
7722
7765
  }
7723
7766
 
7724
- const defaults$6 = {
7767
+ const defaults$7 = {
7725
7768
  characters: [
7726
7769
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
7727
7770
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" },
@@ -7764,7 +7807,7 @@ function matchAll(text, regexp) {
7764
7807
  }
7765
7808
  class TelNonBreaking extends Rule {
7766
7809
  constructor(options) {
7767
- super({ ...defaults$6, ...options });
7810
+ super({ ...defaults$7, ...options });
7768
7811
  this.regex = constructRegex(this.options.characters);
7769
7812
  }
7770
7813
  static schema() {
@@ -8052,11 +8095,63 @@ class TextContent extends Rule {
8052
8095
  }
8053
8096
  }
8054
8097
 
8055
- const regexp$1 = /&([a-z0-9]+|#x?[0-9a-f]+);/gi;
8098
+ const defaults$6 = {
8099
+ ignoreCase: false,
8100
+ requireSemicolon: true,
8101
+ };
8102
+ const regexp$1 = /&(?:[a-z0-9]+|#x?[0-9a-f]+)(;|[^a-z0-9]|$)/gi;
8103
+ const lowercaseEntities = entities$1.map((it) => it.toLowerCase());
8104
+ function isNumerical(entity) {
8105
+ return entity.startsWith("&#");
8106
+ }
8107
+ function getLocation(location, entity, match) {
8108
+ var _a;
8109
+ /* istanbul ignore next: never happens in practive */
8110
+ const index = (_a = match.index) !== null && _a !== void 0 ? _a : 0;
8111
+ return sliceLocation(location, index, index + entity.length);
8112
+ }
8113
+ function getDescription(context, options) {
8114
+ const url = "https://html.spec.whatwg.org/multipage/named-characters.html";
8115
+ let message;
8116
+ if (context) {
8117
+ if (context.terminated) {
8118
+ message = `Unrecognized character reference \`${context.entity}\`.`;
8119
+ }
8120
+ else {
8121
+ message = `Character reference \`${context.entity}\` must be terminated by a semicolon.`;
8122
+ }
8123
+ }
8124
+ else {
8125
+ message = `Unrecognized character reference.`;
8126
+ }
8127
+ return [
8128
+ message,
8129
+ `HTML5 defines a set of [valid character references](${url}) but this is not a valid one.`,
8130
+ "",
8131
+ "Ensure that:",
8132
+ "",
8133
+ "1. The character is one of the listed names.",
8134
+ ...(options.ignoreCase ? [] : ["1. The case is correct (names are case sensitive)."]),
8135
+ ...(options.requireSemicolon ? ["1. The name is terminated with a `;`."] : []),
8136
+ ].join("\n");
8137
+ }
8056
8138
  class UnknownCharReference extends Rule {
8139
+ constructor(options) {
8140
+ super({ ...defaults$6, ...options });
8141
+ }
8142
+ static schema() {
8143
+ return {
8144
+ ignoreCase: {
8145
+ type: "boolean",
8146
+ },
8147
+ requireSemicolon: {
8148
+ type: "boolean",
8149
+ },
8150
+ };
8151
+ }
8057
8152
  documentation(context) {
8058
8153
  return {
8059
- description: `HTML defines a set of valid character references but ${context || "this"} is not a valid one.`,
8154
+ description: getDescription(context, this.options),
8060
8155
  url: "https://html-validate.org/rules/unrecognized-char-ref.html",
8061
8156
  };
8062
8157
  }
@@ -8068,7 +8163,9 @@ class UnknownCharReference extends Rule {
8068
8163
  if (child.nodeType !== NodeType.TEXT_NODE) {
8069
8164
  continue;
8070
8165
  }
8071
- this.findCharacterReferences(child.textContent, child.location);
8166
+ this.findCharacterReferences(node, child.textContent, child.location, {
8167
+ isAttribute: false,
8168
+ });
8072
8169
  }
8073
8170
  });
8074
8171
  this.on("attr", (event) => {
@@ -8076,25 +8173,72 @@ class UnknownCharReference extends Rule {
8076
8173
  if (!event.value) {
8077
8174
  return;
8078
8175
  }
8079
- this.findCharacterReferences(event.value.toString(), event.valueLocation);
8176
+ this.findCharacterReferences(event.target, event.value.toString(), event.valueLocation, {
8177
+ isAttribute: true,
8178
+ });
8080
8179
  });
8081
8180
  }
8082
- findCharacterReferences(text, location) {
8181
+ get entities() {
8182
+ if (this.options.ignoreCase) {
8183
+ return lowercaseEntities;
8184
+ }
8185
+ else {
8186
+ return entities$1;
8187
+ }
8188
+ }
8189
+ /* eslint-disable-next-line complexity */
8190
+ findCharacterReferences(node, text, location, { isAttribute }) {
8191
+ const { requireSemicolon } = this.options;
8192
+ const isQuerystring = isAttribute && text.includes("?");
8193
+ for (const { match, entity, raw, terminated } of this.getMatches(text)) {
8194
+ /* assume numeric entities are valid for now */
8195
+ if (isNumerical(entity)) {
8196
+ continue;
8197
+ }
8198
+ /* special case: when attributes use query parameters we skip checking
8199
+ * unterminated attributes */
8200
+ if (isQuerystring && !terminated) {
8201
+ continue;
8202
+ }
8203
+ const found = this.entities.includes(entity);
8204
+ /* ignore if this is a known character reference name */
8205
+ if (found && (terminated || !requireSemicolon)) {
8206
+ continue;
8207
+ }
8208
+ if (found && !terminated) {
8209
+ const entityLocation = getLocation(location, entity, match);
8210
+ const message = `Character reference "{{ entity }}" must be terminated by a semicolon`;
8211
+ const context = {
8212
+ entity: raw,
8213
+ terminated: false,
8214
+ };
8215
+ this.report(node, message, entityLocation, context);
8216
+ continue;
8217
+ }
8218
+ const entityLocation = getLocation(location, entity, match);
8219
+ const message = `Unrecognized character reference "{{ entity }}"`;
8220
+ const context = {
8221
+ entity: raw,
8222
+ terminated: true,
8223
+ };
8224
+ this.report(node, message, entityLocation, context);
8225
+ }
8226
+ }
8227
+ *getMatches(text) {
8083
8228
  let match;
8084
8229
  do {
8085
8230
  match = regexp$1.exec(text);
8086
8231
  if (match) {
8087
- const entity = match[0];
8088
- /* assume numeric entities are valid for now */
8089
- if (entity.startsWith("&#")) {
8090
- continue;
8232
+ const terminator = match[1]; // === ";" ? match[1] : "";
8233
+ const terminated = terminator === ";";
8234
+ const needSlice = terminator !== ";" && terminator.length > 0;
8235
+ const entity = needSlice ? match[0].slice(0, -1) : match[0];
8236
+ if (this.options.ignoreCase) {
8237
+ yield { match, entity: entity.toLowerCase(), raw: entity, terminated };
8091
8238
  }
8092
- /* ignore if this is a known character reference name */
8093
- if (entities$1.includes(entity)) {
8094
- continue;
8239
+ else {
8240
+ yield { match, entity, raw: entity, terminated };
8095
8241
  }
8096
- const entityLocation = sliceLocation(location, match.index, match.index + entity.length);
8097
- this.report(null, `Unrecognized character reference "${entity}"`, entityLocation, entity);
8098
8242
  }
8099
8243
  } while (match);
8100
8244
  }
@@ -8695,6 +8839,7 @@ const bundledRules = {
8695
8839
  "input-attributes": InputAttributes,
8696
8840
  "input-missing-label": InputMissingLabel,
8697
8841
  "long-title": LongTitle,
8842
+ "map-dup-name": MapDupName,
8698
8843
  "meta-refresh": MetaRefresh,
8699
8844
  "missing-doctype": MissingDoctype,
8700
8845
  "multiple-labeled-controls": MultipleLabeledControls,
@@ -8804,6 +8949,7 @@ const config$1 = {
8804
8949
  "empty-title": "error",
8805
8950
  "input-attributes": "error",
8806
8951
  "long-title": "error",
8952
+ "map-dup-name": "error",
8807
8953
  "meta-refresh": "error",
8808
8954
  "multiple-labeled-controls": "error",
8809
8955
  "no-autoplay": ["error", { include: ["audio", "video"] }],
@@ -8862,6 +9008,7 @@ const config = {
8862
9008
  "element-required-ancestor": "error",
8863
9009
  "element-required-attributes": "error",
8864
9010
  "element-required-content": "error",
9011
+ "map-dup-name": "error",
8865
9012
  "multiple-labeled-controls": "error",
8866
9013
  "no-deprecated-attr": "error",
8867
9014
  "no-dup-attr": "error",
@@ -10950,10 +11097,19 @@ class HtmlValidate {
10950
11097
  /** @public */
10951
11098
  const name = "html-validate";
10952
11099
  /** @public */
10953
- const version = "7.8.0";
11100
+ const version = "7.9.0";
10954
11101
  /** @public */
10955
11102
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
10956
11103
 
11104
+ /**
11105
+ * Helper function to assist IDE with completion and type-checking.
11106
+ *
11107
+ * @public
11108
+ */
11109
+ function definePlugin(plugin) {
11110
+ return plugin;
11111
+ }
11112
+
10957
11113
  const defaults$1 = {
10958
11114
  silent: false,
10959
11115
  version,
@@ -11384,5 +11540,5 @@ function getFormatter(name) {
11384
11540
  return (_a = availableFormatters[name]) !== null && _a !== void 0 ? _a : null;
11385
11541
  }
11386
11542
 
11387
- 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, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, getFormatter as j, ensureError as k, legacyRequire as l, configDataFromFile as m, compatibilityCheck as n, codeframe as o, presets as p, isTextNode as q, ruleExists as r, sliceLocation as s, isElementNode as t, generateIdSelector as u, version as v, name as w, bugs as x };
11543
+ 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, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, definePlugin as j, getFormatter as k, legacyRequire as l, ensureError as m, configDataFromFile as n, compatibilityCheck as o, presets as p, codeframe as q, ruleExists as r, sliceLocation as s, isTextNode as t, isElementNode as u, version as v, generateIdSelector as w, name as x, bugs as y };
11388
11544
  //# sourceMappingURL=core.js.map