html-validate 11.2.0 → 11.3.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
@@ -1075,6 +1075,7 @@ const MetaCopyableProperty = [
1075
1075
  "labelable",
1076
1076
  "submitButton",
1077
1077
  "attributes",
1078
+ "patternAttributes",
1078
1079
  "aria",
1079
1080
  "permittedContent",
1080
1081
  "permittedDescendants",
@@ -1087,6 +1088,54 @@ function setMetaProperty(dst, key, value) {
1087
1088
  dst[key] = value;
1088
1089
  }
1089
1090
 
1091
+ const SYNTAX_CHARACTERS = /[$()*+.?[\\\]^{|}]/;
1092
+ const CONTROL_ESCAPES = /* @__PURE__ */ new Map([
1093
+ [" ", "t"],
1094
+ ["\n", "n"],
1095
+ ["\v", "v"],
1096
+ ["\f", "f"],
1097
+ ["\r", "r"]
1098
+ ]);
1099
+ const OTHER_PUNCTUATORS = /^[!"#%&',:;<=>@`~-]$/;
1100
+ const WHITE_SPACE = /^[\t\v\f\uFEFF\p{Zs}]$/u;
1101
+ const LINE_TERMINATOR = /^[\n\r\u2028\u2029]$/;
1102
+ const SURROGATE = /^[\uD800-\uDFFF]$/;
1103
+ function isDecimalDigitOrASCIILetter(ch) {
1104
+ return /^[\dA-Za-z]$/.test(ch);
1105
+ }
1106
+ function needEscape(ch) {
1107
+ return OTHER_PUNCTUATORS.test(ch) || WHITE_SPACE.test(ch) || LINE_TERMINATOR.test(ch) || SURROGATE.test(ch);
1108
+ }
1109
+ function unicodeEscape(ch) {
1110
+ return `\\u${ch.codePointAt(0).toString(16).padStart(4, "0")}`;
1111
+ }
1112
+ function encodeForRegExpEscape(ch) {
1113
+ if (SYNTAX_CHARACTERS.test(ch) || ch === "/") {
1114
+ return `\\${ch}`;
1115
+ }
1116
+ if (CONTROL_ESCAPES.has(ch)) {
1117
+ return `\\${CONTROL_ESCAPES.get(ch)}`;
1118
+ }
1119
+ if (needEscape(ch)) {
1120
+ if (/[\u0000-\u00FF]/.test(ch)) {
1121
+ return `\\x${ch.codePointAt(0).toString(16).padStart(2, "0")}`;
1122
+ }
1123
+ return ch.split("").map((c) => unicodeEscape(c)).join("");
1124
+ }
1125
+ return ch;
1126
+ }
1127
+ function regexpEscape(str) {
1128
+ let escaped = "";
1129
+ for (const c of str) {
1130
+ if (escaped === "" && isDecimalDigitOrASCIILetter(c)) {
1131
+ escaped += `\\x${c.codePointAt(0).toString(16).padStart(2, "0")}`;
1132
+ } else {
1133
+ escaped += encodeForRegExpEscape(c);
1134
+ }
1135
+ }
1136
+ return escaped;
1137
+ }
1138
+
1090
1139
  function isSet(value) {
1091
1140
  return value !== void 0;
1092
1141
  }
@@ -1124,18 +1173,35 @@ function migrateSingleAttribute(src, key) {
1124
1173
  return stripUndefined({ ...result, ...attr });
1125
1174
  }
1126
1175
  }
1176
+ function isPatternAttribute$1(key) {
1177
+ return key.includes("*");
1178
+ }
1179
+ function patternToRegex(pattern) {
1180
+ const escaped = pattern.split("*").map(regexpEscape).join(".+");
1181
+ return new RegExp(`^${escaped}$`, "i");
1182
+ }
1127
1183
  function migrateAttributes(src) {
1128
1184
  const keys = [
1129
1185
  ...Object.keys(src.attributes ?? {}),
1130
1186
  ...src.requiredAttributes ?? [],
1131
1187
  ...src.deprecatedAttributes ?? []
1132
- /* eslint-disable-next-line sonarjs/no-alphabetical-sort -- not really needed in this case, this is a-z anyway */
1133
- ].toSorted();
1188
+ ].filter((key) => !isPatternAttribute$1(key)).toSorted();
1134
1189
  const entries = keys.map((key) => {
1135
1190
  return [key, migrateSingleAttribute(src, key)];
1136
1191
  });
1137
1192
  return Object.fromEntries(entries);
1138
1193
  }
