praxis-kit 3.1.1 → 4.0.1

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.
@@ -1,7 +1,144 @@
1
1
  import { Plugin } from 'vite';
2
- import { Cardinality, ChildRulePosition, Severity } from '@praxis-kit/core';
2
+ import { Except, Simplify } from 'type-fest';
3
3
  import ts from 'typescript';
4
4
 
5
+ type StringMap<T = unknown> = Record<string, T>;
6
+ type AnyRecord = StringMap<unknown>;
7
+
8
+ declare enum DiagnosticCategory {
9
+ Contract = 0,
10
+ HTML = 1,
11
+ ARIA = 2,
12
+ Composition = 3,
13
+ Rendering = 4,
14
+ Accessibility = 5,
15
+ Performance = 6,
16
+ Internal = 7,
17
+ Deprecation = 8,
18
+ Lint = 9
19
+ }
20
+
21
+ declare enum DiagnosticCode {
22
+ MissingRequiredChild = "COMP1001",
23
+ InvalidParent = "COMP1002",
24
+ InvalidChild = "COMP1003",
25
+ UnexpectedChild = "COMP1004",
26
+ AmbiguousChild = "COMP1005",
27
+ CardinalityMin = "COMP1006",
28
+ CardinalityMax = "COMP1007",
29
+ PositionViolation = "COMP1008",
30
+ AllowedAsViolation = "COMP1009",
31
+ SlotExclusive = "SLOT1001",
32
+ SlotSingleChild = "SLOT1002",
33
+ SlotDiscardedChildren = "SLOT1003",
34
+ SlotRenderFn = "SLOT1004",
35
+ MissingAriaRelationship = "ARIA2001",
36
+ AriaViolation = "ARIA2002",
37
+ AriaAttributeInvalid = "ARIA2003",
38
+ AriaMissingLiveRegion = "ARIA2004",
39
+ AriaMissingAtomic = "ARIA2005",
40
+ AriaRelevantInvalidToken = "ARIA2006",
41
+ AriaRelevantSuperseded = "ARIA2007",
42
+ AriaInvalidRole = "ARIA2008",
43
+ AriaMissingAccessibleName = "ARIA2009",
44
+ AriaAttributeOnPresentational = "ARIA2010",
45
+ AriaHiddenOnFocusable = "ARIA2011",
46
+ AriaRequiredProperty = "ARIA2012",
47
+ AriaInvalidAttributeValue = "ARIA2013",
48
+ AriaRedundantLevelAttribute = "ARIA2014",
49
+ InvalidHeadingHierarchy = "HTML3001",
50
+ HtmlEmptyRole = "HTML3002",
51
+ HtmlImplicitRoleRedundant = "HTML3003",
52
+ HtmlImplicitRoleOverride = "HTML3004",
53
+ HtmlStandaloneRegionOverride = "HTML3005",
54
+ HtmlLandmarkRoleOverride = "HTML3006",
55
+ HtmlInvalidChild = "HTML3007",
56
+ InvalidRenderingTarget = "RENDER4001",
57
+ LintDeadCompoundKey = "LINT5001",
58
+ LintDeadCompoundValue = "LINT5002",
59
+ LintDeadCompoundNonLiteral = "LINT5003",
60
+ LintMissingStrict = "LINT5004",
61
+ LintInvalidDefaultKey = "LINT5005",
62
+ LintInvalidDefaultValue = "LINT5006",
63
+ LintInvalidDefaultNonLiteral = "LINT5007",
64
+ LintNegativeMin = "LINT5008",
65
+ LintNegativeMax = "LINT5009",
66
+ LintMaxLessThanMin = "LINT5010",
67
+ LintZeroMax = "LINT5011",
68
+ LintMultipleFirst = "LINT5012",
69
+ LintMultipleLast = "LINT5013",
70
+ LintMinSumExceedsCapacity = "LINT5014",
71
+ LintCardinalityViolation = "LINT5015",
72
+ LintAriaTagOverride = "LINT5016",
73
+ ContractUnknownVariantDim = "COMP1010",
74
+ ContractUnknownVariantValue = "COMP1011",
75
+ ContractUnknownRecipeKey = "COMP1012",
76
+ ContractInvalidVariantValue = "COMP1013",
77
+ TailwindMultipleDisplayProps = "CSS6001",
78
+ TailwindReservedLayoutLiteral = "CSS6002",
79
+ TailwindDeadVariantClass = "CSS6003",
80
+ PluginInvalidShape = "PLUGIN7001",
81
+ PluginPipelineReturnType = "PLUGIN7002",
82
+ InternalError = "INTERNAL9000"
83
+ }
84
+
85
+ interface SourcePosition {
86
+ line: number;
87
+ col: number;
88
+ }
89
+ interface SourceLocation {
90
+ file: string;
91
+ start: SourcePosition;
92
+ end?: SourcePosition;
93
+ }
94
+
95
+ declare enum Severity$1 {
96
+ Debug = 0,
97
+ Info = 1,
98
+ Warning = 2,
99
+ Error = 3,
100
+ Fatal = 4
101
+ }
102
+
103
+ interface DiagnosticSuggestion {
104
+ title: string;
105
+ description?: string;
106
+ fix?: string;
107
+ }
108
+
109
+ type Context = AnyRecord;
110
+ type Metadata = AnyRecord;
111
+ interface Diagnostic$1 {
112
+ code: DiagnosticCode;
113
+ severity: Severity$1;
114
+ category: DiagnosticCategory;
115
+ message: string;
116
+ rationale?: string;
117
+ component?: string;
118
+ contract?: string;
119
+ location?: SourceLocation;
120
+ suggestions?: DiagnosticSuggestion[];
121
+ context?: Context;
122
+ metadata?: Metadata;
123
+ }
124
+
125
+ type DiagnosticInput = Except<Diagnostic$1, 'severity'>;
126
+
127
+ type MinMax = {
128
+ min: number;
129
+ max: number;
130
+ };
131
+ /** Unboundedness is encoded in the type, not a sentinel value, enabling exhaustive switches. */
132
+ type Cardinality = Simplify<{
133
+ kind: 'bounded';
134
+ } & MinMax> | {
135
+ kind: 'unbounded';
136
+ };
137
+
138
+ type ChildRulePosition = 'first' | 'last' | 'any';
139
+
140
+ type Severity = 'error' | 'warning' | (string & {});
141
+
5
142
  /**
6
143
  * One enforcement.children rule whose cardinality is statically extractable
7
144
  * from the factory call AST. The `Cardinality` discriminated union matches the
@@ -34,7 +171,7 @@ type ComponentConstraint = {
34
171
 
35
172
  /** A diagnostic produced by the static analysis pass. */
