praxis-kit 6.2.1 → 6.5.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/_shared/diagnostics.d.ts +20 -0
- package/dist/_shared/diagnostics.js +20 -0
- package/dist/{chunk-4YVNOMUS.js → chunk-AM5J6PY3.js} +839 -198
- package/dist/contract/index.d.ts +1 -0
- package/dist/lit/index.d.ts +1 -0
- package/dist/lit/index.js +828 -187
- package/dist/preact/index.d.ts +1 -0
- package/dist/preact/index.js +839 -198
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/dist/react/legacy.d.ts +2 -2
- package/dist/react/legacy.js +1 -1
- package/dist/{react-options-DQTX4YS_.d.ts → react-options-DoFNqNdY.d.ts} +1 -0
- package/dist/solid/index.d.ts +1 -0
- package/dist/solid/index.js +839 -198
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +839 -198
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.js +15 -0
- package/dist/vite-plugin/index.d.ts +20 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +839 -198
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +828 -187
- package/package.json +13 -6
package/dist/vue/index.js
CHANGED
|
@@ -965,11 +965,28 @@ var ContractDiagnostics = {
|
|
|
965
965
|
|
|
966
966
|
// ../../lib/contract/src/diagnostics/html.ts
|
|
967
967
|
import { DiagnosticCategory as DiagnosticCategory3, DiagnosticCode as DiagnosticCode3 } from "../_shared/diagnostics.js";
|
|
968
|
+
var ATTRIBUTE_IGNORED_CODES = {
|
|
969
|
+
checked: DiagnosticCode3.HtmlInputCheckedIgnoredForType,
|
|
970
|
+
multiple: DiagnosticCode3.HtmlInputMultipleIgnoredForType,
|
|
971
|
+
maxLength: DiagnosticCode3.HtmlInputMaxLengthIgnoredForType,
|
|
972
|
+
minLength: DiagnosticCode3.HtmlInputMinLengthIgnoredForType,
|
|
973
|
+
pattern: DiagnosticCode3.HtmlInputPatternIgnoredForType,
|
|
974
|
+
min: DiagnosticCode3.HtmlInputMinIgnoredForType,
|
|
975
|
+
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
976
|
+
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
977
|
+
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
978
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
|
|
979
|
+
size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
|
|
980
|
+
alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
|
|
981
|
+
height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
|
|
982
|
+
width: DiagnosticCode3.HtmlInputWidthIgnoredForType
|
|
983
|
+
};
|
|
968
984
|
var HtmlDiagnostics = {
|
|
969
985
|
emptyRole(tag) {
|
|
970
986
|
return {
|
|
971
987
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
972
988
|
category: DiagnosticCategory3.HTML,
|
|
989
|
+
severity: "warning",
|
|
973
990
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
974
991
|
};
|
|
975
992
|
},
|
|
@@ -977,6 +994,7 @@ var HtmlDiagnostics = {
|
|
|
977
994
|
return {
|
|
978
995
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
979
996
|
category: DiagnosticCategory3.HTML,
|
|
997
|
+
severity: "warning",
|
|
980
998
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
981
999
|
};
|
|
982
1000
|
},
|
|
@@ -984,6 +1002,7 @@ var HtmlDiagnostics = {
|
|
|
984
1002
|
return {
|
|
985
1003
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
986
1004
|
category: DiagnosticCategory3.HTML,
|
|
1005
|
+
severity: "error",
|
|
987
1006
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
988
1007
|
};
|
|
989
1008
|
},
|
|
@@ -991,6 +1010,7 @@ var HtmlDiagnostics = {
|
|
|
991
1010
|
return {
|
|
992
1011
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
993
1012
|
category: DiagnosticCategory3.HTML,
|
|
1013
|
+
severity: "error",
|
|
994
1014
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
995
1015
|
};
|
|
996
1016
|
},
|
|
@@ -998,6 +1018,7 @@ var HtmlDiagnostics = {
|
|
|
998
1018
|
return {
|
|
999
1019
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
1000
1020
|
category: DiagnosticCategory3.HTML,
|
|
1021
|
+
severity: "error",
|
|
1001
1022
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
1002
1023
|
};
|
|
1003
1024
|
},
|
|
@@ -1005,34 +1026,148 @@ var HtmlDiagnostics = {
|
|
|
1005
1026
|
return {
|
|
1006
1027
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
1007
1028
|
category: DiagnosticCategory3.HTML,
|
|
1029
|
+
severity: "error",
|
|
1008
1030
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
1009
1031
|
};
|
|
1032
|
+
},
|
|
1033
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
1034
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
1035
|
+
return {
|
|
1036
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
1037
|
+
category: DiagnosticCategory3.HTML,
|
|
1038
|
+
severity: "error",
|
|
1039
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
1040
|
+
rationale: 'The WAI-ARIA "ARIA in HTML" specification restricts which explicit roles a native element may take. A role outside that set is ignored or produces undefined behavior in assistive technology.'
|
|
1041
|
+
};
|
|
1042
|
+
},
|
|
1043
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
1044
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
1045
|
+
input: {
|
|
1046
|
+
unsupportedType(type) {
|
|
1047
|
+
return {
|
|
1048
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
1049
|
+
category: DiagnosticCategory3.HTML,
|
|
1050
|
+
severity: "warning",
|
|
1051
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
1052
|
+
rationale: 'An unrecognized input type is not invalid markup \u2014 the spec requires the "text" fallback \u2014 but it usually means a typo, since the input keeps working while silently losing the intended type-specific behavior (validation, virtual keyboard, picker UI).',
|
|
1053
|
+
suggestions: [
|
|
1054
|
+
{
|
|
1055
|
+
title: "Check for a typo in the type value",
|
|
1056
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
1057
|
+
}
|
|
1058
|
+
]
|
|
1059
|
+
};
|
|
1060
|
+
},
|
|
1061
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
1062
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
1063
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
1064
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
1065
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
1066
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
1067
|
+
return {
|
|
1068
|
+
code,
|
|
1069
|
+
category: DiagnosticCategory3.HTML,
|
|
1070
|
+
severity: "warning",
|
|
1071
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
1072
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
1073
|
+
suggestions: [
|
|
1074
|
+
{
|
|
1075
|
+
title: `Remove "${attribute}"`,
|
|
1076
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
1077
|
+
}
|
|
1078
|
+
]
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1010
1081
|
}
|
|
1011
1082
|
};
|
|
1012
1083
|
|
|
1013
|
-
// ../../lib/contract/src/diagnostics/
|
|
1084
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
1014
1085
|
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "../_shared/diagnostics.js";
|
|
1086
|
+
function accessibilityFact(input) {
|
|
1087
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
1088
|
+
}
|
|
1089
|
+
var InputAccessibilityDiagnostics = {
|
|
1090
|
+
missingAccessibleName() {
|
|
1091
|
+
return accessibilityFact({
|
|
1092
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
1093
|
+
severity: "warning",
|
|
1094
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1095
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
1096
|
+
suggestions: [
|
|
1097
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1098
|
+
{
|
|
1099
|
+
title: "Add an associated <label>",
|
|
1100
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1101
|
+
}
|
|
1102
|
+
]
|
|
1103
|
+
});
|
|
1104
|
+
},
|
|
1105
|
+
placeholderIsNotLabel() {
|
|
1106
|
+
return accessibilityFact({
|
|
1107
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
1108
|
+
severity: "warning",
|
|
1109
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1110
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1111
|
+
suggestions: [
|
|
1112
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1113
|
+
{
|
|
1114
|
+
title: "Add an associated <label>",
|
|
1115
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1116
|
+
}
|
|
1117
|
+
]
|
|
1118
|
+
});
|
|
1119
|
+
},
|
|
1120
|
+
passwordMissingAutocomplete() {
|
|
1121
|
+
return accessibilityFact({
|
|
1122
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1123
|
+
severity: "warning",
|
|
1124
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1125
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1126
|
+
suggestions: [
|
|
1127
|
+
{
|
|
1128
|
+
title: 'Set autoComplete="current-password"',
|
|
1129
|
+
description: "Use this for sign-in forms."
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
title: 'Set autoComplete="new-password"',
|
|
1133
|
+
description: "Use this for sign-up / change-password forms."
|
|
1134
|
+
}
|
|
1135
|
+
]
|
|
1136
|
+
});
|
|
1137
|
+
},
|
|
1138
|
+
requiredReadOnlyConflict() {
|
|
1139
|
+
return accessibilityFact({
|
|
1140
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1141
|
+
severity: "warning",
|
|
1142
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1143
|
+
rationale: "This combination is valid HTML but usually indicates an unintended state. Consider using disabled instead of readOnly, or only applying required when the field is editable."
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1149
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
1015
1150
|
var SlotDiagnostics = {
|
|
1016
1151
|
exclusive(name) {
|
|
1017
1152
|
return {
|
|
1018
|
-
code:
|
|
1019
|
-
category:
|
|
1153
|
+
code: DiagnosticCode5.SlotExclusive,
|
|
1154
|
+
category: DiagnosticCategory5.Contract,
|
|
1020
1155
|
component: name,
|
|
1021
1156
|
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
1022
1157
|
};
|
|
1023
1158
|
},
|
|
1024
1159
|
singleChildRequired(name, elementTerm) {
|
|
1025
1160
|
return {
|
|
1026
|
-
code:
|
|
1027
|
-
category:
|
|
1161
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1162
|
+
category: DiagnosticCategory5.Contract,
|
|
1028
1163
|
component: name,
|
|
1029
1164
|
message: `${name}: asChild requires a ${elementTerm} child`
|
|
1030
1165
|
};
|
|
1031
1166
|
},
|
|
1032
1167
|
singleChildExceeded(name, elementTerm, count) {
|
|
1033
1168
|
return {
|
|
1034
|
-
code:
|
|
1035
|
-
category:
|
|
1169
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1170
|
+
category: DiagnosticCategory5.Contract,
|
|
1036
1171
|
component: name,
|
|
1037
1172
|
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
1038
1173
|
};
|
|
@@ -1040,16 +1175,16 @@ var SlotDiagnostics = {
|
|
|
1040
1175
|
discardedChildren(name, elementTerm, count) {
|
|
1041
1176
|
const suffix = count === 1 ? "" : "ren";
|
|
1042
1177
|
return {
|
|
1043
|
-
code:
|
|
1044
|
-
category:
|
|
1178
|
+
code: DiagnosticCode5.SlotDiscardedChildren,
|
|
1179
|
+
category: DiagnosticCategory5.Contract,
|
|
1045
1180
|
component: name,
|
|
1046
1181
|
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
1047
1182
|
};
|
|
1048
1183
|
},
|
|
1049
1184
|
renderFnRequired(name, received) {
|
|
1050
1185
|
return {
|
|
1051
|
-
code:
|
|
1052
|
-
category:
|
|
1186
|
+
code: DiagnosticCode5.SlotRenderFn,
|
|
1187
|
+
category: DiagnosticCategory5.Contract,
|
|
1053
1188
|
component: name,
|
|
1054
1189
|
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
1055
1190
|
};
|
|
@@ -1078,8 +1213,132 @@ var InvariantBase = class {
|
|
|
1078
1213
|
}
|
|
1079
1214
|
};
|
|
1080
1215
|
|
|
1081
|
-
// ../../lib/contract/src/aria/
|
|
1216
|
+
// ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1217
|
+
var REQUIRED_ARIA_PROPERTIES = {
|
|
1218
|
+
combobox: ["aria-expanded"],
|
|
1219
|
+
option: ["aria-selected"],
|
|
1220
|
+
slider: ["aria-valuenow"],
|
|
1221
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1222
|
+
spinbutton: ["aria-valuenow"]
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
// ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1226
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1227
|
+
|
|
1228
|
+
// ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1082
1229
|
var NO_VIOLATIONS = [{ valid: true }];
|
|
1230
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1231
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1232
|
+
}
|
|
1233
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1234
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1235
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1236
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1237
|
+
const results = [];
|
|
1238
|
+
for (const attribute of requiredAttributes) {
|
|
1239
|
+
if (attribute in props) continue;
|
|
1240
|
+
results.push({
|
|
1241
|
+
valid: false,
|
|
1242
|
+
fixable: false,
|
|
1243
|
+
severity: "warning",
|
|
1244
|
+
attribute,
|
|
1245
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
return results;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1252
|
+
var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1253
|
+
["alert", "assertive"],
|
|
1254
|
+
["status", "polite"],
|
|
1255
|
+
["log", "polite"],
|
|
1256
|
+
["timer", "off"]
|
|
1257
|
+
]);
|
|
1258
|
+
var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1259
|
+
|
|
1260
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1261
|
+
var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1262
|
+
// Boolean (true | false)
|
|
1263
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1264
|
+
["aria-busy", { kind: "boolean" }],
|
|
1265
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1266
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1267
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1268
|
+
["aria-modal", { kind: "boolean" }],
|
|
1269
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1270
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1271
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1272
|
+
["aria-required", { kind: "boolean" }],
|
|
1273
|
+
["aria-selected", { kind: "boolean" }],
|
|
1274
|
+
// Tristate (true | false | mixed)
|
|
1275
|
+
["aria-checked", { kind: "tristate" }],
|
|
1276
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1277
|
+
// Numeric (any finite number)
|
|
1278
|
+
["aria-valuenow", { kind: "number" }],
|
|
1279
|
+
["aria-valuemin", { kind: "number" }],
|
|
1280
|
+
["aria-valuemax", { kind: "number" }],
|
|
1281
|
+
// Integer with optional range
|
|
1282
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1283
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1284
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1285
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1286
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1287
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1288
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1289
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1290
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1291
|
+
// Enum (specific allowed tokens)
|
|
1292
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1293
|
+
[
|
|
1294
|
+
"aria-current",
|
|
1295
|
+
{
|
|
1296
|
+
kind: "enum",
|
|
1297
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1298
|
+
}
|
|
1299
|
+
],
|
|
1300
|
+
[
|
|
1301
|
+
"aria-haspopup",
|
|
1302
|
+
{
|
|
1303
|
+
kind: "enum",
|
|
1304
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1305
|
+
}
|
|
1306
|
+
],
|
|
1307
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1308
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1309
|
+
["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
|
|
1310
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1311
|
+
]);
|
|
1312
|
+
|
|
1313
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1314
|
+
var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1315
|
+
"additions",
|
|
1316
|
+
"removals",
|
|
1317
|
+
"text",
|
|
1318
|
+
"all"
|
|
1319
|
+
]);
|
|
1320
|
+
|
|
1321
|
+
// ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1322
|
+
var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1323
|
+
["h1", 1],
|
|
1324
|
+
["h2", 2],
|
|
1325
|
+
["h3", 3],
|
|
1326
|
+
["h4", 4],
|
|
1327
|
+
["h5", 5],
|
|
1328
|
+
["h6", 6]
|
|
1329
|
+
]);
|
|
1330
|
+
|
|
1331
|
+
// ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
|
|
1332
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1333
|
+
"a",
|
|
1334
|
+
"button",
|
|
1335
|
+
"input",
|
|
1336
|
+
"select",
|
|
1337
|
+
"textarea"
|
|
1338
|
+
]);
|
|
1339
|
+
|
|
1340
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1341
|
+
var NO_VIOLATIONS2 = [{ valid: true }];
|
|
1083
1342
|
function isIntrinsicTag(tag) {
|
|
1084
1343
|
return isString(tag);
|
|
1085
1344
|
}
|
|
@@ -1111,7 +1370,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1111
1370
|
tag,
|
|
1112
1371
|
role: "",
|
|
1113
1372
|
attribute: void 0,
|
|
1114
|
-
severity:
|
|
1373
|
+
severity: d.severity,
|
|
1115
1374
|
phase: "evaluate"
|
|
1116
1375
|
}
|
|
1117
1376
|
]
|
|
@@ -1141,6 +1400,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1141
1400
|
const violations = [];
|
|
1142
1401
|
const fixes = [];
|
|
1143
1402
|
iterate.forEach(rules, (rule) => {
|
|
1403
|
+
if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
|
|
1144
1404
|
iterate.forEach(rule(context), (result) => {
|
|
1145
1405
|
if (result.valid) return;
|
|
1146
1406
|
const {
|
|
@@ -1166,7 +1426,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1166
1426
|
return { violations, fixes };
|
|
1167
1427
|
}
|
|
1168
1428
|
static #getRules(context) {
|
|
1169
|
-
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) &&
|
|
1429
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1170
1430
|
return _AriaPolicyEngine.#pipeline;
|
|
1171
1431
|
}
|
|
1172
1432
|
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
@@ -1368,44 +1628,47 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1368
1628
|
implicitRole
|
|
1369
1629
|
}) {
|
|
1370
1630
|
const role = props.role;
|
|
1371
|
-
if (!implicitRole || !role || role === implicitRole) return
|
|
1631
|
+
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
|
|
1372
1632
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1633
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1373
1634
|
return [
|
|
1374
1635
|
{
|
|
1375
1636
|
valid: false,
|
|
1376
1637
|
fixable: true,
|
|
1377
|
-
severity:
|
|
1638
|
+
severity: diagnostic.severity,
|
|
1378
1639
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1379
|
-
diagnostic
|
|
1640
|
+
diagnostic
|
|
1380
1641
|
}
|
|
1381
1642
|
];
|
|
1382
1643
|
}
|
|
1383
|
-
return
|
|
1644
|
+
return NO_VIOLATIONS2;
|
|
1384
1645
|
}
|
|
1385
1646
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1386
1647
|
const role = props.role;
|
|
1387
|
-
if (!implicitRole || !role || role !== implicitRole) return
|
|
1648
|
+
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
|
|
1649
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1388
1650
|
return [
|
|
1389
1651
|
{
|
|
1390
1652
|
valid: false,
|
|
1391
1653
|
fixable: true,
|
|
1392
|
-
severity:
|
|
1654
|
+
severity: diagnostic.severity,
|
|
1393
1655
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1394
|
-
diagnostic
|
|
1656
|
+
diagnostic
|
|
1395
1657
|
}
|
|
1396
1658
|
];
|
|
1397
1659
|
}
|
|
1398
1660
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1399
1661
|
const role = props.role;
|
|
1400
|
-
if (role !== "region") return
|
|
1401
|
-
if (!isStandaloneTag(tag)) return
|
|
1662
|
+
if (role !== "region") return NO_VIOLATIONS2;
|
|
1663
|
+
if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
|
|
1664
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1402
1665
|
return [
|
|
1403
1666
|
{
|
|
1404
1667
|
valid: false,
|
|
1405
1668
|
fixable: true,
|
|
1406
|
-
severity:
|
|
1669
|
+
severity: diagnostic.severity,
|
|
1407
1670
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1408
|
-
diagnostic
|
|
1671
|
+
diagnostic
|
|
1409
1672
|
}
|
|
1410
1673
|
];
|
|
1411
1674
|
}
|
|
@@ -1414,7 +1677,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1414
1677
|
props,
|
|
1415
1678
|
effectiveRole
|
|
1416
1679
|
}) {
|
|
1417
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1680
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1418
1681
|
const results = [];
|
|
1419
1682
|
iterate.forEachEntry(props, (key) => {
|
|
1420
1683
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1432,62 +1695,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1432
1695
|
return results;
|
|
1433
1696
|
}
|
|
1434
1697
|
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1435
|
-
// Accepted value shapes for typed ARIA attributes.
|
|
1436
|
-
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1437
|
-
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1438
|
-
// Boolean (true | false)
|
|
1439
|
-
["aria-atomic", { kind: "boolean" }],
|
|
1440
|
-
["aria-busy", { kind: "boolean" }],
|
|
1441
|
-
["aria-disabled", { kind: "boolean" }],
|
|
1442
|
-
["aria-expanded", { kind: "boolean" }],
|
|
1443
|
-
["aria-hidden", { kind: "boolean" }],
|
|
1444
|
-
["aria-modal", { kind: "boolean" }],
|
|
1445
|
-
["aria-multiline", { kind: "boolean" }],
|
|
1446
|
-
["aria-multiselectable", { kind: "boolean" }],
|
|
1447
|
-
["aria-readonly", { kind: "boolean" }],
|
|
1448
|
-
["aria-required", { kind: "boolean" }],
|
|
1449
|
-
["aria-selected", { kind: "boolean" }],
|
|
1450
|
-
// Tristate (true | false | mixed)
|
|
1451
|
-
["aria-checked", { kind: "tristate" }],
|
|
1452
|
-
["aria-pressed", { kind: "tristate" }],
|
|
1453
|
-
// Numeric (any finite number)
|
|
1454
|
-
["aria-valuenow", { kind: "number" }],
|
|
1455
|
-
["aria-valuemin", { kind: "number" }],
|
|
1456
|
-
["aria-valuemax", { kind: "number" }],
|
|
1457
|
-
// Integer with optional range
|
|
1458
|
-
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1459
|
-
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1460
|
-
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1461
|
-
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1462
|
-
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1463
|
-
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1464
|
-
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1465
|
-
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1466
|
-
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1467
|
-
// Enum (specific allowed tokens)
|
|
1468
|
-
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1469
|
-
[
|
|
1470
|
-
"aria-current",
|
|
1471
|
-
{
|
|
1472
|
-
kind: "enum",
|
|
1473
|
-
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1474
|
-
}
|
|
1475
|
-
],
|
|
1476
|
-
[
|
|
1477
|
-
"aria-haspopup",
|
|
1478
|
-
{
|
|
1479
|
-
kind: "enum",
|
|
1480
|
-
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1481
|
-
}
|
|
1482
|
-
],
|
|
1483
|
-
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1484
|
-
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1485
|
-
[
|
|
1486
|
-
"aria-orientation",
|
|
1487
|
-
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1488
|
-
],
|
|
1489
|
-
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1490
|
-
]);
|
|
1491
1698
|
static #isValidAriaValue(value, type) {
|
|
1492
1699
|
switch (type.kind) {
|
|
1493
1700
|
case "boolean":
|
|
@@ -1532,11 +1739,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1532
1739
|
}
|
|
1533
1740
|
}
|
|
1534
1741
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1535
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1742
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1536
1743
|
const results = [];
|
|
1537
1744
|
iterate.forEachEntry(props, (key, value) => {
|
|
1538
1745
|
if (!key.startsWith("aria-")) return;
|
|
1539
|
-
const type =
|
|
1746
|
+
const type = ARIA_VALUE_TYPES.get(key);
|
|
1540
1747
|
if (!isNonNull(type)) return;
|
|
1541
1748
|
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1542
1749
|
results.push({
|
|
@@ -1555,26 +1762,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1555
1762
|
return results;
|
|
1556
1763
|
}
|
|
1557
1764
|
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1558
|
-
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1559
|
-
["h1", 1],
|
|
1560
|
-
["h2", 2],
|
|
1561
|
-
["h3", 3],
|
|
1562
|
-
["h4", 4],
|
|
1563
|
-
["h5", 5],
|
|
1564
|
-
["h6", 6]
|
|
1565
|
-
]);
|
|
1566
1765
|
static #checkRedundantAriaLevel({
|
|
1567
1766
|
tag,
|
|
1568
1767
|
props,
|
|
1569
1768
|
effectiveRole
|
|
1570
1769
|
}) {
|
|
1571
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1572
|
-
const implicitLevel =
|
|
1573
|
-
if (!isNonNull(implicitLevel)) return
|
|
1770
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1771
|
+
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1772
|
+
if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
|
|
1574
1773
|
const raw = props["aria-level"];
|
|
1575
|
-
if (!isNonNull(raw)) return
|
|
1774
|
+
if (!isNonNull(raw)) return NO_VIOLATIONS2;
|
|
1576
1775
|
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1577
|
-
if (!Number.isFinite(n) || n !== implicitLevel) return
|
|
1776
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
|
|
1578
1777
|
return [
|
|
1579
1778
|
{
|
|
1580
1779
|
valid: false,
|
|
@@ -1587,20 +1786,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1587
1786
|
];
|
|
1588
1787
|
}
|
|
1589
1788
|
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1590
|
-
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1591
|
-
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1592
|
-
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1593
|
-
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1594
|
-
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1595
1789
|
static #checkNameRequiredRoles({
|
|
1596
1790
|
tag,
|
|
1597
1791
|
props,
|
|
1598
1792
|
effectiveRole
|
|
1599
1793
|
}) {
|
|
1600
|
-
if (!effectiveRole || !
|
|
1601
|
-
|
|
1602
|
-
if ("
|
|
1603
|
-
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
|
|
1794
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
|
|
1795
|
+
if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
|
|
1796
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
|
|
1604
1797
|
return [
|
|
1605
1798
|
{
|
|
1606
1799
|
valid: false,
|
|
@@ -1610,51 +1803,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1610
1803
|
}
|
|
1611
1804
|
];
|
|
1612
1805
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1620
|
-
["spinbutton", ["aria-valuenow"]]
|
|
1621
|
-
]);
|
|
1622
|
-
static #checkRequiredAriaProperties({
|
|
1623
|
-
props,
|
|
1624
|
-
effectiveRole
|
|
1625
|
-
}) {
|
|
1626
|
-
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1627
|
-
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1628
|
-
if (!isNonNull(required)) return NO_VIOLATIONS;
|
|
1629
|
-
const results = [];
|
|
1630
|
-
iterate.forEach(required, (attr) => {
|
|
1631
|
-
if (attr in props) return;
|
|
1632
|
-
results.push({
|
|
1633
|
-
valid: false,
|
|
1634
|
-
fixable: false,
|
|
1635
|
-
severity: "warning",
|
|
1636
|
-
attribute: attr,
|
|
1637
|
-
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1638
|
-
});
|
|
1639
|
-
});
|
|
1640
|
-
return results;
|
|
1806
|
+
static #requiredAriaPropertiesRule = {
|
|
1807
|
+
attributesByRole: REQUIRED_ARIA_PROPERTIES,
|
|
1808
|
+
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
1809
|
+
};
|
|
1810
|
+
static #checkRequiredAriaProperties(context) {
|
|
1811
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
1641
1812
|
}
|
|
1642
|
-
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1643
|
-
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1644
|
-
"a",
|
|
1645
|
-
"button",
|
|
1646
|
-
"input",
|
|
1647
|
-
"select",
|
|
1648
|
-
"textarea"
|
|
1649
|
-
]);
|
|
1650
1813
|
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1651
1814
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1652
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
1653
|
-
const isInteractive =
|
|
1815
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
|
|
1816
|
+
const isInteractive = INTERACTIVE_TAGS.has(tag);
|
|
1654
1817
|
if (!isInteractive) {
|
|
1655
1818
|
const tabindex = props.tabindex;
|
|
1656
1819
|
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1657
|
-
if (!Number.isFinite(n) || n < 0) return
|
|
1820
|
+
if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
|
|
1658
1821
|
}
|
|
1659
1822
|
return [
|
|
1660
1823
|
{
|
|
@@ -1673,7 +1836,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1673
1836
|
props,
|
|
1674
1837
|
effectiveRole
|
|
1675
1838
|
}) {
|
|
1676
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
1839
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
|
|
1677
1840
|
const results = [];
|
|
1678
1841
|
iterate.forEachEntry(props, (key) => {
|
|
1679
1842
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1689,18 +1852,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1689
1852
|
});
|
|
1690
1853
|
return results;
|
|
1691
1854
|
}
|
|
1692
|
-
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1693
|
-
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1694
|
-
["alert", "assertive"],
|
|
1695
|
-
["status", "polite"],
|
|
1696
|
-
["log", "polite"],
|
|
1697
|
-
["timer", "off"]
|
|
1698
|
-
]);
|
|
1699
1855
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1700
|
-
if (!effectiveRole) return
|
|
1701
|
-
const impliedLive =
|
|
1702
|
-
if (!impliedLive) return
|
|
1703
|
-
if ("aria-live" in props) return
|
|
1856
|
+
if (!effectiveRole) return NO_VIOLATIONS2;
|
|
1857
|
+
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
1858
|
+
if (!impliedLive) return NO_VIOLATIONS2;
|
|
1859
|
+
if ("aria-live" in props) return NO_VIOLATIONS2;
|
|
1704
1860
|
const injectLive = {
|
|
1705
1861
|
kind: `injectLive:${effectiveRole}`,
|
|
1706
1862
|
apply: (ctx) => ({
|
|
@@ -1719,20 +1875,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1719
1875
|
}
|
|
1720
1876
|
];
|
|
1721
1877
|
}
|
|
1722
|
-
static #
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
valid: false,
|
|
1729
|
-
fixable: false,
|
|
1730
|
-
severity: "warning",
|
|
1731
|
-
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1732
|
-
}
|
|
1733
|
-
];
|
|
1878
|
+
static #missingAtomicRule = {
|
|
1879
|
+
attributesByRole: ATOMIC_REQUIREMENTS,
|
|
1880
|
+
diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
|
|
1881
|
+
};
|
|
1882
|
+
static #checkMissingAtomic(context) {
|
|
1883
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
|
|
1734
1884
|
}
|
|
1735
|
-
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1736
1885
|
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1737
1886
|
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1738
1887
|
// produce inconsistent results on cache hits.
|
|
@@ -1746,10 +1895,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1746
1895
|
};
|
|
1747
1896
|
static #checkInvalidAriaRelevant({ props }) {
|
|
1748
1897
|
const relevant = props["aria-relevant"];
|
|
1749
|
-
if (relevant === void 0) return
|
|
1750
|
-
if (typeof relevant !== "string") return
|
|
1898
|
+
if (relevant === void 0) return NO_VIOLATIONS2;
|
|
1899
|
+
if (typeof relevant !== "string") return NO_VIOLATIONS2;
|
|
1751
1900
|
const tokens = relevant.trim().split(/\s+/);
|
|
1752
|
-
const invalid = tokens.filter((t) => !
|
|
1901
|
+
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
1753
1902
|
if (invalid.length > 0) {
|
|
1754
1903
|
return [
|
|
1755
1904
|
{
|
|
@@ -1774,7 +1923,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1774
1923
|
}
|
|
1775
1924
|
];
|
|
1776
1925
|
}
|
|
1777
|
-
return
|
|
1926
|
+
return NO_VIOLATIONS2;
|
|
1778
1927
|
}
|
|
1779
1928
|
};
|
|
1780
1929
|
|
|
@@ -2094,6 +2243,425 @@ var readonlyProps = ({
|
|
|
2094
2243
|
// ../core/src/html/evaluators.ts
|
|
2095
2244
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
2096
2245
|
|
|
2246
|
+
// ../core/src/html/spec/vocabulary/input.ts
|
|
2247
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2248
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2249
|
+
"number",
|
|
2250
|
+
"range",
|
|
2251
|
+
"date",
|
|
2252
|
+
"month",
|
|
2253
|
+
"week",
|
|
2254
|
+
"time",
|
|
2255
|
+
"datetime-local"
|
|
2256
|
+
];
|
|
2257
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2258
|
+
...TEXT_INPUT_TYPES,
|
|
2259
|
+
...NUMERIC_INPUT_TYPES,
|
|
2260
|
+
"checkbox",
|
|
2261
|
+
"radio",
|
|
2262
|
+
"file",
|
|
2263
|
+
"color",
|
|
2264
|
+
"hidden",
|
|
2265
|
+
"button",
|
|
2266
|
+
"submit",
|
|
2267
|
+
"reset",
|
|
2268
|
+
"image"
|
|
2269
|
+
]);
|
|
2270
|
+
|
|
2271
|
+
// ../core/src/html/spec/attributes/input.ts
|
|
2272
|
+
var INPUT_ATTRIBUTE_TYPE_POLICIES = [
|
|
2273
|
+
{ attribute: "checked", allowedTypes: ["checkbox", "radio"] },
|
|
2274
|
+
{ attribute: "multiple", allowedTypes: ["email", "file"] },
|
|
2275
|
+
{ attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2276
|
+
{ attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2277
|
+
{ attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
|
|
2278
|
+
{ attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2279
|
+
{ attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2280
|
+
{ attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2281
|
+
{ attribute: "accept", allowedTypes: ["file"] },
|
|
2282
|
+
{ attribute: "capture", allowedTypes: ["file"] },
|
|
2283
|
+
{ attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
|
|
2284
|
+
{ attribute: "alt", allowedTypes: ["image"] },
|
|
2285
|
+
{ attribute: "height", allowedTypes: ["image"] },
|
|
2286
|
+
{ attribute: "width", allowedTypes: ["image"] }
|
|
2287
|
+
];
|
|
2288
|
+
|
|
2289
|
+
// ../core/src/html/spec/constraints/input.ts
|
|
2290
|
+
var REQUIRED_READONLY_CONFLICT = {
|
|
2291
|
+
props: ["required", "readOnly"],
|
|
2292
|
+
diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
|
|
2293
|
+
};
|
|
2294
|
+
var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
|
|
2295
|
+
REQUIRED_READONLY_CONFLICT
|
|
2296
|
+
];
|
|
2297
|
+
|
|
2298
|
+
// ../core/src/html/spec/validators/attribute-type-validator.ts
|
|
2299
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2300
|
+
function omit(props, key) {
|
|
2301
|
+
const next = { ...props };
|
|
2302
|
+
delete next[key];
|
|
2303
|
+
return next;
|
|
2304
|
+
}
|
|
2305
|
+
function removeAttributeFix(attribute) {
|
|
2306
|
+
return {
|
|
2307
|
+
kind: `removeAttribute:${attribute}`,
|
|
2308
|
+
apply: ({ props }) => {
|
|
2309
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2310
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2311
|
+
}
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
function createInputAttributeTypeRule({
|
|
2315
|
+
attribute,
|
|
2316
|
+
allowedTypes
|
|
2317
|
+
}) {
|
|
2318
|
+
const rule = ({ tag, props }) => {
|
|
2319
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2320
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2321
|
+
if (allowedTypes.includes(type)) return [];
|
|
2322
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2323
|
+
return [
|
|
2324
|
+
{
|
|
2325
|
+
valid: false,
|
|
2326
|
+
fixable: true,
|
|
2327
|
+
severity: diagnostic.severity,
|
|
2328
|
+
fix: removeAttributeFix(attribute),
|
|
2329
|
+
diagnostic
|
|
2330
|
+
}
|
|
2331
|
+
];
|
|
2332
|
+
};
|
|
2333
|
+
return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
// ../core/src/html/spec/validators/mutually-exclusive-validator.ts
|
|
2337
|
+
function createMutuallyExclusiveRule({
|
|
2338
|
+
props: conflictingProps,
|
|
2339
|
+
diagnostic: createDiagnostic
|
|
2340
|
+
}) {
|
|
2341
|
+
const [first, second] = conflictingProps;
|
|
2342
|
+
const rule = ({ tag, props }) => {
|
|
2343
|
+
if (tag !== "input" || !props[first] || !props[second]) return [];
|
|
2344
|
+
const diagnostic = createDiagnostic();
|
|
2345
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2346
|
+
};
|
|
2347
|
+
return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
// ../core/src/html/input-rules.ts
|
|
2351
|
+
var policyByAttribute = Object.fromEntries(
|
|
2352
|
+
INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
|
|
2353
|
+
);
|
|
2354
|
+
function policyFor(attribute) {
|
|
2355
|
+
return policyByAttribute[attribute];
|
|
2356
|
+
}
|
|
2357
|
+
var supportedInputTypeRule = Object.assign(
|
|
2358
|
+
({ tag, props }) => {
|
|
2359
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2360
|
+
const type = props.type;
|
|
2361
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2362
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2363
|
+
return [
|
|
2364
|
+
{
|
|
2365
|
+
valid: false,
|
|
2366
|
+
fixable: false,
|
|
2367
|
+
severity: diagnostic.severity,
|
|
2368
|
+
diagnostic
|
|
2369
|
+
}
|
|
2370
|
+
];
|
|
2371
|
+
},
|
|
2372
|
+
{ readsProps: ["type"], tags: ["input"] }
|
|
2373
|
+
);
|
|
2374
|
+
var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
|
|
2375
|
+
var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
|
|
2376
|
+
var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
|
|
2377
|
+
var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
|
|
2378
|
+
var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
|
|
2379
|
+
var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
|
|
2380
|
+
var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
|
|
2381
|
+
var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
|
|
2382
|
+
var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
|
|
2383
|
+
var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
|
|
2384
|
+
var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
|
|
2385
|
+
var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
|
|
2386
|
+
var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
|
|
2387
|
+
var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
|
|
2388
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2389
|
+
({ tag, props }) => {
|
|
2390
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2391
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2392
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2393
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2394
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
|
|
2398
|
+
tags: ["input"]
|
|
2399
|
+
}
|
|
2400
|
+
);
|
|
2401
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2402
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2403
|
+
({ tag, props }) => {
|
|
2404
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2405
|
+
const autoComplete = props.autoComplete;
|
|
2406
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2407
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2408
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2409
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2410
|
+
},
|
|
2411
|
+
{ readsProps: ["type", "autoComplete"], tags: ["input"] }
|
|
2412
|
+
);
|
|
2413
|
+
var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
|
|
2414
|
+
var INPUT_RULES = [
|
|
2415
|
+
supportedInputTypeRule,
|
|
2416
|
+
checkedRequiresCheckableTypeRule,
|
|
2417
|
+
multipleRequiresSupportedTypeRule,
|
|
2418
|
+
maxLengthRequiresTextTypeRule,
|
|
2419
|
+
minLengthRequiresTextTypeRule,
|
|
2420
|
+
patternRequiresTextTypeRule,
|
|
2421
|
+
minRequiresNumericTypeRule,
|
|
2422
|
+
maxRequiresNumericTypeRule,
|
|
2423
|
+
stepRequiresNumericTypeRule,
|
|
2424
|
+
acceptRequiresFileTypeRule,
|
|
2425
|
+
captureRequiresFileTypeRule,
|
|
2426
|
+
sizeRequiresTextTypeRule,
|
|
2427
|
+
altRequiresImageTypeRule,
|
|
2428
|
+
heightRequiresImageTypeRule,
|
|
2429
|
+
widthRequiresImageTypeRule,
|
|
2430
|
+
inputAccessibleNameRule,
|
|
2431
|
+
passwordAutocompleteRule,
|
|
2432
|
+
requiredReadOnlyConflictRule
|
|
2433
|
+
];
|
|
2434
|
+
|
|
2435
|
+
// ../core/src/html/spec/types.ts
|
|
2436
|
+
function definePropRolePolicy(prop, map2, fallback) {
|
|
2437
|
+
return { kind: "byProp", prop, map: map2, fallback };
|
|
2438
|
+
}
|
|
2439
|
+
function resolveAllowedRoles(spec, props) {
|
|
2440
|
+
const policy = spec.allowedRoles;
|
|
2441
|
+
if (!policy) return void 0;
|
|
2442
|
+
switch (policy.kind) {
|
|
2443
|
+
case "fixed":
|
|
2444
|
+
return policy.roles;
|
|
2445
|
+
case "byProp": {
|
|
2446
|
+
const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
|
|
2447
|
+
return policy.map[value];
|
|
2448
|
+
}
|
|
2449
|
+
case "dynamic":
|
|
2450
|
+
return policy.resolve({ props });
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// ../core/src/html/spec/roles/input.ts
|
|
2455
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2456
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2457
|
+
radio: ["menuitemradio"],
|
|
2458
|
+
range: [],
|
|
2459
|
+
number: [],
|
|
2460
|
+
search: ["combobox"],
|
|
2461
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2462
|
+
email: ["combobox"],
|
|
2463
|
+
tel: ["combobox"],
|
|
2464
|
+
url: ["combobox"],
|
|
2465
|
+
button: [
|
|
2466
|
+
"link",
|
|
2467
|
+
"menuitem",
|
|
2468
|
+
"menuitemcheckbox",
|
|
2469
|
+
"menuitemradio",
|
|
2470
|
+
"option",
|
|
2471
|
+
"radio",
|
|
2472
|
+
"switch",
|
|
2473
|
+
"tab"
|
|
2474
|
+
],
|
|
2475
|
+
submit: [
|
|
2476
|
+
"link",
|
|
2477
|
+
"menuitem",
|
|
2478
|
+
"menuitemcheckbox",
|
|
2479
|
+
"menuitemradio",
|
|
2480
|
+
"option",
|
|
2481
|
+
"radio",
|
|
2482
|
+
"switch",
|
|
2483
|
+
"tab"
|
|
2484
|
+
],
|
|
2485
|
+
reset: [
|
|
2486
|
+
"link",
|
|
2487
|
+
"menuitem",
|
|
2488
|
+
"menuitemcheckbox",
|
|
2489
|
+
"menuitemradio",
|
|
2490
|
+
"option",
|
|
2491
|
+
"radio",
|
|
2492
|
+
"switch",
|
|
2493
|
+
"tab"
|
|
2494
|
+
],
|
|
2495
|
+
image: [
|
|
2496
|
+
"link",
|
|
2497
|
+
"menuitem",
|
|
2498
|
+
"menuitemcheckbox",
|
|
2499
|
+
"menuitemradio",
|
|
2500
|
+
"option",
|
|
2501
|
+
"radio",
|
|
2502
|
+
"switch",
|
|
2503
|
+
"tab"
|
|
2504
|
+
],
|
|
2505
|
+
hidden: []
|
|
2506
|
+
};
|
|
2507
|
+
|
|
2508
|
+
// ../core/src/html/spec/elements/input.ts
|
|
2509
|
+
var inputElementSpec = {
|
|
2510
|
+
tag: "input",
|
|
2511
|
+
allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
|
|
2512
|
+
attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
|
|
2513
|
+
mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
|
|
2514
|
+
};
|
|
2515
|
+
|
|
2516
|
+
// ../core/src/html/spec/roles/img.ts
|
|
2517
|
+
var IMG_NAMED_ROLES = [
|
|
2518
|
+
"button",
|
|
2519
|
+
"checkbox",
|
|
2520
|
+
"link",
|
|
2521
|
+
"menuitem",
|
|
2522
|
+
"menuitemcheckbox",
|
|
2523
|
+
"menuitemradio",
|
|
2524
|
+
"option",
|
|
2525
|
+
"progressbar",
|
|
2526
|
+
"scrollbar",
|
|
2527
|
+
"separator",
|
|
2528
|
+
"slider",
|
|
2529
|
+
"switch",
|
|
2530
|
+
"tab",
|
|
2531
|
+
"treeitem"
|
|
2532
|
+
];
|
|
2533
|
+
|
|
2534
|
+
// ../core/src/html/spec/elements/img.ts
|
|
2535
|
+
var imgElementSpec = {
|
|
2536
|
+
tag: "img",
|
|
2537
|
+
allowedRoles: {
|
|
2538
|
+
kind: "dynamic",
|
|
2539
|
+
resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
|
|
2540
|
+
}
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
// ../core/src/html/spec/roles/table.ts
|
|
2544
|
+
var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
|
|
2545
|
+
|
|
2546
|
+
// ../core/src/html/spec/elements/table.ts
|
|
2547
|
+
var tableElementSpec = {
|
|
2548
|
+
tag: "table",
|
|
2549
|
+
allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
|
|
2550
|
+
};
|
|
2551
|
+
|
|
2552
|
+
// ../core/src/html/role-restrictions.ts
|
|
2553
|
+
var ALLOWED_ROLES = {
|
|
2554
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2555
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2556
|
+
footer: ["group", "none", "presentation"],
|
|
2557
|
+
header: ["group", "none", "presentation"],
|
|
2558
|
+
main: [],
|
|
2559
|
+
nav: [],
|
|
2560
|
+
a: [
|
|
2561
|
+
"button",
|
|
2562
|
+
"checkbox",
|
|
2563
|
+
"menuitem",
|
|
2564
|
+
"menuitemcheckbox",
|
|
2565
|
+
"menuitemradio",
|
|
2566
|
+
"option",
|
|
2567
|
+
"radio",
|
|
2568
|
+
"switch",
|
|
2569
|
+
"tab",
|
|
2570
|
+
"treeitem"
|
|
2571
|
+
],
|
|
2572
|
+
button: [
|
|
2573
|
+
"checkbox",
|
|
2574
|
+
"link",
|
|
2575
|
+
"menuitem",
|
|
2576
|
+
"menuitemcheckbox",
|
|
2577
|
+
"menuitemradio",
|
|
2578
|
+
"option",
|
|
2579
|
+
"radio",
|
|
2580
|
+
"switch",
|
|
2581
|
+
"tab"
|
|
2582
|
+
],
|
|
2583
|
+
select: ["menu"],
|
|
2584
|
+
h1: ["tab", "presentation", "none"],
|
|
2585
|
+
h2: ["tab", "presentation", "none"],
|
|
2586
|
+
h3: ["tab", "presentation", "none"],
|
|
2587
|
+
h4: ["tab", "presentation", "none"],
|
|
2588
|
+
h5: ["tab", "presentation", "none"],
|
|
2589
|
+
h6: ["tab", "presentation", "none"],
|
|
2590
|
+
ul: [
|
|
2591
|
+
"directory",
|
|
2592
|
+
"group",
|
|
2593
|
+
"listbox",
|
|
2594
|
+
"menu",
|
|
2595
|
+
"menubar",
|
|
2596
|
+
"radiogroup",
|
|
2597
|
+
"tablist",
|
|
2598
|
+
"toolbar",
|
|
2599
|
+
"tree"
|
|
2600
|
+
],
|
|
2601
|
+
ol: [
|
|
2602
|
+
"directory",
|
|
2603
|
+
"group",
|
|
2604
|
+
"listbox",
|
|
2605
|
+
"menu",
|
|
2606
|
+
"menubar",
|
|
2607
|
+
"radiogroup",
|
|
2608
|
+
"tablist",
|
|
2609
|
+
"toolbar",
|
|
2610
|
+
"tree"
|
|
2611
|
+
],
|
|
2612
|
+
li: [
|
|
2613
|
+
"menuitem",
|
|
2614
|
+
"menuitemcheckbox",
|
|
2615
|
+
"menuitemradio",
|
|
2616
|
+
"option",
|
|
2617
|
+
"none",
|
|
2618
|
+
"presentation",
|
|
2619
|
+
"radio",
|
|
2620
|
+
"separator",
|
|
2621
|
+
"tab",
|
|
2622
|
+
"treeitem"
|
|
2623
|
+
],
|
|
2624
|
+
dialog: ["alertdialog"],
|
|
2625
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2626
|
+
};
|
|
2627
|
+
var ELEMENT_SPECS = {
|
|
2628
|
+
input: inputElementSpec,
|
|
2629
|
+
img: imgElementSpec,
|
|
2630
|
+
table: tableElementSpec
|
|
2631
|
+
};
|
|
2632
|
+
function getAllowedRoles(tag, props) {
|
|
2633
|
+
const spec = ELEMENT_SPECS[tag];
|
|
2634
|
+
if (spec) return resolveAllowedRoles(spec, props);
|
|
2635
|
+
return ALLOWED_ROLES[tag];
|
|
2636
|
+
}
|
|
2637
|
+
var removeRoleFix = {
|
|
2638
|
+
kind: "removeRole",
|
|
2639
|
+
apply: ({ props }) => {
|
|
2640
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2641
|
+
const { role: _role, ...rest } = props;
|
|
2642
|
+
return { applied: true, next: rest, previous: props };
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
var roleNotPermittedRule = Object.assign(
|
|
2646
|
+
({ tag, props, implicitRole }) => {
|
|
2647
|
+
const role = props.role;
|
|
2648
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2649
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2650
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2651
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2652
|
+
return [
|
|
2653
|
+
{
|
|
2654
|
+
valid: false,
|
|
2655
|
+
fixable: true,
|
|
2656
|
+
severity: diagnostic.severity,
|
|
2657
|
+
fix: removeRoleFix,
|
|
2658
|
+
diagnostic
|
|
2659
|
+
}
|
|
2660
|
+
];
|
|
2661
|
+
},
|
|
2662
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2663
|
+
);
|
|
2664
|
+
|
|
2097
2665
|
// ../core/src/html/aria-rules.ts
|
|
2098
2666
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
2099
2667
|
var removeLandmarkRoleOverride = {
|
|
@@ -2104,20 +2672,24 @@ var removeLandmarkRoleOverride = {
|
|
|
2104
2672
|
return { applied: true, next: rest, previous: props };
|
|
2105
2673
|
}
|
|
2106
2674
|
};
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
}
|
|
2675
|
+
var landmarkRoleRule = Object.assign(
|
|
2676
|
+
({ tag, props, implicitRole }) => {
|
|
2677
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2678
|
+
const role = props.role;
|
|
2679
|
+
if (!role || role === implicitRole) return [];
|
|
2680
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2681
|
+
return [
|
|
2682
|
+
{
|
|
2683
|
+
valid: false,
|
|
2684
|
+
fixable: true,
|
|
2685
|
+
severity: diagnostic.severity,
|
|
2686
|
+
fix: removeLandmarkRoleOverride,
|
|
2687
|
+
diagnostic
|
|
2688
|
+
}
|
|
2689
|
+
];
|
|
2690
|
+
},
|
|
2691
|
+
{ tags: [...LANDMARK_TAG_SET] }
|
|
2692
|
+
);
|
|
2121
2693
|
function requireAccessibleName({ tag, props }) {
|
|
2122
2694
|
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2123
2695
|
return [
|
|
@@ -2130,11 +2702,19 @@ function requireAccessibleName({ tag, props }) {
|
|
|
2130
2702
|
];
|
|
2131
2703
|
}
|
|
2132
2704
|
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2705
|
+
var landmarkNameAdvisory = Object.assign(
|
|
2706
|
+
(ctx) => {
|
|
2707
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2708
|
+
return requireAccessibleName(ctx);
|
|
2709
|
+
},
|
|
2710
|
+
{ tags: [...NAMED_LANDMARK_TAGS] }
|
|
2711
|
+
);
|
|
2712
|
+
var HTML_ARIA_RULES = [
|
|
2713
|
+
landmarkRoleRule,
|
|
2714
|
+
landmarkNameAdvisory,
|
|
2715
|
+
roleNotPermittedRule,
|
|
2716
|
+
...INPUT_RULES
|
|
2717
|
+
];
|
|
2138
2718
|
|
|
2139
2719
|
// ../core/src/html/contracts.ts
|
|
2140
2720
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2229,6 +2809,62 @@ var figureContract = contract([
|
|
|
2229
2809
|
]);
|
|
2230
2810
|
var detailsContract = firstChildContract("summary", "summary");
|
|
2231
2811
|
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2812
|
+
var objectContract = contract([
|
|
2813
|
+
{ name: "param", match: isTag("param") },
|
|
2814
|
+
{ name: "content", match: isOpenContent("param") }
|
|
2815
|
+
]);
|
|
2816
|
+
var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
|
|
2817
|
+
var buttonContract = closedContract([
|
|
2818
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2819
|
+
]);
|
|
2820
|
+
var anchorContract = closedContract([
|
|
2821
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2822
|
+
]);
|
|
2823
|
+
var LABELABLE_TAGS = [
|
|
2824
|
+
"button",
|
|
2825
|
+
"input",
|
|
2826
|
+
"meter",
|
|
2827
|
+
"output",
|
|
2828
|
+
"progress",
|
|
2829
|
+
"select",
|
|
2830
|
+
"textarea"
|
|
2831
|
+
];
|
|
2832
|
+
var labelContract = contract([
|
|
2833
|
+
{ name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
|
|
2834
|
+
]);
|
|
2835
|
+
var P_BLOCKED_TAGS = [
|
|
2836
|
+
"address",
|
|
2837
|
+
"article",
|
|
2838
|
+
"aside",
|
|
2839
|
+
"blockquote",
|
|
2840
|
+
"details",
|
|
2841
|
+
"dialog",
|
|
2842
|
+
"div",
|
|
2843
|
+
"dl",
|
|
2844
|
+
"fieldset",
|
|
2845
|
+
"figure",
|
|
2846
|
+
"footer",
|
|
2847
|
+
"form",
|
|
2848
|
+
"h1",
|
|
2849
|
+
"h2",
|
|
2850
|
+
"h3",
|
|
2851
|
+
"h4",
|
|
2852
|
+
"h5",
|
|
2853
|
+
"h6",
|
|
2854
|
+
"header",
|
|
2855
|
+
"hr",
|
|
2856
|
+
"main",
|
|
2857
|
+
"nav",
|
|
2858
|
+
"ol",
|
|
2859
|
+
"p",
|
|
2860
|
+
"pre",
|
|
2861
|
+
"section",
|
|
2862
|
+
"table",
|
|
2863
|
+
"ul"
|
|
2864
|
+
];
|
|
2865
|
+
var pContract = closedContract([
|
|
2866
|
+
{ name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
|
|
2867
|
+
]);
|
|
2232
2868
|
var mediaContract = contract([
|
|
2233
2869
|
{ name: "source", match: isTag("source") },
|
|
2234
2870
|
{ name: "track", match: isTag("track") },
|
|
@@ -2283,6 +2919,11 @@ var htmlContracts = {
|
|
|
2283
2919
|
details: detailsContract,
|
|
2284
2920
|
fieldset: fieldsetContract,
|
|
2285
2921
|
dialog: dialogContract,
|
|
2922
|
+
object: objectContract,
|
|
2923
|
+
button: buttonContract,
|
|
2924
|
+
a: anchorContract,
|
|
2925
|
+
label: labelContract,
|
|
2926
|
+
p: pContract,
|
|
2286
2927
|
head: headContract,
|
|
2287
2928
|
html: htmlContract
|
|
2288
2929
|
};
|
|
@@ -2595,21 +3236,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2595
3236
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2596
3237
|
|
|
2597
3238
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2598
|
-
import { DiagnosticCategory as
|
|
3239
|
+
import { DiagnosticCategory as DiagnosticCategory6, DiagnosticCode as DiagnosticCode6 } from "../_shared/diagnostics.js";
|
|
2599
3240
|
var PluginDiagnostics = {
|
|
2600
3241
|
invalidShape(received) {
|
|
2601
3242
|
const got = received === null ? "null" : typeof received;
|
|
2602
3243
|
return {
|
|
2603
|
-
code:
|
|
2604
|
-
category:
|
|
3244
|
+
code: DiagnosticCode6.PluginInvalidShape,
|
|
3245
|
+
category: DiagnosticCategory6.Internal,
|
|
2605
3246
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2606
3247
|
};
|
|
2607
3248
|
},
|
|
2608
3249
|
pipelineReturnType(received) {
|
|
2609
3250
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2610
3251
|
return {
|
|
2611
|
-
code:
|
|
2612
|
-
category:
|
|
3252
|
+
code: DiagnosticCode6.PluginPipelineReturnType,
|
|
3253
|
+
category: DiagnosticCategory6.Internal,
|
|
2613
3254
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2614
3255
|
};
|
|
2615
3256
|
}
|