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/lit/index.js
CHANGED
|
@@ -858,11 +858,28 @@ var ContractDiagnostics = {
|
|
|
858
858
|
|
|
859
859
|
// ../../lib/contract/src/diagnostics/html.ts
|
|
860
860
|
import { DiagnosticCategory as DiagnosticCategory3, DiagnosticCode as DiagnosticCode3 } from "../_shared/diagnostics.js";
|
|
861
|
+
var ATTRIBUTE_IGNORED_CODES = {
|
|
862
|
+
checked: DiagnosticCode3.HtmlInputCheckedIgnoredForType,
|
|
863
|
+
multiple: DiagnosticCode3.HtmlInputMultipleIgnoredForType,
|
|
864
|
+
maxLength: DiagnosticCode3.HtmlInputMaxLengthIgnoredForType,
|
|
865
|
+
minLength: DiagnosticCode3.HtmlInputMinLengthIgnoredForType,
|
|
866
|
+
pattern: DiagnosticCode3.HtmlInputPatternIgnoredForType,
|
|
867
|
+
min: DiagnosticCode3.HtmlInputMinIgnoredForType,
|
|
868
|
+
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
869
|
+
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
870
|
+
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
871
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
|
|
872
|
+
size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
|
|
873
|
+
alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
|
|
874
|
+
height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
|
|
875
|
+
width: DiagnosticCode3.HtmlInputWidthIgnoredForType
|
|
876
|
+
};
|
|
861
877
|
var HtmlDiagnostics = {
|
|
862
878
|
emptyRole(tag) {
|
|
863
879
|
return {
|
|
864
880
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
865
881
|
category: DiagnosticCategory3.HTML,
|
|
882
|
+
severity: "warning",
|
|
866
883
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
867
884
|
};
|
|
868
885
|
},
|
|
@@ -870,6 +887,7 @@ var HtmlDiagnostics = {
|
|
|
870
887
|
return {
|
|
871
888
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
872
889
|
category: DiagnosticCategory3.HTML,
|
|
890
|
+
severity: "warning",
|
|
873
891
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
874
892
|
};
|
|
875
893
|
},
|
|
@@ -877,6 +895,7 @@ var HtmlDiagnostics = {
|
|
|
877
895
|
return {
|
|
878
896
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
879
897
|
category: DiagnosticCategory3.HTML,
|
|
898
|
+
severity: "error",
|
|
880
899
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
881
900
|
};
|
|
882
901
|
},
|
|
@@ -884,6 +903,7 @@ var HtmlDiagnostics = {
|
|
|
884
903
|
return {
|
|
885
904
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
886
905
|
category: DiagnosticCategory3.HTML,
|
|
906
|
+
severity: "error",
|
|
887
907
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
888
908
|
};
|
|
889
909
|
},
|
|
@@ -891,6 +911,7 @@ var HtmlDiagnostics = {
|
|
|
891
911
|
return {
|
|
892
912
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
893
913
|
category: DiagnosticCategory3.HTML,
|
|
914
|
+
severity: "error",
|
|
894
915
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
895
916
|
};
|
|
896
917
|
},
|
|
@@ -898,8 +919,122 @@ var HtmlDiagnostics = {
|
|
|
898
919
|
return {
|
|
899
920
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
900
921
|
category: DiagnosticCategory3.HTML,
|
|
922
|
+
severity: "error",
|
|
901
923
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
902
924
|
};
|
|
925
|
+
},
|
|
926
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
927
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
928
|
+
return {
|
|
929
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
930
|
+
category: DiagnosticCategory3.HTML,
|
|
931
|
+
severity: "error",
|
|
932
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
933
|
+
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.'
|
|
934
|
+
};
|
|
935
|
+
},
|
|
936
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
937
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
938
|
+
input: {
|
|
939
|
+
unsupportedType(type) {
|
|
940
|
+
return {
|
|
941
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
942
|
+
category: DiagnosticCategory3.HTML,
|
|
943
|
+
severity: "warning",
|
|
944
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
945
|
+
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).',
|
|
946
|
+
suggestions: [
|
|
947
|
+
{
|
|
948
|
+
title: "Check for a typo in the type value",
|
|
949
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
950
|
+
}
|
|
951
|
+
]
|
|
952
|
+
};
|
|
953
|
+
},
|
|
954
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
955
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
956
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
957
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
958
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
959
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
960
|
+
return {
|
|
961
|
+
code,
|
|
962
|
+
category: DiagnosticCategory3.HTML,
|
|
963
|
+
severity: "warning",
|
|
964
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
965
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
966
|
+
suggestions: [
|
|
967
|
+
{
|
|
968
|
+
title: `Remove "${attribute}"`,
|
|
969
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
970
|
+
}
|
|
971
|
+
]
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
978
|
+
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "../_shared/diagnostics.js";
|
|
979
|
+
function accessibilityFact(input) {
|
|
980
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
981
|
+
}
|
|
982
|
+
var InputAccessibilityDiagnostics = {
|
|
983
|
+
missingAccessibleName() {
|
|
984
|
+
return accessibilityFact({
|
|
985
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
986
|
+
severity: "warning",
|
|
987
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
988
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
989
|
+
suggestions: [
|
|
990
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
991
|
+
{
|
|
992
|
+
title: "Add an associated <label>",
|
|
993
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
994
|
+
}
|
|
995
|
+
]
|
|
996
|
+
});
|
|
997
|
+
},
|
|
998
|
+
placeholderIsNotLabel() {
|
|
999
|
+
return accessibilityFact({
|
|
1000
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
1001
|
+
severity: "warning",
|
|
1002
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1003
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1004
|
+
suggestions: [
|
|
1005
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1006
|
+
{
|
|
1007
|
+
title: "Add an associated <label>",
|
|
1008
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1009
|
+
}
|
|
1010
|
+
]
|
|
1011
|
+
});
|
|
1012
|
+
},
|
|
1013
|
+
passwordMissingAutocomplete() {
|
|
1014
|
+
return accessibilityFact({
|
|
1015
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1016
|
+
severity: "warning",
|
|
1017
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1018
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1019
|
+
suggestions: [
|
|
1020
|
+
{
|
|
1021
|
+
title: 'Set autoComplete="current-password"',
|
|
1022
|
+
description: "Use this for sign-in forms."
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
title: 'Set autoComplete="new-password"',
|
|
1026
|
+
description: "Use this for sign-up / change-password forms."
|
|
1027
|
+
}
|
|
1028
|
+
]
|
|
1029
|
+
});
|
|
1030
|
+
},
|
|
1031
|
+
requiredReadOnlyConflict() {
|
|
1032
|
+
return accessibilityFact({
|
|
1033
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1034
|
+
severity: "warning",
|
|
1035
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1036
|
+
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."
|
|
1037
|
+
});
|
|
903
1038
|
}
|
|
904
1039
|
};
|
|
905
1040
|
|
|
@@ -925,8 +1060,132 @@ var InvariantBase = class {
|
|
|
925
1060
|
}
|
|
926
1061
|
};
|
|
927
1062
|
|
|
928
|
-
// ../../lib/contract/src/aria/
|
|
1063
|
+
// ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1064
|
+
var REQUIRED_ARIA_PROPERTIES = {
|
|
1065
|
+
combobox: ["aria-expanded"],
|
|
1066
|
+
option: ["aria-selected"],
|
|
1067
|
+
slider: ["aria-valuenow"],
|
|
1068
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1069
|
+
spinbutton: ["aria-valuenow"]
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
// ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1073
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1074
|
+
|
|
1075
|
+
// ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
929
1076
|
var NO_VIOLATIONS = [{ valid: true }];
|
|
1077
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1078
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1079
|
+
}
|
|
1080
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1081
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1082
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1083
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1084
|
+
const results = [];
|
|
1085
|
+
for (const attribute of requiredAttributes) {
|
|
1086
|
+
if (attribute in props) continue;
|
|
1087
|
+
results.push({
|
|
1088
|
+
valid: false,
|
|
1089
|
+
fixable: false,
|
|
1090
|
+
severity: "warning",
|
|
1091
|
+
attribute,
|
|
1092
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
return results;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1099
|
+
var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1100
|
+
["alert", "assertive"],
|
|
1101
|
+
["status", "polite"],
|
|
1102
|
+
["log", "polite"],
|
|
1103
|
+
["timer", "off"]
|
|
1104
|
+
]);
|
|
1105
|
+
var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1106
|
+
|
|
1107
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1108
|
+
var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1109
|
+
// Boolean (true | false)
|
|
1110
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1111
|
+
["aria-busy", { kind: "boolean" }],
|
|
1112
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1113
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1114
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1115
|
+
["aria-modal", { kind: "boolean" }],
|
|
1116
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1117
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1118
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1119
|
+
["aria-required", { kind: "boolean" }],
|
|
1120
|
+
["aria-selected", { kind: "boolean" }],
|
|
1121
|
+
// Tristate (true | false | mixed)
|
|
1122
|
+
["aria-checked", { kind: "tristate" }],
|
|
1123
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1124
|
+
// Numeric (any finite number)
|
|
1125
|
+
["aria-valuenow", { kind: "number" }],
|
|
1126
|
+
["aria-valuemin", { kind: "number" }],
|
|
1127
|
+
["aria-valuemax", { kind: "number" }],
|
|
1128
|
+
// Integer with optional range
|
|
1129
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1130
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1131
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1132
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1133
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1134
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1135
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1136
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1137
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1138
|
+
// Enum (specific allowed tokens)
|
|
1139
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1140
|
+
[
|
|
1141
|
+
"aria-current",
|
|
1142
|
+
{
|
|
1143
|
+
kind: "enum",
|
|
1144
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1145
|
+
}
|
|
1146
|
+
],
|
|
1147
|
+
[
|
|
1148
|
+
"aria-haspopup",
|
|
1149
|
+
{
|
|
1150
|
+
kind: "enum",
|
|
1151
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1152
|
+
}
|
|
1153
|
+
],
|
|
1154
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1155
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1156
|
+
["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
|
|
1157
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1158
|
+
]);
|
|
1159
|
+
|
|
1160
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1161
|
+
var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1162
|
+
"additions",
|
|
1163
|
+
"removals",
|
|
1164
|
+
"text",
|
|
1165
|
+
"all"
|
|
1166
|
+
]);
|
|
1167
|
+
|
|
1168
|
+
// ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1169
|
+
var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1170
|
+
["h1", 1],
|
|
1171
|
+
["h2", 2],
|
|
1172
|
+
["h3", 3],
|
|
1173
|
+
["h4", 4],
|
|
1174
|
+
["h5", 5],
|
|
1175
|
+
["h6", 6]
|
|
1176
|
+
]);
|
|
1177
|
+
|
|
1178
|
+
// ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
|
|
1179
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1180
|
+
"a",
|
|
1181
|
+
"button",
|
|
1182
|
+
"input",
|
|
1183
|
+
"select",
|
|
1184
|
+
"textarea"
|
|
1185
|
+
]);
|
|
1186
|
+
|
|
1187
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1188
|
+
var NO_VIOLATIONS2 = [{ valid: true }];
|
|
930
1189
|
function isIntrinsicTag(tag) {
|
|
931
1190
|
return isString(tag);
|
|
932
1191
|
}
|
|
@@ -958,7 +1217,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
958
1217
|
tag,
|
|
959
1218
|
role: "",
|
|
960
1219
|
attribute: void 0,
|
|
961
|
-
severity:
|
|
1220
|
+
severity: d.severity,
|
|
962
1221
|
phase: "evaluate"
|
|
963
1222
|
}
|
|
964
1223
|
]
|
|
@@ -988,6 +1247,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
988
1247
|
const violations = [];
|
|
989
1248
|
const fixes = [];
|
|
990
1249
|
iterate.forEach(rules, (rule) => {
|
|
1250
|
+
if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
|
|
991
1251
|
iterate.forEach(rule(context), (result) => {
|
|
992
1252
|
if (result.valid) return;
|
|
993
1253
|
const {
|
|
@@ -1013,7 +1273,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1013
1273
|
return { violations, fixes };
|
|
1014
1274
|
}
|
|
1015
1275
|
static #getRules(context) {
|
|
1016
|
-
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) &&
|
|
1276
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1017
1277
|
return _AriaPolicyEngine.#pipeline;
|
|
1018
1278
|
}
|
|
1019
1279
|
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
@@ -1215,44 +1475,47 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1215
1475
|
implicitRole
|
|
1216
1476
|
}) {
|
|
1217
1477
|
const role = props.role;
|
|
1218
|
-
if (!implicitRole || !role || role === implicitRole) return
|
|
1478
|
+
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
|
|
1219
1479
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1480
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1220
1481
|
return [
|
|
1221
1482
|
{
|
|
1222
1483
|
valid: false,
|
|
1223
1484
|
fixable: true,
|
|
1224
|
-
severity:
|
|
1485
|
+
severity: diagnostic.severity,
|
|
1225
1486
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1226
|
-
diagnostic
|
|
1487
|
+
diagnostic
|
|
1227
1488
|
}
|
|
1228
1489
|
];
|
|
1229
1490
|
}
|
|
1230
|
-
return
|
|
1491
|
+
return NO_VIOLATIONS2;
|
|
1231
1492
|
}
|
|
1232
1493
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1233
1494
|
const role = props.role;
|
|
1234
|
-
if (!implicitRole || !role || role !== implicitRole) return
|
|
1495
|
+
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
|
|
1496
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1235
1497
|
return [
|
|
1236
1498
|
{
|
|
1237
1499
|
valid: false,
|
|
1238
1500
|
fixable: true,
|
|
1239
|
-
severity:
|
|
1501
|
+
severity: diagnostic.severity,
|
|
1240
1502
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1241
|
-
diagnostic
|
|
1503
|
+
diagnostic
|
|
1242
1504
|
}
|
|
1243
1505
|
];
|
|
1244
1506
|
}
|
|
1245
1507
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1246
1508
|
const role = props.role;
|
|
1247
|
-
if (role !== "region") return
|
|
1248
|
-
if (!isStandaloneTag(tag)) return
|
|
1509
|
+
if (role !== "region") return NO_VIOLATIONS2;
|
|
1510
|
+
if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
|
|
1511
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1249
1512
|
return [
|
|
1250
1513
|
{
|
|
1251
1514
|
valid: false,
|
|
1252
1515
|
fixable: true,
|
|
1253
|
-
severity:
|
|
1516
|
+
severity: diagnostic.severity,
|
|
1254
1517
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1255
|
-
diagnostic
|
|
1518
|
+
diagnostic
|
|
1256
1519
|
}
|
|
1257
1520
|
];
|
|
1258
1521
|
}
|
|
@@ -1261,7 +1524,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1261
1524
|
props,
|
|
1262
1525
|
effectiveRole
|
|
1263
1526
|
}) {
|
|
1264
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1527
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1265
1528
|
const results = [];
|
|
1266
1529
|
iterate.forEachEntry(props, (key) => {
|
|
1267
1530
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1279,62 +1542,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1279
1542
|
return results;
|
|
1280
1543
|
}
|
|
1281
1544
|
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1282
|
-
// Accepted value shapes for typed ARIA attributes.
|
|
1283
|
-
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1284
|
-
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1285
|
-
// Boolean (true | false)
|
|
1286
|
-
["aria-atomic", { kind: "boolean" }],
|
|
1287
|
-
["aria-busy", { kind: "boolean" }],
|
|
1288
|
-
["aria-disabled", { kind: "boolean" }],
|
|
1289
|
-
["aria-expanded", { kind: "boolean" }],
|
|
1290
|
-
["aria-hidden", { kind: "boolean" }],
|
|
1291
|
-
["aria-modal", { kind: "boolean" }],
|
|
1292
|
-
["aria-multiline", { kind: "boolean" }],
|
|
1293
|
-
["aria-multiselectable", { kind: "boolean" }],
|
|
1294
|
-
["aria-readonly", { kind: "boolean" }],
|
|
1295
|
-
["aria-required", { kind: "boolean" }],
|
|
1296
|
-
["aria-selected", { kind: "boolean" }],
|
|
1297
|
-
// Tristate (true | false | mixed)
|
|
1298
|
-
["aria-checked", { kind: "tristate" }],
|
|
1299
|
-
["aria-pressed", { kind: "tristate" }],
|
|
1300
|
-
// Numeric (any finite number)
|
|
1301
|
-
["aria-valuenow", { kind: "number" }],
|
|
1302
|
-
["aria-valuemin", { kind: "number" }],
|
|
1303
|
-
["aria-valuemax", { kind: "number" }],
|
|
1304
|
-
// Integer with optional range
|
|
1305
|
-
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1306
|
-
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1307
|
-
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1308
|
-
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1309
|
-
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1310
|
-
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1311
|
-
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1312
|
-
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1313
|
-
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1314
|
-
// Enum (specific allowed tokens)
|
|
1315
|
-
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1316
|
-
[
|
|
1317
|
-
"aria-current",
|
|
1318
|
-
{
|
|
1319
|
-
kind: "enum",
|
|
1320
|
-
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1321
|
-
}
|
|
1322
|
-
],
|
|
1323
|
-
[
|
|
1324
|
-
"aria-haspopup",
|
|
1325
|
-
{
|
|
1326
|
-
kind: "enum",
|
|
1327
|
-
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1328
|
-
}
|
|
1329
|
-
],
|
|
1330
|
-
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1331
|
-
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1332
|
-
[
|
|
1333
|
-
"aria-orientation",
|
|
1334
|
-
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1335
|
-
],
|
|
1336
|
-
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1337
|
-
]);
|
|
1338
1545
|
static #isValidAriaValue(value, type) {
|
|
1339
1546
|
switch (type.kind) {
|
|
1340
1547
|
case "boolean":
|
|
@@ -1379,11 +1586,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1379
1586
|
}
|
|
1380
1587
|
}
|
|
1381
1588
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1382
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1589
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1383
1590
|
const results = [];
|
|
1384
1591
|
iterate.forEachEntry(props, (key, value) => {
|
|
1385
1592
|
if (!key.startsWith("aria-")) return;
|
|
1386
|
-
const type =
|
|
1593
|
+
const type = ARIA_VALUE_TYPES.get(key);
|
|
1387
1594
|
if (!isNonNull(type)) return;
|
|
1388
1595
|
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1389
1596
|
results.push({
|
|
@@ -1402,26 +1609,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1402
1609
|
return results;
|
|
1403
1610
|
}
|
|
1404
1611
|
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1405
|
-
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1406
|
-
["h1", 1],
|
|
1407
|
-
["h2", 2],
|
|
1408
|
-
["h3", 3],
|
|
1409
|
-
["h4", 4],
|
|
1410
|
-
["h5", 5],
|
|
1411
|
-
["h6", 6]
|
|
1412
|
-
]);
|
|
1413
1612
|
static #checkRedundantAriaLevel({
|
|
1414
1613
|
tag,
|
|
1415
1614
|
props,
|
|
1416
1615
|
effectiveRole
|
|
1417
1616
|
}) {
|
|
1418
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1419
|
-
const implicitLevel =
|
|
1420
|
-
if (!isNonNull(implicitLevel)) return
|
|
1617
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1618
|
+
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1619
|
+
if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
|
|
1421
1620
|
const raw = props["aria-level"];
|
|
1422
|
-
if (!isNonNull(raw)) return
|
|
1621
|
+
if (!isNonNull(raw)) return NO_VIOLATIONS2;
|
|
1423
1622
|
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1424
|
-
if (!Number.isFinite(n) || n !== implicitLevel) return
|
|
1623
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
|
|
1425
1624
|
return [
|
|
1426
1625
|
{
|
|
1427
1626
|
valid: false,
|
|
@@ -1434,20 +1633,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1434
1633
|
];
|
|
1435
1634
|
}
|
|
1436
1635
|
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1437
|
-
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1438
|
-
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1439
|
-
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1440
|
-
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1441
|
-
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1442
1636
|
static #checkNameRequiredRoles({
|
|
1443
1637
|
tag,
|
|
1444
1638
|
props,
|
|
1445
1639
|
effectiveRole
|
|
1446
1640
|
}) {
|
|
1447
|
-
if (!effectiveRole || !
|
|
1448
|
-
|
|
1449
|
-
if ("
|
|
1450
|
-
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
|
|
1641
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
|
|
1642
|
+
if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
|
|
1643
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
|
|
1451
1644
|
return [
|
|
1452
1645
|
{
|
|
1453
1646
|
valid: false,
|
|
@@ -1457,51 +1650,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1457
1650
|
}
|
|
1458
1651
|
];
|
|
1459
1652
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1467
|
-
["spinbutton", ["aria-valuenow"]]
|
|
1468
|
-
]);
|
|
1469
|
-
static #checkRequiredAriaProperties({
|
|
1470
|
-
props,
|
|
1471
|
-
effectiveRole
|
|
1472
|
-
}) {
|
|
1473
|
-
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1474
|
-
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1475
|
-
if (!isNonNull(required)) return NO_VIOLATIONS;
|
|
1476
|
-
const results = [];
|
|
1477
|
-
iterate.forEach(required, (attr) => {
|
|
1478
|
-
if (attr in props) return;
|
|
1479
|
-
results.push({
|
|
1480
|
-
valid: false,
|
|
1481
|
-
fixable: false,
|
|
1482
|
-
severity: "warning",
|
|
1483
|
-
attribute: attr,
|
|
1484
|
-
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1485
|
-
});
|
|
1486
|
-
});
|
|
1487
|
-
return results;
|
|
1653
|
+
static #requiredAriaPropertiesRule = {
|
|
1654
|
+
attributesByRole: REQUIRED_ARIA_PROPERTIES,
|
|
1655
|
+
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
1656
|
+
};
|
|
1657
|
+
static #checkRequiredAriaProperties(context) {
|
|
1658
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
1488
1659
|
}
|
|
1489
|
-
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1490
|
-
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1491
|
-
"a",
|
|
1492
|
-
"button",
|
|
1493
|
-
"input",
|
|
1494
|
-
"select",
|
|
1495
|
-
"textarea"
|
|
1496
|
-
]);
|
|
1497
1660
|
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1498
1661
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1499
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
1500
|
-
const isInteractive =
|
|
1662
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
|
|
1663
|
+
const isInteractive = INTERACTIVE_TAGS.has(tag);
|
|
1501
1664
|
if (!isInteractive) {
|
|
1502
1665
|
const tabindex = props.tabindex;
|
|
1503
1666
|
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1504
|
-
if (!Number.isFinite(n) || n < 0) return
|
|
1667
|
+
if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
|
|
1505
1668
|
}
|
|
1506
1669
|
return [
|
|
1507
1670
|
{
|
|
@@ -1520,7 +1683,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1520
1683
|
props,
|
|
1521
1684
|
effectiveRole
|
|
1522
1685
|
}) {
|
|
1523
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
1686
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
|
|
1524
1687
|
const results = [];
|
|
1525
1688
|
iterate.forEachEntry(props, (key) => {
|
|
1526
1689
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1536,18 +1699,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1536
1699
|
});
|
|
1537
1700
|
return results;
|
|
1538
1701
|
}
|
|
1539
|
-
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1540
|
-
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1541
|
-
["alert", "assertive"],
|
|
1542
|
-
["status", "polite"],
|
|
1543
|
-
["log", "polite"],
|
|
1544
|
-
["timer", "off"]
|
|
1545
|
-
]);
|
|
1546
1702
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1547
|
-
if (!effectiveRole) return
|
|
1548
|
-
const impliedLive =
|
|
1549
|
-
if (!impliedLive) return
|
|
1550
|
-
if ("aria-live" in props) return
|
|
1703
|
+
if (!effectiveRole) return NO_VIOLATIONS2;
|
|
1704
|
+
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
1705
|
+
if (!impliedLive) return NO_VIOLATIONS2;
|
|
1706
|
+
if ("aria-live" in props) return NO_VIOLATIONS2;
|
|
1551
1707
|
const injectLive = {
|
|
1552
1708
|
kind: `injectLive:${effectiveRole}`,
|
|
1553
1709
|
apply: (ctx) => ({
|
|
@@ -1566,20 +1722,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1566
1722
|
}
|
|
1567
1723
|
];
|
|
1568
1724
|
}
|
|
1569
|
-
static #
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
valid: false,
|
|
1576
|
-
fixable: false,
|
|
1577
|
-
severity: "warning",
|
|
1578
|
-
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1579
|
-
}
|
|
1580
|
-
];
|
|
1725
|
+
static #missingAtomicRule = {
|
|
1726
|
+
attributesByRole: ATOMIC_REQUIREMENTS,
|
|
1727
|
+
diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
|
|
1728
|
+
};
|
|
1729
|
+
static #checkMissingAtomic(context) {
|
|
1730
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
|
|
1581
1731
|
}
|
|
1582
|
-
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1583
1732
|
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1584
1733
|
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1585
1734
|
// produce inconsistent results on cache hits.
|
|
@@ -1593,10 +1742,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1593
1742
|
};
|
|
1594
1743
|
static #checkInvalidAriaRelevant({ props }) {
|
|
1595
1744
|
const relevant = props["aria-relevant"];
|
|
1596
|
-
if (relevant === void 0) return
|
|
1597
|
-
if (typeof relevant !== "string") return
|
|
1745
|
+
if (relevant === void 0) return NO_VIOLATIONS2;
|
|
1746
|
+
if (typeof relevant !== "string") return NO_VIOLATIONS2;
|
|
1598
1747
|
const tokens = relevant.trim().split(/\s+/);
|
|
1599
|
-
const invalid = tokens.filter((t) => !
|
|
1748
|
+
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
1600
1749
|
if (invalid.length > 0) {
|
|
1601
1750
|
return [
|
|
1602
1751
|
{
|
|
@@ -1621,7 +1770,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1621
1770
|
}
|
|
1622
1771
|
];
|
|
1623
1772
|
}
|
|
1624
|
-
return
|
|
1773
|
+
return NO_VIOLATIONS2;
|
|
1625
1774
|
}
|
|
1626
1775
|
};
|
|
1627
1776
|
|
|
@@ -1941,6 +2090,425 @@ var readonlyProps = ({
|
|
|
1941
2090
|
// ../core/src/html/evaluators.ts
|
|
1942
2091
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
1943
2092
|
|
|
2093
|
+
// ../core/src/html/spec/vocabulary/input.ts
|
|
2094
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2095
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2096
|
+
"number",
|
|
2097
|
+
"range",
|
|
2098
|
+
"date",
|
|
2099
|
+
"month",
|
|
2100
|
+
"week",
|
|
2101
|
+
"time",
|
|
2102
|
+
"datetime-local"
|
|
2103
|
+
];
|
|
2104
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2105
|
+
...TEXT_INPUT_TYPES,
|
|
2106
|
+
...NUMERIC_INPUT_TYPES,
|
|
2107
|
+
"checkbox",
|
|
2108
|
+
"radio",
|
|
2109
|
+
"file",
|
|
2110
|
+
"color",
|
|
2111
|
+
"hidden",
|
|
2112
|
+
"button",
|
|
2113
|
+
"submit",
|
|
2114
|
+
"reset",
|
|
2115
|
+
"image"
|
|
2116
|
+
]);
|
|
2117
|
+
|
|
2118
|
+
// ../core/src/html/spec/attributes/input.ts
|
|
2119
|
+
var INPUT_ATTRIBUTE_TYPE_POLICIES = [
|
|
2120
|
+
{ attribute: "checked", allowedTypes: ["checkbox", "radio"] },
|
|
2121
|
+
{ attribute: "multiple", allowedTypes: ["email", "file"] },
|
|
2122
|
+
{ attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2123
|
+
{ attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2124
|
+
{ attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
|
|
2125
|
+
{ attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2126
|
+
{ attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2127
|
+
{ attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2128
|
+
{ attribute: "accept", allowedTypes: ["file"] },
|
|
2129
|
+
{ attribute: "capture", allowedTypes: ["file"] },
|
|
2130
|
+
{ attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
|
|
2131
|
+
{ attribute: "alt", allowedTypes: ["image"] },
|
|
2132
|
+
{ attribute: "height", allowedTypes: ["image"] },
|
|
2133
|
+
{ attribute: "width", allowedTypes: ["image"] }
|
|
2134
|
+
];
|
|
2135
|
+
|
|
2136
|
+
// ../core/src/html/spec/constraints/input.ts
|
|
2137
|
+
var REQUIRED_READONLY_CONFLICT = {
|
|
2138
|
+
props: ["required", "readOnly"],
|
|
2139
|
+
diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
|
|
2140
|
+
};
|
|
2141
|
+
var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
|
|
2142
|
+
REQUIRED_READONLY_CONFLICT
|
|
2143
|
+
];
|
|
2144
|
+
|
|
2145
|
+
// ../core/src/html/spec/validators/attribute-type-validator.ts
|
|
2146
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2147
|
+
function omit(props, key) {
|
|
2148
|
+
const next = { ...props };
|
|
2149
|
+
delete next[key];
|
|
2150
|
+
return next;
|
|
2151
|
+
}
|
|
2152
|
+
function removeAttributeFix(attribute) {
|
|
2153
|
+
return {
|
|
2154
|
+
kind: `removeAttribute:${attribute}`,
|
|
2155
|
+
apply: ({ props }) => {
|
|
2156
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2157
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
function createInputAttributeTypeRule({
|
|
2162
|
+
attribute,
|
|
2163
|
+
allowedTypes
|
|
2164
|
+
}) {
|
|
2165
|
+
const rule = ({ tag, props }) => {
|
|
2166
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2167
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2168
|
+
if (allowedTypes.includes(type)) return [];
|
|
2169
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2170
|
+
return [
|
|
2171
|
+
{
|
|
2172
|
+
valid: false,
|
|
2173
|
+
fixable: true,
|
|
2174
|
+
severity: diagnostic.severity,
|
|
2175
|
+
fix: removeAttributeFix(attribute),
|
|
2176
|
+
diagnostic
|
|
2177
|
+
}
|
|
2178
|
+
];
|
|
2179
|
+
};
|
|
2180
|
+
return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// ../core/src/html/spec/validators/mutually-exclusive-validator.ts
|
|
2184
|
+
function createMutuallyExclusiveRule({
|
|
2185
|
+
props: conflictingProps,
|
|
2186
|
+
diagnostic: createDiagnostic
|
|
2187
|
+
}) {
|
|
2188
|
+
const [first, second] = conflictingProps;
|
|
2189
|
+
const rule = ({ tag, props }) => {
|
|
2190
|
+
if (tag !== "input" || !props[first] || !props[second]) return [];
|
|
2191
|
+
const diagnostic = createDiagnostic();
|
|
2192
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2193
|
+
};
|
|
2194
|
+
return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// ../core/src/html/input-rules.ts
|
|
2198
|
+
var policyByAttribute = Object.fromEntries(
|
|
2199
|
+
INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
|
|
2200
|
+
);
|
|
2201
|
+
function policyFor(attribute) {
|
|
2202
|
+
return policyByAttribute[attribute];
|
|
2203
|
+
}
|
|
2204
|
+
var supportedInputTypeRule = Object.assign(
|
|
2205
|
+
({ tag, props }) => {
|
|
2206
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2207
|
+
const type = props.type;
|
|
2208
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2209
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2210
|
+
return [
|
|
2211
|
+
{
|
|
2212
|
+
valid: false,
|
|
2213
|
+
fixable: false,
|
|
2214
|
+
severity: diagnostic.severity,
|
|
2215
|
+
diagnostic
|
|
2216
|
+
}
|
|
2217
|
+
];
|
|
2218
|
+
},
|
|
2219
|
+
{ readsProps: ["type"], tags: ["input"] }
|
|
2220
|
+
);
|
|
2221
|
+
var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
|
|
2222
|
+
var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
|
|
2223
|
+
var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
|
|
2224
|
+
var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
|
|
2225
|
+
var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
|
|
2226
|
+
var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
|
|
2227
|
+
var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
|
|
2228
|
+
var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
|
|
2229
|
+
var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
|
|
2230
|
+
var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
|
|
2231
|
+
var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
|
|
2232
|
+
var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
|
|
2233
|
+
var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
|
|
2234
|
+
var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
|
|
2235
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2236
|
+
({ tag, props }) => {
|
|
2237
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2238
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2239
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2240
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2241
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2242
|
+
},
|
|
2243
|
+
{
|
|
2244
|
+
readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
|
|
2245
|
+
tags: ["input"]
|
|
2246
|
+
}
|
|
2247
|
+
);
|
|
2248
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2249
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2250
|
+
({ tag, props }) => {
|
|
2251
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2252
|
+
const autoComplete = props.autoComplete;
|
|
2253
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2254
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2255
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2256
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2257
|
+
},
|
|
2258
|
+
{ readsProps: ["type", "autoComplete"], tags: ["input"] }
|
|
2259
|
+
);
|
|
2260
|
+
var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
|
|
2261
|
+
var INPUT_RULES = [
|
|
2262
|
+
supportedInputTypeRule,
|
|
2263
|
+
checkedRequiresCheckableTypeRule,
|
|
2264
|
+
multipleRequiresSupportedTypeRule,
|
|
2265
|
+
maxLengthRequiresTextTypeRule,
|
|
2266
|
+
minLengthRequiresTextTypeRule,
|
|
2267
|
+
patternRequiresTextTypeRule,
|
|
2268
|
+
minRequiresNumericTypeRule,
|
|
2269
|
+
maxRequiresNumericTypeRule,
|
|
2270
|
+
stepRequiresNumericTypeRule,
|
|
2271
|
+
acceptRequiresFileTypeRule,
|
|
2272
|
+
captureRequiresFileTypeRule,
|
|
2273
|
+
sizeRequiresTextTypeRule,
|
|
2274
|
+
altRequiresImageTypeRule,
|
|
2275
|
+
heightRequiresImageTypeRule,
|
|
2276
|
+
widthRequiresImageTypeRule,
|
|
2277
|
+
inputAccessibleNameRule,
|
|
2278
|
+
passwordAutocompleteRule,
|
|
2279
|
+
requiredReadOnlyConflictRule
|
|
2280
|
+
];
|
|
2281
|
+
|
|
2282
|
+
// ../core/src/html/spec/types.ts
|
|
2283
|
+
function definePropRolePolicy(prop, map2, fallback) {
|
|
2284
|
+
return { kind: "byProp", prop, map: map2, fallback };
|
|
2285
|
+
}
|
|
2286
|
+
function resolveAllowedRoles(spec, props) {
|
|
2287
|
+
const policy = spec.allowedRoles;
|
|
2288
|
+
if (!policy) return void 0;
|
|
2289
|
+
switch (policy.kind) {
|
|
2290
|
+
case "fixed":
|
|
2291
|
+
return policy.roles;
|
|
2292
|
+
case "byProp": {
|
|
2293
|
+
const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
|
|
2294
|
+
return policy.map[value];
|
|
2295
|
+
}
|
|
2296
|
+
case "dynamic":
|
|
2297
|
+
return policy.resolve({ props });
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// ../core/src/html/spec/roles/input.ts
|
|
2302
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2303
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2304
|
+
radio: ["menuitemradio"],
|
|
2305
|
+
range: [],
|
|
2306
|
+
number: [],
|
|
2307
|
+
search: ["combobox"],
|
|
2308
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2309
|
+
email: ["combobox"],
|
|
2310
|
+
tel: ["combobox"],
|
|
2311
|
+
url: ["combobox"],
|
|
2312
|
+
button: [
|
|
2313
|
+
"link",
|
|
2314
|
+
"menuitem",
|
|
2315
|
+
"menuitemcheckbox",
|
|
2316
|
+
"menuitemradio",
|
|
2317
|
+
"option",
|
|
2318
|
+
"radio",
|
|
2319
|
+
"switch",
|
|
2320
|
+
"tab"
|
|
2321
|
+
],
|
|
2322
|
+
submit: [
|
|
2323
|
+
"link",
|
|
2324
|
+
"menuitem",
|
|
2325
|
+
"menuitemcheckbox",
|
|
2326
|
+
"menuitemradio",
|
|
2327
|
+
"option",
|
|
2328
|
+
"radio",
|
|
2329
|
+
"switch",
|
|
2330
|
+
"tab"
|
|
2331
|
+
],
|
|
2332
|
+
reset: [
|
|
2333
|
+
"link",
|
|
2334
|
+
"menuitem",
|
|
2335
|
+
"menuitemcheckbox",
|
|
2336
|
+
"menuitemradio",
|
|
2337
|
+
"option",
|
|
2338
|
+
"radio",
|
|
2339
|
+
"switch",
|
|
2340
|
+
"tab"
|
|
2341
|
+
],
|
|
2342
|
+
image: [
|
|
2343
|
+
"link",
|
|
2344
|
+
"menuitem",
|
|
2345
|
+
"menuitemcheckbox",
|
|
2346
|
+
"menuitemradio",
|
|
2347
|
+
"option",
|
|
2348
|
+
"radio",
|
|
2349
|
+
"switch",
|
|
2350
|
+
"tab"
|
|
2351
|
+
],
|
|
2352
|
+
hidden: []
|
|
2353
|
+
};
|
|
2354
|
+
|
|
2355
|
+
// ../core/src/html/spec/elements/input.ts
|
|
2356
|
+
var inputElementSpec = {
|
|
2357
|
+
tag: "input",
|
|
2358
|
+
allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
|
|
2359
|
+
attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
|
|
2360
|
+
mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// ../core/src/html/spec/roles/img.ts
|
|
2364
|
+
var IMG_NAMED_ROLES = [
|
|
2365
|
+
"button",
|
|
2366
|
+
"checkbox",
|
|
2367
|
+
"link",
|
|
2368
|
+
"menuitem",
|
|
2369
|
+
"menuitemcheckbox",
|
|
2370
|
+
"menuitemradio",
|
|
2371
|
+
"option",
|
|
2372
|
+
"progressbar",
|
|
2373
|
+
"scrollbar",
|
|
2374
|
+
"separator",
|
|
2375
|
+
"slider",
|
|
2376
|
+
"switch",
|
|
2377
|
+
"tab",
|
|
2378
|
+
"treeitem"
|
|
2379
|
+
];
|
|
2380
|
+
|
|
2381
|
+
// ../core/src/html/spec/elements/img.ts
|
|
2382
|
+
var imgElementSpec = {
|
|
2383
|
+
tag: "img",
|
|
2384
|
+
allowedRoles: {
|
|
2385
|
+
kind: "dynamic",
|
|
2386
|
+
resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
// ../core/src/html/spec/roles/table.ts
|
|
2391
|
+
var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
|
|
2392
|
+
|
|
2393
|
+
// ../core/src/html/spec/elements/table.ts
|
|
2394
|
+
var tableElementSpec = {
|
|
2395
|
+
tag: "table",
|
|
2396
|
+
allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
|
|
2397
|
+
};
|
|
2398
|
+
|
|
2399
|
+
// ../core/src/html/role-restrictions.ts
|
|
2400
|
+
var ALLOWED_ROLES = {
|
|
2401
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2402
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2403
|
+
footer: ["group", "none", "presentation"],
|
|
2404
|
+
header: ["group", "none", "presentation"],
|
|
2405
|
+
main: [],
|
|
2406
|
+
nav: [],
|
|
2407
|
+
a: [
|
|
2408
|
+
"button",
|
|
2409
|
+
"checkbox",
|
|
2410
|
+
"menuitem",
|
|
2411
|
+
"menuitemcheckbox",
|
|
2412
|
+
"menuitemradio",
|
|
2413
|
+
"option",
|
|
2414
|
+
"radio",
|
|
2415
|
+
"switch",
|
|
2416
|
+
"tab",
|
|
2417
|
+
"treeitem"
|
|
2418
|
+
],
|
|
2419
|
+
button: [
|
|
2420
|
+
"checkbox",
|
|
2421
|
+
"link",
|
|
2422
|
+
"menuitem",
|
|
2423
|
+
"menuitemcheckbox",
|
|
2424
|
+
"menuitemradio",
|
|
2425
|
+
"option",
|
|
2426
|
+
"radio",
|
|
2427
|
+
"switch",
|
|
2428
|
+
"tab"
|
|
2429
|
+
],
|
|
2430
|
+
select: ["menu"],
|
|
2431
|
+
h1: ["tab", "presentation", "none"],
|
|
2432
|
+
h2: ["tab", "presentation", "none"],
|
|
2433
|
+
h3: ["tab", "presentation", "none"],
|
|
2434
|
+
h4: ["tab", "presentation", "none"],
|
|
2435
|
+
h5: ["tab", "presentation", "none"],
|
|
2436
|
+
h6: ["tab", "presentation", "none"],
|
|
2437
|
+
ul: [
|
|
2438
|
+
"directory",
|
|
2439
|
+
"group",
|
|
2440
|
+
"listbox",
|
|
2441
|
+
"menu",
|
|
2442
|
+
"menubar",
|
|
2443
|
+
"radiogroup",
|
|
2444
|
+
"tablist",
|
|
2445
|
+
"toolbar",
|
|
2446
|
+
"tree"
|
|
2447
|
+
],
|
|
2448
|
+
ol: [
|
|
2449
|
+
"directory",
|
|
2450
|
+
"group",
|
|
2451
|
+
"listbox",
|
|
2452
|
+
"menu",
|
|
2453
|
+
"menubar",
|
|
2454
|
+
"radiogroup",
|
|
2455
|
+
"tablist",
|
|
2456
|
+
"toolbar",
|
|
2457
|
+
"tree"
|
|
2458
|
+
],
|
|
2459
|
+
li: [
|
|
2460
|
+
"menuitem",
|
|
2461
|
+
"menuitemcheckbox",
|
|
2462
|
+
"menuitemradio",
|
|
2463
|
+
"option",
|
|
2464
|
+
"none",
|
|
2465
|
+
"presentation",
|
|
2466
|
+
"radio",
|
|
2467
|
+
"separator",
|
|
2468
|
+
"tab",
|
|
2469
|
+
"treeitem"
|
|
2470
|
+
],
|
|
2471
|
+
dialog: ["alertdialog"],
|
|
2472
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2473
|
+
};
|
|
2474
|
+
var ELEMENT_SPECS = {
|
|
2475
|
+
input: inputElementSpec,
|
|
2476
|
+
img: imgElementSpec,
|
|
2477
|
+
table: tableElementSpec
|
|
2478
|
+
};
|
|
2479
|
+
function getAllowedRoles(tag, props) {
|
|
2480
|
+
const spec = ELEMENT_SPECS[tag];
|
|
2481
|
+
if (spec) return resolveAllowedRoles(spec, props);
|
|
2482
|
+
return ALLOWED_ROLES[tag];
|
|
2483
|
+
}
|
|
2484
|
+
var removeRoleFix = {
|
|
2485
|
+
kind: "removeRole",
|
|
2486
|
+
apply: ({ props }) => {
|
|
2487
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2488
|
+
const { role: _role, ...rest } = props;
|
|
2489
|
+
return { applied: true, next: rest, previous: props };
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
var roleNotPermittedRule = Object.assign(
|
|
2493
|
+
({ tag, props, implicitRole }) => {
|
|
2494
|
+
const role = props.role;
|
|
2495
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2496
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2497
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2498
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2499
|
+
return [
|
|
2500
|
+
{
|
|
2501
|
+
valid: false,
|
|
2502
|
+
fixable: true,
|
|
2503
|
+
severity: diagnostic.severity,
|
|
2504
|
+
fix: removeRoleFix,
|
|
2505
|
+
diagnostic
|
|
2506
|
+
}
|
|
2507
|
+
];
|
|
2508
|
+
},
|
|
2509
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2510
|
+
);
|
|
2511
|
+
|
|
1944
2512
|
// ../core/src/html/aria-rules.ts
|
|
1945
2513
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
1946
2514
|
var removeLandmarkRoleOverride = {
|
|
@@ -1951,20 +2519,24 @@ var removeLandmarkRoleOverride = {
|
|
|
1951
2519
|
return { applied: true, next: rest, previous: props };
|
|
1952
2520
|
}
|
|
1953
2521
|
};
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
}
|
|
2522
|
+
var landmarkRoleRule = Object.assign(
|
|
2523
|
+
({ tag, props, implicitRole }) => {
|
|
2524
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2525
|
+
const role = props.role;
|
|
2526
|
+
if (!role || role === implicitRole) return [];
|
|
2527
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2528
|
+
return [
|
|
2529
|
+
{
|
|
2530
|
+
valid: false,
|
|
2531
|
+
fixable: true,
|
|
2532
|
+
severity: diagnostic.severity,
|
|
2533
|
+
fix: removeLandmarkRoleOverride,
|
|
2534
|
+
diagnostic
|
|
2535
|
+
}
|
|
2536
|
+
];
|
|
2537
|
+
},
|
|
2538
|
+
{ tags: [...LANDMARK_TAG_SET] }
|
|
2539
|
+
);
|
|
1968
2540
|
function requireAccessibleName({ tag, props }) {
|
|
1969
2541
|
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
1970
2542
|
return [
|
|
@@ -1977,11 +2549,19 @@ function requireAccessibleName({ tag, props }) {
|
|
|
1977
2549
|
];
|
|
1978
2550
|
}
|
|
1979
2551
|
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2552
|
+
var landmarkNameAdvisory = Object.assign(
|
|
2553
|
+
(ctx) => {
|
|
2554
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2555
|
+
return requireAccessibleName(ctx);
|
|
2556
|
+
},
|
|
2557
|
+
{ tags: [...NAMED_LANDMARK_TAGS] }
|
|
2558
|
+
);
|
|
2559
|
+
var HTML_ARIA_RULES = [
|
|
2560
|
+
landmarkRoleRule,
|
|
2561
|
+
landmarkNameAdvisory,
|
|
2562
|
+
roleNotPermittedRule,
|
|
2563
|
+
...INPUT_RULES
|
|
2564
|
+
];
|
|
1985
2565
|
|
|
1986
2566
|
// ../core/src/html/contracts.ts
|
|
1987
2567
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2076,6 +2656,62 @@ var figureContract = contract([
|
|
|
2076
2656
|
]);
|
|
2077
2657
|
var detailsContract = firstChildContract("summary", "summary");
|
|
2078
2658
|
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2659
|
+
var objectContract = contract([
|
|
2660
|
+
{ name: "param", match: isTag("param") },
|
|
2661
|
+
{ name: "content", match: isOpenContent("param") }
|
|
2662
|
+
]);
|
|
2663
|
+
var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
|
|
2664
|
+
var buttonContract = closedContract([
|
|
2665
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2666
|
+
]);
|
|
2667
|
+
var anchorContract = closedContract([
|
|
2668
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2669
|
+
]);
|
|
2670
|
+
var LABELABLE_TAGS = [
|
|
2671
|
+
"button",
|
|
2672
|
+
"input",
|
|
2673
|
+
"meter",
|
|
2674
|
+
"output",
|
|
2675
|
+
"progress",
|
|
2676
|
+
"select",
|
|
2677
|
+
"textarea"
|
|
2678
|
+
];
|
|
2679
|
+
var labelContract = contract([
|
|
2680
|
+
{ name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
|
|
2681
|
+
]);
|
|
2682
|
+
var P_BLOCKED_TAGS = [
|
|
2683
|
+
"address",
|
|
2684
|
+
"article",
|
|
2685
|
+
"aside",
|
|
2686
|
+
"blockquote",
|
|
2687
|
+
"details",
|
|
2688
|
+
"dialog",
|
|
2689
|
+
"div",
|
|
2690
|
+
"dl",
|
|
2691
|
+
"fieldset",
|
|
2692
|
+
"figure",
|
|
2693
|
+
"footer",
|
|
2694
|
+
"form",
|
|
2695
|
+
"h1",
|
|
2696
|
+
"h2",
|
|
2697
|
+
"h3",
|
|
2698
|
+
"h4",
|
|
2699
|
+
"h5",
|
|
2700
|
+
"h6",
|
|
2701
|
+
"header",
|
|
2702
|
+
"hr",
|
|
2703
|
+
"main",
|
|
2704
|
+
"nav",
|
|
2705
|
+
"ol",
|
|
2706
|
+
"p",
|
|
2707
|
+
"pre",
|
|
2708
|
+
"section",
|
|
2709
|
+
"table",
|
|
2710
|
+
"ul"
|
|
2711
|
+
];
|
|
2712
|
+
var pContract = closedContract([
|
|
2713
|
+
{ name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
|
|
2714
|
+
]);
|
|
2079
2715
|
var mediaContract = contract([
|
|
2080
2716
|
{ name: "source", match: isTag("source") },
|
|
2081
2717
|
{ name: "track", match: isTag("track") },
|
|
@@ -2130,6 +2766,11 @@ var htmlContracts = {
|
|
|
2130
2766
|
details: detailsContract,
|
|
2131
2767
|
fieldset: fieldsetContract,
|
|
2132
2768
|
dialog: dialogContract,
|
|
2769
|
+
object: objectContract,
|
|
2770
|
+
button: buttonContract,
|
|
2771
|
+
a: anchorContract,
|
|
2772
|
+
label: labelContract,
|
|
2773
|
+
p: pContract,
|
|
2133
2774
|
head: headContract,
|
|
2134
2775
|
html: htmlContract
|
|
2135
2776
|
};
|
|
@@ -2442,21 +3083,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2442
3083
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2443
3084
|
|
|
2444
3085
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2445
|
-
import { DiagnosticCategory as
|
|
3086
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
2446
3087
|
var PluginDiagnostics = {
|
|
2447
3088
|
invalidShape(received) {
|
|
2448
3089
|
const got = received === null ? "null" : typeof received;
|
|
2449
3090
|
return {
|
|
2450
|
-
code:
|
|
2451
|
-
category:
|
|
3091
|
+
code: DiagnosticCode5.PluginInvalidShape,
|
|
3092
|
+
category: DiagnosticCategory5.Internal,
|
|
2452
3093
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2453
3094
|
};
|
|
2454
3095
|
},
|
|
2455
3096
|
pipelineReturnType(received) {
|
|
2456
3097
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2457
3098
|
return {
|
|
2458
|
-
code:
|
|
2459
|
-
category:
|
|
3099
|
+
code: DiagnosticCode5.PluginPipelineReturnType,
|
|
3100
|
+
category: DiagnosticCategory5.Internal,
|
|
2460
3101
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2461
3102
|
};
|
|
2462
3103
|
}
|