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
|
@@ -993,11 +993,28 @@ var ContractDiagnostics = {
|
|
|
993
993
|
|
|
994
994
|
// ../../lib/contract/src/diagnostics/html.ts
|
|
995
995
|
import { DiagnosticCategory as DiagnosticCategory3, DiagnosticCode as DiagnosticCode3 } from "./_shared/diagnostics.js";
|
|
996
|
+
var ATTRIBUTE_IGNORED_CODES = {
|
|
997
|
+
checked: DiagnosticCode3.HtmlInputCheckedIgnoredForType,
|
|
998
|
+
multiple: DiagnosticCode3.HtmlInputMultipleIgnoredForType,
|
|
999
|
+
maxLength: DiagnosticCode3.HtmlInputMaxLengthIgnoredForType,
|
|
1000
|
+
minLength: DiagnosticCode3.HtmlInputMinLengthIgnoredForType,
|
|
1001
|
+
pattern: DiagnosticCode3.HtmlInputPatternIgnoredForType,
|
|
1002
|
+
min: DiagnosticCode3.HtmlInputMinIgnoredForType,
|
|
1003
|
+
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
1004
|
+
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
1005
|
+
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
1006
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
|
|
1007
|
+
size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
|
|
1008
|
+
alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
|
|
1009
|
+
height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
|
|
1010
|
+
width: DiagnosticCode3.HtmlInputWidthIgnoredForType
|
|
1011
|
+
};
|
|
996
1012
|
var HtmlDiagnostics = {
|
|
997
1013
|
emptyRole(tag) {
|
|
998
1014
|
return {
|
|
999
1015
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
1000
1016
|
category: DiagnosticCategory3.HTML,
|
|
1017
|
+
severity: "warning",
|
|
1001
1018
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
1002
1019
|
};
|
|
1003
1020
|
},
|
|
@@ -1005,6 +1022,7 @@ var HtmlDiagnostics = {
|
|
|
1005
1022
|
return {
|
|
1006
1023
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
1007
1024
|
category: DiagnosticCategory3.HTML,
|
|
1025
|
+
severity: "warning",
|
|
1008
1026
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
1009
1027
|
};
|
|
1010
1028
|
},
|
|
@@ -1012,6 +1030,7 @@ var HtmlDiagnostics = {
|
|
|
1012
1030
|
return {
|
|
1013
1031
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
1014
1032
|
category: DiagnosticCategory3.HTML,
|
|
1033
|
+
severity: "error",
|
|
1015
1034
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
1016
1035
|
};
|
|
1017
1036
|
},
|
|
@@ -1019,6 +1038,7 @@ var HtmlDiagnostics = {
|
|
|
1019
1038
|
return {
|
|
1020
1039
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
1021
1040
|
category: DiagnosticCategory3.HTML,
|
|
1041
|
+
severity: "error",
|
|
1022
1042
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
1023
1043
|
};
|
|
1024
1044
|
},
|
|
@@ -1026,6 +1046,7 @@ var HtmlDiagnostics = {
|
|
|
1026
1046
|
return {
|
|
1027
1047
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
1028
1048
|
category: DiagnosticCategory3.HTML,
|
|
1049
|
+
severity: "error",
|
|
1029
1050
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
1030
1051
|
};
|
|
1031
1052
|
},
|
|
@@ -1033,34 +1054,148 @@ var HtmlDiagnostics = {
|
|
|
1033
1054
|
return {
|
|
1034
1055
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
1035
1056
|
category: DiagnosticCategory3.HTML,
|
|
1057
|
+
severity: "error",
|
|
1036
1058
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
1037
1059
|
};
|
|
1060
|
+
},
|
|
1061
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
1062
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
1063
|
+
return {
|
|
1064
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
1065
|
+
category: DiagnosticCategory3.HTML,
|
|
1066
|
+
severity: "error",
|
|
1067
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
1068
|
+
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.'
|
|
1069
|
+
};
|
|
1070
|
+
},
|
|
1071
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
1072
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
1073
|
+
input: {
|
|
1074
|
+
unsupportedType(type) {
|
|
1075
|
+
return {
|
|
1076
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
1077
|
+
category: DiagnosticCategory3.HTML,
|
|
1078
|
+
severity: "warning",
|
|
1079
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
1080
|
+
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).',
|
|
1081
|
+
suggestions: [
|
|
1082
|
+
{
|
|
1083
|
+
title: "Check for a typo in the type value",
|
|
1084
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
1085
|
+
}
|
|
1086
|
+
]
|
|
1087
|
+
};
|
|
1088
|
+
},
|
|
1089
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
1090
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
1091
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
1092
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
1093
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
1094
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
1095
|
+
return {
|
|
1096
|
+
code,
|
|
1097
|
+
category: DiagnosticCategory3.HTML,
|
|
1098
|
+
severity: "warning",
|
|
1099
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
1100
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
1101
|
+
suggestions: [
|
|
1102
|
+
{
|
|
1103
|
+
title: `Remove "${attribute}"`,
|
|
1104
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
1105
|
+
}
|
|
1106
|
+
]
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1038
1109
|
}
|
|
1039
1110
|
};
|
|
1040
1111
|
|
|
1041
|
-
// ../../lib/contract/src/diagnostics/
|
|
1112
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
1042
1113
|
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "./_shared/diagnostics.js";
|
|
1114
|
+
function accessibilityFact(input) {
|
|
1115
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
1116
|
+
}
|
|
1117
|
+
var InputAccessibilityDiagnostics = {
|
|
1118
|
+
missingAccessibleName() {
|
|
1119
|
+
return accessibilityFact({
|
|
1120
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
1121
|
+
severity: "warning",
|
|
1122
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1123
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
1124
|
+
suggestions: [
|
|
1125
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1126
|
+
{
|
|
1127
|
+
title: "Add an associated <label>",
|
|
1128
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1129
|
+
}
|
|
1130
|
+
]
|
|
1131
|
+
});
|
|
1132
|
+
},
|
|
1133
|
+
placeholderIsNotLabel() {
|
|
1134
|
+
return accessibilityFact({
|
|
1135
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
1136
|
+
severity: "warning",
|
|
1137
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1138
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1139
|
+
suggestions: [
|
|
1140
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1141
|
+
{
|
|
1142
|
+
title: "Add an associated <label>",
|
|
1143
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1144
|
+
}
|
|
1145
|
+
]
|
|
1146
|
+
});
|
|
1147
|
+
},
|
|
1148
|
+
passwordMissingAutocomplete() {
|
|
1149
|
+
return accessibilityFact({
|
|
1150
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1151
|
+
severity: "warning",
|
|
1152
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1153
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1154
|
+
suggestions: [
|
|
1155
|
+
{
|
|
1156
|
+
title: 'Set autoComplete="current-password"',
|
|
1157
|
+
description: "Use this for sign-in forms."
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
title: 'Set autoComplete="new-password"',
|
|
1161
|
+
description: "Use this for sign-up / change-password forms."
|
|
1162
|
+
}
|
|
1163
|
+
]
|
|
1164
|
+
});
|
|
1165
|
+
},
|
|
1166
|
+
requiredReadOnlyConflict() {
|
|
1167
|
+
return accessibilityFact({
|
|
1168
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1169
|
+
severity: "warning",
|
|
1170
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1171
|
+
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."
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1177
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "./_shared/diagnostics.js";
|
|
1043
1178
|
var SlotDiagnostics = {
|
|
1044
1179
|
exclusive(name) {
|
|
1045
1180
|
return {
|
|
1046
|
-
code:
|
|
1047
|
-
category:
|
|
1181
|
+
code: DiagnosticCode5.SlotExclusive,
|
|
1182
|
+
category: DiagnosticCategory5.Contract,
|
|
1048
1183
|
component: name,
|
|
1049
1184
|
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
1050
1185
|
};
|
|
1051
1186
|
},
|
|
1052
1187
|
singleChildRequired(name, elementTerm) {
|
|
1053
1188
|
return {
|
|
1054
|
-
code:
|
|
1055
|
-
category:
|
|
1189
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1190
|
+
category: DiagnosticCategory5.Contract,
|
|
1056
1191
|
component: name,
|
|
1057
1192
|
message: `${name}: asChild requires a ${elementTerm} child`
|
|
1058
1193
|
};
|
|
1059
1194
|
},
|
|
1060
1195
|
singleChildExceeded(name, elementTerm, count) {
|
|
1061
1196
|
return {
|
|
1062
|
-
code:
|
|
1063
|
-
category:
|
|
1197
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1198
|
+
category: DiagnosticCategory5.Contract,
|
|
1064
1199
|
component: name,
|
|
1065
1200
|
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
1066
1201
|
};
|
|
@@ -1068,16 +1203,16 @@ var SlotDiagnostics = {
|
|
|
1068
1203
|
discardedChildren(name, elementTerm, count) {
|
|
1069
1204
|
const suffix = count === 1 ? "" : "ren";
|
|
1070
1205
|
return {
|
|
1071
|
-
code:
|
|
1072
|
-
category:
|
|
1206
|
+
code: DiagnosticCode5.SlotDiscardedChildren,
|
|
1207
|
+
category: DiagnosticCategory5.Contract,
|
|
1073
1208
|
component: name,
|
|
1074
1209
|
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
1075
1210
|
};
|
|
1076
1211
|
},
|
|
1077
1212
|
renderFnRequired(name, received) {
|
|
1078
1213
|
return {
|
|
1079
|
-
code:
|
|
1080
|
-
category:
|
|
1214
|
+
code: DiagnosticCode5.SlotRenderFn,
|
|
1215
|
+
category: DiagnosticCategory5.Contract,
|
|
1081
1216
|
component: name,
|
|
1082
1217
|
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
1083
1218
|
};
|
|
@@ -1106,8 +1241,132 @@ var InvariantBase = class {
|
|
|
1106
1241
|
}
|
|
1107
1242
|
};
|
|
1108
1243
|
|
|
1109
|
-
// ../../lib/contract/src/aria/
|
|
1244
|
+
// ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1245
|
+
var REQUIRED_ARIA_PROPERTIES = {
|
|
1246
|
+
combobox: ["aria-expanded"],
|
|
1247
|
+
option: ["aria-selected"],
|
|
1248
|
+
slider: ["aria-valuenow"],
|
|
1249
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1250
|
+
spinbutton: ["aria-valuenow"]
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
// ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1254
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1255
|
+
|
|
1256
|
+
// ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1110
1257
|
var NO_VIOLATIONS = [{ valid: true }];
|
|
1258
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1259
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1260
|
+
}
|
|
1261
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1262
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1263
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1264
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1265
|
+
const results = [];
|
|
1266
|
+
for (const attribute of requiredAttributes) {
|
|
1267
|
+
if (attribute in props) continue;
|
|
1268
|
+
results.push({
|
|
1269
|
+
valid: false,
|
|
1270
|
+
fixable: false,
|
|
1271
|
+
severity: "warning",
|
|
1272
|
+
attribute,
|
|
1273
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
return results;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1280
|
+
var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1281
|
+
["alert", "assertive"],
|
|
1282
|
+
["status", "polite"],
|
|
1283
|
+
["log", "polite"],
|
|
1284
|
+
["timer", "off"]
|
|
1285
|
+
]);
|
|
1286
|
+
var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1287
|
+
|
|
1288
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1289
|
+
var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1290
|
+
// Boolean (true | false)
|
|
1291
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1292
|
+
["aria-busy", { kind: "boolean" }],
|
|
1293
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1294
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1295
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1296
|
+
["aria-modal", { kind: "boolean" }],
|
|
1297
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1298
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1299
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1300
|
+
["aria-required", { kind: "boolean" }],
|
|
1301
|
+
["aria-selected", { kind: "boolean" }],
|
|
1302
|
+
// Tristate (true | false | mixed)
|
|
1303
|
+
["aria-checked", { kind: "tristate" }],
|
|
1304
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1305
|
+
// Numeric (any finite number)
|
|
1306
|
+
["aria-valuenow", { kind: "number" }],
|
|
1307
|
+
["aria-valuemin", { kind: "number" }],
|
|
1308
|
+
["aria-valuemax", { kind: "number" }],
|
|
1309
|
+
// Integer with optional range
|
|
1310
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1311
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1312
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1313
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1314
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1315
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1316
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1317
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1318
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1319
|
+
// Enum (specific allowed tokens)
|
|
1320
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1321
|
+
[
|
|
1322
|
+
"aria-current",
|
|
1323
|
+
{
|
|
1324
|
+
kind: "enum",
|
|
1325
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1326
|
+
}
|
|
1327
|
+
],
|
|
1328
|
+
[
|
|
1329
|
+
"aria-haspopup",
|
|
1330
|
+
{
|
|
1331
|
+
kind: "enum",
|
|
1332
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1333
|
+
}
|
|
1334
|
+
],
|
|
1335
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1336
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1337
|
+
["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
|
|
1338
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1339
|
+
]);
|
|
1340
|
+
|
|
1341
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1342
|
+
var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1343
|
+
"additions",
|
|
1344
|
+
"removals",
|
|
1345
|
+
"text",
|
|
1346
|
+
"all"
|
|
1347
|
+
]);
|
|
1348
|
+
|
|
1349
|
+
// ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1350
|
+
var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1351
|
+
["h1", 1],
|
|
1352
|
+
["h2", 2],
|
|
1353
|
+
["h3", 3],
|
|
1354
|
+
["h4", 4],
|
|
1355
|
+
["h5", 5],
|
|
1356
|
+
["h6", 6]
|
|
1357
|
+
]);
|
|
1358
|
+
|
|
1359
|
+
// ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
|
|
1360
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1361
|
+
"a",
|
|
1362
|
+
"button",
|
|
1363
|
+
"input",
|
|
1364
|
+
"select",
|
|
1365
|
+
"textarea"
|
|
1366
|
+
]);
|
|
1367
|
+
|
|
1368
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1369
|
+
var NO_VIOLATIONS2 = [{ valid: true }];
|
|
1111
1370
|
function isIntrinsicTag(tag) {
|
|
1112
1371
|
return isString(tag);
|
|
1113
1372
|
}
|
|
@@ -1139,7 +1398,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1139
1398
|
tag,
|
|
1140
1399
|
role: "",
|
|
1141
1400
|
attribute: void 0,
|
|
1142
|
-
severity:
|
|
1401
|
+
severity: d.severity,
|
|
1143
1402
|
phase: "evaluate"
|
|
1144
1403
|
}
|
|
1145
1404
|
]
|
|
@@ -1169,6 +1428,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1169
1428
|
const violations = [];
|
|
1170
1429
|
const fixes = [];
|
|
1171
1430
|
iterate.forEach(rules, (rule) => {
|
|
1431
|
+
if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
|
|
1172
1432
|
iterate.forEach(rule(context), (result) => {
|
|
1173
1433
|
if (result.valid) return;
|
|
1174
1434
|
const {
|
|
@@ -1194,7 +1454,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1194
1454
|
return { violations, fixes };
|
|
1195
1455
|
}
|
|
1196
1456
|
static #getRules(context) {
|
|
1197
|
-
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) &&
|
|
1457
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1198
1458
|
return _AriaPolicyEngine.#pipeline;
|
|
1199
1459
|
}
|
|
1200
1460
|
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
@@ -1396,44 +1656,47 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1396
1656
|
implicitRole
|
|
1397
1657
|
}) {
|
|
1398
1658
|
const role = props.role;
|
|
1399
|
-
if (!implicitRole || !role || role === implicitRole) return
|
|
1659
|
+
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
|
|
1400
1660
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1661
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1401
1662
|
return [
|
|
1402
1663
|
{
|
|
1403
1664
|
valid: false,
|
|
1404
1665
|
fixable: true,
|
|
1405
|
-
severity:
|
|
1666
|
+
severity: diagnostic.severity,
|
|
1406
1667
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1407
|
-
diagnostic
|
|
1668
|
+
diagnostic
|
|
1408
1669
|
}
|
|
1409
1670
|
];
|
|
1410
1671
|
}
|
|
1411
|
-
return
|
|
1672
|
+
return NO_VIOLATIONS2;
|
|
1412
1673
|
}
|
|
1413
1674
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1414
1675
|
const role = props.role;
|
|
1415
|
-
if (!implicitRole || !role || role !== implicitRole) return
|
|
1676
|
+
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
|
|
1677
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1416
1678
|
return [
|
|
1417
1679
|
{
|
|
1418
1680
|
valid: false,
|
|
1419
1681
|
fixable: true,
|
|
1420
|
-
severity:
|
|
1682
|
+
severity: diagnostic.severity,
|
|
1421
1683
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1422
|
-
diagnostic
|
|
1684
|
+
diagnostic
|
|
1423
1685
|
}
|
|
1424
1686
|
];
|
|
1425
1687
|
}
|
|
1426
1688
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1427
1689
|
const role = props.role;
|
|
1428
|
-
if (role !== "region") return
|
|
1429
|
-
if (!isStandaloneTag(tag)) return
|
|
1690
|
+
if (role !== "region") return NO_VIOLATIONS2;
|
|
1691
|
+
if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
|
|
1692
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1430
1693
|
return [
|
|
1431
1694
|
{
|
|
1432
1695
|
valid: false,
|
|
1433
1696
|
fixable: true,
|
|
1434
|
-
severity:
|
|
1697
|
+
severity: diagnostic.severity,
|
|
1435
1698
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1436
|
-
diagnostic
|
|
1699
|
+
diagnostic
|
|
1437
1700
|
}
|
|
1438
1701
|
];
|
|
1439
1702
|
}
|
|
@@ -1442,7 +1705,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1442
1705
|
props,
|
|
1443
1706
|
effectiveRole
|
|
1444
1707
|
}) {
|
|
1445
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1708
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1446
1709
|
const results = [];
|
|
1447
1710
|
iterate.forEachEntry(props, (key) => {
|
|
1448
1711
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1460,62 +1723,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1460
1723
|
return results;
|
|
1461
1724
|
}
|
|
1462
1725
|
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1463
|
-
// Accepted value shapes for typed ARIA attributes.
|
|
1464
|
-
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1465
|
-
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1466
|
-
// Boolean (true | false)
|
|
1467
|
-
["aria-atomic", { kind: "boolean" }],
|
|
1468
|
-
["aria-busy", { kind: "boolean" }],
|
|
1469
|
-
["aria-disabled", { kind: "boolean" }],
|
|
1470
|
-
["aria-expanded", { kind: "boolean" }],
|
|
1471
|
-
["aria-hidden", { kind: "boolean" }],
|
|
1472
|
-
["aria-modal", { kind: "boolean" }],
|
|
1473
|
-
["aria-multiline", { kind: "boolean" }],
|
|
1474
|
-
["aria-multiselectable", { kind: "boolean" }],
|
|
1475
|
-
["aria-readonly", { kind: "boolean" }],
|
|
1476
|
-
["aria-required", { kind: "boolean" }],
|
|
1477
|
-
["aria-selected", { kind: "boolean" }],
|
|
1478
|
-
// Tristate (true | false | mixed)
|
|
1479
|
-
["aria-checked", { kind: "tristate" }],
|
|
1480
|
-
["aria-pressed", { kind: "tristate" }],
|
|
1481
|
-
// Numeric (any finite number)
|
|
1482
|
-
["aria-valuenow", { kind: "number" }],
|
|
1483
|
-
["aria-valuemin", { kind: "number" }],
|
|
1484
|
-
["aria-valuemax", { kind: "number" }],
|
|
1485
|
-
// Integer with optional range
|
|
1486
|
-
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1487
|
-
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1488
|
-
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1489
|
-
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1490
|
-
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1491
|
-
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1492
|
-
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1493
|
-
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1494
|
-
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1495
|
-
// Enum (specific allowed tokens)
|
|
1496
|
-
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1497
|
-
[
|
|
1498
|
-
"aria-current",
|
|
1499
|
-
{
|
|
1500
|
-
kind: "enum",
|
|
1501
|
-
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1502
|
-
}
|
|
1503
|
-
],
|
|
1504
|
-
[
|
|
1505
|
-
"aria-haspopup",
|
|
1506
|
-
{
|
|
1507
|
-
kind: "enum",
|
|
1508
|
-
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1509
|
-
}
|
|
1510
|
-
],
|
|
1511
|
-
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1512
|
-
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1513
|
-
[
|
|
1514
|
-
"aria-orientation",
|
|
1515
|
-
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1516
|
-
],
|
|
1517
|
-
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1518
|
-
]);
|
|
1519
1726
|
static #isValidAriaValue(value, type) {
|
|
1520
1727
|
switch (type.kind) {
|
|
1521
1728
|
case "boolean":
|
|
@@ -1560,11 +1767,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1560
1767
|
}
|
|
1561
1768
|
}
|
|
1562
1769
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1563
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1770
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1564
1771
|
const results = [];
|
|
1565
1772
|
iterate.forEachEntry(props, (key, value) => {
|
|
1566
1773
|
if (!key.startsWith("aria-")) return;
|
|
1567
|
-
const type =
|
|
1774
|
+
const type = ARIA_VALUE_TYPES.get(key);
|
|
1568
1775
|
if (!isNonNull(type)) return;
|
|
1569
1776
|
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1570
1777
|
results.push({
|
|
@@ -1583,26 +1790,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1583
1790
|
return results;
|
|
1584
1791
|
}
|
|
1585
1792
|
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1586
|
-
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1587
|
-
["h1", 1],
|
|
1588
|
-
["h2", 2],
|
|
1589
|
-
["h3", 3],
|
|
1590
|
-
["h4", 4],
|
|
1591
|
-
["h5", 5],
|
|
1592
|
-
["h6", 6]
|
|
1593
|
-
]);
|
|
1594
1793
|
static #checkRedundantAriaLevel({
|
|
1595
1794
|
tag,
|
|
1596
1795
|
props,
|
|
1597
1796
|
effectiveRole
|
|
1598
1797
|
}) {
|
|
1599
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1600
|
-
const implicitLevel =
|
|
1601
|
-
if (!isNonNull(implicitLevel)) return
|
|
1798
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1799
|
+
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1800
|
+
if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
|
|
1602
1801
|
const raw = props["aria-level"];
|
|
1603
|
-
if (!isNonNull(raw)) return
|
|
1802
|
+
if (!isNonNull(raw)) return NO_VIOLATIONS2;
|
|
1604
1803
|
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1605
|
-
if (!Number.isFinite(n) || n !== implicitLevel) return
|
|
1804
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
|
|
1606
1805
|
return [
|
|
1607
1806
|
{
|
|
1608
1807
|
valid: false,
|
|
@@ -1615,20 +1814,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1615
1814
|
];
|
|
1616
1815
|
}
|
|
1617
1816
|
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1618
|
-
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1619
|
-
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1620
|
-
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1621
|
-
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1622
|
-
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1623
1817
|
static #checkNameRequiredRoles({
|
|
1624
1818
|
tag,
|
|
1625
1819
|
props,
|
|
1626
1820
|
effectiveRole
|
|
1627
1821
|
}) {
|
|
1628
|
-
if (!effectiveRole || !
|
|
1629
|
-
|
|
1630
|
-
if ("
|
|
1631
|
-
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
|
|
1822
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
|
|
1823
|
+
if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
|
|
1824
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
|
|
1632
1825
|
return [
|
|
1633
1826
|
{
|
|
1634
1827
|
valid: false,
|
|
@@ -1638,51 +1831,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1638
1831
|
}
|
|
1639
1832
|
];
|
|
1640
1833
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1648
|
-
["spinbutton", ["aria-valuenow"]]
|
|
1649
|
-
]);
|
|
1650
|
-
static #checkRequiredAriaProperties({
|
|
1651
|
-
props,
|
|
1652
|
-
effectiveRole
|
|
1653
|
-
}) {
|
|
1654
|
-
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1655
|
-
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1656
|
-
if (!isNonNull(required)) return NO_VIOLATIONS;
|
|
1657
|
-
const results = [];
|
|
1658
|
-
iterate.forEach(required, (attr) => {
|
|
1659
|
-
if (attr in props) return;
|
|
1660
|
-
results.push({
|
|
1661
|
-
valid: false,
|
|
1662
|
-
fixable: false,
|
|
1663
|
-
severity: "warning",
|
|
1664
|
-
attribute: attr,
|
|
1665
|
-
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1666
|
-
});
|
|
1667
|
-
});
|
|
1668
|
-
return results;
|
|
1834
|
+
static #requiredAriaPropertiesRule = {
|
|
1835
|
+
attributesByRole: REQUIRED_ARIA_PROPERTIES,
|
|
1836
|
+
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
1837
|
+
};
|
|
1838
|
+
static #checkRequiredAriaProperties(context) {
|
|
1839
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
1669
1840
|
}
|
|
1670
|
-
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1671
|
-
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1672
|
-
"a",
|
|
1673
|
-
"button",
|
|
1674
|
-
"input",
|
|
1675
|
-
"select",
|
|
1676
|
-
"textarea"
|
|
1677
|
-
]);
|
|
1678
1841
|
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1679
1842
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1680
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
1681
|
-
const isInteractive =
|
|
1843
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
|
|
1844
|
+
const isInteractive = INTERACTIVE_TAGS.has(tag);
|
|
1682
1845
|
if (!isInteractive) {
|
|
1683
1846
|
const tabindex = props.tabindex;
|
|
1684
1847
|
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1685
|
-
if (!Number.isFinite(n) || n < 0) return
|
|
1848
|
+
if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
|
|
1686
1849
|
}
|
|
1687
1850
|
return [
|
|
1688
1851
|
{
|
|
@@ -1701,7 +1864,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1701
1864
|
props,
|
|
1702
1865
|
effectiveRole
|
|
1703
1866
|
}) {
|
|
1704
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
1867
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
|
|
1705
1868
|
const results = [];
|
|
1706
1869
|
iterate.forEachEntry(props, (key) => {
|
|
1707
1870
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1717,18 +1880,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1717
1880
|
});
|
|
1718
1881
|
return results;
|
|
1719
1882
|
}
|
|
1720
|
-
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1721
|
-
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1722
|
-
["alert", "assertive"],
|
|
1723
|
-
["status", "polite"],
|
|
1724
|
-
["log", "polite"],
|
|
1725
|
-
["timer", "off"]
|
|
1726
|
-
]);
|
|
1727
1883
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1728
|
-
if (!effectiveRole) return
|
|
1729
|
-
const impliedLive =
|
|
1730
|
-
if (!impliedLive) return
|
|
1731
|
-
if ("aria-live" in props) return
|
|
1884
|
+
if (!effectiveRole) return NO_VIOLATIONS2;
|
|
1885
|
+
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
1886
|
+
if (!impliedLive) return NO_VIOLATIONS2;
|
|
1887
|
+
if ("aria-live" in props) return NO_VIOLATIONS2;
|
|
1732
1888
|
const injectLive = {
|
|
1733
1889
|
kind: `injectLive:${effectiveRole}`,
|
|
1734
1890
|
apply: (ctx) => ({
|
|
@@ -1747,20 +1903,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1747
1903
|
}
|
|
1748
1904
|
];
|
|
1749
1905
|
}
|
|
1750
|
-
static #
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
valid: false,
|
|
1757
|
-
fixable: false,
|
|
1758
|
-
severity: "warning",
|
|
1759
|
-
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1760
|
-
}
|
|
1761
|
-
];
|
|
1906
|
+
static #missingAtomicRule = {
|
|
1907
|
+
attributesByRole: ATOMIC_REQUIREMENTS,
|
|
1908
|
+
diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
|
|
1909
|
+
};
|
|
1910
|
+
static #checkMissingAtomic(context) {
|
|
1911
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
|
|
1762
1912
|
}
|
|
1763
|
-
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1764
1913
|
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1765
1914
|
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1766
1915
|
// produce inconsistent results on cache hits.
|
|
@@ -1774,10 +1923,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1774
1923
|
};
|
|
1775
1924
|
static #checkInvalidAriaRelevant({ props }) {
|
|
1776
1925
|
const relevant = props["aria-relevant"];
|
|
1777
|
-
if (relevant === void 0) return
|
|
1778
|
-
if (typeof relevant !== "string") return
|
|
1926
|
+
if (relevant === void 0) return NO_VIOLATIONS2;
|
|
1927
|
+
if (typeof relevant !== "string") return NO_VIOLATIONS2;
|
|
1779
1928
|
const tokens = relevant.trim().split(/\s+/);
|
|
1780
|
-
const invalid = tokens.filter((t) => !
|
|
1929
|
+
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
1781
1930
|
if (invalid.length > 0) {
|
|
1782
1931
|
return [
|
|
1783
1932
|
{
|
|
@@ -1802,7 +1951,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1802
1951
|
}
|
|
1803
1952
|
];
|
|
1804
1953
|
}
|
|
1805
|
-
return
|
|
1954
|
+
return NO_VIOLATIONS2;
|
|
1806
1955
|
}
|
|
1807
1956
|
};
|
|
1808
1957
|
|
|
@@ -2122,6 +2271,425 @@ var readonlyProps = ({
|
|
|
2122
2271
|
// ../core/src/html/evaluators.ts
|
|
2123
2272
|
import { warnDiagnostics as warnDiagnostics2 } from "./_shared/diagnostics.js";
|
|
2124
2273
|
|
|
2274
|
+
// ../core/src/html/spec/vocabulary/input.ts
|
|
2275
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2276
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2277
|
+
"number",
|
|
2278
|
+
"range",
|
|
2279
|
+
"date",
|
|
2280
|
+
"month",
|
|
2281
|
+
"week",
|
|
2282
|
+
"time",
|
|
2283
|
+
"datetime-local"
|
|
2284
|
+
];
|
|
2285
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2286
|
+
...TEXT_INPUT_TYPES,
|
|
2287
|
+
...NUMERIC_INPUT_TYPES,
|
|
2288
|
+
"checkbox",
|
|
2289
|
+
"radio",
|
|
2290
|
+
"file",
|
|
2291
|
+
"color",
|
|
2292
|
+
"hidden",
|
|
2293
|
+
"button",
|
|
2294
|
+
"submit",
|
|
2295
|
+
"reset",
|
|
2296
|
+
"image"
|
|
2297
|
+
]);
|
|
2298
|
+
|
|
2299
|
+
// ../core/src/html/spec/attributes/input.ts
|
|
2300
|
+
var INPUT_ATTRIBUTE_TYPE_POLICIES = [
|
|
2301
|
+
{ attribute: "checked", allowedTypes: ["checkbox", "radio"] },
|
|
2302
|
+
{ attribute: "multiple", allowedTypes: ["email", "file"] },
|
|
2303
|
+
{ attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2304
|
+
{ attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2305
|
+
{ attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
|
|
2306
|
+
{ attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2307
|
+
{ attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2308
|
+
{ attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2309
|
+
{ attribute: "accept", allowedTypes: ["file"] },
|
|
2310
|
+
{ attribute: "capture", allowedTypes: ["file"] },
|
|
2311
|
+
{ attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
|
|
2312
|
+
{ attribute: "alt", allowedTypes: ["image"] },
|
|
2313
|
+
{ attribute: "height", allowedTypes: ["image"] },
|
|
2314
|
+
{ attribute: "width", allowedTypes: ["image"] }
|
|
2315
|
+
];
|
|
2316
|
+
|
|
2317
|
+
// ../core/src/html/spec/constraints/input.ts
|
|
2318
|
+
var REQUIRED_READONLY_CONFLICT = {
|
|
2319
|
+
props: ["required", "readOnly"],
|
|
2320
|
+
diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
|
|
2321
|
+
};
|
|
2322
|
+
var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
|
|
2323
|
+
REQUIRED_READONLY_CONFLICT
|
|
2324
|
+
];
|
|
2325
|
+
|
|
2326
|
+
// ../core/src/html/spec/validators/attribute-type-validator.ts
|
|
2327
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2328
|
+
function omit(props, key) {
|
|
2329
|
+
const next = { ...props };
|
|
2330
|
+
delete next[key];
|
|
2331
|
+
return next;
|
|
2332
|
+
}
|
|
2333
|
+
function removeAttributeFix(attribute) {
|
|
2334
|
+
return {
|
|
2335
|
+
kind: `removeAttribute:${attribute}`,
|
|
2336
|
+
apply: ({ props }) => {
|
|
2337
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2338
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function createInputAttributeTypeRule({
|
|
2343
|
+
attribute,
|
|
2344
|
+
allowedTypes
|
|
2345
|
+
}) {
|
|
2346
|
+
const rule = ({ tag, props }) => {
|
|
2347
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2348
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2349
|
+
if (allowedTypes.includes(type)) return [];
|
|
2350
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2351
|
+
return [
|
|
2352
|
+
{
|
|
2353
|
+
valid: false,
|
|
2354
|
+
fixable: true,
|
|
2355
|
+
severity: diagnostic.severity,
|
|
2356
|
+
fix: removeAttributeFix(attribute),
|
|
2357
|
+
diagnostic
|
|
2358
|
+
}
|
|
2359
|
+
];
|
|
2360
|
+
};
|
|
2361
|
+
return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
// ../core/src/html/spec/validators/mutually-exclusive-validator.ts
|
|
2365
|
+
function createMutuallyExclusiveRule({
|
|
2366
|
+
props: conflictingProps,
|
|
2367
|
+
diagnostic: createDiagnostic
|
|
2368
|
+
}) {
|
|
2369
|
+
const [first, second] = conflictingProps;
|
|
2370
|
+
const rule = ({ tag, props }) => {
|
|
2371
|
+
if (tag !== "input" || !props[first] || !props[second]) return [];
|
|
2372
|
+
const diagnostic = createDiagnostic();
|
|
2373
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2374
|
+
};
|
|
2375
|
+
return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
// ../core/src/html/input-rules.ts
|
|
2379
|
+
var policyByAttribute = Object.fromEntries(
|
|
2380
|
+
INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
|
|
2381
|
+
);
|
|
2382
|
+
function policyFor(attribute) {
|
|
2383
|
+
return policyByAttribute[attribute];
|
|
2384
|
+
}
|
|
2385
|
+
var supportedInputTypeRule = Object.assign(
|
|
2386
|
+
({ tag, props }) => {
|
|
2387
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2388
|
+
const type = props.type;
|
|
2389
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2390
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2391
|
+
return [
|
|
2392
|
+
{
|
|
2393
|
+
valid: false,
|
|
2394
|
+
fixable: false,
|
|
2395
|
+
severity: diagnostic.severity,
|
|
2396
|
+
diagnostic
|
|
2397
|
+
}
|
|
2398
|
+
];
|
|
2399
|
+
},
|
|
2400
|
+
{ readsProps: ["type"], tags: ["input"] }
|
|
2401
|
+
);
|
|
2402
|
+
var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
|
|
2403
|
+
var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
|
|
2404
|
+
var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
|
|
2405
|
+
var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
|
|
2406
|
+
var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
|
|
2407
|
+
var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
|
|
2408
|
+
var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
|
|
2409
|
+
var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
|
|
2410
|
+
var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
|
|
2411
|
+
var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
|
|
2412
|
+
var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
|
|
2413
|
+
var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
|
|
2414
|
+
var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
|
|
2415
|
+
var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
|
|
2416
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2417
|
+
({ tag, props }) => {
|
|
2418
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2419
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2420
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2421
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2422
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2423
|
+
},
|
|
2424
|
+
{
|
|
2425
|
+
readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
|
|
2426
|
+
tags: ["input"]
|
|
2427
|
+
}
|
|
2428
|
+
);
|
|
2429
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2430
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2431
|
+
({ tag, props }) => {
|
|
2432
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2433
|
+
const autoComplete = props.autoComplete;
|
|
2434
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2435
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2436
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2437
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2438
|
+
},
|
|
2439
|
+
{ readsProps: ["type", "autoComplete"], tags: ["input"] }
|
|
2440
|
+
);
|
|
2441
|
+
var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
|
|
2442
|
+
var INPUT_RULES = [
|
|
2443
|
+
supportedInputTypeRule,
|
|
2444
|
+
checkedRequiresCheckableTypeRule,
|
|
2445
|
+
multipleRequiresSupportedTypeRule,
|
|
2446
|
+
maxLengthRequiresTextTypeRule,
|
|
2447
|
+
minLengthRequiresTextTypeRule,
|
|
2448
|
+
patternRequiresTextTypeRule,
|
|
2449
|
+
minRequiresNumericTypeRule,
|
|
2450
|
+
maxRequiresNumericTypeRule,
|
|
2451
|
+
stepRequiresNumericTypeRule,
|
|
2452
|
+
acceptRequiresFileTypeRule,
|
|
2453
|
+
captureRequiresFileTypeRule,
|
|
2454
|
+
sizeRequiresTextTypeRule,
|
|
2455
|
+
altRequiresImageTypeRule,
|
|
2456
|
+
heightRequiresImageTypeRule,
|
|
2457
|
+
widthRequiresImageTypeRule,
|
|
2458
|
+
inputAccessibleNameRule,
|
|
2459
|
+
passwordAutocompleteRule,
|
|
2460
|
+
requiredReadOnlyConflictRule
|
|
2461
|
+
];
|
|
2462
|
+
|
|
2463
|
+
// ../core/src/html/spec/types.ts
|
|
2464
|
+
function definePropRolePolicy(prop, map2, fallback) {
|
|
2465
|
+
return { kind: "byProp", prop, map: map2, fallback };
|
|
2466
|
+
}
|
|
2467
|
+
function resolveAllowedRoles(spec, props) {
|
|
2468
|
+
const policy = spec.allowedRoles;
|
|
2469
|
+
if (!policy) return void 0;
|
|
2470
|
+
switch (policy.kind) {
|
|
2471
|
+
case "fixed":
|
|
2472
|
+
return policy.roles;
|
|
2473
|
+
case "byProp": {
|
|
2474
|
+
const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
|
|
2475
|
+
return policy.map[value];
|
|
2476
|
+
}
|
|
2477
|
+
case "dynamic":
|
|
2478
|
+
return policy.resolve({ props });
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
// ../core/src/html/spec/roles/input.ts
|
|
2483
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2484
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2485
|
+
radio: ["menuitemradio"],
|
|
2486
|
+
range: [],
|
|
2487
|
+
number: [],
|
|
2488
|
+
search: ["combobox"],
|
|
2489
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2490
|
+
email: ["combobox"],
|
|
2491
|
+
tel: ["combobox"],
|
|
2492
|
+
url: ["combobox"],
|
|
2493
|
+
button: [
|
|
2494
|
+
"link",
|
|
2495
|
+
"menuitem",
|
|
2496
|
+
"menuitemcheckbox",
|
|
2497
|
+
"menuitemradio",
|
|
2498
|
+
"option",
|
|
2499
|
+
"radio",
|
|
2500
|
+
"switch",
|
|
2501
|
+
"tab"
|
|
2502
|
+
],
|
|
2503
|
+
submit: [
|
|
2504
|
+
"link",
|
|
2505
|
+
"menuitem",
|
|
2506
|
+
"menuitemcheckbox",
|
|
2507
|
+
"menuitemradio",
|
|
2508
|
+
"option",
|
|
2509
|
+
"radio",
|
|
2510
|
+
"switch",
|
|
2511
|
+
"tab"
|
|
2512
|
+
],
|
|
2513
|
+
reset: [
|
|
2514
|
+
"link",
|
|
2515
|
+
"menuitem",
|
|
2516
|
+
"menuitemcheckbox",
|
|
2517
|
+
"menuitemradio",
|
|
2518
|
+
"option",
|
|
2519
|
+
"radio",
|
|
2520
|
+
"switch",
|
|
2521
|
+
"tab"
|
|
2522
|
+
],
|
|
2523
|
+
image: [
|
|
2524
|
+
"link",
|
|
2525
|
+
"menuitem",
|
|
2526
|
+
"menuitemcheckbox",
|
|
2527
|
+
"menuitemradio",
|
|
2528
|
+
"option",
|
|
2529
|
+
"radio",
|
|
2530
|
+
"switch",
|
|
2531
|
+
"tab"
|
|
2532
|
+
],
|
|
2533
|
+
hidden: []
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
// ../core/src/html/spec/elements/input.ts
|
|
2537
|
+
var inputElementSpec = {
|
|
2538
|
+
tag: "input",
|
|
2539
|
+
allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
|
|
2540
|
+
attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
|
|
2541
|
+
mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
// ../core/src/html/spec/roles/img.ts
|
|
2545
|
+
var IMG_NAMED_ROLES = [
|
|
2546
|
+
"button",
|
|
2547
|
+
"checkbox",
|
|
2548
|
+
"link",
|
|
2549
|
+
"menuitem",
|
|
2550
|
+
"menuitemcheckbox",
|
|
2551
|
+
"menuitemradio",
|
|
2552
|
+
"option",
|
|
2553
|
+
"progressbar",
|
|
2554
|
+
"scrollbar",
|
|
2555
|
+
"separator",
|
|
2556
|
+
"slider",
|
|
2557
|
+
"switch",
|
|
2558
|
+
"tab",
|
|
2559
|
+
"treeitem"
|
|
2560
|
+
];
|
|
2561
|
+
|
|
2562
|
+
// ../core/src/html/spec/elements/img.ts
|
|
2563
|
+
var imgElementSpec = {
|
|
2564
|
+
tag: "img",
|
|
2565
|
+
allowedRoles: {
|
|
2566
|
+
kind: "dynamic",
|
|
2567
|
+
resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
|
|
2568
|
+
}
|
|
2569
|
+
};
|
|
2570
|
+
|
|
2571
|
+
// ../core/src/html/spec/roles/table.ts
|
|
2572
|
+
var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
|
|
2573
|
+
|
|
2574
|
+
// ../core/src/html/spec/elements/table.ts
|
|
2575
|
+
var tableElementSpec = {
|
|
2576
|
+
tag: "table",
|
|
2577
|
+
allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
|
|
2578
|
+
};
|
|
2579
|
+
|
|
2580
|
+
// ../core/src/html/role-restrictions.ts
|
|
2581
|
+
var ALLOWED_ROLES = {
|
|
2582
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2583
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2584
|
+
footer: ["group", "none", "presentation"],
|
|
2585
|
+
header: ["group", "none", "presentation"],
|
|
2586
|
+
main: [],
|
|
2587
|
+
nav: [],
|
|
2588
|
+
a: [
|
|
2589
|
+
"button",
|
|
2590
|
+
"checkbox",
|
|
2591
|
+
"menuitem",
|
|
2592
|
+
"menuitemcheckbox",
|
|
2593
|
+
"menuitemradio",
|
|
2594
|
+
"option",
|
|
2595
|
+
"radio",
|
|
2596
|
+
"switch",
|
|
2597
|
+
"tab",
|
|
2598
|
+
"treeitem"
|
|
2599
|
+
],
|
|
2600
|
+
button: [
|
|
2601
|
+
"checkbox",
|
|
2602
|
+
"link",
|
|
2603
|
+
"menuitem",
|
|
2604
|
+
"menuitemcheckbox",
|
|
2605
|
+
"menuitemradio",
|
|
2606
|
+
"option",
|
|
2607
|
+
"radio",
|
|
2608
|
+
"switch",
|
|
2609
|
+
"tab"
|
|
2610
|
+
],
|
|
2611
|
+
select: ["menu"],
|
|
2612
|
+
h1: ["tab", "presentation", "none"],
|
|
2613
|
+
h2: ["tab", "presentation", "none"],
|
|
2614
|
+
h3: ["tab", "presentation", "none"],
|
|
2615
|
+
h4: ["tab", "presentation", "none"],
|
|
2616
|
+
h5: ["tab", "presentation", "none"],
|
|
2617
|
+
h6: ["tab", "presentation", "none"],
|
|
2618
|
+
ul: [
|
|
2619
|
+
"directory",
|
|
2620
|
+
"group",
|
|
2621
|
+
"listbox",
|
|
2622
|
+
"menu",
|
|
2623
|
+
"menubar",
|
|
2624
|
+
"radiogroup",
|
|
2625
|
+
"tablist",
|
|
2626
|
+
"toolbar",
|
|
2627
|
+
"tree"
|
|
2628
|
+
],
|
|
2629
|
+
ol: [
|
|
2630
|
+
"directory",
|
|
2631
|
+
"group",
|
|
2632
|
+
"listbox",
|
|
2633
|
+
"menu",
|
|
2634
|
+
"menubar",
|
|
2635
|
+
"radiogroup",
|
|
2636
|
+
"tablist",
|
|
2637
|
+
"toolbar",
|
|
2638
|
+
"tree"
|
|
2639
|
+
],
|
|
2640
|
+
li: [
|
|
2641
|
+
"menuitem",
|
|
2642
|
+
"menuitemcheckbox",
|
|
2643
|
+
"menuitemradio",
|
|
2644
|
+
"option",
|
|
2645
|
+
"none",
|
|
2646
|
+
"presentation",
|
|
2647
|
+
"radio",
|
|
2648
|
+
"separator",
|
|
2649
|
+
"tab",
|
|
2650
|
+
"treeitem"
|
|
2651
|
+
],
|
|
2652
|
+
dialog: ["alertdialog"],
|
|
2653
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2654
|
+
};
|
|
2655
|
+
var ELEMENT_SPECS = {
|
|
2656
|
+
input: inputElementSpec,
|
|
2657
|
+
img: imgElementSpec,
|
|
2658
|
+
table: tableElementSpec
|
|
2659
|
+
};
|
|
2660
|
+
function getAllowedRoles(tag, props) {
|
|
2661
|
+
const spec = ELEMENT_SPECS[tag];
|
|
2662
|
+
if (spec) return resolveAllowedRoles(spec, props);
|
|
2663
|
+
return ALLOWED_ROLES[tag];
|
|
2664
|
+
}
|
|
2665
|
+
var removeRoleFix = {
|
|
2666
|
+
kind: "removeRole",
|
|
2667
|
+
apply: ({ props }) => {
|
|
2668
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2669
|
+
const { role: _role, ...rest } = props;
|
|
2670
|
+
return { applied: true, next: rest, previous: props };
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2673
|
+
var roleNotPermittedRule = Object.assign(
|
|
2674
|
+
({ tag, props, implicitRole }) => {
|
|
2675
|
+
const role = props.role;
|
|
2676
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2677
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2678
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2679
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2680
|
+
return [
|
|
2681
|
+
{
|
|
2682
|
+
valid: false,
|
|
2683
|
+
fixable: true,
|
|
2684
|
+
severity: diagnostic.severity,
|
|
2685
|
+
fix: removeRoleFix,
|
|
2686
|
+
diagnostic
|
|
2687
|
+
}
|
|
2688
|
+
];
|
|
2689
|
+
},
|
|
2690
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2691
|
+
);
|
|
2692
|
+
|
|
2125
2693
|
// ../core/src/html/aria-rules.ts
|
|
2126
2694
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
2127
2695
|
var removeLandmarkRoleOverride = {
|
|
@@ -2132,20 +2700,24 @@ var removeLandmarkRoleOverride = {
|
|
|
2132
2700
|
return { applied: true, next: rest, previous: props };
|
|
2133
2701
|
}
|
|
2134
2702
|
};
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
}
|
|
2703
|
+
var landmarkRoleRule = Object.assign(
|
|
2704
|
+
({ tag, props, implicitRole }) => {
|
|
2705
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2706
|
+
const role = props.role;
|
|
2707
|
+
if (!role || role === implicitRole) return [];
|
|
2708
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2709
|
+
return [
|
|
2710
|
+
{
|
|
2711
|
+
valid: false,
|
|
2712
|
+
fixable: true,
|
|
2713
|
+
severity: diagnostic.severity,
|
|
2714
|
+
fix: removeLandmarkRoleOverride,
|
|
2715
|
+
diagnostic
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
},
|
|
2719
|
+
{ tags: [...LANDMARK_TAG_SET] }
|
|
2720
|
+
);
|
|
2149
2721
|
function requireAccessibleName({ tag, props }) {
|
|
2150
2722
|
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2151
2723
|
return [
|
|
@@ -2158,11 +2730,19 @@ function requireAccessibleName({ tag, props }) {
|
|
|
2158
2730
|
];
|
|
2159
2731
|
}
|
|
2160
2732
|
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2733
|
+
var landmarkNameAdvisory = Object.assign(
|
|
2734
|
+
(ctx) => {
|
|
2735
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2736
|
+
return requireAccessibleName(ctx);
|
|
2737
|
+
},
|
|
2738
|
+
{ tags: [...NAMED_LANDMARK_TAGS] }
|
|
2739
|
+
);
|
|
2740
|
+
var HTML_ARIA_RULES = [
|
|
2741
|
+
landmarkRoleRule,
|
|
2742
|
+
landmarkNameAdvisory,
|
|
2743
|
+
roleNotPermittedRule,
|
|
2744
|
+
...INPUT_RULES
|
|
2745
|
+
];
|
|
2166
2746
|
|
|
2167
2747
|
// ../core/src/html/contracts.ts
|
|
2168
2748
|
import { warnDiagnostics } from "./_shared/diagnostics.js";
|
|
@@ -2257,6 +2837,62 @@ var figureContract = contract([
|
|
|
2257
2837
|
]);
|
|
2258
2838
|
var detailsContract = firstChildContract("summary", "summary");
|
|
2259
2839
|
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2840
|
+
var objectContract = contract([
|
|
2841
|
+
{ name: "param", match: isTag("param") },
|
|
2842
|
+
{ name: "content", match: isOpenContent("param") }
|
|
2843
|
+
]);
|
|
2844
|
+
var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
|
|
2845
|
+
var buttonContract = closedContract([
|
|
2846
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2847
|
+
]);
|
|
2848
|
+
var anchorContract = closedContract([
|
|
2849
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2850
|
+
]);
|
|
2851
|
+
var LABELABLE_TAGS = [
|
|
2852
|
+
"button",
|
|
2853
|
+
"input",
|
|
2854
|
+
"meter",
|
|
2855
|
+
"output",
|
|
2856
|
+
"progress",
|
|
2857
|
+
"select",
|
|
2858
|
+
"textarea"
|
|
2859
|
+
];
|
|
2860
|
+
var labelContract = contract([
|
|
2861
|
+
{ name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
|
|
2862
|
+
]);
|
|
2863
|
+
var P_BLOCKED_TAGS = [
|
|
2864
|
+
"address",
|
|
2865
|
+
"article",
|
|
2866
|
+
"aside",
|
|
2867
|
+
"blockquote",
|
|
2868
|
+
"details",
|
|
2869
|
+
"dialog",
|
|
2870
|
+
"div",
|
|
2871
|
+
"dl",
|
|
2872
|
+
"fieldset",
|
|
2873
|
+
"figure",
|
|
2874
|
+
"footer",
|
|
2875
|
+
"form",
|
|
2876
|
+
"h1",
|
|
2877
|
+
"h2",
|
|
2878
|
+
"h3",
|
|
2879
|
+
"h4",
|
|
2880
|
+
"h5",
|
|
2881
|
+
"h6",
|
|
2882
|
+
"header",
|
|
2883
|
+
"hr",
|
|
2884
|
+
"main",
|
|
2885
|
+
"nav",
|
|
2886
|
+
"ol",
|
|
2887
|
+
"p",
|
|
2888
|
+
"pre",
|
|
2889
|
+
"section",
|
|
2890
|
+
"table",
|
|
2891
|
+
"ul"
|
|
2892
|
+
];
|
|
2893
|
+
var pContract = closedContract([
|
|
2894
|
+
{ name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
|
|
2895
|
+
]);
|
|
2260
2896
|
var mediaContract = contract([
|
|
2261
2897
|
{ name: "source", match: isTag("source") },
|
|
2262
2898
|
{ name: "track", match: isTag("track") },
|
|
@@ -2311,6 +2947,11 @@ var htmlContracts = {
|
|
|
2311
2947
|
details: detailsContract,
|
|
2312
2948
|
fieldset: fieldsetContract,
|
|
2313
2949
|
dialog: dialogContract,
|
|
2950
|
+
object: objectContract,
|
|
2951
|
+
button: buttonContract,
|
|
2952
|
+
a: anchorContract,
|
|
2953
|
+
label: labelContract,
|
|
2954
|
+
p: pContract,
|
|
2314
2955
|
head: headContract,
|
|
2315
2956
|
html: htmlContract
|
|
2316
2957
|
};
|
|
@@ -2623,21 +3264,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2623
3264
|
import { throwDiagnostics } from "./_shared/diagnostics.js";
|
|
2624
3265
|
|
|
2625
3266
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2626
|
-
import { DiagnosticCategory as
|
|
3267
|
+
import { DiagnosticCategory as DiagnosticCategory6, DiagnosticCode as DiagnosticCode6 } from "./_shared/diagnostics.js";
|
|
2627
3268
|
var PluginDiagnostics = {
|
|
2628
3269
|
invalidShape(received) {
|
|
2629
3270
|
const got = received === null ? "null" : typeof received;
|
|
2630
3271
|
return {
|
|
2631
|
-
code:
|
|
2632
|
-
category:
|
|
3272
|
+
code: DiagnosticCode6.PluginInvalidShape,
|
|
3273
|
+
category: DiagnosticCategory6.Internal,
|
|
2633
3274
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2634
3275
|
};
|
|
2635
3276
|
},
|
|
2636
3277
|
pipelineReturnType(received) {
|
|
2637
3278
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2638
3279
|
return {
|
|
2639
|
-
code:
|
|
2640
|
-
category:
|
|
3280
|
+
code: DiagnosticCode6.PluginPipelineReturnType,
|
|
3281
|
+
category: DiagnosticCategory6.Internal,
|
|
2641
3282
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2642
3283
|
};
|
|
2643
3284
|
}
|