@bamboocss/vite 1.35.2 → 1.35.4

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.cjs CHANGED
@@ -170,20 +170,32 @@ const replaceAssetReferences = (bundle, previous, next, sourcemap) => {
170
170
  }
171
171
  };
172
172
  /**
173
+ * Could this bundle entry be the generated stylesheet?
174
+ *
175
+ * The filename is checked before the bytes because the alternative decodes every asset in the
176
+ * bundle to a UTF-8 string in order to search it — fonts, images and sourcemaps included. On an
177
+ * app with a large asset graph that is seconds of decode and a lot of garbage, twice over, to
178
+ * answer a question the extension already answers. The marker is a CSS custom property, so it
179
+ * cannot occur anywhere but CSS.
180
+ */
181
+ const carriesGeneratedCss = (output) => output.type === "asset" && output.fileName.endsWith(".css");
182
+ /**
173
183
  * Prune and rename compiler-owned CSS, then give changed assets a hash of their final bytes.
174
184
  *
175
185
  * Rollup has already expanded `[hash]` when `generateBundle` runs. Mutating only `source`
176
186
  * would therefore leave two different reachable subsets under one CDN key. The extra final
177
187
  * hash is not cosmetic: it makes late graph reachability cache-safe.
178
188
  */
179
- const optimizeStaticCssAssets = (bundle, session) => {
189
+ const optimizeStaticCssAssets = (bundle, session, options = {}) => {
190
+ const { rename = true } = options;
180
191
  for (const [bundleName, output] of Object.entries(bundle)) {
181
- if (output.type !== "asset") continue;
192
+ if (!carriesGeneratedCss(output)) continue;
182
193
  const source = typeof output.source === "string" ? output.source : Buffer.from(output.source).toString();
183
194
  if (!source.includes("--made-with-bamboo")) continue;
184
195
  const optimized = pruneStaticCss(source, session);
185
196
  output.source = optimized;
186
197
  if (optimized === source) continue;
198
+ if (!rename) continue;
187
199
  const nextName = output.fileName.replace(/\.css$/, `.b-${(0, _bamboocss_shared.toHash)(optimized)}.css`);
188
200
  if (nextName === output.fileName) continue;
189
201
  if (bundle[nextName] && bundle[nextName] !== output) throw new Error(`bamboocss: final CSS asset name collision at ${JSON.stringify(nextName)}.`);
@@ -208,7 +220,7 @@ const optimizeStaticCssAssets = (bundle, session) => {
208
220
  * process just wrote, which is a race on any watch rebuild.
209
221
  */
210
222
  const bamboocssCss = (options) => {
211
- const { configPath, cwd, session } = options;
223
+ const { configPath, cwd, session, renameCssAsset = true } = options;
212
224
  const builder = new _bamboocss_node.Builder();
213
225
  let server;
214
226
  let command = "build";
@@ -292,7 +304,13 @@ const bamboocssCss = (options) => {
292
304
  generateBundle: {
293
305
  order: "post",
294
306
  handler(_, bundle) {
295
- optimizeStaticCssAssets(bundle, session);
307
+ const rolldown = Boolean(this.meta?.rolldownVersion);
308
+ optimizeStaticCssAssets(bundle, session, { rename: renameCssAsset && !rolldown });
309
+ if (!session.transformedFiles.size) return;
310
+ if (!Object.values(bundle).some((output) => {
311
+ if (!carriesGeneratedCss(output)) return false;
312
+ return (typeof output.source === "string" ? output.source : Buffer.from(output.source).toString()).includes("--made-with-bamboo");
313
+ })) throw new Error(`bamboocss: ${session.transformedFiles.size} module(s) were compiled to Bamboo class values, but no emitted asset carries the generated stylesheet. The build would ship unstyled.\n\nThis happens when another plugin, or the bundler itself, drops or replaces the CSS asset after it is emitted. If you are on Rolldown, report this — the rename that used to cause it is already disabled there. Otherwise look for a plugin running in \`generateBundle\` that rewrites CSS assets.`);
296
314
  }
297
315
  }
298
316
  };
@@ -709,9 +727,10 @@ const propertyKey = (nameNode) => {
709
727
  * there is nothing to match — the host here is any import of the generated css module, which
710
728
  * a file defining a recipe necessarily has, since `cva` came from it.
711
729
  */
712
- const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule) => {
730
+ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule, helperModuleFromSubpath) => {
713
731
  const sourceFile = call.getSourceFile();
714
732
  let host;
733
+ let subpathModule;
715
734
  for (const declaration of sourceFile.getImportDeclarations()) {
716
735
  const mod = declaration.getModuleSpecifierValue();
717
736
  if (declaration.isTypeOnly()) continue;
@@ -724,8 +743,10 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
724
743
  }
725
744
  }
726
745
  if (!host && isGeneratedCssModule(mod) && declaration.getNamedImports().length > 0) host = declaration;
746
+ if (!subpathModule) subpathModule = helperModuleFromSubpath?.(mod);
727
747
  }
728
- if (!host && !newImportModule) return void 0;
748
+ const fallbackModule = newImportModule ?? subpathModule;
749
+ if (!host && !fallbackModule) return void 0;
729
750
  if (declaredAtModuleScope(sourceFile).has(imported)) return void 0;
730
751
  if (isShadowed(call, imported)) return void 0;
731
752
  if (!host) {
@@ -736,7 +757,7 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
736
757
  insert: {
737
758
  pos: anchor.getEnd(),
738
759
  names: [imported],
739
- module: newImportModule
760
+ module: fallbackModule
740
761
  }
741
762
  };
742
763
  }
@@ -1512,6 +1533,32 @@ const foldSource = (options) => {
1512
1533
  const isBambooCssModule = (mod) => matchesModule(mod, cssModules);
1513
1534
  const isGeneratedCssModule = (mod) => matchesModule(mod, [generatedCssModule]);
1514
1535
  /**
1536
+ * Where the compiler's helpers can be imported from, for a file that imports the generated
1537
+ * css module by *subpath* rather than through its barrel.
1538
+ *
1539
+ * `styled-system/css/cva.js` is a real spelling, and it cannot host the helper: that module
1540
+ * exports `cva`, not `cvaMap`. Matching only the barrel meant no host was found and every
1541
+ * runtime recipe selection in the file declined — a failure whose reported reason said
1542
+ * nothing about import spelling, and whose suggested remedies all pointed elsewhere.
1543
+ *
1544
+ * The sibling `cx` module is what actually exports them. The prefix is verified against the
1545
+ * configured output before anything is derived, so an unrelated `foo/css/bar.js` is left
1546
+ * alone, and the caller's extension is preserved rather than guessed at.
1547
+ */
1548
+ const helperModuleFromSubpath = (mod) => {
1549
+ const normalized = mod.replaceAll("\\", "/");
1550
+ const at = normalized.lastIndexOf("/css/");
1551
+ if (at < 0) return void 0;
1552
+ const prefix = normalized.slice(0, at + 5 - 1);
1553
+ if (!isGeneratedCssModule(prefix)) return void 0;
1554
+ const rest = normalized.slice(at + 5);
1555
+ if (!rest || rest.includes("/")) return void 0;
1556
+ const dot = rest.lastIndexOf(".");
1557
+ const extension = dot > 0 ? rest.slice(dot) : "";
1558
+ if (rest === `cx${extension}`) return void 0;
1559
+ return `${prefix}/cx${extension}`;
1560
+ };
1561
+ /**
1515
1562
  * The generated css entry as spelled beside an imported config recipe.
1516
1563
  *
1517
1564
  * A decision table needs only `cvaMap`, but a module importing a config recipe often has
@@ -1822,7 +1869,7 @@ const foldSource = (options) => {
1822
1869
  }
1823
1870
  const lowered = lowerRecipeCall(call, entry, styleCompiler, isInertExpression, resolvedSelection, inlineSlot, maxRecipeStates);
1824
1871
  if (lowered.kind === "dynamic-style") {
1825
- const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name));
1872
+ const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath);
1826
1873
  if (helper) {
1827
1874
  candidates.push({
1828
1875
  item,
@@ -1849,7 +1896,7 @@ const foldSource = (options) => {
1849
1896
  continue;
1850
1897
  }
1851
1898
  if (lowered.kind === "slots") {
1852
- const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name)) : void 0;
1899
+ const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath) : void 0;
1853
1900
  if (!lowered.helper || helper) {
1854
1901
  const replacement = lowered.helper && helper && helper.name !== lowered.helper ? lowered.expression.replaceAll(`${lowered.helper}(`, `${helper.name}(`) : lowered.expression;
1855
1902
  candidates.push({
@@ -2302,7 +2349,7 @@ const foldSource = (options) => {
2302
2349
  const end = call.getEnd();
2303
2350
  if (code.slice(start, end) !== call.getText()) continue;
2304
2351
  if (collides([[start, end]])) continue;
2305
- const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()));
2352
+ const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()), helperModuleFromSubpath);
2306
2353
  if (!helper) continue;
2307
2354
  const keys = Object.keys(entry.config.variants ?? {});
2308
2355
  magic.overwrite(start, end, `${helper.name}(${args[0].getText()}, ${JSON.stringify(keys)})`);
@@ -2659,7 +2706,7 @@ const formatSkipped = (id, skipped) => {
2659
2706
  * with no matching rule.
2660
2707
  */
2661
2708
  const bamboocss = (options = {}) => {
2662
- const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates } = options;
2709
+ const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates, renameCssAsset = true } = options;
2663
2710
  if (maxRecipeStates !== void 0 && (!Number.isSafeInteger(maxRecipeStates) || maxRecipeStates < 1)) throw new Error("bamboocss: `maxRecipeStates` must be a positive safe integer.");
2664
2711
  /** Totals across the build, for the summary. */
2665
2712
  const totals = {
@@ -2695,7 +2742,7 @@ const bamboocss = (options = {}) => {
2695
2742
  separator: "\n"
2696
2743
  });
2697
2744
  const threw = entries.some((entry) => entry.reason === "compile-failed");
2698
- return /* @__PURE__ */ new Error(`bamboocss: ${entries.length} call(s) could not be compiled.\n\n${detail}\n\n` + (threw ? "`compile-failed` is a module the compiler threw on — see the error logged for it above. Nothing was established about its calls either way.\n\n" : "") + "Bamboo emits no runtime styling fallback or recipe layer. Make the values finite and statically analyzable, move variation into declared recipe variants, or safelist intentional dynamic classes with `staticCss`.");
2745
+ return /* @__PURE__ */ new Error(`bamboocss: ${entries.length} call(s) could not be compiled.\n\n${detail}\n\n` + (threw ? "`compile-failed` is a module the compiler threw on — see the error logged for it above. Nothing was established about its calls either way.\n\n" : "") + (entries.some((entry) => entry.reason === "runtime-binding") ? "`runtime-binding` is a Bamboo value read outside a call the compiler rewrote — most often an inline `cva`/`sva` imported by another module. Its declaration is erased where it is declared, so the import receives `undefined`; a recipe used in more than one file has to be a config recipe under `theme.extend.recipes`. The location given is the reference to change, not the declaration.\n\n" : "") + "Bamboo emits no runtime styling fallback or recipe layer. Make the values finite and statically analyzable, move variation into declared recipe variants, or safelist intentional dynamic classes with `staticCss`.\n\nSet `BAMBOO_DIAGNOSTIC_LIMIT=all` to list every finding rather than the first few.");
2699
2746
  };
2700
2747
  /**
2701
2748
  * Recipe configs read out of modules other than the one being transformed.
@@ -2724,7 +2771,8 @@ const bamboocss = (options = {}) => {
2724
2771
  return [bamboocssCss({
2725
2772
  configPath,
2726
2773
  cwd,
2727
- session: staticSession
2774
+ session: staticSession,
2775
+ renameCssAsset
2728
2776
  }), {
2729
2777
  name: "bamboocss:compiler",
2730
2778
  enforce: "pre",
package/dist/index.d.cts CHANGED
@@ -45,6 +45,24 @@ interface BambooVitePluginOptions {
45
45
  * build time and memory for the exact compound-variant decision table. @default 65536
46
46
  */
47
47
  maxRecipeStates?: number;
48
+ /**
49
+ * Give the pruned stylesheet a final name derived from its own bytes.
50
+ *
51
+ * Rollup expands `[hash]` before `generateBundle`, so pruning after it can leave two
52
+ * different reachable subsets under one CDN key. Renaming closes that, at the cost of
53
+ * replacing an entry in the output bundle — which not every consumer of the bundle
54
+ * tolerates. Rolldown drops the asset outright (detected automatically, no flag needed),
55
+ * and a framework that relocates assets itself, such as react-router's SSR build, can
56
+ * lose track of the new name.
57
+ *
58
+ * Turning it off keeps the pruned bytes and Vite's own content hash. That hash is computed
59
+ * before pruning, so it stops describing the file exactly; in practice it still changes
60
+ * whenever the extracted CSS does, and only a change that alters *reachability alone*
61
+ * can now reuse a key. Prefer that over an asset your framework cannot find.
62
+ *
63
+ * @default true
64
+ */
65
+ renameCssAsset?: boolean;
48
66
  }
49
67
  /**
50
68
  * Vite integration for Bamboo CSS.
package/dist/index.d.mts CHANGED
@@ -45,6 +45,24 @@ interface BambooVitePluginOptions {
45
45
  * build time and memory for the exact compound-variant decision table. @default 65536
46
46
  */
47
47
  maxRecipeStates?: number;
48
+ /**
49
+ * Give the pruned stylesheet a final name derived from its own bytes.
50
+ *
51
+ * Rollup expands `[hash]` before `generateBundle`, so pruning after it can leave two
52
+ * different reachable subsets under one CDN key. Renaming closes that, at the cost of
53
+ * replacing an entry in the output bundle — which not every consumer of the bundle
54
+ * tolerates. Rolldown drops the asset outright (detected automatically, no flag needed),
55
+ * and a framework that relocates assets itself, such as react-router's SSR build, can
56
+ * lose track of the new name.
57
+ *
58
+ * Turning it off keeps the pruned bytes and Vite's own content hash. That hash is computed
59
+ * before pruning, so it stops describing the file exactly; in practice it still changes
60
+ * whenever the extracted CSS does, and only a change that alters *reachability alone*
61
+ * can now reuse a key. Prefer that over an asset your framework cannot find.
62
+ *
63
+ * @default true
64
+ */
65
+ renameCssAsset?: boolean;
48
66
  }
