praxis-kit 1.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.
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+
3
+ // ../ts-plugin/src/ast.ts
4
+ function getObjectProperty(ts, obj, key) {
5
+ for (const prop of obj.properties) {
6
+ if (!ts.isPropertyAssignment(prop)) continue;
7
+ const name = prop.name;
8
+ if (ts.isIdentifier(name) && name.text === key) return prop;
9
+ if (ts.isStringLiteral(name) && name.text === key) return prop;
10
+ }
11
+ return void 0;
12
+ }
13
+ function asObjectLiteralExpression(ts, node) {
14
+ if (!node) return void 0;
15
+ return ts.isObjectLiteralExpression(node) ? node : void 0;
16
+ }
17
+ function asArrayLiteralExpression(ts, node) {
18
+ if (!node) return void 0;
19
+ return ts.isArrayLiteralExpression(node) ? node : void 0;
20
+ }
21
+ function asNumericValue(ts, node) {
22
+ if (!node) return void 0;
23
+ if (ts.isNumericLiteral(node)) return Number(node.text);
24
+ if (ts.isPrefixUnaryExpression(node) && (node.operator === ts.SyntaxKind.MinusToken || node.operator === ts.SyntaxKind.PlusToken) && ts.isNumericLiteral(node.operand)) {
25
+ const val = Number(node.operand.text);
26
+ return node.operator === ts.SyntaxKind.MinusToken ? -val : val;
27
+ }
28
+ return void 0;
29
+ }
30
+ function isFactoryCall(ts, node, names) {
31
+ const { expression } = node;
32
+ if (ts.isIdentifier(expression)) return names.has(expression.text);
33
+ if (ts.isPropertyAccessExpression(expression)) return names.has(expression.name.text);
34
+ return false;
35
+ }
36
+ function getFirstObjectArg(ts, node) {
37
+ const [first] = node.arguments;
38
+ if (!first) return void 0;
39
+ return ts.isObjectLiteralExpression(first) ? first : void 0;
40
+ }
41
+
42
+ // ../ts-plugin/src/diagnostics/walk-enforcement.ts
43
+ function walkEnforcement(ts, sourceFile, calleeNames, cb) {
44
+ function visit(node) {
45
+ if (ts.isCallExpression(node) && isFactoryCall(ts, node, calleeNames)) {
46
+ const arg = getFirstObjectArg(ts, node);
47
+ if (arg) {
48
+ const enfProp = getObjectProperty(ts, arg, "enforcement");
49
+ if (enfProp) {
50
+ const enf = asObjectLiteralExpression(ts, enfProp.initializer);
51
+ if (enf) cb(node, enf);
52
+ }
53
+ }
54
+ }
55
+ ts.forEachChild(node, visit);
56
+ }
57
+ visit(sourceFile);
58
+ }
59
+
60
+ // ../ts-plugin/src/diagnostics/no-enforcement-without-strict.ts
61
+ var MISSING_STRICT_CODE = 90001;
62
+ function checkNoEnforcementWithoutStrict(ts, sourceFile, calleeNames) {
63
+ const diagnostics = [];
64
+ walkEnforcement(ts, sourceFile, calleeNames, (node, enf) => {
65
+ const hasStrict = getObjectProperty(ts, enf, "strict") !== void 0;
66
+ if (!hasStrict) {
67
+ for (const field of ["children", "aria"]) {
68
+ const fieldProp = getObjectProperty(ts, enf, field);
69
+ if (!fieldProp) continue;
70
+ if (field === "children") {
71
+ const arr = asArrayLiteralExpression(ts, fieldProp.initializer);
72
+ if (!arr || arr.elements.length === 0) continue;
73
+ }
74
+ diagnostics.push({
75
+ file: sourceFile,
76
+ start: node.getStart(sourceFile),
77
+ length: node.getWidth(sourceFile),
78
+ category: ts.DiagnosticCategory.Warning,
79
+ code: MISSING_STRICT_CODE,
80
+ messageText: `enforcement.${field} is defined but enforcement.strict is not explicitly set. Adapter defaults vary \u2014 declare strict explicitly so the behavior is clear at the call site.`,
81
+ source: "@praxis-kit/ts-plugin"
82
+ });
83
+ break;
84
+ }
85
+ }
86
+ });
87
+ return diagnostics;
88
+ }
89
+
90
+ // ../ts-plugin/src/diagnostics/valid-cardinality.ts
91
+ var NEGATIVE_MIN_CODE = 90002;
92
+ var NEGATIVE_MAX_CODE = 90003;
93
+ var MAX_LESS_THAN_MIN_CODE = 90004;
94
+ var ZERO_MAX_CODE = 90005;
95
+ function checkValidCardinality(ts, sourceFile, calleeNames) {
96
+ const diagnostics = [];
97
+ walkEnforcement(ts, sourceFile, calleeNames, (_, enf) => {
98
+ const childrenProp = getObjectProperty(ts, enf, "children");
99
+ if (!childrenProp) return;
100
+ const arr = asArrayLiteralExpression(ts, childrenProp.initializer);
101
+ if (!arr) return;
102
+ for (const element of arr.elements) {
103
+ if (!ts.isObjectLiteralExpression(element)) continue;
104
+ const cardProp = getObjectProperty(ts, element, "cardinality");
105
+ if (!cardProp) continue;
106
+ const card = asObjectLiteralExpression(ts, cardProp.initializer);
107
+ if (!card) continue;
108
+ const minProp = getObjectProperty(ts, card, "min");
109
+ const maxProp = getObjectProperty(ts, card, "max");
110
+ const min = minProp ? asNumericValue(ts, minProp.initializer) : void 0;
111
+ const max = maxProp ? asNumericValue(ts, maxProp.initializer) : void 0;
112
+ if (min !== void 0 && min < 0) {
113
+ diagnostics.push({
114
+ file: sourceFile,
115
+ start: minProp.getStart(sourceFile),
116
+ length: minProp.getWidth(sourceFile),
117
+ category: ts.DiagnosticCategory.Error,
118
+ code: NEGATIVE_MIN_CODE,
119
+ messageText: `cardinality.min must be >= 0 (got ${min}).`,
120
+ source: "@praxis-kit/ts-plugin"
121
+ });
122
+ }
123
+ if (max !== void 0 && max < 0) {
124
+ diagnostics.push({
125
+ file: sourceFile,
126
+ start: maxProp.getStart(sourceFile),
127
+ length: maxProp.getWidth(sourceFile),
128
+ category: ts.DiagnosticCategory.Error,
129
+ code: NEGATIVE_MAX_CODE,
130
+ messageText: `cardinality.max must be >= 0 (got ${max}).`,
131
+ source: "@praxis-kit/ts-plugin"
132
+ });
133
+ }
134
+ if (max !== void 0 && max === 0) {
135
+ diagnostics.push({
136
+ file: sourceFile,
137
+ start: maxProp.getStart(sourceFile),
138
+ length: maxProp.getWidth(sourceFile),
139
+ category: ts.DiagnosticCategory.Warning,
140
+ code: ZERO_MAX_CODE,
141
+ messageText: `cardinality.max of 0 means no children of this type are allowed. Use 0 intentionally or remove the rule.`,
142
+ source: "@praxis-kit/ts-plugin"
143
+ });
144
+ }
145
+ if (min !== void 0 && max !== void 0 && min >= 0 && max > 0 && max < min) {
146
+ diagnostics.push({
147
+ file: sourceFile,
148
+ start: cardProp.getStart(sourceFile),
149
+ length: cardProp.getWidth(sourceFile),
150
+ category: ts.DiagnosticCategory.Error,
151
+ code: MAX_LESS_THAN_MIN_CODE,
152
+ messageText: `cardinality.max (${max}) must be >= cardinality.min (${min}). This rule can never be satisfied.`,
153
+ source: "@praxis-kit/ts-plugin"
154
+ });
155
+ }
156
+ }
157
+ });
158
+ return diagnostics;
159
+ }
160
+
161
+ // ../ts-plugin/src/index.ts
162
+ var DEFAULT_CALLEE_NAMES = ["createContractComponent"];
163
+ function init(modules) {
164
+ const ts = modules.typescript;
165
+ function create(info) {
166
+ const calleeNames = new Set(
167
+ info.config.calleeNames ?? DEFAULT_CALLEE_NAMES
168
+ );
169
+ const proxy = /* @__PURE__ */ Object.create(null);
170
+ const proxyRecord = proxy;
171
+ for (const k of Object.keys(info.languageService)) {
172
+ const x = info.languageService[k];
173
+ proxyRecord[k] = typeof x === "function" ? x.bind(info.languageService) : x;
174
+ }
175
+ proxy.getSemanticDiagnostics = (fileName) => {
176
+ const existing = info.languageService.getSemanticDiagnostics(fileName);
177
+ const program = info.languageService.getProgram();
178
+ if (!program) return existing;
179
+ const sourceFile = program.getSourceFile(fileName);
180
+ if (!sourceFile) return existing;
181
+ const extra = [
182
+ ...checkNoEnforcementWithoutStrict(ts, sourceFile, calleeNames),
183
+ ...checkValidCardinality(ts, sourceFile, calleeNames)
184
+ ];
185
+ return [...existing, ...extra];
186
+ };
187
+ return proxy;
188
+ }
189
+ return { create };
190
+ }
191
+ module.exports = init;
@@ -0,0 +1,10 @@
1
+ import tsserverlibrary from 'typescript/lib/tsserverlibrary';
2
+
3
+ type TS = typeof tsserverlibrary;
4
+ declare function init(modules: {
5
+ typescript: TS;
6
+ }): {
7
+ create: (info: tsserverlibrary.server.PluginCreateInfo) => tsserverlibrary.LanguageService;
8
+ };
9
+
10
+ export { init as default };
@@ -0,0 +1,395 @@
1
+ import { Plugin } from 'vite';
2
+ import { Cardinality, ChildRulePosition, Severity } from '@praxis-kit/core';
3
+ import ts from 'typescript';
4
+
5
+ /**
6
+ * One enforcement.children rule whose cardinality is statically extractable
7
+ * from the factory call AST. The `Cardinality` discriminated union matches the
8
+ * shape used by the runtime `ChildrenEvaluator` — unbounded rules contribute
9
+ * Infinity to `totalMax`.
10
+ */
11
+ type StaticBound = {
12
+ cardinality: Cardinality;
13
+ position: ChildRulePosition;
14
+ };
15
+ /**
16
+ * A component definition collected from a createPolymorphicComponent /
17
+ * createContractComponent call whose enforcement.children array contains at
18
+ * least one rule with a statically-extractable cardinality.
19
+ */
20
+ type ComponentConstraint = {
21
+ /** Variable name the component is bound to (e.g. "Button"). */
22
+ name: string;
23
+ /** One entry per ChildRuleInput whose cardinality is a literal object. */
24
+ rules: StaticBound[];
25
+ /** Sum of all rule.cardinality.min values. */
26
+ totalMin: number;
27
+ /** Sum of all rule.cardinality.max values; Infinity if any rule is unbounded. */
28
+ totalMax: number;
29
+ /** The default HTML tag declared in the factory call (`tag: 'button'`), if statically present. */
30
+ defaultTag?: string;
31
+ /** True when `enforcement.aria` is a non-empty array literal in the factory call. */
32
+ hasAriaRules: boolean;
33
+ };
34
+
35
+ /** A diagnostic produced by the static analysis pass. */
36
+ type Diagnostic = {
37
+ message: string;
38
+ /** 1-based line number in the source file. */
39
+ line: number;
40
+ /** 1-based column number. */
41
+ col: number;
42
+ /**
43
+ * Uses the same Severity vocabulary as ValidationViolation in
44
+ * @praxis-kit/contract — 'error' | 'warning'. The plugin wrapper maps
45
+ * 'warning' → this.warn() and 'error' → this.error() for Rollup/Vite.
46
+ */
47
+ severity: Severity;
48
+ };
49
+
50
+ /** Options accepted by contractPlugin() and analyze(). */
51
+ type PluginOptions = {
52
+ /**
53
+ * Factory function names to look for.
54
+ * @default ['createPolymorphicComponent', 'createContractComponent']
55
+ */
56
+ calleeNames?: string[];
57
+ /**
58
+ * Severity of cardinality violations in Vite build output.
59
+ * Matches the Severity vocabulary used by ValidationViolation.
60
+ * @default 'warning'
61
+ */
62
+ severity?: Severity;
63
+ };
64
+
65
+ type ComponentTokens = {
66
+ base: string[];
67
+ variantClasses: string[];
68
+ compoundClasses: string[];
69
+ tagClasses: string[];
70
+ };
71
+ type DesignTokenManifest = {
72
+ components: Record<string, ComponentTokens>;
73
+ allClasses: string[];
74
+ };
75
+ /**
76
+ * Collects design tokens from all factory calls in a single source file.
77
+ * Each entry in the returned map is keyed by the component variable name.
78
+ *
79
+ * Only `const X = factory(...)` declarations are handled; exported or
80
+ * destructured patterns fall through (same scope as `collectConstraints`).
81
+ */
82
+ declare function collectFileTokens(source: ts.SourceFile, calleeNames: ReadonlySet<string>): Map<string, ComponentTokens>;
83
+ declare function buildManifest(allTokens: Map<string, ComponentTokens>): DesignTokenManifest;
84
+ type DesignTokensOptions = {
85
+ /**
86
+ * Path where the manifest JSON is written, relative to the Vite project root.
87
+ * @default 'praxis-tokens.json'
88
+ */
89
+ outFile?: string;
90
+ } & Pick<PluginOptions, 'calleeNames'>;
91
+ /**
92
+ * Vite plugin that collects every statically-declared class string from
93
+ * `createContractComponent` factory calls and writes a design token manifest
94
+ * to a JSON file on each build.
95
+ *
96
+ * The manifest contains per-component class lists and a flat `allClasses`
97
+ * union that can be used as a Tailwind content source to prevent purging of
98
+ * variant classes.
99
+ *
100
+ * @example
101
+ * // vite.config.ts
102
+ * import { designTokensPlugin } from '@praxis-kit/vite-plugin'
103
+ * export default { plugins: [designTokensPlugin({ outFile: 'praxis-tokens.json' })] }
104
+ *
105
+ * @example
106
+ * // tailwind.config.js
107
+ * export default { content: ['./src/**', './praxis-tokens.json'] }
108
+ */
109
+ declare function designTokensPlugin(options?: DesignTokensOptions): Plugin;
110
+
111
+ /**
112
+ * Pure analysis entry point. Parses `code` as TypeScript/TSX, extracts
113
+ * enforcement.children constraints from factory calls, and validates JSX usage
114
+ * sites in the same file.
115
+ *
116
+ * Statically-analyzable scope:
117
+ * - Single file: component must be defined and used in the same source file.
118
+ * - Literal-only children: JSX sites that include any JSX expression ({...})
119
+ * are skipped — their child count is unknowable at build time.
120
+ * - Named const declarations: `export const X = factory(...)` and destructured
121
+ * patterns are not collected; cross-file analysis is future work.
122
+ */
123
+ declare function analyze(code: string, filename: string, options?: PluginOptions): Diagnostic[];
124
+
125
+ /**
126
+ * Compile-time asChild → render-prop transform.
127
+ *
128
+ * Rewrites JSX usage sites of the form:
129
+ * <Component asChild ...props>
130
+ * <childTag ...childProps>children</childTag>
131
+ * </Component>
132
+ *
133
+ * to the render-prop form:
134
+ * <Component render={(_p) => <childTag ...childProps {..._p} />} ...props />
135
+ *
136
+ * The render-prop form eliminates the Slot/cloneElement/mergeProps path at
137
+ * runtime — resolved props are passed directly to the render callback with no
138
+ * element cloning.
139
+ *
140
+ * **Safety conditions** — the transform is skipped if any of these are true:
141
+ * 1. The child has a dynamic `className` expression (cannot merge safely).
142
+ * A string-literal `className` IS handled: the transform generates
143
+ * `{..._p, className: _p.className + ' childCls'}`.
144
+ * 2. The child has a bare `style` or `on*` attribute without an initializer.
145
+ * Static object-literal and expression-valued `style` props are merged:
146
+ * `style={{..._p.style, ...childStyle}}`. Event handlers are composed:
147
+ * `onClick={(_e) => { (childHandler)(_e); _p.onClick?.(_e); }}`.
148
+ * 3. The component name starts with a lowercase letter (HTML intrinsic — not
149
+ * a polymorphic component).
150
+ * 4. There are zero or more than one meaningful child elements.
151
+ *
152
+ * The transform is conservative: any condition that is not statically clear
153
+ * causes the node to be left unchanged.
154
+ */
155
+
156
+ /**
157
+ * Applies the asChild → render-prop transform to the given TypeScript source
158
+ * file and returns the printed output.
159
+ *
160
+ * Returns null if no `asChild` attribute is found in the source (fast path —
161
+ * avoids parsing overhead for files that don't use the pattern).
162
+ */
163
+ declare function transformAsChild(source: ts.SourceFile): string | null;
164
+
165
+ /**
166
+ * Compile-time dead compound variant pruning.
167
+ *
168
+ * Removes entries from `styling.compounds` whose conditions can never fire:
169
+ * - condition key is not in `styling.variants`
170
+ * - condition value (string) is not a valid value for that variant key
171
+ * - condition value (array) has no element that is a valid value
172
+ *
173
+ * Only entries whose conditions are all statically evaluable (string/array
174
+ * literals throughout) are candidates for pruning — dynamic conditions (variable
175
+ * references, computed values) are left unchanged.
176
+ *
177
+ * Returns null if no factory calls with `styling.compounds` are found, or if
178
+ * every compound in every factory call passes the validity check.
179
+ */
180
+
181
+ /**
182
+ * Applies dead-compound pruning to the given TypeScript source file.
183
+ *
184
+ * Returns null when the file has no factory calls with `styling.compounds`, or
185
+ * when every compound entry passes validity — i.e., when no pruning is needed.
186
+ */
187
+ declare function pruneDeadCompounds(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
188
+
189
+ /**
190
+ * Compile-time variant class precomputation.
191
+ *
192
+ * Extracts `styling.variants`, `styling.defaults`, and `styling.compounds` from
193
+ * factory call ASTs, enumerates all statically-known prop combinations, computes
194
+ * the variant class string for each, and injects the resulting map as
195
+ * `styling.precomputedClasses` directly into the source.
196
+ *
197
+ * At runtime, `VariantClassResolver` checks `precomputedClasses` before
198
+ * calling CVA — a plain object lookup replaces a CVA invocation + LRU cache
199
+ * write for every covered combination.
200
+ *
201
+ * **Skipped when any of the following are true:**
202
+ * - `styling.variants` is absent or contains non-literal values
203
+ * - `styling.compounds` contains non-literal conditions or class values
204
+ * - The total number of combinations exceeds MAX_COMBINATIONS
205
+ * - The styling object already has a `precomputedClasses` property
206
+ */
207
+
208
+ /**
209
+ * Builds a precomputed class map for the given styling object literal.
210
+ *
211
+ * Returns null when static extraction is not possible (non-literal values,
212
+ * no variants, or combination count exceeds MAX_COMBINATIONS).
213
+ */
214
+ declare function buildPrecomputedClasses(stylingObj: ts.ObjectLiteralExpression): Record<string, string> | null;
215
+ /**
216
+ * Injects precomputed variant class maps into all factory calls in the given
217
+ * source file that have fully-static `styling.variants` configurations.
218
+ *
219
+ * Returns null when no factory calls with injectable variants are found.
220
+ */
221
+ declare function injectPrecomputedClasses(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
222
+
223
+ /**
224
+ * Compile-time static composition transform.
225
+ *
226
+ * For same-file factory calls that have `precomputedClasses` injected (by
227
+ * classExtractPlugin), replaces static JSX usage sites with direct element
228
+ * creation — bypassing the runtime render pipeline entirely.
229
+ *
230
+ * Example:
231
+ * // Source (same file defines Button and uses it)
232
+ * const Button = createContractComponent({ tag: 'button', styling: { precomputedClasses: {...} } })
233
+ * <Button size="lg">Click</Button>
234
+ *
235
+ * // Output
236
+ * const Button = createContractComponent({ ... }) // unchanged — still exported
237
+ * <button className="btn btn-lg">Click</button> // inlined!
238
+ *
239
+ * **Eligibility conditions** — a usage site is inlined only when:
240
+ * 1. The component is defined in the same file via a factory call with
241
+ * `precomputedClasses` (classExtractPlugin must run first in the chain)
242
+ * 2. No `as`, `asChild`, `render`, or spread attributes at the usage site
243
+ * 3. All variant props are static string literals
244
+ * 4. `className` is absent or a static string literal
245
+ * 5. The factory config has no top-level `defaults` and no `enforcement`
246
+ * (either would require runtime prop normalization that inlining skips)
247
+ *
248
+ * The factory call itself is intentionally left in the output so the component
249
+ * remains exportable for cross-file consumption that falls back to the runtime
250
+ * path. Dead-code elimination at the bundler level can remove it when no
251
+ * runtime path remains.
252
+ *
253
+ * **Deferred:** cross-file inlining (component defined in one module, used in
254
+ * another) requires Vite module-graph traversal and is not yet implemented.
255
+ */
256
+
257
+ /**
258
+ * Applies the static composition transform to the given source file.
259
+ *
260
+ * Returns null when:
261
+ * - No eligible factory calls are found in the file
262
+ * - No eligible usage sites are found after analysis
263
+ */
264
+ declare function composeStatically(source: ts.SourceFile, calleeNames: ReadonlySet<string>): string | null;
265
+
266
+ /**
267
+ * Vite plugin that performs static enforcement.children cardinality checks at
268
+ * build time for components created with createContractComponent.
269
+ *
270
+ * **Single-file scope:** Components defined and used in the same `.tsx` / `.jsx`
271
+ * file are validated during `transform`. JSX children containing expressions
272
+ * (`{...}`) are skipped — their count is unknowable at compile time.
273
+ *
274
+ * **Cross-file scope:** Components imported from other files are validated in
275
+ * `buildEnd` once the full constraint registry is populated. Only named imports
276
+ * whose source file was also transformed by this plugin are checked.
277
+ *
278
+ * @example
279
+ * // vite.config.ts
280
+ * import { contractPlugin } from '@praxis-kit/vite-plugin'
281
+ * export default { plugins: [contractPlugin()] }
282
+ */
283
+ declare function contractPlugin(options?: PluginOptions): Plugin;
284
+ /**
285
+ * Vite plugin that removes dead `styling.compounds` entries from factory calls
286
+ * at build time, reducing bundle size and eliminating unreachable CVA compound
287
+ * checks at runtime.
288
+ *
289
+ * A compound entry is dead when any of its conditions reference a variant key
290
+ * that does not exist in `styling.variants`, or a value that is not valid for
291
+ * that key. Only entries whose conditions are fully static (string/array
292
+ * literals) are pruned — dynamic conditions are left unchanged.
293
+ *
294
+ * Place before `contractPlugin` so the pruned source is what gets analyzed.
295
+ *
296
+ * @example
297
+ * // vite.config.ts
298
+ * import { compoundPrunePlugin, contractPlugin } from '@praxis-kit/vite-plugin'
299
+ * export default { plugins: [compoundPrunePlugin(), contractPlugin()] }
300
+ */
301
+ declare function compoundPrunePlugin(options?: Pick<PluginOptions, 'calleeNames'>): Plugin;
302
+ /**
303
+ * Vite plugin that pre-computes variant class strings at build time and injects
304
+ * them as a static `precomputedClasses` map into each factory call's `styling`
305
+ * object.
306
+ *
307
+ * At runtime, `VariantClassResolver` checks this map before calling CVA — a
308
+ * plain object lookup replaces a CVA invocation + LRU cache write for every
309
+ * statically-known combination. Only combinations that appear in the map are
310
+ * accelerated; invalid or dynamic variant values fall through to the existing
311
+ * compute path unchanged.
312
+ *
313
+ * Injection is skipped when:
314
+ * - `styling.variants` is absent or contains non-literal values
315
+ * - `styling.compounds` contains non-literal conditions or classes
316
+ * - The number of valid combinations exceeds 512
317
+ *
318
+ * Place after `compoundPrunePlugin` so the injected map reflects the live
319
+ * compound set.
320
+ *
321
+ * @example
322
+ * // vite.config.ts
323
+ * import { compoundPrunePlugin, classExtractPlugin, contractPlugin } from '@praxis-kit/vite-plugin'
324
+ * export default { plugins: [compoundPrunePlugin(), classExtractPlugin(), contractPlugin()] }
325
+ */
326
+ declare function classExtractPlugin(options?: Pick<PluginOptions, 'calleeNames'>): Plugin;
327
+ /**
328
+ * Vite plugin that transforms `asChild` JSX usage sites to the render-prop form
329
+ * at build time, eliminating the Slot/cloneElement/mergeProps runtime path.
330
+ *
331
+ * Only transforms sites where the transform is semantically safe:
332
+ * - Exactly one static child element
333
+ * - Child has no `className`, `style`, or event handler props
334
+ *
335
+ * Complex asChild patterns (conflicting props, dynamic children, Slottable
336
+ * siblings) are left unchanged and handled by the runtime Slot path.
337
+ *
338
+ * Place before `contractPlugin` so cardinality analysis sees the transformed
339
+ * source.
340
+ *
341
+ * @example
342
+ * // vite.config.ts
343
+ * import { slotTransformPlugin, contractPlugin } from '@praxis-kit/vite-plugin'
344
+ * export default { plugins: [slotTransformPlugin(), contractPlugin()] }
345
+ */
346
+ declare function slotTransformPlugin(): Plugin;
347
+ /**
348
+ * Vite plugin that replaces same-file polymorphic component usage sites with
349
+ * direct element creation at build time, eliminating the runtime render
350
+ * pipeline for statically-analyzable usages.
351
+ *
352
+ * **Requires classExtractPlugin to run first** so that `precomputedClasses` is
353
+ * present in the factory call before this plugin reads it. Place after
354
+ * `classExtractPlugin` in the plugins array.
355
+ *
356
+ * A usage site is inlined when:
357
+ * - The component is defined in the same file with `precomputedClasses` injected
358
+ * - No `as`, `asChild`, `render`, or spread attributes at the site
359
+ * - All variant props are static string literals
360
+ * - `className` is absent or a static string literal
361
+ * - The factory config has no `defaults` or `enforcement`
362
+ *
363
+ * Sites that do not meet all conditions fall through to the normal runtime path
364
+ * unchanged.
365
+ *
366
+ * @example
367
+ * // vite.config.ts
368
+ * import { classExtractPlugin, staticCompositionPlugin } from '@praxis-kit/vite-plugin'
369
+ * export default { plugins: [classExtractPlugin(), staticCompositionPlugin()] }
370
+ */
371
+ declare function staticCompositionPlugin(options?: Pick<PluginOptions, 'calleeNames'>): Plugin;
372
+ /**
373
+ * Convenience plugin bundle that applies all three build-time rendering
374
+ * optimisations in the correct dependency order:
375
+ *
376
+ * 1. `slotTransformPlugin` — rewrites `asChild` → render-prop form,
377
+ * eliminating `cloneElement` for static sites
378
+ * 2. `classExtractPlugin` — injects `precomputedClasses` into factory
379
+ * calls for O(1) variant class resolution
380
+ * 3. `staticCompositionPlugin` — inlines same-file static usages into direct
381
+ * element creation, bypassing the runtime
382
+ * pipeline entirely
383
+ *
384
+ * Place before `contractPlugin` so cardinality analysis sees the transformed
385
+ * source. Especially effective for SSR builds where each component renders
386
+ * exactly once per request and eliminates per-render pipeline overhead.
387
+ *
388
+ * @example
389
+ * // vite.config.ts
390
+ * import { ssrOptimizePlugin, contractPlugin } from '@praxis-kit/vite-plugin'
391
+ * export default { plugins: [ssrOptimizePlugin(), contractPlugin()] }
392
+ */
393
+ declare function ssrOptimizePlugin(options?: Pick<PluginOptions, 'calleeNames'>): Plugin[];
394
+
395
+ export { type ComponentConstraint, type ComponentTokens, type DesignTokenManifest, type DesignTokensOptions, type Diagnostic, type PluginOptions, type StaticBound, analyze, buildManifest, buildPrecomputedClasses, classExtractPlugin, collectFileTokens, composeStatically, compoundPrunePlugin, contractPlugin, designTokensPlugin, injectPrecomputedClasses, pruneDeadCompounds, slotTransformPlugin, ssrOptimizePlugin, staticCompositionPlugin, transformAsChild };