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/esm/core.js CHANGED
@@ -1066,6 +1066,7 @@ const MetaCopyableProperty = [
1066
1066
  "labelable",
1067
1067
  "submitButton",
1068
1068
  "attributes",
1069
+ "patternAttributes",
1069
1070
  "aria",
1070
1071
  "permittedContent",
1071
1072
  "permittedDescendants",
@@ -1078,6 +1079,54 @@ function setMetaProperty(dst, key, value) {
1078
1079
  dst[key] = value;
1079
1080
  }
1080
1081
 
1082
+ const SYNTAX_CHARACTERS = /[$()*+.?[\\\]^{|}]/;
1083
+ const CONTROL_ESCAPES = /* @__PURE__ */ new Map([
1084
+ [" ", "t"],
1085
+ ["\n", "n"],
1086
+ ["\v", "v"],
1087
+ ["\f", "f"],
1088
+ ["\r", "r"]
1089
+ ]);
1090
+ const OTHER_PUNCTUATORS = /^[!"#%&',:;<=>@`~-]$/;
1091
+ const WHITE_SPACE = /^[\t\v\f\uFEFF\p{Zs}]$/u;
1092
+ const LINE_TERMINATOR = /^[\n\r\u2028\u2029]$/;
1093
+ const SURROGATE = /^[\uD800-\uDFFF]$/;
1094
+ function isDecimalDigitOrASCIILetter(ch) {
1095
+ return /^[\dA-Za-z]$/.test(ch);
1096
+ }
1097
+ function needEscape(ch) {
1098
+ return OTHER_PUNCTUATORS.test(ch) || WHITE_SPACE.test(ch) || LINE_TERMINATOR.test(ch) || SURROGATE.test(ch);
1099
+ }
1100
+ function unicodeEscape(ch) {
1101
+ return `\\u${ch.codePointAt(0).toString(16).padStart(4, "0")}`;
1102
+ }
1103
+ function encodeForRegExpEscape(ch) {
1104
+ if (SYNTAX_CHARACTERS.test(ch) || ch === "/") {
1105
+ return `\\${ch}`;
1106
+ }
1107
+ if (CONTROL_ESCAPES.has(ch)) {
1108
+ return `\\${CONTROL_ESCAPES.get(ch)}`;
1109
+ }
1110
+ if (needEscape(ch)) {
1111
+ if (/[\u0000-\u00FF]/.test(ch)) {
1112
+ return `\\x${ch.codePointAt(0).toString(16).padStart(2, "0")}`;
1113
+ }
1114
+ return ch.split("").map((c) => unicodeEscape(c)).join("");
1115
+ }
1116
+ return ch;
1117
+ }
1118
+ function regexpEscape(str) {
1119
+ let escaped = "";
1120
+ for (const c of str) {
1121
+ if (escaped === "" && isDecimalDigitOrASCIILetter(c)) {
1122
+ escaped += `\\x${c.codePointAt(0).toString(16).padStart(2, "0")}`;
1123
+ } else {
1124
+ escaped += encodeForRegExpEscape(c);
1125
+ }
1126
+ }
1127
+ return escaped;
1128
+ }
1129
+
1081
1130
  function isSet(value) {
1082
1131
  return value !== void 0;
1083
1132
  }
@@ -1115,18 +1164,35 @@ function migrateSingleAttribute(src, key) {
1115
1164
  return stripUndefined({ ...result, ...attr });
1116
1165
  }
1117
1166
  }
1167
+ function isPatternAttribute$1(key) {
1168
+ return key.includes("*");
1169
+ }
1170
+ function patternToRegex(pattern) {
1171
+ const escaped = pattern.split("*").map(regexpEscape).join(".+");
1172
+ return new RegExp(`^${escaped}$`, "i");
1173
+ }
1118
1174
  function migrateAttributes(src) {
1119
1175
  const keys = [
1120
1176
  ...Object.keys(src.attributes ?? {}),
1121
1177
  ...src.requiredAttributes ?? [],
1122
1178
  ...src.deprecatedAttributes ?? []
1123
- /* eslint-disable-next-line sonarjs/no-alphabetical-sort -- not really needed in this case, this is a-z anyway */
1124
- ].toSorted();
1179
+ ].filter((key) => !isPatternAttribute$1(key)).toSorted();
1125
1180
  const entries = keys.map((key) => {
1126
1181
  return [key, migrateSingleAttribute(src, key)];
1127
1182
  });
1128
1183
  return Object.fromEntries(entries);
1129
1184
  }
