html-validate 8.10.0 → 8.11.1

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.
@@ -45,6 +45,7 @@ exports.TextNode = core.TextNode;
45
45
  exports.UserError = core.UserError;
46
46
  exports.Validator = core.Validator;
47
47
  exports.WrappedError = core.WrappedError;
48
+ exports.ariaNaming = core.ariaNaming;
48
49
  exports.classifyNodeText = core.classifyNodeText;
49
50
  exports.configPresets = core.Presets;
50
51
  exports.defineConfig = core.defineConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"browser.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/cjs/core.js CHANGED
@@ -594,8 +594,13 @@ const patternProperties = {
594
594
  implicitRole: {
595
595
  title: "Implicit ARIA role for this element",
596
596
  description: "Some elements have implicit ARIA roles.",
597
+ deprecated: true,
597
598
  "function": true
598
599
  },
600
+ aria: {
601
+ title: "WAI-ARIA properties for this element",
602
+ $ref: "#/definitions/Aria"
603
+ },
599
604
  scriptSupporting: {
600
605
  title: "Mark element as script-supporting",
601
606
  description: "Script-supporting elements are elements which can be inserted where othersise not permitted to assist in templating",
@@ -692,6 +697,39 @@ const patternProperties = {
692
697
  }
693
698
  };
694
699
  const definitions = {
700
+ Aria: {
701
+ type: "object",
702
+ additionalProperties: false,
703
+ properties: {
704
+ implicitRole: {
705
+ title: "Implicit ARIA role for this element",
706
+ description: "Some elements have implicit ARIA roles.",
707
+ anyOf: [
708
+ {
709
+ type: "string"
710
+ },
711
+ {
712
+ "function": true
713
+ }
714
+ ]
715
+ },
716
+ naming: {
717
+ title: "Prohibit or allow this element to be named by aria-label or aria-labelledby",
718
+ anyOf: [
719
+ {
720
+ type: "string",
721
+ "enum": [
722
+ "prohibited",
723
+ "allowed"
724
+ ]
725
+ },
726
+ {
727
+ "function": true
728
+ }
729
+ ]
730
+ }
731
+ }
732
+ },
695
733
  contentCategory: {
696
734
  anyOf: [
697
735
  {
@@ -992,6 +1030,7 @@ const MetaCopyableProperty = [
992
1030
  "formAssociated",
993
1031
  "labelable",
994
1032
  "attributes",
1033
+ "aria",
995
1034
  "permittedContent",
996
1035
  "permittedDescendants",
997
1036
  "permittedOrder",
@@ -1052,7 +1091,27 @@ function migrateAttributes(src) {
1052
1091
  });
1053
1092
  return Object.fromEntries(entries);
1054
1093
  }
1094
+ function normalizeAriaImplicitRole(value) {
1095
+ if (!value) {
1096
+ return () => null;
1097
+ }
1098
+ if (typeof value === "string") {
1099
+ return () => value;
1100
+ }
1101
+ return value;
1102
+ }
1103
+ function normalizeAriaNaming(value) {
1104
+ if (!value) {
1105
+ return () => "allowed";
1106
+ }
1107
+ if (typeof value === "string") {
1108
+ return () => value;
1109
+ }
1110
+ return value;
1111
+ }
1055
1112
  function migrateElement(src) {
1113
+ var _a, _b;
1114
+ const implicitRole = normalizeAriaImplicitRole(src.implicitRole ?? ((_a = src.aria) == null ? void 0 : _a.implicitRole));
1056
1115
  const result = {
1057
1116
  ...src,
1058
1117
  ...{
@@ -1061,7 +1120,11 @@ function migrateElement(src) {
1061
1120
  attributes: migrateAttributes(src),
1062
1121
  textContent: src.textContent,
1063
1122
  focusable: src.focusable ?? false,
1064
- implicitRole: src.implicitRole ?? (() => null)
1123
+ implicitRole,
1124
+ aria: {
1125
+ implicitRole,
1126
+ naming: normalizeAriaNaming((_b = src.aria) == null ? void 0 : _b.naming)
1127
+ }
1065
1128
  };
1066
1129
  delete result.deprecatedAttributes;
1067
1130
  delete result.requiredAttributes;
@@ -1900,11 +1963,82 @@ function factory$1(name, context) {
1900
1963
  }
1901
1964
  }
1902
1965
 
1966
+ const escapedCodepoints = ["9", "a", "d"];
1967
+ function* splitSelectorElements(selector) {
1968
+ let begin = 0;
1969
+ let end = 0;
1970
+ function initialState(ch, p) {
1971
+ if (ch === "\\") {
1972
+ return 1 /* ESCAPED */;
1973
+ }
1974
+ if (ch === " ") {
1975
+ end = p;
1976
+ return 2 /* WHITESPACE */;
1977
+ }
1978
+ return 0 /* INITIAL */;
1979
+ }
1980
+ function escapedState(ch) {
1981
+ if (escapedCodepoints.includes(ch)) {
1982
+ return 1 /* ESCAPED */;
1983
+ }
1984
+ return 0 /* INITIAL */;
1985
+ }
1986
+ function* whitespaceState(ch, p) {
1987
+ if (ch === " ") {
1988
+ return 2 /* WHITESPACE */;
1989
+ }
1990
+ yield selector.slice(begin, end);
1991
+ begin = p;
1992
+ end = p;
1993
+ return 0 /* INITIAL */;
1994
+ }
1995
+ let state = 0 /* INITIAL */;
1996
+ for (let p = 0; p < selector.length; p++) {
1997
+ const ch = selector[p];
1998
+ switch (state) {
1999
+ case 0 /* INITIAL */:
2000
+ state = initialState(ch, p);
2001
+ break;
2002
+ case 1 /* ESCAPED */:
2003
+ state = escapedState(ch);
2004
+ break;
2005
+ case 2 /* WHITESPACE */:
2006
+ state = yield* whitespaceState(ch, p);
2007
+ break;
2008
+ }
2009
+ }
2010
+ if (begin !== selector.length) {
2011
+ yield selector.slice(begin);
2012
+ }
2013
+ }
2014
+
1903
2015
  function stripslashes(value) {
1904
2016
  return value.replace(/\\(.)/g, "$1");
1905
2017
  }
2018
+ function unescapeCodepoint(value) {
2019
+ const replacement = {
2020
+ "\\9 ": " ",
2021
+ "\\a ": "\n",
2022
+ "\\d ": "\r"
2023
+ };
2024
+ return value.replace(
2025
+ /(\\[\u0039\u0061\u0064] )/g,
2026
+ (_, codepoint) => replacement[codepoint]
2027
+ );
2028
+ }
1906
2029
  function escapeSelectorComponent(text) {
1907
- return text.toString().replace(/([^a-z0-9_-])/gi, "\\$1");
2030
+ const codepoints = {
2031
+ " ": "\\9 ",
2032
+ "\n": "\\a ",
2033
+ "\r": "\\d "
2034
+ };
2035
+ return text.toString().replace(/([\t\n\r]|[^a-z0-9_-])/gi, (_, ch) => {
2036
+ if (codepoints[ch]) {
2037
+ return codepoints[ch];
2038
+ } else {
2039
+ return `\\${ch}`;
2040
+ }
2041
+ });
1908
2042
  }
1909
2043
  function generateIdSelector(id) {
1910
2044
  const escaped = escapeSelectorComponent(id);
@@ -2019,7 +2153,10 @@ class PseudoClassMatcher extends Matcher {
2019
2153
  }
2020
2154
  class Pattern {
2021
2155
  constructor(pattern) {
2022
- const match = pattern.match(/^([~+\->]?)((?:[*]|[^.#[:]+)?)(.*)$/);
2156
+ const match = pattern.match(/^([~+\->]?)((?:[*]|[^.#[:]+)?)([^]*)$/);
2157
+ if (!match) {
2158
+ throw new Error(`Failed to create selector pattern from "${pattern}"`);
2159
+ }
2023
2160
  match.shift();
2024
2161
  this.selector = pattern;
2025
2162
  this.combinator = parseCombinator(match.shift(), pattern);
@@ -2074,13 +2211,8 @@ class Selector {
2074
2211
  }
2075
2212
  static parse(selector) {
2076
2213
  selector = selector.replace(/([+~>]) /g, "$1");
2077
- let begin = 0;
2078
- const delimiter = /((?:[^\\]) +|$)/g;
2079
- return Array.from(selector.matchAll(delimiter), (match) => {
2080
- const end = match.index + 1;
2081
- const part = selector.slice(begin, end);
2082
- begin = end + 1;
2083
- return new Pattern(part);
2214
+ return Array.from(splitSelectorElements(selector), (element) => {
2215
+ return new Pattern(unescapeCodepoint(element));
2084
2216
  });
2085
2217
  }
2086
2218
  static findCandidates(root, pattern) {
@@ -2411,7 +2543,8 @@ class HtmlElement extends DOMNode {
2411
2543
  return this.cacheSet(ROLE, role.value);
2412
2544
  }
2413
2545
  if (this.metaElement) {
2414
- const implicitRole = this.metaElement.implicitRole(this._adapter);
2546
+ const { aria } = this.metaElement;
2547
+ const implicitRole = aria.implicitRole(this._adapter);
2415
2548
  return this.cacheSet(ROLE, implicitRole);
2416
2549
  }
2417
2550
  return this.cacheSet(ROLE, null);
@@ -3153,6 +3286,48 @@ function interpolate(text, data) {
3153
3286
  });
3154
3287
  }
3155
3288
 
3289
+ const cacheKey = Symbol("aria-naming");
3290
+ const defaultValue = "allowed";
3291
+ const prohibitedRoles = [
3292
+ "caption",
3293
+ "code",
3294
+ "deletion",
3295
+ "emphasis",
3296
+ "generic",
3297
+ "insertion",
3298
+ "paragraph",
3299
+ "presentation",
3300
+ "strong",
3301
+ "subscript",
3302
+ "superscript"
3303
+ ];
3304
+ function byRole(role) {
3305
+ return prohibitedRoles.includes(role) ? "prohibited" : "allowed";
3306
+ }
3307
+ function byMeta(element, meta) {
3308
+ return meta.aria.naming(element._adapter);
3309
+ }
3310
+ function ariaNaming(element) {
3311
+ var _a;
3312
+ const cached = element.cacheGet(cacheKey);
3313
+ if (cached) {
3314
+ return cached;
3315
+ }
3316
+ const role = (_a = element.getAttribute("role")) == null ? void 0 : _a.value;
3317
+ if (role) {
3318
+ if (role instanceof DynamicValue) {
3319
+ return element.cacheSet(cacheKey, defaultValue);
3320
+ } else {
3321
+ return element.cacheSet(cacheKey, byRole(role));
3322
+ }
3323
+ }
3324
+ const meta = element.meta;
3325
+ if (!meta) {
3326
+ return element.cacheSet(cacheKey, defaultValue);
3327
+ }
3328
+ return element.cacheSet(cacheKey, byMeta(element, meta));
3329
+ }
3330
+
3156
3331
  const patternCache = /* @__PURE__ */ new Map();
3157
3332
  function compileStringPattern(pattern) {
3158
3333
  const regexp = pattern.replace(/[*]+/g, ".+");
@@ -3671,7 +3846,7 @@ class Rule {
3671
3846
  }
3672
3847
  }
3673
3848
 
3674
- const defaults$u = {
3849
+ const defaults$v = {
3675
3850
  allowExternal: true,
3676
3851
  allowRelative: true,
3677
3852
  allowAbsolute: true,
@@ -3712,7 +3887,7 @@ function matchList(value, list) {
3712
3887
  }
3713
3888
  class AllowedLinks extends Rule {
3714
3889
  constructor(options) {
3715
- super({ ...defaults$u, ...options });
3890
+ super({ ...defaults$v, ...options });
3716
3891
  this.allowExternal = parseAllow(this.options.allowExternal);
3717
3892
  this.allowRelative = parseAllow(this.options.allowRelative);
3718
3893
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3876,7 +4051,7 @@ class AllowedLinks extends Rule {
3876
4051
  }
3877
4052
  }
3878
4053
 
3879
- const defaults$t = {
4054
+ const defaults$u = {
3880
4055
  accessible: true
3881
4056
  };
3882
4057
  function findByTarget(target, siblings) {
@@ -3906,7 +4081,7 @@ function getDescription$1(context) {
3906
4081
  }
3907
4082
  class AreaAlt extends Rule {
3908
4083
  constructor(options) {
3909
- super({ ...defaults$t, ...options });
4084
+ super({ ...defaults$u, ...options });
3910
4085
  }
3911
4086
  static schema() {
3912
4087
  return {
@@ -3985,6 +4160,9 @@ class AriaHiddenBody extends Rule {
3985
4160
  }
3986
4161
  }
3987
4162
 
4163
+ const defaults$t = {
4164
+ allowAnyNamable: false
4165
+ };
3988
4166
  const whitelisted = [
3989
4167
  "main",
3990
4168
  "nav",
@@ -4023,6 +4201,9 @@ function isValidUsage(target, meta) {
4023
4201
  return false;
4024
4202
  }
4025
4203
  class AriaLabelMisuse extends Rule {
4204
+ constructor(options) {
4205
+ super({ ...defaults$t, ...options });
4206
+ }
4026
4207
  documentation() {
4027
4208
  const valid = [
4028
4209
  "Interactive elements",
@@ -4065,6 +4246,9 @@ ${lines}`,
4065
4246
  if (isValidUsage(target, meta)) {
4066
4247
  return;
4067
4248
  }
4249
+ if (this.options.allowAnyNamable && ariaNaming(target) === "allowed") {
4250
+ return;
4251
+ }
4068
4252
  this.report(target, `"aria-label" cannot be used on this element`, attr.keyLocation);
4069
4253
  }
4070
4254
  }
@@ -7562,7 +7746,7 @@ class NoRedundantRole extends Rule {
7562
7746
  if (!meta) {
7563
7747
  return;
7564
7748
  }
7565
- const implicitRole = meta.implicitRole(target._adapter);
7749
+ const implicitRole = meta.aria.implicitRole(target._adapter);
7566
7750
  if (!implicitRole) {
7567
7751
  return;
7568
7752
  }
@@ -9344,7 +9528,7 @@ const config$4 = {
9344
9528
  rules: {
9345
9529
  "area-alt": ["error", { accessible: true }],
9346
9530
  "aria-hidden-body": "error",
9347
- "aria-label-misuse": "error",
9531
+ "aria-label-misuse": ["error", { allowAnyNamable: false }],
9348
9532
  "deprecated-rule": "warn",
9349
9533
  "empty-heading": "error",
9350
9534
  "empty-title": "error",
@@ -9396,7 +9580,7 @@ const config$1 = {
9396
9580
  rules: {
9397
9581
  "area-alt": ["error", { accessible: true }],
9398
9582
  "aria-hidden-body": "error",
9399
- "aria-label-misuse": "error",
9583
+ "aria-label-misuse": ["error", { allowAnyNamable: false }],
9400
9584
  "attr-case": "error",
9401
9585
  "attr-delimiter": "error",
9402
9586
  "attr-quotes": "error",
@@ -9477,6 +9661,7 @@ var recommended = config$1;
9477
9661
  const config = {
9478
9662
  rules: {
9479
9663
  "area-alt": ["error", { accessible: false }],
9664
+ "aria-label-misuse": ["error", { allowAnyNamable: true }],
9480
9665
  "attr-spacing": "error",
9481
9666
  "attribute-allowed-values": "error",
9482
9667
  "attribute-misuse": "error",
@@ -11729,7 +11914,7 @@ class HtmlValidate {
11729
11914
  }
11730
11915
 
11731
11916
  const name = "html-validate";
11732
- const version = "8.10.0";
11917
+ const version = "8.11.1";
11733
11918
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11734
11919
 
11735
11920
  function definePlugin(plugin) {
@@ -12636,6 +12821,7 @@ exports.TextNode = TextNode;
12636
12821
  exports.UserError = UserError;
12637
12822
  exports.Validator = Validator;
12638
12823
  exports.WrappedError = WrappedError;
12824
+ exports.ariaNaming = ariaNaming;
12639
12825
  exports.bugs = bugs;
12640
12826
  exports.classifyNodeText = classifyNodeText;
12641
12827
  exports.codeframe = codeframe;