49
67
  /**
50
68
  * Vite integration for Bamboo CSS.
package/dist/index.mjs CHANGED
@@ -140,20 +140,32 @@ const replaceAssetReferences = (bundle, previous, next, sourcemap) => {
140
140
  }
141
141
  };
142
142
  /**
143
+ * Could this bundle entry be the generated stylesheet?
144
+ *
145
+ * The filename is checked before the bytes because the alternative decodes every asset in the
146
+ * bundle to a UTF-8 string in order to search it — fonts, images and sourcemaps included. On an
147
+ * app with a large asset graph that is seconds of decode and a lot of garbage, twice over, to
148
+ * answer a question the extension already answers. The marker is a CSS custom property, so it
149
+ * cannot occur anywhere but CSS.
150
+ */
151
+ const carriesGeneratedCss = (output) => output.type === "asset" && output.fileName.endsWith(".css");
152
+ /**
143
153
  * Prune and rename compiler-owned CSS, then give changed assets a hash of their final bytes.
144
154
  *
145
155
  * Rollup has already expanded `[hash]` when `generateBundle` runs. Mutating only `source`
146
156
  * would therefore leave two different reachable subsets under one CDN key. The extra final
147
157
  * hash is not cosmetic: it makes late graph reachability cache-safe.
148
158
  */
