@bamboocss/vite 1.25.0 → 1.28.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/index.d.cts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { Plugin } from "vite";
2
2
  import { Context } from "@bamboocss/core";
3
- import { Dict, ParserResultInterface } from "@bamboocss/types";
3
+ import { Dict, ParserResultInterface, ResultItem } from "@bamboocss/types";
4
4
  import MagicString from "magic-string";
5
+ import { SourceFile } from "ts-morph";
6
+
5
7
  //#region src/css.d.ts
6
8
  /**
7
9
  * What a project imports to get the stylesheet.
@@ -30,6 +32,49 @@ interface BambooCssPluginOptions {
30
32
  */
31
33
  declare const bamboocssCss: (options?: BambooCssPluginOptions) => Plugin;
32
34
  //#endregion
35
+ //#region src/fold-recipe.d.ts
36
+ /**
37
+ * Lowering a call of an inline recipe to the class string it produces.
38
+ *
39
+ * The prize is not the `cva` runtime, which is small. It is the *config*: `cva({ base, variants })`
40
+ * ships the whole style object to the browser purely so the runtime can hash it into a name and
41
+ * pick classes off it. Those styles are already in the stylesheet. Once every call of a binding
42
+ * is lowered, the binding is unreferenced and a bundler drops the config with it — measured at
43
+ * 81 kB gzipped across one application's 1,297 inline recipes, against 4.5 kB for the runtime.
44
+ *
45
+ * Correct by construction rather than by a matching reimplementation: the class names come from
46
+ * `getRecipeIdentity` and `getRecipeClassNames`, the same functions the browser runs, and the
47
+ * prefixing and hashing from `classFormatter`, which is what the encoder emitted rules under.
48
+ */
49
+ type Dict$1 = Record<string, unknown>;
50
+ /** A recipe's config, as the extractor resolved it. */
51
+ interface RecipeConfig {
52
+ className?: string;
53
+ base?: Dict$1;
54
+ variants?: Record<string, Record<string, unknown>>;
55
+ defaultVariants?: Record<string, unknown>;
56
+ compoundVariants?: unknown[];
57
+ /** Present on a slot recipe, which resolves to one class per slot rather than a string. */
58
+ slots?: unknown;
59
+ }
60
+ /**
61
+ * A recipe the fold can lower against.
62
+ *
63
+ * `name` is hashed once here rather than per call site: `getRecipeIdentity` serialises the
64
+ * whole config to hash it, which measured as 91% of the per-call work when it sat inside
65
+ * `lowerRecipeCall`. The runtime does it once per `cva()` for the same reason.
66
+ *
67
+ * `box` is the *definition's* node, carried so the fold can register the module the config
68
+ * came from as a watch dependency. Without it, editing a config in another module leaves
69
+ * every literal folded against it stale — the stylesheet gets a new identity and the element
70
+ * keeps the old class.
71
+ */
72
+ interface RecipeEntry {
73
+ config: RecipeConfig;
74
+ name: string;
75
+ box: ResultItem['box'];
76
+ }
77
+ //#endregion
33
78
  //#region src/runtime-css.d.ts
34
79
  /**
35
80
  * The generated runtime's `css`, rebuilt in-process from a resolved context.
@@ -60,7 +105,7 @@ declare const createRuntimeCss: (ctx: Context) => RuntimeCss;
60
105
  * Why a call site was left alone. Surfaced through `panda`-style diagnostics so a
61
106
  * user can tell the difference between "this folded" and "this silently didn't".
62
107
  */
