praxis-kit 3.1.1 → 4.0.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/README.md +141 -126
- package/dist/chunk-TJRHF6MS.js +2777 -0
- package/dist/codemod/index.js +20 -20
- package/dist/contract/index.d.ts +2 -258
- package/dist/contract/index.js +158 -1190
- package/dist/eslint/index.js +403 -178
- package/dist/lit/index.d.ts +5 -235
- package/dist/lit/index.js +1384 -747
- package/dist/merge-refs-DUuHyTRO.d.ts +144 -0
- package/dist/preact/index.d.ts +9 -254
- package/dist/preact/index.js +1686 -1025
- package/dist/react/index.d.ts +23 -11
- package/dist/react/index.js +115 -116
- package/dist/react/legacy.d.ts +7 -11
- package/dist/react/legacy.js +116 -136
- package/dist/solid/index.d.ts +5 -250
- package/dist/solid/index.js +1476 -706
- package/dist/svelte/index.d.ts +5 -325
- package/dist/svelte/index.js +1407 -725
- package/dist/tailwind/index.d.ts +5 -4
- package/dist/tailwind/index.js +366 -90
- package/dist/ts-plugin/index.cjs +5 -5
- package/dist/vite-plugin/index.d.ts +91 -120
- package/dist/vite-plugin/index.js +568 -347
- package/dist/vue/index.d.ts +12 -257
- package/dist/vue/index.js +1701 -926
- package/dist/web/index.d.ts +9 -238
- package/dist/web/index.js +1369 -732
- package/package.json +4 -3
- package/dist/chunk-6RJN5B6L.js +0 -2221
- package/dist/merge-refs-BKyE8h8M.d.ts +0 -381
|
@@ -0,0 +1,2777 @@
|
|
|
1
|
+
// ../../lib/adapter-utils/src/define-component.ts
|
|
2
|
+
function defineContractComponent(options) {
|
|
3
|
+
return (factory) => factory(options);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// ../../lib/adapter-utils/src/invariant.ts
|
|
7
|
+
function panic(message) {
|
|
8
|
+
throw new Error(message);
|
|
9
|
+
}
|
|
10
|
+
function invariant(condition, message) {
|
|
11
|
+
if (!condition) panic(message);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ../../lib/primitive/src/utils/is-object.ts
|
|
15
|
+
function isObject(value, excludeArrays = false) {
|
|
16
|
+
if (value === null || typeof value !== "object") return false;
|
|
17
|
+
return excludeArrays ? !Array.isArray(value) : true;
|
|
18
|
+
}
|
|
19
|
+
function isString(value) {
|
|
20
|
+
return typeof value === "string";
|
|
21
|
+
}
|
|
22
|
+
function isNumber(value) {
|
|
23
|
+
return typeof value === "number";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ../../lib/primitive/src/merge/constants.ts
|
|
27
|
+
var EVENT_HANDLER_RE = /^on[A-Z]/;
|
|
28
|
+
|
|
29
|
+
// ../../lib/primitive/src/merge/predicates.ts
|
|
30
|
+
function isFunction(value) {
|
|
31
|
+
return typeof value === "function";
|
|
32
|
+
}
|
|
33
|
+
function isPlainObject(value) {
|
|
34
|
+
if (!isObject(value)) return false;
|
|
35
|
+
const proto = Object.getPrototypeOf(value);
|
|
36
|
+
return proto === Object.prototype || proto === null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ../../lib/primitive/src/utils/assert-never.ts
|
|
40
|
+
function assertNever(value) {
|
|
41
|
+
throw new Error(`Unexpected value: ${String(value)}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ../../lib/primitive/src/utils/iterate.ts
|
|
45
|
+
function find(iterable, callback) {
|
|
46
|
+
for (const value of iterable) {
|
|
47
|
+
const result = callback(value);
|
|
48
|
+
if (result != null) {
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function some(iterable, predicate) {
|
|
55
|
+
for (const value of iterable) {
|
|
56
|
+
if (predicate(value)) return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
function every(iterable, predicate) {
|
|
61
|
+
let index = 0;
|
|
62
|
+
for (const value of iterable) {
|
|
63
|
+
if (!predicate(value, index++)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
function* filter(iterable, predicate) {
|
|
70
|
+
let index = 0;
|
|
71
|
+
for (const value of iterable) {
|
|
72
|
+
if (predicate(value, index++)) {
|
|
73
|
+
yield value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function* map(iterable, callback) {
|
|
78
|
+
let index = 0;
|
|
79
|
+
for (const value of iterable) {
|
|
80
|
+
yield callback(value, index++);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function forEach(iterable, callback) {
|
|
84
|
+
let index = 0;
|
|
85
|
+
for (const value of iterable) {
|
|
86
|
+
callback(value, index++);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function reduce(iterable, initial, callback) {
|
|
90
|
+
let accumulator = initial;
|
|
91
|
+
let index = 0;
|
|
92
|
+
for (const value of iterable) {
|
|
93
|
+
accumulator = callback(accumulator, value, index++);
|
|
94
|
+
}
|
|
95
|
+
return accumulator;
|
|
96
|
+
}
|
|
97
|
+
function collect(iterable, callback) {
|
|
98
|
+
const result = {};
|
|
99
|
+
let index = 0;
|
|
100
|
+
for (const value of iterable) {
|
|
101
|
+
const entry = callback(value, index++);
|
|
102
|
+
if (entry === null) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
result[entry[0]] = entry[1];
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function findLast(value, callback) {
|
|
110
|
+
for (let index = value.length - 1; index >= 0; index--) {
|
|
111
|
+
const result = callback(value[index], index);
|
|
112
|
+
if (result != null) {
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
function* items(collection) {
|
|
119
|
+
for (let i = 0; i < collection.length; i++) {
|
|
120
|
+
const item = collection.item(i);
|
|
121
|
+
if (item !== null) {
|
|
122
|
+
yield item;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function nodeList(list) {
|
|
127
|
+
return {
|
|
128
|
+
*[Symbol.iterator]() {
|
|
129
|
+
for (let i = 0; i < list.length; i++) {
|
|
130
|
+
const node = list.item(i);
|
|
131
|
+
if (node !== null) {
|
|
132
|
+
yield node;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function mapEntries(m) {
|
|
139
|
+
return m.entries();
|
|
140
|
+
}
|
|
141
|
+
function set(s) {
|
|
142
|
+
return s.values();
|
|
143
|
+
}
|
|
144
|
+
function hasOwn(object, key) {
|
|
145
|
+
return Object.hasOwn(object, key);
|
|
146
|
+
}
|
|
147
|
+
function* entries(object) {
|
|
148
|
+
for (const key in object) {
|
|
149
|
+
if (!hasOwn(object, key)) continue;
|
|
150
|
+
yield [key, object[key]];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function* keys(object) {
|
|
154
|
+
for (const [key] of entries(object)) {
|
|
155
|
+
yield key;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function* values(object) {
|
|
159
|
+
for (const [, value] of entries(object)) {
|
|
160
|
+
yield value;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function mapValues(object, callback) {
|
|
164
|
+
const result = {};
|
|
165
|
+
for (const [key, value] of entries(object)) {
|
|
166
|
+
result[key] = callback(value, key);
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
function forEachEntry(object, callback) {
|
|
171
|
+
for (const [key, value] of entries(object)) {
|
|
172
|
+
callback(key, value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function forEachKey(object, callback) {
|
|
176
|
+
for (const key of keys(object)) {
|
|
177
|
+
callback(key);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function forEachValue(object, callback) {
|
|
181
|
+
for (const value of values(object)) {
|
|
182
|
+
callback(value);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function forEachSet(s, callback) {
|
|
186
|
+
for (const value of s) {
|
|
187
|
+
callback(value);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
var iterate = Object.freeze({
|
|
191
|
+
entries,
|
|
192
|
+
filter,
|
|
193
|
+
find,
|
|
194
|
+
findLast,
|
|
195
|
+
forEach,
|
|
196
|
+
forEachEntry,
|
|
197
|
+
forEachKey,
|
|
198
|
+
forEachSet,
|
|
199
|
+
forEachValue,
|
|
200
|
+
items,
|
|
201
|
+
keys,
|
|
202
|
+
map,
|
|
203
|
+
mapEntries,
|
|
204
|
+
mapValues,
|
|
205
|
+
nodeList,
|
|
206
|
+
reduce,
|
|
207
|
+
collect,
|
|
208
|
+
set,
|
|
209
|
+
some,
|
|
210
|
+
every,
|
|
211
|
+
values
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// ../../lib/primitive/src/utils/merge-refs.ts
|
|
215
|
+
function mergeRefsCore(...refs) {
|
|
216
|
+
const active = refs.filter((r) => r != null);
|
|
217
|
+
if (active.length === 0) return null;
|
|
218
|
+
if (active.length === 1) return active[0];
|
|
219
|
+
return (value) => {
|
|
220
|
+
iterate.forEach(active, (ref) => {
|
|
221
|
+
if (typeof ref === "function") {
|
|
222
|
+
ref(value);
|
|
223
|
+
} else {
|
|
224
|
+
ref.current = value;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ../../lib/primitive/src/constants/aria/global-aria-attributes.ts
|
|
231
|
+
var GLOBAL_ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
232
|
+
"aria-atomic",
|
|
233
|
+
"aria-busy",
|
|
234
|
+
"aria-controls",
|
|
235
|
+
"aria-current",
|
|
236
|
+
"aria-describedby",
|
|
237
|
+
"aria-details",
|
|
238
|
+
"aria-disabled",
|
|
239
|
+
"aria-errormessage",
|
|
240
|
+
"aria-flowto",
|
|
241
|
+
"aria-hidden",
|
|
242
|
+
"aria-keyshortcuts",
|
|
243
|
+
"aria-label",
|
|
244
|
+
"aria-labelledby",
|
|
245
|
+
"aria-live",
|
|
246
|
+
"aria-owns",
|
|
247
|
+
"aria-relevant",
|
|
248
|
+
"aria-roledescription"
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
// ../../lib/primitive/src/constants/aria/implicit-role-record.ts
|
|
252
|
+
var IMPLICIT_ROLE_RECORD = {
|
|
253
|
+
// Landmarks
|
|
254
|
+
article: "article",
|
|
255
|
+
aside: "complementary",
|
|
256
|
+
footer: "contentinfo",
|
|
257
|
+
header: "banner",
|
|
258
|
+
main: "main",
|
|
259
|
+
nav: "navigation",
|
|
260
|
+
// Interactive
|
|
261
|
+
a: "link",
|
|
262
|
+
button: "button",
|
|
263
|
+
select: "listbox",
|
|
264
|
+
textarea: "textbox",
|
|
265
|
+
// Headings
|
|
266
|
+
h1: "heading",
|
|
267
|
+
h2: "heading",
|
|
268
|
+
h3: "heading",
|
|
269
|
+
h4: "heading",
|
|
270
|
+
h5: "heading",
|
|
271
|
+
h6: "heading",
|
|
272
|
+
// Lists
|
|
273
|
+
ul: "list",
|
|
274
|
+
ol: "list",
|
|
275
|
+
li: "listitem",
|
|
276
|
+
// Tables
|
|
277
|
+
table: "table",
|
|
278
|
+
tr: "row",
|
|
279
|
+
td: "cell",
|
|
280
|
+
th: "columnheader",
|
|
281
|
+
// Structural / semantic
|
|
282
|
+
dialog: "dialog",
|
|
283
|
+
fieldset: "group",
|
|
284
|
+
figure: "figure",
|
|
285
|
+
meter: "meter",
|
|
286
|
+
output: "status",
|
|
287
|
+
progress: "progressbar"
|
|
288
|
+
};
|
|
289
|
+
var INPUT_TYPE_ROLE_MAP = {
|
|
290
|
+
checkbox: "checkbox",
|
|
291
|
+
radio: "radio",
|
|
292
|
+
range: "slider",
|
|
293
|
+
number: "spinbutton",
|
|
294
|
+
search: "searchbox",
|
|
295
|
+
text: "textbox",
|
|
296
|
+
email: "textbox",
|
|
297
|
+
tel: "textbox",
|
|
298
|
+
url: "textbox",
|
|
299
|
+
button: "button",
|
|
300
|
+
submit: "button",
|
|
301
|
+
reset: "button",
|
|
302
|
+
image: "button"
|
|
303
|
+
};
|
|
304
|
+
var STRONG_ROLES = [
|
|
305
|
+
"main",
|
|
306
|
+
"navigation",
|
|
307
|
+
"complementary",
|
|
308
|
+
"contentinfo",
|
|
309
|
+
"banner"
|
|
310
|
+
];
|
|
311
|
+
var STANDALONE_ROLES = ["article"];
|
|
312
|
+
var STRONG_ROLES_SET = new Set(STRONG_ROLES);
|
|
313
|
+
var STANDALONE_ROLES_SET = new Set(STANDALONE_ROLES);
|
|
314
|
+
|
|
315
|
+
// ../../lib/primitive/src/constants/aria/role-restricted-attributes.ts
|
|
316
|
+
var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
|
|
317
|
+
[
|
|
318
|
+
"aria-activedescendant",
|
|
319
|
+
/* @__PURE__ */ new Set([
|
|
320
|
+
"application",
|
|
321
|
+
"combobox",
|
|
322
|
+
"grid",
|
|
323
|
+
"group",
|
|
324
|
+
"listbox",
|
|
325
|
+
"menu",
|
|
326
|
+
"menubar",
|
|
327
|
+
"radiogroup",
|
|
328
|
+
"spinbutton",
|
|
329
|
+
"tablist",
|
|
330
|
+
"toolbar",
|
|
331
|
+
"textbox",
|
|
332
|
+
"tree",
|
|
333
|
+
"treegrid"
|
|
334
|
+
])
|
|
335
|
+
],
|
|
336
|
+
["aria-autocomplete", /* @__PURE__ */ new Set(["combobox", "searchbox", "textbox"])],
|
|
337
|
+
[
|
|
338
|
+
"aria-checked",
|
|
339
|
+
/* @__PURE__ */ new Set(["checkbox", "menuitemcheckbox", "option", "radio", "switch", "treeitem"])
|
|
340
|
+
],
|
|
341
|
+
["aria-colcount", /* @__PURE__ */ new Set(["grid", "table", "treegrid"])],
|
|
342
|
+
["aria-colindex", /* @__PURE__ */ new Set(["cell", "columnheader", "gridcell", "row", "rowheader"])],
|
|
343
|
+
["aria-colspan", /* @__PURE__ */ new Set(["cell", "columnheader", "gridcell", "rowheader"])],
|
|
344
|
+
[
|
|
345
|
+
"aria-expanded",
|
|
346
|
+
/* @__PURE__ */ new Set([
|
|
347
|
+
"button",
|
|
348
|
+
"combobox",
|
|
349
|
+
"gridcell",
|
|
350
|
+
"listbox",
|
|
351
|
+
"menuitem",
|
|
352
|
+
"menuitemcheckbox",
|
|
353
|
+
"menuitemradio",
|
|
354
|
+
"row",
|
|
355
|
+
"rowheader",
|
|
356
|
+
"tab",
|
|
357
|
+
"treeitem"
|
|
358
|
+
])
|
|
359
|
+
],
|
|
360
|
+
[
|
|
361
|
+
"aria-haspopup",
|
|
362
|
+
/* @__PURE__ */ new Set([
|
|
363
|
+
"button",
|
|
364
|
+
"combobox",
|
|
365
|
+
"gridcell",
|
|
366
|
+
"listbox",
|
|
367
|
+
"menuitem",
|
|
368
|
+
"menuitemcheckbox",
|
|
369
|
+
"menuitemradio",
|
|
370
|
+
"tab",
|
|
371
|
+
"treeitem"
|
|
372
|
+
])
|
|
373
|
+
],
|
|
374
|
+
["aria-level", /* @__PURE__ */ new Set(["heading", "listitem", "row", "treeitem"])],
|
|
375
|
+
["aria-modal", /* @__PURE__ */ new Set(["alertdialog", "dialog"])],
|
|
376
|
+
["aria-multiline", /* @__PURE__ */ new Set(["textbox"])],
|
|
377
|
+
["aria-multiselectable", /* @__PURE__ */ new Set(["grid", "listbox", "tablist", "tree", "treegrid"])],
|
|
378
|
+
[
|
|
379
|
+
"aria-orientation",
|
|
380
|
+
/* @__PURE__ */ new Set(["scrollbar", "select", "separator", "slider", "tablist", "toolbar", "tree"])
|
|
381
|
+
],
|
|
382
|
+
["aria-placeholder", /* @__PURE__ */ new Set(["searchbox", "textbox"])],
|
|
383
|
+
[
|
|
384
|
+
"aria-posinset",
|
|
385
|
+
/* @__PURE__ */ new Set([
|
|
386
|
+
"article",
|
|
387
|
+
"listitem",
|
|
388
|
+
"menuitem",
|
|
389
|
+
"menuitemcheckbox",
|
|
390
|
+
"menuitemradio",
|
|
391
|
+
"option",
|
|
392
|
+
"radio",
|
|
393
|
+
"row",
|
|
394
|
+
"tab"
|
|
395
|
+
])
|
|
396
|
+
],
|
|
397
|
+
["aria-pressed", /* @__PURE__ */ new Set(["button"])],
|
|
398
|
+
[
|
|
399
|
+
"aria-readonly",
|
|
400
|
+
/* @__PURE__ */ new Set([
|
|
401
|
+
"combobox",
|
|
402
|
+
"grid",
|
|
403
|
+
"gridcell",
|
|
404
|
+
"listbox",
|
|
405
|
+
"radiogroup",
|
|
406
|
+
"slider",
|
|
407
|
+
"spinbutton",
|
|
408
|
+
"textbox",
|
|
409
|
+
"tree",
|
|
410
|
+
"treegrid"
|
|
411
|
+
])
|
|
412
|
+
],
|
|
413
|
+
[
|
|
414
|
+
"aria-required",
|
|
415
|
+
/* @__PURE__ */ new Set([
|
|
416
|
+
"combobox",
|
|
417
|
+
"gridcell",
|
|
418
|
+
"listbox",
|
|
419
|
+
"radiogroup",
|
|
420
|
+
"spinbutton",
|
|
421
|
+
"textbox",
|
|
422
|
+
"tree",
|
|
423
|
+
"treegrid"
|
|
424
|
+
])
|
|
425
|
+
],
|
|
426
|
+
["aria-rowcount", /* @__PURE__ */ new Set(["grid", "table", "treegrid"])],
|
|
427
|
+
["aria-rowindex", /* @__PURE__ */ new Set(["cell", "columnheader", "gridcell", "row", "rowheader"])],
|
|
428
|
+
["aria-rowspan", /* @__PURE__ */ new Set(["cell", "columnheader", "gridcell", "rowheader"])],
|
|
429
|
+
[
|
|
430
|
+
"aria-selected",
|
|
431
|
+
/* @__PURE__ */ new Set(["columnheader", "gridcell", "option", "row", "rowheader", "tab", "treeitem"])
|
|
432
|
+
],
|
|
433
|
+
[
|
|
434
|
+
"aria-setsize",
|
|
435
|
+
/* @__PURE__ */ new Set([
|
|
436
|
+
"article",
|
|
437
|
+
"listitem",
|
|
438
|
+
"menuitem",
|
|
439
|
+
"menuitemcheckbox",
|
|
440
|
+
"menuitemradio",
|
|
441
|
+
"option",
|
|
442
|
+
"radio",
|
|
443
|
+
"row",
|
|
444
|
+
"tab"
|
|
445
|
+
])
|
|
446
|
+
],
|
|
447
|
+
["aria-sort", /* @__PURE__ */ new Set(["columnheader", "rowheader"])],
|
|
448
|
+
[
|
|
449
|
+
"aria-valuemax",
|
|
450
|
+
/* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
|
|
451
|
+
],
|
|
452
|
+
[
|
|
453
|
+
"aria-valuemin",
|
|
454
|
+
/* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
|
|
455
|
+
],
|
|
456
|
+
[
|
|
457
|
+
"aria-valuenow",
|
|
458
|
+
/* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
|
|
459
|
+
],
|
|
460
|
+
[
|
|
461
|
+
"aria-valuetext",
|
|
462
|
+
/* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
|
|
463
|
+
]
|
|
464
|
+
]);
|
|
465
|
+
|
|
466
|
+
// ../../lib/primitive/src/constants/primitive/slot-name.ts
|
|
467
|
+
var SLOT_NAME = "Slot";
|
|
468
|
+
|
|
469
|
+
// ../../lib/primitive/src/guards/foundational/is-defined.ts
|
|
470
|
+
function isUndefined(value) {
|
|
471
|
+
return value === void 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ../../lib/primitive/src/guards/foundational/is-null.ts
|
|
475
|
+
function isNull(value) {
|
|
476
|
+
return value === null;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ../../lib/primitive/src/guards/aria/is-aria-attribute.ts
|
|
480
|
+
function isGlobalAriaAttribute(attr) {
|
|
481
|
+
return GLOBAL_ARIA_ATTRIBUTES.has(attr);
|
|
482
|
+
}
|
|
483
|
+
function isAriaAttributeValidForRole(attr, role) {
|
|
484
|
+
const allowedRoles = ROLE_RESTRICTED_ATTRIBUTES.get(attr);
|
|
485
|
+
if (isUndefined(allowedRoles)) return true;
|
|
486
|
+
if (isUndefined(role)) return false;
|
|
487
|
+
return allowedRoles.has(role);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ../../lib/primitive/src/guards/aria/is-aria-role.ts
|
|
491
|
+
function isStrongImplicitRole(tag) {
|
|
492
|
+
if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
|
|
493
|
+
return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
|
|
494
|
+
}
|
|
495
|
+
function isStandaloneTag(tag) {
|
|
496
|
+
if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
|
|
497
|
+
return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
|
|
498
|
+
}
|
|
499
|
+
function getInputImplicitRole(type) {
|
|
500
|
+
if (type == null || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
|
|
501
|
+
return INPUT_TYPE_ROLE_MAP[type];
|
|
502
|
+
}
|
|
503
|
+
function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
|
|
504
|
+
const isNamed = typeof ariaLabel === "string" || typeof ariaLabelledBy === "string";
|
|
505
|
+
if (!isNamed) return void 0;
|
|
506
|
+
if (tag === "section") return "region";
|
|
507
|
+
if (tag === "form") return "form";
|
|
508
|
+
return void 0;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ../../lib/primitive/src/guards/children/component-id.ts
|
|
512
|
+
var COMPONENT_ID = /* @__PURE__ */ Symbol.for("praxis.component-id");
|
|
513
|
+
var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
|
|
514
|
+
|
|
515
|
+
// ../../lib/primitive/src/guards/children/is-tag.ts
|
|
516
|
+
function getAsProp(child) {
|
|
517
|
+
if (!isObject(child) || !("props" in child)) return void 0;
|
|
518
|
+
const props = child.props;
|
|
519
|
+
if (!isObject(props)) return void 0;
|
|
520
|
+
const as = props.as;
|
|
521
|
+
return isString(as) && as !== "" ? as : void 0;
|
|
522
|
+
}
|
|
523
|
+
function getTag(child) {
|
|
524
|
+
if (!isObject(child) || !("type" in child)) return void 0;
|
|
525
|
+
const t = child.type;
|
|
526
|
+
if (isString(t)) return t;
|
|
527
|
+
if (typeof t === "function" || isObject(t)) {
|
|
528
|
+
const defaultTag = t[COMPONENT_DEFAULT_TAG];
|
|
529
|
+
if (!isString(defaultTag)) return void 0;
|
|
530
|
+
return getAsProp(child) ?? defaultTag;
|
|
531
|
+
}
|
|
532
|
+
return void 0;
|
|
533
|
+
}
|
|
534
|
+
function isTag(...args) {
|
|
535
|
+
if (isString(args[0])) {
|
|
536
|
+
const set3 = new Set(args);
|
|
537
|
+
return (child2) => {
|
|
538
|
+
const tag2 = getTag(child2);
|
|
539
|
+
return tag2 !== void 0 && set3.has(tag2);
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
const [child, ...tags] = args;
|
|
543
|
+
const set2 = new Set(tags);
|
|
544
|
+
const tag = getTag(child);
|
|
545
|
+
return tag !== void 0 && set2.has(tag);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ../../lib/contract/src/aria/aria-role-policy.ts
|
|
549
|
+
function getImplicitRole(tag, props) {
|
|
550
|
+
if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
|
|
551
|
+
if (tag === "input") return getInputImplicitRole(props?.type);
|
|
552
|
+
if (tag === "img") return props?.alt === "" ? "none" : "img";
|
|
553
|
+
if (tag === "section" || tag === "form") {
|
|
554
|
+
return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
|
|
555
|
+
}
|
|
556
|
+
return void 0;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// ../../lib/contract/src/strict/invariant-base.ts
|
|
560
|
+
var InvariantBase = class {
|
|
561
|
+
diagnostics;
|
|
562
|
+
constructor(diagnostics) {
|
|
563
|
+
this.diagnostics = diagnostics;
|
|
564
|
+
}
|
|
565
|
+
get active() {
|
|
566
|
+
return this.diagnostics.active;
|
|
567
|
+
}
|
|
568
|
+
violate(input) {
|
|
569
|
+
this.diagnostics.error(input);
|
|
570
|
+
}
|
|
571
|
+
// Always caps at warn — never throws. ARIA 'warning' violations route here
|
|
572
|
+
// so they surface even in strict='throw' mode without aborting a render.
|
|
573
|
+
warn(input) {
|
|
574
|
+
this.diagnostics.warn(input);
|
|
575
|
+
}
|
|
576
|
+
invariant(condition, input) {
|
|
577
|
+
if (!condition) this.violate(input);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// ../../lib/diagnostics/src/category.ts
|
|
582
|
+
var DiagnosticCategory = /* @__PURE__ */ ((DiagnosticCategory2) => {
|
|
583
|
+
DiagnosticCategory2[DiagnosticCategory2["Contract"] = 0] = "Contract";
|
|
584
|
+
DiagnosticCategory2[DiagnosticCategory2["HTML"] = 1] = "HTML";
|
|
585
|
+
DiagnosticCategory2[DiagnosticCategory2["ARIA"] = 2] = "ARIA";
|
|
586
|
+
DiagnosticCategory2[DiagnosticCategory2["Composition"] = 3] = "Composition";
|
|
587
|
+
DiagnosticCategory2[DiagnosticCategory2["Rendering"] = 4] = "Rendering";
|
|
588
|
+
DiagnosticCategory2[DiagnosticCategory2["Accessibility"] = 5] = "Accessibility";
|
|
589
|
+
DiagnosticCategory2[DiagnosticCategory2["Performance"] = 6] = "Performance";
|
|
590
|
+
DiagnosticCategory2[DiagnosticCategory2["Internal"] = 7] = "Internal";
|
|
591
|
+
DiagnosticCategory2[DiagnosticCategory2["Deprecation"] = 8] = "Deprecation";
|
|
592
|
+
DiagnosticCategory2[DiagnosticCategory2["Lint"] = 9] = "Lint";
|
|
593
|
+
return DiagnosticCategory2;
|
|
594
|
+
})(DiagnosticCategory || {});
|
|
595
|
+
|
|
596
|
+
// ../../lib/diagnostics/src/error.ts
|
|
597
|
+
var PraxisError = class extends Error {
|
|
598
|
+
diagnostic;
|
|
599
|
+
constructor(diagnostic) {
|
|
600
|
+
super(diagnostic.message);
|
|
601
|
+
this.name = "PraxisError";
|
|
602
|
+
this.diagnostic = diagnostic;
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// ../../lib/diagnostics/src/severity.ts
|
|
607
|
+
var Severity = /* @__PURE__ */ ((Severity2) => {
|
|
608
|
+
Severity2[Severity2["Debug"] = 0] = "Debug";
|
|
609
|
+
Severity2[Severity2["Info"] = 1] = "Info";
|
|
610
|
+
Severity2[Severity2["Warning"] = 2] = "Warning";
|
|
611
|
+
Severity2[Severity2["Error"] = 3] = "Error";
|
|
612
|
+
Severity2[Severity2["Fatal"] = 4] = "Fatal";
|
|
613
|
+
return Severity2;
|
|
614
|
+
})(Severity || {});
|
|
615
|
+
|
|
616
|
+
// ../../lib/diagnostics/src/policy.ts
|
|
617
|
+
var DefaultPolicy = class {
|
|
618
|
+
reportThreshold;
|
|
619
|
+
throwThreshold;
|
|
620
|
+
constructor({
|
|
621
|
+
reportThreshold = 1 /* Info */,
|
|
622
|
+
throwThreshold = 4 /* Fatal */
|
|
623
|
+
} = {}) {
|
|
624
|
+
this.reportThreshold = reportThreshold;
|
|
625
|
+
this.throwThreshold = throwThreshold;
|
|
626
|
+
}
|
|
627
|
+
resolve(diagnostic) {
|
|
628
|
+
if (diagnostic.severity >= this.throwThreshold) return 2 /* Throw */;
|
|
629
|
+
if (diagnostic.severity >= this.reportThreshold) return 1 /* Report */;
|
|
630
|
+
return 0 /* Ignore */;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// ../../lib/diagnostics/src/diagnostics.ts
|
|
635
|
+
var Diagnostics = class {
|
|
636
|
+
reporter;
|
|
637
|
+
policy;
|
|
638
|
+
// Pre-computed at construction time: true if Warning-level diagnostics are not ignored.
|
|
639
|
+
active;
|
|
640
|
+
constructor(reporter, policy = new DefaultPolicy()) {
|
|
641
|
+
this.reporter = reporter;
|
|
642
|
+
this.policy = policy;
|
|
643
|
+
this.active = policy.resolve({ severity: 2 /* Warning */ }) !== 0 /* Ignore */;
|
|
644
|
+
}
|
|
645
|
+
report(diagnostic) {
|
|
646
|
+
const enforcement = this.policy.resolve(diagnostic);
|
|
647
|
+
if (enforcement === 0 /* Ignore */) return diagnostic;
|
|
648
|
+
if (enforcement === 2 /* Throw */) throw new PraxisError(diagnostic);
|
|
649
|
+
this.reporter.report(diagnostic);
|
|
650
|
+
return diagnostic;
|
|
651
|
+
}
|
|
652
|
+
warn(input) {
|
|
653
|
+
return this.report({ ...input, severity: 2 /* Warning */ });
|
|
654
|
+
}
|
|
655
|
+
error(input) {
|
|
656
|
+
return this.report({ ...input, severity: 3 /* Error */ });
|
|
657
|
+
}
|
|
658
|
+
info(input) {
|
|
659
|
+
return this.report({ ...input, severity: 1 /* Info */ });
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// ../../lib/diagnostics/src/formatter.ts
|
|
664
|
+
function formatDiagnostic(diagnostic) {
|
|
665
|
+
const level = Severity[diagnostic.severity];
|
|
666
|
+
const category = DiagnosticCategory[diagnostic.category];
|
|
667
|
+
const prefix = category !== void 0 ? `[${category}] ` : "";
|
|
668
|
+
return `${level} ${diagnostic.code}: ${prefix}${diagnostic.message}`;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// ../../lib/diagnostics/src/console-reporter.ts
|
|
672
|
+
var ConsoleReporter = class {
|
|
673
|
+
report(diagnostic) {
|
|
674
|
+
const message = formatDiagnostic(diagnostic);
|
|
675
|
+
switch (diagnostic.severity) {
|
|
676
|
+
case 0 /* Debug */:
|
|
677
|
+
console.debug(message);
|
|
678
|
+
break;
|
|
679
|
+
case 1 /* Info */:
|
|
680
|
+
console.info(message);
|
|
681
|
+
break;
|
|
682
|
+
case 2 /* Warning */:
|
|
683
|
+
console.warn(message);
|
|
684
|
+
break;
|
|
685
|
+
case 3 /* Error */:
|
|
686
|
+
case 4 /* Fatal */:
|
|
687
|
+
console.error(message);
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
// ../../lib/diagnostics/src/null-reporter.ts
|
|
694
|
+
var nullReporter = {
|
|
695
|
+
report() {
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// ../../lib/diagnostics/src/presets.ts
|
|
700
|
+
var ignoreAllPolicy = {
|
|
701
|
+
resolve(_) {
|
|
702
|
+
return 0 /* Ignore */;
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
var warnOnlyReporter = {
|
|
706
|
+
report(diagnostic) {
|
|
707
|
+
console.warn(formatDiagnostic(diagnostic));
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
var silentDiagnostics = new Diagnostics(nullReporter, ignoreAllPolicy);
|
|
711
|
+
var warnDiagnostics = new Diagnostics(
|
|
712
|
+
warnOnlyReporter,
|
|
713
|
+
new DefaultPolicy({ reportThreshold: 2 /* Warning */, throwThreshold: 4 /* Fatal */ })
|
|
714
|
+
);
|
|
715
|
+
var throwDiagnostics = new Diagnostics(
|
|
716
|
+
new ConsoleReporter(),
|
|
717
|
+
new DefaultPolicy({ reportThreshold: 2 /* Warning */, throwThreshold: 3 /* Error */ })
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
// ../../lib/contract/src/diagnostics/aria.ts
|
|
721
|
+
var AriaDiagnostics = {
|
|
722
|
+
/** Generic bridge for violations produced by external AriaRule functions. */
|
|
723
|
+
fromViolation(v) {
|
|
724
|
+
return {
|
|
725
|
+
code: "ARIA2002" /* AriaViolation */,
|
|
726
|
+
category: 2 /* ARIA */,
|
|
727
|
+
message: v.message
|
|
728
|
+
};
|
|
729
|
+
},
|
|
730
|
+
attributeInvalid(key, role) {
|
|
731
|
+
return {
|
|
732
|
+
code: "ARIA2003" /* AriaAttributeInvalid */,
|
|
733
|
+
category: 2 /* ARIA */,
|
|
734
|
+
message: `"${key}" is not valid on role="${role}". It will be removed.`,
|
|
735
|
+
rationale: "Invalid ARIA attributes are ignored by assistive technology and may trigger accessibility-tree warnings in browser devtools.",
|
|
736
|
+
suggestions: [
|
|
737
|
+
{
|
|
738
|
+
title: "Remove the attribute",
|
|
739
|
+
description: `"${key}" is not in the allowed attribute set for role="${role}".`
|
|
740
|
+
}
|
|
741
|
+
]
|
|
742
|
+
};
|
|
743
|
+
},
|
|
744
|
+
missingLiveRegion(role, impliedLive) {
|
|
745
|
+
return {
|
|
746
|
+
code: "ARIA2004" /* AriaMissingLiveRegion */,
|
|
747
|
+
category: 2 /* ARIA */,
|
|
748
|
+
message: `role="${role}" implies aria-live="${impliedLive}" but it is missing. It has been injected.`,
|
|
749
|
+
rationale: "Live-region roles announce dynamic content changes to screen readers. Without aria-live the politeness level is unspecified and announcements may be silent.",
|
|
750
|
+
suggestions: [
|
|
751
|
+
{
|
|
752
|
+
title: `Add aria-live="${impliedLive}"`,
|
|
753
|
+
description: `role="${role}" conventionally implies aria-live="${impliedLive}".`,
|
|
754
|
+
fix: `aria-live="${impliedLive}"`
|
|
755
|
+
}
|
|
756
|
+
]
|
|
757
|
+
};
|
|
758
|
+
},
|
|
759
|
+
missingAtomic(role) {
|
|
760
|
+
return {
|
|
761
|
+
code: "ARIA2005" /* AriaMissingAtomic */,
|
|
762
|
+
category: 2 /* ARIA */,
|
|
763
|
+
message: `role="${role}" is a live region. Consider setting aria-atomic="true" if the full region should be announced as a unit, or aria-atomic="false" if only changed nodes should be read.`,
|
|
764
|
+
rationale: "aria-atomic controls whether assistive technology announces the entire live region or only the changed nodes. Omitting it leaves the behaviour browser-defined."
|
|
765
|
+
};
|
|
766
|
+
},
|
|
767
|
+
relevantInvalidTokens(invalid) {
|
|
768
|
+
const quoted = invalid.map((t) => `"${t}"`).join(", ");
|
|
769
|
+
return {
|
|
770
|
+
code: "ARIA2006" /* AriaRelevantInvalidToken */,
|
|
771
|
+
category: 2 /* ARIA */,
|
|
772
|
+
message: `aria-relevant contains invalid token(s): ${quoted}. Valid tokens are: additions, removals, text, all.`,
|
|
773
|
+
rationale: "aria-relevant accepts a space-separated list of change types. Unrecognised tokens are silently ignored by assistive technology, making the attribute ineffective.",
|
|
774
|
+
suggestions: [
|
|
775
|
+
{
|
|
776
|
+
title: "Use only valid tokens",
|
|
777
|
+
description: "Valid values are: additions, removals, text, all (or a space-separated combination)."
|
|
778
|
+
}
|
|
779
|
+
]
|
|
780
|
+
};
|
|
781
|
+
},
|
|
782
|
+
relevantSuperseded() {
|
|
783
|
+
return {
|
|
784
|
+
code: "ARIA2007" /* AriaRelevantSuperseded */,
|
|
785
|
+
category: 2 /* ARIA */,
|
|
786
|
+
message: 'aria-relevant includes "all" alongside other tokens. "all" supersedes additions, removals, and text \u2014 use aria-relevant="all" alone.',
|
|
787
|
+
rationale: '"all" is equivalent to "additions removals text". Combining it with other tokens is redundant and may confuse readers of the markup.',
|
|
788
|
+
suggestions: [
|
|
789
|
+
{
|
|
790
|
+
title: 'Use aria-relevant="all"',
|
|
791
|
+
fix: 'aria-relevant="all"'
|
|
792
|
+
}
|
|
793
|
+
]
|
|
794
|
+
};
|
|
795
|
+
},
|
|
796
|
+
missingAccessibleName(tag) {
|
|
797
|
+
return {
|
|
798
|
+
code: "ARIA2009" /* AriaMissingAccessibleName */,
|
|
799
|
+
category: 2 /* ARIA */,
|
|
800
|
+
message: `<${tag}> has no accessible name. Add aria-label or aria-labelledby.`,
|
|
801
|
+
rationale: "Elements with a landmark or interactive role must have an accessible name so that assistive technology can identify them when presenting the page outline.",
|
|
802
|
+
suggestions: [
|
|
803
|
+
{
|
|
804
|
+
title: "Add aria-label",
|
|
805
|
+
description: `Add aria-label="\u2026" directly to the <${tag}> element.`
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
title: "Add aria-labelledby",
|
|
809
|
+
description: "Point aria-labelledby at the id of an existing heading or label element."
|
|
810
|
+
}
|
|
811
|
+
]
|
|
812
|
+
};
|
|
813
|
+
},
|
|
814
|
+
attributeOnPresentational(attr, tag) {
|
|
815
|
+
return {
|
|
816
|
+
code: "ARIA2010" /* AriaAttributeOnPresentational */,
|
|
817
|
+
category: 2 /* ARIA */,
|
|
818
|
+
message: `"${attr}" is not allowed on a presentational <${tag}>. Presentational elements are invisible to assistive technology.`,
|
|
819
|
+
rationale: 'role="none" and role="presentation" (including <img alt="">) remove an element from the accessibility tree. ARIA attributes on such elements are ignored by assistive technology.',
|
|
820
|
+
suggestions: [
|
|
821
|
+
{
|
|
822
|
+
title: "Remove the attribute",
|
|
823
|
+
description: `"${attr}" has no effect when the element has role="none" or role="presentation".`
|
|
824
|
+
}
|
|
825
|
+
]
|
|
826
|
+
};
|
|
827
|
+
},
|
|
828
|
+
ariaHiddenOnFocusable(tag) {
|
|
829
|
+
return {
|
|
830
|
+
code: "ARIA2011" /* AriaHiddenOnFocusable */,
|
|
831
|
+
category: 2 /* ARIA */,
|
|
832
|
+
message: `aria-hidden="true" must not be used on focusable <${tag}> elements. Screen reader users who navigate by keyboard will encounter the element but receive no information about it.`,
|
|
833
|
+
rationale: 'aria-hidden removes an element from the accessibility tree while leaving it keyboard-reachable. This creates a "ghost" \u2014 a focusable element assistive technology cannot describe.',
|
|
834
|
+
suggestions: [
|
|
835
|
+
{
|
|
836
|
+
title: "Remove aria-hidden",
|
|
837
|
+
description: "If the element should be hidden from all users, use the HTML hidden attribute or CSS display:none instead."
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
title: "Make the element non-focusable",
|
|
841
|
+
description: 'If the element is intentionally decorative, add tabindex="-1" and disable it so it is not reachable by keyboard.'
|
|
842
|
+
}
|
|
843
|
+
]
|
|
844
|
+
};
|
|
845
|
+
},
|
|
846
|
+
invalidAttributeValue(attr, value, expected) {
|
|
847
|
+
const got = value === null ? "null" : value === void 0 ? "undefined" : typeof value === "string" ? `"${value}"` : String(value);
|
|
848
|
+
return {
|
|
849
|
+
code: "ARIA2013" /* AriaInvalidAttributeValue */,
|
|
850
|
+
category: 2 /* ARIA */,
|
|
851
|
+
message: `"${attr}" has an invalid value (${got}). Expected: ${expected}.`,
|
|
852
|
+
rationale: "ARIA attributes with invalid values are silently ignored by assistive technology, making the markup semantically inert.",
|
|
853
|
+
suggestions: [
|
|
854
|
+
{
|
|
855
|
+
title: `Use a valid value for ${attr}`,
|
|
856
|
+
description: `Valid values are: ${expected}.`
|
|
857
|
+
}
|
|
858
|
+
]
|
|
859
|
+
};
|
|
860
|
+
},
|
|
861
|
+
redundantAriaLevel(tag, level) {
|
|
862
|
+
return {
|
|
863
|
+
code: "ARIA2014" /* AriaRedundantLevelAttribute */,
|
|
864
|
+
category: 2 /* ARIA */,
|
|
865
|
+
message: `aria-level="${level}" is redundant on <${tag}>: the element already has an implicit heading level of ${level}. Remove the attribute.`,
|
|
866
|
+
rationale: 'Restating the implicit aria-level adds noise without semantic value. Use aria-level only to override the native heading level (e.g. aria-level="3" on <h2>).',
|
|
867
|
+
suggestions: [
|
|
868
|
+
{
|
|
869
|
+
title: "Remove aria-level",
|
|
870
|
+
description: `<${tag}> already implies aria-level="${level}".`
|
|
871
|
+
}
|
|
872
|
+
]
|
|
873
|
+
};
|
|
874
|
+
},
|
|
875
|
+
requiredProperty(attr, role) {
|
|
876
|
+
return {
|
|
877
|
+
code: "ARIA2012" /* AriaRequiredProperty */,
|
|
878
|
+
category: 2 /* ARIA */,
|
|
879
|
+
message: `"${attr}" is required for role="${role}" but is missing.`,
|
|
880
|
+
rationale: `WAI-ARIA 1.2 specifies required states and properties for certain roles. Without "${attr}", assistive technology cannot correctly communicate the element's state to users.`,
|
|
881
|
+
suggestions: [
|
|
882
|
+
{
|
|
883
|
+
title: `Add ${attr}`,
|
|
884
|
+
description: `role="${role}" requires "${attr}" to be present.`
|
|
885
|
+
}
|
|
886
|
+
]
|
|
887
|
+
};
|
|
888
|
+
},
|
|
889
|
+
invalidRole(role, tag) {
|
|
890
|
+
return {
|
|
891
|
+
code: "ARIA2008" /* AriaInvalidRole */,
|
|
892
|
+
category: 2 /* ARIA */,
|
|
893
|
+
message: `Invalid role "${role ?? ""}" on <${tag}>.`,
|
|
894
|
+
rationale: "An unrecognised or misapplied ARIA role is ignored by assistive technology and may degrade the accessibility of the element."
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
// ../../lib/contract/src/diagnostics/contract.ts
|
|
900
|
+
var ContractDiagnostics = {
|
|
901
|
+
unexpectedChild(typeName, index, context) {
|
|
902
|
+
return {
|
|
903
|
+
code: "COMP1004" /* UnexpectedChild */,
|
|
904
|
+
category: 0 /* Contract */,
|
|
905
|
+
component: context,
|
|
906
|
+
message: `${context}: unexpected child "${typeName}" at index ${index}.`
|
|
907
|
+
};
|
|
908
|
+
},
|
|
909
|
+
ambiguousChild(typeName, index, ruleNames, context) {
|
|
910
|
+
const quoted = ruleNames.map((n) => `"${n}"`).join(" and ");
|
|
911
|
+
return {
|
|
912
|
+
code: "COMP1005" /* AmbiguousChild */,
|
|
913
|
+
category: 0 /* Contract */,
|
|
914
|
+
component: context,
|
|
915
|
+
message: `${context}: child "${typeName}" at index ${index} matches multiple child rules: ${quoted}.`
|
|
916
|
+
};
|
|
917
|
+
},
|
|
918
|
+
cardinalityMin(ruleName, min, context) {
|
|
919
|
+
return {
|
|
920
|
+
code: "COMP1006" /* CardinalityMin */,
|
|
921
|
+
category: 0 /* Contract */,
|
|
922
|
+
component: context,
|
|
923
|
+
message: `${context}: "${ruleName}" requires at least ${min}.`
|
|
924
|
+
};
|
|
925
|
+
},
|
|
926
|
+
cardinalityMax(ruleName, max, context) {
|
|
927
|
+
return {
|
|
928
|
+
code: "COMP1007" /* CardinalityMax */,
|
|
929
|
+
category: 0 /* Contract */,
|
|
930
|
+
component: context,
|
|
931
|
+
message: `${context}: "${ruleName}" allows at most ${max}.`
|
|
932
|
+
};
|
|
933
|
+
},
|
|
934
|
+
positionViolation(ruleName, position, index, context) {
|
|
935
|
+
return {
|
|
936
|
+
code: "COMP1008" /* PositionViolation */,
|
|
937
|
+
category: 0 /* Contract */,
|
|
938
|
+
component: context,
|
|
939
|
+
message: `${context}: "${ruleName}" must be ${position}, got index ${index}`
|
|
940
|
+
};
|
|
941
|
+
},
|
|
942
|
+
unknownVariantDim(component, label, dim) {
|
|
943
|
+
return {
|
|
944
|
+
code: "COMP1010" /* ContractUnknownVariantDim */,
|
|
945
|
+
category: 0 /* Contract */,
|
|
946
|
+
message: `${component}: ${label} references unknown variant "${dim}".`
|
|
947
|
+
};
|
|
948
|
+
},
|
|
949
|
+
unknownVariantValue(component, label, dim, value, valid) {
|
|
950
|
+
return {
|
|
951
|
+
code: "COMP1011" /* ContractUnknownVariantValue */,
|
|
952
|
+
category: 0 /* Contract */,
|
|
953
|
+
message: `${component}: ${label} sets "${dim}" to unknown value "${value}" (valid: ${valid.join(", ")}).`
|
|
954
|
+
};
|
|
955
|
+
},
|
|
956
|
+
unknownRecipeKey(component, key) {
|
|
957
|
+
return {
|
|
958
|
+
code: "COMP1012" /* ContractUnknownRecipeKey */,
|
|
959
|
+
category: 0 /* Contract */,
|
|
960
|
+
message: `${component} Unknown recipeKey "${key}" \u2014 no preset with that name exists.`
|
|
961
|
+
};
|
|
962
|
+
},
|
|
963
|
+
invalidVariantValue(component, key, value) {
|
|
964
|
+
return {
|
|
965
|
+
code: "COMP1013" /* ContractInvalidVariantValue */,
|
|
966
|
+
category: 0 /* Contract */,
|
|
967
|
+
message: `${component} Variant "${key}=${value}" is not a defined value for the "${key}" dimension.`
|
|
968
|
+
};
|
|
969
|
+
},
|
|
970
|
+
allowedAsViolation(tag, allowedAs, component) {
|
|
971
|
+
const allowed = allowedAs.map((t) => `"${String(t)}"`).join(", ");
|
|
972
|
+
return {
|
|
973
|
+
code: "COMP1009" /* AllowedAsViolation */,
|
|
974
|
+
category: 0 /* Contract */,
|
|
975
|
+
component,
|
|
976
|
+
message: `<${component}>: "as" prop received "${tag}" but only [${allowed}] are allowed.`
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// ../../lib/contract/src/diagnostics/html.ts
|
|
982
|
+
var HtmlDiagnostics = {
|
|
983
|
+
emptyRole(tag) {
|
|
984
|
+
return {
|
|
985
|
+
code: "HTML3002" /* HtmlEmptyRole */,
|
|
986
|
+
category: 1 /* HTML */,
|
|
987
|
+
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
988
|
+
};
|
|
989
|
+
},
|
|
990
|
+
implicitRoleRedundant(tag, implicitRole) {
|
|
991
|
+
return {
|
|
992
|
+
code: "HTML3003" /* HtmlImplicitRoleRedundant */,
|
|
993
|
+
category: 1 /* HTML */,
|
|
994
|
+
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
995
|
+
};
|
|
996
|
+
},
|
|
997
|
+
implicitRoleOverride(tag, implicitRole, role) {
|
|
998
|
+
return {
|
|
999
|
+
code: "HTML3004" /* HtmlImplicitRoleOverride */,
|
|
1000
|
+
category: 1 /* HTML */,
|
|
1001
|
+
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
1002
|
+
};
|
|
1003
|
+
},
|
|
1004
|
+
standaloneRegionOverride(tag, implicitRole) {
|
|
1005
|
+
return {
|
|
1006
|
+
code: "HTML3005" /* HtmlStandaloneRegionOverride */,
|
|
1007
|
+
category: 1 /* HTML */,
|
|
1008
|
+
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
1009
|
+
};
|
|
1010
|
+
},
|
|
1011
|
+
landmarkRoleOverride(tag, implicitRole, role) {
|
|
1012
|
+
return {
|
|
1013
|
+
code: "HTML3006" /* HtmlLandmarkRoleOverride */,
|
|
1014
|
+
category: 1 /* HTML */,
|
|
1015
|
+
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
1016
|
+
};
|
|
1017
|
+
},
|
|
1018
|
+
invalidChild(child, parent, allowed) {
|
|
1019
|
+
return {
|
|
1020
|
+
code: "HTML3007" /* HtmlInvalidChild */,
|
|
1021
|
+
category: 1 /* HTML */,
|
|
1022
|
+
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1028
|
+
var SlotDiagnostics = {
|
|
1029
|
+
exclusive(name) {
|
|
1030
|
+
return {
|
|
1031
|
+
code: "SLOT1001" /* SlotExclusive */,
|
|
1032
|
+
category: 0 /* Contract */,
|
|
1033
|
+
component: name,
|
|
1034
|
+
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
1035
|
+
};
|
|
1036
|
+
},
|
|
1037
|
+
singleChildRequired(name, elementTerm) {
|
|
1038
|
+
return {
|
|
1039
|
+
code: "SLOT1002" /* SlotSingleChild */,
|
|
1040
|
+
category: 0 /* Contract */,
|
|
1041
|
+
component: name,
|
|
1042
|
+
message: `${name}: asChild requires a ${elementTerm} child`
|
|
1043
|
+
};
|
|
1044
|
+
},
|
|
1045
|
+
singleChildExceeded(name, elementTerm, count) {
|
|
1046
|
+
return {
|
|
1047
|
+
code: "SLOT1002" /* SlotSingleChild */,
|
|
1048
|
+
category: 0 /* Contract */,
|
|
1049
|
+
component: name,
|
|
1050
|
+
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
1051
|
+
};
|
|
1052
|
+
},
|
|
1053
|
+
discardedChildren(name, elementTerm, count) {
|
|
1054
|
+
const suffix = count === 1 ? "" : "ren";
|
|
1055
|
+
return {
|
|
1056
|
+
code: "SLOT1003" /* SlotDiscardedChildren */,
|
|
1057
|
+
category: 0 /* Contract */,
|
|
1058
|
+
component: name,
|
|
1059
|
+
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
1060
|
+
};
|
|
1061
|
+
},
|
|
1062
|
+
renderFnRequired(name, received) {
|
|
1063
|
+
return {
|
|
1064
|
+
code: "SLOT1004" /* SlotRenderFn */,
|
|
1065
|
+
category: 0 /* Contract */,
|
|
1066
|
+
component: name,
|
|
1067
|
+
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1073
|
+
var VALID = [{ valid: true }];
|
|
1074
|
+
function isIntrinsicTag(tag) {
|
|
1075
|
+
return isString(tag);
|
|
1076
|
+
}
|
|
1077
|
+
function omitProp(obj, key) {
|
|
1078
|
+
const { [key]: _, ...rest } = obj;
|
|
1079
|
+
return rest;
|
|
1080
|
+
}
|
|
1081
|
+
var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
1082
|
+
#extraRules;
|
|
1083
|
+
#planCache = /* @__PURE__ */ new Map();
|
|
1084
|
+
static #MAX_CACHE = 100;
|
|
1085
|
+
// Memoized AriaFix objects keyed by attribute name — the ARIA attribute set is
|
|
1086
|
+
// finite so this Map is bounded and avoids recreating closures on every cache miss.
|
|
1087
|
+
static #removeAttributeFixCache = /* @__PURE__ */ new Map();
|
|
1088
|
+
constructor(diagnostics, options) {
|
|
1089
|
+
super(diagnostics);
|
|
1090
|
+
this.#extraRules = options?.rules ?? [];
|
|
1091
|
+
}
|
|
1092
|
+
static #normalizeEmptyRole(tag, props) {
|
|
1093
|
+
if (props.role !== "") return { normalized: false };
|
|
1094
|
+
const d = HtmlDiagnostics.emptyRole(tag);
|
|
1095
|
+
return {
|
|
1096
|
+
normalized: true,
|
|
1097
|
+
result: {
|
|
1098
|
+
props: omitProp(props, "role"),
|
|
1099
|
+
violations: [
|
|
1100
|
+
{
|
|
1101
|
+
message: d.message,
|
|
1102
|
+
diagnostic: d,
|
|
1103
|
+
tag,
|
|
1104
|
+
role: "",
|
|
1105
|
+
attribute: void 0,
|
|
1106
|
+
severity: "warning",
|
|
1107
|
+
phase: "evaluate"
|
|
1108
|
+
}
|
|
1109
|
+
]
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
static #deriveContext(tag, props) {
|
|
1114
|
+
if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
|
|
1115
|
+
const implicitRole = getImplicitRole(tag, props);
|
|
1116
|
+
const hasRole2 = implicitRole != null || isString(props.role) && props.role.length > 0;
|
|
1117
|
+
if (!hasRole2) return { proceed: false, result: { props, violations: [] } };
|
|
1118
|
+
const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
|
|
1119
|
+
if (normalized.normalized) return { proceed: false, result: normalized.result };
|
|
1120
|
+
const effectiveRole = props.role ?? implicitRole;
|
|
1121
|
+
return {
|
|
1122
|
+
proceed: true,
|
|
1123
|
+
tag,
|
|
1124
|
+
implicitRole,
|
|
1125
|
+
effectiveRole,
|
|
1126
|
+
context: { tag, props, implicitRole, effectiveRole }
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
static #runRules(rules, context) {
|
|
1130
|
+
const violations = [];
|
|
1131
|
+
const fixes = [];
|
|
1132
|
+
iterate.forEach(rules, (rule) => {
|
|
1133
|
+
iterate.forEach(rule(context), (result) => {
|
|
1134
|
+
if (result.valid) return;
|
|
1135
|
+
const {
|
|
1136
|
+
tag,
|
|
1137
|
+
props: { role }
|
|
1138
|
+
} = context;
|
|
1139
|
+
const { message, attribute, severity } = result;
|
|
1140
|
+
const resolvedMessage = message ?? result.diagnostic?.message;
|
|
1141
|
+
const fallbackDiag = resolvedMessage == null ? AriaDiagnostics.invalidRole(role, tag) : void 0;
|
|
1142
|
+
violations.push({
|
|
1143
|
+
message: resolvedMessage ?? fallbackDiag.message,
|
|
1144
|
+
tag,
|
|
1145
|
+
role,
|
|
1146
|
+
attribute,
|
|
1147
|
+
severity,
|
|
1148
|
+
phase: "evaluate",
|
|
1149
|
+
...result.diagnostic != null && { diagnostic: result.diagnostic },
|
|
1150
|
+
...fallbackDiag != null && { diagnostic: fallbackDiag }
|
|
1151
|
+
});
|
|
1152
|
+
if (result.fixable) fixes.push(result.fix);
|
|
1153
|
+
});
|
|
1154
|
+
});
|
|
1155
|
+
return { violations, fixes };
|
|
1156
|
+
}
|
|
1157
|
+
static #getRules(context) {
|
|
1158
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || context.effectiveRole != null && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1159
|
+
return _AriaPolicyEngine.#pipeline;
|
|
1160
|
+
}
|
|
1161
|
+
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
1162
|
+
}
|
|
1163
|
+
static evaluate(tag, props) {
|
|
1164
|
+
const derived = _AriaPolicyEngine.#deriveContext(tag, props);
|
|
1165
|
+
if (!derived.proceed) return derived.result;
|
|
1166
|
+
const { tag: narrowedTag, implicitRole, context } = derived;
|
|
1167
|
+
const { violations, fixes } = _AriaPolicyEngine.#runRules(
|
|
1168
|
+
_AriaPolicyEngine.#getRules(context),
|
|
1169
|
+
context
|
|
1170
|
+
);
|
|
1171
|
+
const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
|
|
1172
|
+
return { props: next, violations };
|
|
1173
|
+
}
|
|
1174
|
+
static #evaluateWithRules(tag, props, extraRules) {
|
|
1175
|
+
const derived = _AriaPolicyEngine.#deriveContext(tag, props);
|
|
1176
|
+
if (!derived.proceed) return derived.result;
|
|
1177
|
+
const { tag: narrowedTag, implicitRole, context } = derived;
|
|
1178
|
+
const { violations, fixes } = _AriaPolicyEngine.#runRules(
|
|
1179
|
+
[..._AriaPolicyEngine.#getRules(context), ...extraRules],
|
|
1180
|
+
context
|
|
1181
|
+
);
|
|
1182
|
+
const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
|
|
1183
|
+
return { props: next, violations };
|
|
1184
|
+
}
|
|
1185
|
+
report(violations) {
|
|
1186
|
+
iterate.forEach(violations, (v) => {
|
|
1187
|
+
const d = v.diagnostic ?? AriaDiagnostics.fromViolation(v);
|
|
1188
|
+
if (v.severity === "error") this.violate(d);
|
|
1189
|
+
else this.warn(d);
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
// Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs).
|
|
1193
|
+
// Non-aria props (className, onClick, etc.) do not affect ARIA decisions and are excluded
|
|
1194
|
+
// so cache hits survive re-renders that only change non-aria props.
|
|
1195
|
+
// Note: #extraRules are NOT included in the key — each engine instance has its own Map,
|
|
1196
|
+
// so two engines with different rules never share cache entries. If caching ever becomes
|
|
1197
|
+
// static/shared, rule identity would need to be folded into the key.
|
|
1198
|
+
static #createPlanKey(tag, props) {
|
|
1199
|
+
if (!isIntrinsicTag(tag)) return null;
|
|
1200
|
+
const parts = [tag];
|
|
1201
|
+
if (typeof props.role === "string") parts.push(`role:${props.role}`);
|
|
1202
|
+
if (tag === "input" && typeof props.type === "string") parts.push(`type:${props.type}`);
|
|
1203
|
+
if (tag === "img") parts.push(`alt:${props.alt === "" ? "empty" : "present"}`);
|
|
1204
|
+
const ariaEntries = [];
|
|
1205
|
+
iterate.forEachEntry(props, (k, v) => {
|
|
1206
|
+
if (!k.startsWith("aria-")) return;
|
|
1207
|
+
if (!isString(v) && !isNumber(v) && typeof v !== "boolean") return;
|
|
1208
|
+
ariaEntries.push(`${k}:${String(v)}`);
|
|
1209
|
+
});
|
|
1210
|
+
if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
|
|
1211
|
+
return parts.join("|");
|
|
1212
|
+
}
|
|
1213
|
+
static #computePlan(inputProps, resultProps) {
|
|
1214
|
+
const removals = /* @__PURE__ */ new Set();
|
|
1215
|
+
const updates = {};
|
|
1216
|
+
iterate.forEachKey(inputProps, (key) => {
|
|
1217
|
+
if (!(key in resultProps)) removals.add(key);
|
|
1218
|
+
});
|
|
1219
|
+
iterate.forEachEntry(resultProps, (key, resultVal) => {
|
|
1220
|
+
if (inputProps[key] !== resultVal) updates[key] = resultVal;
|
|
1221
|
+
});
|
|
1222
|
+
return { removals, updates };
|
|
1223
|
+
}
|
|
1224
|
+
static #applyPlan(props, removals, updates) {
|
|
1225
|
+
const hasRemovals = removals.size > 0;
|
|
1226
|
+
const hasUpdates = Object.keys(updates).length > 0;
|
|
1227
|
+
if (!hasRemovals && !hasUpdates) return props;
|
|
1228
|
+
const next = {};
|
|
1229
|
+
iterate.forEachEntry(props, (k, v) => {
|
|
1230
|
+
if (!removals.has(k)) next[k] = v;
|
|
1231
|
+
});
|
|
1232
|
+
Object.assign(next, updates);
|
|
1233
|
+
return next;
|
|
1234
|
+
}
|
|
1235
|
+
validate(tag, props) {
|
|
1236
|
+
const key = _AriaPolicyEngine.#createPlanKey(tag, props);
|
|
1237
|
+
if (!isNull(key)) {
|
|
1238
|
+
const cached = this.#planCache.get(key);
|
|
1239
|
+
if (cached !== void 0) {
|
|
1240
|
+
this.#planCache.delete(key);
|
|
1241
|
+
this.#planCache.set(key, cached);
|
|
1242
|
+
if (cached.violations.length > 0) this.report(cached.violations);
|
|
1243
|
+
return {
|
|
1244
|
+
props: _AriaPolicyEngine.#applyPlan(props, cached.removals, cached.updates),
|
|
1245
|
+
violations: cached.violations
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
const result = this.#extraRules.length ? _AriaPolicyEngine.#evaluateWithRules(tag, props, this.#extraRules) : _AriaPolicyEngine.evaluate(tag, props);
|
|
1250
|
+
if (result.violations.length > 0) this.report(result.violations);
|
|
1251
|
+
if (!isNull(key)) {
|
|
1252
|
+
const { removals, updates } = _AriaPolicyEngine.#computePlan(
|
|
1253
|
+
props,
|
|
1254
|
+
result.props
|
|
1255
|
+
);
|
|
1256
|
+
const plan = { removals, updates, violations: result.violations };
|
|
1257
|
+
this.#planCache.set(key, plan);
|
|
1258
|
+
if (this.#planCache.size > _AriaPolicyEngine.#MAX_CACHE) {
|
|
1259
|
+
const lru = this.#planCache.keys().next().value;
|
|
1260
|
+
if (lru !== void 0) this.#planCache.delete(lru);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return result;
|
|
1264
|
+
}
|
|
1265
|
+
static #hasRole(props) {
|
|
1266
|
+
return isString(props.role) && props.role.length > 0;
|
|
1267
|
+
}
|
|
1268
|
+
static #applyFixes(tag, implicitRole, props, fixes) {
|
|
1269
|
+
if (fixes.length === 0) return props;
|
|
1270
|
+
const sorted = [...fixes].sort((a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity));
|
|
1271
|
+
let next = props;
|
|
1272
|
+
iterate.forEach(sorted, ({ apply }) => {
|
|
1273
|
+
const effectiveRole = next.role ?? implicitRole;
|
|
1274
|
+
const fixContext = { tag, implicitRole, effectiveRole, props: next };
|
|
1275
|
+
const fixResult = apply(fixContext);
|
|
1276
|
+
if (fixResult.applied) next = fixResult.next;
|
|
1277
|
+
});
|
|
1278
|
+
return next;
|
|
1279
|
+
}
|
|
1280
|
+
static #removeRole = {
|
|
1281
|
+
kind: "removeRole",
|
|
1282
|
+
apply: ({ props }) => {
|
|
1283
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
1284
|
+
return { applied: true, next: omitProp(props, "role"), previous: props };
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
static #makeRemoveAttributeFix(attr) {
|
|
1288
|
+
const cached = _AriaPolicyEngine.#removeAttributeFixCache.get(attr);
|
|
1289
|
+
if (cached) return cached;
|
|
1290
|
+
const fix = {
|
|
1291
|
+
kind: `removeAttribute:${attr}`,
|
|
1292
|
+
apply: ({ props }) => {
|
|
1293
|
+
if (!(attr in props)) return { applied: false, next: props };
|
|
1294
|
+
return { applied: true, next: omitProp(props, attr), previous: props };
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
_AriaPolicyEngine.#removeAttributeFixCache.set(attr, fix);
|
|
1298
|
+
return fix;
|
|
1299
|
+
}
|
|
1300
|
+
// Snapshot diagnostic model: all rules evaluate against the same (tag, props, implicitRole) snapshot.
|
|
1301
|
+
static #pipeline = [
|
|
1302
|
+
_AriaPolicyEngine.#checkInvalidRoleOverride,
|
|
1303
|
+
_AriaPolicyEngine.#checkRedundantRole,
|
|
1304
|
+
_AriaPolicyEngine.#checkStandaloneRegion,
|
|
1305
|
+
_AriaPolicyEngine.#checkAriaAttributeValues,
|
|
1306
|
+
_AriaPolicyEngine.#checkInvalidAriaAttributes,
|
|
1307
|
+
_AriaPolicyEngine.#checkRequiredAriaProperties,
|
|
1308
|
+
_AriaPolicyEngine.#checkNameRequiredRoles,
|
|
1309
|
+
_AriaPolicyEngine.#checkRedundantAriaLevel,
|
|
1310
|
+
_AriaPolicyEngine.#checkMissingLiveRegion,
|
|
1311
|
+
_AriaPolicyEngine.#checkMissingAtomic,
|
|
1312
|
+
_AriaPolicyEngine.#checkInvalidAriaRelevant,
|
|
1313
|
+
_AriaPolicyEngine.#checkAriaHiddenOnFocusable,
|
|
1314
|
+
_AriaPolicyEngine.#checkPresentationalAriaAttributes
|
|
1315
|
+
];
|
|
1316
|
+
// Rules for elements with an implicit role but no explicit role (not a live region).
|
|
1317
|
+
static #implicitOnlyRules = [
|
|
1318
|
+
_AriaPolicyEngine.#checkAriaAttributeValues,
|
|
1319
|
+
_AriaPolicyEngine.#checkInvalidAriaAttributes,
|
|
1320
|
+
_AriaPolicyEngine.#checkRequiredAriaProperties,
|
|
1321
|
+
_AriaPolicyEngine.#checkNameRequiredRoles,
|
|
1322
|
+
_AriaPolicyEngine.#checkRedundantAriaLevel,
|
|
1323
|
+
_AriaPolicyEngine.#checkAriaHiddenOnFocusable,
|
|
1324
|
+
_AriaPolicyEngine.#checkPresentationalAriaAttributes
|
|
1325
|
+
];
|
|
1326
|
+
static #checkInvalidRoleOverride({
|
|
1327
|
+
tag,
|
|
1328
|
+
props,
|
|
1329
|
+
implicitRole
|
|
1330
|
+
}) {
|
|
1331
|
+
const role = props.role;
|
|
1332
|
+
if (!implicitRole || !role || role === implicitRole) return VALID;
|
|
1333
|
+
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1334
|
+
return [
|
|
1335
|
+
{
|
|
1336
|
+
valid: false,
|
|
1337
|
+
fixable: true,
|
|
1338
|
+
severity: "error",
|
|
1339
|
+
fix: _AriaPolicyEngine.#removeRole,
|
|
1340
|
+
diagnostic: HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role)
|
|
1341
|
+
}
|
|
1342
|
+
];
|
|
1343
|
+
}
|
|
1344
|
+
return VALID;
|
|
1345
|
+
}
|
|
1346
|
+
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1347
|
+
const role = props.role;
|
|
1348
|
+
if (!implicitRole || !role || role !== implicitRole) return VALID;
|
|
1349
|
+
return [
|
|
1350
|
+
{
|
|
1351
|
+
valid: false,
|
|
1352
|
+
fixable: true,
|
|
1353
|
+
severity: "warning",
|
|
1354
|
+
fix: _AriaPolicyEngine.#removeRole,
|
|
1355
|
+
diagnostic: HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole)
|
|
1356
|
+
}
|
|
1357
|
+
];
|
|
1358
|
+
}
|
|
1359
|
+
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1360
|
+
const role = props.role;
|
|
1361
|
+
if (role !== "region") return VALID;
|
|
1362
|
+
if (!isStandaloneTag(tag)) return VALID;
|
|
1363
|
+
return [
|
|
1364
|
+
{
|
|
1365
|
+
valid: false,
|
|
1366
|
+
fixable: true,
|
|
1367
|
+
severity: "error",
|
|
1368
|
+
fix: _AriaPolicyEngine.#removeRole,
|
|
1369
|
+
diagnostic: HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag)
|
|
1370
|
+
}
|
|
1371
|
+
];
|
|
1372
|
+
}
|
|
1373
|
+
static #checkInvalidAriaAttributes({
|
|
1374
|
+
tag,
|
|
1375
|
+
props,
|
|
1376
|
+
effectiveRole
|
|
1377
|
+
}) {
|
|
1378
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
|
|
1379
|
+
const results = [];
|
|
1380
|
+
iterate.forEachEntry(props, (key) => {
|
|
1381
|
+
if (!key.startsWith("aria-")) return;
|
|
1382
|
+
if (isGlobalAriaAttribute(key)) return;
|
|
1383
|
+
if (isAriaAttributeValidForRole(key, effectiveRole)) return;
|
|
1384
|
+
results.push({
|
|
1385
|
+
valid: false,
|
|
1386
|
+
severity: "warning",
|
|
1387
|
+
fixable: true,
|
|
1388
|
+
attribute: key,
|
|
1389
|
+
diagnostic: AriaDiagnostics.attributeInvalid(key, effectiveRole ?? tag),
|
|
1390
|
+
fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
|
|
1391
|
+
});
|
|
1392
|
+
});
|
|
1393
|
+
return results;
|
|
1394
|
+
}
|
|
1395
|
+
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1396
|
+
// Accepted value shapes for typed ARIA attributes.
|
|
1397
|
+
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1398
|
+
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1399
|
+
// Boolean (true | false)
|
|
1400
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1401
|
+
["aria-busy", { kind: "boolean" }],
|
|
1402
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1403
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1404
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1405
|
+
["aria-modal", { kind: "boolean" }],
|
|
1406
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1407
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1408
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1409
|
+
["aria-required", { kind: "boolean" }],
|
|
1410
|
+
["aria-selected", { kind: "boolean" }],
|
|
1411
|
+
// Tristate (true | false | mixed)
|
|
1412
|
+
["aria-checked", { kind: "tristate" }],
|
|
1413
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1414
|
+
// Numeric (any finite number)
|
|
1415
|
+
["aria-valuenow", { kind: "number" }],
|
|
1416
|
+
["aria-valuemin", { kind: "number" }],
|
|
1417
|
+
["aria-valuemax", { kind: "number" }],
|
|
1418
|
+
// Integer with optional range
|
|
1419
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1420
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1421
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1422
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1423
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1424
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1425
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1426
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1427
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1428
|
+
// Enum (specific allowed tokens)
|
|
1429
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1430
|
+
[
|
|
1431
|
+
"aria-current",
|
|
1432
|
+
{
|
|
1433
|
+
kind: "enum",
|
|
1434
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1435
|
+
}
|
|
1436
|
+
],
|
|
1437
|
+
[
|
|
1438
|
+
"aria-haspopup",
|
|
1439
|
+
{
|
|
1440
|
+
kind: "enum",
|
|
1441
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1442
|
+
}
|
|
1443
|
+
],
|
|
1444
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1445
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1446
|
+
[
|
|
1447
|
+
"aria-orientation",
|
|
1448
|
+
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1449
|
+
],
|
|
1450
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1451
|
+
]);
|
|
1452
|
+
static #isValidAriaValue(value, type) {
|
|
1453
|
+
switch (type.kind) {
|
|
1454
|
+
case "boolean":
|
|
1455
|
+
return value === "true" || value === "false" || value === true || value === false;
|
|
1456
|
+
case "tristate":
|
|
1457
|
+
return value === "true" || value === "false" || value === "mixed" || value === true || value === false;
|
|
1458
|
+
case "number": {
|
|
1459
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1460
|
+
if (typeof value !== "string") return false;
|
|
1461
|
+
const n = parseFloat(value);
|
|
1462
|
+
return Number.isFinite(n);
|
|
1463
|
+
}
|
|
1464
|
+
case "integer": {
|
|
1465
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? parseInt(value, 10) : NaN;
|
|
1466
|
+
if (!Number.isFinite(n) || !Number.isInteger(n)) return false;
|
|
1467
|
+
if (type.min !== void 0 && n < type.min) return false;
|
|
1468
|
+
if (type.max !== void 0 && n > type.max) return false;
|
|
1469
|
+
return true;
|
|
1470
|
+
}
|
|
1471
|
+
case "enum":
|
|
1472
|
+
return typeof value === "string" && type.values.has(value);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
static #describeExpected(type) {
|
|
1476
|
+
switch (type.kind) {
|
|
1477
|
+
case "boolean":
|
|
1478
|
+
return '"true" or "false"';
|
|
1479
|
+
case "tristate":
|
|
1480
|
+
return '"true", "false", or "mixed"';
|
|
1481
|
+
case "number":
|
|
1482
|
+
return "a finite number";
|
|
1483
|
+
case "integer": {
|
|
1484
|
+
const parts = ["an integer"];
|
|
1485
|
+
if (type.min !== void 0 && type.max !== void 0)
|
|
1486
|
+
parts.push(`between ${type.min} and ${type.max}`);
|
|
1487
|
+
else if (type.min !== void 0) parts.push(`\u2265 ${type.min}`);
|
|
1488
|
+
else if (type.max !== void 0) parts.push(`\u2264 ${type.max}`);
|
|
1489
|
+
return parts.join(" ");
|
|
1490
|
+
}
|
|
1491
|
+
case "enum":
|
|
1492
|
+
return [...type.values].map((v) => `"${v}"`).join(", ");
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1496
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
|
|
1497
|
+
const results = [];
|
|
1498
|
+
iterate.forEachEntry(props, (key, value) => {
|
|
1499
|
+
if (!key.startsWith("aria-")) return;
|
|
1500
|
+
const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
|
|
1501
|
+
if (type == null) return;
|
|
1502
|
+
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1503
|
+
results.push({
|
|
1504
|
+
valid: false,
|
|
1505
|
+
fixable: true,
|
|
1506
|
+
severity: "warning",
|
|
1507
|
+
attribute: key,
|
|
1508
|
+
diagnostic: AriaDiagnostics.invalidAttributeValue(
|
|
1509
|
+
key,
|
|
1510
|
+
value,
|
|
1511
|
+
_AriaPolicyEngine.#describeExpected(type)
|
|
1512
|
+
),
|
|
1513
|
+
fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
|
|
1514
|
+
});
|
|
1515
|
+
});
|
|
1516
|
+
return results;
|
|
1517
|
+
}
|
|
1518
|
+
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1519
|
+
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1520
|
+
["h1", 1],
|
|
1521
|
+
["h2", 2],
|
|
1522
|
+
["h3", 3],
|
|
1523
|
+
["h4", 4],
|
|
1524
|
+
["h5", 5],
|
|
1525
|
+
["h6", 6]
|
|
1526
|
+
]);
|
|
1527
|
+
static #checkRedundantAriaLevel({
|
|
1528
|
+
tag,
|
|
1529
|
+
props,
|
|
1530
|
+
effectiveRole
|
|
1531
|
+
}) {
|
|
1532
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
|
|
1533
|
+
const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1534
|
+
if (implicitLevel == null) return VALID;
|
|
1535
|
+
const raw = props["aria-level"];
|
|
1536
|
+
if (raw == null) return VALID;
|
|
1537
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1538
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return VALID;
|
|
1539
|
+
return [
|
|
1540
|
+
{
|
|
1541
|
+
valid: false,
|
|
1542
|
+
fixable: true,
|
|
1543
|
+
severity: "warning",
|
|
1544
|
+
attribute: "aria-level",
|
|
1545
|
+
diagnostic: AriaDiagnostics.redundantAriaLevel(tag, implicitLevel),
|
|
1546
|
+
fix: _AriaPolicyEngine.#makeRemoveAttributeFix("aria-level")
|
|
1547
|
+
}
|
|
1548
|
+
];
|
|
1549
|
+
}
|
|
1550
|
+
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1551
|
+
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1552
|
+
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1553
|
+
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1554
|
+
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1555
|
+
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1556
|
+
static #checkNameRequiredRoles({
|
|
1557
|
+
tag,
|
|
1558
|
+
props,
|
|
1559
|
+
effectiveRole
|
|
1560
|
+
}) {
|
|
1561
|
+
if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID;
|
|
1562
|
+
if ("aria-label" in props || "aria-labelledby" in props) return VALID;
|
|
1563
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return VALID;
|
|
1564
|
+
return [
|
|
1565
|
+
{
|
|
1566
|
+
valid: false,
|
|
1567
|
+
fixable: false,
|
|
1568
|
+
severity: "warning",
|
|
1569
|
+
diagnostic: AriaDiagnostics.missingAccessibleName(tag)
|
|
1570
|
+
}
|
|
1571
|
+
];
|
|
1572
|
+
}
|
|
1573
|
+
// WAI-ARIA 1.2 required states and properties, keyed by role.
|
|
1574
|
+
// Source: https://www.w3.org/TR/wai-aria-1.2/#requiredState
|
|
1575
|
+
static #REQUIRED_PROPERTIES = /* @__PURE__ */ new Map([
|
|
1576
|
+
["combobox", ["aria-expanded"]],
|
|
1577
|
+
["option", ["aria-selected"]],
|
|
1578
|
+
["slider", ["aria-valuenow"]],
|
|
1579
|
+
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1580
|
+
["spinbutton", ["aria-valuenow"]]
|
|
1581
|
+
]);
|
|
1582
|
+
static #checkRequiredAriaProperties({
|
|
1583
|
+
props,
|
|
1584
|
+
effectiveRole
|
|
1585
|
+
}) {
|
|
1586
|
+
if (!effectiveRole) return VALID;
|
|
1587
|
+
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1588
|
+
if (required == null) return VALID;
|
|
1589
|
+
const results = [];
|
|
1590
|
+
iterate.forEach(required, (attr) => {
|
|
1591
|
+
if (attr in props) return;
|
|
1592
|
+
results.push({
|
|
1593
|
+
valid: false,
|
|
1594
|
+
fixable: false,
|
|
1595
|
+
severity: "warning",
|
|
1596
|
+
attribute: attr,
|
|
1597
|
+
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1598
|
+
});
|
|
1599
|
+
});
|
|
1600
|
+
return results;
|
|
1601
|
+
}
|
|
1602
|
+
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1603
|
+
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1604
|
+
"a",
|
|
1605
|
+
"button",
|
|
1606
|
+
"input",
|
|
1607
|
+
"select",
|
|
1608
|
+
"textarea"
|
|
1609
|
+
]);
|
|
1610
|
+
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1611
|
+
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1612
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID;
|
|
1613
|
+
const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
|
|
1614
|
+
if (!isInteractive) {
|
|
1615
|
+
const tabindex = props.tabindex;
|
|
1616
|
+
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1617
|
+
if (!Number.isFinite(n) || n < 0) return VALID;
|
|
1618
|
+
}
|
|
1619
|
+
return [
|
|
1620
|
+
{
|
|
1621
|
+
valid: false,
|
|
1622
|
+
fixable: false,
|
|
1623
|
+
severity: "error",
|
|
1624
|
+
attribute: "aria-hidden",
|
|
1625
|
+
diagnostic: AriaDiagnostics.ariaHiddenOnFocusable(tag)
|
|
1626
|
+
}
|
|
1627
|
+
];
|
|
1628
|
+
}
|
|
1629
|
+
// Presentational elements (role=none/presentation, including <img alt="">) are removed
|
|
1630
|
+
// from the accessibility tree — ARIA attributes on them are meaningless and misleading.
|
|
1631
|
+
static #checkPresentationalAriaAttributes({
|
|
1632
|
+
tag,
|
|
1633
|
+
props,
|
|
1634
|
+
effectiveRole
|
|
1635
|
+
}) {
|
|
1636
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID;
|
|
1637
|
+
const results = [];
|
|
1638
|
+
iterate.forEachEntry(props, (key) => {
|
|
1639
|
+
if (!key.startsWith("aria-")) return;
|
|
1640
|
+
if (key === "aria-hidden") return;
|
|
1641
|
+
results.push({
|
|
1642
|
+
valid: false,
|
|
1643
|
+
fixable: true,
|
|
1644
|
+
severity: "warning",
|
|
1645
|
+
attribute: key,
|
|
1646
|
+
diagnostic: AriaDiagnostics.attributeOnPresentational(key, tag),
|
|
1647
|
+
fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
|
|
1648
|
+
});
|
|
1649
|
+
});
|
|
1650
|
+
return results;
|
|
1651
|
+
}
|
|
1652
|
+
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1653
|
+
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1654
|
+
["alert", "assertive"],
|
|
1655
|
+
["status", "polite"],
|
|
1656
|
+
["log", "polite"],
|
|
1657
|
+
["timer", "off"]
|
|
1658
|
+
]);
|
|
1659
|
+
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1660
|
+
if (!effectiveRole) return VALID;
|
|
1661
|
+
const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
|
|
1662
|
+
if (!impliedLive) return VALID;
|
|
1663
|
+
if ("aria-live" in props) return VALID;
|
|
1664
|
+
const injectLive = {
|
|
1665
|
+
kind: `injectLive:${effectiveRole}`,
|
|
1666
|
+
apply: (ctx) => ({
|
|
1667
|
+
applied: true,
|
|
1668
|
+
next: { ...ctx.props, "aria-live": impliedLive },
|
|
1669
|
+
previous: ctx.props
|
|
1670
|
+
})
|
|
1671
|
+
};
|
|
1672
|
+
return [
|
|
1673
|
+
{
|
|
1674
|
+
valid: false,
|
|
1675
|
+
fixable: true,
|
|
1676
|
+
severity: "warning",
|
|
1677
|
+
fix: injectLive,
|
|
1678
|
+
diagnostic: AriaDiagnostics.missingLiveRegion(effectiveRole, impliedLive)
|
|
1679
|
+
}
|
|
1680
|
+
];
|
|
1681
|
+
}
|
|
1682
|
+
static #checkMissingAtomic({ effectiveRole, props }) {
|
|
1683
|
+
if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole)) return VALID;
|
|
1684
|
+
if ("aria-atomic" in props) return VALID;
|
|
1685
|
+
return [
|
|
1686
|
+
{
|
|
1687
|
+
valid: false,
|
|
1688
|
+
fixable: false,
|
|
1689
|
+
severity: "warning",
|
|
1690
|
+
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1691
|
+
}
|
|
1692
|
+
];
|
|
1693
|
+
}
|
|
1694
|
+
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1695
|
+
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1696
|
+
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1697
|
+
// produce inconsistent results on cache hits.
|
|
1698
|
+
static #normalizeRelevantAllFix = {
|
|
1699
|
+
kind: "normalizeRelevantAll",
|
|
1700
|
+
apply: ({ props: p }) => ({
|
|
1701
|
+
applied: true,
|
|
1702
|
+
next: { ...p, "aria-relevant": "all" },
|
|
1703
|
+
previous: p
|
|
1704
|
+
})
|
|
1705
|
+
};
|
|
1706
|
+
static #checkInvalidAriaRelevant({ props }) {
|
|
1707
|
+
const relevant = props["aria-relevant"];
|
|
1708
|
+
if (relevant === void 0) return VALID;
|
|
1709
|
+
if (typeof relevant !== "string") return VALID;
|
|
1710
|
+
const tokens = relevant.trim().split(/\s+/);
|
|
1711
|
+
const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
|
|
1712
|
+
if (invalid.length > 0) {
|
|
1713
|
+
return [
|
|
1714
|
+
{
|
|
1715
|
+
valid: false,
|
|
1716
|
+
fixable: true,
|
|
1717
|
+
severity: "warning",
|
|
1718
|
+
attribute: "aria-relevant",
|
|
1719
|
+
diagnostic: AriaDiagnostics.relevantInvalidTokens(invalid),
|
|
1720
|
+
fix: _AriaPolicyEngine.#makeRemoveAttributeFix("aria-relevant")
|
|
1721
|
+
}
|
|
1722
|
+
];
|
|
1723
|
+
}
|
|
1724
|
+
if (tokens.includes("all") && tokens.length > 1) {
|
|
1725
|
+
return [
|
|
1726
|
+
{
|
|
1727
|
+
valid: false,
|
|
1728
|
+
fixable: true,
|
|
1729
|
+
severity: "warning",
|
|
1730
|
+
attribute: "aria-relevant",
|
|
1731
|
+
diagnostic: AriaDiagnostics.relevantSuperseded(),
|
|
1732
|
+
fix: _AriaPolicyEngine.#normalizeRelevantAllFix
|
|
1733
|
+
}
|
|
1734
|
+
];
|
|
1735
|
+
}
|
|
1736
|
+
return VALID;
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
|
|
1740
|
+
// ../../lib/contract/src/children/get-type-name.ts
|
|
1741
|
+
function getTypeName(value) {
|
|
1742
|
+
if (value === null) return "null";
|
|
1743
|
+
if (value === void 0) return "undefined";
|
|
1744
|
+
const primitive = typeof value;
|
|
1745
|
+
if (primitive !== "object") {
|
|
1746
|
+
return primitive;
|
|
1747
|
+
}
|
|
1748
|
+
const name = value.constructor?.name;
|
|
1749
|
+
return typeof name === "string" && name !== "Object" ? name : "object";
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
// ../../lib/contract/src/children/normalize-child-rule.ts
|
|
1753
|
+
function normalizeCardinality(input, impliesSingleton) {
|
|
1754
|
+
const min = input?.min ?? 0;
|
|
1755
|
+
const max = input?.max ?? (impliesSingleton ? 1 : Infinity);
|
|
1756
|
+
if (min === 0 && max === Infinity) {
|
|
1757
|
+
return { kind: "unbounded" };
|
|
1758
|
+
}
|
|
1759
|
+
if (min > max) {
|
|
1760
|
+
throw new RangeError(`normalizeChildRule: min (${min}) cannot exceed max (${max})`);
|
|
1761
|
+
}
|
|
1762
|
+
return {
|
|
1763
|
+
kind: "bounded",
|
|
1764
|
+
min,
|
|
1765
|
+
max
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
function normalizeChildRule(rule) {
|
|
1769
|
+
const position = rule.position ?? "any";
|
|
1770
|
+
const impliesSingleton = position === "first" || position === "last";
|
|
1771
|
+
return {
|
|
1772
|
+
...rule,
|
|
1773
|
+
position,
|
|
1774
|
+
cardinality: normalizeCardinality(rule.cardinality, impliesSingleton)
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// ../../lib/contract/src/children/rules-matcher.ts
|
|
1779
|
+
function getChildType(child) {
|
|
1780
|
+
if (!isObject(child) || !("type" in child)) return void 0;
|
|
1781
|
+
return child.type;
|
|
1782
|
+
}
|
|
1783
|
+
function buildPartialIndex(rules) {
|
|
1784
|
+
const typeIndex = /* @__PURE__ */ new Map();
|
|
1785
|
+
const duplicateTypes = /* @__PURE__ */ new Set();
|
|
1786
|
+
const untypedIndices = [];
|
|
1787
|
+
iterate.forEach(rules, (rule, ri) => {
|
|
1788
|
+
const t = rule.type;
|
|
1789
|
+
if (t === void 0) {
|
|
1790
|
+
untypedIndices.push(ri);
|
|
1791
|
+
} else if (typeIndex.has(t)) {
|
|
1792
|
+
duplicateTypes.add(t);
|
|
1793
|
+
} else {
|
|
1794
|
+
typeIndex.set(t, ri);
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
if (duplicateTypes.size > 0) {
|
|
1798
|
+
iterate.forEachSet(duplicateTypes, (t) => {
|
|
1799
|
+
typeIndex.delete(t);
|
|
1800
|
+
});
|
|
1801
|
+
iterate.forEach(rules, (rule, ri) => {
|
|
1802
|
+
if (duplicateTypes.has(rule.type)) untypedIndices.push(ri);
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
return { typeIndex, untypedIndices };
|
|
1806
|
+
}
|
|
1807
|
+
var RuleMatcher = class {
|
|
1808
|
+
#rules;
|
|
1809
|
+
#typeIndex;
|
|
1810
|
+
#untypedIndices;
|
|
1811
|
+
constructor(rules) {
|
|
1812
|
+
this.#rules = rules;
|
|
1813
|
+
const { typeIndex, untypedIndices } = buildPartialIndex(rules);
|
|
1814
|
+
this.#typeIndex = typeIndex;
|
|
1815
|
+
this.#untypedIndices = untypedIndices;
|
|
1816
|
+
}
|
|
1817
|
+
match(children) {
|
|
1818
|
+
const forward = /* @__PURE__ */ new Map();
|
|
1819
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
1820
|
+
const unexpectedIndices = /* @__PURE__ */ new Set();
|
|
1821
|
+
const ambiguousIndices = /* @__PURE__ */ new Set();
|
|
1822
|
+
iterate.forEach(this.#rules, (_, ri) => {
|
|
1823
|
+
reverse.set(ri, /* @__PURE__ */ new Set());
|
|
1824
|
+
});
|
|
1825
|
+
iterate.forEach(children, (child, ci) => {
|
|
1826
|
+
const t = getChildType(child);
|
|
1827
|
+
if (t !== void 0) {
|
|
1828
|
+
const ri = this.#typeIndex.get(t);
|
|
1829
|
+
if (ri !== void 0) {
|
|
1830
|
+
let childEntry = forward.get(ci);
|
|
1831
|
+
if (!childEntry) {
|
|
1832
|
+
childEntry = /* @__PURE__ */ new Set();
|
|
1833
|
+
forward.set(ci, childEntry);
|
|
1834
|
+
}
|
|
1835
|
+
childEntry.add(ri);
|
|
1836
|
+
reverse.get(ri).add(ci);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
iterate.forEach(this.#untypedIndices, (ri) => {
|
|
1840
|
+
if (!this.#rules[ri].match(child)) return;
|
|
1841
|
+
let childEntry = forward.get(ci);
|
|
1842
|
+
if (!childEntry) {
|
|
1843
|
+
childEntry = /* @__PURE__ */ new Set();
|
|
1844
|
+
forward.set(ci, childEntry);
|
|
1845
|
+
}
|
|
1846
|
+
childEntry.add(ri);
|
|
1847
|
+
reverse.get(ri).add(ci);
|
|
1848
|
+
});
|
|
1849
|
+
const entry = forward.get(ci);
|
|
1850
|
+
if (!entry) {
|
|
1851
|
+
unexpectedIndices.add(ci);
|
|
1852
|
+
} else if (entry.size > 1) {
|
|
1853
|
+
ambiguousIndices.add(ci);
|
|
1854
|
+
}
|
|
1855
|
+
});
|
|
1856
|
+
return { matrix: { childToRules: { forward, reverse } }, unexpectedIndices, ambiguousIndices };
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
|
|
1860
|
+
// ../../lib/contract/src/children/rule-validator.ts
|
|
1861
|
+
var RuleValidator = class _RuleValidator extends InvariantBase {
|
|
1862
|
+
#context;
|
|
1863
|
+
constructor(context, diagnostics) {
|
|
1864
|
+
super(diagnostics);
|
|
1865
|
+
this.#context = context;
|
|
1866
|
+
}
|
|
1867
|
+
validate(rules, matrix, childCount) {
|
|
1868
|
+
const firstIndex = 0;
|
|
1869
|
+
const lastIndex = childCount - 1;
|
|
1870
|
+
iterate.forEach(rules, (rule, ri) => {
|
|
1871
|
+
const matches = matrix.childToRules.reverse.get(ri);
|
|
1872
|
+
const matchCount = matches.size;
|
|
1873
|
+
this.#validateCardinality(rule, matchCount);
|
|
1874
|
+
if (matchCount === 0) return;
|
|
1875
|
+
this.#validatePositions(rule, matches, firstIndex, lastIndex);
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
#validateCardinality(rule, matchCount) {
|
|
1879
|
+
const { cardinality, name } = rule;
|
|
1880
|
+
if (cardinality.kind !== "bounded") {
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
const { min, max } = cardinality;
|
|
1884
|
+
if (matchCount < min) {
|
|
1885
|
+
this.violate(ContractDiagnostics.cardinalityMin(name, min, this.#context));
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
if (matchCount > max) {
|
|
1889
|
+
this.violate(ContractDiagnostics.cardinalityMax(name, max, this.#context));
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
#validatePositions(rule, matches, firstIndex, lastIndex) {
|
|
1893
|
+
const { name, position } = rule;
|
|
1894
|
+
iterate.forEachSet(matches, (index) => {
|
|
1895
|
+
if (_RuleValidator.#isValidPosition(index, position, firstIndex, lastIndex)) {
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
this.violate(ContractDiagnostics.positionViolation(name, position, index, this.#context));
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
static #isValidPosition(matchIndex, position, firstIndex, lastIndex) {
|
|
1902
|
+
switch (position) {
|
|
1903
|
+
case "first":
|
|
1904
|
+
return matchIndex === firstIndex;
|
|
1905
|
+
case "last":
|
|
1906
|
+
return matchIndex === lastIndex;
|
|
1907
|
+
case "any":
|
|
1908
|
+
return true;
|
|
1909
|
+
default:
|
|
1910
|
+
return assertNever(position);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
// ../../lib/contract/src/children/children-evaluator.ts
|
|
1916
|
+
var ChildrenEvaluator = class extends InvariantBase {
|
|
1917
|
+
#context;
|
|
1918
|
+
#rules;
|
|
1919
|
+
#ruleNames;
|
|
1920
|
+
#matcher;
|
|
1921
|
+
#ruleValidator;
|
|
1922
|
+
constructor(rules, diagnostics, context = "Component") {
|
|
1923
|
+
super(diagnostics);
|
|
1924
|
+
this.#context = context;
|
|
1925
|
+
this.#rules = rules.map((r) => normalizeChildRule(r));
|
|
1926
|
+
this.#ruleNames = this.#rules.map((r) => r.name);
|
|
1927
|
+
iterate.forEach(this.#rules, (rule) => {
|
|
1928
|
+
const { name, position, cardinality } = rule;
|
|
1929
|
+
if ((position === "first" || position === "last") && (cardinality.kind === "unbounded" || cardinality.max > 1)) {
|
|
1930
|
+
throw new RangeError(
|
|
1931
|
+
`ChildrenEvaluator [${context}]: rule "${name}" sets position="${position}" with an unbound or >1 max. position="first|last" implies max=1.`
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
});
|
|
1935
|
+
this.#matcher = new RuleMatcher(this.#rules);
|
|
1936
|
+
this.#ruleValidator = new RuleValidator(context, diagnostics);
|
|
1937
|
+
}
|
|
1938
|
+
evaluate(children) {
|
|
1939
|
+
if (!this.active) return;
|
|
1940
|
+
const { matrix, unexpectedIndices, ambiguousIndices } = this.#matcher.match(children);
|
|
1941
|
+
this.#ruleValidator.validate(this.#rules, matrix, children.length);
|
|
1942
|
+
if (unexpectedIndices.size === 0 && ambiguousIndices.size === 0) return;
|
|
1943
|
+
const violating = [...unexpectedIndices, ...ambiguousIndices].sort((a, b) => a - b);
|
|
1944
|
+
iterate.forEach(violating, (ci) => {
|
|
1945
|
+
const typeName = getTypeName(children[ci]);
|
|
1946
|
+
if (unexpectedIndices.has(ci)) {
|
|
1947
|
+
this.violate(ContractDiagnostics.unexpectedChild(typeName, ci, this.#context));
|
|
1948
|
+
} else {
|
|
1949
|
+
const matches = matrix.childToRules.forward.get(ci);
|
|
1950
|
+
const names = [...matches].map((ri) => this.#ruleNames[ri] ?? `#${ri}`);
|
|
1951
|
+
this.violate(ContractDiagnostics.ambiguousChild(typeName, ci, names, this.#context));
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
|
|
1957
|
+
// ../../lib/contract/src/props/get-disabled-props.ts
|
|
1958
|
+
var disabledProps = ({
|
|
1959
|
+
disabled,
|
|
1960
|
+
"aria-disabled": ariaDisabled,
|
|
1961
|
+
"data-disabled": dataDisabled
|
|
1962
|
+
}) => {
|
|
1963
|
+
if (!disabled) return {};
|
|
1964
|
+
return {
|
|
1965
|
+
...ariaDisabled === void 0 && { "aria-disabled": "true" },
|
|
1966
|
+
...dataDisabled === void 0 && { "data-disabled": "" }
|
|
1967
|
+
};
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
// ../../lib/contract/src/props/get-invalid-props.ts
|
|
1971
|
+
var invalidProps = ({
|
|
1972
|
+
invalid,
|
|
1973
|
+
"aria-invalid": ariaInvalid,
|
|
1974
|
+
"data-invalid": dataInvalid
|
|
1975
|
+
}) => {
|
|
1976
|
+
if (!invalid) return {};
|
|
1977
|
+
return {
|
|
1978
|
+
...ariaInvalid === void 0 && { "aria-invalid": "true" },
|
|
1979
|
+
...dataInvalid === void 0 && { "data-invalid": "" }
|
|
1980
|
+
};
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
// ../../lib/contract/src/props/get-readonly-props.ts
|
|
1984
|
+
var readonlyProps = ({
|
|
1985
|
+
readOnly,
|
|
1986
|
+
"aria-readonly": ariaReadonly,
|
|
1987
|
+
"data-readonly": dataReadonly
|
|
1988
|
+
}) => {
|
|
1989
|
+
if (!readOnly) return {};
|
|
1990
|
+
return {
|
|
1991
|
+
...ariaReadonly === void 0 && {
|
|
1992
|
+
"aria-readonly": "true"
|
|
1993
|
+
},
|
|
1994
|
+
...dataReadonly === void 0 && {
|
|
1995
|
+
"data-readonly": ""
|
|
1996
|
+
}
|
|
1997
|
+
};
|
|
1998
|
+
};
|
|
1999
|
+
|
|
2000
|
+
// ../core/src/html/aria-rules.ts
|
|
2001
|
+
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
2002
|
+
var removeLandmarkRoleOverride = {
|
|
2003
|
+
kind: "removeRole",
|
|
2004
|
+
apply: ({ props }) => {
|
|
2005
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2006
|
+
const { role: _r, ...rest } = props;
|
|
2007
|
+
return { applied: true, next: rest, previous: props };
|
|
2008
|
+
}
|
|
2009
|
+
};
|
|
2010
|
+
function landmarkRoleRule({ tag, props, implicitRole }) {
|
|
2011
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2012
|
+
const role = props.role;
|
|
2013
|
+
if (!role || role === implicitRole) return [];
|
|
2014
|
+
return [
|
|
2015
|
+
{
|
|
2016
|
+
valid: false,
|
|
2017
|
+
fixable: true,
|
|
2018
|
+
severity: "error",
|
|
2019
|
+
fix: removeLandmarkRoleOverride,
|
|
2020
|
+
diagnostic: HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role)
|
|
2021
|
+
}
|
|
2022
|
+
];
|
|
2023
|
+
}
|
|
2024
|
+
function requireAccessibleName({ tag, props }) {
|
|
2025
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2026
|
+
return [
|
|
2027
|
+
{
|
|
2028
|
+
valid: false,
|
|
2029
|
+
fixable: false,
|
|
2030
|
+
severity: "warning",
|
|
2031
|
+
diagnostic: AriaDiagnostics.missingAccessibleName(tag)
|
|
2032
|
+
}
|
|
2033
|
+
];
|
|
2034
|
+
}
|
|
2035
|
+
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
2036
|
+
function landmarkNameAdvisory(ctx) {
|
|
2037
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2038
|
+
return requireAccessibleName(ctx);
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
// ../core/src/html/contracts.ts
|
|
2042
|
+
function isOpenContent(...blockedTags) {
|
|
2043
|
+
const set2 = new Set(blockedTags);
|
|
2044
|
+
return (child) => isObject(child) && "type" in child && (!isString(child.type) || !set2.has(child.type));
|
|
2045
|
+
}
|
|
2046
|
+
var METADATA_TAGS = ["script", "template"];
|
|
2047
|
+
var metadataMatch = isTag(...METADATA_TAGS);
|
|
2048
|
+
function metadata(name = "metadata") {
|
|
2049
|
+
return { name, match: metadataMatch };
|
|
2050
|
+
}
|
|
2051
|
+
function firstOptional(name, tag) {
|
|
2052
|
+
return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
|
|
2053
|
+
}
|
|
2054
|
+
function contract(children) {
|
|
2055
|
+
return { diagnostics: warnDiagnostics, children };
|
|
2056
|
+
}
|
|
2057
|
+
function ariaContract(aria) {
|
|
2058
|
+
return { diagnostics: warnDiagnostics, aria };
|
|
2059
|
+
}
|
|
2060
|
+
function firstChildContract(name, tag) {
|
|
2061
|
+
return contract([firstOptional(name, tag), { name: "content", match: isOpenContent(tag) }]);
|
|
2062
|
+
}
|
|
2063
|
+
var VOID_TAGS = [
|
|
2064
|
+
"area",
|
|
2065
|
+
"base",
|
|
2066
|
+
"br",
|
|
2067
|
+
"col",
|
|
2068
|
+
"embed",
|
|
2069
|
+
"hr",
|
|
2070
|
+
"img",
|
|
2071
|
+
"input",
|
|
2072
|
+
"link",
|
|
2073
|
+
"meta",
|
|
2074
|
+
"param",
|
|
2075
|
+
"source",
|
|
2076
|
+
"track",
|
|
2077
|
+
"wbr"
|
|
2078
|
+
];
|
|
2079
|
+
var TEXT_ONLY_TAGS = ["option", "script", "style", "textarea", "title"];
|
|
2080
|
+
var LANDMARK_TAGS = ["article", "aside", "footer", "header", "main", "nav"];
|
|
2081
|
+
var listContract = contract([{ name: "list-item", match: isTag("li", ...METADATA_TAGS) }]);
|
|
2082
|
+
var tableContract = contract([
|
|
2083
|
+
firstOptional("caption", "caption"),
|
|
2084
|
+
{ name: "colgroup", match: isTag("colgroup") },
|
|
2085
|
+
{ name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
|
|
2086
|
+
{ name: "tbody", match: isTag("tbody") },
|
|
2087
|
+
{ name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
|
|
2088
|
+
{ name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
|
|
2089
|
+
]);
|
|
2090
|
+
var tableBodyContract = contract([
|
|
2091
|
+
{ name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
|
|
2092
|
+
]);
|
|
2093
|
+
var tableRowContract = contract([
|
|
2094
|
+
{ name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
|
|
2095
|
+
]);
|
|
2096
|
+
var colgroupContract = contract([{ name: "column", match: isTag("col", "template") }]);
|
|
2097
|
+
var dlContract = contract([
|
|
2098
|
+
{ name: "term", match: isTag("dt") },
|
|
2099
|
+
{ name: "description", match: isTag("dd") },
|
|
2100
|
+
{ name: "group", match: isTag("div") },
|
|
2101
|
+
metadata()
|
|
2102
|
+
]);
|
|
2103
|
+
var selectContract = contract([
|
|
2104
|
+
{ name: "option", match: isTag("option", "optgroup", "hr", ...METADATA_TAGS) }
|
|
2105
|
+
]);
|
|
2106
|
+
var optgroupContract = contract([
|
|
2107
|
+
{ name: "option", match: isTag("option", ...METADATA_TAGS) }
|
|
2108
|
+
]);
|
|
2109
|
+
var datalistContract = contract([
|
|
2110
|
+
{ name: "option", match: isTag("option", ...METADATA_TAGS) }
|
|
2111
|
+
]);
|
|
2112
|
+
var pictureContract = contract([
|
|
2113
|
+
{ name: "source", match: isTag("source", ...METADATA_TAGS) },
|
|
2114
|
+
{ name: "image", match: isTag("img"), cardinality: { min: 1, max: 1 }, position: "last" }
|
|
2115
|
+
]);
|
|
2116
|
+
var figureContract = contract([
|
|
2117
|
+
{ name: "caption", match: isTag("figcaption"), cardinality: { max: 1 } },
|
|
2118
|
+
{ name: "content", match: isOpenContent("figcaption") }
|
|
2119
|
+
]);
|
|
2120
|
+
var detailsContract = firstChildContract("summary", "summary");
|
|
2121
|
+
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2122
|
+
var mediaContract = contract([
|
|
2123
|
+
{ name: "source", match: isTag("source") },
|
|
2124
|
+
{ name: "track", match: isTag("track") },
|
|
2125
|
+
metadata(),
|
|
2126
|
+
{ name: "content", match: isOpenContent("source", "track", ...METADATA_TAGS) }
|
|
2127
|
+
]);
|
|
2128
|
+
var headContract = contract([
|
|
2129
|
+
{
|
|
2130
|
+
name: "metadata",
|
|
2131
|
+
match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
|
|
2132
|
+
}
|
|
2133
|
+
]);
|
|
2134
|
+
var htmlContract = contract([
|
|
2135
|
+
{ name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
|
|
2136
|
+
{ name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
|
|
2137
|
+
]);
|
|
2138
|
+
var voidContract = contract([]);
|
|
2139
|
+
var textOnlyContract = contract([
|
|
2140
|
+
{
|
|
2141
|
+
name: "text",
|
|
2142
|
+
match: (child) => isString(child) || isNumber(child)
|
|
2143
|
+
}
|
|
2144
|
+
]);
|
|
2145
|
+
var landmarkContract = ariaContract([landmarkRoleRule, landmarkNameAdvisory]);
|
|
2146
|
+
var dialogContract = ariaContract([requireAccessibleName]);
|
|
2147
|
+
var menuContract = ariaContract([requireAccessibleName]);
|
|
2148
|
+
var menubarContract = ariaContract([requireAccessibleName]);
|
|
2149
|
+
var treeContract = ariaContract([requireAccessibleName]);
|
|
2150
|
+
var gridContract = ariaContract([requireAccessibleName]);
|
|
2151
|
+
var listboxContract = ariaContract([requireAccessibleName]);
|
|
2152
|
+
var tablistContract = ariaContract([requireAccessibleName]);
|
|
2153
|
+
var radiogroupContract = ariaContract([requireAccessibleName]);
|
|
2154
|
+
var CONTRACT_GROUPS = [
|
|
2155
|
+
[VOID_TAGS, voidContract],
|
|
2156
|
+
[TEXT_ONLY_TAGS, textOnlyContract],
|
|
2157
|
+
[LANDMARK_TAGS, landmarkContract],
|
|
2158
|
+
[["ul", "ol", "menu"], listContract],
|
|
2159
|
+
[["audio", "video"], mediaContract],
|
|
2160
|
+
[["thead", "tbody", "tfoot"], tableBodyContract]
|
|
2161
|
+
];
|
|
2162
|
+
function contractMap(groups) {
|
|
2163
|
+
return Object.fromEntries(
|
|
2164
|
+
groups.flatMap(([tags, enforcement]) => tags.map((tag) => [tag, enforcement]))
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
var htmlContracts = {
|
|
2168
|
+
...contractMap(CONTRACT_GROUPS),
|
|
2169
|
+
table: tableContract,
|
|
2170
|
+
tr: tableRowContract,
|
|
2171
|
+
colgroup: colgroupContract,
|
|
2172
|
+
dl: dlContract,
|
|
2173
|
+
select: selectContract,
|
|
2174
|
+
optgroup: optgroupContract,
|
|
2175
|
+
datalist: datalistContract,
|
|
2176
|
+
picture: pictureContract,
|
|
2177
|
+
figure: figureContract,
|
|
2178
|
+
details: detailsContract,
|
|
2179
|
+
fieldset: fieldsetContract,
|
|
2180
|
+
dialog: dialogContract,
|
|
2181
|
+
head: headContract,
|
|
2182
|
+
html: htmlContract
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
// ../core/src/html/evaluators.ts
|
|
2186
|
+
var htmlDiagnostics = warnDiagnostics;
|
|
2187
|
+
function buildEvaluatorMap() {
|
|
2188
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
2189
|
+
iterate.forEachEntry(htmlContracts, (tag, { children }) => {
|
|
2190
|
+
if (children?.length) {
|
|
2191
|
+
map2.set(tag, new ChildrenEvaluator(children, htmlDiagnostics, `<${tag}>`));
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
return map2;
|
|
2195
|
+
}
|
|
2196
|
+
var HTML_EVALUATORS = buildEvaluatorMap();
|
|
2197
|
+
function getHtmlChildrenEvaluator(tag) {
|
|
2198
|
+
return typeof tag === "string" ? HTML_EVALUATORS.get(tag) : void 0;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// ../core/src/html/prop-normalizers.ts
|
|
2202
|
+
var HTML_FORM_NORMALIZERS = /* @__PURE__ */ new Map([
|
|
2203
|
+
["button", [disabledProps]],
|
|
2204
|
+
["input", [disabledProps, readonlyProps, invalidProps]],
|
|
2205
|
+
["select", [disabledProps]],
|
|
2206
|
+
["textarea", [disabledProps, readonlyProps]],
|
|
2207
|
+
["fieldset", [disabledProps]],
|
|
2208
|
+
["optgroup", [disabledProps]]
|
|
2209
|
+
]);
|
|
2210
|
+
function getHtmlPropNormalizers(tag) {
|
|
2211
|
+
return typeof tag === "string" ? HTML_FORM_NORMALIZERS.get(tag) : void 0;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// ../../lib/styling/src/variant-pass/compile-variant-lookup.ts
|
|
2215
|
+
function buildPrecomputedKey(props) {
|
|
2216
|
+
return Object.entries(props).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`).join("|");
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
// ../../lib/styling/src/variant-pass/variant-pass.ts
|
|
2220
|
+
function createVariantPass(activeProps, config) {
|
|
2221
|
+
return {
|
|
2222
|
+
name: "variant",
|
|
2223
|
+
execute() {
|
|
2224
|
+
const preset = typeof activeProps["preset"] === "string" ? activeProps["preset"] : void 0;
|
|
2225
|
+
const presetDefaults = preset !== void 0 && config.presets?.[preset] !== void 0 ? config.presets[preset] : {};
|
|
2226
|
+
const resolved = {
|
|
2227
|
+
...config.defaults,
|
|
2228
|
+
...presetDefaults,
|
|
2229
|
+
...activeProps
|
|
2230
|
+
};
|
|
2231
|
+
const classes = [];
|
|
2232
|
+
iterate.forEachEntry(config.variants, (key, valueMap) => {
|
|
2233
|
+
const active = resolved[key];
|
|
2234
|
+
if (typeof active === "string") {
|
|
2235
|
+
const cls = valueMap[active];
|
|
2236
|
+
if (cls !== void 0) classes.push(cls);
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
return { context: { classes } };
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// ../core/src/resolver/resolver.ts
|
|
2245
|
+
function enforceAllowedAs(tag, allowedAs, diagnostics, displayName) {
|
|
2246
|
+
if (allowedAs.includes(tag)) return;
|
|
2247
|
+
if (!diagnostics) return;
|
|
2248
|
+
const component = displayName ?? String(tag);
|
|
2249
|
+
diagnostics.error(ContractDiagnostics.allowedAsViolation(String(tag), allowedAs, component));
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
// ../../lib/adapter-utils/src/apply-prop-normalizers.ts
|
|
2253
|
+
function applyPropNormalizers(tag, props, additional) {
|
|
2254
|
+
const normalizers = [...getHtmlPropNormalizers(tag) ?? [], ...additional ?? []];
|
|
2255
|
+
if (normalizers.length === 0) return props;
|
|
2256
|
+
return normalizers.reduce((acc, fn) => ({ ...acc, ...fn(acc) }), props);
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// ../../lib/adapter-utils/src/build-engines.ts
|
|
2260
|
+
function buildEngines(diagnostics, childRules, context) {
|
|
2261
|
+
return childRules?.length ? { childrenEvaluator: new ChildrenEvaluator(childRules, diagnostics, context) } : {};
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
// ../../lib/adapter-utils/src/resolve-adapter-common-options.ts
|
|
2265
|
+
function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponent", defaultDiagnostics = throwDiagnostics) {
|
|
2266
|
+
return {
|
|
2267
|
+
name: options.name ?? defaultName,
|
|
2268
|
+
diagnostics: options.enforcement?.diagnostics ?? defaultDiagnostics
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// ../../lib/adapter-utils/src/slot-validator.ts
|
|
2273
|
+
var SlotValidator = class extends InvariantBase {
|
|
2274
|
+
#name;
|
|
2275
|
+
#elementTerm;
|
|
2276
|
+
constructor(name, diagnostics, elementTerm) {
|
|
2277
|
+
super(diagnostics);
|
|
2278
|
+
this.#name = name;
|
|
2279
|
+
this.#elementTerm = elementTerm;
|
|
2280
|
+
}
|
|
2281
|
+
assertExclusive() {
|
|
2282
|
+
this.violate(SlotDiagnostics.exclusive(this.#name));
|
|
2283
|
+
}
|
|
2284
|
+
warnDiscardedChildren(count) {
|
|
2285
|
+
this.warn(SlotDiagnostics.discardedChildren(this.#name, this.#elementTerm, count));
|
|
2286
|
+
}
|
|
2287
|
+
assertSingleChild(count) {
|
|
2288
|
+
this.violate(
|
|
2289
|
+
count === 0 ? SlotDiagnostics.singleChildRequired(this.#name, this.#elementTerm) : SlotDiagnostics.singleChildExceeded(this.#name, this.#elementTerm, count)
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
|
|
2294
|
+
// ../../lib/adapter-utils/src/slot/policies.ts
|
|
2295
|
+
import { clsx } from "clsx";
|
|
2296
|
+
function chainHandlers(childHandler, slotHandler) {
|
|
2297
|
+
return (...args) => {
|
|
2298
|
+
childHandler(...args);
|
|
2299
|
+
const event = args[0];
|
|
2300
|
+
if (!(typeof event === "object" && event !== null && "defaultPrevented" in event && event.defaultPrevented)) {
|
|
2301
|
+
slotHandler(...args);
|
|
2302
|
+
}
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
function mergeClassNames(slot, child) {
|
|
2306
|
+
return clsx(slot, child);
|
|
2307
|
+
}
|
|
2308
|
+
function mergeStyles(slot, child) {
|
|
2309
|
+
if (!isPlainObject(slot) || !isPlainObject(child)) return child;
|
|
2310
|
+
return { ...slot, ...child };
|
|
2311
|
+
}
|
|
2312
|
+
var policyHandlers = {
|
|
2313
|
+
chain: (slotVal, childVal) => chainHandlers(childVal, slotVal),
|
|
2314
|
+
concat: (slotVal, childVal) => mergeClassNames(slotVal, childVal),
|
|
2315
|
+
"shallow-merge": (slotVal, childVal) => mergeStyles(slotVal, childVal),
|
|
2316
|
+
"child-wins": (_slotVal, childVal) => childVal
|
|
2317
|
+
};
|
|
2318
|
+
|
|
2319
|
+
// ../../lib/adapter-utils/src/slot/merge-slot-props.ts
|
|
2320
|
+
function mergeSlotProps(slotProps, childProps) {
|
|
2321
|
+
const merged = { ...slotProps };
|
|
2322
|
+
iterate.forEachKey(childProps, (key) => {
|
|
2323
|
+
if (!Object.hasOwn(childProps, key)) return;
|
|
2324
|
+
merged[key] = applyMergePolicy(key, slotProps[key], childProps[key]);
|
|
2325
|
+
});
|
|
2326
|
+
return merged;
|
|
2327
|
+
}
|
|
2328
|
+
function classifyProp(key, slotVal, childVal) {
|
|
2329
|
+
if (EVENT_HANDLER_RE.test(key) && isFunction(slotVal) && isFunction(childVal)) return "chain";
|
|
2330
|
+
if (key === "className") return "concat";
|
|
2331
|
+
if (key === "style") return "shallow-merge";
|
|
2332
|
+
return "child-wins";
|
|
2333
|
+
}
|
|
2334
|
+
function applyMergePolicy(key, slotVal, childVal) {
|
|
2335
|
+
return policyHandlers[classifyProp(key, slotVal, childVal)](slotVal, childVal);
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
// ../../lib/adapter-utils/src/build-definition.ts
|
|
2339
|
+
function buildDefinition(name, tag) {
|
|
2340
|
+
return {
|
|
2341
|
+
identity: {
|
|
2342
|
+
id: name,
|
|
2343
|
+
name,
|
|
2344
|
+
tag
|
|
2345
|
+
},
|
|
2346
|
+
capabilities: {},
|
|
2347
|
+
metadata: {},
|
|
2348
|
+
diagnostics: []
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
// ../../lib/adapter-utils/src/build-variant-config.ts
|
|
2353
|
+
function flattenClassName(cls) {
|
|
2354
|
+
return Array.isArray(cls) ? cls.join(" ") : cls;
|
|
2355
|
+
}
|
|
2356
|
+
function mapVariantRecord(source, mapper) {
|
|
2357
|
+
return iterate.mapValues(
|
|
2358
|
+
source,
|
|
2359
|
+
(inner) => iterate.mapValues(inner, mapper)
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
function buildVariantConfig(variants, presets, defaults, compounds) {
|
|
2363
|
+
return {
|
|
2364
|
+
variants: mapVariantRecord(variants ?? {}, flattenClassName),
|
|
2365
|
+
...presets !== void 0 && Object.keys(presets).length > 0 && { presets },
|
|
2366
|
+
...defaults !== void 0 && Object.keys(defaults).length > 0 && { defaults },
|
|
2367
|
+
...compounds !== void 0 && compounds.length > 0 && {
|
|
2368
|
+
compounds
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// ../../runtime/core/src/apply-attributes.ts
|
|
2374
|
+
function applyAttributes(nodeId, incoming, decoration, removedKeys) {
|
|
2375
|
+
const existing = decoration[nodeId] ?? {};
|
|
2376
|
+
const next = { ...existing.attributes };
|
|
2377
|
+
if (removedKeys !== void 0) {
|
|
2378
|
+
iterate.forEachSet(removedKeys, (key) => {
|
|
2379
|
+
delete next[key];
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
Object.assign(next, incoming);
|
|
2383
|
+
const { attributes: _dropped, ...rest } = existing;
|
|
2384
|
+
const nextDecoration = Object.keys(next).length > 0 ? { ...rest, attributes: next } : rest;
|
|
2385
|
+
return { ...decoration, [nodeId]: nextDecoration };
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
// ../../runtime/core/src/get-active-props.ts
|
|
2389
|
+
function getActiveProps(nodeId, decoration) {
|
|
2390
|
+
const dec = decoration[nodeId];
|
|
2391
|
+
return { ...dec?.attributes ?? {}, ...dec?.variants ?? {} };
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
// ../../runtime/core/src/render-component.ts
|
|
2395
|
+
function renderComponent(definition, tree, render, backend) {
|
|
2396
|
+
return backend.render({ definition, tree, render });
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// ../../runtime/core/src/build-tree-context.ts
|
|
2400
|
+
function buildNode(input, slotAssignments, seenIds, diagnostics) {
|
|
2401
|
+
if (seenIds.has(input.id)) {
|
|
2402
|
+
diagnostics.push({
|
|
2403
|
+
code: "duplicate-node-id",
|
|
2404
|
+
message: `Duplicate node id "${input.id}"`,
|
|
2405
|
+
severity: "error"
|
|
2406
|
+
});
|
|
2407
|
+
} else {
|
|
2408
|
+
seenIds.add(input.id);
|
|
2409
|
+
}
|
|
2410
|
+
if (input.slot !== void 0) {
|
|
2411
|
+
slotAssignments.set(input.id, input.slot);
|
|
2412
|
+
}
|
|
2413
|
+
const children = Object.freeze(
|
|
2414
|
+
(input.children ?? []).map((child) => buildNode(child, slotAssignments, seenIds, diagnostics))
|
|
2415
|
+
);
|
|
2416
|
+
if (input.kind === "native") {
|
|
2417
|
+
return Object.freeze({ kind: "native", id: input.id, tag: input.tag, children });
|
|
2418
|
+
}
|
|
2419
|
+
return Object.freeze({ kind: "component", id: input.id, children });
|
|
2420
|
+
}
|
|
2421
|
+
function buildTreeContext(root) {
|
|
2422
|
+
const slotAssignments = /* @__PURE__ */ new Map();
|
|
2423
|
+
const diagnostics = [];
|
|
2424
|
+
const rootNode = buildNode(root, slotAssignments, /* @__PURE__ */ new Set(), diagnostics);
|
|
2425
|
+
return Object.freeze({ root: rootNode, slotAssignments, diagnostics });
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// ../../runtime/core/src/build-render-context.ts
|
|
2429
|
+
function buildRenderContext(decoration = {}) {
|
|
2430
|
+
return { decoration: new Map(Object.entries(decoration)) };
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
// ../../lib/adapter-utils/src/resolve-compounds.ts
|
|
2434
|
+
function matchesCompound(active, compound) {
|
|
2435
|
+
const { class: _, ...conditions } = compound;
|
|
2436
|
+
return Object.entries(conditions).every(([key, expected]) => {
|
|
2437
|
+
const actual = active[key];
|
|
2438
|
+
return Array.isArray(expected) ? expected.includes(actual) : actual === expected;
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
function resolveCompounds(active, compounds) {
|
|
2442
|
+
if (!compounds?.length) return [];
|
|
2443
|
+
const out = [];
|
|
2444
|
+
iterate.forEach(compounds, (compound) => {
|
|
2445
|
+
if (matchesCompound(active, compound)) {
|
|
2446
|
+
out.push(flattenClassName(compound.class));
|
|
2447
|
+
}
|
|
2448
|
+
});
|
|
2449
|
+
return out;
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
// ../../lib/adapter-utils/src/resolve-classes.ts
|
|
2453
|
+
function resolveClasses(decoration, variantConfig, variantDefaults, recipe, compounds, variantLookup) {
|
|
2454
|
+
const active = getActiveProps("root", decoration);
|
|
2455
|
+
if (variantLookup !== void 0 && recipe === void 0) {
|
|
2456
|
+
const activeVariants = {};
|
|
2457
|
+
iterate.forEachKey(variantConfig.variants, (key) => {
|
|
2458
|
+
const value = active[key];
|
|
2459
|
+
if (typeof value === "string") activeVariants[key] = value;
|
|
2460
|
+
});
|
|
2461
|
+
const lookupKey = buildPrecomputedKey(activeVariants);
|
|
2462
|
+
const precomputedValue = variantLookup[lookupKey];
|
|
2463
|
+
if (precomputedValue !== void 0) {
|
|
2464
|
+
return {
|
|
2465
|
+
variantClasses: precomputedValue !== "" ? [precomputedValue] : [],
|
|
2466
|
+
compoundClasses: []
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
const passInput = recipe !== void 0 ? { ...active, preset: recipe } : active;
|
|
2471
|
+
const { context } = createVariantPass(passInput, variantConfig).execute({ classes: [] });
|
|
2472
|
+
const variantClasses = context.classes;
|
|
2473
|
+
const presetOverrides = recipe !== void 0 ? variantConfig.presets?.[recipe] ?? {} : {};
|
|
2474
|
+
const resolvedValues = {
|
|
2475
|
+
...variantDefaults,
|
|
2476
|
+
...presetOverrides,
|
|
2477
|
+
...active
|
|
2478
|
+
};
|
|
2479
|
+
const compoundClasses = resolveCompounds(resolvedValues, compounds);
|
|
2480
|
+
return { variantClasses, compoundClasses };
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
// ../../lib/adapter-utils/src/build-pipeline.ts
|
|
2484
|
+
function buildStylePipeline(variants, presets, defaults, compounds, variantLookup) {
|
|
2485
|
+
if (variants === void 0 && compounds === void 0 && presets === void 0 && variantLookup === void 0) {
|
|
2486
|
+
return void 0;
|
|
2487
|
+
}
|
|
2488
|
+
const variantConfig = buildVariantConfig(variants, presets, defaults, compounds);
|
|
2489
|
+
return {
|
|
2490
|
+
execute(decoration, recipe) {
|
|
2491
|
+
return resolveClasses(decoration, variantConfig, defaults, recipe, compounds, variantLookup);
|
|
2492
|
+
}
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
// ../../lib/adapter-utils/src/join-classes.ts
|
|
2497
|
+
function joinClasses(...classes) {
|
|
2498
|
+
return classes.filter(Boolean).join(" ");
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
// ../../lib/adapter-utils/src/decoration-utils.ts
|
|
2502
|
+
function withAttributes(dec, attributes) {
|
|
2503
|
+
const { attributes: _a, ...rest } = dec;
|
|
2504
|
+
return attributes !== void 0 ? { ...rest, attributes } : rest;
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
// ../../lib/adapter-utils/src/apply-aria.ts
|
|
2508
|
+
function applyAria(decoration, tag, ariaEngine) {
|
|
2509
|
+
if (ariaEngine === void 0) return decoration;
|
|
2510
|
+
const dec = decoration["root"];
|
|
2511
|
+
if (dec?.attributes === void 0) return decoration;
|
|
2512
|
+
const result = ariaEngine.validate(tag, dec.attributes);
|
|
2513
|
+
const cleaned = result.props;
|
|
2514
|
+
return {
|
|
2515
|
+
...decoration,
|
|
2516
|
+
root: withAttributes(dec, Object.keys(cleaned).length > 0 ? cleaned : void 0)
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
// ../../lib/adapter-utils/src/apply-filter-props.ts
|
|
2521
|
+
function applyFilterProps(decoration, filterFn, variantKeys) {
|
|
2522
|
+
if (filterFn === void 0) return decoration;
|
|
2523
|
+
const dec = decoration["root"];
|
|
2524
|
+
if (dec?.attributes === void 0) return decoration;
|
|
2525
|
+
const kept = Object.fromEntries(
|
|
2526
|
+
Object.entries(dec.attributes).filter(([k]) => !filterFn(k, variantKeys))
|
|
2527
|
+
);
|
|
2528
|
+
return {
|
|
2529
|
+
...decoration,
|
|
2530
|
+
root: withAttributes(dec, Object.keys(kept).length > 0 ? kept : void 0)
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
// ../../lib/adapter-utils/src/apply-ref.ts
|
|
2535
|
+
function applyRef(decoration, ref) {
|
|
2536
|
+
if (ref == null) return decoration;
|
|
2537
|
+
const existing = decoration["root"] ?? {};
|
|
2538
|
+
return { ...decoration, root: { ...existing, ref } };
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// ../../adapters/react/src/shared/merge-refs.ts
|
|
2542
|
+
function mergeRefs(...refs) {
|
|
2543
|
+
return mergeRefsCore(...refs);
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
// ../../adapters/react/src/shared/slot/Slottable.tsx
|
|
2547
|
+
import { Fragment } from "react";
|
|
2548
|
+
import { jsx } from "react/jsx-runtime";
|
|
2549
|
+
function Slottable({ children }) {
|
|
2550
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
// ../../adapters/react/src/shared/slot/clone.ts
|
|
2554
|
+
import { cloneElement } from "react";
|
|
2555
|
+
function cloneWithProps(child, props, ref) {
|
|
2556
|
+
return cloneElement(child, ref !== null ? { ...props, ref } : props);
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
// ../../adapters/react/src/shared/slot/applySlot.ts
|
|
2560
|
+
import { isValidElement as isValidElement3 } from "react";
|
|
2561
|
+
|
|
2562
|
+
// ../../adapters/react/src/shared/slot/extractSlottable.ts
|
|
2563
|
+
import { createElement, Fragment as Fragment2, isValidElement as isValidElement2 } from "react";
|
|
2564
|
+
|
|
2565
|
+
// ../../adapters/react/src/shared/slot/predicates.ts
|
|
2566
|
+
import { isValidElement } from "react";
|
|
2567
|
+
function isSlottableElement(value) {
|
|
2568
|
+
return isValidElement(value) && value.type === Slottable;
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
// ../../adapters/react/src/shared/slot/extractSlottable.ts
|
|
2572
|
+
function extractSlottable(children) {
|
|
2573
|
+
const childrenArray = Array.isArray(children) ? children : [children];
|
|
2574
|
+
const slottables = childrenArray.filter(isSlottableElement);
|
|
2575
|
+
invariant(slottables.length <= 1, "Slot: multiple Slottable children are not allowed");
|
|
2576
|
+
if (slottables.length === 0) {
|
|
2577
|
+
return null;
|
|
2578
|
+
}
|
|
2579
|
+
const [slottable] = slottables;
|
|
2580
|
+
invariant(slottable, "Missing Slottable element");
|
|
2581
|
+
const child = slottable.props.children;
|
|
2582
|
+
invariant(
|
|
2583
|
+
child !== null && child !== void 0,
|
|
2584
|
+
"Slottable expects exactly one React element child, received null"
|
|
2585
|
+
);
|
|
2586
|
+
invariant(
|
|
2587
|
+
typeof child !== "string" && typeof child !== "number",
|
|
2588
|
+
"Slottable expects exactly one React element child, received text content"
|
|
2589
|
+
);
|
|
2590
|
+
invariant(isValidElement2(child), "Slottable expects exactly one React element child");
|
|
2591
|
+
invariant(child.type !== Fragment2, "Slottable child cannot be a Fragment");
|
|
2592
|
+
const index = childrenArray.indexOf(slottable);
|
|
2593
|
+
return {
|
|
2594
|
+
child,
|
|
2595
|
+
// Fragment wrapper: Slot owns no DOM node; ref composition happens only on the merge target.
|
|
2596
|
+
rebuild(merged) {
|
|
2597
|
+
const out = childrenArray.map((node, i) => i === index ? merged : node);
|
|
2598
|
+
return createElement(Fragment2, null, ...out);
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// ../../adapters/react/src/shared/slot/applySlot.ts
|
|
2604
|
+
function applySlot(children, slotProps, ref, cloneSlotChild) {
|
|
2605
|
+
const extraction = extractSlottable(children);
|
|
2606
|
+
if (extraction) {
|
|
2607
|
+
const merged = cloneSlotChild({ child: extraction.child, slotProps, ref });
|
|
2608
|
+
return extraction.rebuild(merged);
|
|
2609
|
+
}
|
|
2610
|
+
invariant(isValidElement3(children), "Slot: child must be a valid React element");
|
|
2611
|
+
return cloneSlotChild({ child: children, slotProps, ref });
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
// ../../adapters/react/src/shared/slot/make-render-as-child.ts
|
|
2615
|
+
import { isValidElement as isValidElement4 } from "react";
|
|
2616
|
+
function makeRenderAsChild(cloneSlotChild) {
|
|
2617
|
+
return function renderAsChild(children, className, ref) {
|
|
2618
|
+
if (!isValidElement4(children)) throw new Error("asChild requires a React element child");
|
|
2619
|
+
const slotProps = className ? { className } : {};
|
|
2620
|
+
return cloneSlotChild({ child: children, slotProps, ref: ref ?? null });
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
// ../../adapters/react/src/shared/apply-display-name.ts
|
|
2625
|
+
function applyDisplayName(component, name) {
|
|
2626
|
+
const displayName = name ?? "PolymorphicComponent";
|
|
2627
|
+
Object.assign(component, {
|
|
2628
|
+
displayName,
|
|
2629
|
+
[COMPONENT_ID]: /* @__PURE__ */ Symbol.for(`praxis.component.${displayName}`)
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
// ../../adapters/react/src/backend/extract-decoration.ts
|
|
2634
|
+
function isStyleValue(v) {
|
|
2635
|
+
return isString(v) || isNumber(v);
|
|
2636
|
+
}
|
|
2637
|
+
function isAttributeValue(v) {
|
|
2638
|
+
return isString(v) || isNumber(v) || typeof v === "boolean";
|
|
2639
|
+
}
|
|
2640
|
+
function assignIfNotEmpty(target, key, value) {
|
|
2641
|
+
if (Object.keys(value).length > 0) {
|
|
2642
|
+
target[key] = value;
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
function extractStyles(value, styles) {
|
|
2646
|
+
if (!isObject(value)) return false;
|
|
2647
|
+
iterate.forEachEntry(value, (k, v) => {
|
|
2648
|
+
if (isStyleValue(v)) {
|
|
2649
|
+
styles[k] = v;
|
|
2650
|
+
}
|
|
2651
|
+
});
|
|
2652
|
+
return true;
|
|
2653
|
+
}
|
|
2654
|
+
function extractListener(key, value, listeners) {
|
|
2655
|
+
if (!key.startsWith("on") || !isFunction(value)) {
|
|
2656
|
+
return false;
|
|
2657
|
+
}
|
|
2658
|
+
listeners[key] = value;
|
|
2659
|
+
return true;
|
|
2660
|
+
}
|
|
2661
|
+
function extractAttributeOrVariant(key, value, attributes, variants, variantKeys) {
|
|
2662
|
+
if (variantKeys?.has(key)) {
|
|
2663
|
+
variants[key] = value;
|
|
2664
|
+
} else {
|
|
2665
|
+
attributes[key] = value;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
function extractDecoration(props, variantKeys) {
|
|
2669
|
+
const attributes = {};
|
|
2670
|
+
const styles = {};
|
|
2671
|
+
const listeners = {};
|
|
2672
|
+
const variants = {};
|
|
2673
|
+
const dec = {};
|
|
2674
|
+
iterate.forEachEntry(props, (key, value) => {
|
|
2675
|
+
if (key === "children" || key === "slot" || key === "ref") return;
|
|
2676
|
+
if (key === "style" && extractStyles(value, styles)) return;
|
|
2677
|
+
if (extractListener(key, value, listeners)) return;
|
|
2678
|
+
if (isAttributeValue(value)) {
|
|
2679
|
+
extractAttributeOrVariant(key, value, attributes, variants, variantKeys);
|
|
2680
|
+
}
|
|
2681
|
+
});
|
|
2682
|
+
assignIfNotEmpty(dec, "attributes", attributes);
|
|
2683
|
+
assignIfNotEmpty(dec, "styles", styles);
|
|
2684
|
+
assignIfNotEmpty(dec, "listeners", listeners);
|
|
2685
|
+
assignIfNotEmpty(dec, "variants", variants);
|
|
2686
|
+
const ref = props.ref;
|
|
2687
|
+
if (ref !== void 0) {
|
|
2688
|
+
dec.ref = ref;
|
|
2689
|
+
}
|
|
2690
|
+
return dec;
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
// ../../adapters/react/src/current/helpers/render-normally.ts
|
|
2694
|
+
import { cloneElement as cloneElement2 } from "react";
|
|
2695
|
+
|
|
2696
|
+
// ../../adapters/react/src/backend/react-backend.ts
|
|
2697
|
+
import React from "react";
|
|
2698
|
+
|
|
2699
|
+
// ../../adapters/react/src/backend/build-props.ts
|
|
2700
|
+
function buildPropsFromDecoration(id, decoration) {
|
|
2701
|
+
return {
|
|
2702
|
+
key: id,
|
|
2703
|
+
...decoration?.attributes,
|
|
2704
|
+
...decoration?.styles !== void 0 ? { style: decoration.styles } : {},
|
|
2705
|
+
...decoration?.listeners,
|
|
2706
|
+
...decoration?.ref !== void 0 ? { ref: decoration.ref } : {}
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
// ../../adapters/react/src/backend/react-backend.ts
|
|
2711
|
+
function renderNode(node, render) {
|
|
2712
|
+
const children = node.children.map((child) => renderNode(child, render));
|
|
2713
|
+
if (node.kind === "component") {
|
|
2714
|
+
return React.createElement(React.Fragment, { key: node.id }, ...children);
|
|
2715
|
+
}
|
|
2716
|
+
const decoration = render.decoration.get(node.id);
|
|
2717
|
+
const props = buildPropsFromDecoration(node.id, decoration);
|
|
2718
|
+
return React.createElement(node.tag, props, ...children);
|
|
2719
|
+
}
|
|
2720
|
+
var reactBackend = {
|
|
2721
|
+
render(context) {
|
|
2722
|
+
return renderNode(context.tree.root, context.render);
|
|
2723
|
+
}
|
|
2724
|
+
};
|
|
2725
|
+
|
|
2726
|
+
// ../../adapters/react/src/current/helpers/render-normally.ts
|
|
2727
|
+
function renderNormally(definition, tag, decoration, children) {
|
|
2728
|
+
const treeCtx = buildTreeContext({ kind: "native", tag, id: "root", children: [] });
|
|
2729
|
+
const rendered = renderComponent(
|
|
2730
|
+
definition,
|
|
2731
|
+
treeCtx,
|
|
2732
|
+
buildRenderContext(decoration),
|
|
2733
|
+
reactBackend
|
|
2734
|
+
);
|
|
2735
|
+
return children === void 0 ? rendered : cloneElement2(rendered, {}, children);
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
// ../../adapters/react/src/current/helpers/render-with-callback.ts
|
|
2739
|
+
function renderWithCallback(render, domProps, className, ref) {
|
|
2740
|
+
const props = { ...domProps };
|
|
2741
|
+
if (className) props.className = className;
|
|
2742
|
+
if (ref !== void 0) props.ref = ref;
|
|
2743
|
+
return render(props);
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
export {
|
|
2747
|
+
isFunction,
|
|
2748
|
+
isPlainObject,
|
|
2749
|
+
SLOT_NAME,
|
|
2750
|
+
COMPONENT_DEFAULT_TAG,
|
|
2751
|
+
AriaPolicyEngine,
|
|
2752
|
+
getHtmlChildrenEvaluator,
|
|
2753
|
+
enforceAllowedAs,
|
|
2754
|
+
applyPropNormalizers,
|
|
2755
|
+
defineContractComponent,
|
|
2756
|
+
buildEngines,
|
|
2757
|
+
resolveAdapterCommonOptions,
|
|
2758
|
+
SlotValidator,
|
|
2759
|
+
mergeSlotProps,
|
|
2760
|
+
buildDefinition,
|
|
2761
|
+
flattenClassName,
|
|
2762
|
+
applyAttributes,
|
|
2763
|
+
buildStylePipeline,
|
|
2764
|
+
joinClasses,
|
|
2765
|
+
applyAria,
|
|
2766
|
+
applyFilterProps,
|
|
2767
|
+
applyRef,
|
|
2768
|
+
mergeRefs,
|
|
2769
|
+
applyDisplayName,
|
|
2770
|
+
cloneWithProps,
|
|
2771
|
+
Slottable,
|
|
2772
|
+
applySlot,
|
|
2773
|
+
makeRenderAsChild,
|
|
2774
|
+
extractDecoration,
|
|
2775
|
+
renderNormally,
|
|
2776
|
+
renderWithCallback
|
|
2777
|
+
};
|