@massimo-cassandro/minimo 1.20.7 → 1.20.8
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/design-tokens/README.md +13 -12
- package/design-tokens/_src/typography.minimo.tokens.mjs +19 -13
- package/design-tokens/tokens-config-sample.mjs +19 -14
- package/design-tokens/utilities/build-tokens-src/config.mjs +11 -11
- package/design-tokens/utilities/build-tokens-src/formats/css.mjs +19 -2
- package/design-tokens/utilities/build-tokens-src/formats/{penpot.mjs → json.mjs} +61 -58
- package/design-tokens/utilities/build-tokens-src/platforms.mjs +22 -21
- package/design-tokens/utilities/build-tokens.mjs +37 -29
- package/index.js +6 -2
- package/package.json +1 -1
- package/src/custom-properties.css +9 -9
- package/src/utilities/dom-builder-helpers/form-tags.js +1 -1
- package/design-tokens/tokens/design-tokens/_src/alerts.minimo.tokens.jsonc +0 -28
- package/design-tokens/tokens/design-tokens/_src/badge.minimo.tokens.jsonc +0 -42
- package/design-tokens/tokens/design-tokens/_src/btn-close.minimo.tokens.jsonc +0 -102
- package/design-tokens/tokens/design-tokens/_src/btn-color-themes.minimo.tokens.jsonc +0 -182
- package/design-tokens/tokens/design-tokens/_src/btn.minimo.tokens.jsonc +0 -210
- package/design-tokens/tokens/design-tokens/_src/color-accent.minimo.tokens.jsonc +0 -16
- package/design-tokens/tokens/design-tokens/_src/color-neutral.minimo.tokens.jsonc +0 -18
- package/design-tokens/tokens/design-tokens/_src/color-primary.minimo.tokens.jsonc +0 -18
- package/design-tokens/tokens/design-tokens/_src/color-secondary.minimo.tokens.jsonc +0 -18
- package/design-tokens/tokens/design-tokens/_src/flex-grid.minimo.tokens.jsonc +0 -24
- package/design-tokens/tokens/design-tokens/_src/form-validation.minimo.tokens.jsonc +0 -22
- package/design-tokens/tokens/design-tokens/_src/form.minimo.tokens.jsonc +0 -366
- package/design-tokens/tokens/design-tokens/_src/headings.minimo.tokens.jsonc +0 -70
- package/design-tokens/tokens/design-tokens/_src/layout.minimo.tokens.jsonc +0 -28
- package/design-tokens/tokens/design-tokens/_src/link.minimo.tokens.jsonc +0 -44
- package/design-tokens/tokens/design-tokens/_src/radius.minimo.tokens.jsonc +0 -42
- package/design-tokens/tokens/design-tokens/_src/shadows.minimo.tokens.jsonc +0 -19
- package/design-tokens/tokens/design-tokens/_src/size.minimo.tokens.jsonc +0 -46
- package/design-tokens/tokens/design-tokens/_src/status.minimo.tokens.jsonc +0 -98
- package/design-tokens/tokens/design-tokens/_src/switch.minimo.tokens.jsonc +0 -32
- package/design-tokens/tokens/design-tokens/_src/table.minimo.tokens.jsonc +0 -84
- package/design-tokens/tokens/design-tokens/_src/text.minimo.tokens.jsonc +0 -38
- package/design-tokens/tokens/design-tokens/_src/typography.minimo.tokens.jsonc +0 -110
- package/design-tokens/tokens/design-tokens/_src/utility.minimo.tokens.jsonc +0 -14
- package/design-tokens/tokens/design-tokens/_src/z-index.minimo.tokens.jsonc +0 -18
- package/design-tokens/tokens/src/components/inner-nav/inner-nav.minimo.tokens.jsonc +0 -20
- package/design-tokens/tokens/src/components/modal-alert/modal-alert.minimo.tokens.jsonc +0 -246
- package/design-tokens/tokens/src/components/modal-popup/modal-popup.minimo.tokens.jsonc +0 -128
- package/design-tokens/tokens/src/components/sf-macro/sf-macro.minimo.tokens.jsonc +0 -84
- package/design-tokens/tokens/src/components/slide-up-down-toggle/slide-up-down-toggle.minimo.tokens.jsonc +0 -14
- package/design-tokens/tokens/src/components/snackbar/snackbar.minimo.tokens.jsonc +0 -146
- package/design-tokens/tokens/src/components/spinner/spinner.minimo.tokens.jsonc +0 -24
- package/design-tokens/tokens/src/components/unsplash-page/unsplash-page.minimo.tokens.jsonc +0 -170
- package/design-tokens/tokens/src/web-components/s-datatable-component/simple-datatable.minimo.tokens.jsonc +0 -51
package/design-tokens/README.md
CHANGED
|
@@ -56,7 +56,7 @@ Read comments inside the sample file for more info.
|
|
|
56
56
|
|
|
57
57
|
Reads design token source files and generates:
|
|
58
58
|
- A **CSS file** containing all tokens as custom properties inside `:root { ... }`
|
|
59
|
-
- Optionally, one or more **W3C DTCG JSON/JSONC** files
|
|
59
|
+
- Optionally, one or more **W3C DTCG JSON/JSONC** files, independent of any specific consuming tool (usable in Penpot, Figma via plugin, Token Studio, ...).
|
|
60
60
|
|
|
61
61
|
#### Usage
|
|
62
62
|
|
|
@@ -129,32 +129,33 @@ Typography tokens are expanded into multiple properties:
|
|
|
129
129
|
|
|
130
130
|
The generated file is linted and auto-fixed with stylelint using the config specified in `stylelintConfigPath`.
|
|
131
131
|
|
|
132
|
-
####
|
|
132
|
+
#### JSON output
|
|
133
133
|
|
|
134
|
-
When `
|
|
134
|
+
When `jsonBuildPath` is set, the script also generates W3C DTCG token files, independent of any specific consuming tool (e.g. importable into Penpot via the [Design Tokens plugin](https://penpot.app/penpot-files/design-tokens), into Figma via a compatible plugin, or into Token Studio).
|
|
135
135
|
|
|
136
|
-
**Single file** (`
|
|
136
|
+
**Single file** (`jsonDestFile` is a string):
|
|
137
137
|
```
|
|
138
|
-
|
|
138
|
+
jsonBuildPath/tokens.jsonc ← all tokens in one file
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
**One file per source** (`
|
|
141
|
+
**One file per source** (`jsonDestFile` is `null` or omitted):
|
|
142
142
|
```
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
jsonBuildPath/colors.tokens.jsonc
|
|
144
|
+
jsonBuildPath/spacing.tokens.jsonc
|
|
145
|
+
jsonBuildPath/typography.tokens.jsonc
|
|
146
146
|
```
|
|
147
|
+
All files are written flat in `jsonBuildPath`, regardless of the subdirectory structure of the source files — no subdirectories are created. If two source files with different paths would produce the same output filename, the build fails with an error listing the colliding files instead of silently overwriting one with the other.
|
|
147
148
|
|
|
148
|
-
The `
|
|
149
|
+
The `jsonFormat` option controls the output:
|
|
149
150
|
|
|
150
151
|
| Value | Extension | Disclaimer header |
|
|
151
152
|
|---|---|---|
|
|
152
153
|
| `'jsonc'` | `.jsonc` | Yes |
|
|
153
154
|
| `'json'` | `.json` | No |
|
|
154
155
|
|
|
155
|
-
Alias references (`{color.brand.primary}`) are preserved in the output so
|
|
156
|
+
Alias references (`{color.brand.primary}`) are preserved in the output so consuming tools maintain token links between primitive and semantic tokens.
|
|
156
157
|
|
|
157
|
-
>
|
|
158
|
+
> JSON token files intentionally use a minimal set of transforms (no px→rem conversion, no shorthand expansion) because consuming tools generally expect original values and structured objects, not resolved CSS strings.
|
|
158
159
|
|
|
159
160
|
---
|
|
160
161
|
|
|
@@ -12,43 +12,49 @@ export default {
|
|
|
12
12
|
size: {
|
|
13
13
|
base: {
|
|
14
14
|
$value: 'clamp(.9rem, .6538rem + .3846vw, 1rem)',
|
|
15
|
-
$type: 'string'
|
|
15
|
+
$type: 'string',
|
|
16
|
+
$description: '64 to 90 rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiLjlyZW0iLCJtYXhGb250U2l6ZSI6IjFyZW0ifQ%3D%3D'
|
|
16
17
|
},
|
|
17
18
|
xxs: {
|
|
18
19
|
$value: 'clamp(.6rem, .3538rem + .3846vw, .7rem)',
|
|
19
|
-
$type: 'string'
|
|
20
|
+
$type: 'string',
|
|
21
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiLjZyZW0iLCJtYXhGb250U2l6ZSI6Ii43cmVtIn0%3D'
|
|
20
22
|
},
|
|
21
23
|
xs: {
|
|
22
24
|
$value: 'clamp(.7rem, .4538rem + .3846vw, .8rem)',
|
|
23
|
-
$type: 'string'
|
|
25
|
+
$type: 'string',
|
|
26
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiLjdyZW0iLCJtYXhGb250U2l6ZSI6Ii44cmVtIn0%3D'
|
|
24
27
|
},
|
|
25
28
|
sm: {
|
|
26
29
|
$value: 'clamp(.8rem, .5538rem + .3846vw, .9rem)',
|
|
27
|
-
$type: 'string'
|
|
30
|
+
$type: 'string',
|
|
31
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiLjhyZW0iLCJtYXhGb250U2l6ZSI6Ii45cmVtIn0%3D'
|
|
28
32
|
},
|
|
29
|
-
|
|
30
|
-
// $value: 'clamp(1rem, 0.6923rem + 0.4808vw, 1.125rem)',
|
|
31
|
-
// $type: 'string'
|
|
32
|
-
// },
|
|
33
|
+
|
|
33
34
|
md: {
|
|
34
35
|
$value: 'clamp(1.05rem, .6808rem + .5769vw, 1.2rem)',
|
|
35
|
-
$type: 'string'
|
|
36
|
+
$type: 'string',
|
|
37
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiMS4wNXJlbSIsIm1heEZvbnRTaXplIjoiMS4ycmVtIn0%3D'
|
|
36
38
|
},
|
|
37
39
|
lg: {
|
|
38
40
|
$value: 'clamp(1.2rem, .7077rem + .7692vw, 1.4rem)',
|
|
39
|
-
$type: 'string'
|
|
41
|
+
$type: 'string',
|
|
42
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiMS4ycmVtIiwibWF4Rm9udFNpemUiOiIxLjRyZW0ifQ%3D%3D'
|
|
40
43
|
},
|
|
41
44
|
xl: {
|
|
42
45
|
$value: 'clamp(1.6rem, 1.1077rem + .7692vw, 1.8rem)',
|
|
43
|
-
$type: 'string'
|
|
46
|
+
$type: 'string',
|
|
47
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiMS42cmVtIiwibWF4Rm9udFNpemUiOiIxLjhyZW0ifQ%3D%3D'
|
|
44
48
|
},
|
|
45
49
|
'2xl': {
|
|
46
50
|
$value: 'clamp(2rem, .7692rem + 1.9231vw, 2.5rem)',
|
|
47
|
-
$type: 'string'
|
|
51
|
+
$type: 'string',
|
|
52
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiMnJlbSIsIm1heEZvbnRTaXplIjoiMi41cmVtIn0%3D'
|
|
48
53
|
},
|
|
49
54
|
'3xl': {
|
|
50
55
|
$value: 'clamp(2.2rem, .2308rem + 3.0769vw, 3rem)',
|
|
51
|
-
$type: 'string'
|
|
56
|
+
$type: 'string',
|
|
57
|
+
$description: '64-90rem: https://clamp.font-size.app/?config=eyJyb290IjoiMTYiLCJtaW5XaWR0aCI6IjY0cmVtIiwibWF4V2lkdGgiOiI5MHJlbSIsIm1pbkZvbnRTaXplIjoiMi4ycmVtIiwibWF4Rm9udFNpemUiOiIzcmVtIn0%3D'
|
|
52
58
|
}
|
|
53
59
|
},
|
|
54
60
|
|
|
@@ -80,11 +80,12 @@ const config = {
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
// ---------------------------------------------------------------------------
|
|
83
|
-
//
|
|
83
|
+
// JSON TOKENS OUTPUT
|
|
84
84
|
// ---------------------------------------------------------------------------
|
|
85
|
-
// Optional design tokens output
|
|
86
|
-
// Generates one or more json(c) files in addition to the CSS output file
|
|
87
|
-
// in Penpot
|
|
85
|
+
// Optional design tokens output, independent of any specific consuming tool
|
|
86
|
+
// Generates one or more json(c) files in addition to the CSS output file —
|
|
87
|
+
// usable in Penpot, Figma (via plugin), Token Studio, or as an alternative
|
|
88
|
+
// to the .mjs source files
|
|
88
89
|
// ---------------------------------------------------------------------------
|
|
89
90
|
|
|
90
91
|
// How to handle math expressions in dimension token $values (e.g. "{size.base} * .25"):
|
|
@@ -92,21 +93,25 @@ const config = {
|
|
|
92
93
|
// 'calc' — wrap in CSS calc(): "calc({size.base} * .25)"
|
|
93
94
|
// 'resolve' — (default) evaluate numerically, inheriting the unit from the first referenced token
|
|
94
95
|
// e.g. {size.base} = 16px, "{size.base} * .25" -> "4px"
|
|
95
|
-
|
|
96
|
+
jsonExpression: 'resolve',
|
|
96
97
|
|
|
97
|
-
// Output format for
|
|
98
|
+
// Output format for the JSON token files: 'jsonc' or 'json'
|
|
98
99
|
// 'jsonc' -> adds a generated-file disclaimer header, uses .jsonc extension
|
|
99
100
|
// 'json' -> plain JSON, no disclaimer
|
|
100
|
-
|
|
101
|
+
jsonFormat: 'jsonc',
|
|
101
102
|
|
|
102
|
-
// Output directory for the
|
|
103
|
+
// Output directory for the JSON design token files.
|
|
103
104
|
// Set to null (or omit) to disable json output entirely.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
// In multi-file mode (jsonDestFile: null), all files are written flat in
|
|
106
|
+
// this directory (no subdirectories mirroring the source tree); the build
|
|
107
|
+
// fails with an error if two source files would produce the same filename.
|
|
108
|
+
jsonBuildPath: null, // or './path/to/generated/design-tokens-dir',
|
|
109
|
+
|
|
110
|
+
// Base name (without extension) for the aggregated JSON output file.
|
|
111
|
+
// The extension is added automatically based on jsonFormat.
|
|
112
|
+
// Set to null (or omit) to generate one file per .mjs source file instead
|
|
113
|
+
// (all placed flat in jsonBuildPath — see jsonBuildPath above).
|
|
114
|
+
jsonDestFile: null, // or 'tokens' (or else),
|
|
110
115
|
|
|
111
116
|
|
|
112
117
|
// ---------------------------------------------------------------------------
|
|
@@ -51,29 +51,29 @@ export const destFile = buildConfig.destFile;
|
|
|
51
51
|
// Stylelint
|
|
52
52
|
export const stylelintConfigPath = resolveFromConfig(buildConfig.stylelintConfigPath);
|
|
53
53
|
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
export const
|
|
54
|
+
// JSON — platform is active only if jsonBuildPath is set in the config
|
|
55
|
+
const jsonBuildPathRaw = buildConfig.jsonBuildPath ?? null;
|
|
56
|
+
export const jsonBuildPath = jsonBuildPathRaw ? resolveFromConfig(jsonBuildPathRaw) : null;
|
|
57
57
|
|
|
58
|
-
//
|
|
58
|
+
// jsonDestFile: base name (no extension) for the aggregated output file.
|
|
59
59
|
// null → one file per source (mirrors the source structure)
|
|
60
60
|
// string → single aggregated file (e.g. 'tokens' → 'tokens.jsonc')
|
|
61
|
-
// The extension is added automatically based on
|
|
62
|
-
export const
|
|
61
|
+
// The extension is added automatically based on jsonFormat.
|
|
62
|
+
export const jsonDestFile = buildConfig.jsonDestFile ?? null;
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// jsonFormat: controls output format for the generated JSON token files.
|
|
65
65
|
// 'jsonc' → .jsonc extension + generated-file disclaimer header
|
|
66
66
|
// 'json' → plain .json, no disclaimer
|
|
67
67
|
// Any value other than 'jsonc' is normalised to 'json'.
|
|
68
|
-
export const
|
|
68
|
+
export const jsonFormat = buildConfig.jsonFormat === 'jsonc' ? 'jsonc' : 'json';
|
|
69
69
|
|
|
70
|
-
//
|
|
70
|
+
// jsonExpression: controls how math expressions in dimension token values are handled.
|
|
71
71
|
// 'keep' (default) — write the expression as-is
|
|
72
72
|
// 'calc' — wrap in CSS calc()
|
|
73
73
|
// 'resolve' — evaluate numerically; unit inherited from the first referenced token
|
|
74
74
|
const VALID_EXPR_MODES = ['keep', 'calc', 'resolve'];
|
|
75
|
-
export const
|
|
76
|
-
? buildConfig.
|
|
75
|
+
export const jsonExpression = VALID_EXPR_MODES.includes(buildConfig.jsonExpression)
|
|
76
|
+
? buildConfig.jsonExpression
|
|
77
77
|
: 'resolve';
|
|
78
78
|
|
|
79
79
|
// pxToRem: if true (default), dimension token values expressed in px are
|
|
@@ -131,6 +131,17 @@ const buildTypography = (tokenName, original, resolveRefs) => {
|
|
|
131
131
|
return lines.join('\n');
|
|
132
132
|
};
|
|
133
133
|
|
|
134
|
+
// Builds a trailing ` /* ... */` comment from a token's $description, to be
|
|
135
|
+
// appended at the end of its generated custom prop declaration (same line).
|
|
136
|
+
// Newlines are collapsed (declarations are parsed one line at a time, see
|
|
137
|
+
// parseCustomProps in ../merge-css.mjs) and `*/` is escaped so the comment
|
|
138
|
+
// can't be closed early by its own content.
|
|
139
|
+
const buildDescriptionComment = (description) => {
|
|
140
|
+
if (!description) return '';
|
|
141
|
+
const flat = String(description).replace(/\s*\n\s*/g, ' ').trim().replace(/\*\//g, '* /');
|
|
142
|
+
return flat ? ` /* ${flat} */` : '';
|
|
143
|
+
};
|
|
144
|
+
|
|
134
145
|
// Builds a customPropsGroups title comment, e.g.:
|
|
135
146
|
// /* ****** LAYOUT ****** */
|
|
136
147
|
// Dashes fill out a fixed total width so titles line up regardless of name length.
|
|
@@ -185,9 +196,15 @@ StyleDictionary.registerFormat({
|
|
|
185
196
|
.flatMap((token) => {
|
|
186
197
|
const type = token.$type ?? token.type;
|
|
187
198
|
const orig = token.original?.$value ?? token.original?.value;
|
|
199
|
+
const description = token.$description ?? token.description ?? token.comment;
|
|
200
|
+
const commentSuffix = buildDescriptionComment(description);
|
|
188
201
|
|
|
189
202
|
if (type === 'typography') {
|
|
190
|
-
|
|
203
|
+
const typographyLines = buildTypography(token.name, orig ?? {}, resolveRefs)
|
|
204
|
+
.split('\n')
|
|
205
|
+
.map((line) => `${line}${commentSuffix}`)
|
|
206
|
+
.join('\n');
|
|
207
|
+
return [typographyLines];
|
|
191
208
|
}
|
|
192
209
|
|
|
193
210
|
let value;
|
|
@@ -230,7 +247,7 @@ StyleDictionary.registerFormat({
|
|
|
230
247
|
value = String(token.$value ?? token.value);
|
|
231
248
|
}
|
|
232
249
|
|
|
233
|
-
return [` --${token.name}: ${value}
|
|
250
|
+
return [` --${token.name}: ${value};${commentSuffix}`];
|
|
234
251
|
});
|
|
235
252
|
|
|
236
253
|
// Build a name → declaration-tail map from the declarations generated
|
|
@@ -1,30 +1,33 @@
|
|
|
1
|
-
// build-tokens-src/formats/
|
|
2
|
-
// Registers the 'json/
|
|
1
|
+
// build-tokens-src/formats/json.mjs
|
|
2
|
+
// Registers the 'json/tokens' format and exports buildJsonFiles() and
|
|
3
3
|
// collectConcreteFilePaths().
|
|
4
4
|
//
|
|
5
|
-
// The format produces a W3C DTCG nested file
|
|
6
|
-
//
|
|
5
|
+
// The format produces a W3C DTCG nested JSON(C) file, independent of any
|
|
6
|
+
// specific consuming tool (usable in Penpot, Figma via plugin, Token Studio,
|
|
7
|
+
// Supernova, or any other DTCG-compatible tool).
|
|
7
8
|
//
|
|
8
|
-
// Output format is controlled by the
|
|
9
|
+
// Output format is controlled by the jsonFormat option in the project config:
|
|
9
10
|
// 'jsonc' -> .jsonc extension + generated-file disclaimer header
|
|
10
11
|
// 'json' -> plain .json, no disclaimer
|
|
11
12
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
13
|
+
// buildJsonFiles() builds the `files` array for the json platform:
|
|
14
|
+
// jsonDestFile set -> single aggregated file
|
|
15
|
+
// jsonDestFile null -> one file per concrete source file, all placed flat
|
|
16
|
+
// in jsonBuildPath (no subdirectories) — see the
|
|
17
|
+
// collision check in buildJsonFiles()
|
|
16
18
|
//
|
|
17
|
-
// ── Expression handling (
|
|
19
|
+
// ── Expression handling (jsonExpression option) ─────────────────────────────
|
|
18
20
|
//
|
|
19
21
|
// Dimension tokens may carry math expressions in their $value, e.g.:
|
|
20
22
|
// { $type: "dimension", $value: "{size.base} * .25" }
|
|
21
23
|
//
|
|
22
|
-
// The
|
|
24
|
+
// The jsonExpression config option controls how these are handled:
|
|
23
25
|
//
|
|
24
|
-
// 'keep' (default) — write the expression as-is;
|
|
25
|
-
//
|
|
26
|
+
// 'keep' (default) — write the expression as-is; the consuming tool is
|
|
27
|
+
// expected to evaluate it.
|
|
26
28
|
// 'calc' — wrap in a CSS calc(): "calc({size.base} * .25)"
|
|
27
|
-
// aliases are left as {references} for
|
|
29
|
+
// aliases are left as {references} for the consuming
|
|
30
|
+
// tool to resolve.
|
|
28
31
|
// 'resolve' — evaluate the expression numerically and write a concrete value.
|
|
29
32
|
// The unit is inherited from the first dimension token
|
|
30
33
|
// referenced in the expression (e.g. {size.base} = 16px
|
|
@@ -34,7 +37,7 @@
|
|
|
34
37
|
import StyleDictionary from 'style-dictionary';
|
|
35
38
|
import path from 'node:path';
|
|
36
39
|
|
|
37
|
-
// Disclaimer prepended to every generated file when
|
|
40
|
+
// Disclaimer prepended to every generated file when jsonFormat is 'jsonc'
|
|
38
41
|
const DISCLAIMER = [
|
|
39
42
|
'// -----------------------------------------------------------------------',
|
|
40
43
|
'// Generated file — do not edit manually.',
|
|
@@ -126,10 +129,10 @@ const toCalc = (orig) => `calc(${orig})`;
|
|
|
126
129
|
// ── Format ───────────────────────────────────────────────────────────────────
|
|
127
130
|
|
|
128
131
|
StyleDictionary.registerFormat({
|
|
129
|
-
name: 'json/
|
|
132
|
+
name: 'json/tokens',
|
|
130
133
|
format: ({ dictionary, options }) => {
|
|
131
134
|
const root = {};
|
|
132
|
-
const expressionMode = options.
|
|
135
|
+
const expressionMode = options.jsonExpression ?? 'keep'; // 'keep' | 'calc' | 'resolve'
|
|
133
136
|
const tokenMap = (expressionMode === 'resolve') ? makeTokenMap(dictionary) : null;
|
|
134
137
|
|
|
135
138
|
for (const token of dictionary.allTokens) {
|
|
@@ -161,7 +164,7 @@ StyleDictionary.registerFormat({
|
|
|
161
164
|
} else {
|
|
162
165
|
// Resolution failed — keep the original and emit a warning
|
|
163
166
|
// eslint-disable-next-line no-console
|
|
164
|
-
console.warn(`[build-tokens]
|
|
167
|
+
console.warn(`[build-tokens] json: could not resolve expression "${orig}" for token "${token.path.join('.')}". Keeping original.`);
|
|
165
168
|
value = orig;
|
|
166
169
|
}
|
|
167
170
|
} else if (expressionMode === 'calc') {
|
|
@@ -173,7 +176,7 @@ StyleDictionary.registerFormat({
|
|
|
173
176
|
}
|
|
174
177
|
} else {
|
|
175
178
|
// Non-expression value: preserve {references} wherever they appear.
|
|
176
|
-
// - Pure alias: "{some.token}" → kept as-is
|
|
179
|
+
// - Pure alias: "{some.token}" → kept as-is to preserve token links
|
|
177
180
|
// - CSS function: "color-mix(in srgb, {some.token} 60%, #000)" → kept as-is
|
|
178
181
|
// - Plain value: "#ff0000", "16px", … → use the resolved $value
|
|
179
182
|
const containsRef = typeof orig === 'string' && orig.includes('{');
|
|
@@ -193,23 +196,6 @@ StyleDictionary.registerFormat({
|
|
|
193
196
|
},
|
|
194
197
|
});
|
|
195
198
|
|
|
196
|
-
// ── commonDir ────────────────────────────────────────────────────────────────
|
|
197
|
-
// Returns the longest common directory prefix shared by all given file paths.
|
|
198
|
-
// Used to compute relative paths that mirror the source subdirectory structure.
|
|
199
|
-
|
|
200
|
-
const commonDir = (filePaths) => {
|
|
201
|
-
if (filePaths.length === 0) return '';
|
|
202
|
-
if (filePaths.length === 1) return path.dirname(filePaths[0]);
|
|
203
|
-
|
|
204
|
-
const dirs = filePaths.map(fp => path.dirname(path.resolve(fp)));
|
|
205
|
-
const parts = dirs[0].split(path.sep);
|
|
206
|
-
|
|
207
|
-
let i = 0;
|
|
208
|
-
while (i < parts.length && dirs.every(d => d.split(path.sep)[i] === parts[i])) i++;
|
|
209
|
-
|
|
210
|
-
return parts.slice(0, i).join(path.sep);
|
|
211
|
-
};
|
|
212
|
-
|
|
213
199
|
// ── collectConcreteFilePaths ──────────────────────────────────────────────────
|
|
214
200
|
//
|
|
215
201
|
// Extracts the unique concrete file paths from a Style Dictionary instance.
|
|
@@ -224,42 +210,59 @@ export const collectConcreteFilePaths = async (sd) => {
|
|
|
224
210
|
return [...paths].sort();
|
|
225
211
|
};
|
|
226
212
|
|
|
227
|
-
// ──
|
|
213
|
+
// ── buildJsonFiles ──────────────────────────────────────────────────────────
|
|
228
214
|
//
|
|
229
215
|
// @param {string[]} concreteFilePaths From collectConcreteFilePaths()
|
|
230
|
-
// @param {string|null}
|
|
231
|
-
// @param {'json'|'jsonc'}
|
|
232
|
-
// @param {'keep'|'calc'|'resolve'}
|
|
216
|
+
// @param {string|null} jsonDestFile Aggregated file base name, or null
|
|
217
|
+
// @param {'json'|'jsonc'} jsonFormat Output format
|
|
218
|
+
// @param {'keep'|'calc'|'resolve'} jsonExpression Expression handling mode
|
|
233
219
|
// @returns {object[]} File descriptors for the Style Dictionary platform
|
|
234
220
|
|
|
235
|
-
export const
|
|
221
|
+
export const buildJsonFiles = (
|
|
236
222
|
concreteFilePaths,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
223
|
+
jsonDestFile,
|
|
224
|
+
jsonFormat = 'json',
|
|
225
|
+
jsonExpression = 'keep'
|
|
240
226
|
) => {
|
|
241
|
-
const ext =
|
|
242
|
-
const jsonc =
|
|
227
|
+
const ext = jsonFormat === 'jsonc' ? '.jsonc' : '.json';
|
|
228
|
+
const jsonc = jsonFormat === 'jsonc';
|
|
243
229
|
|
|
244
|
-
if (
|
|
230
|
+
if (jsonDestFile) {
|
|
245
231
|
return [{
|
|
246
|
-
destination:
|
|
247
|
-
format: 'json/
|
|
248
|
-
options: { jsonc,
|
|
232
|
+
destination: jsonDestFile + ext,
|
|
233
|
+
format: 'json/tokens',
|
|
234
|
+
options: { jsonc, jsonExpression },
|
|
249
235
|
}];
|
|
250
236
|
}
|
|
251
237
|
|
|
252
|
-
|
|
238
|
+
// One file per source, all placed flat in jsonBuildPath — no subdirectories
|
|
239
|
+
// mirroring the source tree. Since source files can share a basename across
|
|
240
|
+
// different directories (e.g. two components each with their own
|
|
241
|
+
// <name>.minimo.tokens.mjs), fail loudly on collisions instead of letting
|
|
242
|
+
// one silently overwrite the other.
|
|
243
|
+
const pathsByDestName = new Map();
|
|
244
|
+
for (const filePath of concreteFilePaths) {
|
|
245
|
+
const destName = path.basename(filePath).replace(/\.[^.]+$/, '') + ext;
|
|
246
|
+
if (!pathsByDestName.has(destName)) pathsByDestName.set(destName, []);
|
|
247
|
+
pathsByDestName.get(destName).push(filePath);
|
|
248
|
+
}
|
|
253
249
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
250
|
+
const collisions = [...pathsByDestName.entries()].filter(([, paths]) => paths.length > 1);
|
|
251
|
+
if (collisions.length > 0) {
|
|
252
|
+
const details = collisions
|
|
253
|
+
.map(([destName, paths]) => ` "${destName}" <- ${paths.join(', ')}`)
|
|
254
|
+
.join('\n');
|
|
255
|
+
throw new Error(
|
|
256
|
+
`[build-tokens] json: output filename collision — these source files would overwrite each other (json output files are flattened into a single directory):\n${details}`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
258
259
|
|
|
260
|
+
return concreteFilePaths.map((filePath) => {
|
|
261
|
+
const absPath = path.resolve(filePath);
|
|
259
262
|
return {
|
|
260
|
-
destination:
|
|
261
|
-
format: 'json/
|
|
262
|
-
options: { jsonc,
|
|
263
|
+
destination: path.basename(filePath).replace(/\.[^.]+$/, '') + ext,
|
|
264
|
+
format: 'json/tokens',
|
|
265
|
+
options: { jsonc, jsonExpression },
|
|
263
266
|
filter: (token) => {
|
|
264
267
|
const fp = token.filePath ? path.resolve(token.filePath) : '';
|
|
265
268
|
return fp === absPath;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// build-tokens-src/platforms.mjs
|
|
2
2
|
// Builds and returns the `platforms` object for the Style Dictionary config.
|
|
3
3
|
//
|
|
4
|
-
// The `
|
|
5
|
-
// Output format (json/jsonc) and file splitting are delegated to
|
|
4
|
+
// The `json` platform is included only if jsonBuildPath is set.
|
|
5
|
+
// Output format (json/jsonc) and file splitting are delegated to buildJsonFiles().
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { buildJsonFiles } from './formats/json.mjs';
|
|
8
8
|
|
|
9
9
|
/** @typedef {import('style-dictionary/types').PlatformConfig} PlatformConfig */
|
|
10
10
|
|
|
@@ -24,10 +24,11 @@ export const CSS_TRANSFORMS = [
|
|
|
24
24
|
'typography/css',
|
|
25
25
|
];
|
|
26
26
|
|
|
27
|
-
// Transforms applied to the
|
|
28
|
-
// Kept minimal on purpose:
|
|
29
|
-
// and alias references ({...}) must be preserved to maintain
|
|
30
|
-
|
|
27
|
+
// Transforms applied to the JSON platform.
|
|
28
|
+
// Kept minimal on purpose: consuming tools expect original values (e.g. "16px"
|
|
29
|
+
// not "1rem") and alias references ({...}) must be preserved to maintain
|
|
30
|
+
// token links.
|
|
31
|
+
const JSON_TRANSFORMS = [
|
|
31
32
|
'attribute/cti',
|
|
32
33
|
'name/kebab',
|
|
33
34
|
'color/css-modern',
|
|
@@ -37,13 +38,13 @@ const PENPOT_TRANSFORMS = [
|
|
|
37
38
|
* @param {object} opts
|
|
38
39
|
* @param {string} opts.buildPath Absolute path for CSS output directory
|
|
39
40
|
* @param {string} opts.destFile CSS output filename (e.g. "tokens.css")
|
|
40
|
-
* @param {string|null} opts.
|
|
41
|
-
* @param {string|null} opts.
|
|
42
|
-
* @param {'json'|'jsonc'} opts.
|
|
41
|
+
* @param {string|null} opts.jsonBuildPath Absolute path for JSON output (null = disabled)
|
|
42
|
+
* @param {string|null} opts.jsonDestFile Base name for aggregated file; null = one file per source
|
|
43
|
+
* @param {'json'|'jsonc'} opts.jsonFormat Output format for JSON files
|
|
43
44
|
* @param {string[]} opts.concreteFilePaths Concrete (expanded) source file paths for multi-file mode.
|
|
44
45
|
* Collected from sd.allTokens after SD initialisation.
|
|
45
|
-
* Ignored when
|
|
46
|
-
* @param {'keep'|'calc'|'resolve'} opts.
|
|
46
|
+
* Ignored when jsonDestFile is set.
|
|
47
|
+
* @param {'keep'|'calc'|'resolve'} opts.jsonExpression How to handle math expressions in dimension tokens.
|
|
47
48
|
* @param {{name: string, prefixes: string[]}[]} opts.customPropsGroups Named groups of name prefixes, moved to the top of the CSS output in list order.
|
|
48
49
|
* @param {boolean} opts.pxToRem If false, skips the px→rem transform on the CSS platform. Default: true.
|
|
49
50
|
* @returns {Record<string, PlatformConfig>} platforms object ready for Style Dictionary config
|
|
@@ -51,10 +52,10 @@ const PENPOT_TRANSFORMS = [
|
|
|
51
52
|
export const buildPlatforms = ({
|
|
52
53
|
buildPath,
|
|
53
54
|
destFile,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
jsonBuildPath,
|
|
56
|
+
jsonDestFile,
|
|
57
|
+
jsonFormat,
|
|
58
|
+
jsonExpression = 'keep',
|
|
58
59
|
concreteFilePaths = [],
|
|
59
60
|
customPropsGroups = [],
|
|
60
61
|
pxToRem = true,
|
|
@@ -82,11 +83,11 @@ export const buildPlatforms = ({
|
|
|
82
83
|
},
|
|
83
84
|
};
|
|
84
85
|
|
|
85
|
-
if (
|
|
86
|
-
platforms.
|
|
87
|
-
buildPath:
|
|
88
|
-
transforms:
|
|
89
|
-
files:
|
|
86
|
+
if (jsonBuildPath) {
|
|
87
|
+
platforms.json = {
|
|
88
|
+
buildPath: jsonBuildPath + '/',
|
|
89
|
+
transforms: JSON_TRANSFORMS,
|
|
90
|
+
files: buildJsonFiles(concreteFilePaths, jsonDestFile, jsonFormat, jsonExpression),
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
93
|
|