63
- type SkipReason = 'dynamic' | 'raw-call' | 'not-foldable' | 'recipe-call' | 'unsupported-kind' | 'not-imported' | 'no-call-expression' | 'overlapping' | 'empty' | 'unresolved-token';
108
+ type SkipReason = 'dynamic' | 'raw-call' | 'not-foldable' | 'recipe-call' | 'unsupported-kind' | 'not-imported' | 'no-call-expression' | 'overlapping' | 'empty' | 'unresolved-token' | 'runtime-binding';
64
109
  interface FoldedCall {
65
110
  name: string;
66
111
  /**
@@ -119,6 +164,61 @@ interface FoldOptions {
119
164
  * On by default.
120
165
  */
121
166
  partial?: boolean;
167
+ /**
168
+ * Parse another module, for a recipe whose `cva` config lives outside this one.
169
+ *
170
+ * Threaded in rather than read off `ctx`, because the project belongs to the node
171
+ * context and this signature takes the core one. Its absence is a supported state:
172
+ * without it a cross-module recipe call is still *reported*, which is the half that
173
+ * used to be missing entirely — it simply cannot be lowered.
174
+ *
175
+ * Pulling the module on demand is also what makes the fold order-independent. A
176
+ * bundler transforms a consumer before the module it imports, so a registry built
177
+ * from what has been transformed so far would fold or decline the same file
178
+ * depending on discovery order.
179
+ */
180
+ parseModule?: (filePath: string) => ParserResultInterface | undefined;
181
+ /**
182
+ * Configs of modules other than this one, shared across a build.
183
+ *
184
+ * Owned by the caller because it has to outlive one call: the declaring module would
185
+ * otherwise be re-parsed once per module that imports it, which is `consumers x module
186
+ * size` on the transform path — measured at 909ms against 8.3ms for fifty consumers of a
187
+ * hundred-recipe module. The caller clears an entry when the file changes, which is the
188
+ * only place that knows.
189
+ */
190
+ recipeConfigCache?: Map<string, ForeignRecipes>;
191
+ /**
192
+ * Also report bamboo bindings the rewrite left behind, whatever the skip ledger says.
193
+ *
194
+ * The ledger holds only calls something recognised, so it answers "of the calls I looked
195
+ * at, which survived" — and a guarantee built on it is worth exactly what the recogniser
196
+ * is. A cross-module recipe call used to appear in neither column, so a build could report
197
+ * a clean sweep while shipping hundreds of them.
198
+ *
199
+ * This asks what the guarantee actually claims: after the rewrite, is anything from a
200
+ * bamboo module still referenced? Off by default because it costs an identifier walk, and
201
+ * only `strict` needs an answer it can fail a build on.
202
+ */
203
+ reportSurvivors?: boolean;
204
+ /**
205
+ * The module's own AST, when the caller already holds it.
206
+ *
207
+ * Only `reportSurvivors` needs it, and only for the case it exists to catch: a module whose
208
+ * bamboo usage produced no parser result at all has no call to reach the AST through.
209
+ */
210
+ sourceFile?: SourceFile;
211
+ }
212
+ /**
213
+ * What one foreign module contributes, in a form that outlives it.
214
+ *
215
+ * Plain data only. Anything holding a ts-morph node would be read after the next
216
+ * `addSourceFile` forgets that module's tree.
217
+ */
218
+ interface ForeignRecipes {
219
+ configs: Map<string, RecipeEntry>;
220
+ /** How that module spelled the css module, for a helper import written into a consumer. */
221
+ cssSpecifier?: string;
122
222
  }
123
223
  declare const foldSource: (options: FoldOptions) => FoldResult;
124
224
  //#endregion
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import MagicString from "magic-string";
2
- import { Node } from "ts-morph";
2
+ import { Node, SourceFile } from "ts-morph";
3
3
  import { Context } from "@bamboocss/core";
4
4
  import { Plugin } from "vite";
5
- import { Dict, ParserResultInterface } from "@bamboocss/types";
5
+ import { Dict, ParserResultInterface, ResultItem } from "@bamboocss/types";
6
6
 
7
7
  //#region src/css.d.ts
8
8
  /**
@@ -32,6 +32,49 @@ interface BambooCssPluginOptions {
32
32
  */
33
33
  declare const bamboocssCss: (options?: BambooCssPluginOptions) => Plugin;
34
34
  //#endregion
35
+ //#region src/fold-recipe.d.ts
36
+ /**
37
+ * Lowering a call of an inline recipe to the class string it produces.
38
+ *
39
+ * The prize is not the `cva` runtime, which is small. It is the *config*: `cva({ base, variants })`
40
+ * ships the whole style object to the browser purely so the runtime can hash it into a name and
41
+ * pick classes off it. Those styles are already in the stylesheet. Once every call of a binding
42
+ * is lowered, the binding is unreferenced and a bundler drops the config with it — measured at
43
+ * 81 kB gzipped across one application's 1,297 inline recipes, against 4.5 kB for the runtime.
44
+ *
45
+ * Correct by construction rather than by a matching reimplementation: the class names come from
46
+ * `getRecipeIdentity` and `getRecipeClassNames`, the same functions the browser runs, and the
47
+ * prefixing and hashing from `classFormatter`, which is what the encoder emitted rules under.
48
+ */
49
+ type Dict$1 = Record<string, unknown>;
50
+ /** A recipe's config, as the extractor resolved it. */
51
+ interface RecipeConfig {
52
+ className?: string;
53
+ base?: Dict$1;
54
+ variants?: Record<string, Record<string, unknown>>;
55
+ defaultVariants?: Record<string, unknown>;
56
+ compoundVariants?: unknown[];
57
+ /** Present on a slot recipe, which resolves to one class per slot rather than a string. */
58
+ slots?: unknown;
59
+ }
60
+ /**
61
+ * A recipe the fold can lower against.
62
+ *
63
+ * `name` is hashed once here rather than per call site: `getRecipeIdentity` serialises the
64
+ * whole config to hash it, which measured as 91% of the per-call work when it sat inside
65
+ * `lowerRecipeCall`. The runtime does it once per `cva()` for the same reason.
66
+ *
67
+ * `box` is the *definition's* node, carried so the fold can register the module the config
68
+ * came from as a watch dependency. Without it, editing a config in another module leaves
69
+ * every literal folded against it stale — the stylesheet gets a new identity and the element
70
+ * keeps the old class.
71
+ */
72
+ interface RecipeEntry {
73
+ config: RecipeConfig;
74
+ name: string;
75
+ box: ResultItem['box'];
76
+ }
77
+ //#endregion
35
78
  //#region src/runtime-css.d.ts
