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