praxis-kit 0.1.1 → 1.0.2
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/{build-runtime-CJ_nQEaZ.js → build-runtime-cNkW-D62.js} +331 -207
- package/dist/contract/index.d.ts +396 -14
- package/dist/contract/index.js +66 -1
- package/dist/eslint/index.js +11 -5
- package/dist/{index-DiPzmjWW.d.ts → index-zJDSXTxL.d.ts} +342 -66
- package/dist/lit/index.d.ts +251 -55
- package/dist/lit/index.js +275 -215
- package/dist/preact/index.d.ts +296 -52
- package/dist/preact/index.js +277 -210
- package/dist/react/index.d.ts +13 -4
- package/dist/react/index.js +22 -72
- package/dist/react/legacy.d.ts +9 -4
- package/dist/react/legacy.js +14 -2
- package/dist/solid/index.d.ts +253 -40
- package/dist/solid/index.js +271 -210
- package/dist/svelte/index.d.ts +203 -33
- package/dist/svelte/index.js +270 -207
- package/dist/tailwind/index.d.ts +12 -1
- package/dist/vite-plugin/index.js +289 -199
- package/dist/vue/index.d.ts +252 -41
- package/dist/vue/index.js +277 -210
- package/dist/web/index.d.ts +271 -53
- package/dist/web/index.js +275 -215
- package/package.json +5 -5
package/dist/svelte/index.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { clsx } from "clsx";
|
|
2
2
|
import { DiagnosticCategory, DiagnosticCode, resolveDiagnostics, silentDiagnostics, throwDiagnostics, throwDiagnostics as throwDiagnostics$1, warnDiagnostics } from "../_shared/diagnostics.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
-
//#region ../../lib/adapter-utils/src/runtime/define-component.ts
|
|
5
|
-
function defineContractComponent(options) {
|
|
6
|
-
return (factory) => factory(options);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
4
|
//#region ../../lib/adapter-utils/src/runtime/assemble-compound-component.ts
|
|
10
5
|
/**
|
|
11
6
|
* Assembles a compound component by attaching sub-components to a
|
|
@@ -1389,72 +1384,100 @@ var InvariantBase = class {
|
|
|
1389
1384
|
}
|
|
1390
1385
|
};
|
|
1391
1386
|
//#endregion
|
|
1392
|
-
//#region ../../lib/contract/src/aria/
|
|
1393
|
-
const REQUIRED_ARIA_PROPERTIES = {
|
|
1394
|
-
combobox: ["aria-expanded"],
|
|
1395
|
-
option: ["aria-selected"],
|
|
1396
|
-
slider: ["aria-valuenow"],
|
|
1397
|
-
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1398
|
-
spinbutton: ["aria-valuenow"]
|
|
1399
|
-
};
|
|
1400
|
-
//#endregion
|
|
1401
|
-
//#region ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1402
|
-
const NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1403
|
-
//#endregion
|
|
1404
|
-
//#region ../../lib/contract/src/aria/spec/roles/name-prohibited.ts
|
|
1405
|
-
const NAME_PROHIBITED_ROLES = /* @__PURE__ */ new Set([
|
|
1406
|
-
"caption",
|
|
1407
|
-
"code",
|
|
1408
|
-
"deletion",
|
|
1409
|
-
"emphasis",
|
|
1410
|
-
"generic",
|
|
1411
|
-
"insertion",
|
|
1412
|
-
"none",
|
|
1413
|
-
"paragraph",
|
|
1414
|
-
"presentation",
|
|
1415
|
-
"strong",
|
|
1416
|
-
"subscript",
|
|
1417
|
-
"superscript"
|
|
1418
|
-
]);
|
|
1419
|
-
const NAME_PROHIBITED_ATTRIBUTES = ["aria-label", "aria-labelledby"];
|
|
1420
|
-
//#endregion
|
|
1421
|
-
//#region ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1422
|
-
const NO_VIOLATIONS$1 = [{ valid: true }];
|
|
1423
|
-
function requiredAttributeByRole(roles, attribute) {
|
|
1424
|
-
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1425
|
-
}
|
|
1387
|
+
//#region ../../lib/contract/src/aria/plan-cache.ts
|
|
1426
1388
|
/**
|
|
1427
|
-
*
|
|
1389
|
+
* Validation-plan cache machinery for `AriaPolicyEngine`: computing a cache key from a
|
|
1390
|
+
* (tag, props) usage site, and diffing/replaying the props transformation a validation pass
|
|
1391
|
+
* produced (an {@link AriaPlan}'s `removals`/`updates`) without re-running the ARIA rule
|
|
1392
|
+
* pipeline itself. Extracted because these four functions are a cohesive subsystem in their own
|
|
1393
|
+
* right — a small revalidation-caching scheme — not specifically ARIA *policy*; the actual
|
|
1394
|
+
* cache storage (an `LRUCache` instance) stays owned by `AriaPolicyEngine` itself, since it's
|
|
1395
|
+
* per-engine-instance state, not something this module needs to hold.
|
|
1396
|
+
*/
|
|
1397
|
+
/**
|
|
1398
|
+
* Cache key covering only the aria-relevant subset of props (tag + role + aria-* attrs) —
|
|
1399
|
+
* exactly what the built-in pipeline reads. Non-aria props (className, onClick, etc.) do
|
|
1400
|
+
* not affect built-in ARIA decisions and are excluded so cache hits survive re-renders
|
|
1401
|
+
* that only change non-aria props. This key alone is unsound for a policy engine's own
|
|
1402
|
+
* extra rules, which may read arbitrary props outside this set — extend it with
|
|
1403
|
+
* {@link extraRulesKeySuffix}, or bypass the cache, to account for that.
|
|
1428
1404
|
*
|
|
1429
|
-
*
|
|
1430
|
-
*
|
|
1405
|
+
* Returns `null` (meaning "don't cache") when `tag` isn't a plain intrinsic string tag —
|
|
1406
|
+
* `isString(tag)` inline rather than importing `AriaPolicyEngine`'s own `isIntrinsicTag`: the
|
|
1407
|
+
* check is a one-liner, and importing it back from the engine module would be a needless
|
|
1408
|
+
* dependency in the other direction for a plan-cache module the engine itself depends on.
|
|
1431
1409
|
*/
|
|
1432
|
-
function
|
|
1433
|
-
if (!
|
|
1434
|
-
const
|
|
1435
|
-
if (
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1410
|
+
function createPlanKey(tag, props) {
|
|
1411
|
+
if (!isString(tag)) return null;
|
|
1412
|
+
const parts = [tag];
|
|
1413
|
+
if (isString(props.role)) parts.push(`role:${props.role}`);
|
|
1414
|
+
if (tag === "input" && isString(props.type)) parts.push(`type:${props.type}`);
|
|
1415
|
+
if (tag === "img") parts.push(`alt:${props.alt === "" ? "empty" : "present"}`);
|
|
1416
|
+
const ariaEntries = [];
|
|
1417
|
+
iterate.forEachEntry(props, (k, v) => {
|
|
1418
|
+
if (!k.startsWith("aria-")) return;
|
|
1419
|
+
if (!isString(v) && !isNumber(v) && typeof v !== "boolean") return;
|
|
1420
|
+
ariaEntries.push(`${k}:${String(v)}`);
|
|
1421
|
+
});
|
|
1422
|
+
if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
|
|
1423
|
+
return parts.join("|");
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Extends the base cache key with the props each extra rule declares it reads (`readsProps`).
|
|
1427
|
+
* Returns `null` — meaning "don't cache" — if any extra rule omits `readsProps` (it may read
|
|
1428
|
+
* arbitrary props the key can't account for) or if a declared prop's value isn't a primitive
|
|
1429
|
+
* (object/array identity isn't stably representable in a string key).
|
|
1430
|
+
*/
|
|
1431
|
+
function extraRulesKeySuffix(extraRules, props) {
|
|
1432
|
+
const parts = [];
|
|
1433
|
+
for (const rule of extraRules) {
|
|
1434
|
+
const readsProps = rule.readsProps;
|
|
1435
|
+
if (!isNonNull(readsProps)) return null;
|
|
1436
|
+
for (const propKey of readsProps) {
|
|
1437
|
+
const v = props[propKey];
|
|
1438
|
+
if (isDefined(v) && !isString(v) && !isNumber(v) && typeof v !== "boolean") return null;
|
|
1439
|
+
parts.push(`x:${propKey}:${String(v)}`);
|
|
1440
|
+
}
|
|
1446
1441
|
}
|
|
1447
|
-
return
|
|
1442
|
+
return parts.sort().join("|");
|
|
1443
|
+
}
|
|
1444
|
+
/** Diffs a validation pass's input/output props into a replayable plan: which keys were removed
|
|
1445
|
+
* entirely, and which keys were added or changed value (and to what). */
|
|
1446
|
+
function computePlan(inputProps, resultProps) {
|
|
1447
|
+
const removals = /* @__PURE__ */ new Set();
|
|
1448
|
+
const updates = {};
|
|
1449
|
+
iterate.forEachKey(inputProps, (key) => {
|
|
1450
|
+
if (!(key in resultProps)) removals.add(key);
|
|
1451
|
+
});
|
|
1452
|
+
iterate.forEachEntry(resultProps, (key, resultVal) => {
|
|
1453
|
+
if (inputProps[key] !== resultVal) updates[key] = resultVal;
|
|
1454
|
+
});
|
|
1455
|
+
return {
|
|
1456
|
+
removals,
|
|
1457
|
+
updates
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
/** Replays a cached plan's removals/updates against a fresh `props` object, without re-running
|
|
1461
|
+
* the rule pipeline that originally produced the plan. */
|
|
1462
|
+
function applyPlan(props, removals, updates) {
|
|
1463
|
+
const hasRemovals = removals.size > 0;
|
|
1464
|
+
const hasUpdates = Object.keys(updates).length > 0;
|
|
1465
|
+
if (!hasRemovals && !hasUpdates) return props;
|
|
1466
|
+
const next = {};
|
|
1467
|
+
iterate.forEachEntry(props, (k, v) => {
|
|
1468
|
+
if (!removals.has(k)) next[k] = v;
|
|
1469
|
+
});
|
|
1470
|
+
Object.assign(next, updates);
|
|
1471
|
+
return next;
|
|
1448
1472
|
}
|
|
1449
1473
|
//#endregion
|
|
1450
|
-
//#region ../../lib/contract/src/aria/spec/
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1474
|
+
//#region ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1475
|
+
const VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1476
|
+
"additions",
|
|
1477
|
+
"removals",
|
|
1478
|
+
"text",
|
|
1479
|
+
"all"
|
|
1456
1480
|
]);
|
|
1457
|
-
const ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1458
1481
|
//#endregion
|
|
1459
1482
|
//#region ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1460
1483
|
const ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
@@ -1579,24 +1602,6 @@ const ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
|
1579
1602
|
}]
|
|
1580
1603
|
]);
|
|
1581
1604
|
//#endregion
|
|
1582
|
-
//#region ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1583
|
-
const VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1584
|
-
"additions",
|
|
1585
|
-
"removals",
|
|
1586
|
-
"text",
|
|
1587
|
-
"all"
|
|
1588
|
-
]);
|
|
1589
|
-
//#endregion
|
|
1590
|
-
//#region ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1591
|
-
const HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1592
|
-
["h1", 1],
|
|
1593
|
-
["h2", 2],
|
|
1594
|
-
["h3", 3],
|
|
1595
|
-
["h4", 4],
|
|
1596
|
-
["h5", 5],
|
|
1597
|
-
["h6", 6]
|
|
1598
|
-
]);
|
|
1599
|
-
//#endregion
|
|
1600
1605
|
//#region ../../lib/contract/src/aria/spec/elements/focusable.ts
|
|
1601
1606
|
function parseTabIndex(raw) {
|
|
1602
1607
|
if (typeof raw === "number") return Number.isInteger(raw) ? raw : void 0;
|
|
@@ -1647,24 +1652,156 @@ function isPotentiallyFocusable(tag, props) {
|
|
|
1647
1652
|
}
|
|
1648
1653
|
}
|
|
1649
1654
|
//#endregion
|
|
1650
|
-
//#region ../../lib/contract/src/aria/
|
|
1655
|
+
//#region ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1656
|
+
const HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1657
|
+
["h1", 1],
|
|
1658
|
+
["h2", 2],
|
|
1659
|
+
["h3", 3],
|
|
1660
|
+
["h4", 4],
|
|
1661
|
+
["h5", 5],
|
|
1662
|
+
["h6", 6]
|
|
1663
|
+
]);
|
|
1664
|
+
//#endregion
|
|
1665
|
+
//#region ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1651
1666
|
const NO_VIOLATIONS = [{ valid: true }];
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
return isString(tag);
|
|
1667
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1668
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1655
1669
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1670
|
+
/**
|
|
1671
|
+
* Reports missing attributes required by an element's effective ARIA role.
|
|
1672
|
+
*
|
|
1673
|
+
* Shared by validators that differ only in their role-to-attribute mapping and diagnostic
|
|
1674
|
+
* generation.
|
|
1675
|
+
*/
|
|
1676
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1677
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1678
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1679
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1680
|
+
const results = [];
|
|
1681
|
+
for (const attribute of requiredAttributes) {
|
|
1682
|
+
if (attribute in props) continue;
|
|
1683
|
+
results.push({
|
|
1684
|
+
valid: false,
|
|
1685
|
+
fixable: false,
|
|
1686
|
+
severity: "warning",
|
|
1687
|
+
attribute,
|
|
1688
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
return results;
|
|
1659
1692
|
}
|
|
1693
|
+
//#endregion
|
|
1694
|
+
//#region ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1695
|
+
const LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1696
|
+
["alert", "assertive"],
|
|
1697
|
+
["status", "polite"],
|
|
1698
|
+
["log", "polite"],
|
|
1699
|
+
["timer", "off"]
|
|
1700
|
+
]);
|
|
1701
|
+
const ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1702
|
+
//#endregion
|
|
1703
|
+
//#region ../../lib/contract/src/aria/spec/roles/name-prohibited.ts
|
|
1704
|
+
const NAME_PROHIBITED_ROLES = /* @__PURE__ */ new Set([
|
|
1705
|
+
"caption",
|
|
1706
|
+
"code",
|
|
1707
|
+
"deletion",
|
|
1708
|
+
"emphasis",
|
|
1709
|
+
"generic",
|
|
1710
|
+
"insertion",
|
|
1711
|
+
"none",
|
|
1712
|
+
"paragraph",
|
|
1713
|
+
"presentation",
|
|
1714
|
+
"strong",
|
|
1715
|
+
"subscript",
|
|
1716
|
+
"superscript"
|
|
1717
|
+
]);
|
|
1718
|
+
const NAME_PROHIBITED_ATTRIBUTES = ["aria-label", "aria-labelledby"];
|
|
1719
|
+
//#endregion
|
|
1720
|
+
//#region ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1721
|
+
const NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1722
|
+
//#endregion
|
|
1723
|
+
//#region ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1724
|
+
const REQUIRED_ARIA_PROPERTIES = {
|
|
1725
|
+
combobox: ["aria-expanded"],
|
|
1726
|
+
option: ["aria-selected"],
|
|
1727
|
+
slider: ["aria-valuenow"],
|
|
1728
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1729
|
+
spinbutton: ["aria-valuenow"]
|
|
1730
|
+
};
|
|
1731
|
+
//#endregion
|
|
1732
|
+
//#region ../../lib/contract/src/aria/value-validation.ts
|
|
1733
|
+
/**
|
|
1734
|
+
* ARIA attribute value type system: what counts as a valid value for a given
|
|
1735
|
+
* {@link AriaValueType}, and how to describe what was expected when it isn't. Extracted from
|
|
1736
|
+
* `AriaPolicyEngine` — these three functions are cohesive on their own (they're describing a
|
|
1737
|
+
* type system, not policy), and `#checkAriaAttributeValues` (kept in `aria-policy-engine.ts`
|
|
1738
|
+
* alongside every other rule implementation — see that file's own pipeline) is now just a thin
|
|
1739
|
+
* caller of {@link isValidAriaValue}/{@link describeExpected}.
|
|
1740
|
+
*/
|
|
1741
|
+
/**
|
|
1742
|
+
* Strict numeric coercion for ARIA attribute values. Unlike `parseFloat`/`parseInt`, the whole
|
|
1743
|
+
* string must be a number — `"12abc"` is rejected, not read as `12` — and an empty / whitespace
|
|
1744
|
+
* string is rejected rather than coerced to `0` the way `Number("")` would.
|
|
1745
|
+
*
|
|
1746
|
+
* Exported (not local to `isValidAriaValue`) because `AriaPolicyEngine.#checkRedundantAriaLevel`
|
|
1747
|
+
* also needs it, independent of the general ARIA value-type check this module otherwise exists
|
|
1748
|
+
* for — `aria-level`'s "redundant" check is about comparing a numeric attribute value to a
|
|
1749
|
+
* known implicit level, not about validating the value's type.
|
|
1750
|
+
*/
|
|
1660
1751
|
function strictNumeric(value) {
|
|
1661
|
-
if (
|
|
1662
|
-
if (
|
|
1752
|
+
if (isNumber(value)) return Number.isFinite(value) ? value : void 0;
|
|
1753
|
+
if (!isString(value)) return void 0;
|
|
1663
1754
|
const trimmed = value.trim();
|
|
1664
1755
|
if (trimmed === "") return void 0;
|
|
1665
1756
|
const n = Number(trimmed);
|
|
1666
1757
|
return Number.isFinite(n) ? n : void 0;
|
|
1667
1758
|
}
|
|
1759
|
+
/** Returns whether `value` satisfies `type`'s own value-type constraint (boolean/tristate/
|
|
1760
|
+
* number/integer/enum) — the WAI-ARIA attribute value type system, independent of which
|
|
1761
|
+
* attribute or role it's being checked for. */
|
|
1762
|
+
function isValidAriaValue(value, type) {
|
|
1763
|
+
switch (type.kind) {
|
|
1764
|
+
case "boolean": return value === "true" || value === "false" || value === true || value === false;
|
|
1765
|
+
case "tristate": return value === "true" || value === "false" || value === "mixed" || value === true || value === false;
|
|
1766
|
+
case "number": return strictNumeric(value) !== void 0;
|
|
1767
|
+
case "integer": {
|
|
1768
|
+
const n = strictNumeric(value);
|
|
1769
|
+
if (isUndefined(n) || !Number.isInteger(n)) return false;
|
|
1770
|
+
if (isDefined(type.min) && n < type.min) return false;
|
|
1771
|
+
if (isDefined(type.max) && n > type.max) return false;
|
|
1772
|
+
return true;
|
|
1773
|
+
}
|
|
1774
|
+
case "enum": return isString(value) && type.values.has(value);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
/** Describes what a valid value for `type` looks like, for use in an invalid-value diagnostic
|
|
1778
|
+
* message (e.g. `aria-valuenow` got `"abc"`, expected "a finite number"). */
|
|
1779
|
+
function describeExpected(type) {
|
|
1780
|
+
switch (type.kind) {
|
|
1781
|
+
case "boolean": return "\"true\" or \"false\"";
|
|
1782
|
+
case "tristate": return "\"true\", \"false\", or \"mixed\"";
|
|
1783
|
+
case "number": return "a finite number";
|
|
1784
|
+
case "integer": {
|
|
1785
|
+
const parts = ["an integer"];
|
|
1786
|
+
if (isDefined(type.min) && isDefined(type.max)) parts.push(`between ${type.min} and ${type.max}`);
|
|
1787
|
+
else if (isDefined(type.min)) parts.push(`≥ ${type.min}`);
|
|
1788
|
+
else if (isDefined(type.max)) parts.push(`≤ ${type.max}`);
|
|
1789
|
+
return parts.join(" ");
|
|
1790
|
+
}
|
|
1791
|
+
case "enum": return [...type.values].map((v) => `"${v}"`).join(", ");
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
//#endregion
|
|
1795
|
+
//#region ../../lib/contract/src/aria/aria-policy-engine.ts
|
|
1796
|
+
const VALID_RESULT = [{ valid: true }];
|
|
1797
|
+
const NO_VARIANT_KEYS = /* @__PURE__ */ new Set();
|
|
1798
|
+
function isIntrinsicTag(tag) {
|
|
1799
|
+
return isString(tag);
|
|
1800
|
+
}
|
|
1801
|
+
function omitProp(obj, key) {
|
|
1802
|
+
const { [key]: _, ...rest } = obj;
|
|
1803
|
+
return rest;
|
|
1804
|
+
}
|
|
1668
1805
|
var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
1669
1806
|
#extraRules;
|
|
1670
1807
|
#variantKeys;
|
|
@@ -1797,73 +1934,20 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
1797
1934
|
else this.warn(d);
|
|
1798
1935
|
});
|
|
1799
1936
|
}
|
|
1800
|
-
static #createPlanKey(tag, props) {
|
|
1801
|
-
if (!isIntrinsicTag(tag)) return null;
|
|
1802
|
-
const parts = [tag];
|
|
1803
|
-
if (typeof props.role === "string") parts.push(`role:${props.role}`);
|
|
1804
|
-
if (tag === "input" && typeof props.type === "string") parts.push(`type:${props.type}`);
|
|
1805
|
-
if (tag === "img") parts.push(`alt:${props.alt === "" ? "empty" : "present"}`);
|
|
1806
|
-
const ariaEntries = [];
|
|
1807
|
-
iterate.forEachEntry(props, (k, v) => {
|
|
1808
|
-
if (!k.startsWith("aria-")) return;
|
|
1809
|
-
if (!isString(v) && !isNumber(v) && typeof v !== "boolean") return;
|
|
1810
|
-
ariaEntries.push(`${k}:${String(v)}`);
|
|
1811
|
-
});
|
|
1812
|
-
if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
|
|
1813
|
-
return parts.join("|");
|
|
1814
|
-
}
|
|
1815
|
-
static #extraRulesKeySuffix(extraRules, props) {
|
|
1816
|
-
const parts = [];
|
|
1817
|
-
for (const rule of extraRules) {
|
|
1818
|
-
const readsProps = rule.readsProps;
|
|
1819
|
-
if (!isNonNull(readsProps)) return null;
|
|
1820
|
-
for (const propKey of readsProps) {
|
|
1821
|
-
const v = props[propKey];
|
|
1822
|
-
if (v !== void 0 && !isString(v) && !isNumber(v) && typeof v !== "boolean") return null;
|
|
1823
|
-
parts.push(`x:${propKey}:${String(v)}`);
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
return parts.sort().join("|");
|
|
1827
|
-
}
|
|
1828
|
-
static #computePlan(inputProps, resultProps) {
|
|
1829
|
-
const removals = /* @__PURE__ */ new Set();
|
|
1830
|
-
const updates = {};
|
|
1831
|
-
iterate.forEachKey(inputProps, (key) => {
|
|
1832
|
-
if (!(key in resultProps)) removals.add(key);
|
|
1833
|
-
});
|
|
1834
|
-
iterate.forEachEntry(resultProps, (key, resultVal) => {
|
|
1835
|
-
if (inputProps[key] !== resultVal) updates[key] = resultVal;
|
|
1836
|
-
});
|
|
1837
|
-
return {
|
|
1838
|
-
removals,
|
|
1839
|
-
updates
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1842
|
-
static #applyPlan(props, removals, updates) {
|
|
1843
|
-
const hasRemovals = removals.size > 0;
|
|
1844
|
-
const hasUpdates = Object.keys(updates).length > 0;
|
|
1845
|
-
if (!hasRemovals && !hasUpdates) return props;
|
|
1846
|
-
const next = {};
|
|
1847
|
-
iterate.forEachEntry(props, (k, v) => {
|
|
1848
|
-
if (!removals.has(k)) next[k] = v;
|
|
1849
|
-
});
|
|
1850
|
-
Object.assign(next, updates);
|
|
1851
|
-
return next;
|
|
1852
|
-
}
|
|
1853
1937
|
validate(tag, props, extraProps) {
|
|
1854
1938
|
const ruleProps = extraProps ?? props;
|
|
1855
|
-
const baseKey =
|
|
1939
|
+
const baseKey = createPlanKey(tag, props);
|
|
1856
1940
|
let key = baseKey;
|
|
1857
1941
|
if (this.#extraRules.length > 0) {
|
|
1858
|
-
const suffix =
|
|
1942
|
+
const suffix = extraRulesKeySuffix(this.#extraRules, ruleProps);
|
|
1859
1943
|
key = isNonNull(baseKey) && isNonNull(suffix) ? `${baseKey}|${suffix}` : null;
|
|
1860
1944
|
}
|
|
1861
1945
|
if (!isNull(key)) {
|
|
1862
1946
|
const cached = this.#planCache.get(key);
|
|
1863
|
-
if (cached
|
|
1947
|
+
if (isDefined(cached)) {
|
|
1864
1948
|
if (cached.violations.length > 0) this.report(cached.violations);
|
|
1865
1949
|
return {
|
|
1866
|
-
props:
|
|
1950
|
+
props: applyPlan(props, cached.removals, cached.updates),
|
|
1867
1951
|
violations: cached.violations
|
|
1868
1952
|
};
|
|
1869
1953
|
}
|
|
@@ -1871,7 +1955,7 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
1871
1955
|
const result = this.#extraRules.length ? AriaPolicyEngine.#evaluateWithRules(tag, props, this.#extraRules, extraProps, this.#variantKeys) : AriaPolicyEngine.evaluate(tag, props);
|
|
1872
1956
|
if (result.violations.length > 0) this.report(result.violations);
|
|
1873
1957
|
if (!isNull(key)) {
|
|
1874
|
-
const { removals, updates } =
|
|
1958
|
+
const { removals, updates } = computePlan(props, result.props);
|
|
1875
1959
|
const plan = {
|
|
1876
1960
|
removals,
|
|
1877
1961
|
updates,
|
|
@@ -1962,7 +2046,7 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
1962
2046
|
];
|
|
1963
2047
|
static #checkInvalidRoleOverride({ tag, props, implicitRole }) {
|
|
1964
2048
|
const role = props.role;
|
|
1965
|
-
if (!implicitRole ||
|
|
2049
|
+
if (!implicitRole || isUndefined(role) || role === implicitRole) return VALID_RESULT;
|
|
1966
2050
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1967
2051
|
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1968
2052
|
return [{
|
|
@@ -1973,11 +2057,11 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
1973
2057
|
diagnostic
|
|
1974
2058
|
}];
|
|
1975
2059
|
}
|
|
1976
|
-
return
|
|
2060
|
+
return VALID_RESULT;
|
|
1977
2061
|
}
|
|
1978
2062
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1979
2063
|
const role = props.role;
|
|
1980
|
-
if (!implicitRole ||
|
|
2064
|
+
if (!implicitRole || isUndefined(role) || role !== implicitRole) return VALID_RESULT;
|
|
1981
2065
|
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1982
2066
|
return [{
|
|
1983
2067
|
valid: false,
|
|
@@ -1988,8 +2072,8 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
1988
2072
|
}];
|
|
1989
2073
|
}
|
|
1990
2074
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1991
|
-
if (props.role !== "region") return
|
|
1992
|
-
if (!hasStandaloneRole(tag)) return
|
|
2075
|
+
if (props.role !== "region") return VALID_RESULT;
|
|
2076
|
+
if (!hasStandaloneRole(tag)) return VALID_RESULT;
|
|
1993
2077
|
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1994
2078
|
return [{
|
|
1995
2079
|
valid: false,
|
|
@@ -2000,7 +2084,7 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2000
2084
|
}];
|
|
2001
2085
|
}
|
|
2002
2086
|
static #checkInvalidAriaAttributes({ tag, props, effectiveRole }) {
|
|
2003
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
2087
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return VALID_RESULT;
|
|
2004
2088
|
const results = [];
|
|
2005
2089
|
iterate.forEachEntry(props, (key) => {
|
|
2006
2090
|
if (!key.startsWith("aria-")) return;
|
|
@@ -2017,63 +2101,33 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2017
2101
|
});
|
|
2018
2102
|
return results;
|
|
2019
2103
|
}
|
|
2020
|
-
static #isValidAriaValue(value, type) {
|
|
2021
|
-
switch (type.kind) {
|
|
2022
|
-
case "boolean": return value === "true" || value === "false" || value === true || value === false;
|
|
2023
|
-
case "tristate": return value === "true" || value === "false" || value === "mixed" || value === true || value === false;
|
|
2024
|
-
case "number": return strictNumeric(value) !== void 0;
|
|
2025
|
-
case "integer": {
|
|
2026
|
-
const n = strictNumeric(value);
|
|
2027
|
-
if (n === void 0 || !Number.isInteger(n)) return false;
|
|
2028
|
-
if (type.min !== void 0 && n < type.min) return false;
|
|
2029
|
-
if (type.max !== void 0 && n > type.max) return false;
|
|
2030
|
-
return true;
|
|
2031
|
-
}
|
|
2032
|
-
case "enum": return typeof value === "string" && type.values.has(value);
|
|
2033
|
-
}
|
|
2034
|
-
}
|
|
2035
|
-
static #describeExpected(type) {
|
|
2036
|
-
switch (type.kind) {
|
|
2037
|
-
case "boolean": return "\"true\" or \"false\"";
|
|
2038
|
-
case "tristate": return "\"true\", \"false\", or \"mixed\"";
|
|
2039
|
-
case "number": return "a finite number";
|
|
2040
|
-
case "integer": {
|
|
2041
|
-
const parts = ["an integer"];
|
|
2042
|
-
if (type.min !== void 0 && type.max !== void 0) parts.push(`between ${type.min} and ${type.max}`);
|
|
2043
|
-
else if (type.min !== void 0) parts.push(`≥ ${type.min}`);
|
|
2044
|
-
else if (type.max !== void 0) parts.push(`≤ ${type.max}`);
|
|
2045
|
-
return parts.join(" ");
|
|
2046
|
-
}
|
|
2047
|
-
case "enum": return [...type.values].map((v) => `"${v}"`).join(", ");
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
2104
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
2051
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
2105
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return VALID_RESULT;
|
|
2052
2106
|
const results = [];
|
|
2053
2107
|
iterate.forEachEntry(props, (key, value) => {
|
|
2054
2108
|
if (!key.startsWith("aria-")) return;
|
|
2055
2109
|
const type = ARIA_VALUE_TYPES.get(key);
|
|
2056
2110
|
if (!isNonNull(type)) return;
|
|
2057
|
-
if (
|
|
2111
|
+
if (isValidAriaValue(value, type)) return;
|
|
2058
2112
|
results.push({
|
|
2059
2113
|
valid: false,
|
|
2060
2114
|
fixable: true,
|
|
2061
2115
|
severity: "warning",
|
|
2062
2116
|
attribute: key,
|
|
2063
|
-
diagnostic: AriaDiagnostics.invalidAttributeValue(key, value,
|
|
2117
|
+
diagnostic: AriaDiagnostics.invalidAttributeValue(key, value, describeExpected(type)),
|
|
2064
2118
|
fix: AriaPolicyEngine.#makeRemoveAttributeFix(key)
|
|
2065
2119
|
});
|
|
2066
2120
|
});
|
|
2067
2121
|
return results;
|
|
2068
2122
|
}
|
|
2069
2123
|
static #checkRedundantAriaLevel({ tag, props, effectiveRole }) {
|
|
2070
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
2124
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return VALID_RESULT;
|
|
2071
2125
|
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
2072
|
-
if (!isNonNull(implicitLevel)) return
|
|
2126
|
+
if (!isNonNull(implicitLevel)) return VALID_RESULT;
|
|
2073
2127
|
const raw = props["aria-level"];
|
|
2074
|
-
if (!isNonNull(raw)) return
|
|
2128
|
+
if (!isNonNull(raw)) return VALID_RESULT;
|
|
2075
2129
|
const n = strictNumeric(raw);
|
|
2076
|
-
if (n === void 0 || n !== implicitLevel) return
|
|
2130
|
+
if (n === void 0 || n !== implicitLevel) return VALID_RESULT;
|
|
2077
2131
|
return [{
|
|
2078
2132
|
valid: false,
|
|
2079
2133
|
fixable: true,
|
|
@@ -2084,7 +2138,7 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2084
2138
|
}];
|
|
2085
2139
|
}
|
|
2086
2140
|
static #checkNameProhibitedRoles({ props, effectiveRole }) {
|
|
2087
|
-
if (!effectiveRole || effectiveRole === "none" || effectiveRole === "presentation" || !NAME_PROHIBITED_ROLES.has(effectiveRole)) return
|
|
2141
|
+
if (!effectiveRole || effectiveRole === "none" || effectiveRole === "presentation" || !NAME_PROHIBITED_ROLES.has(effectiveRole)) return VALID_RESULT;
|
|
2088
2142
|
const results = [];
|
|
2089
2143
|
for (const key of NAME_PROHIBITED_ATTRIBUTES) {
|
|
2090
2144
|
if (!(key in props)) continue;
|
|
@@ -2100,9 +2154,9 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2100
2154
|
return results;
|
|
2101
2155
|
}
|
|
2102
2156
|
static #checkNameRequiredRoles({ tag, props, effectiveRole }) {
|
|
2103
|
-
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return
|
|
2104
|
-
if ("aria-label" in props || "aria-labelledby" in props) return
|
|
2105
|
-
if (tag === "img" &&
|
|
2157
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID_RESULT;
|
|
2158
|
+
if ("aria-label" in props || "aria-labelledby" in props) return VALID_RESULT;
|
|
2159
|
+
if (tag === "img" && isString(props.alt) && props.alt.length > 0) return VALID_RESULT;
|
|
2106
2160
|
return [{
|
|
2107
2161
|
valid: false,
|
|
2108
2162
|
fixable: false,
|
|
@@ -2115,12 +2169,12 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2115
2169
|
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
2116
2170
|
};
|
|
2117
2171
|
static #checkRequiredAriaProperties(context) {
|
|
2118
|
-
if (!isNonNull(context.props.role)) return
|
|
2172
|
+
if (!isNonNull(context.props.role)) return VALID_RESULT;
|
|
2119
2173
|
return checkRequiredAttributes(AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
2120
2174
|
}
|
|
2121
2175
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
2122
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
2123
|
-
if (!isPotentiallyFocusable(tag, props)) return
|
|
2176
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID_RESULT;
|
|
2177
|
+
if (!isPotentiallyFocusable(tag, props)) return VALID_RESULT;
|
|
2124
2178
|
return [{
|
|
2125
2179
|
valid: false,
|
|
2126
2180
|
fixable: false,
|
|
@@ -2130,7 +2184,7 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2130
2184
|
}];
|
|
2131
2185
|
}
|
|
2132
2186
|
static #checkPresentationalAriaAttributes({ tag, props, effectiveRole }) {
|
|
2133
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
2187
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID_RESULT;
|
|
2134
2188
|
const results = [];
|
|
2135
2189
|
iterate.forEachEntry(props, (key) => {
|
|
2136
2190
|
if (!key.startsWith("aria-")) return;
|
|
@@ -2147,10 +2201,10 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2147
2201
|
return results;
|
|
2148
2202
|
}
|
|
2149
2203
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
2150
|
-
if (!effectiveRole) return
|
|
2204
|
+
if (!effectiveRole) return VALID_RESULT;
|
|
2151
2205
|
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
2152
|
-
if (!impliedLive) return
|
|
2153
|
-
if ("aria-live" in props) return
|
|
2206
|
+
if (!impliedLive) return VALID_RESULT;
|
|
2207
|
+
if ("aria-live" in props) return VALID_RESULT;
|
|
2154
2208
|
return [{
|
|
2155
2209
|
valid: false,
|
|
2156
2210
|
fixable: true,
|
|
@@ -2190,8 +2244,8 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2190
2244
|
};
|
|
2191
2245
|
static #checkInvalidAriaRelevant({ props }) {
|
|
2192
2246
|
const relevant = props["aria-relevant"];
|
|
2193
|
-
if (relevant
|
|
2194
|
-
if (
|
|
2247
|
+
if (isUndefined(relevant)) return VALID_RESULT;
|
|
2248
|
+
if (!isString(relevant)) return VALID_RESULT;
|
|
2195
2249
|
const tokens = relevant.trim().split(/\s+/);
|
|
2196
2250
|
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
2197
2251
|
if (invalid.length > 0) return [{
|
|
@@ -2210,7 +2264,7 @@ var AriaPolicyEngine = class AriaPolicyEngine extends InvariantBase {
|
|
|
2210
2264
|
diagnostic: AriaDiagnostics.relevantSuperseded(),
|
|
2211
2265
|
fix: AriaPolicyEngine.#normalizeRelevantAllFix
|
|
2212
2266
|
}];
|
|
2213
|
-
return
|
|
2267
|
+
return VALID_RESULT;
|
|
2214
2268
|
}
|
|
2215
2269
|
};
|
|
2216
2270
|
//#endregion
|
|
@@ -4474,9 +4528,18 @@ function buildRuntime(options) {
|
|
|
4474
4528
|
* the same way on a plain bundle as on a component function/class, so `Card.Header` is itself
|
|
4475
4529
|
* just another bundle, passed to its own `<Polymorphic bundle={Card.Header}>`. Pass `onElement`
|
|
4476
4530
|
* to run setup once the real DOM element exists.
|
|
4531
|
+
*
|
|
4532
|
+
* `TDefault`/`Props`/`Variants`/`TPreset` are each `ContractXOf<C>`-derived *defaults* on this
|
|
4533
|
+
* function's own type parameter list, mirroring every other adapter's identical fix — see
|
|
4534
|
+
* `@praxis-kit/react`'s own doc comment for why. The biggest reduction of any adapter (7 generics
|
|
4535
|
+
* down to `C` + `TSubComponents`): the old `TOptions extends WithChildRules` parameter — needed by
|
|
4536
|
+
* `BuiltRuntime<G, TOptions>` to conditionally include `childrenEvaluator` based on the *literal*
|
|
4537
|
+
* `enforcement.children`/`exclusiveChildren`/`allowText` shape — was never anything but a
|
|
4538
|
+
* self-referential reconstruction of the same options type `C` now *is* directly, so it's simply
|
|
4539
|
+
* `C` itself below, not a separate parameter.
|
|
4477
4540
|
*/
|
|
4478
4541
|
function createContractComponent(options) {
|
|
4479
4542
|
return assembleCompoundComponent(buildRuntime(options), options.subComponents);
|
|
4480
4543
|
}
|
|
4481
4544
|
//#endregion
|
|
4482
|
-
export { createContractComponent
|
|
4545
|
+
export { createContractComponent };
|