36
79
  /**
37
80
  * The generated runtime's `css`, rebuilt in-process from a resolved context.
@@ -62,7 +105,7 @@ declare const createRuntimeCss: (ctx: Context) => RuntimeCss;
62
105
  * Why a call site was left alone. Surfaced through `panda`-style diagnostics so a
63
106
  * user can tell the difference between "this folded" and "this silently didn't".
64
107
  */
65
- type SkipReason = 'dynamic' | 'raw-call' | 'not-foldable' | 'recipe-call' | 'unsupported-kind' | 'not-imported' | 'no-call-expression' | 'overlapping' | 'empty' | 'unresolved-token';
108
+ type SkipReason = 'dynamic' | 'raw-call' | 'not-foldable' | 'recipe-call' | 'unsupported-kind' | 'not-imported' | 'no-call-expression' | 'overlapping' | 'empty' | 'unresolved-token' | 'runtime-binding';
66
109
  interface FoldedCall {
67
110
  name: string;
68
111
  /**
@@ -121,6 +164,61 @@ interface FoldOptions {
121
164
  * On by default.
122
165
  */
123
166
  partial?: boolean;
167
+ /**
168
+ * Parse another module, for a recipe whose `cva` config lives outside this one.
169
+ *
170
+ * Threaded in rather than read off `ctx`, because the project belongs to the node
171
+ * context and this signature takes the core one. Its absence is a supported state:
172
+ * without it a cross-module recipe call is still *reported*, which is the half that
173
+ * used to be missing entirely — it simply cannot be lowered.
174
+ *
175
+ * Pulling the module on demand is also what makes the fold order-independent. A
176
+ * bundler transforms a consumer before the module it imports, so a registry built
177
+ * from what has been transformed so far would fold or decline the same file
178
+ * depending on discovery order.
179
+ */
180
+ parseModule?: (filePath: string) => ParserResultInterface | undefined;
181
+ /**
182
+ * Configs of modules other than this one, shared across a build.
183
+ *
184
+ * Owned by the caller because it has to outlive one call: the declaring module would
185
+ * otherwise be re-parsed once per module that imports it, which is `consumers x module
186
+ * size` on the transform path — measured at 909ms against 8.3ms for fifty consumers of a
187
+ * hundred-recipe module. The caller clears an entry when the file changes, which is the
188
+ * only place that knows.
189
+ */
190
+ recipeConfigCache?: Map<string, ForeignRecipes>;
191
+ /**
192
+ * Also report bamboo bindings the rewrite left behind, whatever the skip ledger says.
193
+ *
194
+ * The ledger holds only calls something recognised, so it answers "of the calls I looked
195
+ * at, which survived" — and a guarantee built on it is worth exactly what the recogniser
196
+ * is. A cross-module recipe call used to appear in neither column, so a build could report
197
+ * a clean sweep while shipping hundreds of them.
198
+ *
199
+ * This asks what the guarantee actually claims: after the rewrite, is anything from a
200
+ * bamboo module still referenced? Off by default because it costs an identifier walk, and
201
+ * only `strict` needs an answer it can fail a build on.
202
+ */
203
+ reportSurvivors?: boolean;
204
+ /**
205
+ * The module's own AST, when the caller already holds it.
206
+ *
207
+ * Only `reportSurvivors` needs it, and only for the case it exists to catch: a module whose
208
+ * bamboo usage produced no parser result at all has no call to reach the AST through.
209
+ */
210
+ sourceFile?: SourceFile;
211
+ }
212
+ /**
213
+ * What one foreign module contributes, in a form that outlives it.
214
+ *
215
+ * Plain data only. Anything holding a ts-morph node would be read after the next
216
+ * `addSourceFile` forgets that module's tree.
217
+ */
218
+ interface ForeignRecipes {
219
+ configs: Map<string, RecipeEntry>;
220
+ /** How that module spelled the css module, for a helper import written into a consumer. */
221
+ cssSpecifier?: string;
124
222
  }
125
223
  declare const foldSource: (options: FoldOptions) => FoldResult;
126
224
  //#endregion