149
- const optimizeStaticCssAssets = (bundle, session) => {
159
+ const optimizeStaticCssAssets = (bundle, session, options = {}) => {
160
+ const { rename = true } = options;
150
161
  for (const [bundleName, output] of Object.entries(bundle)) {
151
- if (output.type !== "asset") continue;
162
+ if (!carriesGeneratedCss(output)) continue;
152
163
  const source = typeof output.source === "string" ? output.source : Buffer.from(output.source).toString();
153
164
  if (!source.includes("--made-with-bamboo")) continue;
154
165
  const optimized = pruneStaticCss(source, session);
155
166
  output.source = optimized;
156
167
  if (optimized === source) continue;
168
+ if (!rename) continue;
157
169
  const nextName = output.fileName.replace(/\.css$/, `.b-${toHash(optimized)}.css`);
158
170
  if (nextName === output.fileName) continue;
159
171
  if (bundle[nextName] && bundle[nextName] !== output) throw new Error(`bamboocss: final CSS asset name collision at ${JSON.stringify(nextName)}.`);
@@ -178,7 +190,7 @@ const optimizeStaticCssAssets = (bundle, session) => {
178
190
  * process just wrote, which is a race on any watch rebuild.
179
191
  */
180
192
  const bamboocssCss = (options) => {
181
- const { configPath, cwd, session } = options;
193
+ const { configPath, cwd, session, renameCssAsset = true } = options;
182
194
  const builder = new Builder();
183
195
  let server;
184
196
  let command = "build";
@@ -262,7 +274,13 @@ const bamboocssCss = (options) => {
262
274
  generateBundle: {
263
275
  order: "post",
264
276
  handler(_, bundle) {
265
- optimizeStaticCssAssets(bundle, session);
277
+ const rolldown = Boolean(this.meta?.rolldownVersion);
278
+ optimizeStaticCssAssets(bundle, session, { rename: renameCssAsset && !rolldown });
279
+ if (!session.transformedFiles.size) return;
280
+ if (!Object.values(bundle).some((output) => {
281
+ if (!carriesGeneratedCss(output)) return false;
282
+ return (typeof output.source === "string" ? output.source : Buffer.from(output.source).toString()).includes("--made-with-bamboo");
283
+ })) throw new Error(`bamboocss: ${session.transformedFiles.size} module(s) were compiled to Bamboo class values, but no emitted asset carries the generated stylesheet. The build would ship unstyled.\n\nThis happens when another plugin, or the bundler itself, drops or replaces the CSS asset after it is emitted. If you are on Rolldown, report this — the rename that used to cause it is already disabled there. Otherwise look for a plugin running in \`generateBundle\` that rewrites CSS assets.`);
266
284
  }
267
285
  }
268
286
  };
@@ -679,9 +697,10 @@ const propertyKey = (nameNode) => {
679
697
  * there is nothing to match — the host here is any import of the generated css module, which
680
698
  * a file defining a recipe necessarily has, since `cva` came from it.
681
699
  */
682
- const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule) => {
700
+ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule, helperModuleFromSubpath) => {
683
701
  const sourceFile = call.getSourceFile();
684
702
  let host;
703
+ let subpathModule;
685
704
  for (const declaration of sourceFile.getImportDeclarations()) {
686
705
  const mod = declaration.getModuleSpecifierValue();
687
706
  if (declaration.isTypeOnly()) continue;
@@ -694,8 +713,10 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
694
713
  }
695
714
  }
696
715
  if (!host && isGeneratedCssModule(mod) && declaration.getNamedImports().length > 0) host = declaration;
716
+ if (!subpathModule) subpathModule = helperModuleFromSubpath?.(mod);
697
717
  }
698
- if (!host && !newImportModule) return void 0;
718
+ const fallbackModule = newImportModule ?? subpathModule;
719
+ if (!host && !fallbackModule) return void 0;
699
720
  if (declaredAtModuleScope(sourceFile).has(imported)) return void 0;
700
721
  if (isShadowed(call, imported)) return void 0;
701
722
  if (!host) {
@@ -706,7 +727,7 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
706
727
  insert: {
707
728
  pos: anchor.getEnd(),
708
729
  names: [imported],
709
- module: newImportModule
730
+ module: fallbackModule
710
731
  }
711
732
  };
712
733
  }
@@ -1482,6 +1503,32 @@ const foldSource = (options) => {
1482
1503
  const isBambooCssModule = (mod) => matchesModule(mod, cssModules);
1483
1504
  const isGeneratedCssModule = (mod) => matchesModule(mod, [generatedCssModule]);
1484
1505
  /**
1506
+ * Where the compiler's helpers can be imported from, for a file that imports the generated
1507
+ * css module by *subpath* rather than through its barrel.
1508
+ *
1509
+ * `styled-system/css/cva.js` is a real spelling, and it cannot host the helper: that module
1510
+ * exports `cva`, not `cvaMap`. Matching only the barrel meant no host was found and every
1511
+ * runtime recipe selection in the file declined — a failure whose reported reason said
1512
+ * nothing about import spelling, and whose suggested remedies all pointed elsewhere.
1513
+ *
1514
+ * The sibling `cx` module is what actually exports them. The prefix is verified against the
1515
+ * configured output before anything is derived, so an unrelated `foo/css/bar.js` is left
1516
+ * alone, and the caller's extension is preserved rather than guessed at.
1517
+ */
1518
+ const helperModuleFromSubpath = (mod) => {
1519
+ const normalized = mod.replaceAll("\\", "/");
1520
+ const at = normalized.lastIndexOf("/css/");
1521
+ if (at < 0) return void 0;
1522
+ const prefix = normalized.slice(0, at + 5 - 1);
1523
+ if (!isGeneratedCssModule(prefix)) return void 0;
1524
+ const rest = normalized.slice(at + 5);
1525
+ if (!rest || rest.includes("/")) return void 0;
1526
+ const dot = rest.lastIndexOf(".");
1527
+ const extension = dot > 0 ? rest.slice(dot) : "";
1528
+ if (rest === `cx${extension}`) return void 0;
1529
+ return `${prefix}/cx${extension}`;
1530
+ };
1531
+ /**
1485
1532
  * The generated css entry as spelled beside an imported config recipe.
1486
1533
  *
1487
1534
  * A decision table needs only `cvaMap`, but a module importing a config recipe often has
@@ -1792,7 +1839,7 @@ const foldSource = (options) => {
1792
1839
  }
1793
1840
  const lowered = lowerRecipeCall(call, entry, styleCompiler, isInertExpression, resolvedSelection, inlineSlot, maxRecipeStates);
1794
1841
  if (lowered.kind === "dynamic-style") {
1795
- const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name));
1842
+ const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath);
1796
1843
  if (helper) {
1797
1844
  candidates.push({
1798
1845
  item,
@@ -1819,7 +1866,7 @@ const foldSource = (options) => {
1819
1866
  continue;
1820
1867
  }
1821
1868
  if (lowered.kind === "slots") {
1822
- const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name)) : void 0;
1869
+ const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath) : void 0;
1823
1870
  if (!lowered.helper || helper) {
1824
1871
  const replacement = lowered.helper && helper && helper.name !== lowered.helper ? lowered.expression.replaceAll(`${lowered.helper}(`, `${helper.name}(`) : lowered.expression;
1825
1872
  candidates.push({
@@ -2272,7 +2319,7 @@ const foldSource = (options) => {
2272
2319
  const end = call.getEnd();
2273
2320
  if (code.slice(start, end) !== call.getText()) continue;
2274
2321
  if (collides([[start, end]])) continue;
2275
- const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()));
2322
+ const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()), helperModuleFromSubpath);
2276
2323
  if (!helper) continue;
2277
2324
  const keys = Object.keys(entry.config.variants ?? {});
2278
2325
  magic.overwrite(start, end, `${helper.name}(${args[0].getText()}, ${JSON.stringify(keys)})`);
@@ -2629,7 +2676,7 @@ const formatSkipped = (id, skipped) => {
2629
2676
  * with no matching rule.
2630
2677
  */
2631
2678
  const bamboocss = (options = {}) => {
2632
- const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates } = options;
2679
+ const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates, renameCssAsset = true } = options;
2633
2680
  if (maxRecipeStates !== void 0 && (!Number.isSafeInteger(maxRecipeStates) || maxRecipeStates < 1)) throw new Error("bamboocss: `maxRecipeStates` must be a positive safe integer.");
2634
2681
  /** Totals across the build, for the summary. */
2635
2682
  const totals = {
@@ -2665,7 +2712,7 @@ const bamboocss = (options = {}) => {
2665
2712
  separator: "\n"
2666
2713
  });
2667
2714
  const threw = entries.some((entry) => entry.reason === "compile-failed");
2668
- return /* @__PURE__ */ new Error(`bamboocss: ${entries.length} call(s) could not be compiled.\n\n${detail}\n\n` + (threw ? "`compile-failed` is a module the compiler threw on — see the error logged for it above. Nothing was established about its calls either way.\n\n" : "") + "Bamboo emits no runtime styling fallback or recipe layer. Make the values finite and statically analyzable, move variation into declared recipe variants, or safelist intentional dynamic classes with `staticCss`.");
2715
+ return /* @__PURE__ */ new Error(`bamboocss: ${entries.length} call(s) could not be compiled.\n\n${detail}\n\n` + (threw ? "`compile-failed` is a module the compiler threw on — see the error logged for it above. Nothing was established about its calls either way.\n\n" : "") + (entries.some((entry) => entry.reason === "runtime-binding") ? "`runtime-binding` is a Bamboo value read outside a call the compiler rewrote — most often an inline `cva`/`sva` imported by another module. Its declaration is erased where it is declared, so the import receives `undefined`; a recipe used in more than one file has to be a config recipe under `theme.extend.recipes`. The location given is the reference to change, not the declaration.\n\n" : "") + "Bamboo emits no runtime styling fallback or recipe layer. Make the values finite and statically analyzable, move variation into declared recipe variants, or safelist intentional dynamic classes with `staticCss`.\n\nSet `BAMBOO_DIAGNOSTIC_LIMIT=all` to list every finding rather than the first few.");
2669
2716
  };
2670
2717
  /**
2671
2718
  * Recipe configs read out of modules other than the one being transformed.
@@ -2694,7 +2741,8 @@ const bamboocss = (options = {}) => {
2694
2741
  return [bamboocssCss({
2695
2742
  configPath,
2696
2743
  cwd,
2697
- session: staticSession
2744
+ session: staticSession,
2745
+ renameCssAsset
2698
2746
  }), {
2699
2747
  name: "bamboocss:compiler",
2700
2748
  enforce: "pre",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bamboocss/vite",
3
- "version": "1.35.2",
3
+ "version": "1.35.4",
4
4
  "description": "Vite integration for Bamboo CSS",
5
5
  "homepage": "https://bamboocss.com",
6
6
  "license": "MIT",
@@ -40,18 +40,18 @@
40
40
  "postcss": "8.5.26",
41
41
  "postcss-selector-parser": "7.1.5",
42
42
  "ts-morph": "28.0.0",
43
- "@bamboocss/config": "1.35.2",
44
- "@bamboocss/core": "1.35.2",
45
- "@bamboocss/node": "1.35.2",
46
- "@bamboocss/extractor": "1.35.2",
47
- "@bamboocss/logger": "1.35.2",
48
- "@bamboocss/shared": "1.35.2",
49
- "@bamboocss/types": "1.35.2"
43
+ "@bamboocss/config": "1.35.4",
44
+ "@bamboocss/core": "1.35.4",
45
+ "@bamboocss/extractor": "1.35.4",
46
+ "@bamboocss/logger": "1.35.4",
47
+ "@bamboocss/node": "1.35.4",
48
+ "@bamboocss/types": "1.35.4",
49
+ "@bamboocss/shared": "1.35.4"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@jridgewell/trace-mapping": "^0.3.31",
53
53
  "vite": "7.2.6",
54
- "@bamboocss/fixture": "1.35.2"
54
+ "@bamboocss/fixture": "1.35.4"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "vite": ">=5"