@bamboocss/generator 1.28.1 → 1.30.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.cjs CHANGED
@@ -1373,15 +1373,35 @@ function generateSvaFn(ctx) {
1373
1373
  }
1374
1374
  //#endregion
1375
1375
  //#region src/artifacts/js/token.ts
1376
+ /**
1377
+ * `token()` hands back the variable reference for every token, and `token.value()` the
1378
+ * resolved literal.
1379
+ *
1380
+ * It used to be the other way round, decided per token: a base token resolved to its literal
1381
+ * and a virtual or conditional one to its `var()`. That made the return kind a property of
1382
+ * the *theme* rather than of the call, so adding `_dark` to a token silently changed what
1383
+ * every caller received — same call, same path, a colour before and a variable after, with
1384
+ * both typed `string` and nothing to catch it.
1385
+ *
1386
+ * Always-a-reference is the predictable half and the one that keeps working when a theme
1387
+ * switches, so it takes the short name. The literal is still reachable, but has to be asked
1388
+ * for — which is also the honest signal, since it is the form that stops responding to
1389
+ * conditions.
1390
+ *
1391
+ * `value` keeps the old per-token split rather than becoming `token.value` for everything:
1392
+ * a virtual or conditional token has no single literal to hand back, so its `var()` is still
1393
+ * the only truthful answer.
1394
+ */
1376
1395
  function generateTokenJs(ctx) {
1377
1396
  const { tokens } = ctx;
1378
1397
  const map = /* @__PURE__ */ new Map();
1379
1398
  tokens.allTokens.forEach((token) => {
1380
1399
  const { varRef, isVirtual } = token.extensions;
1381
- const value = isVirtual || token.extensions.condition !== "base" ? varRef : token.value;
1400
+ const variable = tokens.view.getVar(token.name) ?? varRef;
1401
+ const value = tokens.view.get(token.name) ?? (isVirtual || token.extensions.condition !== "base" ? varRef : token.value);
1382
1402
  map.set(token.name, {
1383
1403
  value,
1384
- variable: varRef
1404
+ variable
1385
1405
  });
1386
1406
  });
1387
1407
  const obj = Object.fromEntries(map);
@@ -1389,22 +1409,40 @@ function generateTokenJs(ctx) {
1389
1409
  js: outdent.default`
1390
1410
  const tokens = ${JSON.stringify(obj, null, 2)}
1391
1411
 
1392
- export function token(path, fallback) {
1393
- return tokens[path]?.value || fallback
1412
+ // No fallback parameter: \`token(path) ?? fallback\` says the same thing in the language, and
1413
+ // the parameter had to be proved side-effect-free before a build could fold the call away.
1414
+ export function token(path) {
1415
+ return tokens[path]?.variable
1394
1416
  }
1395
1417
 
1396
- function tokenVar(path, fallback) {
1397
- return tokens[path]?.variable || fallback
1418
+ function tokenValue(path) {
1419
+ return tokens[path]?.value
1398
1420
  }
1399
1421
 
1400
- token.var = tokenVar
1422
+ token.value = tokenValue
1401
1423
  `,
1402
1424
  dts: outdent.default`
1403
- ${ctx.file.importType("Token", "./tokens")}
1425
+ ${ctx.file.importType("Token, LiteralToken", "./tokens")}
1404
1426
 
1405
1427
  export declare const token: {
1406
- (path: Token, fallback?: string): string
1407
- var: (path: Token, fallback?: string) => string
1428
+ /**
1429
+ * The css variable reference — \`var(--colors-red-300)\`. Stays correct across themes.
1430
+ *
1431
+ * The parameter is the closed set of tokens the theme declares, so this always answers.
1432
+ * A path cast past that type does not, which is the usual bargain for a cast.
1433
+ */
1434
+ (path: Token): string
1435
+ /**
1436
+ * The resolved literal — \`#fca5a5\`. Use where css variables cannot be resolved, such as
1437
+ * a canvas fill or a charting library.
1438
+ *
1439
+ * Restricted to the tokens that have one. A virtual or conditional token resolves to its
1440
+ * \`var()\` because there is no single value to hand back, and a negative token to
1441
+ * \`calc(var(--spacing-4) * -1)\` because it has no declaration of its own — so asking for
1442
+ * a literal that cannot exist is a type error rather than a reference the caller then
1443
+ * hands to a canvas.
1444
+ */
1445
+ value: (path: LiteralToken) => string
1408
1446
  }
1409
1447
 
1410
1448
  ${ctx.file.exportTypeStar("./tokens")}
@@ -2430,6 +2468,8 @@ const restrict = (key, value, config) => {
2430
2468
  };
2431
2469
  //#endregion
2432
2470
  //#region src/artifacts/types/token-types.ts
2471
+ /** A css function whose result only the browser can produce, so it is never a literal. */
2472
+ const COMPUTED_BY_CSS = /\b(?:var|env|attr)\s*\(/i;
2433
2473
  const categories = [
2434
2474
  "aspectRatios",
2435
2475
  "zIndex",
@@ -2459,6 +2499,7 @@ function generateTokenTypes(ctx) {
2459
2499
  const { tokens } = ctx;
2460
2500
  const set = /* @__PURE__ */ new Set();
2461
2501
  const tokenSet = /* @__PURE__ */ new Set();
2502
+ const literalTokenSet = /* @__PURE__ */ new Set();
2462
2503
  const result = new Set(["export type Tokens = {"]);
2463
2504
  if (tokens.isEmpty) result.add("[token: string]: string");
2464
2505
  else {
@@ -2470,6 +2511,14 @@ function generateTokenTypes(ctx) {
2470
2511
  tokenSet.add(`${key}.$\{${categoryName}}`);
2471
2512
  result.add(`\t\t${key}: ${categoryName}`);
2472
2513
  }
2514
+ for (const token of tokens.allTokens) {
2515
+ const { category, prop } = token.extensions;
2516
+ if (!category || prop == null) continue;
2517
+ const resolved = tokens.view.get(token.name);
2518
+ if (resolved === void 0) continue;
2519
+ if (typeof resolved === "string" && COMPUTED_BY_CSS.test(resolved)) continue;
2520
+ literalTokenSet.add(`${category}.${prop}`);
2521
+ }
2473
2522
  }
2474
2523
  result.add("} & { [token: string]: never }");
2475
2524
  set.add(Array.from(result).join("\n"));
@@ -2478,7 +2527,7 @@ function generateTokenTypes(ctx) {
2478
2527
  arr.unshift(`export type Token = ${(0, _bamboocss_shared.unionType)(tokenSet, {
2479
2528
  stringify: (t) => `\`${t}\``,
2480
2529
  fallback: "string"
2481
- })}`);
2530
+ })}`, `export type LiteralToken = ${(0, _bamboocss_shared.unionType)(literalTokenSet, { fallback: tokenSet.size ? "never" : "string" })}`);
2482
2531
  return outdent.outdent.string(arr.join("\n\n"));
2483
2532
  }
