praxis-kit 4.1.3 → 6.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/dist/_shared/diagnostics.d.ts +10 -1
- package/dist/_shared/diagnostics.js +12 -0
- package/dist/{chunk-R2RKHZNX.js → chunk-P5ZJBPAR.js} +147 -52
- package/dist/codemod/index.js +39 -22
- package/dist/contract/index.d.ts +45 -3
- package/dist/eslint/index.js +16 -16
- package/dist/lit/index.d.ts +45 -3
- package/dist/lit/index.js +155 -58
- package/dist/preact/index.d.ts +45 -3
- package/dist/preact/index.js +146 -52
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/dist/react/legacy.d.ts +2 -2
- package/dist/react/legacy.js +1 -1
- package/dist/{react-options-XrRof248.d.ts → react-options-AitAOrje.d.ts} +45 -3
- package/dist/solid/index.d.ts +45 -3
- package/dist/solid/index.js +180 -80
- package/dist/svelte/Polymorphic.svelte +146 -0
- package/dist/svelte/index.d.ts +66 -5
- package/dist/svelte/index.js +134 -48
- package/dist/tailwind/index.d.ts +89 -54
- package/dist/tailwind/index.js +85 -46
- package/dist/vite-plugin/index.d.ts +31 -0
- package/dist/vite-plugin/index.js +32 -15
- package/dist/vue/index.d.ts +45 -3
- package/dist/vue/index.js +136 -49
- package/dist/web/index.d.ts +45 -3
- package/dist/web/index.js +155 -63
- package/package.json +61 -8
package/dist/tailwind/index.js
CHANGED
|
@@ -358,6 +358,14 @@ function createClassPipeline(resolved) {
|
|
|
358
358
|
};
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
// ../../lib/pipeline-kit/src/core/compose-pipelines.ts
|
|
362
|
+
function composePipelines(first, second) {
|
|
363
|
+
return (...args) => second(first(...args));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ../../lib/tailwind/src/create-tailwind-pipeline.ts
|
|
367
|
+
import { ConsoleReporter, DefaultPolicy, Diagnostics, Severity } from "../_shared/diagnostics.js";
|
|
368
|
+
|
|
361
369
|
// ../../lib/tailwind/src/class-builder.ts
|
|
362
370
|
var ClassBuilder = class {
|
|
363
371
|
build(tokens) {
|
|
@@ -371,6 +379,7 @@ var ClassBuilder = class {
|
|
|
371
379
|
}
|
|
372
380
|
case "utility":
|
|
373
381
|
case "gap":
|
|
382
|
+
case "shared":
|
|
374
383
|
case "conditional": {
|
|
375
384
|
normal.push(token.raw);
|
|
376
385
|
break;
|
|
@@ -444,6 +453,16 @@ var CONDITIONALS = {
|
|
|
444
453
|
"[&.flex": "flex",
|
|
445
454
|
"[&.grid": "grid"
|
|
446
455
|
};
|
|
456
|
+
var SHARED_PREFIXES = [
|
|
457
|
+
/^order/,
|
|
458
|
+
/^justify-(?!items-|self-)/,
|
|
459
|
+
/^content-/,
|
|
460
|
+
/^items-/,
|
|
461
|
+
/^self-/,
|
|
462
|
+
/^place-content-/,
|
|
463
|
+
/^place-items-/,
|
|
464
|
+
/^place-self-/
|
|
465
|
+
];
|
|
447
466
|
var ClassClassifier = class _ClassClassifier {
|
|
448
467
|
static #getBaseUtility(token) {
|
|
449
468
|
let depth = 0;
|
|
@@ -476,23 +495,16 @@ var ClassClassifier = class _ClassClassifier {
|
|
|
476
495
|
}
|
|
477
496
|
);
|
|
478
497
|
if (conditional !== null) return conditional;
|
|
479
|
-
|
|
480
|
-
kind: "gap",
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
kind: "
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
};
|
|
498
|
+
if (base === "gap" || base.startsWith("gap-")) {
|
|
499
|
+
return { kind: "gap", raw: token };
|
|
500
|
+
}
|
|
501
|
+
if (SHARED_PREFIXES.some((rule) => rule.test(base))) {
|
|
502
|
+
return { kind: "shared", raw: token };
|
|
503
|
+
}
|
|
504
|
+
return { kind: "utility", base, raw: token };
|
|
487
505
|
}
|
|
488
506
|
};
|
|
489
507
|
|
|
490
|
-
// ../../lib/tailwind/src/dependency-rules.ts
|
|
491
|
-
var defaultDependencyRules = {
|
|
492
|
-
flex: [/^flex-/, /^grow/, /^shrink/, /^basis-/],
|
|
493
|
-
grid: [/^grid-/, /^col-/, /^row-/, /^auto-cols-/, /^auto-rows-/]
|
|
494
|
-
};
|
|
495
|
-
|
|
496
508
|
// ../../lib/tailwind/src/dependency-evaluator.ts
|
|
497
509
|
var DependencyEvaluator = class {
|
|
498
510
|
constructor(rules) {
|
|
@@ -513,7 +525,8 @@ var DependencyEvaluator = class {
|
|
|
513
525
|
(layout) => this.rules[layout].some((rule) => rule.test(token.base)) ? state.family === layout : null
|
|
514
526
|
) ?? true;
|
|
515
527
|
}
|
|
516
|
-
case "gap":
|
|
528
|
+
case "gap":
|
|
529
|
+
case "shared": {
|
|
517
530
|
return state.family !== "none";
|
|
518
531
|
}
|
|
519
532
|
default:
|
|
@@ -522,26 +535,22 @@ var DependencyEvaluator = class {
|
|
|
522
535
|
}
|
|
523
536
|
};
|
|
524
537
|
|
|
525
|
-
// ../../lib/tailwind/src/
|
|
526
|
-
var
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
538
|
+
// ../../lib/tailwind/src/dependency-rules.ts
|
|
539
|
+
var defaultDependencyRules = {
|
|
540
|
+
flex: [/^flex-/, /^grow/, /^shrink/, /^basis-/],
|
|
541
|
+
grid: [
|
|
542
|
+
/^grid-/,
|
|
543
|
+
/^col-/,
|
|
544
|
+
/^row-/,
|
|
545
|
+
/^auto-cols-/,
|
|
546
|
+
/^auto-rows-/,
|
|
547
|
+
// justify-items/-self are no-ops on flex containers per the CSS box
|
|
548
|
+
// alignment spec (flex items ignore them), so treat as grid-only.
|
|
549
|
+
/^justify-items-/,
|
|
550
|
+
/^justify-self-/
|
|
551
|
+
]
|
|
540
552
|
};
|
|
541
553
|
|
|
542
|
-
// ../../lib/tailwind/src/create-tailwind-pipeline.ts
|
|
543
|
-
import { ConsoleReporter, Diagnostics, DefaultPolicy, Severity } from "../_shared/diagnostics.js";
|
|
544
|
-
|
|
545
554
|
// ../../lib/tailwind/src/diagnostics.ts
|
|
546
555
|
import { DiagnosticCategory, DiagnosticCode } from "../_shared/diagnostics.js";
|
|
547
556
|
var TailwindDiagnostics = {
|
|
@@ -568,6 +577,23 @@ var TailwindDiagnostics = {
|
|
|
568
577
|
}
|
|
569
578
|
};
|
|
570
579
|
|
|
580
|
+
// ../../lib/tailwind/src/layout-state.ts
|
|
581
|
+
var LayoutState = class {
|
|
582
|
+
#mode;
|
|
583
|
+
#family;
|
|
584
|
+
constructor(mode) {
|
|
585
|
+
this.#mode = mode;
|
|
586
|
+
this.#family = mode === "none" ? "none" : LAYOUT_FAMILY_MAP[mode];
|
|
587
|
+
Object.freeze(this);
|
|
588
|
+
}
|
|
589
|
+
get mode() {
|
|
590
|
+
return this.#mode;
|
|
591
|
+
}
|
|
592
|
+
get family() {
|
|
593
|
+
return this.#family;
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
|
|
571
597
|
// ../../lib/tailwind/src/create-tailwind-pipeline.ts
|
|
572
598
|
var DEV = process.env.NODE_ENV !== "production";
|
|
573
599
|
var devDiagnostics = new Diagnostics(
|
|
@@ -648,23 +674,36 @@ function warnDeadVariants(diagnostics, options, compoundDims, props, recipe, sta
|
|
|
648
674
|
});
|
|
649
675
|
}
|
|
650
676
|
function createTailwindPipeline(options, diagnostics) {
|
|
651
|
-
const
|
|
677
|
+
const innerPipeline = createClassPipeline(options);
|
|
652
678
|
const compoundDims = compoundDimensions(getCompoundVariants(options));
|
|
679
|
+
const resolveLayoutContext = (tag, props, className, recipe) => {
|
|
680
|
+
const mode = resolveLayout(devDiagnostics, props);
|
|
681
|
+
const resolvedClasses = innerPipeline(tag, props, className, recipe);
|
|
682
|
+
const tokens = classifyTokens(resolvedClasses);
|
|
683
|
+
const state = new LayoutState(mode);
|
|
684
|
+
const filtered = tokens.filter((token) => evaluator.evaluate(token, state));
|
|
685
|
+
return { mode, state, filtered, tokens, props, recipe };
|
|
686
|
+
};
|
|
687
|
+
const buildClassString = (ctx) => {
|
|
688
|
+
const built = builder.build(ctx.filtered);
|
|
689
|
+
if (ctx.mode === "none") return built;
|
|
690
|
+
return ctx.filtered.some((t) => t.kind === "layout" && t.value === ctx.mode) ? built : cn(ctx.mode, built);
|
|
691
|
+
};
|
|
692
|
+
const emitDiagnosticsFromContext = (ctx) => {
|
|
693
|
+
if (!DEV) return;
|
|
694
|
+
warnReservedLayoutLiterals(diagnostics, ctx.tokens);
|
|
695
|
+
warnDeadVariants(diagnostics, options, compoundDims, ctx.props, ctx.recipe, ctx.state);
|
|
696
|
+
};
|
|
697
|
+
const combined = (ctx) => {
|
|
698
|
+
const built = buildClassString(ctx);
|
|
699
|
+
emitDiagnosticsFromContext(ctx);
|
|
700
|
+
return built;
|
|
701
|
+
};
|
|
702
|
+
const mainPipeline = composePipelines(resolveLayoutContext, combined);
|
|
653
703
|
return {
|
|
654
704
|
ownedKeys: LAYOUT_OWNED_KEYS,
|
|
655
705
|
pipeline(tag, props, className, recipe) {
|
|
656
|
-
|
|
657
|
-
const raw = pipeline(tag, props, className, recipe);
|
|
658
|
-
const tokens = classifyTokens(raw);
|
|
659
|
-
const state = new LayoutState(mode);
|
|
660
|
-
if (DEV) {
|
|
661
|
-
warnReservedLayoutLiterals(diagnostics, tokens);
|
|
662
|
-
warnDeadVariants(diagnostics, options, compoundDims, props, recipe, state);
|
|
663
|
-
}
|
|
664
|
-
const filtered = tokens.filter((token) => evaluator.evaluate(token, state));
|
|
665
|
-
const built = builder.build(filtered);
|
|
666
|
-
if (mode === "none") return built;
|
|
667
|
-
return filtered.some((t) => t.kind === "layout" && t.value === mode) ? built : cn(mode, built);
|
|
706
|
+
return mainPipeline(tag, props, className, recipe);
|
|
668
707
|
}
|
|
669
708
|
};
|
|
670
709
|
}
|
|
@@ -165,6 +165,13 @@ type ComponentConstraint = {
|
|
|
165
165
|
defaultTag?: string;
|
|
166
166
|
/** True when `enforcement.aria` is a non-empty array literal in the factory call. */
|
|
167
167
|
hasAriaRules: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* True when `enforcement.exclusiveChildren` is statically `true` in the factory call.
|
|
170
|
+
* Only when this is true does an "over the max" child count constitute a real
|
|
171
|
+
* violation — enforcement.children is open-by-default, so extra unmatched children
|
|
172
|
+
* are otherwise allowed regardless of `totalMax`.
|
|
173
|
+
*/
|
|
174
|
+
exclusiveChildren: boolean;
|
|
168
175
|
};
|
|
169
176
|
|
|
170
177
|
/** A diagnostic produced by the static analysis pass. */
|
|
@@ -208,6 +215,11 @@ type PluginOptions = {
|
|
|
208
215
|
* are skipped — their child count is unknowable at build time.
|
|
209
216
|
* - Named const declarations: `export const X = factory(...)` and destructured
|
|
210
217
|
* patterns are not collected; cross-file analysis is future work.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* const diagnostics = analyze(source, 'Button.tsx', { severity: 'error' })
|
|
222
|
+
* ```
|
|
211
223
|
*/
|
|
212
224
|
declare function analyze(code: string, filename: string, options?: PluginOptions): Diagnostic[];
|
|
213
225
|
|
|
@@ -228,6 +240,25 @@ declare function analyze(code: string, filename: string, options?: PluginOptions
|
|
|
228
240
|
* - `styling.compounds` contains non-literal conditions or class values
|
|
229
241
|
* - The total number of combinations exceeds MAX_COMBINATIONS
|
|
230
242
|
* - The styling object already has a `precomputedClasses` property
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* Before:
|
|
246
|
+
* ```ts
|
|
247
|
+
* styling: {
|
|
248
|
+
* variants: { size: { sm: 'text-sm', lg: 'text-lg' } },
|
|
249
|
+
* }
|
|
250
|
+
* ```
|
|
251
|
+
* After:
|
|
252
|
+
* ```ts
|
|
253
|
+
* styling: {
|
|
254
|
+
* variants: { size: { sm: 'text-sm', lg: 'text-lg' } },
|
|
255
|
+
* precomputedClasses: {
|
|
256
|
+
* '__none__:': '',
|
|
257
|
+
* '__none__:size:s:sm': 'text-sm',
|
|
258
|
+
* '__none__:size:s:lg': 'text-lg',
|
|
259
|
+
* },
|
|
260
|
+
* }
|
|
261
|
+
* ```
|
|
231
262
|
*/
|
|
232
263
|
|
|
233
264
|
/**
|
|
@@ -189,7 +189,7 @@ function asArray(node) {
|
|
|
189
189
|
if (!node) return void 0;
|
|
190
190
|
return ts.isArrayLiteralExpression(node) ? node : void 0;
|
|
191
191
|
}
|
|
192
|
-
function
|
|
192
|
+
function asNonNegativeInt(node) {
|
|
193
193
|
if (!node) return void 0;
|
|
194
194
|
if (ts.isNumericLiteral(node)) {
|
|
195
195
|
const n = Number(node.text);
|
|
@@ -197,6 +197,17 @@ function asPositiveInt(node) {
|
|
|
197
197
|
}
|
|
198
198
|
return void 0;
|
|
199
199
|
}
|
|
200
|
+
function asBooleanLiteral(node) {
|
|
201
|
+
if (!node) return void 0;
|
|
202
|
+
switch (node.kind) {
|
|
203
|
+
case ts.SyntaxKind.TrueKeyword:
|
|
204
|
+
return true;
|
|
205
|
+
case ts.SyntaxKind.FalseKeyword:
|
|
206
|
+
return false;
|
|
207
|
+
default:
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
200
211
|
function isFactoryCall(call, names) {
|
|
201
212
|
const { expression } = call;
|
|
202
213
|
if (ts.isIdentifier(expression)) return names.has(expression.text);
|
|
@@ -204,7 +215,7 @@ function isFactoryCall(call, names) {
|
|
|
204
215
|
return false;
|
|
205
216
|
}
|
|
206
217
|
function firstObjectArg(call) {
|
|
207
|
-
const
|
|
218
|
+
const first = call.arguments[0];
|
|
208
219
|
return first && ts.isObjectLiteralExpression(first) ? first : void 0;
|
|
209
220
|
}
|
|
210
221
|
function* walk(node) {
|
|
@@ -316,11 +327,11 @@ function enumerateCombinations(variantMap) {
|
|
|
316
327
|
return total <= MAX_COMBINATIONS;
|
|
317
328
|
});
|
|
318
329
|
if (!totalUnderMaxLimit) return null;
|
|
319
|
-
function
|
|
330
|
+
function enumerateRecursive(remaining) {
|
|
320
331
|
if (remaining.length === 0) return [{}];
|
|
321
332
|
const first = remaining[0];
|
|
322
333
|
const rest = remaining.slice(1);
|
|
323
|
-
const restCombos =
|
|
334
|
+
const restCombos = enumerateRecursive(rest);
|
|
324
335
|
const valueKeys = Object.keys(variantMap[first]);
|
|
325
336
|
const out = [];
|
|
326
337
|
iterate.forEach(restCombos, (combo) => {
|
|
@@ -331,7 +342,7 @@ function enumerateCombinations(variantMap) {
|
|
|
331
342
|
});
|
|
332
343
|
return out;
|
|
333
344
|
}
|
|
334
|
-
return
|
|
345
|
+
return enumerateRecursive(keys2);
|
|
335
346
|
}
|
|
336
347
|
function buildCacheKey(props) {
|
|
337
348
|
const parts = Object.keys(props).sort().map((k) => `${k}:s:${props[k]}`);
|
|
@@ -459,8 +470,8 @@ function extractBound(element) {
|
|
|
459
470
|
if (cardObj) {
|
|
460
471
|
const minNode = getProperty(cardObj, "min");
|
|
461
472
|
const maxNode = getProperty(cardObj, "max");
|
|
462
|
-
const min =
|
|
463
|
-
const max =
|
|
473
|
+
const min = asNonNegativeInt(minNode) ?? 0;
|
|
474
|
+
const max = asNonNegativeInt(maxNode);
|
|
464
475
|
if (min === 0 && max === void 0) {
|
|
465
476
|
cardinality = { kind: "unbounded" };
|
|
466
477
|
} else {
|
|
@@ -497,6 +508,8 @@ function processVariableStatement(node, calleeNames, out) {
|
|
|
497
508
|
const ariaNode = enfObj ? getProperty(enfObj, "aria") : void 0;
|
|
498
509
|
const ariaArr = asArray(ariaNode);
|
|
499
510
|
const hasAriaRules = ariaArr !== void 0 && ariaArr.elements.length > 0;
|
|
511
|
+
const exclusiveChildrenNode = enfObj ? getProperty(enfObj, "exclusiveChildren") : void 0;
|
|
512
|
+
const exclusiveChildren = asBooleanLiteral(exclusiveChildrenNode) ?? false;
|
|
500
513
|
const childrenProp = enfObj ? getProperty(enfObj, "children") : void 0;
|
|
501
514
|
const childrenArr = asArray(childrenProp);
|
|
502
515
|
const rules = [];
|
|
@@ -506,7 +519,7 @@ function processVariableStatement(node, calleeNames, out) {
|
|
|
506
519
|
if (bound) rules.push(bound);
|
|
507
520
|
});
|
|
508
521
|
}
|
|
509
|
-
if (rules.length === 0 && !hasAriaRules && !defaultTag) return;
|
|
522
|
+
if (rules.length === 0 && !hasAriaRules && !defaultTag && !exclusiveChildren) return;
|
|
510
523
|
let totalMin = 0;
|
|
511
524
|
let totalMax = 0;
|
|
512
525
|
iterate.forEach(rules, (rule) => {
|
|
@@ -522,7 +535,8 @@ function processVariableStatement(node, calleeNames, out) {
|
|
|
522
535
|
totalMin,
|
|
523
536
|
totalMax,
|
|
524
537
|
...defaultTag !== void 0 && { defaultTag },
|
|
525
|
-
hasAriaRules
|
|
538
|
+
hasAriaRules,
|
|
539
|
+
exclusiveChildren
|
|
526
540
|
});
|
|
527
541
|
});
|
|
528
542
|
}
|
|
@@ -734,7 +748,7 @@ function analyzeJsxSites(source, constraints, severity) {
|
|
|
734
748
|
const getPos = () => pos ??= source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
735
749
|
if (count !== void 0) {
|
|
736
750
|
const c = byName.get(tagName);
|
|
737
|
-
if (c && (count.max < c.totalMin || count.min > c.totalMax)) {
|
|
751
|
+
if (c && (count.max < c.totalMin || c.exclusiveChildren && count.min > c.totalMax)) {
|
|
738
752
|
const { line, character } = getPos();
|
|
739
753
|
diagnostics.push({
|
|
740
754
|
diagnostic: ViteDiagnostics.cardinalityViolation(
|
|
@@ -868,8 +882,8 @@ function diagnoseUsages(source, constraints, severity) {
|
|
|
868
882
|
const constraint = byName.get(tagName);
|
|
869
883
|
if (!constraint) return;
|
|
870
884
|
if (count === void 0) return;
|
|
871
|
-
const { totalMin, totalMax, name } = constraint;
|
|
872
|
-
if (count.max < totalMin || count.min > totalMax) {
|
|
885
|
+
const { totalMin, totalMax, name, exclusiveChildren } = constraint;
|
|
886
|
+
if (count.max < totalMin || exclusiveChildren && count.min > totalMax) {
|
|
873
887
|
const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
874
888
|
diagnostics.push({
|
|
875
889
|
diagnostic: ViteDiagnostics.cardinalityViolation(
|
|
@@ -948,9 +962,11 @@ var ConstraintRegistry = class {
|
|
|
948
962
|
if (count === void 0) return;
|
|
949
963
|
const constraint = this.resolveConstraint(fileId, tagName);
|
|
950
964
|
if (!constraint) return;
|
|
951
|
-
const { totalMin, totalMax, name } = constraint;
|
|
965
|
+
const { totalMin, totalMax, name, exclusiveChildren } = constraint;
|
|
952
966
|
const { min, max } = count;
|
|
953
|
-
|
|
967
|
+
const tooFew = max < totalMin;
|
|
968
|
+
const tooMany = exclusiveChildren && min > totalMax;
|
|
969
|
+
if (!tooFew && !tooMany) return;
|
|
954
970
|
result.push({
|
|
955
971
|
fileId,
|
|
956
972
|
diagnostic: ViteDiagnostics.cardinalityViolation(name, totalMin, totalMax, min, max),
|
|
@@ -1379,8 +1395,9 @@ function composeStatically(source, calleeNames, importedComponents = /* @__PURE_
|
|
|
1379
1395
|
}
|
|
1380
1396
|
|
|
1381
1397
|
// ../../plugins/vite/src/analyze.ts
|
|
1398
|
+
import { extname } from "path";
|
|
1382
1399
|
function analyze(code, filename, options) {
|
|
1383
|
-
const ext = filename
|
|
1400
|
+
const ext = extname(filename).slice(1);
|
|
1384
1401
|
if (!JSX_EXTS.has(ext)) return [];
|
|
1385
1402
|
const calleeNames = new Set(options?.calleeNames ?? DEFAULT_CALLEE_NAMES);
|
|
1386
1403
|
const severity = options?.severity ?? "warning";
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequireAtLeastOne, Simplify, ReadonlyDeep } from 'type-fest';
|
|
2
|
-
import { Diagnostics, DiagnosticInput } from '../_shared/diagnostics.js';
|
|
2
|
+
import { Diagnostics, DiagnosticInput, DiagnosticsMode } from '../_shared/diagnostics.js';
|
|
3
3
|
import * as vue from 'vue';
|
|
4
4
|
import { AllowedComponentProps } from 'vue';
|
|
5
5
|
|
|
@@ -31,6 +31,26 @@ type MinMax = {
|
|
|
31
31
|
};
|
|
32
32
|
type CardinalityInput = Partial<MinMax>;
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Resolved per-instance state available to a dynamic (`dynamic(...)`) child
|
|
36
|
+
* rule field — the same tag/props every adapter already computes before
|
|
37
|
+
* evaluating children, exposed so a rule can vary by them (e.g. cardinality
|
|
38
|
+
* that depends on the resolved `as` tag).
|
|
39
|
+
*/
|
|
40
|
+
type ChildRuleContext = {
|
|
41
|
+
readonly tag: unknown;
|
|
42
|
+
readonly props: Readonly<AnyRecord>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
declare const RULE_BRAND: unique symbol;
|
|
46
|
+
|
|
47
|
+
type DynamicRule<T, C = unknown> = {
|
|
48
|
+
readonly [RULE_BRAND]: true;
|
|
49
|
+
resolve(context: C): T;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type Rule<T, C = unknown> = T | DynamicRule<T, C>;
|
|
53
|
+
|
|
34
54
|
type ChildRuleMatch<T, U extends T = T> = (child: T) => child is U;
|
|
35
55
|
|
|
36
56
|
type ChildRulePosition = 'first' | 'last' | 'any';
|
|
@@ -38,7 +58,13 @@ type ChildRulePosition = 'first' | 'last' | 'any';
|
|
|
38
58
|
type ChildRuleInput<T = unknown, U extends T = T> = {
|
|
39
59
|
name: string;
|
|
40
60
|
match: ChildRuleMatch<T, U>;
|
|
41
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Either a static cardinality, or `dynamic((ctx) => ...)` to derive it from
|
|
63
|
+
* the resolved tag/props (e.g. a different max depending on `as`). `match`
|
|
64
|
+
* stays static-only — it's already a function, so a dynamic wrapper would
|
|
65
|
+
* be indistinguishable from the predicate itself without one.
|
|
66
|
+
*/
|
|
67
|
+
cardinality?: Rule<CardinalityInput, ChildRuleContext>;
|
|
42
68
|
position?: ChildRulePosition;
|
|
43
69
|
/**
|
|
44
70
|
* Optional component-type reference for O(1) dispatch index.
|
|
@@ -214,9 +240,25 @@ type AriaRule<C extends AriaContext = AriaContext> = (context: C) => readonly Ar
|
|
|
214
240
|
type PropNormalizer = (props: Readonly<AnyRecord & IntrinsicProps>) => Partial<AnyRecord & IntrinsicProps>;
|
|
215
241
|
|
|
216
242
|
type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
217
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Accepts a preset name (`'warn'`, `'throw'`, `'silent'`) or a full `Diagnostics`
|
|
245
|
+
* instance for custom reporting/policy. The string form needs no import from
|
|
246
|
+
* `@praxis-kit/diagnostics`.
|
|
247
|
+
*/
|
|
248
|
+
readonly diagnostics?: Diagnostics | DiagnosticsMode;
|
|
218
249
|
readonly aria?: readonly AriaRule[];
|
|
219
250
|
readonly children?: readonly ChildRuleInput[];
|
|
251
|
+
/**
|
|
252
|
+
* When true, only children matching a `children` rule (or text, per `allowText`)
|
|
253
|
+
* are valid — anything else is rejected. Default: false (open — children not
|
|
254
|
+
* matching any rule are allowed).
|
|
255
|
+
*/
|
|
256
|
+
readonly exclusiveChildren?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* When false, text/number child nodes are rejected regardless of exclusiveChildren
|
|
259
|
+
* or any listed rule. Default: true.
|
|
260
|
+
*/
|
|
261
|
+
readonly allowText?: boolean;
|
|
220
262
|
readonly props?: readonly PropNormalizer[];
|
|
221
263
|
/** Restricts the `as` prop to this set of tags. Violations route through diagnostics. */
|
|
222
264
|
readonly allowedAs?: readonly TAllowed[];
|