1185
+ function migratePatternAttributes(src) {
1186
+ const attrs = src.attributes ?? {};
1187
+ return Object.entries(attrs).filter(([key]) => isPatternAttribute$1(key)).map(([pattern]) => {
1188
+ const { delete: deleted, ...attr } = migrateSingleAttribute(src, pattern);
1189
+ const regexp = patternToRegex(pattern);
1190
+ if (deleted) {
1191
+ return { pattern, regexp, delete: true };
1192
+ }
1193
+ return { ...attr, pattern, regexp };
1194
+ });
1195
+ }
1130
1196
  function normalizeAriaImplicitRole(value) {
1131
1197
  if (!value) {
1132
1198
  return () => null;
@@ -1151,6 +1217,7 @@ function migrateElement(src) {
1151
1217
  ...src,
1152
1218
  formAssociated: void 0,
1153
1219
  attributes: migrateAttributes(src),
1220
+ patternAttributes: migratePatternAttributes(src),
1154
1221
  textContent: src.textContent,
1155
1222
  focusable: src.focusable ?? false,
1156
1223
  implicitRole,
@@ -1380,6 +1447,18 @@ class MetaTable {
1380
1447
  }
1381
1448
  }
1382
1449
  merged.attributes = mergedAttrs;
1450
+ const mergedPatterns = [
1451
+ ...b.patternAttributes,
1452
+ ...a.patternAttributes ?? []
1453
+ ];
1454
+ const seenPatterns = /* @__PURE__ */ new Set();
1455
+ merged.patternAttributes = mergedPatterns.filter((entry) => {
1456
+ if (seenPatterns.has(entry.pattern)) {
1457
+ return false;
1458
+ }
1459
+ seenPatterns.add(entry.pattern);
1460
+ return !entry.delete;
1461
+ });
1383
1462
  if (a.aria) {
1384
1463
  merged.aria = { ...a.aria, ...b.aria };
1385
1464
  }
@@ -3457,6 +3536,10 @@ function isKeywordIgnored(options, keyword, matcher = (list, it) => list.include
3457
3536
  return false;
3458
3537
  }
3459
3538
 
3539
+ function isPatternAttribute(attrName, dynamicAttributes) {
3540
+ return dynamicAttributes.some((entry) => entry.regexp.test(attrName));
3541
+ }
3542
+
3460
3543
  const ARIA_HIDDEN_CACHE = Symbol(isAriaHidden.name);
3461
3544
  const HTML_HIDDEN_CACHE = Symbol(isHTMLHidden.name);
3462
3545
  const INERT_CACHE = Symbol(isInert.name);
@@ -8959,18 +9042,7 @@ class NoTrailingWhitespace extends Rule {
8959
9042
  }
8960
9043
  }
8961
9044
 
8962
- const skipPatterns = [
8963
- /^data-/i,
8964
- /^aria-/i,
8965
- /^on[a-z]/i,
8966
- /^xml(ns)?:/i,
8967
- /^:/,
8968
- /^@/,
8969
- /^ng-/i,
8970
- /^v-/i,
8971
- /^x-/i,
8972
- /^\[/
8973
- ];
9045
+ const skipPatterns = [/^:/, /^@/, /^ng-/i, /^v-/i, /^x-/i, /^\[/, /^#/];
8974
9046
  function isKnownDynamicAttr(attr) {
8975
9047
  return skipPatterns.some((pattern) => pattern.test(attr));
8976
9048
  }
@@ -8992,6 +9064,9 @@ class NoUnknownAttributes extends Rule {
8992
9064
  if (attr in meta.attributes) {
8993
9065
  return;
8994
9066
  }
9067
+ if (isPatternAttribute(attr, meta.patternAttributes)) {
9068
+ return;
9069
+ }
8995
9070
  if (isKnownDynamicAttr(attr)) {
8996
9071
  return;
8997
9072
  }
@@ -12630,7 +12705,7 @@ class EventHandler {
12630
12705
  }
12631
12706
 
12632
12707
  const name = "html-validate";
12633
- const version = "11.2.0";
12708
+ const version = "11.3.0";
12634
12709
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12635
12710
 
12636
12711
  function freeze(src) {