@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 +102 -34
- package/dist/index.d.cts +20 -10
- package/dist/index.d.mts +20 -10
- package/dist/index.mjs +102 -34
- package/package.json +7 -7
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
|
|
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
|
|
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
|
-
|
|
1393
|
-
|
|
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
|
|
1397
|
-
return tokens[path]?.
|
|
1418
|
+
function tokenValue(path) {
|
|
1419
|
+
return tokens[path]?.value
|
|
1398
1420
|
}
|
|
1399
1421
|
|
|
1400
|
-
token.
|
|
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
|
-
|
|
1407
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
*
|
|
3876
|
-
*
|
|
3877
|
-
*
|
|
3878
|
-
*
|
|
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
|
-
*
|
|
3881
|
-
*
|
|
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
|
-
*
|
|
3884
|
-
*
|
|
3885
|
-
*
|
|
3886
|
-
*
|
|
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 {
|
|
3893
|
-
if (
|
|
3894
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
102
|
-
*
|
|
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
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
102
|
-
*
|
|
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
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
1367
|
-
|
|
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
|
|
1371
|
-
return tokens[path]?.
|
|
1392
|
+
function tokenValue(path) {
|
|
1393
|
+
return tokens[path]?.value
|
|
1372
1394
|
}
|
|
1373
1395
|
|
|
1374
|
-
token.
|
|
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
|
-
|
|
1381
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
*
|
|
3850
|
-
*
|
|
3851
|
-
*
|
|
3852
|
-
*
|
|
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
|
-
*
|
|
3855
|
-
*
|
|
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
|
-
*
|
|
3858
|
-
*
|
|
3859
|
-
*
|
|
3860
|
-
*
|
|
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 {
|
|
3867
|
-
if (
|
|
3868
|
-
|
|
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.
|
|
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.
|
|
42
|
-
"@bamboocss/is-valid-prop": "^1.
|
|
43
|
-
"@bamboocss/logger": "1.
|
|
44
|
-
"@bamboocss/shared": "1.
|
|
45
|
-
"@bamboocss/token-dictionary": "1.
|
|
46
|
-
"@bamboocss/types": "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"
|