36
173
  type Diagnostic = {
37
- message: string;
174
+ diagnostic: DiagnosticInput;
38
175
  /** 1-based line number in the source file. */
39
176
  line: number;
40
177
  /** 1-based column number. */
@@ -62,6 +199,78 @@ type PluginOptions = {
62
199
  severity?: Severity;
63
200
  };
64
201
 
202
+ /**
203
+ * Pure analysis entry point. Parses `code` as TypeScript/TSX, extracts
204
+ * enforcement.children constraints from factory calls, and validates JSX usage
205
+ * sites in the same file.
206
+ *
207
+ * Statically-analyzable scope:
208
+ * - Single file: component must be defined and used in the same source file.
209
+ * - Literal-only children: JSX sites that include any JSX expression ({...})
210
+ * are skipped — their child count is unknowable at build time.
211
+ * - Named const declarations: `export const X = factory(...)` and destructured
212
+ * patterns are not collected; cross-file analysis is future work.
213
+ */
214
+ declare function analyze(code: string, filename: string, options?: PluginOptions): Diagnostic[];
215
+
216
+ /**
217
+ * Compile-time variant class precomputation.
218
+ *
219
+ * Extracts `styling.variants`, `styling.defaults`, and `styling.compounds` from
220
+ * factory call ASTs, enumerates all statically-known prop combinations, computes
221
+ * the variant class string for each, and injects the resulting map as
222
+ * `styling.precomputedClasses` directly into the source.
223
+ *
224
+ * At runtime, `VariantClassResolver` checks `precomputedClasses` before
225
+ * calling CVA — a plain object lookup replaces a CVA invocation + LRU cache
226
+ * write for every covered combination.
227
+ *
228
+ * **Skipped when any of the following are true:**
229
+ * - `styling.variants` is absent or contains non-literal values
230
+ * - `styling.compounds` contains non-literal conditions or class values
231
+ * - The total number of combinations exceeds MAX_COMBINATIONS
232
+ * - The styling object already has a `precomputedClasses` property
233
+ */
234
+
235
+ /**
236
+ * Builds a precomputed class map for the given styling object literal.
237
+ *
238
+ * Returns null when static extraction is not possible (non-literal values,
239
+ * no variants, or combination count exceeds MAX_COMBINATIONS).
240
+ */
241
+ declare function buildPrecomputedClasses(stylingObj: ts.ObjectLiteralExpression): Record<string, string> | null;
242
+ /**
243
+ * Injects precomputed variant class maps into all factory calls in the given
244
+ * source file that have fully-static `styling.variants` configurations.
245
+ *
246
+ * Returns null when no factory calls with injectable variants are found.
247
+ */
248
+ declare function injectPrecomputedClasses(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
249
+
250
+ /**
251
+ * Compile-time dead compound variant pruning.
252
+ *
253
+ * Removes entries from `styling.compounds` whose conditions can never fire:
254
+ * - condition key is not in `styling.variants`
255
+ * - condition value (string) is not a valid value for that variant key
256
+ * - condition value (array) has no element that is a valid value
257
+ *
258
+ * Only entries whose conditions are all statically evaluable (string/array
259
+ * literals throughout) are candidates for pruning — dynamic conditions (variable
260
+ * references, computed values) are left unchanged.
261
+ *
262
+ * Returns null if no factory calls with `styling.compounds` are found, or if
263
+ * every compound in every factory call passes the validity check.
264
+ */
265
+
266
+ /**
267
+ * Applies dead-compound pruning to the given TypeScript source file.
268
+ *
269
+ * Returns null when the file has no factory calls with `styling.compounds`, or
270
+ * when every compound entry passes validity — i.e., when no pruning is needed.
271
+ */
272
+ declare function pruneDeadCompounds(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
273
+
65
274
  type ComponentTokens = {
66
275
  base: string[];
67
276
  variantClasses: string[];
@@ -80,6 +289,7 @@ type DesignTokenManifest = {
80
289
  * destructured patterns fall through (same scope as `collectConstraints`).
81
290
  */
82
291
  declare function collectFileTokens(source: ts.SourceFile, calleeNames: ReadonlySet<string>): Map<string, ComponentTokens>;
292
+ /** Builds a DesignTokenManifest from the accumulated per-component token maps, including a flat sorted `allClasses` union. */
83
293
  declare function buildManifest(allTokens: Map<string, ComponentTokens>): DesignTokenManifest;
84
294
  type DesignTokensOptions = {
85
295
  /**
@@ -108,6 +318,22 @@ type DesignTokensOptions = {
108
318
  */
109
319
  declare function designTokensPlugin(options?: DesignTokensOptions): Plugin;
110
320
 
321
+ type ImportBinding = {
322
+ /** The exported name in the source module (the name before `as`, if any). */
323
+ readonly importedName: string;
324
+ /** The module specifier string (e.g. `'./Button'`). */
325
+ readonly specifier: string;
326
+ };
327
+
328
+ /**
329
+ * Applies the asChild → render-prop transform to the given TypeScript source
330
+ * file and returns the printed output.
331
+ *
332
+ * Returns null if no `asChild` attribute is found in the source (fast path —
333
+ * avoids parsing overhead for files that don't use the pattern).
334
+ */
335
+ declare function transformAsChild(source: ts.SourceFile): string | null;
336
+
111
337
  /**
112
338
  * Compile-time static composition transform.
113
339
  *
@@ -169,125 +395,6 @@ declare function extractStaticComponents(source: ts.SourceFile, calleeNames: Rea
169
395
  */
170
396
  declare function composeStatically(source: ts.SourceFile, calleeNames: ReadonlySet<string>, importedComponents?: ReadonlyMap<string, StaticComponent>): string | null;
171
397
 
172
- type ImportBinding = {
173
- /** The exported name in the source module (the name before `as`, if any). */
174
- readonly importedName: string;
175
- /** The module specifier string (e.g. `'./Button'`). */
176
- readonly specifier: string;
177
- };
178
-
179
- /**
180
- * Pure analysis entry point. Parses `code` as TypeScript/TSX, extracts
181
- * enforcement.children constraints from factory calls, and validates JSX usage
182
- * sites in the same file.
183
- *
184
- * Statically-analyzable scope:
185
- * - Single file: component must be defined and used in the same source file.
186
- * - Literal-only children: JSX sites that include any JSX expression ({...})
187
- * are skipped — their child count is unknowable at build time.
188
- * - Named const declarations: `export const X = factory(...)` and destructured
189
- * patterns are not collected; cross-file analysis is future work.
190
- */
191
- declare function analyze(code: string, filename: string, options?: PluginOptions): Diagnostic[];
192
-
193
- /**
194
- * Compile-time asChild → render-prop transform.
195
- *
196
- * Rewrites JSX usage sites of the form:
197
- * <Component asChild ...props>
198
- * <childTag ...childProps>children</childTag>
199
- * </Component>
200
- *
201
- * to the render-prop form:
202
- * <Component render={(_p) => <childTag ...childProps {..._p} />} ...props />
203
- *
204
- * The render-prop form eliminates the Slot/cloneElement/mergeProps path at
205
- * runtime — resolved props are passed directly to the render callback with no
206
- * element cloning.
207
- *
208
- * **Safety conditions** — the transform is skipped if any of these are true:
209
- * 1. The child has a dynamic `className` expression (cannot merge safely).
210
- * A string-literal `className` IS handled: the transform generates
211
- * `{..._p, className: _p.className + ' childCls'}`.
212
- * 2. The child has a bare `style` or `on*` attribute without an initializer.
213
- * Static object-literal and expression-valued `style` props are merged:
214
- * `style={{..._p.style, ...childStyle}}`. Event handlers are composed:
215
- * `onClick={(_e) => { (childHandler)(_e); _p.onClick?.(_e); }}`.
216
- * 3. The component name starts with a lowercase letter (HTML intrinsic — not
217
- * a polymorphic component).
218
- * 4. There are zero or more than one meaningful child elements.
219
- *
220
- * The transform is conservative: any condition that is not statically clear
221
- * causes the node to be left unchanged.
222
- */
223
-
224
- /**
225
- * Applies the asChild → render-prop transform to the given TypeScript source
226
- * file and returns the printed output.
227
- *
228
- * Returns null if no `asChild` attribute is found in the source (fast path —
229
- * avoids parsing overhead for files that don't use the pattern).
230
- */
231
- declare function transformAsChild(source: ts.SourceFile): string | null;
232
-
233
- /**
234
- * Compile-time dead compound variant pruning.
235
- *
236
- * Removes entries from `styling.compounds` whose conditions can never fire:
237
- * - condition key is not in `styling.variants`
238
- * - condition value (string) is not a valid value for that variant key
239
- * - condition value (array) has no element that is a valid value
240
- *
241
- * Only entries whose conditions are all statically evaluable (string/array
242
- * literals throughout) are candidates for pruning — dynamic conditions (variable
243
- * references, computed values) are left unchanged.
244
- *
245
- * Returns null if no factory calls with `styling.compounds` are found, or if
246
- * every compound in every factory call passes the validity check.
247
- */
248
-
249
- /**
250
- * Applies dead-compound pruning to the given TypeScript source file.
251
- *
252
- * Returns null when the file has no factory calls with `styling.compounds`, or
253
- * when every compound entry passes validity — i.e., when no pruning is needed.
254
- */
255
- declare function pruneDeadCompounds(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
256
-
257
- /**
258
- * Compile-time variant class precomputation.
259
- *
260
- * Extracts `styling.variants`, `styling.defaults`, and `styling.compounds` from
261
- * factory call ASTs, enumerates all statically-known prop combinations, computes
262
- * the variant class string for each, and injects the resulting map as
263
- * `styling.precomputedClasses` directly into the source.
264
- *
265
- * At runtime, `VariantClassResolver` checks `precomputedClasses` before
266
- * calling CVA — a plain object lookup replaces a CVA invocation + LRU cache
267
- * write for every covered combination.
268
- *
269
- * **Skipped when any of the following are true:**
270
- * - `styling.variants` is absent or contains non-literal values
271
- * - `styling.compounds` contains non-literal conditions or class values
272
- * - The total number of combinations exceeds MAX_COMBINATIONS
273
- * - The styling object already has a `precomputedClasses` property
274
- */
275
-
276
- /**
277
- * Builds a precomputed class map for the given styling object literal.
278
- *
279
- * Returns null when static extraction is not possible (non-literal values,
280
- * no variants, or combination count exceeds MAX_COMBINATIONS).
281
- */
282
- declare function buildPrecomputedClasses(stylingObj: ts.ObjectLiteralExpression): Record<string, string> | null;
283
- /**
284
- * Injects precomputed variant class maps into all factory calls in the given
285
- * source file that have fully-static `styling.variants` configurations.
286
- *
287
- * Returns null when no factory calls with injectable variants are found.
288
- */
289
- declare function injectPrecomputedClasses(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
290
-
291
398
  /**
292
399
  * Vite plugin that performs static enforcement.children cardinality checks at
293
400
  * build time for components created with createContractComponent.