praxis-kit 0.1.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/dist/_shared/diagnostics.d.ts +312 -0
  4. package/dist/_shared/diagnostics.js +360 -0
  5. package/dist/build-runtime-CJ_nQEaZ.js +5065 -0
  6. package/dist/codemod/index.d.ts +2 -0
  7. package/dist/codemod/index.js +176520 -0
  8. package/dist/contract/index.d.ts +677 -0
  9. package/dist/contract/index.js +341 -0
  10. package/dist/eslint/index.d.ts +90 -0
  11. package/dist/eslint/index.js +1047 -0
  12. package/dist/guards/index.d.ts +78 -0
  13. package/dist/guards/index.js +118 -0
  14. package/dist/html/index.d.ts +151 -0
  15. package/dist/html/index.js +1244 -0
  16. package/dist/index-BIBd_iPD.d.ts +951 -0
  17. package/dist/lit/index.d.ts +862 -0
  18. package/dist/lit/index.js +4893 -0
  19. package/dist/preact/index.d.ts +796 -0
  20. package/dist/preact/index.js +5043 -0
  21. package/dist/react/index.d.ts +28 -0
  22. package/dist/react/index.js +205 -0
  23. package/dist/react/legacy.d.ts +29 -0
  24. package/dist/react/legacy.js +80 -0
  25. package/dist/solid/index.d.ts +728 -0
  26. package/dist/solid/index.js +4821 -0
  27. package/dist/svelte/Polymorphic.svelte +190 -0
  28. package/dist/svelte/_polymorphic-runtime.d.ts +102 -0
  29. package/dist/svelte/_polymorphic-runtime.js +371 -0
  30. package/dist/svelte/index.d.ts +994 -0
  31. package/dist/svelte/index.js +4482 -0
  32. package/dist/tailwind/index.d.ts +197 -0
  33. package/dist/tailwind/index.js +767 -0
  34. package/dist/tailwind/safelist.css +20 -0
  35. package/dist/ts-plugin/index.cjs +166 -0
  36. package/dist/ts-plugin/index.d.cts +9 -0
  37. package/dist/utils/index.d.ts +19 -0
  38. package/dist/utils/index.js +21 -0
  39. package/dist/vite-plugin/index.d.ts +200 -0
  40. package/dist/vite-plugin/index.js +2106 -0
  41. package/dist/vue/index.d.ts +729 -0
  42. package/dist/vue/index.js +4945 -0
  43. package/dist/web/index.d.ts +832 -0
  44. package/dist/web/index.js +4868 -0
  45. package/package.json +258 -0