1194
+ function migratePatternAttributes(src) {
1195
+ const attrs = src.attributes ?? {};
1196
+ return Object.entries(attrs).filter(([key]) => isPatternAttribute$1(key)).map(([pattern]) => {
1197
+ const { delete: deleted, ...attr } = migrateSingleAttribute(src, pattern);
1198
+ const regexp = patternToRegex(pattern);
1199
+ if (deleted) {
1200
+ return { pattern, regexp, delete: true };
1201
+ }
1202
+ return { ...attr, pattern, regexp };
1203
+ });
1204
+ }
1139
1205
  function normalizeAriaImplicitRole(value) {
1140
1206
  if (!value) {
1141
1207
  return () => null;
@@ -1160,6 +1226,7 @@ function migrateElement(src) {
1160
1226
  ...src,
1161
1227
  formAssociated: void 0,
1162
1228
  attributes: migrateAttributes(src),
1229
+ patternAttributes: migratePatternAttributes(src),
1163
1230
  textContent: src.textContent,
1164
1231
  focusable: src.focusable ?? false,
1165
1232
  implicitRole,
@@ -1389,6 +1456,18 @@ class MetaTable {
1389
1456
  }
1390
1457
  }
1391
1458
  merged.attributes = mergedAttrs;
1459
+ const mergedPatterns = [
1460
+ ...b.patternAttributes,
1461
+ ...a.patternAttributes ?? []
1462
+ ];
1463
+ const seenPatterns = /* @__PURE__ */ new Set();
1464
+ merged.patternAttributes = mergedPatterns.filter((entry) => {
1465
+ if (seenPatterns.has(entry.pattern)) {
1466
+ return false;
1467
+ }
1468
+ seenPatterns.add(entry.pattern);
1469
+ return !entry.delete;
1470
+ });
1392
1471
  if (a.aria) {
1393
1472
  merged.aria = { ...a.aria, ...b.aria };
1394
1473
  }
@@ -3466,6 +3545,10 @@ function isKeywordIgnored(options, keyword, matcher = (list, it) => list.include
3466
3545
  return false;
3467
3546
  }
3468
3547
 
3548
+ function isPatternAttribute(attrName, dynamicAttributes) {
3549
+ return dynamicAttributes.some((entry) => entry.regexp.test(attrName));
3550
+ }
3551
+
3469
3552
  const ARIA_HIDDEN_CACHE = Symbol(isAriaHidden.name);
3470
3553
  const HTML_HIDDEN_CACHE = Symbol(isHTMLHidden.name);
3471
3554
  const INERT_CACHE = Symbol(isInert.name);
@@ -8968,18 +9051,7 @@ class NoTrailingWhitespace extends Rule {
8968
9051
  }
8969
9052
  }
8970
9053
 
8971
- const skipPatterns = [
8972
- /^data-/i,
8973
- /^aria-/i,
8974
- /^on[a-z]/i,
8975
- /^xml(ns)?:/i,
8976
- /^:/,
8977
- /^@/,
8978
- /^ng-/i,
8979
- /^v-/i,
8980
- /^x-/i,
8981
- /^\[/
8982
- ];
9054
+ const skipPatterns = [/^:/, /^@/, /^ng-/i, /^v-/i, /^x-/i, /^\[/, /^#/];
8983
9055
  function isKnownDynamicAttr(attr) {
8984
9056
  return skipPatterns.some((pattern) => pattern.test(attr));
8985
9057
  }
@@ -9001,6 +9073,9 @@ class NoUnknownAttributes extends Rule {
9001
9073
  if (attr in meta.attributes) {
9002
9074
  return;
9003
9075
  }
9076
+ if (isPatternAttribute(attr, meta.patternAttributes)) {
9077
+ return;
9078
+ }
9004
9079
  if (isKnownDynamicAttr(attr)) {
9005
9080
  return;
9006
9081
  }
@@ -12639,7 +12714,7 @@ class EventHandler {
12639
12714
  }
12640
12715
 
12641
12716
  const name = "html-validate";
12642
- const version = "11.2.0";
12717
+ const version = "11.3.0";
12643
12718
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12644
12719
 
12645
12720
  function freeze(src) {