@bamboocss/generator 1.29.0 → 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
@@ -1409,37 +1409,40 @@ function generateTokenJs(ctx) {
1409
1409
  js: outdent.default`
1410
1410
  const tokens = ${JSON.stringify(obj, null, 2)}
1411
1411
 
1412
- // \`??\`, not \`||\`. The fallback is for a path that names no token, and \`||\` also swallows a
1413
- // token whose value is legitimately falsy — \`zIndex: { base: { value: 0 } }\` returned the
1414
- // fallback instead of 0. Nothing in the default preset has one, so this only ever bit a
1415
- // custom theme.
1416
- export function token(path, fallback) {
1417
- return tokens[path]?.variable ?? 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
1418
1416
  }
1419
1417
 
1420
- function tokenValue(path, fallback) {
1421
- return tokens[path]?.value ?? fallback
1418
+ function tokenValue(path) {
1419
+ return tokens[path]?.value
1422
1420
  }
1423
1421
 
1424
- // \`token.var\` predates \`token()\` returning the reference itself. Kept as the same
1425
- // function rather than removed, so the spelling that was correct before still is.
1426
- token.var = token
1427
1422
  token.value = tokenValue
1428
1423
  `,
1429
1424
  dts: outdent.default`
1430
- ${ctx.file.importType("Token", "./tokens")}
1425
+ ${ctx.file.importType("Token, LiteralToken", "./tokens")}
1431
1426
 
1432
1427
  export declare const token: {
1433
- /** The css variable reference — \`var(--colors-red-300)\`. Stays correct across themes. */
1434
- (path: Token, fallback?: string): string
1435
- /** Alias of \`token()\`, kept for compatibility. */
1436
- 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
1437
1435
  /**
1438
1436
  * The resolved literal — \`#fca5a5\`. Use where css variables cannot be resolved, such as
1439
- * canvas or a charting library. A conditional token has no single literal and still
1440
- * returns its \`var()\`.
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.
1441
1444
  */
1442
- value: (path: Token, fallback?: string) => string
1445
+ value: (path: LiteralToken) => string
1443
1446
  }
1444
1447
 
1445
1448
  ${ctx.file.exportTypeStar("./tokens")}
@@ -2465,6 +2468,8 @@ const restrict = (key, value, config) => {
2465
2468
  };
2466
2469
  //#endregion
2467
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;
2468
2473
  const categories = [
2469
2474
  "aspectRatios",
2470
2475
  "zIndex",
@@ -2494,6 +2499,7 @@ function generateTokenTypes(ctx) {
2494
2499
  const { tokens } = ctx;
2495
2500
  const set = /* @__PURE__ */ new Set();
2496
2501
  const tokenSet = /* @__PURE__ */ new Set();
2502
+ const literalTokenSet = /* @__PURE__ */ new Set();
2497
2503
  const result = new Set(["export type Tokens = {"]);
2498
2504
  if (tokens.isEmpty) result.add("[token: string]: string");
2499
2505
  else {
@@ -2505,6 +2511,14 @@ function generateTokenTypes(ctx) {
2505
2511
  tokenSet.add(`${key}.$\{${categoryName}}`);
2506
2512
  result.add(`\t\t${key}: ${categoryName}`);
2507
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
+ }
2508
2522
  }
2509
2523
  result.add("} & { [token: string]: never }");
2510
2524
  set.add(Array.from(result).join("\n"));
@@ -2513,7 +2527,7 @@ function generateTokenTypes(ctx) {
2513
2527
  arr.unshift(`export type Token = ${(0, _bamboocss_shared.unionType)(tokenSet, {
2514
2528
  stringify: (t) => `\`${t}\``,
2515
2529
  fallback: "string"
2516
- })}`);
2530
+ })}`, `export type LiteralToken = ${(0, _bamboocss_shared.unionType)(literalTokenSet, { fallback: tokenSet.size ? "never" : "string" })}`);
2517
2531
  return outdent.outdent.string(arr.join("\n\n"));
2518
2532
  }
2519
2533
  //#endregion