@@ -0,0 +1,1047 @@
1
+ import { RuleCreator } from "@typescript-eslint/utils/eslint-utils";
2
+ import "../_shared/diagnostics.js";
3
+ //#region ../../lib/foundation/src/iterate.ts
4
+ function find(iterable, callback) {
5
+ for (const value of iterable) {
6
+ const result = callback(value);
7
+ if (result != null) return result;
8
+ }
9
+ return null;
10
+ }
11
+ function some(iterable, predicate) {
12
+ for (const value of iterable) if (predicate(value)) return true;
13
+ return false;
14
+ }
15
+ function every(iterable, predicate) {
16
+ let index = 0;
17
+ for (const value of iterable) if (!predicate(value, index++)) return false;
18
+ return true;
19
+ }
20
+ function* filter(iterable, predicate) {
21
+ let index = 0;
22
+ for (const value of iterable) if (predicate(value, index++)) yield value;
23
+ }
24
+ function* map(iterable, callback) {
25
+ let index = 0;
26
+ for (const value of iterable) yield callback(value, index++);
27
+ }
28
+ function forEach(iterable, callback) {
29
+ let index = 0;
30
+ for (const value of iterable) callback(value, index++);
31
+ }
32
+ function reduce(iterable, initial, callback) {
33
+ let accumulator = initial;
34
+ let index = 0;
35
+ for (const value of iterable) accumulator = callback(accumulator, value, index++);
36
+ return accumulator;
37
+ }
38
+ /**
39
+ * Transforms an iterable into a Record.
40
+ *
41
+ * The callback returns a `[key, value]` tuple for each element. Returning
42
+ * `null` aborts the collection and causes `collect()` to return `null`.
43
+ */
44
+ function collect(iterable, callback) {
45
+ const result = {};
46
+ let index = 0;
47
+ for (const value of iterable) {
48
+ const entry = callback(value, index++);
49
+ if (entry === null) return null;
50
+ result[entry[0]] = entry[1];
51
+ }
52
+ return result;
53
+ }
54
+ function findLast(value, callback) {
55
+ for (let index = value.length - 1; index >= 0; index--) {
56
+ const result = callback(value[index], index);
57
+ if (result != null) return result;
58
+ }
59
+ return null;
60
+ }
61
+ function* items(collection) {
62
+ for (let i = 0; i < collection.length; i++) {
63
+ const item = collection.item(i);
64
+ if (item !== null) yield item;
65
+ }
66
+ }
67
+ function nodeList(list) {
68
+ return { *[Symbol.iterator]() {
69
+ for (let i = 0; i < list.length; i++) {
70
+ const node = list.item(i);
71
+ if (node !== null) yield node;
72
+ }
73
+ } };
74
+ }
75
+ function mapEntries(m) {
76
+ return m.entries();
77
+ }
78
+ function set(s) {
79
+ return s.values();
80
+ }
81
+ function hasOwn(object, key) {
82
+ return Object.hasOwn(object, key);
83
+ }
84
+ function* entries(object) {
85
+ for (const key in object) {
86
+ if (!hasOwn(object, key)) continue;
87
+ yield [key, object[key]];
88
+ }
89
+ }
90
+ function* keys(object) {
91
+ for (const [key] of entries(object)) yield key;
92
+ }
93
+ function* values(object) {
94
+ for (const [, value] of entries(object)) yield value;
95
+ }
96
+ function mapValues(object, callback) {
97
+ const result = {};
98
+ for (const [key, value] of entries(object)) result[key] = callback(value, key);
99
+ return result;
100
+ }
101
+ function forEachEntry(object, callback) {
102
+ for (const [key, value] of entries(object)) callback(key, value);
103
+ }
104
+ function forEachKey(object, callback) {
105
+ for (const key of keys(object)) callback(key);
106
+ }
107
+ function forEachValue(object, callback) {
108
+ for (const value of values(object)) callback(value);
109
+ }
110
+ function forEachSet(s, callback) {
111
+ for (const value of s) callback(value);
112
+ }
113
+ const iterate = Object.freeze({
114
+ entries,
115
+ filter,
116
+ find,
117
+ findLast,
118
+ forEach,
119
+ forEachEntry,
120
+ forEachKey,
121
+ forEachSet,
122
+ forEachValue,
123
+ items,
124
+ keys,
125
+ map,
126
+ mapEntries,
127
+ mapValues,
128
+ nodeList,
129
+ reduce,
130
+ collect,
131
+ set,
132
+ some,
133
+ every,
134
+ values
135
+ });
136
+ //#endregion
137
+ //#region ../../lib/foundation/src/type-guards.ts
138
+ function isString(value) {
139
+ return typeof value === "string";
140
+ }
141
+ function isObject(value, excludeArrays = false) {
142
+ if (value === null || typeof value !== "object") return false;
143
+ return excludeArrays ? !Array.isArray(value) : true;
144
+ }
145
+ //#endregion
146
+ //#region ../../plugins/eslint/src/utils/ast.ts
147
+ /** Narrows any node-shaped value to one whose `value` property is a string — the common check
148
+ * behind both `asStringLiteral` and `getPropertyKey`'s `Literal` branch. */
149
+ function hasStringValue(node) {
150
+ return isObject(node, true) && isString(node.value);
151
+ }
152
+ function asObjectExpression(node) {
153
+ return node?.type === "ObjectExpression" ? node : void 0;
154
+ }
155
+ function asArrayExpression(node) {
156
+ return node?.type === "ArrayExpression" ? node : void 0;
157
+ }
158
+ function asNumericLiteral(node) {
159
+ if (node?.type === "Literal") {
160
+ const { value } = node;
161
+ if (typeof value === "number") return value;
162
+ }
163
+ if (node?.type === "UnaryExpression") {
164
+ const { operator, argument } = node;
165
+ if ((operator === "-" || operator === "+") && argument.type === "Literal" && typeof argument.value === "number") return operator === "-" ? -argument.value : argument.value;
166
+ }
167
+ }
168
+ function asStringLiteral(node) {
169
+ if (node?.type === "Literal" && hasStringValue(node)) return node.value;
170
+ }
171
+ function getPropertyKey(prop) {
172
+ const { key } = prop;
173
+ if (prop.computed) return void 0;
174
+ if (key.type === "Identifier") return key.name;
175
+ if (key.type === "Literal" && hasStringValue(key)) return key.value;
176
+ }
177
+ function getObjectProperty(obj, key) {
178
+ return obj.properties.find((prop) => {
179
+ if (prop.type !== "Property" || prop.kind !== "init") return false;
180
+ return getPropertyKey(prop) === key;
181
+ });
182
+ }
183
+ function getFirstObjectArg(node) {
184
+ const [first] = node.arguments;
185
+ return first?.type === "ObjectExpression" ? first : void 0;
186
+ }
187
+ function isFactoryCall(node, calleeNames) {
188
+ const { callee } = node;
189
+ if (callee.type === "Identifier") return calleeNames.has(callee.name);
190
+ if (callee.type === "MemberExpression" && !callee.computed) {
191
+ const { property } = callee;
192
+ return property.type === "Identifier" && calleeNames.has(property.name);
193
+ }
194
+ return false;
195
+ }
196
+ function extractVariantValues(node) {
197
+ const valuesObj = asObjectExpression(node);
198
+ if (!valuesObj) return void 0;
199
+ const values = /* @__PURE__ */ new Set();
200
+ iterate.forEach(valuesObj.properties, (prop) => {
201
+ if (prop.type !== "Property") return;
202
+ const key = getPropertyKey(prop);
203
+ if (key) values.add(key);
204
+ });
205
+ return values;
206
+ }
207
+ function extractVariantMap(variantsNode) {
208
+ const variantsObj = asObjectExpression(variantsNode);
209
+ if (!variantsObj) return void 0;
210
+ const map = /* @__PURE__ */ new Map();
211
+ iterate.forEach(variantsObj.properties, (prop) => {
212
+ if (prop.type !== "Property") return;
213
+ const key = getPropertyKey(prop);
214
+ if (!key) return;
215
+ const values = extractVariantValues(prop.value);
216
+ if (!values) return;
217
+ map.set(key, values);
218
+ });
219
+ return map;
220
+ }
221
+ //#endregion
222
+ //#region ../../plugins/eslint/src/diagnostics.ts
223
+ /**
224
+ * ESLint-compatible `{{ }}` template strings for meta.messages.
225
+ * Rules source their message text from here rather than constructing it inline.
226
+ */
227
+ const EslintDiagnosticTemplates = {
228
+ redundantRole: "role=\"{{ role }}\" is redundant on <{{ tag }}>: the element already carries this implicit ARIA role. Remove the attribute.",
229
+ invalidChild: "<{{ child }}> is not a valid direct child of <{{ parent }}>. Allowed: {{ allowed }}.",
230
+ deadCompoundKey: "\"{{ key }}\" is not a variant defined in styling.variants. This compound condition can never match.",
231
+ deadCompoundValue: "\"{{ value }}\" is not a valid value for variant \"{{ key }}\". Expected one of: {{ allowed }}. This compound condition can never match.",
232
+ deadCompoundNonLiteral: "Compound value for \"{{ key }}\" is not a string literal and cannot be statically validated.",
233
+ missingStrict: "enforcement.{{ field }} is defined but enforcement.diagnostics is not explicitly set. Pass a Diagnostics instance (e.g. warnDiagnostics, throwDiagnostics) so the enforcement behavior is clear at the call site.",
234
+ invalidDefaultKey: "\"{{ key }}\" is not a variant defined in styling.variants. This default will have no effect.",
235
+ invalidDefaultValue: "\"{{ value }}\" is not a valid value for variant \"{{ key }}\". Expected one of: {{ allowed }}. This default will have no effect.",
236
+ invalidDefaultNonLiteral: "Default value for \"{{ key }}\" is not a string literal and cannot be statically validated.",
237
+ negativeMin: "cardinality.min must be >= 0 (got {{ value }}).",
238
+ negativeMax: "cardinality.max must be >= 0 (got {{ value }}).",
239
+ maxLessThanMin: "cardinality.max ({{ max }}) must be >= cardinality.min ({{ min }}). This rule can never be satisfied.",
240
+ multipleFirst: "Multiple enforcement.children rules require position: \"first\". Only one child can occupy the first position.",
241
+ multipleLast: "Multiple enforcement.children rules require position: \"last\". Only one child can occupy the last position.",
242
+ minSumExceedsCapacity: "A rule with position: \"only\" requires min >= 1, but {{ count }} other rule(s) also require min >= 1. These constraints cannot be satisfied simultaneously."
243
+ };
244
+ const noDeadCompound = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`)({
245
+ name: "no-dead-compound",
246
+ meta: {
247
+ type: "problem",
248
+ docs: { description: "Disallow compound variant conditions that reference unknown variant keys or values — compounds that can never fire." },
249
+ messages: {
250
+ unknownVariantKey: EslintDiagnosticTemplates.deadCompoundKey,
251
+ unknownVariantValue: EslintDiagnosticTemplates.deadCompoundValue,
252
+ nonLiteralValue: EslintDiagnosticTemplates.deadCompoundNonLiteral
253
+ },
254
+ schema: [{
255
+ type: "object",
256
+ properties: {
257
+ calleeNames: {
258
+ type: "array",
259
+ items: { type: "string" }
260
+ },
261
+ reportNonLiteral: { type: "boolean" }
262
+ },
263
+ additionalProperties: false
264
+ }]
265
+ },
266
+ defaultOptions: [{}],
267
+ create(context) {
268
+ const calleeNames = new Set(context.options[0]?.calleeNames ?? ["createContractComponent"]);
269
+ const reportNonLiteral = context.options[0]?.reportNonLiteral ?? false;
270
+ return { CallExpression(node) {
271
+ if (!isFactoryCall(node, calleeNames)) return;
272
+ const arg = getFirstObjectArg(node);
273
+ if (!arg) return;
274
+ const stylingProp = getObjectProperty(arg, "styling");
275
+ if (!stylingProp) return;
276
+ const styling = asObjectExpression(stylingProp.value);
277
+ if (!styling) return;
278
+ const variantsProp = getObjectProperty(styling, "variants");
279
+ if (!variantsProp) return;
280
+ const variantMap = extractVariantMap(variantsProp.value);
281
+ if (!variantMap || variantMap.size === 0) return;
282
+ const compoundsProp = getObjectProperty(styling, "compounds");
283
+ if (!compoundsProp) return;
284
+ const compounds = asArrayExpression(compoundsProp.value);
285
+ if (!compounds) return;
286
+ iterate.forEach(compounds.elements, (element) => {
287
+ if (!element || element.type !== "ObjectExpression") return;
288
+ iterate.forEach(element.properties, (prop) => {
289
+ if (prop.type !== "Property") return;
290
+ const literalProp = prop;
291
+ const key = getPropertyKey(literalProp);
292
+ if (!key || key === "class" || key === "className") return;
293
+ if (!variantMap.has(key)) {
294
+ context.report({
295
+ node: prop,
296
+ messageId: "unknownVariantKey",
297
+ data: { key }
298
+ });
299
+ return;
300
+ }
301
+ const value = asStringLiteral(literalProp.value);
302
+ if (value === void 0) {
303
+ if (reportNonLiteral) context.report({
304
+ node: prop,
305
+ messageId: "nonLiteralValue",
306
+ data: { key }
307
+ });
308
+ return;
309
+ }
310
+ const allowed = variantMap.get(key);
311
+ if (!allowed.has(value)) context.report({
312
+ node: prop,
313
+ messageId: "unknownVariantValue",
314
+ data: {
315
+ key,
316
+ value,
317
+ allowed: iterate.reduce([...allowed], "", (acc, value, index) => index === 0 ? `"${value}"` : `${acc}, "${value}"`)
318
+ }
319
+ });
320
+ });
321
+ });
322
+ } };
323
+ }
324
+ });
325
+ const noEnforcementWithoutStrict = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`)({
326
+ name: "no-enforcement-without-strict",
327
+ meta: {
328
+ type: "problem",
329
+ docs: { description: "Require enforcement.strict when enforcement.children or enforcement.aria is defined." },
330
+ messages: { missingStrict: EslintDiagnosticTemplates.missingStrict },
331
+ schema: [{
332
+ type: "object",
333
+ properties: { calleeNames: {
334
+ type: "array",
335
+ items: { type: "string" }
336
+ } },
337
+ additionalProperties: false
338
+ }]
339
+ },
340
+ defaultOptions: [{}],
341
+ create(context) {
342
+ const calleeNames = new Set(context.options[0]?.calleeNames ?? ["createContractComponent"]);
343
+ return { CallExpression(node) {
344
+ if (!isFactoryCall(node, calleeNames)) return;
345
+ const arg = getFirstObjectArg(node);
346
+ if (!arg) return;
347
+ const enfProp = getObjectProperty(arg, "enforcement");
348
+ if (!enfProp) return;
349
+ const enf = asObjectExpression(enfProp.value);
350
+ if (!enf) return;
351
+ if (getObjectProperty(enf, "diagnostics") !== void 0) return;
352
+ const field = iterate.find(["children", "aria"], (field) => {
353
+ const fieldProp = getObjectProperty(enf, field);
354
+ if (!fieldProp) return null;
355
+ if (field === "children") {
356
+ const arr = asArrayExpression(fieldProp.value);
357
+ if (!arr || arr.elements.length === 0) return null;
358
+ }
359
+ return field;
360
+ });
361
+ if (field) context.report({
362
+ node,
363
+ messageId: "missingStrict",
364
+ data: { field }
365
+ });
366
+ } };
367
+ }
368
+ });
369
+ //#endregion
370
+ //#region ../../plugins/eslint/src/rules/no-invalid-default.ts
371
+ function formatAllowedValues(values) {
372
+ return Array.from(values, (value) => `"${value}"`).join(", ");
373
+ }
374
+ const noInvalidDefault = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`)({
375
+ name: "no-invalid-default",
376
+ meta: {
377
+ type: "problem",
378
+ docs: { description: "Disallow styling.defaults entries whose keys or values do not exist in styling.variants." },
379
+ messages: {
380
+ unknownDefaultKey: EslintDiagnosticTemplates.invalidDefaultKey,
381
+ unknownDefaultValue: EslintDiagnosticTemplates.invalidDefaultValue,
382
+ nonLiteralValue: EslintDiagnosticTemplates.invalidDefaultNonLiteral
383
+ },
384
+ schema: [{
385
+ type: "object",
386
+ properties: {
387
+ calleeNames: {
388
+ type: "array",
389
+ items: { type: "string" }
390
+ },
391
+ reportNonLiteral: { type: "boolean" }
392
+ },
393
+ additionalProperties: false
394
+ }]
395
+ },
396
+ defaultOptions: [{}],
397
+ create(context) {
398
+ const calleeNames = new Set(context.options[0]?.calleeNames ?? ["createContractComponent"]);
399
+ const reportNonLiteral = context.options[0]?.reportNonLiteral ?? false;
400
+ return { CallExpression(node) {
401
+ if (!isFactoryCall(node, calleeNames)) return;
402
+ const arg = getFirstObjectArg(node);
403
+ if (!arg) return;
404
+ const stylingProp = getObjectProperty(arg, "styling");
405
+ if (!stylingProp) return;
406
+ const styling = asObjectExpression(stylingProp.value);
407
+ if (!styling) return;
408
+ const variantsProp = getObjectProperty(styling, "variants");
409
+ if (!variantsProp) return;
410
+ const variantMap = extractVariantMap(variantsProp.value);
411
+ if (!variantMap || variantMap.size === 0) return;
412
+ const defaultsProp = getObjectProperty(styling, "defaults");
413
+ if (!defaultsProp) return;
414
+ const defaults = asObjectExpression(defaultsProp.value);
415
+ if (!defaults) return;
416
+ iterate.forEach(defaults.properties, (prop) => {
417
+ if (prop.type !== "Property") return;
418
+ const key = getPropertyKey(prop);
419
+ if (!key) return;
420
+ if (!variantMap.has(key)) {
421
+ context.report({
422
+ node: prop,
423
+ messageId: "unknownDefaultKey",
424
+ data: { key }
425
+ });
426
+ return;
427
+ }
428
+ const value = asStringLiteral(prop.value);
429
+ if (value === void 0) {
430
+ if (reportNonLiteral) context.report({
431
+ node: prop,
432
+ messageId: "nonLiteralValue",
433
+ data: { key }
434
+ });
435
+ return;
436
+ }
437
+ const allowed = variantMap.get(key);
438
+ if (!allowed.has(value)) context.report({
439
+ node: prop,
440
+ messageId: "unknownDefaultValue",
441
+ data: {
442
+ key,
443
+ value,
444
+ allowed: formatAllowedValues(allowed)
445
+ }
446
+ });
447
+ });
448
+ } };
449
+ }
450
+ });
451
+ //#endregion
452
+ //#region ../../plugins/eslint/src/utils/content-model-builders.ts
453
+ function categories(...values) {
454
+ return new Set(values);
455
+ }
456
+ function tags(...values) {
457
+ return new Set(values);
458
+ }
459
+ function category(...allowed) {
460
+ return { model: {
461
+ kind: "category",
462
+ allowed: categories(...allowed)
463
+ } };
464
+ }
465
+ function specific(...allowed) {
466
+ return { model: {
467
+ kind: "specific",
468
+ allowed: tags(...allowed)
469
+ } };
470
+ }
471
+ function phrasing() {
472
+ return category("phrasing");
473
+ }
474
+ function phrasingOrHeading() {
475
+ return category("phrasing", "heading");
476
+ }
477
+ /** Builds a `{ tag: categorySet }` slice for `TAG_CATEGORIES`, one category set shared across
478
+ * every tag in `tagNames` — the common case where a whole group of tags shares identical
479
+ * category membership (e.g. every plain flow+phrasing inline element). Spread the result into
480
+ * `TAG_CATEGORIES`'s object literal rather than mutating an already-built map — each tag's
481
+ * entry is then created exactly once, with no runtime mutation after construction. */
482
+ function categoriesFor(categoryList, tagNames) {
483
+ return Object.fromEntries(tagNames.map((tagName) => [tagName, categories(...categoryList)]));
484
+ }
485
+ /**
486
+ * Identity function over a content-model table literal — exists purely so a `HTML_CONTENT_MODELS`
487
+ * table can be authored as a plain object literal (readable, one entry per line) while still
488
+ * getting `const`-context literal inference for each entry's `kind` discriminant, the same
489
+ * benefit an explicit `ContentModelMap` annotation would otherwise cost by widening every
490
+ * property to the annotation's type up front. TypeScript's `const` type parameter modifier
491
+ * (5.0+) does the actual work; this function just gives it a name at the call site.
492
+ */
493
+ function defineContentModels(definitions) {
494
+ return definitions;
495
+ }
496
+ //#endregion
497
+ //#region ../../plugins/eslint/src/utils/html-nesting.ts
498
+ const FLOW_PHRASING_TAGS = [
499
+ "abbr",
500
+ "b",
501
+ "bdi",
502
+ "bdo",
503
+ "br",
504
+ "cite",
505
+ "code",
506
+ "data",
507
+ "dfn",
508
+ "em",
509
+ "i",
510
+ "kbd",
511
+ "mark",
512
+ "output",
513
+ "q",
514
+ "ruby",
515
+ "s",
516
+ "samp",
517
+ "small",
518
+ "span",
519
+ "strong",
520
+ "sub",
521
+ "sup",
522
+ "time",
523
+ "u",
524
+ "var",
525
+ "wbr"
526
+ ];
527
+ const FLOW_PHRASING_EMBEDDED_TAGS = [
528
+ "audio",
529
+ "canvas",
530
+ "img",
531
+ "math",
532
+ "object",
533
+ "picture",
534
+ "svg",
535
+ "video"
536
+ ];
537
+ const FLOW_PHRASING_INTERACTIVE_TAGS = [
538
+ "button",
539
+ "input",
540
+ "label",
541
+ "select",
542
+ "textarea"
543
+ ];
544
+ const FLOW_PHRASING_EMBEDDED_INTERACTIVE_TAGS = ["embed", "iframe"];
545
+ const FLOW_PHRASING_METADATA_TAGS = [
546
+ "meta",
547
+ "noscript",
548
+ "script",
549
+ "template",
550
+ "link"
551
+ ];
552
+ const FLOW_ONLY_TAGS = [
553
+ "address",
554
+ "blockquote",
555
+ "dialog",
556
+ "div",
557
+ "dl",
558
+ "fieldset",
559
+ "figure",
560
+ "footer",
561
+ "form",
562
+ "header",
563
+ "hr",
564
+ "li",
565
+ "main",
566
+ "menu",
567
+ "ol",
568
+ "p",
569
+ "pre",
570
+ "summary",
571
+ "table",
572
+ "ul"
573
+ ];
574
+ const FLOW_SECTIONING_TAGS = [
575
+ "article",
576
+ "aside",
577
+ "nav",
578
+ "section"
579
+ ];
580
+ const FLOW_INTERACTIVE_TAGS = ["details"];
581
+ const FLOW_METADATA_TAGS = ["style"];
582
+ const FLOW_HEADING_TAGS = [
583
+ "h1",
584
+ "h2",
585
+ "h3",
586
+ "h4",
587
+ "h5",
588
+ "h6",
589
+ "hgroup"
590
+ ];
591
+ const METADATA_ONLY_TAGS = ["base", "title"];
592
+ const TAG_CATEGORIES = {
593
+ ...categoriesFor(["flow", "phrasing"], FLOW_PHRASING_TAGS),
594
+ ...categoriesFor([
595
+ "flow",
596
+ "phrasing",
597
+ "embedded"
598
+ ], FLOW_PHRASING_EMBEDDED_TAGS),
599
+ ...categoriesFor([
600
+ "flow",
601
+ "phrasing",
602
+ "interactive"
603
+ ], FLOW_PHRASING_INTERACTIVE_TAGS),
604
+ ...categoriesFor([
605
+ "flow",
606
+ "phrasing",
607
+ "embedded",
608
+ "interactive"
609
+ ], FLOW_PHRASING_EMBEDDED_INTERACTIVE_TAGS),
610
+ ...categoriesFor([
611
+ "metadata",
612
+ "flow",
613
+ "phrasing"
614
+ ], FLOW_PHRASING_METADATA_TAGS),
615
+ ...categoriesFor(["flow"], FLOW_ONLY_TAGS),
616
+ ...categoriesFor(["flow", "sectioning"], FLOW_SECTIONING_TAGS),
617
+ ...categoriesFor(["flow", "interactive"], FLOW_INTERACTIVE_TAGS),
618
+ ...categoriesFor(["flow", "metadata"], FLOW_METADATA_TAGS),
619
+ ...categoriesFor(["flow", "heading"], FLOW_HEADING_TAGS),
620
+ ...categoriesFor(["metadata"], METADATA_ONLY_TAGS)
621
+ };
622
+ const HTML_CONTENT_MODELS = defineContentModels({
623
+ colgroup: specific("col", "template"),
624
+ dl: specific("dt", "dd", "div", "script", "template"),
625
+ menu: specific("li", "script", "template"),
626
+ ol: specific("li", "script", "template"),
627
+ optgroup: specific("option", "script", "template"),
628
+ picture: specific("source", "img", "script", "template"),
629
+ select: specific("option", "optgroup", "hr", "script", "template"),
630
+ table: specific("caption", "colgroup", "thead", "tbody", "tfoot", "tr", "script", "template"),
631
+ tbody: specific("tr", "script", "template"),
632
+ tfoot: specific("tr", "script", "template"),
633
+ thead: specific("tr", "script", "template"),
634
+ tr: specific("td", "th", "script", "template"),
635
+ ul: specific("li", "script", "template"),
636
+ abbr: phrasing(),
637
+ b: phrasing(),
638
+ bdi: phrasing(),
639
+ bdo: phrasing(),
640
+ cite: phrasing(),
641
+ code: phrasing(),
642
+ data: phrasing(),
643
+ dfn: phrasing(),
644
+ dt: phrasing(),
645
+ em: phrasing(),
646
+ h1: phrasing(),
647
+ h2: phrasing(),
648
+ h3: phrasing(),
649
+ h4: phrasing(),
650
+ h5: phrasing(),
651
+ h6: phrasing(),
652
+ i: phrasing(),
653
+ kbd: phrasing(),
654
+ label: phrasing(),
655
+ mark: phrasing(),
656
+ output: phrasing(),
657
+ p: phrasing(),
658
+ q: phrasing(),
659
+ ruby: phrasing(),
660
+ s: phrasing(),
661
+ samp: phrasing(),
662
+ small: phrasing(),
663
+ span: phrasing(),
664
+ strong: phrasing(),
665
+ sub: phrasing(),
666
+ sup: phrasing(),
667
+ time: phrasing(),
668
+ u: phrasing(),
669
+ var: phrasing(),
670
+ legend: phrasingOrHeading(),
671
+ summary: phrasingOrHeading()
672
+ });
673
+ function getTagCategorySet(tagCategories, tagName) {
674
+ return tagCategories[tagName];
675
+ }
676
+ function getContentModelDefinition(contentModels, tagName) {
677
+ return contentModels[tagName];
678
+ }
679
+ //#endregion
680
+ //#region ../../plugins/eslint/src/rules/no-invalid-html-nesting.ts
681
+ const createRule$3 = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`);
682
+ function describeAllowed(definition) {
683
+ const { model } = definition;
684
+ switch (model.kind) {
685
+ case "specific": return [...model.allowed].join(", ");
686
+ case "category": return [...model.allowed].map((c) => `${c} content`).join(", ");
687
+ case "transparent":
688
+ case "nothing":
689
+ case "structured": return "";
690
+ }
691
+ }
692
+ const ALLOWED_TEXT = Object.fromEntries(Object.entries(HTML_CONTENT_MODELS).filter((entry) => entry[1] !== void 0).map(([tag, definition]) => [tag, describeAllowed(definition)]));
693
+ function getIntrinsicTag(name) {
694
+ if (name.type !== "JSXIdentifier") return void 0;
695
+ return /^[a-z]/.test(name.name) ? name.name : void 0;
696
+ }
697
+ /**
698
+ * Evaluates a `ContentModelDefinition` against a candidate child tag.
699
+ *
700
+ * Only `specific` (explicit tag allowlist) and `category` (content-category membership) are
701
+ * evaluated today — the same two kinds every entry in `HTML_CONTENT_MODELS` currently uses.
702
+ * `transparent`/`nothing`/`structured` are real HTML content-model concepts (see their own doc
703
+ * comments in `types/content-model.ts`) that this validator doesn't check yet: `transparent`
704
+ * needs the parent's own rendering context, `nothing` needs a void-element check this rule
705
+ * doesn't perform, and `structured` needs an order-aware grammar evaluator, not a flat
706
+ * membership test. Each passes through (no violation reported) rather than being treated as
707
+ * disallowing everything, so introducing one of these kinds to a future table entry doesn't
708
+ * silently start flagging valid markup the validator can't actually reason about yet.
709
+ */
710
+ function isAllowed(childTag, definition) {
711
+ const { model } = definition;
712
+ switch (model.kind) {
713
+ case "specific": return model.allowed.has(childTag);
714
+ case "category": {
715
+ const cats = getTagCategorySet(TAG_CATEGORIES, childTag);
716
+ if (!cats) return true;
717
+ return [...model.allowed].some((c) => cats.has(c));
718
+ }
719
+ case "transparent":
720
+ case "nothing":
721
+ case "structured": return true;
722
+ }
723
+ }
724
+ const noInvalidHtmlNesting = createRule$3({
725
+ name: "no-invalid-html-nesting",
726
+ meta: {
727
+ type: "problem",
728
+ docs: { description: "Disallow HTML children that violate the HTML5 content model for their parent element." },
729
+ messages: { invalidChild: EslintDiagnosticTemplates.invalidChild },
730
+ schema: []
731
+ },
732
+ defaultOptions: [],
733
+ create(context) {
734
+ return { JSXElement(node) {
735
+ const parentTag = getIntrinsicTag(node.openingElement.name);
736
+ if (!parentTag) return;
737
+ const definition = getContentModelDefinition(HTML_CONTENT_MODELS, parentTag);
738
+ if (!definition) return;
739
+ iterate.forEach(node.children, (child) => {
740
+ if (child.type !== "JSXElement") return;
741
+ const childTag = getIntrinsicTag(child.openingElement.name);
742
+ if (childTag === void 0) return;
743
+ if (isAllowed(childTag, definition)) return;
744
+ context.report({
745
+ node: child,
746
+ messageId: "invalidChild",
747
+ data: {
748
+ child: childTag,
749
+ parent: parentTag,
750
+ allowed: ALLOWED_TEXT[parentTag] ?? ""
751
+ }
752
+ });
753
+ });
754
+ } };
755
+ }
756
+ });
757
+ //#endregion
758
+ //#region ../../plugins/eslint/src/utils/implicit-roles.ts
759
+ const IMPLICIT_ROLES = {
760
+ article: "article",
761
+ aside: "complementary",
762
+ button: "button",
763
+ datalist: "listbox",
764
+ details: "group",
765
+ dialog: "dialog",
766
+ figure: "figure",
767
+ form: "form",
768
+ h1: "heading",
769
+ h2: "heading",
770
+ h3: "heading",
771
+ h4: "heading",
772
+ h5: "heading",
773
+ h6: "heading",
774
+ hr: "separator",
775
+ img: "img",
776
+ li: "listitem",
777
+ main: "main",
778
+ math: "math",
779
+ menu: "list",
780
+ meter: "meter",
781
+ nav: "navigation",
782
+ ol: "list",
783
+ option: "option",
784
+ output: "status",
785
+ progress: "progressbar",
786
+ search: "search",
787
+ section: "region",
788
+ select: "listbox",
789
+ summary: "button",
790
+ table: "table",
791
+ tbody: "rowgroup",
792
+ td: "cell",
793
+ textarea: "textbox",
794
+ tfoot: "rowgroup",
795
+ th: "columnheader",
796
+ thead: "rowgroup",
797
+ tr: "row",
798
+ ul: "list"
799
+ };
800
+ //#endregion
801
+ //#region ../../plugins/eslint/src/rules/no-redundant-role.ts
802
+ const createRule$2 = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`);
803
+ function isRedundantRole(explicit, implicit) {
804
+ return explicit === implicit;
805
+ }
806
+ function getJsxTagName(node) {
807
+ const name = node.name;
808
+ if (name.type === "JSXIdentifier") return name.name;
809
+ }
810
+ function getJsxStringAttribute(node, attrName) {
811
+ return iterate.find(node.attributes, (attr) => {
812
+ if (attr.type !== "JSXAttribute") return null;
813
+ const { name: key } = attr;
814
+ if (key.type !== "JSXIdentifier" || key.name !== attrName) return null;
815
+ const { value: val } = attr;
816
+ if (val === null) return null;
817
+ if (val.type === "Literal" && isString(val.value)) return {
818
+ node: attr,
819
+ value: val.value
820
+ };
821
+ if (val.type === "JSXExpressionContainer" && val.expression.type === "Literal" && isString(val.expression.value)) return {
822
+ node: attr,
823
+ value: val.expression.value
824
+ };
825
+ return null;
826
+ }) ?? void 0;
827
+ }
828
+ const noRedundantRole = createRule$2({
829
+ name: "no-redundant-role",
830
+ meta: {
831
+ type: "suggestion",
832
+ docs: { description: "Disallow role attributes that duplicate the implicit ARIA role of the HTML element." },
833
+ fixable: "code",
834
+ messages: { redundantRole: EslintDiagnosticTemplates.redundantRole },
835
+ schema: []
836
+ },
837
+ defaultOptions: [],
838
+ create(context) {
839
+ return { JSXOpeningElement(node) {
840
+ const tag = getJsxTagName(node);
841
+ if (!tag) return;
842
+ const implicitRole = IMPLICIT_ROLES[tag];
843
+ if (!implicitRole) return;
844
+ const roleAttr = getJsxStringAttribute(node, "role");
845
+ if (!roleAttr) return;
846
+ if (isRedundantRole(roleAttr.value, implicitRole)) context.report({
847
+ node: roleAttr.node,
848
+ messageId: "redundantRole",
849
+ data: {
850
+ tag,
851
+ role: roleAttr.value
852
+ },
853
+ fix(fixer) {
854
+ return fixer.remove(roleAttr.node);
855
+ }
856
+ });
857
+ } };
858
+ }
859
+ });
860
+ const validCardinality = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`)({
861
+ name: "valid-cardinality",
862
+ meta: {
863
+ type: "problem",
864
+ docs: { description: "Enforce valid min/max values in enforcement.children cardinality rules." },
865
+ messages: {
866
+ negativeMin: EslintDiagnosticTemplates.negativeMin,
867
+ negativeMax: EslintDiagnosticTemplates.negativeMax,
868
+ maxLessThanMin: EslintDiagnosticTemplates.maxLessThanMin
869
+ },
870
+ schema: [{
871
+ type: "object",
872
+ properties: { calleeNames: {
873
+ type: "array",
874
+ items: { type: "string" }
875
+ } },
876
+ additionalProperties: false
877
+ }]
878
+ },
879
+ defaultOptions: [{}],
880
+ create(context) {
881
+ const calleeNames = new Set(context.options[0]?.calleeNames ?? ["createContractComponent"]);
882
+ function validateCardinality(cardProp) {
883
+ const card = asObjectExpression(cardProp.value);
884
+ if (!card) return;
885
+ const minProp = getObjectProperty(card, "min");
886
+ const maxProp = getObjectProperty(card, "max");
887
+ const min = minProp ? asNumericLiteral(minProp.value) : void 0;
888
+ const max = maxProp ? asNumericLiteral(maxProp.value) : void 0;
889
+ if (minProp && min !== void 0 && min < 0) context.report({
890
+ node: minProp,
891
+ messageId: "negativeMin",
892
+ data: { value: String(min) }
893
+ });
894
+ if (maxProp && max !== void 0 && max < 0) context.report({
895
+ node: maxProp,
896
+ messageId: "negativeMax",
897
+ data: { value: String(max) }
898
+ });
899
+ if (min !== void 0 && max !== void 0 && min >= 0 && max > 0 && max < min) context.report({
900
+ node: cardProp,
901
+ messageId: "maxLessThanMin",
902
+ data: {
903
+ min: String(min),
904
+ max: String(max)
905
+ }
906
+ });
907
+ }
908
+ return { CallExpression(node) {
909
+ if (!isFactoryCall(node, calleeNames)) return;
910
+ const arg = getFirstObjectArg(node);
911
+ if (!arg) return;
912
+ const enfProp = getObjectProperty(arg, "enforcement");
913
+ if (!enfProp) return;
914
+ const enf = asObjectExpression(enfProp.value);
915
+ if (!enf) return;
916
+ const childrenProp = getObjectProperty(enf, "children");
917
+ if (!childrenProp) return;
918
+ const arr = asArrayExpression(childrenProp.value);
919
+ if (!arr) return;
920
+ iterate.forEach(arr.elements, (element) => {
921
+ if (!element || element.type !== "ObjectExpression") return;
922
+ const cardProp = getObjectProperty(element, "cardinality");
923
+ if (!cardProp) return;
924
+ validateCardinality(cardProp);
925
+ });
926
+ } };
927
+ }
928
+ });
929
+ //#endregion
930
+ //#region ../../plugins/eslint/src/rules/valid-children-config.ts
931
+ const createRule = RuleCreator((name) => `https://praxis-kit.dev/eslint-rules/${name}`);
932
+ function analyzeChildrenRules(elements) {
933
+ const firstPositionProps = [];
934
+ const lastPositionProps = [];
935
+ let onlyWithMinProp = null;
936
+ let requiredRuleCount = 0;
937
+ iterate.forEach(elements, (element) => {
938
+ if (!element || element.type !== "ObjectExpression") return;
939
+ const positionProp = getObjectProperty(element, "position");
940
+ const position = positionProp ? asStringLiteral(positionProp.value) : void 0;
941
+ const cardProp = getObjectProperty(element, "cardinality");
942
+ const card = cardProp ? asObjectExpression(cardProp.value) : void 0;
943
+ const minProp = card ? getObjectProperty(card, "min") : void 0;
944
+ const min = minProp ? asNumericLiteral(minProp.value) ?? 0 : 0;
945
+ if (position === "first" && positionProp) firstPositionProps.push(positionProp);
946
+ if (position === "last" && positionProp) lastPositionProps.push(positionProp);
947
+ if (min >= 1) {
948
+ requiredRuleCount++;
949
+ if (position === "only" && positionProp && !onlyWithMinProp) onlyWithMinProp = positionProp;
950
+ }
951
+ });
952
+ return {
953
+ firstPositionProps,
954
+ lastPositionProps,
955
+ onlyWithMinProp,
956
+ requiredRuleCount
957
+ };
958
+ }
959
+ const validChildrenConfig = createRule({
960
+ name: "valid-children-config",
961
+ meta: {
962
+ type: "problem",
963
+ docs: { description: "Enforce cross-rule consistency of enforcement.children — detect positional conflicts and cardinality impossibilities." },
964
+ messages: {
965
+ multipleFirst: EslintDiagnosticTemplates.multipleFirst,
966
+ multipleLast: EslintDiagnosticTemplates.multipleLast,
967
+ minSumExceedsCapacity: EslintDiagnosticTemplates.minSumExceedsCapacity
968
+ },
969
+ schema: [{
970
+ type: "object",
971
+ properties: { calleeNames: {
972
+ type: "array",
973
+ items: { type: "string" }
974
+ } },
975
+ additionalProperties: false
976
+ }]
977
+ },
978
+ defaultOptions: [{}],
979
+ create(context) {
980
+ const calleeNames = new Set(context.options[0]?.calleeNames ?? ["createContractComponent"]);
981
+ return { CallExpression(node) {
982
+ if (!isFactoryCall(node, calleeNames)) return;
983
+ const arg = getFirstObjectArg(node);
984
+ if (!arg) return;
985
+ const enfProp = getObjectProperty(arg, "enforcement");
986
+ if (!enfProp) return;
987
+ const enf = asObjectExpression(enfProp.value);
988
+ if (!enf) return;
989
+ const childrenProp = getObjectProperty(enf, "children");
990
+ if (!childrenProp) return;
991
+ const arr = asArrayExpression(childrenProp.value);
992
+ if (!arr) return;
993
+ const { firstPositionProps, lastPositionProps, onlyWithMinProp, requiredRuleCount } = analyzeChildrenRules(arr.elements);
994
+ iterate.forEach(firstPositionProps.slice(1), (prop) => {
995
+ context.report({
996
+ node: prop,
997
+ messageId: "multipleFirst"
998
+ });
999
+ });
1000
+ iterate.forEach(lastPositionProps.slice(1), (prop) => {
1001
+ context.report({
1002
+ node: prop,
1003
+ messageId: "multipleLast"
1004
+ });
1005
+ });
1006
+ if (onlyWithMinProp && requiredRuleCount > 1) context.report({
1007
+ node: onlyWithMinProp,
1008
+ messageId: "minSumExceedsCapacity",
1009
+ data: { count: String(requiredRuleCount - 1) }
1010
+ });
1011
+ } };
1012
+ }
1013
+ });
1014
+ //#endregion
1015
+ //#region ../../plugins/eslint/src/index.ts
1016
+ const plugin = {
1017
+ meta: {
1018
+ name: "@praxis-kit/eslint-plugin",
1019
+ version: "1.0.0"
1020
+ },
1021
+ rules: {
1022
+ "no-dead-compound": noDeadCompound,
1023
+ "no-enforcement-without-strict": noEnforcementWithoutStrict,
1024
+ "no-invalid-default": noInvalidDefault,
1025
+ "no-invalid-html-nesting": noInvalidHtmlNesting,
1026
+ "no-redundant-role": noRedundantRole,
1027
+ "valid-cardinality": validCardinality,
1028
+ "valid-children-config": validChildrenConfig
1029
+ },
1030
+ configs: {}
1031
+ };
1032
+ const recommended = {
1033
+ name: "@praxis-kit/recommended",
1034
+ plugins: { "@praxis-kit": plugin },
1035
+ rules: {
1036
+ "@praxis-kit/no-dead-compound": "error",
1037
+ "@praxis-kit/no-enforcement-without-strict": "error",
1038
+ "@praxis-kit/no-invalid-default": "error",
1039
+ "@praxis-kit/no-invalid-html-nesting": "error",
1040
+ "@praxis-kit/no-redundant-role": "warn",
1041
+ "@praxis-kit/valid-cardinality": "error",
1042
+ "@praxis-kit/valid-children-config": "error"
1043
+ }
1044
+ };
1045
+ plugin.configs["recommended"] = recommended;
1046
+ //#endregion
1047
+ export { plugin as default, plugin, noDeadCompound, noEnforcementWithoutStrict, noInvalidDefault, noInvalidHtmlNesting, noRedundantRole, recommended, validCardinality, validChildrenConfig };