2484
2533
  //#endregion
@@ -3596,13 +3645,26 @@ const CATEGORY_PROPERTY_MAP = {
3596
3645
  const getCategoryProperty = (category) => {
3597
3646
  return category ? CATEGORY_PROPERTY_MAP[category] ?? "color" : "color";
3598
3647
  };
3599
- const generateTokenExamples = (token) => {
3648
+ /**
3649
+ * Whether `token.value()` would answer with an actual literal.
3650
+ *
3651
+ * Takes the *resolved* value rather than the token, because the token alone cannot answer.
3652
+ * A semantic token appears once per condition under one name, and `view.get` is last-write —
3653
+ * so `colors.button.thick` carries `value: '#fff'` on its base variant while the view returns
3654
+ * `var(--colors-button-thick)`, because a `_dark` sibling wrote after it. Asking the token's
3655
+ * own fields said "literal" and the `.d.ts` said otherwise; only the view knows.
3656
+ */
3657
+ const hasLiteralValue = (resolved) => {
3658
+ if (resolved === void 0) return false;
3659
+ return typeof resolved !== "string" || !COMPUTED_BY_CSS.test(resolved);
3660
+ };
3661
+ const generateTokenExamples = (token, resolved) => {
3600
3662
  const prop = getCategoryProperty(token.extensions?.category);
3601
3663
  const tokenName = token.extensions.prop;
3602
- const fullTokenName = token.name;
3664
+ const fullTokenName = token.extensions.category ? `${token.extensions.category}.${tokenName}` : token.name;
3603
3665
  const functionExamples = [`css({ ${prop}: '${tokenName}' })`];
3604
3666
  const tokenFunctionExamples = [`token('${fullTokenName}')`];
3605
- if (token.extensions.varRef) tokenFunctionExamples.push(`token.var('${fullTokenName}')`);
3667
+ if (hasLiteralValue(resolved)) tokenFunctionExamples.push(`token.value('${fullTokenName}')`);
3606
3668
  return {
3607
3669
  functionExamples,
3608
3670
  tokenFunctionExamples
@@ -3623,7 +3685,7 @@ const generateThemeTokenGroups = (ctx, themeName, filterFn) => {
3623
3685
  return Array.from(byCategory.entries()).map(([category, typeTokens]) => {
3624
3686
  if (!typeTokens.length) return null;
3625
3687
  const firstToken = typeTokens[0];
3626
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3688
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3627
3689
  return {
3628
3690
  type: category,
3629
3691
  values: typeTokens.map((token) => {
@@ -3670,7 +3732,7 @@ const generateTokensSpec = (ctx) => {
3670
3732
  const typeTokens = Array.from(tokenMap.values()).filter((token) => !token.extensions.isSemantic && !token.extensions.isVirtual && !token.extensions.conditions && !token.extensions.isNegative);
3671
3733
  if (!typeTokens.length) return null;
3672
3734
  const firstToken = typeTokens[0];
3673
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3735
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3674
3736
  return {
3675
3737
  type: category,
3676
3738
  values: typeTokens.map((token) => ({
@@ -3693,7 +3755,7 @@ const generateSemanticTokensSpec = (ctx) => {
3693
3755
  const typeTokens = Array.from(tokenMap.values()).filter((token) => (token.extensions.isSemantic || token.extensions.conditions) && !token.extensions.isVirtual);
3694
3756
  if (!typeTokens.length) return null;
3695
3757
  const firstToken = typeTokens[0];
3696
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3758
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3697
3759
  return {
3698
3760
  type: category,
3699
3761
  values: typeTokens.map((token) => {
@@ -3872,30 +3934,36 @@ var Generator = class extends _bamboocss_core.Context {
3872
3934
  return names;
3873
3935
  };
3874
3936
  /**
3875
- * Tokens whose javascript value is a `var()` reference rather than a literal.
3876
- * `token('colors.text')` hands those to the caller as a reference, so the declaration
3877
- * has to survive whether or not the generated css mentions it. Ordinary tokens resolve
3878
- * to a literal in javascript and need no such exemption.
3937
+ * The token declarations held open so a runtime `token()` can answer for any path.
3938
+ *
3939
+ * `token()` hands javascript the *variable reference* for every token, so a path the build
3940
+ * cannot resolve could name any of them and every declaration has to survive. That is a
3941
+ * blunt instrument, and deliberately so: the alternative failure is a `var()` with no
3942
+ * declaration behind it, which resolves to the guaranteed-invalid value and inherits
3943
+ * rather than falling back — silently wrong, which is worse than visibly large.
3879
3944
  *
3880
- * The two cases mirror `generateTokenJs`, which is what decides the value javascript
3881
- * actually receives:
3945
+ * It used to be narrower, because `token()` used to return a *literal* for a plain token
3946
+ * and only a `var()` for virtual, conditional and negative ones. That split is gone, and
3947
+ * narrowing this to match it would now strand exactly the base tokens the old split made
3948
+ * safe.
3882
3949
  *
3883
- * - A virtual token, or one carrying a condition, is handed its own `varRef`.
3884
- * - A negative token is handed `calc(var(--x) * -1)`, so it is a reference too — but to
3885
- * the *positive* token's declaration. Its own var is never declared, so the name has
3886
- * to come out of the value.
3950
+ * So the gate below carries the whole saving. `styled-system/tokens` is generated into the
3951
+ * project, so nothing outside it can import them -- if no file under `include` reaches for
3952
+ * a token from javascript, no caller exists to serve and the declarations are as prunable
3953
+ * as any other.
3954
+ *
3955
+ * That gate is all-or-nothing per project, which is the coarse part worth fixing next: a
3956
+ * project whose token calls all resolve to string literals needs none of this, because
3957
+ * `collectTokenReferences` already kept those paths by name. Deciding that needs the
3958
+ * reference accounting the gate does not do yet -- see `tokensReachableFromJs`.
3887
3959
  */
3888
3960
  getAlwaysKeptTokenVars = (tokensReachableFromJs) => {
3889
3961
  const names = /* @__PURE__ */ new Set();
3890
3962
  if (!tokensReachableFromJs) return names;
3891
3963
  this.tokens.allTokens.forEach((token) => {
3892
- const { isVirtual, isNegative, condition, var: varName } = token.extensions;
3893
- if (isVirtual || condition !== "base") {
3894
- if (varName) names.add(varName.startsWith("--") ? varName : `--${varName}`);
3895
- return;
3896
- }
3897
- if (!isNegative) return;
3898
- for (const name of (0, _bamboocss_shared.cssVarRefs)(token.value)) names.add(name);
3964
+ const { var: varName } = token.extensions;
3965
+ if (varName) names.add(varName.startsWith("--") ? varName : `--${varName}`);
3966
+ if (typeof token.value === "string") for (const name of (0, _bamboocss_shared.cssVarRefs)(token.value)) names.add(name);
3899
3967
  });
3900
3968
  return names;
3901
3969
  };
package/dist/index.d.cts CHANGED
@@ -93,18 +93,28 @@ declare class Generator extends Context {
93
93
  */
94
94
  private getThemeTokenVars;
95
95
  /**
96
- * Tokens whose javascript value is a `var()` reference rather than a literal.
97
- * `token('colors.text')` hands those to the caller as a reference, so the declaration
98
- * has to survive whether or not the generated css mentions it. Ordinary tokens resolve
99
- * to a literal in javascript and need no such exemption.
96
+ * The token declarations held open so a runtime `token()` can answer for any path.
100
97
  *
101
- * The two cases mirror `generateTokenJs`, which is what decides the value javascript
102
- * actually receives:
98
+ * `token()` hands javascript the *variable reference* for every token, so a path the build
99
+ * cannot resolve could name any of them and every declaration has to survive. That is a
100
+ * blunt instrument, and deliberately so: the alternative failure is a `var()` with no
101
+ * declaration behind it, which resolves to the guaranteed-invalid value and inherits
102
+ * rather than falling back — silently wrong, which is worse than visibly large.
103
103
  *
104
- * - A virtual token, or one carrying a condition, is handed its own `varRef`.
105
- * - A negative token is handed `calc(var(--x) * -1)`, so it is a reference too — but to
106
- * the *positive* token's declaration. Its own var is never declared, so the name has
107
- * to come out of the value.
104
+ * It used to be narrower, because `token()` used to return a *literal* for a plain token
105
+ * and only a `var()` for virtual, conditional and negative ones. That split is gone, and
106
+ * narrowing this to match it would now strand exactly the base tokens the old split made
107
+ * safe.
108
+ *
109
+ * So the gate below carries the whole saving. `styled-system/tokens` is generated into the
110
+ * project, so nothing outside it can import them -- if no file under `include` reaches for
111
+ * a token from javascript, no caller exists to serve and the declarations are as prunable
112
+ * as any other.
113
+ *
114
+ * That gate is all-or-nothing per project, which is the coarse part worth fixing next: a
115
+ * project whose token calls all resolve to string literals needs none of this, because
116
+ * `collectTokenReferences` already kept those paths by name. Deciding that needs the
117
+ * reference accounting the gate does not do yet -- see `tokensReachableFromJs`.
108
118
  */
109
119
  private getAlwaysKeptTokenVars;
110
120
  getParserCss: (decoder: StyleDecoder) => string;
package/dist/index.d.mts CHANGED
@@ -93,18 +93,28 @@ declare class Generator extends Context {
93
93
  */
94
94
  private getThemeTokenVars;
95
95
  /**
96
- * Tokens whose javascript value is a `var()` reference rather than a literal.
97
- * `token('colors.text')` hands those to the caller as a reference, so the declaration
98
- * has to survive whether or not the generated css mentions it. Ordinary tokens resolve
99
- * to a literal in javascript and need no such exemption.
96
+ * The token declarations held open so a runtime `token()` can answer for any path.
100
97
  *
101
- * The two cases mirror `generateTokenJs`, which is what decides the value javascript
102
- * actually receives:
98
+ * `token()` hands javascript the *variable reference* for every token, so a path the build
99
+ * cannot resolve could name any of them and every declaration has to survive. That is a
100
+ * blunt instrument, and deliberately so: the alternative failure is a `var()` with no
101
+ * declaration behind it, which resolves to the guaranteed-invalid value and inherits
102
+ * rather than falling back — silently wrong, which is worse than visibly large.
103
103
  *
104
- * - A virtual token, or one carrying a condition, is handed its own `varRef`.
105
- * - A negative token is handed `calc(var(--x) * -1)`, so it is a reference too — but to
106
- * the *positive* token's declaration. Its own var is never declared, so the name has
107
- * to come out of the value.
104
+ * It used to be narrower, because `token()` used to return a *literal* for a plain token
105
+ * and only a `var()` for virtual, conditional and negative ones. That split is gone, and
106
+ * narrowing this to match it would now strand exactly the base tokens the old split made
107
+ * safe.
108
+ *
109
+ * So the gate below carries the whole saving. `styled-system/tokens` is generated into the
110
+ * project, so nothing outside it can import them -- if no file under `include` reaches for
111
+ * a token from javascript, no caller exists to serve and the declarations are as prunable
112
+ * as any other.
113
+ *
114
+ * That gate is all-or-nothing per project, which is the coarse part worth fixing next: a
115
+ * project whose token calls all resolve to string literals needs none of this, because
116
+ * `collectTokenReferences` already kept those paths by name. Deciding that needs the
117
+ * reference accounting the gate does not do yet -- see `tokensReachableFromJs`.
108
118
  */
109
119
  private getAlwaysKeptTokenVars;
110
120
  getParserCss: (decoder: StyleDecoder) => string;
package/dist/index.mjs CHANGED
@@ -1347,15 +1347,35 @@ function generateSvaFn(ctx) {
1347
1347
  }
1348
1348
  //#endregion
1349
1349
  //#region src/artifacts/js/token.ts
1350
+ /**
1351
+ * `token()` hands back the variable reference for every token, and `token.value()` the
1352
+ * resolved literal.
1353
+ *
1354
+ * It used to be the other way round, decided per token: a base token resolved to its literal
1355
+ * and a virtual or conditional one to its `var()`. That made the return kind a property of
1356
+ * the *theme* rather than of the call, so adding `_dark` to a token silently changed what
1357
+ * every caller received — same call, same path, a colour before and a variable after, with
1358
+ * both typed `string` and nothing to catch it.
1359
+ *
1360
+ * Always-a-reference is the predictable half and the one that keeps working when a theme
1361
+ * switches, so it takes the short name. The literal is still reachable, but has to be asked
1362
+ * for — which is also the honest signal, since it is the form that stops responding to
1363
+ * conditions.
1364
+ *
1365
+ * `value` keeps the old per-token split rather than becoming `token.value` for everything:
1366
+ * a virtual or conditional token has no single literal to hand back, so its `var()` is still
1367
+ * the only truthful answer.
1368
+ */
1350
1369
  function generateTokenJs(ctx) {
1351
1370
  const { tokens } = ctx;
1352
1371
  const map = /* @__PURE__ */ new Map();
1353
1372
  tokens.allTokens.forEach((token) => {
1354
1373
  const { varRef, isVirtual } = token.extensions;
1355
- const value = isVirtual || token.extensions.condition !== "base" ? varRef : token.value;
1374
+ const variable = tokens.view.getVar(token.name) ?? varRef;
1375
+ const value = tokens.view.get(token.name) ?? (isVirtual || token.extensions.condition !== "base" ? varRef : token.value);
1356
1376
  map.set(token.name, {
1357
1377
  value,
1358
- variable: varRef
1378
+ variable
1359
1379
  });
1360
1380
  });
1361
1381
  const obj = Object.fromEntries(map);
@@ -1363,22 +1383,40 @@ function generateTokenJs(ctx) {
1363
1383
  js: outdent$1`
1364
1384
  const tokens = ${JSON.stringify(obj, null, 2)}
1365
1385
 
1366
- export function token(path, fallback) {
1367
- return tokens[path]?.value || fallback
1386
+ // No fallback parameter: \`token(path) ?? fallback\` says the same thing in the language, and
1387
+ // the parameter had to be proved side-effect-free before a build could fold the call away.
1388
+ export function token(path) {
1389
+ return tokens[path]?.variable
1368
1390
  }
1369
1391
 
1370
- function tokenVar(path, fallback) {
1371
- return tokens[path]?.variable || fallback
1392
+ function tokenValue(path) {
1393
+ return tokens[path]?.value
1372
1394
  }
1373
1395
 
1374
- token.var = tokenVar
1396
+ token.value = tokenValue
1375
1397
  `,
1376
1398
  dts: outdent$1`
1377
- ${ctx.file.importType("Token", "./tokens")}
1399
+ ${ctx.file.importType("Token, LiteralToken", "./tokens")}
1378
1400
 
1379
1401
  export declare const token: {
1380
- (path: Token, fallback?: string): string
1381
- var: (path: Token, fallback?: string) => string
1402
+ /**
1403
+ * The css variable reference — \`var(--colors-red-300)\`. Stays correct across themes.
1404
+ *
1405
+ * The parameter is the closed set of tokens the theme declares, so this always answers.
1406
+ * A path cast past that type does not, which is the usual bargain for a cast.
1407
+ */
1408
+ (path: Token): string
1409
+ /**
1410
+ * The resolved literal — \`#fca5a5\`. Use where css variables cannot be resolved, such as
1411
+ * a canvas fill or a charting library.
1412
+ *
1413
+ * Restricted to the tokens that have one. A virtual or conditional token resolves to its
1414
+ * \`var()\` because there is no single value to hand back, and a negative token to
1415
+ * \`calc(var(--spacing-4) * -1)\` because it has no declaration of its own — so asking for
1416
+ * a literal that cannot exist is a type error rather than a reference the caller then
1417
+ * hands to a canvas.
1418
+ */
1419
+ value: (path: LiteralToken) => string
1382
1420
  }
1383
1421
 
1384
1422
  ${ctx.file.exportTypeStar("./tokens")}
@@ -2404,6 +2442,8 @@ const restrict = (key, value, config) => {
2404
2442
  };
2405
2443
  //#endregion
2406
2444
  //#region src/artifacts/types/token-types.ts
2445
+ /** A css function whose result only the browser can produce, so it is never a literal. */
2446
+ const COMPUTED_BY_CSS = /\b(?:var|env|attr)\s*\(/i;
2407
2447
  const categories = [
2408
2448
  "aspectRatios",
2409
2449
  "zIndex",
@@ -2433,6 +2473,7 @@ function generateTokenTypes(ctx) {
2433
2473
  const { tokens } = ctx;
2434
2474
  const set = /* @__PURE__ */ new Set();
2435
2475
  const tokenSet = /* @__PURE__ */ new Set();
2476
+ const literalTokenSet = /* @__PURE__ */ new Set();
2436
2477
  const result = new Set(["export type Tokens = {"]);
2437
2478
  if (tokens.isEmpty) result.add("[token: string]: string");
2438
2479
  else {
@@ -2444,6 +2485,14 @@ function generateTokenTypes(ctx) {
2444
2485
  tokenSet.add(`${key}.$\{${categoryName}}`);
2445
2486
  result.add(`\t\t${key}: ${categoryName}`);
2446
2487
  }
2488
+ for (const token of tokens.allTokens) {
2489
+ const { category, prop } = token.extensions;
2490
+ if (!category || prop == null) continue;
2491
+ const resolved = tokens.view.get(token.name);
2492
+ if (resolved === void 0) continue;
2493
+ if (typeof resolved === "string" && COMPUTED_BY_CSS.test(resolved)) continue;
2494
+ literalTokenSet.add(`${category}.${prop}`);
2495
+ }
2447
2496
  }
2448
2497
  result.add("} & { [token: string]: never }");
2449
2498
  set.add(Array.from(result).join("\n"));
@@ -2452,7 +2501,7 @@ function generateTokenTypes(ctx) {
2452
2501
  arr.unshift(`export type Token = ${unionType(tokenSet, {
2453
2502
  stringify: (t) => `\`${t}\``,
2454
2503
  fallback: "string"
2455
- })}`);
2504
+ })}`, `export type LiteralToken = ${unionType(literalTokenSet, { fallback: tokenSet.size ? "never" : "string" })}`);
2456
2505
  return outdent.string(arr.join("\n\n"));
2457
2506
  }
2458
2507
  //#endregion
@@ -3570,13 +3619,26 @@ const CATEGORY_PROPERTY_MAP = {
3570
3619
  const getCategoryProperty = (category) => {
3571
3620
  return category ? CATEGORY_PROPERTY_MAP[category] ?? "color" : "color";
3572
3621
  };
3573
- const generateTokenExamples = (token) => {
3622
+ /**
3623
+ * Whether `token.value()` would answer with an actual literal.
3624
+ *
3625
+ * Takes the *resolved* value rather than the token, because the token alone cannot answer.
3626
+ * A semantic token appears once per condition under one name, and `view.get` is last-write —
3627
+ * so `colors.button.thick` carries `value: '#fff'` on its base variant while the view returns
3628
+ * `var(--colors-button-thick)`, because a `_dark` sibling wrote after it. Asking the token's
3629
+ * own fields said "literal" and the `.d.ts` said otherwise; only the view knows.
3630
+ */
3631
+ const hasLiteralValue = (resolved) => {
3632
+ if (resolved === void 0) return false;
3633
+ return typeof resolved !== "string" || !COMPUTED_BY_CSS.test(resolved);
3634
+ };
3635
+ const generateTokenExamples = (token, resolved) => {
3574
3636
  const prop = getCategoryProperty(token.extensions?.category);
3575
3637
  const tokenName = token.extensions.prop;
3576
- const fullTokenName = token.name;
3638
+ const fullTokenName = token.extensions.category ? `${token.extensions.category}.${tokenName}` : token.name;
3577
3639
  const functionExamples = [`css({ ${prop}: '${tokenName}' })`];
3578
3640
  const tokenFunctionExamples = [`token('${fullTokenName}')`];
3579
- if (token.extensions.varRef) tokenFunctionExamples.push(`token.var('${fullTokenName}')`);
3641
+ if (hasLiteralValue(resolved)) tokenFunctionExamples.push(`token.value('${fullTokenName}')`);
3580
3642
  return {
3581
3643
  functionExamples,
3582
3644
  tokenFunctionExamples
@@ -3597,7 +3659,7 @@ const generateThemeTokenGroups = (ctx, themeName, filterFn) => {
3597
3659
  return Array.from(byCategory.entries()).map(([category, typeTokens]) => {
3598
3660
  if (!typeTokens.length) return null;
3599
3661
  const firstToken = typeTokens[0];
3600
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3662
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3601
3663
  return {
3602
3664
  type: category,
3603
3665
  values: typeTokens.map((token) => {
@@ -3644,7 +3706,7 @@ const generateTokensSpec = (ctx) => {
3644
3706
  const typeTokens = Array.from(tokenMap.values()).filter((token) => !token.extensions.isSemantic && !token.extensions.isVirtual && !token.extensions.conditions && !token.extensions.isNegative);
3645
3707
  if (!typeTokens.length) return null;
3646
3708
  const firstToken = typeTokens[0];
3647
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3709
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3648
3710
  return {
3649
3711
  type: category,
3650
3712
  values: typeTokens.map((token) => ({
@@ -3667,7 +3729,7 @@ const generateSemanticTokensSpec = (ctx) => {
3667
3729
  const typeTokens = Array.from(tokenMap.values()).filter((token) => (token.extensions.isSemantic || token.extensions.conditions) && !token.extensions.isVirtual);
3668
3730
  if (!typeTokens.length) return null;
3669
3731
  const firstToken = typeTokens[0];
3670
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3732
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3671
3733
  return {
3672
3734
  type: category,
3673
3735
  values: typeTokens.map((token) => {
@@ -3846,30 +3908,36 @@ var Generator = class extends Context {
3846
3908
  return names;
3847
3909
  };
3848
3910
  /**
3849
- * Tokens whose javascript value is a `var()` reference rather than a literal.
3850
- * `token('colors.text')` hands those to the caller as a reference, so the declaration
3851
- * has to survive whether or not the generated css mentions it. Ordinary tokens resolve
3852
- * to a literal in javascript and need no such exemption.
3911
+ * The token declarations held open so a runtime `token()` can answer for any path.
3912
+ *
3913
+ * `token()` hands javascript the *variable reference* for every token, so a path the build
3914
+ * cannot resolve could name any of them and every declaration has to survive. That is a
3915
+ * blunt instrument, and deliberately so: the alternative failure is a `var()` with no
3916
+ * declaration behind it, which resolves to the guaranteed-invalid value and inherits
3917
+ * rather than falling back — silently wrong, which is worse than visibly large.
3853
3918
  *
3854
- * The two cases mirror `generateTokenJs`, which is what decides the value javascript
3855
- * actually receives:
3919
+ * It used to be narrower, because `token()` used to return a *literal* for a plain token
3920
+ * and only a `var()` for virtual, conditional and negative ones. That split is gone, and
3921
+ * narrowing this to match it would now strand exactly the base tokens the old split made
3922
+ * safe.
3856
3923
  *
3857
- * - A virtual token, or one carrying a condition, is handed its own `varRef`.
3858
- * - A negative token is handed `calc(var(--x) * -1)`, so it is a reference too — but to
3859
- * the *positive* token's declaration. Its own var is never declared, so the name has
3860
- * to come out of the value.
3924
+ * So the gate below carries the whole saving. `styled-system/tokens` is generated into the
3925
+ * project, so nothing outside it can import them -- if no file under `include` reaches for
3926
+ * a token from javascript, no caller exists to serve and the declarations are as prunable
3927
+ * as any other.
3928
+ *
3929
+ * That gate is all-or-nothing per project, which is the coarse part worth fixing next: a
3930
+ * project whose token calls all resolve to string literals needs none of this, because
3931
+ * `collectTokenReferences` already kept those paths by name. Deciding that needs the
3932
+ * reference accounting the gate does not do yet -- see `tokensReachableFromJs`.
3861
3933
  */
3862
3934
  getAlwaysKeptTokenVars = (tokensReachableFromJs) => {
3863
3935
  const names = /* @__PURE__ */ new Set();
3864
3936
  if (!tokensReachableFromJs) return names;
3865
3937
  this.tokens.allTokens.forEach((token) => {
3866
- const { isVirtual, isNegative, condition, var: varName } = token.extensions;
3867
- if (isVirtual || condition !== "base") {
3868
- if (varName) names.add(varName.startsWith("--") ? varName : `--${varName}`);
3869
- return;
3870
- }
3871
- if (!isNegative) return;
3872
- for (const name of cssVarRefs(token.value)) names.add(name);
3938
+ const { var: varName } = token.extensions;
3939
+ if (varName) names.add(varName.startsWith("--") ? varName : `--${varName}`);
3940
+ if (typeof token.value === "string") for (const name of cssVarRefs(token.value)) names.add(name);
3873
3941
  });
3874
3942
  return names;
3875
3943
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bamboocss/generator",
3
- "version": "1.28.1",
3
+ "version": "1.30.0",
4
4
  "description": "The css generator for css bamboo",
5
5
  "homepage": "https://bamboocss.com",
6
6
  "license": "MIT",
@@ -38,12 +38,12 @@
38
38
  "pluralize": "8.0.0",
39
39
  "postcss": "8.5.26",
40
40
  "ts-pattern": "5.9.0",
41
- "@bamboocss/core": "1.28.1",
42
- "@bamboocss/is-valid-prop": "^1.28.1",
43
- "@bamboocss/logger": "1.28.1",
44
- "@bamboocss/shared": "1.28.1",
45
- "@bamboocss/token-dictionary": "1.28.1",
46
- "@bamboocss/types": "1.28.1"
41
+ "@bamboocss/core": "1.30.0",
42
+ "@bamboocss/is-valid-prop": "^1.30.0",
43
+ "@bamboocss/logger": "1.30.0",
44
+ "@bamboocss/shared": "1.30.0",
45
+ "@bamboocss/token-dictionary": "1.30.0",
46
+ "@bamboocss/types": "1.30.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/pluralize": "0.0.33"