@bamboocss/vite 1.35.1 → 1.35.3
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 +65 -16
- package/dist/index.d.cts +18 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +65 -16
- package/package.json +9 -9
package/dist/index.cjs
CHANGED
|
@@ -176,7 +176,8 @@ const replaceAssetReferences = (bundle, previous, next, sourcemap) => {
|
|
|
176
176
|
* would therefore leave two different reachable subsets under one CDN key. The extra final
|
|
177
177
|
* hash is not cosmetic: it makes late graph reachability cache-safe.
|
|
178
178
|
*/
|
|
179
|
-
const optimizeStaticCssAssets = (bundle, session) => {
|
|
179
|
+
const optimizeStaticCssAssets = (bundle, session, options = {}) => {
|
|
180
|
+
const { rename = true } = options;
|
|
180
181
|
for (const [bundleName, output] of Object.entries(bundle)) {
|
|
181
182
|
if (output.type !== "asset") continue;
|
|
182
183
|
const source = typeof output.source === "string" ? output.source : Buffer.from(output.source).toString();
|
|
@@ -184,6 +185,7 @@ const optimizeStaticCssAssets = (bundle, session) => {
|
|
|
184
185
|
const optimized = pruneStaticCss(source, session);
|
|
185
186
|
output.source = optimized;
|
|
186
187
|
if (optimized === source) continue;
|
|
188
|
+
if (!rename) continue;
|
|
187
189
|
const nextName = output.fileName.replace(/\.css$/, `.b-${(0, _bamboocss_shared.toHash)(optimized)}.css`);
|
|
188
190
|
if (nextName === output.fileName) continue;
|
|
189
191
|
if (bundle[nextName] && bundle[nextName] !== output) throw new Error(`bamboocss: final CSS asset name collision at ${JSON.stringify(nextName)}.`);
|
|
@@ -208,7 +210,7 @@ const optimizeStaticCssAssets = (bundle, session) => {
|
|
|
208
210
|
* process just wrote, which is a race on any watch rebuild.
|
|
209
211
|
*/
|
|
210
212
|
const bamboocssCss = (options) => {
|
|
211
|
-
const { configPath, cwd, session } = options;
|
|
213
|
+
const { configPath, cwd, session, renameCssAsset = true } = options;
|
|
212
214
|
const builder = new _bamboocss_node.Builder();
|
|
213
215
|
let server;
|
|
214
216
|
let command = "build";
|
|
@@ -292,7 +294,13 @@ const bamboocssCss = (options) => {
|
|
|
292
294
|
generateBundle: {
|
|
293
295
|
order: "post",
|
|
294
296
|
handler(_, bundle) {
|
|
295
|
-
|
|
297
|
+
const rolldown = Boolean(this.meta?.rolldownVersion);
|
|
298
|
+
optimizeStaticCssAssets(bundle, session, { rename: renameCssAsset && !rolldown });
|
|
299
|
+
if (!session.transformedFiles.size) return;
|
|
300
|
+
if (!Object.values(bundle).some((output) => {
|
|
301
|
+
if (output.type !== "asset") return false;
|
|
302
|
+
return (typeof output.source === "string" ? output.source : Buffer.from(output.source).toString()).includes("--made-with-bamboo");
|
|
303
|
+
})) 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
304
|
}
|
|
297
305
|
}
|
|
298
306
|
};
|
|
@@ -709,9 +717,10 @@ const propertyKey = (nameNode) => {
|
|
|
709
717
|
* there is nothing to match — the host here is any import of the generated css module, which
|
|
710
718
|
* a file defining a recipe necessarily has, since `cva` came from it.
|
|
711
719
|
*/
|
|
712
|
-
const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule) => {
|
|
720
|
+
const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule, helperModuleFromSubpath) => {
|
|
713
721
|
const sourceFile = call.getSourceFile();
|
|
714
722
|
let host;
|
|
723
|
+
let subpathModule;
|
|
715
724
|
for (const declaration of sourceFile.getImportDeclarations()) {
|
|
716
725
|
const mod = declaration.getModuleSpecifierValue();
|
|
717
726
|
if (declaration.isTypeOnly()) continue;
|
|
@@ -724,8 +733,10 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
|
|
|
724
733
|
}
|
|
725
734
|
}
|
|
726
735
|
if (!host && isGeneratedCssModule(mod) && declaration.getNamedImports().length > 0) host = declaration;
|
|
736
|
+
if (!subpathModule) subpathModule = helperModuleFromSubpath?.(mod);
|
|
727
737
|
}
|
|
728
|
-
|
|
738
|
+
const fallbackModule = newImportModule ?? subpathModule;
|
|
739
|
+
if (!host && !fallbackModule) return void 0;
|
|
729
740
|
if (declaredAtModuleScope(sourceFile).has(imported)) return void 0;
|
|
730
741
|
if (isShadowed(call, imported)) return void 0;
|
|
731
742
|
if (!host) {
|
|
@@ -736,7 +747,7 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
|
|
|
736
747
|
insert: {
|
|
737
748
|
pos: anchor.getEnd(),
|
|
738
749
|
names: [imported],
|
|
739
|
-
module:
|
|
750
|
+
module: fallbackModule
|
|
740
751
|
}
|
|
741
752
|
};
|
|
742
753
|
}
|
|
@@ -1512,6 +1523,32 @@ const foldSource = (options) => {
|
|
|
1512
1523
|
const isBambooCssModule = (mod) => matchesModule(mod, cssModules);
|
|
1513
1524
|
const isGeneratedCssModule = (mod) => matchesModule(mod, [generatedCssModule]);
|
|
1514
1525
|
/**
|
|
1526
|
+
* Where the compiler's helpers can be imported from, for a file that imports the generated
|
|
1527
|
+
* css module by *subpath* rather than through its barrel.
|
|
1528
|
+
*
|
|
1529
|
+
* `styled-system/css/cva.js` is a real spelling, and it cannot host the helper: that module
|
|
1530
|
+
* exports `cva`, not `cvaMap`. Matching only the barrel meant no host was found and every
|
|
1531
|
+
* runtime recipe selection in the file declined — a failure whose reported reason said
|
|
1532
|
+
* nothing about import spelling, and whose suggested remedies all pointed elsewhere.
|
|
1533
|
+
*
|
|
1534
|
+
* The sibling `cx` module is what actually exports them. The prefix is verified against the
|
|
1535
|
+
* configured output before anything is derived, so an unrelated `foo/css/bar.js` is left
|
|
1536
|
+
* alone, and the caller's extension is preserved rather than guessed at.
|
|
1537
|
+
*/
|
|
1538
|
+
const helperModuleFromSubpath = (mod) => {
|
|
1539
|
+
const normalized = mod.replaceAll("\\", "/");
|
|
1540
|
+
const at = normalized.lastIndexOf("/css/");
|
|
1541
|
+
if (at < 0) return void 0;
|
|
1542
|
+
const prefix = normalized.slice(0, at + 5 - 1);
|
|
1543
|
+
if (!isGeneratedCssModule(prefix)) return void 0;
|
|
1544
|
+
const rest = normalized.slice(at + 5);
|
|
1545
|
+
if (!rest || rest.includes("/")) return void 0;
|
|
1546
|
+
const dot = rest.lastIndexOf(".");
|
|
1547
|
+
const extension = dot > 0 ? rest.slice(dot) : "";
|
|
1548
|
+
if (rest === `cx${extension}`) return void 0;
|
|
1549
|
+
return `${prefix}/cx${extension}`;
|
|
1550
|
+
};
|
|
1551
|
+
/**
|
|
1515
1552
|
* The generated css entry as spelled beside an imported config recipe.
|
|
1516
1553
|
*
|
|
1517
1554
|
* A decision table needs only `cvaMap`, but a module importing a config recipe often has
|
|
@@ -1822,7 +1859,7 @@ const foldSource = (options) => {
|
|
|
1822
1859
|
}
|
|
1823
1860
|
const lowered = lowerRecipeCall(call, entry, styleCompiler, isInertExpression, resolvedSelection, inlineSlot, maxRecipeStates);
|
|
1824
1861
|
if (lowered.kind === "dynamic-style") {
|
|
1825
|
-
const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name));
|
|
1862
|
+
const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath);
|
|
1826
1863
|
if (helper) {
|
|
1827
1864
|
candidates.push({
|
|
1828
1865
|
item,
|
|
@@ -1849,7 +1886,7 @@ const foldSource = (options) => {
|
|
|
1849
1886
|
continue;
|
|
1850
1887
|
}
|
|
1851
1888
|
if (lowered.kind === "slots") {
|
|
1852
|
-
const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name)) : void 0;
|
|
1889
|
+
const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath) : void 0;
|
|
1853
1890
|
if (!lowered.helper || helper) {
|
|
1854
1891
|
const replacement = lowered.helper && helper && helper.name !== lowered.helper ? lowered.expression.replaceAll(`${lowered.helper}(`, `${helper.name}(`) : lowered.expression;
|
|
1855
1892
|
candidates.push({
|
|
@@ -2302,7 +2339,7 @@ const foldSource = (options) => {
|
|
|
2302
2339
|
const end = call.getEnd();
|
|
2303
2340
|
if (code.slice(start, end) !== call.getText()) continue;
|
|
2304
2341
|
if (collides([[start, end]])) continue;
|
|
2305
|
-
const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()));
|
|
2342
|
+
const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()), helperModuleFromSubpath);
|
|
2306
2343
|
if (!helper) continue;
|
|
2307
2344
|
const keys = Object.keys(entry.config.variants ?? {});
|
|
2308
2345
|
magic.overwrite(start, end, `${helper.name}(${args[0].getText()}, ${JSON.stringify(keys)})`);
|
|
@@ -2348,11 +2385,17 @@ const foldSource = (options) => {
|
|
|
2348
2385
|
if (skipped.filter((item) => SURVIVES_TO_RUNTIME.has(item.reason) && item.end > item.start).some((item) => nameNode.findReferencesAsNodes().some((ref) => ref.getStart() >= item.start && ref.getStart() < item.end))) continue;
|
|
2349
2386
|
const survivor = nameNode.findReferencesAsNodes().find((ref) => !applied.some(([from, to]) => ref.getStart() >= from && ref.getStart() < to));
|
|
2350
2387
|
if (!survivor) continue;
|
|
2388
|
+
const survivorFile = survivor.getSourceFile();
|
|
2389
|
+
const foreign = survivorFile !== sourceFile;
|
|
2351
2390
|
skipped.push({
|
|
2352
2391
|
name: binding,
|
|
2353
2392
|
reason: "runtime-binding",
|
|
2354
|
-
start: survivor.getStart(),
|
|
2355
|
-
end: survivor.getEnd()
|
|
2393
|
+
start: foreign ? nameNode.getStart() : survivor.getStart(),
|
|
2394
|
+
end: foreign ? nameNode.getEnd() : survivor.getEnd(),
|
|
2395
|
+
...foreign ? { origin: {
|
|
2396
|
+
filePath: survivorFile.getFilePath(),
|
|
2397
|
+
line: survivor.getStartLineNumber()
|
|
2398
|
+
} } : {}
|
|
2356
2399
|
});
|
|
2357
2400
|
}
|
|
2358
2401
|
const bambooModules = [
|
|
@@ -2653,7 +2696,7 @@ const formatSkipped = (id, skipped) => {
|
|
|
2653
2696
|
* with no matching rule.
|
|
2654
2697
|
*/
|
|
2655
2698
|
const bamboocss = (options = {}) => {
|
|
2656
|
-
const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates } = options;
|
|
2699
|
+
const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates, renameCssAsset = true } = options;
|
|
2657
2700
|
if (maxRecipeStates !== void 0 && (!Number.isSafeInteger(maxRecipeStates) || maxRecipeStates < 1)) throw new Error("bamboocss: `maxRecipeStates` must be a positive safe integer.");
|
|
2658
2701
|
/** Totals across the build, for the summary. */
|
|
2659
2702
|
const totals = {
|
|
@@ -2689,7 +2732,7 @@ const bamboocss = (options = {}) => {
|
|
|
2689
2732
|
separator: "\n"
|
|
2690
2733
|
});
|
|
2691
2734
|
const threw = entries.some((entry) => entry.reason === "compile-failed");
|
|
2692
|
-
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
|
|
2735
|
+
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.");
|
|
2693
2736
|
};
|
|
2694
2737
|
/**
|
|
2695
2738
|
* Recipe configs read out of modules other than the one being transformed.
|
|
@@ -2718,7 +2761,8 @@ const bamboocss = (options = {}) => {
|
|
|
2718
2761
|
return [bamboocssCss({
|
|
2719
2762
|
configPath,
|
|
2720
2763
|
cwd,
|
|
2721
|
-
session: staticSession
|
|
2764
|
+
session: staticSession,
|
|
2765
|
+
renameCssAsset
|
|
2722
2766
|
}), {
|
|
2723
2767
|
name: "bamboocss:compiler",
|
|
2724
2768
|
enforce: "pre",
|
|
@@ -2815,7 +2859,12 @@ const bamboocss = (options = {}) => {
|
|
|
2815
2859
|
for (const entry of result.skipped) {
|
|
2816
2860
|
if (entry.reason === "not-imported" || entry.reason === "overlapping") continue;
|
|
2817
2861
|
if (entry.name === "cx" && entry.reason === "dynamic") continue;
|
|
2818
|
-
addSurvivor({
|
|
2862
|
+
addSurvivor(entry.origin ? {
|
|
2863
|
+
file: entry.origin.filePath,
|
|
2864
|
+
line: entry.origin.line,
|
|
2865
|
+
name: entry.name,
|
|
2866
|
+
reason: entry.reason
|
|
2867
|
+
} : {
|
|
2819
2868
|
file: filePath,
|
|
2820
2869
|
line: lineAt(code, entry.start),
|
|
2821
2870
|
name: entry.name,
|
|
@@ -2835,7 +2884,7 @@ const bamboocss = (options = {}) => {
|
|
|
2835
2884
|
buildEnd() {
|
|
2836
2885
|
if (survivors.length) throw createSurvivorError(survivors);
|
|
2837
2886
|
if (typeof this.getModuleInfo === "function") {
|
|
2838
|
-
if (!staticSession.cssLoaded) throw new Error(`bamboocss: compiled class values were produced, but ${JSON.stringify(VIRTUAL_CSS_ID)} was not imported.
|
|
2887
|
+
if (!staticSession.cssLoaded) throw new Error(`bamboocss: compiled class values were produced, but ${JSON.stringify(VIRTUAL_CSS_ID)} was not imported. Add \`import ${JSON.stringify(VIRTUAL_CSS_ID)}\` once, from a JavaScript or TypeScript module in the application entry graph.\n\nIt has to be a JS import. \`@import\` from a stylesheet does not reach it: the id names a virtual module resolved by this plugin, and Vite resolves CSS \`@import\` before plugin resolution, so it fails as an unresolvable path. A project that ships one preloaded stylesheet imports this from its entry module instead, and lets Vite emit the CSS asset.`);
|
|
2839
2888
|
const outsideExtraction = [...staticSession.transformedFiles].filter((file) => !staticSession.extractedFiles.has(file));
|
|
2840
2889
|
if (outsideExtraction.length) throw new Error(`bamboocss: ${outsideExtraction.length} statically compiled module(s) are outside the CSS extraction graph:\n\n${(0, _bamboocss_shared.truncateList)(outsideExtraction.map((file) => ` ${file}`), {
|
|
2841
2890
|
unit: "file",
|
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
|
@@ -146,7 +146,8 @@ const replaceAssetReferences = (bundle, previous, next, sourcemap) => {
|
|
|
146
146
|
* would therefore leave two different reachable subsets under one CDN key. The extra final
|
|
147
147
|
* hash is not cosmetic: it makes late graph reachability cache-safe.
|
|
148
148
|
*/
|
|
149
|
-
const optimizeStaticCssAssets = (bundle, session) => {
|
|
149
|
+
const optimizeStaticCssAssets = (bundle, session, options = {}) => {
|
|
150
|
+
const { rename = true } = options;
|
|
150
151
|
for (const [bundleName, output] of Object.entries(bundle)) {
|
|
151
152
|
if (output.type !== "asset") continue;
|
|
152
153
|
const source = typeof output.source === "string" ? output.source : Buffer.from(output.source).toString();
|
|
@@ -154,6 +155,7 @@ const optimizeStaticCssAssets = (bundle, session) => {
|
|
|
154
155
|
const optimized = pruneStaticCss(source, session);
|
|
155
156
|
output.source = optimized;
|
|
156
157
|
if (optimized === source) continue;
|
|
158
|
+
if (!rename) continue;
|
|
157
159
|
const nextName = output.fileName.replace(/\.css$/, `.b-${toHash(optimized)}.css`);
|
|
158
160
|
if (nextName === output.fileName) continue;
|
|
159
161
|
if (bundle[nextName] && bundle[nextName] !== output) throw new Error(`bamboocss: final CSS asset name collision at ${JSON.stringify(nextName)}.`);
|
|
@@ -178,7 +180,7 @@ const optimizeStaticCssAssets = (bundle, session) => {
|
|
|
178
180
|
* process just wrote, which is a race on any watch rebuild.
|
|
179
181
|
*/
|
|
180
182
|
const bamboocssCss = (options) => {
|
|
181
|
-
const { configPath, cwd, session } = options;
|
|
183
|
+
const { configPath, cwd, session, renameCssAsset = true } = options;
|
|
182
184
|
const builder = new Builder();
|
|
183
185
|
let server;
|
|
184
186
|
let command = "build";
|
|
@@ -262,7 +264,13 @@ const bamboocssCss = (options) => {
|
|
|
262
264
|
generateBundle: {
|
|
263
265
|
order: "post",
|
|
264
266
|
handler(_, bundle) {
|
|
265
|
-
|
|
267
|
+
const rolldown = Boolean(this.meta?.rolldownVersion);
|
|
268
|
+
optimizeStaticCssAssets(bundle, session, { rename: renameCssAsset && !rolldown });
|
|
269
|
+
if (!session.transformedFiles.size) return;
|
|
270
|
+
if (!Object.values(bundle).some((output) => {
|
|
271
|
+
if (output.type !== "asset") return false;
|
|
272
|
+
return (typeof output.source === "string" ? output.source : Buffer.from(output.source).toString()).includes("--made-with-bamboo");
|
|
273
|
+
})) 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
274
|
}
|
|
267
275
|
}
|
|
268
276
|
};
|
|
@@ -679,9 +687,10 @@ const propertyKey = (nameNode) => {
|
|
|
679
687
|
* there is nothing to match — the host here is any import of the generated css module, which
|
|
680
688
|
* a file defining a recipe necessarily has, since `cva` came from it.
|
|
681
689
|
*/
|
|
682
|
-
const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule) => {
|
|
690
|
+
const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGeneratedCssModule, isShadowed, newImportModule, helperModuleFromSubpath) => {
|
|
683
691
|
const sourceFile = call.getSourceFile();
|
|
684
692
|
let host;
|
|
693
|
+
let subpathModule;
|
|
685
694
|
for (const declaration of sourceFile.getImportDeclarations()) {
|
|
686
695
|
const mod = declaration.getModuleSpecifierValue();
|
|
687
696
|
if (declaration.isTypeOnly()) continue;
|
|
@@ -694,8 +703,10 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
|
|
|
694
703
|
}
|
|
695
704
|
}
|
|
696
705
|
if (!host && isGeneratedCssModule(mod) && declaration.getNamedImports().length > 0) host = declaration;
|
|
706
|
+
if (!subpathModule) subpathModule = helperModuleFromSubpath?.(mod);
|
|
697
707
|
}
|
|
698
|
-
|
|
708
|
+
const fallbackModule = newImportModule ?? subpathModule;
|
|
709
|
+
if (!host && !fallbackModule) return void 0;
|
|
699
710
|
if (declaredAtModuleScope(sourceFile).has(imported)) return void 0;
|
|
700
711
|
if (isShadowed(call, imported)) return void 0;
|
|
701
712
|
if (!host) {
|
|
@@ -706,7 +717,7 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
|
|
|
706
717
|
insert: {
|
|
707
718
|
pos: anchor.getEnd(),
|
|
708
719
|
names: [imported],
|
|
709
|
-
module:
|
|
720
|
+
module: fallbackModule
|
|
710
721
|
}
|
|
711
722
|
};
|
|
712
723
|
}
|
|
@@ -1482,6 +1493,32 @@ const foldSource = (options) => {
|
|
|
1482
1493
|
const isBambooCssModule = (mod) => matchesModule(mod, cssModules);
|
|
1483
1494
|
const isGeneratedCssModule = (mod) => matchesModule(mod, [generatedCssModule]);
|
|
1484
1495
|
/**
|
|
1496
|
+
* Where the compiler's helpers can be imported from, for a file that imports the generated
|
|
1497
|
+
* css module by *subpath* rather than through its barrel.
|
|
1498
|
+
*
|
|
1499
|
+
* `styled-system/css/cva.js` is a real spelling, and it cannot host the helper: that module
|
|
1500
|
+
* exports `cva`, not `cvaMap`. Matching only the barrel meant no host was found and every
|
|
1501
|
+
* runtime recipe selection in the file declined — a failure whose reported reason said
|
|
1502
|
+
* nothing about import spelling, and whose suggested remedies all pointed elsewhere.
|
|
1503
|
+
*
|
|
1504
|
+
* The sibling `cx` module is what actually exports them. The prefix is verified against the
|
|
1505
|
+
* configured output before anything is derived, so an unrelated `foo/css/bar.js` is left
|
|
1506
|
+
* alone, and the caller's extension is preserved rather than guessed at.
|
|
1507
|
+
*/
|
|
1508
|
+
const helperModuleFromSubpath = (mod) => {
|
|
1509
|
+
const normalized = mod.replaceAll("\\", "/");
|
|
1510
|
+
const at = normalized.lastIndexOf("/css/");
|
|
1511
|
+
if (at < 0) return void 0;
|
|
1512
|
+
const prefix = normalized.slice(0, at + 5 - 1);
|
|
1513
|
+
if (!isGeneratedCssModule(prefix)) return void 0;
|
|
1514
|
+
const rest = normalized.slice(at + 5);
|
|
1515
|
+
if (!rest || rest.includes("/")) return void 0;
|
|
1516
|
+
const dot = rest.lastIndexOf(".");
|
|
1517
|
+
const extension = dot > 0 ? rest.slice(dot) : "";
|
|
1518
|
+
if (rest === `cx${extension}`) return void 0;
|
|
1519
|
+
return `${prefix}/cx${extension}`;
|
|
1520
|
+
};
|
|
1521
|
+
/**
|
|
1485
1522
|
* The generated css entry as spelled beside an imported config recipe.
|
|
1486
1523
|
*
|
|
1487
1524
|
* A decision table needs only `cvaMap`, but a module importing a config recipe often has
|
|
@@ -1792,7 +1829,7 @@ const foldSource = (options) => {
|
|
|
1792
1829
|
}
|
|
1793
1830
|
const lowered = lowerRecipeCall(call, entry, styleCompiler, isInertExpression, resolvedSelection, inlineSlot, maxRecipeStates);
|
|
1794
1831
|
if (lowered.kind === "dynamic-style") {
|
|
1795
|
-
const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name));
|
|
1832
|
+
const helper = ensureRecipeHelperImport(RECIPE_MAP_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath);
|
|
1796
1833
|
if (helper) {
|
|
1797
1834
|
candidates.push({
|
|
1798
1835
|
item,
|
|
@@ -1819,7 +1856,7 @@ const foldSource = (options) => {
|
|
|
1819
1856
|
continue;
|
|
1820
1857
|
}
|
|
1821
1858
|
if (lowered.kind === "slots") {
|
|
1822
|
-
const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name)) : void 0;
|
|
1859
|
+
const helper = lowered.helper ? ensureRecipeHelperImport(lowered.helper, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(name), helperModuleFromSubpath) : void 0;
|
|
1823
1860
|
if (!lowered.helper || helper) {
|
|
1824
1861
|
const replacement = lowered.helper && helper && helper.name !== lowered.helper ? lowered.expression.replaceAll(`${lowered.helper}(`, `${helper.name}(`) : lowered.expression;
|
|
1825
1862
|
candidates.push({
|
|
@@ -2272,7 +2309,7 @@ const foldSource = (options) => {
|
|
|
2272
2309
|
const end = call.getEnd();
|
|
2273
2310
|
if (code.slice(start, end) !== call.getText()) continue;
|
|
2274
2311
|
if (collides([[start, end]])) continue;
|
|
2275
|
-
const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()));
|
|
2312
|
+
const helper = ensureRecipeHelperImport(SPLIT_PROPS_HELPER, call, isBambooCssModule, isGeneratedCssModule, isShadowed, helperModules.get(target.getText()), helperModuleFromSubpath);
|
|
2276
2313
|
if (!helper) continue;
|
|
2277
2314
|
const keys = Object.keys(entry.config.variants ?? {});
|
|
2278
2315
|
magic.overwrite(start, end, `${helper.name}(${args[0].getText()}, ${JSON.stringify(keys)})`);
|
|
@@ -2318,11 +2355,17 @@ const foldSource = (options) => {
|
|
|
2318
2355
|
if (skipped.filter((item) => SURVIVES_TO_RUNTIME.has(item.reason) && item.end > item.start).some((item) => nameNode.findReferencesAsNodes().some((ref) => ref.getStart() >= item.start && ref.getStart() < item.end))) continue;
|
|
2319
2356
|
const survivor = nameNode.findReferencesAsNodes().find((ref) => !applied.some(([from, to]) => ref.getStart() >= from && ref.getStart() < to));
|
|
2320
2357
|
if (!survivor) continue;
|
|
2358
|
+
const survivorFile = survivor.getSourceFile();
|
|
2359
|
+
const foreign = survivorFile !== sourceFile;
|
|
2321
2360
|
skipped.push({
|
|
2322
2361
|
name: binding,
|
|
2323
2362
|
reason: "runtime-binding",
|
|
2324
|
-
start: survivor.getStart(),
|
|
2325
|
-
end: survivor.getEnd()
|
|
2363
|
+
start: foreign ? nameNode.getStart() : survivor.getStart(),
|
|
2364
|
+
end: foreign ? nameNode.getEnd() : survivor.getEnd(),
|
|
2365
|
+
...foreign ? { origin: {
|
|
2366
|
+
filePath: survivorFile.getFilePath(),
|
|
2367
|
+
line: survivor.getStartLineNumber()
|
|
2368
|
+
} } : {}
|
|
2326
2369
|
});
|
|
2327
2370
|
}
|
|
2328
2371
|
const bambooModules = [
|
|
@@ -2623,7 +2666,7 @@ const formatSkipped = (id, skipped) => {
|
|
|
2623
2666
|
* with no matching rule.
|
|
2624
2667
|
*/
|
|
2625
2668
|
const bamboocss = (options = {}) => {
|
|
2626
|
-
const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates } = options;
|
|
2669
|
+
const { configPath, cwd, reportSkipped = false, reportSummary = true, denseClassNames = true, maxRecipeStates, renameCssAsset = true } = options;
|
|
2627
2670
|
if (maxRecipeStates !== void 0 && (!Number.isSafeInteger(maxRecipeStates) || maxRecipeStates < 1)) throw new Error("bamboocss: `maxRecipeStates` must be a positive safe integer.");
|
|
2628
2671
|
/** Totals across the build, for the summary. */
|
|
2629
2672
|
const totals = {
|
|
@@ -2659,7 +2702,7 @@ const bamboocss = (options = {}) => {
|
|
|
2659
2702
|
separator: "\n"
|
|
2660
2703
|
});
|
|
2661
2704
|
const threw = entries.some((entry) => entry.reason === "compile-failed");
|
|
2662
|
-
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
|
|
2705
|
+
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.");
|
|
2663
2706
|
};
|
|
2664
2707
|
/**
|
|
2665
2708
|
* Recipe configs read out of modules other than the one being transformed.
|
|
@@ -2688,7 +2731,8 @@ const bamboocss = (options = {}) => {
|
|
|
2688
2731
|
return [bamboocssCss({
|
|
2689
2732
|
configPath,
|
|
2690
2733
|
cwd,
|
|
2691
|
-
session: staticSession
|
|
2734
|
+
session: staticSession,
|
|
2735
|
+
renameCssAsset
|
|
2692
2736
|
}), {
|
|
2693
2737
|
name: "bamboocss:compiler",
|
|
2694
2738
|
enforce: "pre",
|
|
@@ -2785,7 +2829,12 @@ const bamboocss = (options = {}) => {
|
|
|
2785
2829
|
for (const entry of result.skipped) {
|
|
2786
2830
|
if (entry.reason === "not-imported" || entry.reason === "overlapping") continue;
|
|
2787
2831
|
if (entry.name === "cx" && entry.reason === "dynamic") continue;
|
|
2788
|
-
addSurvivor({
|
|
2832
|
+
addSurvivor(entry.origin ? {
|
|
2833
|
+
file: entry.origin.filePath,
|
|
2834
|
+
line: entry.origin.line,
|
|
2835
|
+
name: entry.name,
|
|
2836
|
+
reason: entry.reason
|
|
2837
|
+
} : {
|
|
2789
2838
|
file: filePath,
|
|
2790
2839
|
line: lineAt(code, entry.start),
|
|
2791
2840
|
name: entry.name,
|
|
@@ -2805,7 +2854,7 @@ const bamboocss = (options = {}) => {
|
|
|
2805
2854
|
buildEnd() {
|
|
2806
2855
|
if (survivors.length) throw createSurvivorError(survivors);
|
|
2807
2856
|
if (typeof this.getModuleInfo === "function") {
|
|
2808
|
-
if (!staticSession.cssLoaded) throw new Error(`bamboocss: compiled class values were produced, but ${JSON.stringify(VIRTUAL_CSS_ID)} was not imported.
|
|
2857
|
+
if (!staticSession.cssLoaded) throw new Error(`bamboocss: compiled class values were produced, but ${JSON.stringify(VIRTUAL_CSS_ID)} was not imported. Add \`import ${JSON.stringify(VIRTUAL_CSS_ID)}\` once, from a JavaScript or TypeScript module in the application entry graph.\n\nIt has to be a JS import. \`@import\` from a stylesheet does not reach it: the id names a virtual module resolved by this plugin, and Vite resolves CSS \`@import\` before plugin resolution, so it fails as an unresolvable path. A project that ships one preloaded stylesheet imports this from its entry module instead, and lets Vite emit the CSS asset.`);
|
|
2809
2858
|
const outsideExtraction = [...staticSession.transformedFiles].filter((file) => !staticSession.extractedFiles.has(file));
|
|
2810
2859
|
if (outsideExtraction.length) throw new Error(`bamboocss: ${outsideExtraction.length} statically compiled module(s) are outside the CSS extraction graph:\n\n${truncateList(outsideExtraction.map((file) => ` ${file}`), {
|
|
2811
2860
|
unit: "file",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamboocss/vite",
|
|
3
|
-
"version": "1.35.
|
|
3
|
+
"version": "1.35.3",
|
|
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.
|
|
44
|
-
"@bamboocss/
|
|
45
|
-
"@bamboocss/
|
|
46
|
-
"@bamboocss/node": "1.35.
|
|
47
|
-
"@bamboocss/
|
|
48
|
-
"@bamboocss/shared": "1.35.
|
|
49
|
-
"@bamboocss/types": "1.35.
|
|
43
|
+
"@bamboocss/config": "1.35.3",
|
|
44
|
+
"@bamboocss/extractor": "1.35.3",
|
|
45
|
+
"@bamboocss/logger": "1.35.3",
|
|
46
|
+
"@bamboocss/node": "1.35.3",
|
|
47
|
+
"@bamboocss/core": "1.35.3",
|
|
48
|
+
"@bamboocss/shared": "1.35.3",
|
|
49
|
+
"@bamboocss/types": "1.35.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
53
53
|
"vite": "7.2.6",
|
|
54
|
-
"@bamboocss/fixture": "1.35.
|
|
54
|
+
"@bamboocss/fixture": "1.35.3"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"vite": ">=5"
|