@@ -3631,13 +3645,26 @@ const CATEGORY_PROPERTY_MAP = {
3631
3645
  const getCategoryProperty = (category) => {
3632
3646
  return category ? CATEGORY_PROPERTY_MAP[category] ?? "color" : "color";
3633
3647
  };
3634
- 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) => {
3635
3662
  const prop = getCategoryProperty(token.extensions?.category);
3636
3663
  const tokenName = token.extensions.prop;
3637
- const fullTokenName = token.name;
3664
+ const fullTokenName = token.extensions.category ? `${token.extensions.category}.${tokenName}` : token.name;
3638
3665
  const functionExamples = [`css({ ${prop}: '${tokenName}' })`];
3639
3666
  const tokenFunctionExamples = [`token('${fullTokenName}')`];
3640
- tokenFunctionExamples.push(`token.value('${fullTokenName}')`);
3667
+ if (hasLiteralValue(resolved)) tokenFunctionExamples.push(`token.value('${fullTokenName}')`);
3641
3668
  return {
3642
3669
  functionExamples,
3643
3670
  tokenFunctionExamples
@@ -3658,7 +3685,7 @@ const generateThemeTokenGroups = (ctx, themeName, filterFn) => {
3658
3685
  return Array.from(byCategory.entries()).map(([category, typeTokens]) => {
3659
3686
  if (!typeTokens.length) return null;
3660
3687
  const firstToken = typeTokens[0];
3661
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3688
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3662
3689
  return {
3663
3690
  type: category,
3664
3691
  values: typeTokens.map((token) => {
@@ -3705,7 +3732,7 @@ const generateTokensSpec = (ctx) => {
3705
3732
  const typeTokens = Array.from(tokenMap.values()).filter((token) => !token.extensions.isSemantic && !token.extensions.isVirtual && !token.extensions.conditions && !token.extensions.isNegative);
3706
3733
  if (!typeTokens.length) return null;
3707
3734
  const firstToken = typeTokens[0];
3708
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3735
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3709
3736
  return {
3710
3737
  type: category,
3711
3738
  values: typeTokens.map((token) => ({
@@ -3728,7 +3755,7 @@ const generateSemanticTokensSpec = (ctx) => {
3728
3755
  const typeTokens = Array.from(tokenMap.values()).filter((token) => (token.extensions.isSemantic || token.extensions.conditions) && !token.extensions.isVirtual);
3729
3756
  if (!typeTokens.length) return null;
3730
3757
  const firstToken = typeTokens[0];
3731
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3758
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3732
3759
  return {
3733
3760
  type: category,
3734
3761
  values: typeTokens.map((token) => {
package/dist/index.mjs CHANGED
@@ -1383,37 +1383,40 @@ function generateTokenJs(ctx) {
1383
1383
  js: outdent$1`
1384
1384
  const tokens = ${JSON.stringify(obj, null, 2)}
1385
1385
 
1386
- // \`??\`, not \`||\`. The fallback is for a path that names no token, and \`||\` also swallows a
1387
- // token whose value is legitimately falsy — \`zIndex: { base: { value: 0 } }\` returned the
1388
- // fallback instead of 0. Nothing in the default preset has one, so this only ever bit a
1389
- // custom theme.
1390
- export function token(path, fallback) {
1391
- return tokens[path]?.variable ?? 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
1392
1390
  }
1393
1391
 
1394
- function tokenValue(path, fallback) {
1395
- return tokens[path]?.value ?? fallback
1392
+ function tokenValue(path) {
1393
+ return tokens[path]?.value
1396
1394
  }
1397
1395
 
1398
- // \`token.var\` predates \`token()\` returning the reference itself. Kept as the same
1399
- // function rather than removed, so the spelling that was correct before still is.
1400
- token.var = token
1401
1396
  token.value = tokenValue
1402
1397
  `,
1403
1398
  dts: outdent$1`
1404
- ${ctx.file.importType("Token", "./tokens")}
1399
+ ${ctx.file.importType("Token, LiteralToken", "./tokens")}
1405
1400
 
1406
1401
  export declare const token: {
1407
- /** The css variable reference — \`var(--colors-red-300)\`. Stays correct across themes. */
1408
- (path: Token, fallback?: string): string
1409
- /** Alias of \`token()\`, kept for compatibility. */
1410
- 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
1411
1409
  /**
1412
1410
  * The resolved literal — \`#fca5a5\`. Use where css variables cannot be resolved, such as
1413
- * canvas or a charting library. A conditional token has no single literal and still
1414
- * returns its \`var()\`.
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.
1415
1418
  */
1416
- value: (path: Token, fallback?: string) => string
1419
+ value: (path: LiteralToken) => string
1417
1420
  }
1418
1421
 
1419
1422
  ${ctx.file.exportTypeStar("./tokens")}
@@ -2439,6 +2442,8 @@ const restrict = (key, value, config) => {
2439
2442
  };
2440
2443
  //#endregion
2441
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;
2442
2447
  const categories = [
2443
2448
  "aspectRatios",
2444
2449
  "zIndex",
@@ -2468,6 +2473,7 @@ function generateTokenTypes(ctx) {
2468
2473
  const { tokens } = ctx;
2469
2474
  const set = /* @__PURE__ */ new Set();
2470
2475
  const tokenSet = /* @__PURE__ */ new Set();
2476
+ const literalTokenSet = /* @__PURE__ */ new Set();
2471
2477
  const result = new Set(["export type Tokens = {"]);
2472
2478
  if (tokens.isEmpty) result.add("[token: string]: string");
2473
2479
  else {
@@ -2479,6 +2485,14 @@ function generateTokenTypes(ctx) {
2479
2485
  tokenSet.add(`${key}.$\{${categoryName}}`);
2480
2486
  result.add(`\t\t${key}: ${categoryName}`);
2481
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
+ }
2482
2496
  }
2483
2497
  result.add("} & { [token: string]: never }");
2484
2498
  set.add(Array.from(result).join("\n"));
@@ -2487,7 +2501,7 @@ function generateTokenTypes(ctx) {
2487
2501
  arr.unshift(`export type Token = ${unionType(tokenSet, {
2488
2502
  stringify: (t) => `\`${t}\``,
2489
2503
  fallback: "string"
2490
- })}`);
2504
+ })}`, `export type LiteralToken = ${unionType(literalTokenSet, { fallback: tokenSet.size ? "never" : "string" })}`);
2491
2505
  return outdent.string(arr.join("\n\n"));
2492
2506
  }
2493
2507
  //#endregion
@@ -3605,13 +3619,26 @@ const CATEGORY_PROPERTY_MAP = {
3605
3619
  const getCategoryProperty = (category) => {
3606
3620
  return category ? CATEGORY_PROPERTY_MAP[category] ?? "color" : "color";
3607
3621
  };
3608
- 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) => {
3609
3636
  const prop = getCategoryProperty(token.extensions?.category);
3610
3637
  const tokenName = token.extensions.prop;
3611
- const fullTokenName = token.name;
3638
+ const fullTokenName = token.extensions.category ? `${token.extensions.category}.${tokenName}` : token.name;
3612
3639
  const functionExamples = [`css({ ${prop}: '${tokenName}' })`];
3613
3640
  const tokenFunctionExamples = [`token('${fullTokenName}')`];
3614
- tokenFunctionExamples.push(`token.value('${fullTokenName}')`);
3641
+ if (hasLiteralValue(resolved)) tokenFunctionExamples.push(`token.value('${fullTokenName}')`);
3615
3642
  return {
3616
3643
  functionExamples,
3617
3644
  tokenFunctionExamples
@@ -3632,7 +3659,7 @@ const generateThemeTokenGroups = (ctx, themeName, filterFn) => {
3632
3659
  return Array.from(byCategory.entries()).map(([category, typeTokens]) => {
3633
3660
  if (!typeTokens.length) return null;
3634
3661
  const firstToken = typeTokens[0];
3635
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3662
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3636
3663
  return {
3637
3664
  type: category,
3638
3665
  values: typeTokens.map((token) => {
@@ -3679,7 +3706,7 @@ const generateTokensSpec = (ctx) => {
3679
3706
  const typeTokens = Array.from(tokenMap.values()).filter((token) => !token.extensions.isSemantic && !token.extensions.isVirtual && !token.extensions.conditions && !token.extensions.isNegative);
3680
3707
  if (!typeTokens.length) return null;
3681
3708
  const firstToken = typeTokens[0];
3682
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3709
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3683
3710
  return {
3684
3711
  type: category,
3685
3712
  values: typeTokens.map((token) => ({
@@ -3702,7 +3729,7 @@ const generateSemanticTokensSpec = (ctx) => {
3702
3729
  const typeTokens = Array.from(tokenMap.values()).filter((token) => (token.extensions.isSemantic || token.extensions.conditions) && !token.extensions.isVirtual);
3703
3730
  if (!typeTokens.length) return null;
3704
3731
  const firstToken = typeTokens[0];
3705
- const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken);
3732
+ const { functionExamples, tokenFunctionExamples } = generateTokenExamples(firstToken, ctx.tokens.view.get(firstToken.name));
3706
3733
  return {
3707
3734
  type: category,
3708
3735
  values: typeTokens.map((token) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bamboocss/generator",
3
- "version": "1.29.0",
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.29.0",
42
- "@bamboocss/is-valid-prop": "^1.29.0",
43
- "@bamboocss/logger": "1.29.0",
44
- "@bamboocss/shared": "1.29.0",
45
- "@bamboocss/token-dictionary": "1.29.0",
46
- "@bamboocss/types": "1.29.0"
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"