dispersa 1.2.0 → 1.3.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/README.md +1 -2
- package/dist/{builders.d.cts → builders-B7_pBy58.d.cts} +180 -6
- package/dist/{builders.d.ts → builders-BEoMaLal.d.ts} +180 -6
- package/dist/{types-8MLtztK3.d.ts → config-schemas-DnEBhIg0.d.cts} +1 -158
- package/dist/{types-BHBHRm0a.d.cts → config-schemas-DnEBhIg0.d.ts} +1 -158
- package/dist/dispersa-DF2ZkG2O.d.ts +567 -0
- package/dist/dispersa-DJeCN0cP.d.cts +567 -0
- package/dist/index.cjs +2117 -2098
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +2117 -2098
- package/dist/index.js.map +1 -1
- package/dist/{lint.cjs → lint/index.cjs} +2 -2
- package/dist/lint/index.cjs.map +1 -0
- package/dist/{lint.d.ts → lint/index.d.cts} +8 -7
- package/dist/{lint.d.cts → lint/index.d.ts} +8 -7
- package/dist/{lint.js → lint/index.js} +2 -2
- package/dist/lint/index.js.map +1 -0
- package/dist/{renderers.d.ts → output-tree-BRbfWSmG.d.ts} +3 -10
- package/dist/{renderers.d.cts → output-tree-Hmi77EMv.d.cts} +3 -10
- package/dist/{builders.cjs → outputs/builders.cjs} +30 -30
- package/dist/outputs/builders.cjs.map +1 -0
- package/dist/outputs/builders.d.cts +7 -0
- package/dist/outputs/builders.d.ts +7 -0
- package/dist/{builders.js → outputs/builders.js} +30 -30
- package/dist/outputs/builders.js.map +1 -0
- package/dist/outputs/index.cjs +3750 -0
- package/dist/outputs/index.cjs.map +1 -0
- package/dist/outputs/index.d.cts +73 -0
- package/dist/outputs/index.d.ts +73 -0
- package/dist/outputs/index.js +3730 -0
- package/dist/outputs/index.js.map +1 -0
- package/dist/{filters.cjs → processing/filters/index.cjs} +24 -18
- package/dist/processing/filters/index.cjs.map +1 -0
- package/dist/processing/filters/index.d.cts +36 -0
- package/dist/processing/filters/index.d.ts +36 -0
- package/dist/{filters.js → processing/filters/index.js} +24 -18
- package/dist/processing/filters/index.js.map +1 -0
- package/dist/{preprocessors.cjs → processing/preprocessors/index.cjs} +3 -3
- package/dist/processing/preprocessors/index.cjs.map +1 -0
- package/dist/processing/preprocessors/index.d.cts +29 -0
- package/dist/processing/preprocessors/index.d.ts +29 -0
- package/dist/{preprocessors.js → processing/preprocessors/index.js} +3 -3
- package/dist/processing/preprocessors/index.js.map +1 -0
- package/dist/{transforms.cjs → processing/transforms/index.cjs} +2 -7
- package/dist/processing/transforms/index.cjs.map +1 -0
- package/dist/{transforms.d.ts → processing/transforms/index.d.cts} +2 -2
- package/dist/{transforms.d.cts → processing/transforms/index.d.ts} +2 -2
- package/dist/{transforms.js → processing/transforms/index.js} +2 -7
- package/dist/processing/transforms/index.js.map +1 -0
- package/dist/{errors.cjs → shared/errors/index.cjs} +12 -2
- package/dist/shared/errors/index.cjs.map +1 -0
- package/dist/{errors-qT4sJgSA.d.ts → shared/errors/index.d.cts} +11 -2
- package/dist/{errors-qT4sJgSA.d.cts → shared/errors/index.d.ts} +11 -2
- package/dist/{errors.js → shared/errors/index.js} +12 -3
- package/dist/shared/errors/index.js.map +1 -0
- package/dist/types-B0cI70Bt.d.cts +453 -0
- package/dist/types-BxDEUCos.d.ts +453 -0
- package/dist/types-DUc4vLZH.d.cts +36 -0
- package/dist/types-s3UoDRKl.d.ts +36 -0
- package/package.json +26 -36
- package/dist/android-CRDfSB3_.d.cts +0 -126
- package/dist/android-DANJjjPO.d.ts +0 -126
- package/dist/builders.cjs.map +0 -1
- package/dist/builders.js.map +0 -1
- package/dist/dispersa-BC1kDF5u.d.ts +0 -118
- package/dist/dispersa-DL3J_Pmz.d.cts +0 -118
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.d.cts +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/filters.cjs.map +0 -1
- package/dist/filters.d.cts +0 -83
- package/dist/filters.d.ts +0 -83
- package/dist/filters.js.map +0 -1
- package/dist/index-Dajm5rvM.d.ts +0 -895
- package/dist/index-De6SjZYH.d.cts +0 -895
- package/dist/lint.cjs.map +0 -1
- package/dist/lint.js.map +0 -1
- package/dist/preprocessors.cjs.map +0 -1
- package/dist/preprocessors.d.cts +0 -17
- package/dist/preprocessors.d.ts +0 -17
- package/dist/preprocessors.js.map +0 -1
- package/dist/renderers.cjs +0 -28
- package/dist/renderers.cjs.map +0 -1
- package/dist/renderers.js +0 -24
- package/dist/renderers.js.map +0 -1
- package/dist/transforms.cjs.map +0 -1
- package/dist/transforms.js.map +0 -1
|
@@ -0,0 +1,3730 @@
|
|
|
1
|
+
import { converter, formatHex8, formatHex } from 'culori';
|
|
2
|
+
import prettier from 'prettier';
|
|
3
|
+
import { kebabCase } from 'change-case';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/shared/errors/index.ts
|
|
16
|
+
var DispersaError, ConfigurationError, BasePermutationError;
|
|
17
|
+
var init_errors = __esm({
|
|
18
|
+
"src/shared/errors/index.ts"() {
|
|
19
|
+
DispersaError = class extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "DispersaError";
|
|
23
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
24
|
+
Error.captureStackTrace(this, this.constructor);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
ConfigurationError = class extends DispersaError {
|
|
29
|
+
constructor(message) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = "ConfigurationError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
BasePermutationError = class extends DispersaError {
|
|
35
|
+
constructor(message = "Base permutation determination failed. Define a default modifier in resolver.") {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "BasePermutationError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// src/shared/utils/token-utils.ts
|
|
44
|
+
function stripInternalTokenMetadata(tokens) {
|
|
45
|
+
const cleaned = {};
|
|
46
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
47
|
+
const { _isAlias: _alias, _sourceModifier: _source, _sourceSet, ...rest } = token;
|
|
48
|
+
cleaned[name] = rest;
|
|
49
|
+
}
|
|
50
|
+
return cleaned;
|
|
51
|
+
}
|
|
52
|
+
function getSortedTokenEntries(tokens) {
|
|
53
|
+
return Object.entries(tokens).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
|
|
54
|
+
}
|
|
55
|
+
function buildNestedTokenObject(tokens, extractValue) {
|
|
56
|
+
const result = {};
|
|
57
|
+
for (const [, token] of getSortedTokenEntries(tokens)) {
|
|
58
|
+
setNestedValue(result, token.path, extractValue(token));
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
function setNestedValue(root, path, value) {
|
|
63
|
+
let current = root;
|
|
64
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
65
|
+
const part = path[i];
|
|
66
|
+
if (part == null) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!(part in current)) {
|
|
70
|
+
current[part] = {};
|
|
71
|
+
}
|
|
72
|
+
current = current[part];
|
|
73
|
+
}
|
|
74
|
+
const lastPart = path[path.length - 1];
|
|
75
|
+
if (lastPart != null) {
|
|
76
|
+
current[lastPart] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function getPureAliasReferenceName(value) {
|
|
80
|
+
if (typeof value !== "string") {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
const match = /^\{([^}]+)\}$/.exec(value);
|
|
84
|
+
return match?.[1]?.trim();
|
|
85
|
+
}
|
|
86
|
+
var init_token_utils = __esm({
|
|
87
|
+
"src/shared/utils/token-utils.ts"() {
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// src/outputs/utils.ts
|
|
92
|
+
function sanitizeDataAttributeName(value) {
|
|
93
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
94
|
+
}
|
|
95
|
+
function escapeCssString(value) {
|
|
96
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, " ");
|
|
97
|
+
}
|
|
98
|
+
function groupTokensByType(tokens, typeGroupMap) {
|
|
99
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const [, token] of getSortedTokenEntries(tokens)) {
|
|
101
|
+
const groupName = typeGroupMap[token.$type ?? ""] ?? "Other";
|
|
102
|
+
const existing = groupMap.get(groupName) ?? [];
|
|
103
|
+
existing.push(token);
|
|
104
|
+
groupMap.set(groupName, existing);
|
|
105
|
+
}
|
|
106
|
+
return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
|
|
107
|
+
name,
|
|
108
|
+
tokens: groupTokens
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
function indentStr(width, level) {
|
|
112
|
+
return " ".repeat(width * level);
|
|
113
|
+
}
|
|
114
|
+
function buildGeneratedFileHeader() {
|
|
115
|
+
return [
|
|
116
|
+
"// Generated by Dispersa - do not edit manually",
|
|
117
|
+
"// https://github.com/dispersa-core/dispersa"
|
|
118
|
+
].join("\n");
|
|
119
|
+
}
|
|
120
|
+
function toSafeIdentifier(name, keywords, capitalize) {
|
|
121
|
+
const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
|
|
122
|
+
const cased = capitalize ? camel.charAt(0).toUpperCase() + camel.slice(1) : camel.charAt(0).toLowerCase() + camel.slice(1);
|
|
123
|
+
const safe = /^\d/.test(cased) ? `_${cased}` : cased;
|
|
124
|
+
const keyCheck = capitalize ? safe.charAt(0).toLowerCase() + safe.slice(1) : safe;
|
|
125
|
+
return keywords.has(keyCheck) ? `\`${safe}\`` : safe;
|
|
126
|
+
}
|
|
127
|
+
function normalizeModifierInputs(inputs) {
|
|
128
|
+
const normalized = {};
|
|
129
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
130
|
+
normalized[key.toLowerCase()] = value.toLowerCase();
|
|
131
|
+
}
|
|
132
|
+
return normalized;
|
|
133
|
+
}
|
|
134
|
+
function assertFileRequired(buildPath, outputFile, outputName, presetLabel) {
|
|
135
|
+
const requiresFile = buildPath !== void 0 && buildPath !== "";
|
|
136
|
+
if (!outputFile && requiresFile) {
|
|
137
|
+
throw new ConfigurationError(
|
|
138
|
+
`Output "${outputName}": file is required for ${presetLabel} output`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function buildStablePermutationKey(modifierInputs, dimensions) {
|
|
143
|
+
return dimensions.map((dimension) => `${dimension}=${modifierInputs[dimension] ?? ""}`).join("|");
|
|
144
|
+
}
|
|
145
|
+
function resolveSelector(selector, modifierName, context, isBase, allModifierInputs) {
|
|
146
|
+
if (typeof selector === "function") {
|
|
147
|
+
return selector(modifierName, context, isBase, allModifierInputs);
|
|
148
|
+
}
|
|
149
|
+
if (typeof selector === "string") {
|
|
150
|
+
return selector;
|
|
151
|
+
}
|
|
152
|
+
if (isBase) {
|
|
153
|
+
return ":root";
|
|
154
|
+
}
|
|
155
|
+
const attrName = sanitizeDataAttributeName(modifierName);
|
|
156
|
+
const attrValue = escapeCssString(context);
|
|
157
|
+
return `[data-${attrName}="${attrValue}"]`;
|
|
158
|
+
}
|
|
159
|
+
function resolveMediaQuery(mediaQuery, modifierName, context, isBase, allModifierInputs) {
|
|
160
|
+
if (typeof mediaQuery === "function") {
|
|
161
|
+
return mediaQuery(modifierName, context, isBase, allModifierInputs);
|
|
162
|
+
}
|
|
163
|
+
if (typeof mediaQuery === "string") {
|
|
164
|
+
return mediaQuery;
|
|
165
|
+
}
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
function stripInternalMetadata(tokens) {
|
|
169
|
+
return stripInternalTokenMetadata(tokens);
|
|
170
|
+
}
|
|
171
|
+
function generatePermutationKey(modifierInputs, resolver, isBase) {
|
|
172
|
+
if (isBase) {
|
|
173
|
+
return "base";
|
|
174
|
+
}
|
|
175
|
+
const metadata = buildMetadata(resolver);
|
|
176
|
+
const normalizedInputs = normalizeModifierInputs(modifierInputs);
|
|
177
|
+
const defaults = metadata.defaults;
|
|
178
|
+
const differences = [];
|
|
179
|
+
for (const dimension of metadata.dimensions) {
|
|
180
|
+
const value = normalizedInputs[dimension];
|
|
181
|
+
if (value !== void 0 && value !== defaults[dimension]) {
|
|
182
|
+
differences.push({ name: dimension, value });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (differences.length === 1 && differences[0]) {
|
|
186
|
+
const diff = differences[0];
|
|
187
|
+
return `${diff.name}-${diff.value}`;
|
|
188
|
+
}
|
|
189
|
+
return buildStablePermutationKey(normalizedInputs, metadata.dimensions);
|
|
190
|
+
}
|
|
191
|
+
function isBasePermutation(modifierInputs, defaults) {
|
|
192
|
+
const normalizedInputs = normalizeModifierInputs(modifierInputs);
|
|
193
|
+
const normalizedDefaults = normalizeModifierInputs(defaults);
|
|
194
|
+
return Object.entries(normalizedDefaults).every(([key, value]) => normalizedInputs[key] === value);
|
|
195
|
+
}
|
|
196
|
+
function buildInMemoryOutputKey(params) {
|
|
197
|
+
const { outputName, extension, modifierInputs, resolver, defaults } = params;
|
|
198
|
+
const permutationKey = generatePermutationKey(
|
|
199
|
+
modifierInputs,
|
|
200
|
+
resolver,
|
|
201
|
+
isBasePermutation(modifierInputs, defaults)
|
|
202
|
+
);
|
|
203
|
+
return `${outputName}-${permutationKey}.${extension}`;
|
|
204
|
+
}
|
|
205
|
+
function buildMetadata(resolver) {
|
|
206
|
+
const metadata = {
|
|
207
|
+
dimensions: [],
|
|
208
|
+
defaults: {}
|
|
209
|
+
};
|
|
210
|
+
if (resolver.modifiers) {
|
|
211
|
+
for (const [name, modifier] of Object.entries(resolver.modifiers)) {
|
|
212
|
+
const normalizedName = name.toLowerCase();
|
|
213
|
+
const defaultContext = modifier.default ?? Object.keys(modifier.contexts)[0] ?? "";
|
|
214
|
+
metadata.dimensions.push(normalizedName);
|
|
215
|
+
metadata.defaults[normalizedName] = defaultContext.toLowerCase();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return metadata;
|
|
219
|
+
}
|
|
220
|
+
function countModifierDifferences(currentInputs, baseInputs) {
|
|
221
|
+
const normalizedCurrent = normalizeModifierInputs(currentInputs);
|
|
222
|
+
const normalizedBase = normalizeModifierInputs(baseInputs);
|
|
223
|
+
let count = 0;
|
|
224
|
+
for (const [key, value] of Object.entries(normalizedCurrent)) {
|
|
225
|
+
if (value !== normalizedBase[key]) {
|
|
226
|
+
count++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return count;
|
|
230
|
+
}
|
|
231
|
+
function getExpectedSource(currentInputs, baseInputs) {
|
|
232
|
+
const normalizedCurrent = normalizeModifierInputs(currentInputs);
|
|
233
|
+
const normalizedBase = normalizeModifierInputs(baseInputs);
|
|
234
|
+
for (const [key, value] of Object.entries(normalizedCurrent)) {
|
|
235
|
+
if (value !== normalizedBase[key]) {
|
|
236
|
+
return `${key}-${value}`;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return "base";
|
|
240
|
+
}
|
|
241
|
+
function parseModifierSource(source) {
|
|
242
|
+
const dashIndex = source.indexOf("-");
|
|
243
|
+
if (dashIndex === -1) {
|
|
244
|
+
return [source, ""];
|
|
245
|
+
}
|
|
246
|
+
return [source.slice(0, dashIndex), source.slice(dashIndex + 1)];
|
|
247
|
+
}
|
|
248
|
+
function filterTokensBySource(tokens, expectedSource) {
|
|
249
|
+
const filtered = {};
|
|
250
|
+
const expected = expectedSource.toLowerCase();
|
|
251
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
252
|
+
const source = typeof token._sourceModifier === "string" ? token._sourceModifier.toLowerCase() : "";
|
|
253
|
+
if (source !== "" && source === expected) {
|
|
254
|
+
filtered[name] = token;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return filtered;
|
|
258
|
+
}
|
|
259
|
+
function filterTokensFromSets(tokens) {
|
|
260
|
+
const filtered = {};
|
|
261
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
262
|
+
const hasModifierSource = typeof token._sourceModifier === "string" && token._sourceModifier !== "";
|
|
263
|
+
if (!hasModifierSource) {
|
|
264
|
+
filtered[name] = token;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return filtered;
|
|
268
|
+
}
|
|
269
|
+
function resolveBaseFileName(fileName, defaults) {
|
|
270
|
+
if (typeof fileName === "function") {
|
|
271
|
+
return fileName({ ...defaults, _base: "true" });
|
|
272
|
+
}
|
|
273
|
+
if (/\{.+?\}/.test(fileName)) {
|
|
274
|
+
const baseInputs = Object.fromEntries(Object.keys(defaults).map((k) => [k, "base"]));
|
|
275
|
+
return collapseBaseSegments(interpolatePattern(fileName, baseInputs));
|
|
276
|
+
}
|
|
277
|
+
const extMatch = fileName.match(/(\.[^.]+)$/);
|
|
278
|
+
const extension = extMatch ? extMatch[1] : "";
|
|
279
|
+
const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
|
|
280
|
+
return `${baseName}-base${extension}`;
|
|
281
|
+
}
|
|
282
|
+
function collapseBaseSegments(value) {
|
|
283
|
+
let result = value;
|
|
284
|
+
let previous = "";
|
|
285
|
+
while (result !== previous) {
|
|
286
|
+
previous = result;
|
|
287
|
+
result = result.replace(/\bbase([/-])base\b/, "base");
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
function interpolatePattern(pattern, modifierInputs) {
|
|
292
|
+
let result = pattern;
|
|
293
|
+
for (const [key, value] of Object.entries(modifierInputs)) {
|
|
294
|
+
result = result.replaceAll(`{${key}}`, value);
|
|
295
|
+
}
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
function resolveFileName(fileName, modifierInputs) {
|
|
299
|
+
if (typeof fileName === "function") {
|
|
300
|
+
return fileName(modifierInputs);
|
|
301
|
+
}
|
|
302
|
+
if (/\{.+?\}/.test(fileName)) {
|
|
303
|
+
return interpolatePattern(fileName, modifierInputs);
|
|
304
|
+
}
|
|
305
|
+
const extMatch = fileName.match(/(\.[^.]+)$/);
|
|
306
|
+
const extension = extMatch ? extMatch[1] : "";
|
|
307
|
+
const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
|
|
308
|
+
const modifierSuffix = Object.entries(modifierInputs).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}-${value}`).join("-");
|
|
309
|
+
if (modifierSuffix) {
|
|
310
|
+
return `${baseName}-${modifierSuffix}${extension}`;
|
|
311
|
+
}
|
|
312
|
+
return fileName;
|
|
313
|
+
}
|
|
314
|
+
var init_utils = __esm({
|
|
315
|
+
"src/outputs/utils.ts"() {
|
|
316
|
+
init_errors();
|
|
317
|
+
init_token_utils();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// src/outputs/metadata.ts
|
|
322
|
+
function sanitizeText(text, format) {
|
|
323
|
+
switch (format) {
|
|
324
|
+
case "css":
|
|
325
|
+
case "tailwind":
|
|
326
|
+
return text.replace(/\*\//g, "*\\/").replace(/\r?\n/g, " ").trim();
|
|
327
|
+
case "kotlin":
|
|
328
|
+
return text.replace(/\*\//g, "* /").replace(/\r?\n/g, " ").trim();
|
|
329
|
+
case "js":
|
|
330
|
+
case "swift":
|
|
331
|
+
return text.replace(/\r?\n/g, " ").trim();
|
|
332
|
+
default:
|
|
333
|
+
return text.trim();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function buildDeprecationText(token) {
|
|
337
|
+
if (token.$deprecated == null || token.$deprecated === false) {
|
|
338
|
+
return "";
|
|
339
|
+
}
|
|
340
|
+
const msg = typeof token.$deprecated === "string" ? token.$deprecated : "";
|
|
341
|
+
return msg ? `DEPRECATED: ${msg}` : "DEPRECATED";
|
|
342
|
+
}
|
|
343
|
+
function buildTokenDescriptionComment(token, format) {
|
|
344
|
+
if (!token.$description || token.$description === "") {
|
|
345
|
+
return void 0;
|
|
346
|
+
}
|
|
347
|
+
const text = sanitizeText(token.$description, format);
|
|
348
|
+
switch (format) {
|
|
349
|
+
case "css":
|
|
350
|
+
case "tailwind":
|
|
351
|
+
return `/* ${text} */`;
|
|
352
|
+
case "js":
|
|
353
|
+
return `// ${text}`;
|
|
354
|
+
case "swift":
|
|
355
|
+
return `/// ${text}`;
|
|
356
|
+
case "kotlin":
|
|
357
|
+
return `/** ${text} */`;
|
|
358
|
+
default:
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function buildTokenDeprecationComment(token, format) {
|
|
363
|
+
const text = buildDeprecationText(token);
|
|
364
|
+
if (!text) {
|
|
365
|
+
return void 0;
|
|
366
|
+
}
|
|
367
|
+
switch (format) {
|
|
368
|
+
case "css":
|
|
369
|
+
case "tailwind":
|
|
370
|
+
return `/* ${text} */`;
|
|
371
|
+
case "js":
|
|
372
|
+
return `// ${text}`;
|
|
373
|
+
case "swift":
|
|
374
|
+
return `/// ${text}`;
|
|
375
|
+
case "kotlin":
|
|
376
|
+
return `/** ${text} */`;
|
|
377
|
+
default:
|
|
378
|
+
return void 0;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function buildModifierComment(modifier, context) {
|
|
382
|
+
return `/* Modifier: ${modifier}=${context} */`;
|
|
383
|
+
}
|
|
384
|
+
function buildSwiftDeprecationAttribute(token) {
|
|
385
|
+
if (token.$deprecated == null || token.$deprecated === false) {
|
|
386
|
+
return void 0;
|
|
387
|
+
}
|
|
388
|
+
const msg = typeof token.$deprecated === "string" ? token.$deprecated : "";
|
|
389
|
+
if (msg) {
|
|
390
|
+
return `@available(*, deprecated, message: "${sanitizeText(msg, "swift")}")`;
|
|
391
|
+
}
|
|
392
|
+
return "@available(*, deprecated)";
|
|
393
|
+
}
|
|
394
|
+
function buildKotlinDeprecationAnnotation(token) {
|
|
395
|
+
if (token.$deprecated == null || token.$deprecated === false) {
|
|
396
|
+
return void 0;
|
|
397
|
+
}
|
|
398
|
+
const msg = typeof token.$deprecated === "string" ? token.$deprecated : "";
|
|
399
|
+
if (msg) {
|
|
400
|
+
return `@Deprecated(message = "${sanitizeText(msg, "kotlin")}")`;
|
|
401
|
+
}
|
|
402
|
+
return "@Deprecated";
|
|
403
|
+
}
|
|
404
|
+
var init_metadata = __esm({
|
|
405
|
+
"src/outputs/metadata.ts"() {
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// src/outputs/js/presets/bundle.ts
|
|
410
|
+
var bundle_exports = {};
|
|
411
|
+
__export(bundle_exports, {
|
|
412
|
+
bundleAsJsModule: () => bundleAsJsModule
|
|
413
|
+
});
|
|
414
|
+
function updateStringTracking(state, char) {
|
|
415
|
+
if (!state.escaped && (char === '"' || char === "'" || char === "`")) {
|
|
416
|
+
if (!state.inString) {
|
|
417
|
+
state.inString = true;
|
|
418
|
+
state.stringChar = char;
|
|
419
|
+
} else if (char === state.stringChar) {
|
|
420
|
+
state.inString = false;
|
|
421
|
+
state.stringChar = "";
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
state.escaped = !state.escaped && char === "\\";
|
|
425
|
+
}
|
|
426
|
+
function extractObjectFromJsModule(formattedJs) {
|
|
427
|
+
const assignmentMatch = /const\s+\w+\s*=\s*\{/.exec(formattedJs);
|
|
428
|
+
if (!assignmentMatch) {
|
|
429
|
+
return "{}";
|
|
430
|
+
}
|
|
431
|
+
const startIndex = assignmentMatch.index + assignmentMatch[0].length - 1;
|
|
432
|
+
const state = { inString: false, stringChar: "", escaped: false };
|
|
433
|
+
let braceCount = 0;
|
|
434
|
+
for (let i = startIndex; i < formattedJs.length; i++) {
|
|
435
|
+
const char = formattedJs[i];
|
|
436
|
+
updateStringTracking(state, char);
|
|
437
|
+
if (state.inString) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (char === "{") {
|
|
441
|
+
braceCount++;
|
|
442
|
+
} else if (char === "}") {
|
|
443
|
+
braceCount--;
|
|
444
|
+
if (braceCount === 0) {
|
|
445
|
+
return formattedJs.substring(startIndex, i + 1);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return "{}";
|
|
450
|
+
}
|
|
451
|
+
function toCamelKey(key) {
|
|
452
|
+
if (key === "") {
|
|
453
|
+
return "";
|
|
454
|
+
}
|
|
455
|
+
return key.split("-").filter((part) => part !== "").map((w, i) => i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
456
|
+
}
|
|
457
|
+
function buildStableDashKey(params) {
|
|
458
|
+
const { modifierInputs, dimensions, defaults } = params;
|
|
459
|
+
const inputs = normalizeModifierInputs(modifierInputs);
|
|
460
|
+
return dimensions.map((dimension) => {
|
|
461
|
+
const value = inputs[dimension] ?? defaults[dimension] ?? "";
|
|
462
|
+
return String(value);
|
|
463
|
+
}).join("-");
|
|
464
|
+
}
|
|
465
|
+
function buildHelperFunction(dimensions) {
|
|
466
|
+
const dimensionOrder = dimensions.map((d) => JSON.stringify(d)).join(", ");
|
|
467
|
+
return [
|
|
468
|
+
`/**`,
|
|
469
|
+
` * Get tokens for a specific modifier combination`,
|
|
470
|
+
` * @param {Object} modifiers - Modifier values (e.g., { theme: 'dark', brand: 'partner-a' })`,
|
|
471
|
+
` * @returns {Object} Resolved tokens for the combination`,
|
|
472
|
+
` */`,
|
|
473
|
+
`export function getTokens(modifiers = {}) {`,
|
|
474
|
+
` const key = [${dimensionOrder}]`,
|
|
475
|
+
` .map(dim => modifiers[dim] ?? tokenBundle._meta.defaults[dim])`,
|
|
476
|
+
` .join('-')`,
|
|
477
|
+
` const camelKey = key`,
|
|
478
|
+
` .split('-')`,
|
|
479
|
+
` .filter(Boolean)`,
|
|
480
|
+
` .map((w, i) => (i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)))`,
|
|
481
|
+
` .join('')`,
|
|
482
|
+
` return tokenBundle.tokens[camelKey]`,
|
|
483
|
+
`}`,
|
|
484
|
+
``,
|
|
485
|
+
``
|
|
486
|
+
].join("\n");
|
|
487
|
+
}
|
|
488
|
+
function assembleJsBundle(metadata, jsBlocks, generateHelper) {
|
|
489
|
+
let output = `const tokenBundle = {
|
|
490
|
+
`;
|
|
491
|
+
output += ` _meta: ${JSON.stringify(metadata, null, 2).replace(/\n/g, "\n ")},
|
|
492
|
+
`;
|
|
493
|
+
output += ` tokens: {
|
|
494
|
+
${jsBlocks.join(",\n")}
|
|
495
|
+
}
|
|
496
|
+
`;
|
|
497
|
+
output += `}
|
|
498
|
+
|
|
499
|
+
`;
|
|
500
|
+
if (generateHelper) {
|
|
501
|
+
output += buildHelperFunction(metadata.dimensions);
|
|
502
|
+
}
|
|
503
|
+
output += `export default tokenBundle
|
|
504
|
+
`;
|
|
505
|
+
return output;
|
|
506
|
+
}
|
|
507
|
+
async function bundleAsJsModule(bundleData, resolver, options, formatTokens) {
|
|
508
|
+
if (!formatTokens) {
|
|
509
|
+
throw new ConfigurationError("JS formatter was not provided");
|
|
510
|
+
}
|
|
511
|
+
const metadata = buildMetadata(resolver);
|
|
512
|
+
const jsBlocks = [];
|
|
513
|
+
for (const { tokens, modifierInputs, isBase } of bundleData) {
|
|
514
|
+
const cleanTokens = stripInternalMetadata(tokens);
|
|
515
|
+
const key = buildStableDashKey({
|
|
516
|
+
modifierInputs,
|
|
517
|
+
dimensions: metadata.dimensions,
|
|
518
|
+
defaults: metadata.defaults
|
|
519
|
+
});
|
|
520
|
+
const camelKey = toCamelKey(key);
|
|
521
|
+
const normalizedInputs = normalizeModifierInputs(modifierInputs);
|
|
522
|
+
const modifierParts = [];
|
|
523
|
+
for (const dim of metadata.dimensions) {
|
|
524
|
+
const value = normalizedInputs[dim.toLowerCase()];
|
|
525
|
+
if (value) {
|
|
526
|
+
modifierParts.push(`${dim}=${value}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const formattedJs = await formatTokens(cleanTokens);
|
|
530
|
+
const tokenObject = extractObjectFromJsModule(formattedJs);
|
|
531
|
+
const indentedObject = tokenObject.replace(/\n/g, "\n ");
|
|
532
|
+
let comment;
|
|
533
|
+
if (modifierParts.length > 0) {
|
|
534
|
+
const modifierPart = modifierParts[0];
|
|
535
|
+
const eqIndex = modifierPart.indexOf("=");
|
|
536
|
+
const modifier = modifierPart.slice(0, eqIndex);
|
|
537
|
+
const context = modifierPart.slice(eqIndex + 1);
|
|
538
|
+
comment = buildModifierComment(modifier, context);
|
|
539
|
+
} else if (isBase) {
|
|
540
|
+
comment = "// Base permutation";
|
|
541
|
+
} else {
|
|
542
|
+
comment = `// ${key}`;
|
|
543
|
+
}
|
|
544
|
+
jsBlocks.push(` ${comment}
|
|
545
|
+
${JSON.stringify(camelKey)}: ${indentedObject}`);
|
|
546
|
+
}
|
|
547
|
+
return assembleJsBundle(metadata, jsBlocks, options?.generateHelper ?? false);
|
|
548
|
+
}
|
|
549
|
+
var init_bundle = __esm({
|
|
550
|
+
"src/outputs/js/presets/bundle.ts"() {
|
|
551
|
+
init_errors();
|
|
552
|
+
init_metadata();
|
|
553
|
+
init_utils();
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// src/outputs/json/presets/bundle.ts
|
|
558
|
+
var bundle_exports2 = {};
|
|
559
|
+
__export(bundle_exports2, {
|
|
560
|
+
bundleAsJson: () => bundleAsJson
|
|
561
|
+
});
|
|
562
|
+
async function bundleAsJson(bundleData, resolver, formatTokens) {
|
|
563
|
+
if (!formatTokens) {
|
|
564
|
+
throw new ConfigurationError("JSON formatter was not provided");
|
|
565
|
+
}
|
|
566
|
+
const metadata = buildMetadata(resolver);
|
|
567
|
+
const tokens = {};
|
|
568
|
+
for (const { tokens: tokenSet, modifierInputs } of bundleData) {
|
|
569
|
+
const cleanTokens = stripInternalMetadata(tokenSet);
|
|
570
|
+
const normalizedInputs = normalizeModifierInputs(modifierInputs);
|
|
571
|
+
const key = buildStablePermutationKey(normalizedInputs, metadata.dimensions);
|
|
572
|
+
const themeJson = await formatTokens(cleanTokens);
|
|
573
|
+
tokens[key] = JSON.parse(themeJson);
|
|
574
|
+
}
|
|
575
|
+
const bundle = { _meta: metadata, tokens };
|
|
576
|
+
return JSON.stringify(bundle, null, 2);
|
|
577
|
+
}
|
|
578
|
+
var init_bundle2 = __esm({
|
|
579
|
+
"src/outputs/json/presets/bundle.ts"() {
|
|
580
|
+
init_errors();
|
|
581
|
+
init_utils();
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
function isColorObject(value) {
|
|
585
|
+
return typeof value === "object" && value !== null && "colorSpace" in value && "components" in value;
|
|
586
|
+
}
|
|
587
|
+
function componentToCulori(component) {
|
|
588
|
+
return component === "none" ? void 0 : component;
|
|
589
|
+
}
|
|
590
|
+
function dtcgObjectToCulori(color) {
|
|
591
|
+
const [c1, c2, c3] = color.components.map(componentToCulori);
|
|
592
|
+
const alpha = color.alpha !== void 0 ? componentToCulori(color.alpha) : void 0;
|
|
593
|
+
const colorSpace = color.colorSpace.toLowerCase();
|
|
594
|
+
switch (colorSpace) {
|
|
595
|
+
// RGB-based color spaces (components are R, G, B in 0-1 range)
|
|
596
|
+
case "srgb":
|
|
597
|
+
return { mode: "rgb", r: c1, g: c2, b: c3, alpha };
|
|
598
|
+
case "srgb-linear":
|
|
599
|
+
return { mode: "lrgb", r: c1, g: c2, b: c3, alpha };
|
|
600
|
+
case "display-p3":
|
|
601
|
+
return { mode: "p3", r: c1, g: c2, b: c3, alpha };
|
|
602
|
+
case "a98-rgb":
|
|
603
|
+
return { mode: "a98", r: c1, g: c2, b: c3, alpha };
|
|
604
|
+
case "prophoto-rgb":
|
|
605
|
+
return { mode: "prophoto", r: c1, g: c2, b: c3, alpha };
|
|
606
|
+
case "rec2020":
|
|
607
|
+
return { mode: "rec2020", r: c1, g: c2, b: c3, alpha };
|
|
608
|
+
// Cylindrical color spaces (Hue, Saturation/Whiteness, Lightness/Blackness)
|
|
609
|
+
case "hsl":
|
|
610
|
+
return { mode: "hsl", h: c1, s: c2, l: c3, alpha };
|
|
611
|
+
case "hwb":
|
|
612
|
+
return { mode: "hwb", h: c1, w: c2, b: c3, alpha };
|
|
613
|
+
// Lab color spaces (Lightness, a/b or Chroma/Hue)
|
|
614
|
+
case "lab":
|
|
615
|
+
return { mode: "lab", l: c1, a: c2, b: c3, alpha };
|
|
616
|
+
case "lch":
|
|
617
|
+
return { mode: "lch", l: c1, c: c2, h: c3, alpha };
|
|
618
|
+
case "oklab":
|
|
619
|
+
return { mode: "oklab", l: c1, a: c2, b: c3, alpha };
|
|
620
|
+
case "oklch":
|
|
621
|
+
return { mode: "oklch", l: c1, c: c2, h: c3, alpha };
|
|
622
|
+
// XYZ color spaces
|
|
623
|
+
case "xyz-d65":
|
|
624
|
+
return { mode: "xyz65", x: c1, y: c2, z: c3, alpha };
|
|
625
|
+
case "xyz-d50":
|
|
626
|
+
return { mode: "xyz50", x: c1, y: c2, z: c3, alpha };
|
|
627
|
+
// Fallback to sRGB if color space is not recognized
|
|
628
|
+
default:
|
|
629
|
+
return { mode: "rgb", r: c1, g: c2, b: c3, alpha };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function colorObjectToHex(color) {
|
|
633
|
+
const culoriColor = dtcgObjectToCulori(color);
|
|
634
|
+
const alpha = color.alpha ?? 1;
|
|
635
|
+
if (alpha < 1) {
|
|
636
|
+
return formatHex8(culoriColor);
|
|
637
|
+
}
|
|
638
|
+
return formatHex(culoriColor);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/processing/transforms/built-in/dimension-converter.ts
|
|
642
|
+
function isDimensionObject(value) {
|
|
643
|
+
return typeof value === "object" && value !== null && "value" in value && "unit" in value;
|
|
644
|
+
}
|
|
645
|
+
function dimensionObjectToString(dimension) {
|
|
646
|
+
return `${dimension.value}${dimension.unit}`;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// src/processing/transforms/built-in/duration-converter.ts
|
|
650
|
+
function isDurationObject(value) {
|
|
651
|
+
return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
|
|
652
|
+
}
|
|
653
|
+
function durationObjectToString(duration) {
|
|
654
|
+
return `${duration.value}${duration.unit}`;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/outputs/css/renderer.ts
|
|
658
|
+
init_errors();
|
|
659
|
+
init_token_utils();
|
|
660
|
+
|
|
661
|
+
// src/outputs/css/presets/bundle.ts
|
|
662
|
+
init_errors();
|
|
663
|
+
init_utils();
|
|
664
|
+
var REF_PREFIX_SETS = "#/sets/";
|
|
665
|
+
var REF_PREFIX_MODIFIERS = "#/modifiers/";
|
|
666
|
+
var getSourceSet = (token) => {
|
|
667
|
+
if (typeof token !== "object" || token === null) {
|
|
668
|
+
return void 0;
|
|
669
|
+
}
|
|
670
|
+
return "_sourceSet" in token && typeof token._sourceSet === "string" ? token._sourceSet : void 0;
|
|
671
|
+
};
|
|
672
|
+
var getSourceModifier = (token) => {
|
|
673
|
+
if (typeof token !== "object" || token === null) {
|
|
674
|
+
return void 0;
|
|
675
|
+
}
|
|
676
|
+
return "_sourceModifier" in token && typeof token._sourceModifier === "string" ? token._sourceModifier : void 0;
|
|
677
|
+
};
|
|
678
|
+
async function bundleAsCss(bundleData, resolver, options, formatTokens) {
|
|
679
|
+
const baseItem = bundleData.find((item) => item.isBase);
|
|
680
|
+
if (!baseItem) {
|
|
681
|
+
throw new BasePermutationError("Base permutation not found in bundle data");
|
|
682
|
+
}
|
|
683
|
+
if (!formatTokens) {
|
|
684
|
+
throw new ConfigurationError("CSS formatter was not provided");
|
|
685
|
+
}
|
|
686
|
+
const orderedBundleData = orderBundleData(bundleData, resolver, baseItem);
|
|
687
|
+
const cssBlocks = [];
|
|
688
|
+
for (const item of orderedBundleData) {
|
|
689
|
+
if (item.isBase) {
|
|
690
|
+
const blocks = await formatBasePermutation(item, resolver, options, formatTokens);
|
|
691
|
+
cssBlocks.push(...blocks);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
const block = await formatModifierPermutation(item, baseItem, options, formatTokens);
|
|
695
|
+
if (block) {
|
|
696
|
+
cssBlocks.push(block);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return cssBlocks.join("\n\n");
|
|
700
|
+
}
|
|
701
|
+
async function formatBasePermutation({ tokens, modifierInputs }, resolver, options, formatTokens) {
|
|
702
|
+
const firstModifierName = resolver.modifiers ? Object.keys(resolver.modifiers)[0] : "";
|
|
703
|
+
const modifier = firstModifierName ?? "";
|
|
704
|
+
const context = modifierInputs[modifier] ?? "";
|
|
705
|
+
const selector = resolveSelector(options?.selector, modifier, context, true, modifierInputs);
|
|
706
|
+
const mediaQuery = resolveMediaQuery(options?.mediaQuery, modifier, context, true, modifierInputs);
|
|
707
|
+
const referenceTokens = stripInternalMetadata(tokens);
|
|
708
|
+
const defaultBlocks = buildDefaultLayerBlocks(tokens, modifierInputs, resolver);
|
|
709
|
+
const cssBlocks = [];
|
|
710
|
+
for (const block of defaultBlocks) {
|
|
711
|
+
const cleanTokens = stripInternalMetadata(block.tokens);
|
|
712
|
+
const css2 = await formatTokens(cleanTokens, {
|
|
713
|
+
selector,
|
|
714
|
+
mediaQuery,
|
|
715
|
+
minify: options?.minify,
|
|
716
|
+
referenceTokens
|
|
717
|
+
});
|
|
718
|
+
const header = block.description ? `/* ${block.key} */
|
|
719
|
+
/* ${block.description} */` : `/* ${block.key} */`;
|
|
720
|
+
cssBlocks.push(`${header}
|
|
721
|
+
${css2}`);
|
|
722
|
+
}
|
|
723
|
+
return cssBlocks;
|
|
724
|
+
}
|
|
725
|
+
async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, options, formatTokens) {
|
|
726
|
+
const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
|
|
727
|
+
if (differenceCount > 1) {
|
|
728
|
+
return void 0;
|
|
729
|
+
}
|
|
730
|
+
const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
|
|
731
|
+
let tokensToInclude = filterTokensBySource(tokens, expectedSource);
|
|
732
|
+
const hasSourceMetadata = Object.values(tokens).some(
|
|
733
|
+
(token) => token != null && getSourceModifier(token) !== void 0
|
|
734
|
+
);
|
|
735
|
+
if (Object.keys(tokensToInclude).length === 0 && !hasSourceMetadata) {
|
|
736
|
+
tokensToInclude = tokens;
|
|
737
|
+
}
|
|
738
|
+
if (Object.keys(tokensToInclude).length === 0) {
|
|
739
|
+
return void 0;
|
|
740
|
+
}
|
|
741
|
+
const [modifier, context] = parseModifierSource(expectedSource);
|
|
742
|
+
const cleanTokens = stripInternalMetadata(tokensToInclude);
|
|
743
|
+
const referenceTokens = stripInternalMetadata(tokens);
|
|
744
|
+
const selector = resolveSelector(options?.selector, modifier, context, false, modifierInputs);
|
|
745
|
+
const mediaQuery = resolveMediaQuery(
|
|
746
|
+
options?.mediaQuery,
|
|
747
|
+
modifier,
|
|
748
|
+
context,
|
|
749
|
+
false,
|
|
750
|
+
modifierInputs
|
|
751
|
+
);
|
|
752
|
+
const css2 = await formatTokens(cleanTokens, {
|
|
753
|
+
selector,
|
|
754
|
+
mediaQuery,
|
|
755
|
+
minify: options?.minify,
|
|
756
|
+
referenceTokens
|
|
757
|
+
});
|
|
758
|
+
return `/* Modifier: ${modifier}=${context} */
|
|
759
|
+
${css2}`;
|
|
760
|
+
}
|
|
761
|
+
function addLayerBlock(blocks, included, key, blockTokens, description) {
|
|
762
|
+
if (Object.keys(blockTokens).length === 0) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
for (const k of Object.keys(blockTokens)) {
|
|
766
|
+
included.add(k);
|
|
767
|
+
}
|
|
768
|
+
blocks.push({ key, description, tokens: blockTokens });
|
|
769
|
+
}
|
|
770
|
+
function collectSetTokens(tokens, setName, included) {
|
|
771
|
+
const result = {};
|
|
772
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
773
|
+
if (!included.has(name) && getSourceSet(token) === setName) {
|
|
774
|
+
result[name] = token;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
function collectModifierTokens(tokens, expectedSource, included) {
|
|
780
|
+
const result = {};
|
|
781
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
782
|
+
if (!included.has(name) && (getSourceModifier(token) ?? "").toLowerCase() === expectedSource) {
|
|
783
|
+
result[name] = token;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return result;
|
|
787
|
+
}
|
|
788
|
+
function collectRemainder(tokens, included) {
|
|
789
|
+
const result = {};
|
|
790
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
791
|
+
if (!included.has(name)) {
|
|
792
|
+
result[name] = token;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return result;
|
|
796
|
+
}
|
|
797
|
+
function buildSetLayerBlocks(tokens, resolver) {
|
|
798
|
+
const blocks = [];
|
|
799
|
+
const included = /* @__PURE__ */ new Set();
|
|
800
|
+
for (const item of resolver.resolutionOrder) {
|
|
801
|
+
const ref = item.$ref;
|
|
802
|
+
if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX_SETS)) {
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const setName = ref.slice(REF_PREFIX_SETS.length);
|
|
806
|
+
addLayerBlock(
|
|
807
|
+
blocks,
|
|
808
|
+
included,
|
|
809
|
+
`Set: ${setName}`,
|
|
810
|
+
collectSetTokens(tokens, setName, included),
|
|
811
|
+
resolver.sets?.[setName]?.description
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
|
|
815
|
+
return blocks;
|
|
816
|
+
}
|
|
817
|
+
function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
|
|
818
|
+
const blocks = [];
|
|
819
|
+
const included = /* @__PURE__ */ new Set();
|
|
820
|
+
const baseInputs = normalizeModifierInputs(baseModifierInputs);
|
|
821
|
+
for (const item of resolver.resolutionOrder) {
|
|
822
|
+
const ref = item.$ref;
|
|
823
|
+
if (typeof ref !== "string") {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver);
|
|
827
|
+
}
|
|
828
|
+
addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
|
|
829
|
+
return blocks;
|
|
830
|
+
}
|
|
831
|
+
function processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver) {
|
|
832
|
+
if (ref.startsWith(REF_PREFIX_SETS)) {
|
|
833
|
+
const setName = ref.slice(REF_PREFIX_SETS.length);
|
|
834
|
+
addLayerBlock(
|
|
835
|
+
blocks,
|
|
836
|
+
included,
|
|
837
|
+
`Set: ${setName}`,
|
|
838
|
+
collectSetTokens(tokens, setName, included),
|
|
839
|
+
resolver.sets?.[setName]?.description
|
|
840
|
+
);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
const modifierName = ref.slice(REF_PREFIX_MODIFIERS.length);
|
|
847
|
+
const modifier = resolver.modifiers?.[modifierName];
|
|
848
|
+
const selectedContext = baseInputs[modifierName.toLowerCase()];
|
|
849
|
+
if (!modifier || !selectedContext) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
|
|
853
|
+
addLayerBlock(
|
|
854
|
+
blocks,
|
|
855
|
+
included,
|
|
856
|
+
`Modifier: ${modifierName}=${selectedContext} (default)`,
|
|
857
|
+
collectModifierTokens(tokens, expectedSource, included),
|
|
858
|
+
modifier.description
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs) {
|
|
862
|
+
const normalizedModifier = modifierName.toLowerCase();
|
|
863
|
+
const normalizedContext = context.toLowerCase();
|
|
864
|
+
return bundleData.find((item) => {
|
|
865
|
+
if (item.isBase) {
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
const inputs = normalizeModifierInputs(item.modifierInputs);
|
|
869
|
+
if (inputs[normalizedModifier] !== normalizedContext) {
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
return Object.entries(baseInputs).every(([k, v]) => k === normalizedModifier || inputs[k] === v);
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
function pushUniqueBundleItem(ordered, includedKeys, item) {
|
|
876
|
+
if (!item) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
const key = stableInputsKey(item.modifierInputs);
|
|
880
|
+
if (includedKeys.has(key)) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
includedKeys.add(key);
|
|
884
|
+
ordered.push(item);
|
|
885
|
+
}
|
|
886
|
+
function appendModifierPermutations(bundleData, modifiers, orderedNames, baseInputs, ordered, includedKeys) {
|
|
887
|
+
for (const modifierName of orderedNames) {
|
|
888
|
+
const modifierDef = modifiers[modifierName];
|
|
889
|
+
if (!modifierDef) {
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
|
|
893
|
+
for (const ctx of Object.keys(modifierDef.contexts)) {
|
|
894
|
+
if (defaultValue === ctx.toLowerCase()) {
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
pushUniqueBundleItem(
|
|
898
|
+
ordered,
|
|
899
|
+
includedKeys,
|
|
900
|
+
findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs)
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function orderBundleData(bundleData, resolver, baseItem) {
|
|
906
|
+
const modifiers = resolver.modifiers;
|
|
907
|
+
if (!modifiers) {
|
|
908
|
+
return bundleData;
|
|
909
|
+
}
|
|
910
|
+
const baseInputs = normalizeModifierInputs(baseItem.modifierInputs);
|
|
911
|
+
const orderedModifierNames = getOrderedModifierNames(resolver);
|
|
912
|
+
if (orderedModifierNames.length === 0) {
|
|
913
|
+
return bundleData;
|
|
914
|
+
}
|
|
915
|
+
const firstModifierDef = modifiers[orderedModifierNames[0] ?? ""];
|
|
916
|
+
if (!firstModifierDef) {
|
|
917
|
+
return bundleData;
|
|
918
|
+
}
|
|
919
|
+
const includedKeys = /* @__PURE__ */ new Set();
|
|
920
|
+
const ordered = [];
|
|
921
|
+
pushUniqueBundleItem(ordered, includedKeys, baseItem);
|
|
922
|
+
appendModifierPermutations(
|
|
923
|
+
bundleData,
|
|
924
|
+
modifiers,
|
|
925
|
+
orderedModifierNames,
|
|
926
|
+
baseInputs,
|
|
927
|
+
ordered,
|
|
928
|
+
includedKeys
|
|
929
|
+
);
|
|
930
|
+
return ordered.length > 0 ? ordered : bundleData;
|
|
931
|
+
}
|
|
932
|
+
function getOrderedModifierNames(resolver) {
|
|
933
|
+
const modifiers = resolver.modifiers ?? {};
|
|
934
|
+
const seen = /* @__PURE__ */ new Set();
|
|
935
|
+
const ordered = [];
|
|
936
|
+
for (const item of resolver.resolutionOrder) {
|
|
937
|
+
const ref = item.$ref;
|
|
938
|
+
if (typeof ref !== "string") {
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
const name = ref.slice(REF_PREFIX_MODIFIERS.length);
|
|
945
|
+
if (seen.has(name)) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
if (!(name in modifiers)) {
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
ordered.push(name);
|
|
952
|
+
seen.add(name);
|
|
953
|
+
}
|
|
954
|
+
for (const name of Object.keys(modifiers)) {
|
|
955
|
+
if (seen.has(name)) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
ordered.push(name);
|
|
959
|
+
seen.add(name);
|
|
960
|
+
}
|
|
961
|
+
return ordered;
|
|
962
|
+
}
|
|
963
|
+
function stableInputsKey(modifierInputs) {
|
|
964
|
+
const normalized = normalizeModifierInputs(modifierInputs);
|
|
965
|
+
return Object.keys(normalized).sort().map((k) => `${k}=${normalized[k]}`).join("|");
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/outputs/css/renderer.ts
|
|
969
|
+
init_utils();
|
|
970
|
+
init_metadata();
|
|
971
|
+
var CssRenderer = class _CssRenderer {
|
|
972
|
+
async format(context, options) {
|
|
973
|
+
const opts = {
|
|
974
|
+
preset: options?.preset ?? "bundle",
|
|
975
|
+
selector: options?.selector,
|
|
976
|
+
mediaQuery: options?.mediaQuery,
|
|
977
|
+
minify: options?.minify ?? false,
|
|
978
|
+
preserveReferences: options?.preserveReferences ?? false
|
|
979
|
+
};
|
|
980
|
+
if (opts.preset === "bundle") {
|
|
981
|
+
return await this.formatBundle(context, opts);
|
|
982
|
+
}
|
|
983
|
+
if (opts.preset === "modifier") {
|
|
984
|
+
return await this.formatModifier(context, opts);
|
|
985
|
+
}
|
|
986
|
+
return await this.formatStandalone(context, opts);
|
|
987
|
+
}
|
|
988
|
+
static PRETTIER_PRINT_WIDTH = 80;
|
|
989
|
+
static PRETTIER_TAB_WIDTH = 2;
|
|
990
|
+
/**
|
|
991
|
+
* Format tokens as CSS custom properties
|
|
992
|
+
*
|
|
993
|
+
* Converts resolved design tokens into CSS format with configurable selector,
|
|
994
|
+
* media queries, and formatting options. Supports DTCG color and dimension objects.
|
|
995
|
+
*
|
|
996
|
+
* Note: This method expects selector and mediaQuery to be resolved strings.
|
|
997
|
+
* Function-based selectors should be resolved before calling format().
|
|
998
|
+
*
|
|
999
|
+
* @param tokens - Resolved tokens to format
|
|
1000
|
+
* @param options - CSS formatting options (selector, media query, minify, etc.)
|
|
1001
|
+
* @returns Formatted CSS string with custom properties
|
|
1002
|
+
*/
|
|
1003
|
+
async formatTokens(tokens, options) {
|
|
1004
|
+
const opts = {
|
|
1005
|
+
preset: "bundle",
|
|
1006
|
+
selector: ":root",
|
|
1007
|
+
mediaQuery: "",
|
|
1008
|
+
minify: false,
|
|
1009
|
+
preserveReferences: false,
|
|
1010
|
+
...options,
|
|
1011
|
+
referenceTokens: options?.referenceTokens ?? tokens
|
|
1012
|
+
};
|
|
1013
|
+
const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
|
|
1014
|
+
const referenceTokens = opts.referenceTokens;
|
|
1015
|
+
const lines = [];
|
|
1016
|
+
this.buildCssBlock(lines, sortedTokens, opts.selector, tokens, referenceTokens, opts);
|
|
1017
|
+
const cssString = lines.join("");
|
|
1018
|
+
return opts.minify ? cssString : await this.formatWithPrettier(cssString);
|
|
1019
|
+
}
|
|
1020
|
+
buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
|
|
1021
|
+
const indent = opts.minify ? "" : " ";
|
|
1022
|
+
const newline = opts.minify ? "" : "\n";
|
|
1023
|
+
const space = opts.minify ? "" : " ";
|
|
1024
|
+
const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
|
|
1025
|
+
const tokenIndent = hasMediaQuery ? indent + indent : indent;
|
|
1026
|
+
if (hasMediaQuery) {
|
|
1027
|
+
lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
|
|
1028
|
+
lines.push(`${indent}${selector}${space}{${newline}`);
|
|
1029
|
+
} else {
|
|
1030
|
+
lines.push(`${selector}${space}{${newline}`);
|
|
1031
|
+
}
|
|
1032
|
+
for (const token of groupTokens) {
|
|
1033
|
+
this.pushTokenLines(
|
|
1034
|
+
lines,
|
|
1035
|
+
token,
|
|
1036
|
+
tokens,
|
|
1037
|
+
referenceTokens,
|
|
1038
|
+
opts.preserveReferences ?? false,
|
|
1039
|
+
tokenIndent,
|
|
1040
|
+
newline,
|
|
1041
|
+
space
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
if (hasMediaQuery) {
|
|
1045
|
+
lines.push(`${indent}}${newline}`);
|
|
1046
|
+
}
|
|
1047
|
+
lines.push(`}${newline}${newline}`);
|
|
1048
|
+
}
|
|
1049
|
+
pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
|
|
1050
|
+
const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
|
|
1051
|
+
const deprecationComment = buildTokenDeprecationComment(token, "css");
|
|
1052
|
+
if (deprecationComment) {
|
|
1053
|
+
lines.push(`${indent}${deprecationComment}${newline}`);
|
|
1054
|
+
}
|
|
1055
|
+
const descriptionComment = buildTokenDescriptionComment(token, "css");
|
|
1056
|
+
if (descriptionComment) {
|
|
1057
|
+
lines.push(`${indent}${descriptionComment}${newline}`);
|
|
1058
|
+
}
|
|
1059
|
+
for (const entry of entries) {
|
|
1060
|
+
lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
async formatWithPrettier(css2) {
|
|
1064
|
+
try {
|
|
1065
|
+
return await prettier.format(css2, {
|
|
1066
|
+
parser: "css",
|
|
1067
|
+
printWidth: _CssRenderer.PRETTIER_PRINT_WIDTH,
|
|
1068
|
+
tabWidth: _CssRenderer.PRETTIER_TAB_WIDTH,
|
|
1069
|
+
useTabs: false
|
|
1070
|
+
});
|
|
1071
|
+
} catch {
|
|
1072
|
+
return css2;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
buildCssEntries(token, tokens, referenceTokens, preserveReferences) {
|
|
1076
|
+
if (preserveReferences) {
|
|
1077
|
+
const refName = getPureAliasReferenceName(token.originalValue);
|
|
1078
|
+
if (refName !== void 0) {
|
|
1079
|
+
return [
|
|
1080
|
+
{
|
|
1081
|
+
name: token.name,
|
|
1082
|
+
value: this.buildCssVarReference(refName, referenceTokens, tokens)
|
|
1083
|
+
}
|
|
1084
|
+
];
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (!this.isCompositeToken(token)) {
|
|
1088
|
+
return [{ name: token.name, value: this.formatValue(token) }];
|
|
1089
|
+
}
|
|
1090
|
+
const leaves = this.collectCompositeLeaves(token.$value);
|
|
1091
|
+
if (leaves.length === 0) {
|
|
1092
|
+
return [{ name: token.name, value: this.formatLeafValue(token.$value) }];
|
|
1093
|
+
}
|
|
1094
|
+
const leafEntries = leaves.map((leaf) => ({
|
|
1095
|
+
name: this.buildCompositeName(token.name, leaf.path),
|
|
1096
|
+
value: this.formatLeafValue(
|
|
1097
|
+
this.resolveCompositeLeafValue(
|
|
1098
|
+
leaf,
|
|
1099
|
+
token.originalValue,
|
|
1100
|
+
tokens,
|
|
1101
|
+
referenceTokens,
|
|
1102
|
+
preserveReferences
|
|
1103
|
+
)
|
|
1104
|
+
)
|
|
1105
|
+
}));
|
|
1106
|
+
const wholeValue = this.buildCompositeWholeValue(token, preserveReferences);
|
|
1107
|
+
if (!wholeValue) {
|
|
1108
|
+
return leafEntries;
|
|
1109
|
+
}
|
|
1110
|
+
return [{ name: token.name, value: wholeValue }, ...leafEntries];
|
|
1111
|
+
}
|
|
1112
|
+
isCompositeToken(token) {
|
|
1113
|
+
const isCompositeType = [
|
|
1114
|
+
"shadow",
|
|
1115
|
+
"typography",
|
|
1116
|
+
"border",
|
|
1117
|
+
"strokeStyle",
|
|
1118
|
+
"transition",
|
|
1119
|
+
"gradient"
|
|
1120
|
+
].includes(token.$type ?? "");
|
|
1121
|
+
if (!isCompositeType) {
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1124
|
+
const value = token.$value;
|
|
1125
|
+
return typeof value === "object" && value !== null || Array.isArray(value);
|
|
1126
|
+
}
|
|
1127
|
+
buildCompositeWholeValue(token, preserveReferences) {
|
|
1128
|
+
if (token.$type === "shadow") {
|
|
1129
|
+
return preserveReferences ? this.buildShadowWholeValue(token) : this.formatValue(token);
|
|
1130
|
+
}
|
|
1131
|
+
if (token.$type === "border") {
|
|
1132
|
+
if (!this.hasBorderShorthandStyle(token)) {
|
|
1133
|
+
return void 0;
|
|
1134
|
+
}
|
|
1135
|
+
return preserveReferences ? this.buildBorderWholeValue(token) : this.formatValue(token);
|
|
1136
|
+
}
|
|
1137
|
+
if (token.$type === "transition") {
|
|
1138
|
+
return preserveReferences ? this.buildTransitionWholeValue(token) : this.formatValue(token);
|
|
1139
|
+
}
|
|
1140
|
+
return void 0;
|
|
1141
|
+
}
|
|
1142
|
+
hasBorderShorthandStyle(token) {
|
|
1143
|
+
const value = token.$value;
|
|
1144
|
+
if (typeof value !== "object" || value === null) {
|
|
1145
|
+
return false;
|
|
1146
|
+
}
|
|
1147
|
+
return typeof value.style === "string";
|
|
1148
|
+
}
|
|
1149
|
+
buildShadowWholeValue(token) {
|
|
1150
|
+
const value = token.$value;
|
|
1151
|
+
if (Array.isArray(value)) {
|
|
1152
|
+
return value.map((shadow, index) => this.buildShadowLayerValue(token.name, shadow, [String(index)])).join(", ");
|
|
1153
|
+
}
|
|
1154
|
+
if (typeof value === "object" && value !== null) {
|
|
1155
|
+
return this.buildShadowLayerValue(token.name, value, []);
|
|
1156
|
+
}
|
|
1157
|
+
return void 0;
|
|
1158
|
+
}
|
|
1159
|
+
buildShadowLayerValue(baseName, shadow, prefix) {
|
|
1160
|
+
if (typeof shadow !== "object" || shadow === null) {
|
|
1161
|
+
return String(shadow);
|
|
1162
|
+
}
|
|
1163
|
+
const shadowObj = shadow;
|
|
1164
|
+
const parts = [];
|
|
1165
|
+
if (shadowObj.inset === true) {
|
|
1166
|
+
parts.push("inset");
|
|
1167
|
+
}
|
|
1168
|
+
parts.push(this.buildCompositeVar(baseName, [...prefix, "offsetX"]));
|
|
1169
|
+
parts.push(this.buildCompositeVar(baseName, [...prefix, "offsetY"]));
|
|
1170
|
+
parts.push(this.buildCompositeVar(baseName, [...prefix, "blur"]));
|
|
1171
|
+
if (shadowObj.spread != null) {
|
|
1172
|
+
parts.push(this.buildCompositeVar(baseName, [...prefix, "spread"]));
|
|
1173
|
+
}
|
|
1174
|
+
parts.push(this.buildCompositeVar(baseName, [...prefix, "color"]));
|
|
1175
|
+
return parts.join(" ");
|
|
1176
|
+
}
|
|
1177
|
+
buildBorderWholeValue(token) {
|
|
1178
|
+
const value = token.$value;
|
|
1179
|
+
if (typeof value !== "object" || value === null) {
|
|
1180
|
+
return void 0;
|
|
1181
|
+
}
|
|
1182
|
+
const border = value;
|
|
1183
|
+
if (typeof border.style !== "string") {
|
|
1184
|
+
return void 0;
|
|
1185
|
+
}
|
|
1186
|
+
return [
|
|
1187
|
+
this.buildCompositeVar(token.name, ["width"]),
|
|
1188
|
+
this.buildCompositeVar(token.name, ["style"]),
|
|
1189
|
+
this.buildCompositeVar(token.name, ["color"])
|
|
1190
|
+
].join(" ");
|
|
1191
|
+
}
|
|
1192
|
+
buildTransitionWholeValue(token) {
|
|
1193
|
+
const value = token.$value;
|
|
1194
|
+
if (typeof value !== "object" || value === null) {
|
|
1195
|
+
return void 0;
|
|
1196
|
+
}
|
|
1197
|
+
return [
|
|
1198
|
+
this.buildCompositeVar(token.name, ["duration"]),
|
|
1199
|
+
`cubic-bezier(${this.buildCompositeVar(token.name, ["timingFunction", "0"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "1"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "2"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "3"])})`,
|
|
1200
|
+
this.buildCompositeVar(token.name, ["delay"])
|
|
1201
|
+
].join(" ");
|
|
1202
|
+
}
|
|
1203
|
+
buildCompositeVar(baseName, path) {
|
|
1204
|
+
return `var(--${this.buildCompositeName(baseName, path)})`;
|
|
1205
|
+
}
|
|
1206
|
+
collectCompositeLeaves(value) {
|
|
1207
|
+
const leaves = [];
|
|
1208
|
+
this.collectLeafEntries(value, [], leaves);
|
|
1209
|
+
return leaves;
|
|
1210
|
+
}
|
|
1211
|
+
collectLeafEntries(value, path, leaves) {
|
|
1212
|
+
if (this.isPrimitiveValue(value)) {
|
|
1213
|
+
leaves.push({ path, value });
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
if (isColorObject(value) || isDimensionObject(value) || isDurationObject(value)) {
|
|
1217
|
+
leaves.push({ path, value });
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
if (Array.isArray(value)) {
|
|
1221
|
+
if (value.length === 0) {
|
|
1222
|
+
leaves.push({ path, value });
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
value.forEach((item, index) => {
|
|
1226
|
+
this.collectLeafEntries(item, [...path, String(index)], leaves);
|
|
1227
|
+
});
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (typeof value === "object" && value !== null) {
|
|
1231
|
+
const entries = Object.entries(value);
|
|
1232
|
+
if (entries.length === 0) {
|
|
1233
|
+
leaves.push({ path, value });
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
for (const [key, child] of entries) {
|
|
1237
|
+
this.collectLeafEntries(child, [...path, this.normalizePathSegment(key)], leaves);
|
|
1238
|
+
}
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
leaves.push({ path, value });
|
|
1242
|
+
}
|
|
1243
|
+
normalizePathSegment(segment) {
|
|
1244
|
+
return segment.trim().replace(/\s+/g, "-");
|
|
1245
|
+
}
|
|
1246
|
+
buildCompositeName(base, path) {
|
|
1247
|
+
if (path.length === 0) {
|
|
1248
|
+
return base;
|
|
1249
|
+
}
|
|
1250
|
+
return `${base}-${path.join("-")}`;
|
|
1251
|
+
}
|
|
1252
|
+
formatLeafValue(value) {
|
|
1253
|
+
if (isColorObject(value)) {
|
|
1254
|
+
return colorObjectToHex(value);
|
|
1255
|
+
}
|
|
1256
|
+
if (isDimensionObject(value)) {
|
|
1257
|
+
return dimensionObjectToString(value);
|
|
1258
|
+
}
|
|
1259
|
+
if (isDurationObject(value)) {
|
|
1260
|
+
return durationObjectToString(value);
|
|
1261
|
+
}
|
|
1262
|
+
if (typeof value === "string") {
|
|
1263
|
+
return value;
|
|
1264
|
+
}
|
|
1265
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
1266
|
+
return String(value);
|
|
1267
|
+
}
|
|
1268
|
+
if (Array.isArray(value)) {
|
|
1269
|
+
return JSON.stringify(value);
|
|
1270
|
+
}
|
|
1271
|
+
if (typeof value === "object" && value != null) {
|
|
1272
|
+
return JSON.stringify(value);
|
|
1273
|
+
}
|
|
1274
|
+
return String(value);
|
|
1275
|
+
}
|
|
1276
|
+
resolveCompositeLeafValue(leaf, originalValue, tokens, referenceTokens, preserveReferences) {
|
|
1277
|
+
if (!preserveReferences) {
|
|
1278
|
+
return leaf.value;
|
|
1279
|
+
}
|
|
1280
|
+
const originalLeafValue = this.getOriginalLeafValue(originalValue, leaf.path);
|
|
1281
|
+
const refName = getPureAliasReferenceName(originalLeafValue);
|
|
1282
|
+
if (refName === void 0) {
|
|
1283
|
+
return leaf.value;
|
|
1284
|
+
}
|
|
1285
|
+
return this.buildCssVarReference(refName, referenceTokens, tokens);
|
|
1286
|
+
}
|
|
1287
|
+
buildCssVarReference(refName, referenceTokens, tokens) {
|
|
1288
|
+
const referencedToken = referenceTokens[refName] ?? tokens[refName];
|
|
1289
|
+
if (!referencedToken) {
|
|
1290
|
+
throw new ConfigurationError(
|
|
1291
|
+
`CSS reference "{${refName}}" could not be resolved. The referenced token is not present in the current output's token set. This usually means a filter (e.g. isAlias()) excluded the referenced token while preserveReferences is true. Either remove the filter, include the referenced token, or set preserveReferences to false.`
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
return `var(--${referencedToken.name})`;
|
|
1295
|
+
}
|
|
1296
|
+
getOriginalLeafValue(value, path) {
|
|
1297
|
+
let current = value;
|
|
1298
|
+
for (const segment of path) {
|
|
1299
|
+
if (Array.isArray(current)) {
|
|
1300
|
+
const index = Number(segment);
|
|
1301
|
+
if (!Number.isInteger(index)) {
|
|
1302
|
+
return void 0;
|
|
1303
|
+
}
|
|
1304
|
+
current = current[index];
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
if (typeof current === "object" && current !== null) {
|
|
1308
|
+
const entries = Object.entries(current);
|
|
1309
|
+
const matched = entries.find(([key]) => this.normalizePathSegment(key) === segment);
|
|
1310
|
+
if (!matched) {
|
|
1311
|
+
return void 0;
|
|
1312
|
+
}
|
|
1313
|
+
current = matched[1];
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
return void 0;
|
|
1317
|
+
}
|
|
1318
|
+
return current;
|
|
1319
|
+
}
|
|
1320
|
+
isPrimitiveValue(value) {
|
|
1321
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Format token value for CSS
|
|
1325
|
+
* Handles DTCG 2025.10 object formats for colors and dimensions
|
|
1326
|
+
*/
|
|
1327
|
+
formatValue(token) {
|
|
1328
|
+
const value = token.$value;
|
|
1329
|
+
const typed = this.formatTypedValue(token.$type, value);
|
|
1330
|
+
if (typed !== void 0) {
|
|
1331
|
+
return typed;
|
|
1332
|
+
}
|
|
1333
|
+
return this.formatPrimitiveOrStructured(value, token.$type);
|
|
1334
|
+
}
|
|
1335
|
+
formatTypedValue(type, value) {
|
|
1336
|
+
if (type === "color" && isColorObject(value)) {
|
|
1337
|
+
return colorObjectToHex(value);
|
|
1338
|
+
}
|
|
1339
|
+
if (type === "dimension") {
|
|
1340
|
+
return typeof value === "string" ? value : dimensionObjectToString(value);
|
|
1341
|
+
}
|
|
1342
|
+
if (type === "duration") {
|
|
1343
|
+
if (isDurationObject(value)) {
|
|
1344
|
+
return durationObjectToString(value);
|
|
1345
|
+
}
|
|
1346
|
+
if (typeof value === "string") {
|
|
1347
|
+
return value;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return void 0;
|
|
1351
|
+
}
|
|
1352
|
+
formatPrimitiveOrStructured(value, tokenType) {
|
|
1353
|
+
if (typeof value === "string") {
|
|
1354
|
+
return value;
|
|
1355
|
+
}
|
|
1356
|
+
if (typeof value === "number") {
|
|
1357
|
+
return String(value);
|
|
1358
|
+
}
|
|
1359
|
+
if (Array.isArray(value)) {
|
|
1360
|
+
return this.formatArrayValue(value, tokenType);
|
|
1361
|
+
}
|
|
1362
|
+
if (typeof value === "object" && value != null) {
|
|
1363
|
+
return this.formatCompositeValue(value, tokenType);
|
|
1364
|
+
}
|
|
1365
|
+
return String(value);
|
|
1366
|
+
}
|
|
1367
|
+
formatArrayValue(value, tokenType) {
|
|
1368
|
+
if (tokenType === "shadow" && value.length > 0 && typeof value[0] === "object") {
|
|
1369
|
+
return value.map((shadowObj) => this.formatShadow(shadowObj)).join(", ");
|
|
1370
|
+
}
|
|
1371
|
+
return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Format composite token values
|
|
1375
|
+
*/
|
|
1376
|
+
formatCompositeValue(value, tokenType) {
|
|
1377
|
+
if (tokenType === "shadow") {
|
|
1378
|
+
return this.formatShadow(value);
|
|
1379
|
+
}
|
|
1380
|
+
if (tokenType === "border") {
|
|
1381
|
+
return this.formatBorder(value);
|
|
1382
|
+
}
|
|
1383
|
+
if (tokenType === "transition") {
|
|
1384
|
+
return this.formatTransition(value);
|
|
1385
|
+
}
|
|
1386
|
+
return JSON.stringify(value);
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Format a single shadow object to CSS box-shadow syntax
|
|
1390
|
+
*/
|
|
1391
|
+
formatShadow(shadow) {
|
|
1392
|
+
const parts = [];
|
|
1393
|
+
if (shadow.inset === true) {
|
|
1394
|
+
parts.push("inset");
|
|
1395
|
+
}
|
|
1396
|
+
parts.push(dimensionObjectToString(shadow.offsetX));
|
|
1397
|
+
parts.push(dimensionObjectToString(shadow.offsetY));
|
|
1398
|
+
parts.push(dimensionObjectToString(shadow.blur));
|
|
1399
|
+
if (shadow.spread != null) {
|
|
1400
|
+
parts.push(dimensionObjectToString(shadow.spread));
|
|
1401
|
+
}
|
|
1402
|
+
if (isColorObject(shadow.color)) {
|
|
1403
|
+
parts.push(colorObjectToHex(shadow.color));
|
|
1404
|
+
} else {
|
|
1405
|
+
parts.push(String(shadow.color));
|
|
1406
|
+
}
|
|
1407
|
+
return parts.join(" ");
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Format a border object to CSS border shorthand syntax
|
|
1411
|
+
*/
|
|
1412
|
+
formatBorder(value) {
|
|
1413
|
+
const parts = [];
|
|
1414
|
+
if (isDimensionObject(value.width)) {
|
|
1415
|
+
parts.push(dimensionObjectToString(value.width));
|
|
1416
|
+
} else if (value.width != null) {
|
|
1417
|
+
parts.push(String(value.width));
|
|
1418
|
+
}
|
|
1419
|
+
if (typeof value.style === "string") {
|
|
1420
|
+
parts.push(value.style);
|
|
1421
|
+
}
|
|
1422
|
+
if (isColorObject(value.color)) {
|
|
1423
|
+
parts.push(colorObjectToHex(value.color));
|
|
1424
|
+
} else if (value.color != null) {
|
|
1425
|
+
parts.push(String(value.color));
|
|
1426
|
+
}
|
|
1427
|
+
return parts.join(" ");
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Format a transition object to CSS transition shorthand syntax
|
|
1431
|
+
*/
|
|
1432
|
+
formatTransition(value) {
|
|
1433
|
+
const parts = [];
|
|
1434
|
+
if (isDurationObject(value.duration)) {
|
|
1435
|
+
parts.push(durationObjectToString(value.duration));
|
|
1436
|
+
} else if (value.duration != null) {
|
|
1437
|
+
parts.push(String(value.duration));
|
|
1438
|
+
}
|
|
1439
|
+
if (Array.isArray(value.timingFunction) && value.timingFunction.length === 4) {
|
|
1440
|
+
parts.push(`cubic-bezier(${value.timingFunction.join(", ")})`);
|
|
1441
|
+
}
|
|
1442
|
+
if (isDurationObject(value.delay)) {
|
|
1443
|
+
parts.push(durationObjectToString(value.delay));
|
|
1444
|
+
} else if (value.delay != null) {
|
|
1445
|
+
parts.push(String(value.delay));
|
|
1446
|
+
}
|
|
1447
|
+
return parts.join(" ");
|
|
1448
|
+
}
|
|
1449
|
+
async formatBundle(context, options) {
|
|
1450
|
+
const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
|
|
1451
|
+
tokens,
|
|
1452
|
+
modifierInputs,
|
|
1453
|
+
isBase: isBasePermutation(modifierInputs, context.meta.defaults)
|
|
1454
|
+
}));
|
|
1455
|
+
return await bundleAsCss(bundleData, context.resolver, options, async (tokens, resolved) => {
|
|
1456
|
+
return await this.formatTokens(tokens, {
|
|
1457
|
+
...resolved,
|
|
1458
|
+
preserveReferences: options.preserveReferences ?? false
|
|
1459
|
+
});
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
async formatStandalone(context, options) {
|
|
1463
|
+
assertFileRequired(
|
|
1464
|
+
context.buildPath,
|
|
1465
|
+
context.output.file,
|
|
1466
|
+
context.output.name,
|
|
1467
|
+
"standalone CSS"
|
|
1468
|
+
);
|
|
1469
|
+
const files = {};
|
|
1470
|
+
for (const { tokens, modifierInputs } of context.permutations) {
|
|
1471
|
+
const { fileName, content } = await this.buildStandaloneFile(
|
|
1472
|
+
tokens,
|
|
1473
|
+
modifierInputs,
|
|
1474
|
+
context,
|
|
1475
|
+
options
|
|
1476
|
+
);
|
|
1477
|
+
files[fileName] = content;
|
|
1478
|
+
}
|
|
1479
|
+
return { kind: "outputTree", files };
|
|
1480
|
+
}
|
|
1481
|
+
async buildStandaloneFile(tokens, modifierInputs, context, options) {
|
|
1482
|
+
const isBase = isBasePermutation(modifierInputs, context.meta.defaults);
|
|
1483
|
+
const { modifierName, modifierContext } = this.resolveModifierContext(
|
|
1484
|
+
modifierInputs,
|
|
1485
|
+
context,
|
|
1486
|
+
isBase
|
|
1487
|
+
);
|
|
1488
|
+
const selector = resolveSelector(
|
|
1489
|
+
options.selector,
|
|
1490
|
+
modifierName,
|
|
1491
|
+
modifierContext,
|
|
1492
|
+
isBase,
|
|
1493
|
+
modifierInputs
|
|
1494
|
+
);
|
|
1495
|
+
const mediaQuery = resolveMediaQuery(
|
|
1496
|
+
options.mediaQuery,
|
|
1497
|
+
modifierName,
|
|
1498
|
+
modifierContext,
|
|
1499
|
+
isBase,
|
|
1500
|
+
modifierInputs
|
|
1501
|
+
);
|
|
1502
|
+
const content = await this.formatTokens(tokens, {
|
|
1503
|
+
selector,
|
|
1504
|
+
mediaQuery,
|
|
1505
|
+
minify: options.minify ?? false,
|
|
1506
|
+
preserveReferences: options.preserveReferences ?? false,
|
|
1507
|
+
referenceTokens: tokens
|
|
1508
|
+
});
|
|
1509
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
1510
|
+
outputName: context.output.name,
|
|
1511
|
+
extension: "css",
|
|
1512
|
+
modifierInputs,
|
|
1513
|
+
resolver: context.resolver,
|
|
1514
|
+
defaults: context.meta.defaults
|
|
1515
|
+
});
|
|
1516
|
+
return { fileName, content };
|
|
1517
|
+
}
|
|
1518
|
+
async formatModifier(context, options) {
|
|
1519
|
+
assertFileRequired(context.buildPath, context.output.file, context.output.name, "modifier CSS");
|
|
1520
|
+
if (!context.resolver.modifiers) {
|
|
1521
|
+
throw new ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
|
|
1522
|
+
}
|
|
1523
|
+
const files = {};
|
|
1524
|
+
const baseResult = await this.buildModifierBaseFile(context, options);
|
|
1525
|
+
if (baseResult) {
|
|
1526
|
+
files[baseResult.fileName] = baseResult.content;
|
|
1527
|
+
}
|
|
1528
|
+
for (const [modifierName, modifierDef] of Object.entries(context.resolver.modifiers)) {
|
|
1529
|
+
for (const contextValue of Object.keys(modifierDef.contexts)) {
|
|
1530
|
+
const result = await this.buildModifierContextFile(
|
|
1531
|
+
modifierName,
|
|
1532
|
+
contextValue,
|
|
1533
|
+
context,
|
|
1534
|
+
options
|
|
1535
|
+
);
|
|
1536
|
+
if (result) {
|
|
1537
|
+
files[result.fileName] = result.content;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return { kind: "outputTree", files };
|
|
1542
|
+
}
|
|
1543
|
+
async buildModifierBaseFile(context, options) {
|
|
1544
|
+
const basePermutation = context.permutations.find(
|
|
1545
|
+
({ modifierInputs }) => isBasePermutation(modifierInputs, context.meta.defaults)
|
|
1546
|
+
);
|
|
1547
|
+
if (!basePermutation) {
|
|
1548
|
+
return void 0;
|
|
1549
|
+
}
|
|
1550
|
+
const setTokens = filterTokensFromSets(basePermutation.tokens);
|
|
1551
|
+
if (Object.keys(setTokens).length === 0) {
|
|
1552
|
+
return void 0;
|
|
1553
|
+
}
|
|
1554
|
+
const setBlocks = buildSetLayerBlocks(setTokens, context.resolver);
|
|
1555
|
+
if (setBlocks.length === 0) {
|
|
1556
|
+
return void 0;
|
|
1557
|
+
}
|
|
1558
|
+
const { selector, mediaQuery } = this.resolveBaseModifierContext(context, options);
|
|
1559
|
+
const content = await this.formatSetBlocksCss(
|
|
1560
|
+
setBlocks,
|
|
1561
|
+
basePermutation.tokens,
|
|
1562
|
+
selector,
|
|
1563
|
+
mediaQuery,
|
|
1564
|
+
options
|
|
1565
|
+
);
|
|
1566
|
+
const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
|
|
1567
|
+
return { fileName, content };
|
|
1568
|
+
}
|
|
1569
|
+
resolveBaseModifierContext(context, options) {
|
|
1570
|
+
const modifiers = context.resolver.modifiers;
|
|
1571
|
+
const firstModifierName = Object.keys(modifiers)[0] ?? "";
|
|
1572
|
+
const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
|
|
1573
|
+
const baseModifierInputs = { ...context.meta.defaults };
|
|
1574
|
+
return {
|
|
1575
|
+
selector: resolveSelector(
|
|
1576
|
+
options.selector,
|
|
1577
|
+
firstModifierName,
|
|
1578
|
+
firstModifierContext,
|
|
1579
|
+
true,
|
|
1580
|
+
baseModifierInputs
|
|
1581
|
+
),
|
|
1582
|
+
mediaQuery: resolveMediaQuery(
|
|
1583
|
+
options.mediaQuery,
|
|
1584
|
+
firstModifierName,
|
|
1585
|
+
firstModifierContext,
|
|
1586
|
+
true,
|
|
1587
|
+
baseModifierInputs
|
|
1588
|
+
)
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
async formatSetBlocksCss(setBlocks, referenceTokens, selector, mediaQuery, options) {
|
|
1592
|
+
const cssBlocks = [];
|
|
1593
|
+
for (const block of setBlocks) {
|
|
1594
|
+
const cleanTokens = stripInternalMetadata(block.tokens);
|
|
1595
|
+
const css2 = await this.formatTokens(cleanTokens, {
|
|
1596
|
+
selector,
|
|
1597
|
+
mediaQuery,
|
|
1598
|
+
minify: options.minify ?? false,
|
|
1599
|
+
preserveReferences: options.preserveReferences ?? false,
|
|
1600
|
+
referenceTokens
|
|
1601
|
+
});
|
|
1602
|
+
const header = block.description ? `/* ${block.key} */
|
|
1603
|
+
/* ${block.description} */` : `/* ${block.key} */`;
|
|
1604
|
+
cssBlocks.push(`${header}
|
|
1605
|
+
${css2}`);
|
|
1606
|
+
}
|
|
1607
|
+
return cssBlocks.join("\n");
|
|
1608
|
+
}
|
|
1609
|
+
collectTokensForModifierContext(modifierName, contextValue, permutations) {
|
|
1610
|
+
const expectedSource = `${modifierName}-${contextValue}`;
|
|
1611
|
+
let tokensFromSource = {};
|
|
1612
|
+
let referenceTokens = {};
|
|
1613
|
+
for (const { tokens, modifierInputs } of permutations) {
|
|
1614
|
+
if (modifierInputs[modifierName] !== contextValue) {
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
tokensFromSource = { ...tokensFromSource, ...filterTokensBySource(tokens, expectedSource) };
|
|
1618
|
+
referenceTokens = { ...referenceTokens, ...tokens };
|
|
1619
|
+
}
|
|
1620
|
+
return { tokensFromSource, referenceTokens };
|
|
1621
|
+
}
|
|
1622
|
+
async buildModifierContextFile(modifierName, contextValue, context, options) {
|
|
1623
|
+
const { tokensFromSource, referenceTokens } = this.collectTokensForModifierContext(
|
|
1624
|
+
modifierName,
|
|
1625
|
+
contextValue,
|
|
1626
|
+
context.permutations
|
|
1627
|
+
);
|
|
1628
|
+
if (Object.keys(tokensFromSource).length === 0) {
|
|
1629
|
+
return void 0;
|
|
1630
|
+
}
|
|
1631
|
+
const defaults = context.meta.defaults;
|
|
1632
|
+
const isBase = contextValue === defaults[modifierName];
|
|
1633
|
+
const modifierInputs = { ...defaults, [modifierName]: contextValue };
|
|
1634
|
+
const selector = resolveSelector(
|
|
1635
|
+
options.selector,
|
|
1636
|
+
modifierName,
|
|
1637
|
+
contextValue,
|
|
1638
|
+
isBase,
|
|
1639
|
+
modifierInputs
|
|
1640
|
+
);
|
|
1641
|
+
const mediaQuery = resolveMediaQuery(
|
|
1642
|
+
options.mediaQuery,
|
|
1643
|
+
modifierName,
|
|
1644
|
+
contextValue,
|
|
1645
|
+
isBase,
|
|
1646
|
+
modifierInputs
|
|
1647
|
+
);
|
|
1648
|
+
const content = await this.formatTokens(tokensFromSource, {
|
|
1649
|
+
selector,
|
|
1650
|
+
mediaQuery,
|
|
1651
|
+
minify: options.minify ?? false,
|
|
1652
|
+
preserveReferences: options.preserveReferences ?? false,
|
|
1653
|
+
referenceTokens
|
|
1654
|
+
});
|
|
1655
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
1656
|
+
outputName: context.output.name,
|
|
1657
|
+
extension: "css",
|
|
1658
|
+
modifierInputs,
|
|
1659
|
+
resolver: context.resolver,
|
|
1660
|
+
defaults: context.meta.defaults
|
|
1661
|
+
});
|
|
1662
|
+
return { fileName, content };
|
|
1663
|
+
}
|
|
1664
|
+
resolveModifierContext(modifierInputs, context, isBase) {
|
|
1665
|
+
if (!context.resolver.modifiers) {
|
|
1666
|
+
return { modifierName: "", modifierContext: "" };
|
|
1667
|
+
}
|
|
1668
|
+
const normalizedInputs = normalizeModifierInputs(modifierInputs);
|
|
1669
|
+
const normalizedDefaults = normalizeModifierInputs(context.meta.defaults);
|
|
1670
|
+
if (isBase) {
|
|
1671
|
+
const firstModifier = Object.keys(context.resolver.modifiers)[0] ?? "";
|
|
1672
|
+
return {
|
|
1673
|
+
modifierName: firstModifier,
|
|
1674
|
+
modifierContext: normalizedInputs[firstModifier] ?? ""
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
for (const [name, value] of Object.entries(normalizedInputs)) {
|
|
1678
|
+
if (value !== normalizedDefaults[name]) {
|
|
1679
|
+
return { modifierName: name, modifierContext: value };
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return { modifierName: "", modifierContext: "" };
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
function cssRenderer() {
|
|
1686
|
+
const rendererInstance = new CssRenderer();
|
|
1687
|
+
return {
|
|
1688
|
+
format: (context, options) => rendererInstance.format(
|
|
1689
|
+
context,
|
|
1690
|
+
options ?? context.output.options
|
|
1691
|
+
)
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/outputs/js/renderer.ts
|
|
1696
|
+
init_utils();
|
|
1697
|
+
init_token_utils();
|
|
1698
|
+
init_metadata();
|
|
1699
|
+
|
|
1700
|
+
// src/outputs/output-tree.ts
|
|
1701
|
+
var outputTree = (files) => {
|
|
1702
|
+
return {
|
|
1703
|
+
kind: "outputTree",
|
|
1704
|
+
files
|
|
1705
|
+
};
|
|
1706
|
+
};
|
|
1707
|
+
var isOutputTree = (value) => {
|
|
1708
|
+
return typeof value === "object" && value !== null && value.kind === "outputTree";
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
// src/outputs/js/renderer.ts
|
|
1712
|
+
var JsModuleRenderer = class {
|
|
1713
|
+
async format(context, options) {
|
|
1714
|
+
const opts = {
|
|
1715
|
+
preset: options?.preset ?? "standalone",
|
|
1716
|
+
structure: options?.structure ?? "nested",
|
|
1717
|
+
minify: options?.minify ?? false,
|
|
1718
|
+
moduleName: options?.moduleName ?? "tokens",
|
|
1719
|
+
generateHelper: options?.generateHelper ?? false
|
|
1720
|
+
};
|
|
1721
|
+
if (opts.preset === "bundle") {
|
|
1722
|
+
const { bundleAsJsModule: bundleAsJsModule2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
1723
|
+
const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
|
|
1724
|
+
tokens: stripInternalMetadata(tokens),
|
|
1725
|
+
modifierInputs,
|
|
1726
|
+
isBase: isBasePermutation(modifierInputs, context.meta.defaults)
|
|
1727
|
+
}));
|
|
1728
|
+
return await bundleAsJsModule2(bundleData, context.resolver, opts, async (tokens) => {
|
|
1729
|
+
return await this.formatTokens(tokens, opts);
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
assertFileRequired(context.buildPath, context.output.file, context.output.name, "JS module");
|
|
1733
|
+
const files = {};
|
|
1734
|
+
for (const { tokens, modifierInputs } of context.permutations) {
|
|
1735
|
+
const cleanTokens = stripInternalMetadata(tokens);
|
|
1736
|
+
const content = await this.formatTokens(cleanTokens, opts);
|
|
1737
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
1738
|
+
outputName: context.output.name,
|
|
1739
|
+
extension: "js",
|
|
1740
|
+
modifierInputs,
|
|
1741
|
+
resolver: context.resolver,
|
|
1742
|
+
defaults: context.meta.defaults
|
|
1743
|
+
});
|
|
1744
|
+
files[fileName] = content;
|
|
1745
|
+
}
|
|
1746
|
+
return outputTree(files);
|
|
1747
|
+
}
|
|
1748
|
+
async formatTokens(tokens, options) {
|
|
1749
|
+
const lines = [];
|
|
1750
|
+
lines.push(...this.formatAsObject(tokens, options));
|
|
1751
|
+
const code = lines.join("\n");
|
|
1752
|
+
if (options.minify) {
|
|
1753
|
+
return code;
|
|
1754
|
+
}
|
|
1755
|
+
return await prettier.format(code, {
|
|
1756
|
+
parser: "babel",
|
|
1757
|
+
printWidth: 80,
|
|
1758
|
+
tabWidth: 2,
|
|
1759
|
+
useTabs: false,
|
|
1760
|
+
semi: false,
|
|
1761
|
+
singleQuote: true,
|
|
1762
|
+
trailingComma: "es5"
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
formatAsObject(tokens, options) {
|
|
1766
|
+
const lines = [];
|
|
1767
|
+
const tokenMap = this.buildTokenMap(tokens);
|
|
1768
|
+
const varName = options.moduleName !== "" ? options.moduleName : "tokens";
|
|
1769
|
+
if (options.structure === "flat") {
|
|
1770
|
+
lines.push(`const ${varName} = {`);
|
|
1771
|
+
this.addFlatProperties(lines, tokens, 1);
|
|
1772
|
+
lines.push("}");
|
|
1773
|
+
} else {
|
|
1774
|
+
lines.push(`const ${varName} = {`);
|
|
1775
|
+
const tokenObj = this.tokensToPlainObject(tokens, "nested");
|
|
1776
|
+
this.addNestedProperties(lines, tokenObj, tokenMap, 1);
|
|
1777
|
+
lines.push("}");
|
|
1778
|
+
}
|
|
1779
|
+
lines.push("");
|
|
1780
|
+
lines.push(`export default ${varName}`);
|
|
1781
|
+
return lines;
|
|
1782
|
+
}
|
|
1783
|
+
buildTokenMap(tokens) {
|
|
1784
|
+
const map = /* @__PURE__ */ new Map();
|
|
1785
|
+
for (const [name, token] of Object.entries(tokens)) {
|
|
1786
|
+
map.set(name, token);
|
|
1787
|
+
}
|
|
1788
|
+
return map;
|
|
1789
|
+
}
|
|
1790
|
+
addFlatProperties(lines, tokens, indent) {
|
|
1791
|
+
const indentStr2 = " ".repeat(indent);
|
|
1792
|
+
const sortedEntries = getSortedTokenEntries(tokens);
|
|
1793
|
+
for (let i = 0; i < sortedEntries.length; i++) {
|
|
1794
|
+
const [name, token] = sortedEntries[i];
|
|
1795
|
+
const isLast = i === sortedEntries.length - 1;
|
|
1796
|
+
this.pushTokenComments(lines, token, indentStr2);
|
|
1797
|
+
lines.push(
|
|
1798
|
+
`${indentStr2}${this.quoteKey(name)}: ${JSON.stringify(token.$value)}${isLast ? "" : ","}`
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
pushTokenComments(lines, token, indent) {
|
|
1803
|
+
const deprecationComment = buildTokenDeprecationComment(token, "js");
|
|
1804
|
+
if (deprecationComment) {
|
|
1805
|
+
lines.push(`${indent}${deprecationComment}`);
|
|
1806
|
+
}
|
|
1807
|
+
const descriptionComment = buildTokenDescriptionComment(token, "js");
|
|
1808
|
+
if (descriptionComment) {
|
|
1809
|
+
lines.push(`${indent}${descriptionComment}`);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
tokensToPlainObject(tokens, structure) {
|
|
1813
|
+
if (structure === "nested") {
|
|
1814
|
+
return buildNestedTokenObject(tokens, (token) => token.$value);
|
|
1815
|
+
}
|
|
1816
|
+
const result = {};
|
|
1817
|
+
for (const [name, token] of getSortedTokenEntries(tokens)) {
|
|
1818
|
+
result[name] = token.$value;
|
|
1819
|
+
}
|
|
1820
|
+
return result;
|
|
1821
|
+
}
|
|
1822
|
+
addNestedProperties(lines, obj, tokenMap, indent) {
|
|
1823
|
+
const indentStr2 = " ".repeat(indent);
|
|
1824
|
+
const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
|
|
1825
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1826
|
+
const entry = entries[i];
|
|
1827
|
+
if (entry == null) {
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
const [key, value] = entry;
|
|
1831
|
+
const isLast = i === entries.length - 1;
|
|
1832
|
+
const isNestedObject = typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1833
|
+
if (!isNestedObject) {
|
|
1834
|
+
const token = tokenMap.get(key);
|
|
1835
|
+
if (token) {
|
|
1836
|
+
this.pushTokenComments(lines, token, indentStr2);
|
|
1837
|
+
}
|
|
1838
|
+
lines.push(
|
|
1839
|
+
`${indentStr2}${this.quoteKey(key)}: ${JSON.stringify(value)}${isLast ? "" : ","}`
|
|
1840
|
+
);
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
|
|
1844
|
+
this.addNestedProperties(lines, value, tokenMap, indent + 1);
|
|
1845
|
+
lines.push(`${indentStr2}}${isLast ? "" : ","}`);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
quoteKey(key) {
|
|
1849
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) {
|
|
1850
|
+
return key;
|
|
1851
|
+
}
|
|
1852
|
+
return `"${key}"`;
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
function jsRenderer() {
|
|
1856
|
+
const rendererInstance = new JsModuleRenderer();
|
|
1857
|
+
return {
|
|
1858
|
+
format: (context, options) => rendererInstance.format(
|
|
1859
|
+
context,
|
|
1860
|
+
options ?? context.output.options
|
|
1861
|
+
)
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// src/outputs/json/renderer.ts
|
|
1866
|
+
init_utils();
|
|
1867
|
+
init_token_utils();
|
|
1868
|
+
var JsonRenderer = class {
|
|
1869
|
+
async format(context, options) {
|
|
1870
|
+
const opts = {
|
|
1871
|
+
preset: options?.preset ?? "standalone",
|
|
1872
|
+
structure: options?.structure ?? "nested",
|
|
1873
|
+
minify: options?.minify ?? false,
|
|
1874
|
+
includeMetadata: options?.includeMetadata ?? false
|
|
1875
|
+
};
|
|
1876
|
+
if (opts.preset === "bundle") {
|
|
1877
|
+
const { bundleAsJson: bundleAsJson2 } = await Promise.resolve().then(() => (init_bundle2(), bundle_exports2));
|
|
1878
|
+
const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
|
|
1879
|
+
tokens: stripInternalMetadata(tokens),
|
|
1880
|
+
modifierInputs,
|
|
1881
|
+
isBase: isBasePermutation(modifierInputs, context.meta.defaults)
|
|
1882
|
+
}));
|
|
1883
|
+
return await bundleAsJson2(bundleData, context.resolver, async (tokens) => {
|
|
1884
|
+
return await this.formatTokens(tokens, opts);
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
assertFileRequired(context.buildPath, context.output.file, context.output.name, "JSON");
|
|
1888
|
+
const files = {};
|
|
1889
|
+
for (const { tokens, modifierInputs } of context.permutations) {
|
|
1890
|
+
const processedTokens = stripInternalMetadata(tokens);
|
|
1891
|
+
const content = await this.formatTokens(processedTokens, opts);
|
|
1892
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
1893
|
+
outputName: context.output.name,
|
|
1894
|
+
extension: "json",
|
|
1895
|
+
modifierInputs,
|
|
1896
|
+
resolver: context.resolver,
|
|
1897
|
+
defaults: context.meta.defaults
|
|
1898
|
+
});
|
|
1899
|
+
files[fileName] = content;
|
|
1900
|
+
}
|
|
1901
|
+
return outputTree(files);
|
|
1902
|
+
}
|
|
1903
|
+
async formatTokens(tokens, options) {
|
|
1904
|
+
const opts = {
|
|
1905
|
+
preset: options.preset ?? "standalone",
|
|
1906
|
+
structure: options.structure ?? "nested",
|
|
1907
|
+
minify: options.minify ?? false,
|
|
1908
|
+
includeMetadata: options.includeMetadata ?? false
|
|
1909
|
+
};
|
|
1910
|
+
let output;
|
|
1911
|
+
if (opts.structure === "flat") {
|
|
1912
|
+
output = opts.includeMetadata ? this.flattenTokens(tokens) : this.flattenValues(tokens);
|
|
1913
|
+
} else {
|
|
1914
|
+
output = opts.includeMetadata ? this.nestTokens(tokens) : this.nestValues(tokens);
|
|
1915
|
+
}
|
|
1916
|
+
const jsonString = JSON.stringify(output);
|
|
1917
|
+
if (!opts.minify) {
|
|
1918
|
+
return await prettier.format(jsonString, {
|
|
1919
|
+
parser: "json",
|
|
1920
|
+
printWidth: 80,
|
|
1921
|
+
tabWidth: 2,
|
|
1922
|
+
useTabs: false
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
return jsonString;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Flatten tokens to simple key-value pairs
|
|
1929
|
+
*/
|
|
1930
|
+
flattenValues(tokens) {
|
|
1931
|
+
const result = {};
|
|
1932
|
+
for (const [name, token] of getSortedTokenEntries(tokens)) {
|
|
1933
|
+
result[name] = token.$value;
|
|
1934
|
+
}
|
|
1935
|
+
return result;
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Flatten tokens to metadata objects
|
|
1939
|
+
*/
|
|
1940
|
+
flattenTokens(tokens) {
|
|
1941
|
+
const result = {};
|
|
1942
|
+
for (const [name, token] of getSortedTokenEntries(tokens)) {
|
|
1943
|
+
result[name] = this.serializeToken(token);
|
|
1944
|
+
}
|
|
1945
|
+
return result;
|
|
1946
|
+
}
|
|
1947
|
+
nestValues(tokens) {
|
|
1948
|
+
return buildNestedTokenObject(tokens, (token) => token.$value);
|
|
1949
|
+
}
|
|
1950
|
+
nestTokens(tokens) {
|
|
1951
|
+
return buildNestedTokenObject(tokens, (token) => this.serializeToken(token));
|
|
1952
|
+
}
|
|
1953
|
+
serializeToken(token) {
|
|
1954
|
+
return {
|
|
1955
|
+
$value: token.$value,
|
|
1956
|
+
...typeof token.$type === "string" && { $type: token.$type },
|
|
1957
|
+
...token.$description != null && token.$description !== "" && { $description: token.$description },
|
|
1958
|
+
...token.$deprecated != null && token.$deprecated !== false && { $deprecated: token.$deprecated },
|
|
1959
|
+
...token.$extensions != null && { $extensions: token.$extensions }
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
function jsonRenderer() {
|
|
1964
|
+
const rendererInstance = new JsonRenderer();
|
|
1965
|
+
return {
|
|
1966
|
+
format: (context, options) => rendererInstance.format(
|
|
1967
|
+
context,
|
|
1968
|
+
options ?? context.output.options
|
|
1969
|
+
)
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
// src/outputs/tailwind/renderer.ts
|
|
1974
|
+
init_token_utils();
|
|
1975
|
+
|
|
1976
|
+
// src/outputs/tailwind/presets/bundle.ts
|
|
1977
|
+
init_errors();
|
|
1978
|
+
init_utils();
|
|
1979
|
+
async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOverrideBlock) {
|
|
1980
|
+
const baseItem = bundleData.find((item) => item.isBase);
|
|
1981
|
+
if (!baseItem) {
|
|
1982
|
+
throw new BasePermutationError("Base permutation not found in bundle data");
|
|
1983
|
+
}
|
|
1984
|
+
const resolvedOpts = resolveOptions(options);
|
|
1985
|
+
const cssBlocks = [];
|
|
1986
|
+
const variantDeclarations = collectVariantDeclarations(bundleData, baseItem, resolvedOpts);
|
|
1987
|
+
const themeOpts = { ...resolvedOpts, variantDeclarations };
|
|
1988
|
+
const baseTokens = stripInternalMetadata(baseItem.tokens);
|
|
1989
|
+
const themeBlock = await formatThemeTokens(baseTokens, themeOpts);
|
|
1990
|
+
cssBlocks.push(themeBlock);
|
|
1991
|
+
for (const item of bundleData) {
|
|
1992
|
+
if (item.isBase) {
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
const block = await formatModifierOverride(item, baseItem, resolvedOpts, formatOverrideBlock);
|
|
1996
|
+
if (block) {
|
|
1997
|
+
cssBlocks.push(block);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
return cssBlocks.join("\n");
|
|
2001
|
+
}
|
|
2002
|
+
function resolveModifierSelectorAndMedia(options, modifier, context, modifierInputs) {
|
|
2003
|
+
const normalized = normalizeModifierInputs(modifierInputs);
|
|
2004
|
+
return {
|
|
2005
|
+
selector: resolveSelector(options.selector, modifier, context, false, normalized),
|
|
2006
|
+
mediaQuery: resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized)
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
|
|
2010
|
+
const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
|
|
2011
|
+
if (differenceCount > 1) {
|
|
2012
|
+
return void 0;
|
|
2013
|
+
}
|
|
2014
|
+
const tokensToInclude = filterTokensByValueChange(tokens, baseItem.tokens);
|
|
2015
|
+
if (Object.keys(tokensToInclude).length === 0) {
|
|
2016
|
+
return void 0;
|
|
2017
|
+
}
|
|
2018
|
+
const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
|
|
2019
|
+
const [modifier, context] = parseModifierSource(expectedSource);
|
|
2020
|
+
const cleanTokens = stripInternalMetadata(tokensToInclude);
|
|
2021
|
+
const { selector, mediaQuery } = resolveModifierSelectorAndMedia(
|
|
2022
|
+
options,
|
|
2023
|
+
modifier,
|
|
2024
|
+
context,
|
|
2025
|
+
modifierInputs
|
|
2026
|
+
);
|
|
2027
|
+
const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
|
|
2028
|
+
return `/* Modifier: ${modifier}=${context} */
|
|
2029
|
+
${css2}`;
|
|
2030
|
+
}
|
|
2031
|
+
function filterTokensByValueChange(currentTokens, baseTokens) {
|
|
2032
|
+
const changed = {};
|
|
2033
|
+
for (const [name, token] of Object.entries(currentTokens)) {
|
|
2034
|
+
const baseToken = baseTokens[name];
|
|
2035
|
+
if (!baseToken) {
|
|
2036
|
+
changed[name] = token;
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
if (JSON.stringify(token.$value) !== JSON.stringify(baseToken.$value)) {
|
|
2040
|
+
changed[name] = token;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
return changed;
|
|
2044
|
+
}
|
|
2045
|
+
function collectVariantDeclarations(bundleData, baseItem, options) {
|
|
2046
|
+
const declarations = [];
|
|
2047
|
+
for (const item of bundleData) {
|
|
2048
|
+
if (item.isBase) {
|
|
2049
|
+
continue;
|
|
2050
|
+
}
|
|
2051
|
+
const differenceCount = countModifierDifferences(item.modifierInputs, baseItem.modifierInputs);
|
|
2052
|
+
if (differenceCount > 1) {
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
const expectedSource = getExpectedSource(item.modifierInputs, baseItem.modifierInputs);
|
|
2056
|
+
const [modifier, context] = parseModifierSource(expectedSource);
|
|
2057
|
+
const variantName = `${modifier}-${context}`;
|
|
2058
|
+
const normalized = normalizeModifierInputs(item.modifierInputs);
|
|
2059
|
+
const mediaQuery = resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized);
|
|
2060
|
+
if (mediaQuery !== "") {
|
|
2061
|
+
declarations.push(`@custom-variant ${variantName} (@media ${mediaQuery});`);
|
|
2062
|
+
continue;
|
|
2063
|
+
}
|
|
2064
|
+
const selector = resolveSelector(options.selector, modifier, context, false, normalized);
|
|
2065
|
+
declarations.push(`@custom-variant ${variantName} (&:where(${selector}, ${selector} *));`);
|
|
2066
|
+
}
|
|
2067
|
+
return declarations;
|
|
2068
|
+
}
|
|
2069
|
+
function resolveOptions(options) {
|
|
2070
|
+
return {
|
|
2071
|
+
preset: options.preset ?? "bundle",
|
|
2072
|
+
includeImport: options.includeImport ?? true,
|
|
2073
|
+
namespace: options.namespace ?? "",
|
|
2074
|
+
minify: options.minify ?? false,
|
|
2075
|
+
selector: options.selector,
|
|
2076
|
+
mediaQuery: options.mediaQuery,
|
|
2077
|
+
variantDeclarations: []
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// src/outputs/tailwind/renderer.ts
|
|
2082
|
+
init_utils();
|
|
2083
|
+
init_metadata();
|
|
2084
|
+
var TAILWIND_NAMESPACE_MAP = {
|
|
2085
|
+
color: "color",
|
|
2086
|
+
dimension: "spacing",
|
|
2087
|
+
fontFamily: "font",
|
|
2088
|
+
fontWeight: "font-weight",
|
|
2089
|
+
duration: "duration",
|
|
2090
|
+
shadow: "shadow",
|
|
2091
|
+
number: "number",
|
|
2092
|
+
cubicBezier: "ease"
|
|
2093
|
+
};
|
|
2094
|
+
var TailwindRenderer = class {
|
|
2095
|
+
async format(context, options) {
|
|
2096
|
+
const opts = {
|
|
2097
|
+
preset: options?.preset ?? "bundle",
|
|
2098
|
+
includeImport: options?.includeImport ?? true,
|
|
2099
|
+
namespace: options?.namespace ?? "",
|
|
2100
|
+
minify: options?.minify ?? false,
|
|
2101
|
+
selector: options?.selector,
|
|
2102
|
+
mediaQuery: options?.mediaQuery,
|
|
2103
|
+
variantDeclarations: []
|
|
2104
|
+
};
|
|
2105
|
+
if (opts.preset === "bundle") {
|
|
2106
|
+
return await this.formatBundle(context, opts);
|
|
2107
|
+
}
|
|
2108
|
+
return await this.formatStandalone(context, opts);
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* Format tokens as Tailwind v4 @theme CSS variables
|
|
2112
|
+
*/
|
|
2113
|
+
async formatTokens(tokens, options) {
|
|
2114
|
+
const lines = [];
|
|
2115
|
+
const indent = options.minify ? "" : " ";
|
|
2116
|
+
const newline = options.minify ? "" : "\n";
|
|
2117
|
+
const space = options.minify ? "" : " ";
|
|
2118
|
+
if (options.includeImport) {
|
|
2119
|
+
lines.push(`@import "tailwindcss";${newline}`);
|
|
2120
|
+
}
|
|
2121
|
+
if (options.variantDeclarations.length > 0) {
|
|
2122
|
+
if (options.includeImport) {
|
|
2123
|
+
lines.push(newline);
|
|
2124
|
+
}
|
|
2125
|
+
for (const declaration of options.variantDeclarations) {
|
|
2126
|
+
lines.push(`${declaration}${newline}`);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
if (options.includeImport || options.variantDeclarations.length > 0) {
|
|
2130
|
+
lines.push(newline);
|
|
2131
|
+
}
|
|
2132
|
+
const themeDirective = options.namespace ? `@theme namespace(${options.namespace})` : "@theme";
|
|
2133
|
+
lines.push(`${themeDirective}${space}{${newline}`);
|
|
2134
|
+
for (const [, token] of getSortedTokenEntries(tokens)) {
|
|
2135
|
+
const varName = this.buildVariableName(token);
|
|
2136
|
+
const varValue = this.formatValue(token);
|
|
2137
|
+
const deprecationComment = buildTokenDeprecationComment(token, "tailwind");
|
|
2138
|
+
if (deprecationComment) {
|
|
2139
|
+
lines.push(`${indent}${deprecationComment}${newline}`);
|
|
2140
|
+
}
|
|
2141
|
+
const descriptionComment = buildTokenDescriptionComment(token, "tailwind");
|
|
2142
|
+
if (descriptionComment) {
|
|
2143
|
+
lines.push(`${indent}${descriptionComment}${newline}`);
|
|
2144
|
+
}
|
|
2145
|
+
lines.push(`${indent}--${varName}:${space}${varValue};${newline}`);
|
|
2146
|
+
}
|
|
2147
|
+
lines.push(`}${newline}`);
|
|
2148
|
+
const cssString = lines.join("");
|
|
2149
|
+
return options.minify ? cssString : await this.formatWithPrettier(cssString);
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Format tokens as plain CSS custom property overrides inside a selector block.
|
|
2153
|
+
* Used for modifier overrides (e.g., dark mode) appended after the @theme block.
|
|
2154
|
+
*/
|
|
2155
|
+
async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
|
|
2156
|
+
const indent = minify ? "" : " ";
|
|
2157
|
+
const newline = minify ? "" : "\n";
|
|
2158
|
+
const space = minify ? "" : " ";
|
|
2159
|
+
const hasMediaQuery = mediaQuery !== "";
|
|
2160
|
+
const tokenIndent = hasMediaQuery ? indent + indent : indent;
|
|
2161
|
+
const lines = [];
|
|
2162
|
+
if (hasMediaQuery) {
|
|
2163
|
+
lines.push(`@media ${mediaQuery}${space}{${newline}`);
|
|
2164
|
+
lines.push(`${indent}${selector}${space}{${newline}`);
|
|
2165
|
+
} else {
|
|
2166
|
+
lines.push(`${selector}${space}{${newline}`);
|
|
2167
|
+
}
|
|
2168
|
+
for (const [, token] of getSortedTokenEntries(tokens)) {
|
|
2169
|
+
const varName = this.buildVariableName(token);
|
|
2170
|
+
const varValue = this.formatValue(token);
|
|
2171
|
+
const deprecationComment = buildTokenDeprecationComment(token, "tailwind");
|
|
2172
|
+
if (deprecationComment) {
|
|
2173
|
+
lines.push(`${tokenIndent}${deprecationComment}${newline}`);
|
|
2174
|
+
}
|
|
2175
|
+
const descriptionComment = buildTokenDescriptionComment(token, "tailwind");
|
|
2176
|
+
if (descriptionComment) {
|
|
2177
|
+
lines.push(`${tokenIndent}${descriptionComment}${newline}`);
|
|
2178
|
+
}
|
|
2179
|
+
lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
|
|
2180
|
+
}
|
|
2181
|
+
if (hasMediaQuery) {
|
|
2182
|
+
lines.push(`${indent}}${newline}`);
|
|
2183
|
+
lines.push(`}${newline}`);
|
|
2184
|
+
} else {
|
|
2185
|
+
lines.push(`}${newline}`);
|
|
2186
|
+
}
|
|
2187
|
+
return lines.join("");
|
|
2188
|
+
}
|
|
2189
|
+
buildVariableName(token) {
|
|
2190
|
+
const prefix = TAILWIND_NAMESPACE_MAP[token.$type ?? ""];
|
|
2191
|
+
if (!prefix) {
|
|
2192
|
+
return token.name;
|
|
2193
|
+
}
|
|
2194
|
+
const nameLower = token.name.toLowerCase();
|
|
2195
|
+
const prefixLower = prefix.toLowerCase();
|
|
2196
|
+
if (nameLower.startsWith(`${prefixLower}-`) || nameLower.startsWith(`${prefixLower}.`)) {
|
|
2197
|
+
return token.name;
|
|
2198
|
+
}
|
|
2199
|
+
return `${prefix}-${token.name}`;
|
|
2200
|
+
}
|
|
2201
|
+
formatValue(token) {
|
|
2202
|
+
const value = token.$value;
|
|
2203
|
+
if (token.$type === "color" && isColorObject(value)) {
|
|
2204
|
+
return colorObjectToHex(value);
|
|
2205
|
+
}
|
|
2206
|
+
if (token.$type === "dimension" && isDimensionObject(value)) {
|
|
2207
|
+
return dimensionObjectToString(value);
|
|
2208
|
+
}
|
|
2209
|
+
if (token.$type === "duration" && isDurationObject(value)) {
|
|
2210
|
+
return durationObjectToString(value);
|
|
2211
|
+
}
|
|
2212
|
+
if (token.$type === "fontFamily") {
|
|
2213
|
+
if (Array.isArray(value)) {
|
|
2214
|
+
return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
|
|
2215
|
+
}
|
|
2216
|
+
return typeof value === "string" ? value : String(value);
|
|
2217
|
+
}
|
|
2218
|
+
if (token.$type === "shadow") {
|
|
2219
|
+
return this.formatShadowValue(value);
|
|
2220
|
+
}
|
|
2221
|
+
if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
|
|
2222
|
+
return `cubic-bezier(${value.join(", ")})`;
|
|
2223
|
+
}
|
|
2224
|
+
if (typeof value === "string") {
|
|
2225
|
+
return value;
|
|
2226
|
+
}
|
|
2227
|
+
if (typeof value === "number") {
|
|
2228
|
+
return String(value);
|
|
2229
|
+
}
|
|
2230
|
+
return String(value);
|
|
2231
|
+
}
|
|
2232
|
+
formatShadowValue(value) {
|
|
2233
|
+
if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object") {
|
|
2234
|
+
return value.map((s) => this.formatSingleShadow(s)).join(", ");
|
|
2235
|
+
}
|
|
2236
|
+
if (typeof value === "object" && value !== null) {
|
|
2237
|
+
return this.formatSingleShadow(value);
|
|
2238
|
+
}
|
|
2239
|
+
return String(value);
|
|
2240
|
+
}
|
|
2241
|
+
formatSingleShadow(shadow) {
|
|
2242
|
+
const parts = [];
|
|
2243
|
+
if (shadow.inset === true) {
|
|
2244
|
+
parts.push("inset");
|
|
2245
|
+
}
|
|
2246
|
+
if (isDimensionObject(shadow.offsetX)) {
|
|
2247
|
+
parts.push(dimensionObjectToString(shadow.offsetX));
|
|
2248
|
+
}
|
|
2249
|
+
if (isDimensionObject(shadow.offsetY)) {
|
|
2250
|
+
parts.push(dimensionObjectToString(shadow.offsetY));
|
|
2251
|
+
}
|
|
2252
|
+
if (isDimensionObject(shadow.blur)) {
|
|
2253
|
+
parts.push(dimensionObjectToString(shadow.blur));
|
|
2254
|
+
}
|
|
2255
|
+
if (shadow.spread != null && isDimensionObject(shadow.spread)) {
|
|
2256
|
+
parts.push(dimensionObjectToString(shadow.spread));
|
|
2257
|
+
}
|
|
2258
|
+
if (isColorObject(shadow.color)) {
|
|
2259
|
+
parts.push(colorObjectToHex(shadow.color));
|
|
2260
|
+
} else if (shadow.color != null) {
|
|
2261
|
+
parts.push(String(shadow.color));
|
|
2262
|
+
}
|
|
2263
|
+
return parts.join(" ");
|
|
2264
|
+
}
|
|
2265
|
+
async formatWithPrettier(css2) {
|
|
2266
|
+
try {
|
|
2267
|
+
return await prettier.format(css2, {
|
|
2268
|
+
parser: "css",
|
|
2269
|
+
printWidth: 80,
|
|
2270
|
+
tabWidth: 2,
|
|
2271
|
+
useTabs: false
|
|
2272
|
+
});
|
|
2273
|
+
} catch {
|
|
2274
|
+
return css2;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
async formatBundle(context, options) {
|
|
2278
|
+
const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
|
|
2279
|
+
tokens,
|
|
2280
|
+
modifierInputs,
|
|
2281
|
+
isBase: isBasePermutation(modifierInputs, context.meta.defaults)
|
|
2282
|
+
}));
|
|
2283
|
+
return await bundleAsTailwind(
|
|
2284
|
+
bundleData,
|
|
2285
|
+
options,
|
|
2286
|
+
async (tokens, opts) => await this.formatTokens(tokens, opts),
|
|
2287
|
+
async (tokens, selector, mediaQuery, minify) => await this.formatOverrideBlock(tokens, selector, mediaQuery, minify)
|
|
2288
|
+
);
|
|
2289
|
+
}
|
|
2290
|
+
async formatStandalone(context, options) {
|
|
2291
|
+
assertFileRequired(
|
|
2292
|
+
context.buildPath,
|
|
2293
|
+
context.output.file,
|
|
2294
|
+
context.output.name,
|
|
2295
|
+
"standalone Tailwind"
|
|
2296
|
+
);
|
|
2297
|
+
const files = {};
|
|
2298
|
+
for (const { tokens, modifierInputs } of context.permutations) {
|
|
2299
|
+
const processedTokens = stripInternalMetadata(tokens);
|
|
2300
|
+
const content = await this.formatTokens(processedTokens, options);
|
|
2301
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
2302
|
+
outputName: context.output.name,
|
|
2303
|
+
extension: "css",
|
|
2304
|
+
modifierInputs,
|
|
2305
|
+
resolver: context.resolver,
|
|
2306
|
+
defaults: context.meta.defaults
|
|
2307
|
+
});
|
|
2308
|
+
files[fileName] = content;
|
|
2309
|
+
}
|
|
2310
|
+
return outputTree(files);
|
|
2311
|
+
}
|
|
2312
|
+
};
|
|
2313
|
+
function tailwindRenderer() {
|
|
2314
|
+
const rendererInstance = new TailwindRenderer();
|
|
2315
|
+
return {
|
|
2316
|
+
format: (context, options) => rendererInstance.format(
|
|
2317
|
+
context,
|
|
2318
|
+
options ?? context.output.options
|
|
2319
|
+
)
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
// src/shared/token-types/types.ts
|
|
2324
|
+
function isShadowToken(token) {
|
|
2325
|
+
return token.$type === "shadow";
|
|
2326
|
+
}
|
|
2327
|
+
function isTypographyToken(token) {
|
|
2328
|
+
return token.$type === "typography";
|
|
2329
|
+
}
|
|
2330
|
+
function isBorderToken(token) {
|
|
2331
|
+
return token.$type === "border";
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
// src/outputs/ios/renderer.ts
|
|
2335
|
+
init_utils();
|
|
2336
|
+
init_metadata();
|
|
2337
|
+
var toSRGB = converter("rgb");
|
|
2338
|
+
var toP3 = converter("p3");
|
|
2339
|
+
var SWIFT_TYPE_GROUP_MAP = {
|
|
2340
|
+
color: "Colors",
|
|
2341
|
+
dimension: "Spacing",
|
|
2342
|
+
fontFamily: "Fonts",
|
|
2343
|
+
fontWeight: "FontWeights",
|
|
2344
|
+
duration: "Durations",
|
|
2345
|
+
shadow: "Shadows",
|
|
2346
|
+
typography: "Typography",
|
|
2347
|
+
number: "Numbers",
|
|
2348
|
+
cubicBezier: "Animations",
|
|
2349
|
+
border: "Borders",
|
|
2350
|
+
gradient: "Gradients"
|
|
2351
|
+
};
|
|
2352
|
+
var SWIFT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2353
|
+
"associatedtype",
|
|
2354
|
+
"class",
|
|
2355
|
+
"deinit",
|
|
2356
|
+
"enum",
|
|
2357
|
+
"extension",
|
|
2358
|
+
"fileprivate",
|
|
2359
|
+
"func",
|
|
2360
|
+
"import",
|
|
2361
|
+
"init",
|
|
2362
|
+
"inout",
|
|
2363
|
+
"internal",
|
|
2364
|
+
"let",
|
|
2365
|
+
"open",
|
|
2366
|
+
"operator",
|
|
2367
|
+
"private",
|
|
2368
|
+
"protocol",
|
|
2369
|
+
"public",
|
|
2370
|
+
"rethrows",
|
|
2371
|
+
"static",
|
|
2372
|
+
"struct",
|
|
2373
|
+
"subscript",
|
|
2374
|
+
"typealias",
|
|
2375
|
+
"var",
|
|
2376
|
+
"break",
|
|
2377
|
+
"case",
|
|
2378
|
+
"continue",
|
|
2379
|
+
"default",
|
|
2380
|
+
"defer",
|
|
2381
|
+
"do",
|
|
2382
|
+
"else",
|
|
2383
|
+
"fallthrough",
|
|
2384
|
+
"for",
|
|
2385
|
+
"guard",
|
|
2386
|
+
"if",
|
|
2387
|
+
"in",
|
|
2388
|
+
"repeat",
|
|
2389
|
+
"return",
|
|
2390
|
+
"switch",
|
|
2391
|
+
"where",
|
|
2392
|
+
"while",
|
|
2393
|
+
"as",
|
|
2394
|
+
"catch",
|
|
2395
|
+
"false",
|
|
2396
|
+
"is",
|
|
2397
|
+
"nil",
|
|
2398
|
+
"super",
|
|
2399
|
+
"self",
|
|
2400
|
+
"Self",
|
|
2401
|
+
"throw",
|
|
2402
|
+
"throws",
|
|
2403
|
+
"true",
|
|
2404
|
+
"try",
|
|
2405
|
+
"Type",
|
|
2406
|
+
"Protocol"
|
|
2407
|
+
]);
|
|
2408
|
+
var IosRenderer = class {
|
|
2409
|
+
async format(context, options) {
|
|
2410
|
+
const opts = {
|
|
2411
|
+
preset: options?.preset ?? "standalone",
|
|
2412
|
+
accessLevel: options?.accessLevel ?? "public",
|
|
2413
|
+
structure: options?.structure ?? "enum",
|
|
2414
|
+
enumName: options?.enumName ?? "DesignTokens",
|
|
2415
|
+
extensionNamespace: options?.extensionNamespace ?? "DesignTokens",
|
|
2416
|
+
colorSpace: options?.colorSpace ?? "sRGB",
|
|
2417
|
+
swiftVersion: options?.swiftVersion ?? "5.9",
|
|
2418
|
+
indent: options?.indent ?? 4,
|
|
2419
|
+
frozen: options?.frozen ?? false
|
|
2420
|
+
};
|
|
2421
|
+
return await this.formatStandalone(context, opts);
|
|
2422
|
+
}
|
|
2423
|
+
formatTokens(tokens, options) {
|
|
2424
|
+
const access = options.accessLevel;
|
|
2425
|
+
const groups = groupTokensByType(tokens, SWIFT_TYPE_GROUP_MAP);
|
|
2426
|
+
const imports = this.collectImports(tokens);
|
|
2427
|
+
const staticPrefix = this.staticLetPrefix(options);
|
|
2428
|
+
const frozen = this.frozenPrefix(options);
|
|
2429
|
+
const lines = [];
|
|
2430
|
+
lines.push(buildGeneratedFileHeader());
|
|
2431
|
+
lines.push("");
|
|
2432
|
+
for (const imp of imports) {
|
|
2433
|
+
lines.push(`import ${imp}`);
|
|
2434
|
+
}
|
|
2435
|
+
lines.push(...this.buildStructDefinitions(tokens, access, options));
|
|
2436
|
+
this.pushTokenLayout(lines, groups, options, access, staticPrefix, frozen);
|
|
2437
|
+
lines.push(...this.buildViewExtensions(tokens, access, options));
|
|
2438
|
+
if (options.structure !== "grouped") {
|
|
2439
|
+
lines.push("");
|
|
2440
|
+
}
|
|
2441
|
+
return lines.join("\n");
|
|
2442
|
+
}
|
|
2443
|
+
pushTokenLayout(lines, groups, options, access, staticPrefix, frozen) {
|
|
2444
|
+
const i1 = indentStr(options.indent, 1);
|
|
2445
|
+
const i2 = indentStr(options.indent, 2);
|
|
2446
|
+
if (options.structure === "grouped") {
|
|
2447
|
+
this.pushGroupedLayout(lines, groups, options, access, i1, i2, staticPrefix, frozen);
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
lines.push("");
|
|
2451
|
+
lines.push(`${frozen}${access} enum ${options.enumName} {`);
|
|
2452
|
+
for (const group of groups) {
|
|
2453
|
+
lines.push(`${i1}${frozen}${access} enum ${group.name} {`);
|
|
2454
|
+
this.pushTokenDeclarations(lines, group.tokens, options, access, i2, staticPrefix);
|
|
2455
|
+
lines.push(`${i1}}`);
|
|
2456
|
+
lines.push("");
|
|
2457
|
+
}
|
|
2458
|
+
lines.push("}");
|
|
2459
|
+
}
|
|
2460
|
+
pushGroupedLayout(lines, groups, options, access, i1, i2, staticPrefix, frozen) {
|
|
2461
|
+
const namespace = options.extensionNamespace;
|
|
2462
|
+
lines.push("");
|
|
2463
|
+
lines.push(`${frozen}${access} enum ${namespace} {}`);
|
|
2464
|
+
lines.push("");
|
|
2465
|
+
for (const group of groups) {
|
|
2466
|
+
lines.push(`${access} extension ${namespace} {`);
|
|
2467
|
+
lines.push(`${i1}${frozen}enum ${group.name} {`);
|
|
2468
|
+
this.pushTokenDeclarations(lines, group.tokens, options, access, i2, staticPrefix);
|
|
2469
|
+
lines.push(`${i1}}`);
|
|
2470
|
+
lines.push("}");
|
|
2471
|
+
lines.push("");
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
pushTokenDeclarations(lines, tokens, options, access, indent, staticPrefix) {
|
|
2475
|
+
for (const token of tokens) {
|
|
2476
|
+
const swiftName = this.buildQualifiedSwiftName(token);
|
|
2477
|
+
const swiftValue = this.formatSwiftValue(token, options);
|
|
2478
|
+
const typeAnnotation = this.getTypeAnnotation(token);
|
|
2479
|
+
const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
|
|
2480
|
+
const docComment = buildTokenDescriptionComment(token, "swift");
|
|
2481
|
+
if (docComment) {
|
|
2482
|
+
lines.push(`${indent}${docComment}`);
|
|
2483
|
+
}
|
|
2484
|
+
const deprecationAttr = buildSwiftDeprecationAttribute(token);
|
|
2485
|
+
if (deprecationAttr) {
|
|
2486
|
+
lines.push(`${indent}${deprecationAttr}`);
|
|
2487
|
+
}
|
|
2488
|
+
lines.push(`${indent}${access} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
collectImports(tokens) {
|
|
2492
|
+
const imports = /* @__PURE__ */ new Set();
|
|
2493
|
+
imports.add("SwiftUI");
|
|
2494
|
+
for (const [, token] of Object.entries(tokens)) {
|
|
2495
|
+
if (token.$type === "duration") {
|
|
2496
|
+
imports.add("Foundation");
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
return Array.from(imports).sort();
|
|
2500
|
+
}
|
|
2501
|
+
/**
|
|
2502
|
+
* Builds a qualified Swift name from a token's path, preserving parent
|
|
2503
|
+
* hierarchy segments to avoid duplicate identifiers.
|
|
2504
|
+
*
|
|
2505
|
+
* For example, `color.blue.400` in the `Colors` group becomes `blue400`
|
|
2506
|
+
* instead of just `_400`.
|
|
2507
|
+
*/
|
|
2508
|
+
buildQualifiedSwiftName(token) {
|
|
2509
|
+
const path = token.path;
|
|
2510
|
+
const withoutTypePrefix = path.length > 1 ? path.slice(1) : path;
|
|
2511
|
+
const joined = withoutTypePrefix.join("_");
|
|
2512
|
+
return toSafeIdentifier(joined, SWIFT_KEYWORDS, false);
|
|
2513
|
+
}
|
|
2514
|
+
formatSwiftValue(token, options) {
|
|
2515
|
+
const { $type, $value: value } = token;
|
|
2516
|
+
switch ($type) {
|
|
2517
|
+
case "color":
|
|
2518
|
+
return this.formatColorValue(value, options);
|
|
2519
|
+
case "dimension":
|
|
2520
|
+
return this.formatDimensionValue(value);
|
|
2521
|
+
case "fontFamily":
|
|
2522
|
+
return this.formatFontFamilyValue(value);
|
|
2523
|
+
case "fontWeight":
|
|
2524
|
+
return this.formatFontWeightValue(value);
|
|
2525
|
+
case "duration":
|
|
2526
|
+
return this.formatDurationValue(value);
|
|
2527
|
+
case "shadow":
|
|
2528
|
+
return this.formatShadowValue(value, options);
|
|
2529
|
+
case "typography":
|
|
2530
|
+
return this.formatTypographyValue(value);
|
|
2531
|
+
case "border":
|
|
2532
|
+
return this.formatBorderValue(value, options);
|
|
2533
|
+
case "gradient":
|
|
2534
|
+
return this.formatGradientValue(value, options);
|
|
2535
|
+
case "number":
|
|
2536
|
+
return String(value);
|
|
2537
|
+
case "cubicBezier":
|
|
2538
|
+
if (Array.isArray(value) && value.length === 4) {
|
|
2539
|
+
return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
|
|
2540
|
+
}
|
|
2541
|
+
break;
|
|
2542
|
+
}
|
|
2543
|
+
return this.formatSwiftPrimitive(value);
|
|
2544
|
+
}
|
|
2545
|
+
formatSwiftPrimitive(value) {
|
|
2546
|
+
if (typeof value === "string") {
|
|
2547
|
+
return `"${this.escapeSwiftString(value)}"`;
|
|
2548
|
+
}
|
|
2549
|
+
if (typeof value === "number") {
|
|
2550
|
+
return String(value);
|
|
2551
|
+
}
|
|
2552
|
+
if (typeof value === "boolean") {
|
|
2553
|
+
return value ? "true" : "false";
|
|
2554
|
+
}
|
|
2555
|
+
return `"${this.escapeSwiftString(String(value))}"`;
|
|
2556
|
+
}
|
|
2557
|
+
formatColorValue(value, options) {
|
|
2558
|
+
if (!isColorObject(value)) {
|
|
2559
|
+
return typeof value === "string" ? `Color("${this.escapeSwiftString(value)}")` : "Color.clear";
|
|
2560
|
+
}
|
|
2561
|
+
const colorObj = value;
|
|
2562
|
+
const alpha = colorObj.alpha ?? 1;
|
|
2563
|
+
if (options.colorSpace === "displayP3") {
|
|
2564
|
+
const p3 = toP3(dtcgObjectToCulori(colorObj));
|
|
2565
|
+
const r2 = this.roundComponent(p3?.r ?? 0);
|
|
2566
|
+
const g2 = this.roundComponent(p3?.g ?? 0);
|
|
2567
|
+
const b2 = this.roundComponent(p3?.b ?? 0);
|
|
2568
|
+
return alpha < 1 ? `Color(.displayP3, red: ${r2}, green: ${g2}, blue: ${b2}, opacity: ${this.roundComponent(alpha)})` : `Color(.displayP3, red: ${r2}, green: ${g2}, blue: ${b2})`;
|
|
2569
|
+
}
|
|
2570
|
+
const rgb = toSRGB(dtcgObjectToCulori(colorObj));
|
|
2571
|
+
const r = this.roundComponent(rgb?.r ?? 0);
|
|
2572
|
+
const g = this.roundComponent(rgb?.g ?? 0);
|
|
2573
|
+
const b = this.roundComponent(rgb?.b ?? 0);
|
|
2574
|
+
return alpha < 1 ? `Color(red: ${r}, green: ${g}, blue: ${b}, opacity: ${this.roundComponent(alpha)})` : `Color(red: ${r}, green: ${g}, blue: ${b})`;
|
|
2575
|
+
}
|
|
2576
|
+
formatDimensionValue(value) {
|
|
2577
|
+
if (isDimensionObject(value)) {
|
|
2578
|
+
return this.dimensionToPoints(value);
|
|
2579
|
+
}
|
|
2580
|
+
return String(value);
|
|
2581
|
+
}
|
|
2582
|
+
formatFontFamilyValue(value) {
|
|
2583
|
+
if (Array.isArray(value)) {
|
|
2584
|
+
const primary = value[0];
|
|
2585
|
+
return typeof primary === "string" ? `"${this.escapeSwiftString(primary)}"` : '"system"';
|
|
2586
|
+
}
|
|
2587
|
+
return typeof value === "string" ? `"${this.escapeSwiftString(value)}"` : '"system"';
|
|
2588
|
+
}
|
|
2589
|
+
formatFontWeightValue(value) {
|
|
2590
|
+
if (typeof value === "number") {
|
|
2591
|
+
return this.numericFontWeight(value);
|
|
2592
|
+
}
|
|
2593
|
+
if (typeof value === "string") {
|
|
2594
|
+
return this.namedFontWeight(value) ?? "Font.Weight.regular";
|
|
2595
|
+
}
|
|
2596
|
+
return "Font.Weight.regular";
|
|
2597
|
+
}
|
|
2598
|
+
numericFontWeight(weight) {
|
|
2599
|
+
if (weight <= 100) {
|
|
2600
|
+
return "Font.Weight.ultraLight";
|
|
2601
|
+
}
|
|
2602
|
+
if (weight <= 200) {
|
|
2603
|
+
return "Font.Weight.thin";
|
|
2604
|
+
}
|
|
2605
|
+
if (weight <= 300) {
|
|
2606
|
+
return "Font.Weight.light";
|
|
2607
|
+
}
|
|
2608
|
+
if (weight <= 400) {
|
|
2609
|
+
return "Font.Weight.regular";
|
|
2610
|
+
}
|
|
2611
|
+
if (weight <= 500) {
|
|
2612
|
+
return "Font.Weight.medium";
|
|
2613
|
+
}
|
|
2614
|
+
if (weight <= 600) {
|
|
2615
|
+
return "Font.Weight.semibold";
|
|
2616
|
+
}
|
|
2617
|
+
if (weight <= 700) {
|
|
2618
|
+
return "Font.Weight.bold";
|
|
2619
|
+
}
|
|
2620
|
+
if (weight <= 800) {
|
|
2621
|
+
return "Font.Weight.heavy";
|
|
2622
|
+
}
|
|
2623
|
+
return "Font.Weight.black";
|
|
2624
|
+
}
|
|
2625
|
+
namedFontWeight(name) {
|
|
2626
|
+
const map = {
|
|
2627
|
+
thin: "Font.Weight.thin",
|
|
2628
|
+
ultralight: "Font.Weight.ultraLight",
|
|
2629
|
+
extralight: "Font.Weight.ultraLight",
|
|
2630
|
+
light: "Font.Weight.light",
|
|
2631
|
+
regular: "Font.Weight.regular",
|
|
2632
|
+
normal: "Font.Weight.regular",
|
|
2633
|
+
medium: "Font.Weight.medium",
|
|
2634
|
+
semibold: "Font.Weight.semibold",
|
|
2635
|
+
demibold: "Font.Weight.semibold",
|
|
2636
|
+
bold: "Font.Weight.bold",
|
|
2637
|
+
heavy: "Font.Weight.heavy",
|
|
2638
|
+
extrabold: "Font.Weight.heavy",
|
|
2639
|
+
black: "Font.Weight.black",
|
|
2640
|
+
ultrabold: "Font.Weight.black"
|
|
2641
|
+
};
|
|
2642
|
+
return map[name.toLowerCase()];
|
|
2643
|
+
}
|
|
2644
|
+
formatDurationValue(value) {
|
|
2645
|
+
if (isDurationObject(value)) {
|
|
2646
|
+
const dur = value;
|
|
2647
|
+
const seconds = dur.unit === "ms" ? dur.value / 1e3 : dur.value;
|
|
2648
|
+
return String(seconds);
|
|
2649
|
+
}
|
|
2650
|
+
return typeof value === "number" ? String(value) : "0";
|
|
2651
|
+
}
|
|
2652
|
+
formatShadowValue(value, options) {
|
|
2653
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
2654
|
+
return this.formatSingleShadow(value[0], options);
|
|
2655
|
+
}
|
|
2656
|
+
if (typeof value === "object" && value !== null) {
|
|
2657
|
+
return this.formatSingleShadow(value, options);
|
|
2658
|
+
}
|
|
2659
|
+
return "ShadowStyle(color: .clear, radius: 0, x: 0, y: 0, spread: 0)";
|
|
2660
|
+
}
|
|
2661
|
+
formatSingleShadow(shadow, options) {
|
|
2662
|
+
const color = isColorObject(shadow.color) ? this.formatColorValue(shadow.color, options) : "Color.black.opacity(0.25)";
|
|
2663
|
+
const radius = isDimensionObject(shadow.blur) ? this.dimensionToCGFloat(shadow.blur) : "8";
|
|
2664
|
+
const x = isDimensionObject(shadow.offsetX) ? this.dimensionToCGFloat(shadow.offsetX) : "0";
|
|
2665
|
+
const y = isDimensionObject(shadow.offsetY) ? this.dimensionToCGFloat(shadow.offsetY) : "0";
|
|
2666
|
+
const spread = isDimensionObject(shadow.spread) ? this.dimensionToCGFloat(shadow.spread) : "0";
|
|
2667
|
+
return `ShadowStyle(color: ${color}, radius: ${radius}, x: ${x}, y: ${y}, spread: ${spread})`;
|
|
2668
|
+
}
|
|
2669
|
+
formatTypographyValue(value) {
|
|
2670
|
+
if (typeof value !== "object" || value === null) {
|
|
2671
|
+
return "TypographyStyle(font: Font.body, tracking: 0, lineSpacing: 0)";
|
|
2672
|
+
}
|
|
2673
|
+
const typo = value;
|
|
2674
|
+
const size = isDimensionObject(typo.fontSize) ? this.dimensionToPoints(typo.fontSize) : "16";
|
|
2675
|
+
const weight = typo.fontWeight != null ? this.formatFontWeightValue(typo.fontWeight) : "Font.Weight.regular";
|
|
2676
|
+
const fontExpr = this.buildFontExpression(typo, size, weight);
|
|
2677
|
+
const tracking = this.extractTracking(typo);
|
|
2678
|
+
const lineSpacing = this.extractLineSpacing(typo);
|
|
2679
|
+
return `TypographyStyle(font: ${fontExpr}, tracking: ${tracking}, lineSpacing: ${lineSpacing})`;
|
|
2680
|
+
}
|
|
2681
|
+
buildFontExpression(typo, size, weight) {
|
|
2682
|
+
if (typo.fontFamily != null) {
|
|
2683
|
+
const family = Array.isArray(typo.fontFamily) ? typo.fontFamily[0] : typo.fontFamily;
|
|
2684
|
+
if (typeof family === "string") {
|
|
2685
|
+
return `Font.custom("${this.escapeSwiftString(family)}", size: ${size}).weight(${weight})`;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
return `Font.system(size: ${size}, weight: ${weight})`;
|
|
2689
|
+
}
|
|
2690
|
+
extractTracking(typo) {
|
|
2691
|
+
if (!isDimensionObject(typo.letterSpacing)) {
|
|
2692
|
+
return "0";
|
|
2693
|
+
}
|
|
2694
|
+
return this.dimensionToPoints(typo.letterSpacing);
|
|
2695
|
+
}
|
|
2696
|
+
extractLineSpacing(typo) {
|
|
2697
|
+
if (typo.lineHeight == null || typeof typo.lineHeight !== "number") {
|
|
2698
|
+
return "0";
|
|
2699
|
+
}
|
|
2700
|
+
if (!isDimensionObject(typo.fontSize)) {
|
|
2701
|
+
return "0";
|
|
2702
|
+
}
|
|
2703
|
+
const basePt = this.dimensionToNumericPoints(typo.fontSize);
|
|
2704
|
+
const lineHeightPt = Math.round(basePt * typo.lineHeight * 100) / 100;
|
|
2705
|
+
return String(lineHeightPt - basePt);
|
|
2706
|
+
}
|
|
2707
|
+
dimensionToNumericPoints(dim) {
|
|
2708
|
+
return dim.unit === "rem" ? dim.value * 16 : dim.value;
|
|
2709
|
+
}
|
|
2710
|
+
dimensionToPoints(dim) {
|
|
2711
|
+
return String(this.dimensionToNumericPoints(dim));
|
|
2712
|
+
}
|
|
2713
|
+
/** Formats a dimension as a CGFloat literal (appends `.0` for integers). */
|
|
2714
|
+
dimensionToCGFloat(dim) {
|
|
2715
|
+
const ptValue = this.dimensionToNumericPoints(dim);
|
|
2716
|
+
return Number.isInteger(ptValue) ? `${ptValue}.0` : String(ptValue);
|
|
2717
|
+
}
|
|
2718
|
+
getTypeAnnotation(token) {
|
|
2719
|
+
switch (token.$type) {
|
|
2720
|
+
case "dimension":
|
|
2721
|
+
return "CGFloat";
|
|
2722
|
+
case "duration":
|
|
2723
|
+
return "TimeInterval";
|
|
2724
|
+
case "number":
|
|
2725
|
+
return "Double";
|
|
2726
|
+
case "fontWeight":
|
|
2727
|
+
return "Font.Weight";
|
|
2728
|
+
case "fontFamily":
|
|
2729
|
+
return "String";
|
|
2730
|
+
default:
|
|
2731
|
+
return void 0;
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
escapeSwiftString(str) {
|
|
2735
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
2736
|
+
}
|
|
2737
|
+
roundComponent(value) {
|
|
2738
|
+
return Math.round(value * 1e4) / 1e4;
|
|
2739
|
+
}
|
|
2740
|
+
/**
|
|
2741
|
+
* Returns the prefix for `static let` declarations.
|
|
2742
|
+
* Swift 6 requires `nonisolated(unsafe)` on global stored properties.
|
|
2743
|
+
*/
|
|
2744
|
+
staticLetPrefix(options) {
|
|
2745
|
+
return options.swiftVersion === "6.0" ? "nonisolated(unsafe) static let " : "static let ";
|
|
2746
|
+
}
|
|
2747
|
+
/** Returns `@frozen ` when the frozen option is enabled, empty string otherwise. */
|
|
2748
|
+
frozenPrefix(options) {
|
|
2749
|
+
return options.frozen ? "@frozen " : "";
|
|
2750
|
+
}
|
|
2751
|
+
/** Returns `: Sendable` when targeting Swift 6, empty string otherwise. */
|
|
2752
|
+
structConformances(options) {
|
|
2753
|
+
return options.swiftVersion === "6.0" ? ": Sendable" : "";
|
|
2754
|
+
}
|
|
2755
|
+
/** Emits all struct definitions needed by the token set. */
|
|
2756
|
+
buildStructDefinitions(tokens, access, options) {
|
|
2757
|
+
const lines = [];
|
|
2758
|
+
if (Object.values(tokens).some(isShadowToken)) {
|
|
2759
|
+
lines.push("");
|
|
2760
|
+
lines.push(...this.buildShadowStyleStruct(access, options));
|
|
2761
|
+
}
|
|
2762
|
+
if (Object.values(tokens).some(isTypographyToken)) {
|
|
2763
|
+
lines.push("");
|
|
2764
|
+
lines.push(...this.buildTypographyStyleStruct(access, options));
|
|
2765
|
+
}
|
|
2766
|
+
if (Object.values(tokens).some(isBorderToken)) {
|
|
2767
|
+
lines.push("");
|
|
2768
|
+
lines.push(...this.buildBorderStyleStruct(access, options));
|
|
2769
|
+
}
|
|
2770
|
+
return lines;
|
|
2771
|
+
}
|
|
2772
|
+
buildShadowStyleStruct(access, options) {
|
|
2773
|
+
const i1 = indentStr(options.indent, 1);
|
|
2774
|
+
const conformances = this.structConformances(options);
|
|
2775
|
+
const frozen = this.frozenPrefix(options);
|
|
2776
|
+
return [
|
|
2777
|
+
`${frozen}${access} struct ShadowStyle${conformances} {`,
|
|
2778
|
+
`${i1}${access} let color: Color`,
|
|
2779
|
+
`${i1}${access} let radius: CGFloat`,
|
|
2780
|
+
`${i1}${access} let x: CGFloat`,
|
|
2781
|
+
`${i1}${access} let y: CGFloat`,
|
|
2782
|
+
`${i1}${access} let spread: CGFloat`,
|
|
2783
|
+
"}"
|
|
2784
|
+
];
|
|
2785
|
+
}
|
|
2786
|
+
buildTypographyStyleStruct(access, options) {
|
|
2787
|
+
const i1 = indentStr(options.indent, 1);
|
|
2788
|
+
const conformances = this.structConformances(options);
|
|
2789
|
+
const frozen = this.frozenPrefix(options);
|
|
2790
|
+
return [
|
|
2791
|
+
`${frozen}${access} struct TypographyStyle${conformances} {`,
|
|
2792
|
+
`${i1}${access} let font: Font`,
|
|
2793
|
+
`${i1}${access} let tracking: CGFloat`,
|
|
2794
|
+
`${i1}${access} let lineSpacing: CGFloat`,
|
|
2795
|
+
"}"
|
|
2796
|
+
];
|
|
2797
|
+
}
|
|
2798
|
+
buildBorderStyleStruct(access, options) {
|
|
2799
|
+
const i1 = indentStr(options.indent, 1);
|
|
2800
|
+
const conformances = this.structConformances(options);
|
|
2801
|
+
const frozen = this.frozenPrefix(options);
|
|
2802
|
+
return [
|
|
2803
|
+
`${frozen}${access} struct BorderStyle${conformances} {`,
|
|
2804
|
+
`${i1}${access} let color: Color`,
|
|
2805
|
+
`${i1}${access} let width: CGFloat`,
|
|
2806
|
+
"}"
|
|
2807
|
+
];
|
|
2808
|
+
}
|
|
2809
|
+
/** Emits convenience View extensions for shadow and typography application. */
|
|
2810
|
+
buildViewExtensions(tokens, access, options) {
|
|
2811
|
+
const lines = [];
|
|
2812
|
+
const i1 = indentStr(options.indent, 1);
|
|
2813
|
+
const i2 = indentStr(options.indent, 2);
|
|
2814
|
+
if (Object.values(tokens).some(isShadowToken)) {
|
|
2815
|
+
lines.push("");
|
|
2816
|
+
lines.push(`${access} extension View {`);
|
|
2817
|
+
lines.push(`${i1}func shadowStyle(_ style: ShadowStyle) -> some View {`);
|
|
2818
|
+
lines.push(
|
|
2819
|
+
`${i2}self.shadow(color: style.color, radius: style.radius, x: style.x, y: style.y)`
|
|
2820
|
+
);
|
|
2821
|
+
lines.push(`${i1}}`);
|
|
2822
|
+
lines.push("}");
|
|
2823
|
+
}
|
|
2824
|
+
if (Object.values(tokens).some(isTypographyToken)) {
|
|
2825
|
+
lines.push("");
|
|
2826
|
+
lines.push(`${access} extension View {`);
|
|
2827
|
+
lines.push(`${i1}func typographyStyle(_ style: TypographyStyle) -> some View {`);
|
|
2828
|
+
lines.push(
|
|
2829
|
+
`${i2}self.font(style.font).tracking(style.tracking).lineSpacing(style.lineSpacing)`
|
|
2830
|
+
);
|
|
2831
|
+
lines.push(`${i1}}`);
|
|
2832
|
+
lines.push("}");
|
|
2833
|
+
}
|
|
2834
|
+
return lines;
|
|
2835
|
+
}
|
|
2836
|
+
formatBorderValue(value, options) {
|
|
2837
|
+
if (typeof value !== "object" || value === null) {
|
|
2838
|
+
return "BorderStyle(color: .clear, width: 0)";
|
|
2839
|
+
}
|
|
2840
|
+
const border = value;
|
|
2841
|
+
const color = isColorObject(border.color) ? this.formatColorValue(border.color, options) : "Color.clear";
|
|
2842
|
+
const width = isDimensionObject(border.width) ? this.dimensionToCGFloat(border.width) : "1.0";
|
|
2843
|
+
return `BorderStyle(color: ${color}, width: ${width})`;
|
|
2844
|
+
}
|
|
2845
|
+
formatGradientValue(value, options) {
|
|
2846
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
2847
|
+
return "Gradient(stops: [])";
|
|
2848
|
+
}
|
|
2849
|
+
const stops = value.map((stop) => {
|
|
2850
|
+
const color = isColorObject(stop.color) ? this.formatColorValue(stop.color, options) : "Color.clear";
|
|
2851
|
+
return `.init(color: ${color}, location: ${stop.position})`;
|
|
2852
|
+
});
|
|
2853
|
+
return `Gradient(stops: [${stops.join(", ")}])`;
|
|
2854
|
+
}
|
|
2855
|
+
async formatStandalone(context, options) {
|
|
2856
|
+
assertFileRequired(
|
|
2857
|
+
context.buildPath,
|
|
2858
|
+
context.output.file,
|
|
2859
|
+
context.output.name,
|
|
2860
|
+
"standalone iOS"
|
|
2861
|
+
);
|
|
2862
|
+
const files = {};
|
|
2863
|
+
for (const { tokens, modifierInputs } of context.permutations) {
|
|
2864
|
+
const processedTokens = stripInternalMetadata(tokens);
|
|
2865
|
+
const content = this.formatTokens(processedTokens, options);
|
|
2866
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
2867
|
+
outputName: context.output.name,
|
|
2868
|
+
extension: "swift",
|
|
2869
|
+
modifierInputs,
|
|
2870
|
+
resolver: context.resolver,
|
|
2871
|
+
defaults: context.meta.defaults
|
|
2872
|
+
});
|
|
2873
|
+
files[fileName] = content;
|
|
2874
|
+
}
|
|
2875
|
+
return outputTree(files);
|
|
2876
|
+
}
|
|
2877
|
+
};
|
|
2878
|
+
function iosRenderer() {
|
|
2879
|
+
const rendererInstance = new IosRenderer();
|
|
2880
|
+
return {
|
|
2881
|
+
format: (context, options) => rendererInstance.format(
|
|
2882
|
+
context,
|
|
2883
|
+
options ?? context.output.options
|
|
2884
|
+
)
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
// src/outputs/android/renderer.ts
|
|
2889
|
+
init_errors();
|
|
2890
|
+
init_token_utils();
|
|
2891
|
+
init_utils();
|
|
2892
|
+
init_metadata();
|
|
2893
|
+
var toSRGB2 = converter("rgb");
|
|
2894
|
+
var toP32 = converter("p3");
|
|
2895
|
+
var KOTLIN_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2896
|
+
"val",
|
|
2897
|
+
"var",
|
|
2898
|
+
"fun",
|
|
2899
|
+
"class",
|
|
2900
|
+
"object",
|
|
2901
|
+
"when",
|
|
2902
|
+
"is",
|
|
2903
|
+
"in",
|
|
2904
|
+
"return",
|
|
2905
|
+
"break",
|
|
2906
|
+
"continue",
|
|
2907
|
+
"do",
|
|
2908
|
+
"while",
|
|
2909
|
+
"for",
|
|
2910
|
+
"if",
|
|
2911
|
+
"else",
|
|
2912
|
+
"try",
|
|
2913
|
+
"catch",
|
|
2914
|
+
"throw",
|
|
2915
|
+
"as",
|
|
2916
|
+
"this",
|
|
2917
|
+
"super",
|
|
2918
|
+
"null",
|
|
2919
|
+
"true",
|
|
2920
|
+
"false"
|
|
2921
|
+
]);
|
|
2922
|
+
var KOTLIN_TYPE_GROUP_MAP = {
|
|
2923
|
+
color: "Colors",
|
|
2924
|
+
dimension: "Spacing",
|
|
2925
|
+
fontFamily: "Fonts",
|
|
2926
|
+
fontWeight: "FontWeights",
|
|
2927
|
+
duration: "Durations",
|
|
2928
|
+
shadow: "Shadows",
|
|
2929
|
+
typography: "Typography",
|
|
2930
|
+
number: "Numbers",
|
|
2931
|
+
cubicBezier: "Animations",
|
|
2932
|
+
border: "Borders"
|
|
2933
|
+
};
|
|
2934
|
+
function resolveColorFormat(format) {
|
|
2935
|
+
if (format === "argb_floats" || format === "argb_float") {
|
|
2936
|
+
return "argb_float";
|
|
2937
|
+
}
|
|
2938
|
+
return "argb_hex";
|
|
2939
|
+
}
|
|
2940
|
+
function escapeKotlinString(str) {
|
|
2941
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
|
|
2942
|
+
}
|
|
2943
|
+
function formatKotlinNumber(value) {
|
|
2944
|
+
return Number.isInteger(value) ? `${value}.0` : String(value);
|
|
2945
|
+
}
|
|
2946
|
+
function roundComponent(value) {
|
|
2947
|
+
return Math.round(value * 1e3) / 1e3;
|
|
2948
|
+
}
|
|
2949
|
+
function toResourceName(family) {
|
|
2950
|
+
return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
2951
|
+
}
|
|
2952
|
+
var AndroidRenderer = class {
|
|
2953
|
+
async format(context, options) {
|
|
2954
|
+
if (!options?.packageName) {
|
|
2955
|
+
throw new ConfigurationError(
|
|
2956
|
+
`Output "${context.output.name}": packageName is required for Android output`
|
|
2957
|
+
);
|
|
2958
|
+
}
|
|
2959
|
+
const visibility = options?.visibility;
|
|
2960
|
+
const opts = {
|
|
2961
|
+
preset: options?.preset ?? "standalone",
|
|
2962
|
+
packageName: options.packageName,
|
|
2963
|
+
objectName: options?.objectName ?? "DesignTokens",
|
|
2964
|
+
colorFormat: resolveColorFormat(options?.colorFormat),
|
|
2965
|
+
colorSpace: options?.colorSpace ?? "sRGB",
|
|
2966
|
+
structure: options?.structure ?? "nested",
|
|
2967
|
+
visibility,
|
|
2968
|
+
visPrefix: visibility ? `${visibility} ` : "",
|
|
2969
|
+
indent: options?.indent ?? 4
|
|
2970
|
+
};
|
|
2971
|
+
if (opts.preset === "bundle") {
|
|
2972
|
+
return await this.formatBundle(context, opts);
|
|
2973
|
+
}
|
|
2974
|
+
return await this.formatStandalone(context, opts);
|
|
2975
|
+
}
|
|
2976
|
+
// -----------------------------------------------------------------------
|
|
2977
|
+
// Token tree (nested mode)
|
|
2978
|
+
// -----------------------------------------------------------------------
|
|
2979
|
+
buildTokenTree(tokens) {
|
|
2980
|
+
const root = { children: /* @__PURE__ */ new Map() };
|
|
2981
|
+
for (const [, token] of getSortedTokenEntries(tokens)) {
|
|
2982
|
+
let current = root;
|
|
2983
|
+
const segments = token.path;
|
|
2984
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
2985
|
+
const seg = segments[i];
|
|
2986
|
+
if (!current.children.has(seg)) {
|
|
2987
|
+
current.children.set(seg, { children: /* @__PURE__ */ new Map() });
|
|
2988
|
+
}
|
|
2989
|
+
current = current.children.get(seg);
|
|
2990
|
+
}
|
|
2991
|
+
const leafName = segments[segments.length - 1] ?? token.name;
|
|
2992
|
+
const leaf = current.children.get(leafName) ?? { children: /* @__PURE__ */ new Map() };
|
|
2993
|
+
leaf.token = token;
|
|
2994
|
+
current.children.set(leafName, leaf);
|
|
2995
|
+
}
|
|
2996
|
+
return root;
|
|
2997
|
+
}
|
|
2998
|
+
// -----------------------------------------------------------------------
|
|
2999
|
+
// Flat structure grouping
|
|
3000
|
+
// -----------------------------------------------------------------------
|
|
3001
|
+
/**
|
|
3002
|
+
* Builds a flattened camelCase name from a token's path, stripping the
|
|
3003
|
+
* type prefix segment (which is already represented by the group object).
|
|
3004
|
+
*/
|
|
3005
|
+
buildFlatKotlinName(token) {
|
|
3006
|
+
const path = token.path;
|
|
3007
|
+
const withoutTypePrefix = path.length > 1 ? path.slice(1) : path;
|
|
3008
|
+
const joined = withoutTypePrefix.join("_");
|
|
3009
|
+
return toSafeIdentifier(joined, KOTLIN_KEYWORDS, false);
|
|
3010
|
+
}
|
|
3011
|
+
// -----------------------------------------------------------------------
|
|
3012
|
+
// Rendering
|
|
3013
|
+
// -----------------------------------------------------------------------
|
|
3014
|
+
formatTokens(tokens, options) {
|
|
3015
|
+
if (options.structure === "flat") {
|
|
3016
|
+
return this.formatAsFlat(tokens, options);
|
|
3017
|
+
}
|
|
3018
|
+
return this.formatAsNested(tokens, options);
|
|
3019
|
+
}
|
|
3020
|
+
formatAsNested(tokens, options) {
|
|
3021
|
+
const tokenTypes = this.collectTokenTypesFromEntries(tokens);
|
|
3022
|
+
const tree = this.buildTokenTree(tokens);
|
|
3023
|
+
return this.buildFile(tokenTypes, options, (lines) => {
|
|
3024
|
+
lines.push(`@Suppress("unused")`);
|
|
3025
|
+
lines.push(`${options.visPrefix}object ${options.objectName} {`);
|
|
3026
|
+
this.renderTreeChildren(lines, tree, 1, options);
|
|
3027
|
+
lines.push("}");
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
formatAsFlat(tokens, options) {
|
|
3031
|
+
const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
|
|
3032
|
+
const tokenTypes = this.collectTokenTypesFromEntries(tokens);
|
|
3033
|
+
return this.buildFile(tokenTypes, options, (lines) => {
|
|
3034
|
+
lines.push(`@Suppress("unused")`);
|
|
3035
|
+
lines.push(`${options.visPrefix}object ${options.objectName} {`);
|
|
3036
|
+
this.renderFlatGroups(lines, groups, 1, options);
|
|
3037
|
+
lines.push("}");
|
|
3038
|
+
});
|
|
3039
|
+
}
|
|
3040
|
+
/**
|
|
3041
|
+
* Shared file preamble: header, package, imports, optional ShadowToken class.
|
|
3042
|
+
* The `renderBody` callback appends the main object(s) to `lines`.
|
|
3043
|
+
*/
|
|
3044
|
+
buildFile(tokenTypes, options, renderBody) {
|
|
3045
|
+
const imports = this.collectImports(tokenTypes, options);
|
|
3046
|
+
const lines = [];
|
|
3047
|
+
lines.push(buildGeneratedFileHeader());
|
|
3048
|
+
lines.push("");
|
|
3049
|
+
lines.push(`package ${options.packageName}`);
|
|
3050
|
+
lines.push("");
|
|
3051
|
+
for (const imp of imports) {
|
|
3052
|
+
lines.push(`import ${imp}`);
|
|
3053
|
+
}
|
|
3054
|
+
if (imports.length > 0) {
|
|
3055
|
+
lines.push("");
|
|
3056
|
+
}
|
|
3057
|
+
if (tokenTypes.has("shadow")) {
|
|
3058
|
+
lines.push(...this.buildShadowTokenClass(options));
|
|
3059
|
+
lines.push("");
|
|
3060
|
+
}
|
|
3061
|
+
renderBody(lines);
|
|
3062
|
+
lines.push("");
|
|
3063
|
+
return lines.join("\n");
|
|
3064
|
+
}
|
|
3065
|
+
renderFlatGroups(lines, groups, baseDepth, options) {
|
|
3066
|
+
const groupIndent = indentStr(options.indent, baseDepth);
|
|
3067
|
+
const valIndent = indentStr(options.indent, baseDepth + 1);
|
|
3068
|
+
for (const group of groups) {
|
|
3069
|
+
lines.push(`${groupIndent}${options.visPrefix}object ${group.name} {`);
|
|
3070
|
+
for (const token of group.tokens) {
|
|
3071
|
+
const kotlinName = this.buildFlatKotlinName(token);
|
|
3072
|
+
const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
|
|
3073
|
+
const annotation = this.typeAnnotationSuffix(token);
|
|
3074
|
+
const descriptionComment = buildTokenDescriptionComment(token, "kotlin");
|
|
3075
|
+
if (descriptionComment) {
|
|
3076
|
+
lines.push(`${valIndent}${descriptionComment}`);
|
|
3077
|
+
}
|
|
3078
|
+
const deprecation = buildKotlinDeprecationAnnotation(token);
|
|
3079
|
+
if (deprecation) {
|
|
3080
|
+
lines.push(`${valIndent}${deprecation}`);
|
|
3081
|
+
}
|
|
3082
|
+
lines.push(
|
|
3083
|
+
`${valIndent}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`
|
|
3084
|
+
);
|
|
3085
|
+
}
|
|
3086
|
+
lines.push(`${groupIndent}}`);
|
|
3087
|
+
lines.push("");
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
renderTreeChildren(lines, node, depth, options) {
|
|
3091
|
+
const pad = indentStr(options.indent, depth);
|
|
3092
|
+
const entries = Array.from(node.children.entries());
|
|
3093
|
+
for (let idx = 0; idx < entries.length; idx++) {
|
|
3094
|
+
const [key, child] = entries[idx];
|
|
3095
|
+
if (child.token && child.children.size === 0) {
|
|
3096
|
+
this.renderLeaf(lines, key, child.token, depth, options);
|
|
3097
|
+
} else if (child.children.size > 0 && !child.token) {
|
|
3098
|
+
const objectName = toSafeIdentifier(key, KOTLIN_KEYWORDS, true);
|
|
3099
|
+
lines.push(`${pad}${options.visPrefix}object ${objectName} {`);
|
|
3100
|
+
this.renderTreeChildren(lines, child, depth + 1, options);
|
|
3101
|
+
lines.push(`${pad}}`);
|
|
3102
|
+
if (idx < entries.length - 1) {
|
|
3103
|
+
lines.push("");
|
|
3104
|
+
}
|
|
3105
|
+
} else {
|
|
3106
|
+
this.renderLeaf(lines, key, child.token, depth, options);
|
|
3107
|
+
this.renderTreeChildren(lines, child, depth, options);
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
renderLeaf(lines, key, token, depth, options) {
|
|
3112
|
+
const pad = indentStr(options.indent, depth);
|
|
3113
|
+
const kotlinName = toSafeIdentifier(key, KOTLIN_KEYWORDS, false);
|
|
3114
|
+
const kotlinValue = this.formatKotlinValue(token, options, depth);
|
|
3115
|
+
const annotation = this.typeAnnotationSuffix(token);
|
|
3116
|
+
const descriptionComment = buildTokenDescriptionComment(token, "kotlin");
|
|
3117
|
+
if (descriptionComment) {
|
|
3118
|
+
lines.push(`${pad}${descriptionComment}`);
|
|
3119
|
+
}
|
|
3120
|
+
const deprecation = buildKotlinDeprecationAnnotation(token);
|
|
3121
|
+
if (deprecation) {
|
|
3122
|
+
lines.push(`${pad}${deprecation}`);
|
|
3123
|
+
}
|
|
3124
|
+
lines.push(`${pad}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`);
|
|
3125
|
+
}
|
|
3126
|
+
// -----------------------------------------------------------------------
|
|
3127
|
+
// Shadow data class
|
|
3128
|
+
// -----------------------------------------------------------------------
|
|
3129
|
+
buildShadowTokenClass(options) {
|
|
3130
|
+
const i1 = indentStr(options.indent, 1);
|
|
3131
|
+
return [
|
|
3132
|
+
"@Immutable",
|
|
3133
|
+
`${options.visPrefix}data class ShadowToken(`,
|
|
3134
|
+
`${i1}val color: Color,`,
|
|
3135
|
+
`${i1}val elevation: Dp,`,
|
|
3136
|
+
`${i1}val offsetX: Dp,`,
|
|
3137
|
+
`${i1}val offsetY: Dp,`,
|
|
3138
|
+
")"
|
|
3139
|
+
];
|
|
3140
|
+
}
|
|
3141
|
+
// -----------------------------------------------------------------------
|
|
3142
|
+
// Imports (tree-shaken)
|
|
3143
|
+
// -----------------------------------------------------------------------
|
|
3144
|
+
collectImports(tokenTypes, options) {
|
|
3145
|
+
const imports = /* @__PURE__ */ new Set();
|
|
3146
|
+
const ns = "androidx.compose";
|
|
3147
|
+
const hasColors = tokenTypes.has("color") || tokenTypes.has("shadow") || tokenTypes.has("border");
|
|
3148
|
+
if (hasColors) {
|
|
3149
|
+
imports.add(`${ns}.ui.graphics.Color`);
|
|
3150
|
+
}
|
|
3151
|
+
if (tokenTypes.has("dimension") || tokenTypes.has("shadow") || tokenTypes.has("border")) {
|
|
3152
|
+
imports.add(`${ns}.ui.unit.Dp`);
|
|
3153
|
+
imports.add(`${ns}.ui.unit.dp`);
|
|
3154
|
+
}
|
|
3155
|
+
if (tokenTypes.has("typography") || tokenTypes.has("fontFamily")) {
|
|
3156
|
+
imports.add(`${ns}.ui.text.TextStyle`);
|
|
3157
|
+
imports.add(`${ns}.ui.unit.sp`);
|
|
3158
|
+
}
|
|
3159
|
+
if (tokenTypes.has("typography") || tokenTypes.has("fontWeight")) {
|
|
3160
|
+
imports.add(`${ns}.ui.text.font.FontWeight`);
|
|
3161
|
+
}
|
|
3162
|
+
if (tokenTypes.has("fontFamily")) {
|
|
3163
|
+
imports.add(`${ns}.ui.text.font.FontFamily`);
|
|
3164
|
+
}
|
|
3165
|
+
if (tokenTypes.has("duration")) {
|
|
3166
|
+
imports.add("kotlin.time.Duration");
|
|
3167
|
+
imports.add("kotlin.time.Duration.Companion.milliseconds");
|
|
3168
|
+
imports.add("kotlin.time.Duration.Companion.seconds");
|
|
3169
|
+
}
|
|
3170
|
+
if (tokenTypes.has("cubicBezier")) {
|
|
3171
|
+
imports.add(`${ns}.animation.core.CubicBezierEasing`);
|
|
3172
|
+
}
|
|
3173
|
+
if (tokenTypes.has("shadow")) {
|
|
3174
|
+
imports.add(`${ns}.runtime.Immutable`);
|
|
3175
|
+
}
|
|
3176
|
+
if (tokenTypes.has("border")) {
|
|
3177
|
+
imports.add(`${ns}.foundation.BorderStroke`);
|
|
3178
|
+
}
|
|
3179
|
+
if (options.colorSpace === "displayP3" && hasColors) {
|
|
3180
|
+
imports.add(`${ns}.ui.graphics.colorspace.ColorSpaces`);
|
|
3181
|
+
}
|
|
3182
|
+
return Array.from(imports).sort();
|
|
3183
|
+
}
|
|
3184
|
+
collectTokenTypesFromEntries(tokens) {
|
|
3185
|
+
const types = /* @__PURE__ */ new Set();
|
|
3186
|
+
for (const [, token] of Object.entries(tokens)) {
|
|
3187
|
+
if (token.$type) {
|
|
3188
|
+
types.add(token.$type);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
return types;
|
|
3192
|
+
}
|
|
3193
|
+
// -----------------------------------------------------------------------
|
|
3194
|
+
// Type annotations
|
|
3195
|
+
// -----------------------------------------------------------------------
|
|
3196
|
+
getTypeAnnotation(token) {
|
|
3197
|
+
switch (token.$type) {
|
|
3198
|
+
case "color":
|
|
3199
|
+
return "Color";
|
|
3200
|
+
case "dimension":
|
|
3201
|
+
return "Dp";
|
|
3202
|
+
case "fontFamily":
|
|
3203
|
+
return "FontFamily";
|
|
3204
|
+
case "fontWeight":
|
|
3205
|
+
return "FontWeight";
|
|
3206
|
+
case "duration":
|
|
3207
|
+
return "Duration";
|
|
3208
|
+
case "shadow":
|
|
3209
|
+
return "ShadowToken";
|
|
3210
|
+
case "cubicBezier":
|
|
3211
|
+
return "CubicBezierEasing";
|
|
3212
|
+
case "number":
|
|
3213
|
+
return "Double";
|
|
3214
|
+
case "typography":
|
|
3215
|
+
return "TextStyle";
|
|
3216
|
+
case "border":
|
|
3217
|
+
return "BorderStroke";
|
|
3218
|
+
default: {
|
|
3219
|
+
const value = token.$value;
|
|
3220
|
+
if (typeof value === "string") {
|
|
3221
|
+
return "String";
|
|
3222
|
+
}
|
|
3223
|
+
if (typeof value === "boolean") {
|
|
3224
|
+
return "Boolean";
|
|
3225
|
+
}
|
|
3226
|
+
if (typeof value === "number") {
|
|
3227
|
+
return "Double";
|
|
3228
|
+
}
|
|
3229
|
+
return void 0;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
typeAnnotationSuffix(token) {
|
|
3234
|
+
const type = this.getTypeAnnotation(token);
|
|
3235
|
+
return type ? `: ${type}` : "";
|
|
3236
|
+
}
|
|
3237
|
+
// -----------------------------------------------------------------------
|
|
3238
|
+
// Value formatting
|
|
3239
|
+
// -----------------------------------------------------------------------
|
|
3240
|
+
formatKotlinValue(token, options, depth) {
|
|
3241
|
+
const value = token.$value;
|
|
3242
|
+
if (token.$type === "color") {
|
|
3243
|
+
return this.formatColorValue(value, options);
|
|
3244
|
+
}
|
|
3245
|
+
if (token.$type === "dimension") {
|
|
3246
|
+
return this.formatDimensionValue(value);
|
|
3247
|
+
}
|
|
3248
|
+
if (token.$type === "fontFamily") {
|
|
3249
|
+
return this.formatFontFamilyValue(value);
|
|
3250
|
+
}
|
|
3251
|
+
if (token.$type === "fontWeight") {
|
|
3252
|
+
return this.formatFontWeightValue(value);
|
|
3253
|
+
}
|
|
3254
|
+
if (token.$type === "duration") {
|
|
3255
|
+
return this.formatDurationValue(value);
|
|
3256
|
+
}
|
|
3257
|
+
if (token.$type === "shadow") {
|
|
3258
|
+
return this.formatShadowValue(value, options, depth);
|
|
3259
|
+
}
|
|
3260
|
+
if (token.$type === "typography") {
|
|
3261
|
+
return this.formatTypographyValue(value, options, depth);
|
|
3262
|
+
}
|
|
3263
|
+
if (token.$type === "border") {
|
|
3264
|
+
return this.formatBorderValue(value, options);
|
|
3265
|
+
}
|
|
3266
|
+
if (token.$type === "number") {
|
|
3267
|
+
return typeof value === "number" ? formatKotlinNumber(value) : String(value);
|
|
3268
|
+
}
|
|
3269
|
+
if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
|
|
3270
|
+
return `CubicBezierEasing(${value[0]}f, ${value[1]}f, ${value[2]}f, ${value[3]}f)`;
|
|
3271
|
+
}
|
|
3272
|
+
if (typeof value === "string") {
|
|
3273
|
+
return `"${escapeKotlinString(value)}"`;
|
|
3274
|
+
}
|
|
3275
|
+
if (typeof value === "number") {
|
|
3276
|
+
return formatKotlinNumber(value);
|
|
3277
|
+
}
|
|
3278
|
+
if (typeof value === "boolean") {
|
|
3279
|
+
return value ? "true" : "false";
|
|
3280
|
+
}
|
|
3281
|
+
return `"${escapeKotlinString(String(value))}"`;
|
|
3282
|
+
}
|
|
3283
|
+
formatColorValue(value, options) {
|
|
3284
|
+
if (!isColorObject(value)) {
|
|
3285
|
+
if (typeof value === "string") {
|
|
3286
|
+
const hex = value.replace("#", "");
|
|
3287
|
+
if (/^[0-9a-fA-F]{6,8}$/.test(hex)) {
|
|
3288
|
+
const argb = hex.length === 8 ? hex : `FF${hex}`;
|
|
3289
|
+
return `Color(0x${argb.toUpperCase()})`;
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return "Color.Unspecified";
|
|
3293
|
+
}
|
|
3294
|
+
const colorObj = value;
|
|
3295
|
+
const alpha = colorObj.alpha ?? 1;
|
|
3296
|
+
if (options.colorFormat === "argb_float" || options.colorSpace === "displayP3") {
|
|
3297
|
+
return this.formatFloatColor(colorObj, alpha, options);
|
|
3298
|
+
}
|
|
3299
|
+
return this.formatHexColor(colorObj, alpha);
|
|
3300
|
+
}
|
|
3301
|
+
formatFloatColor(colorObj, alpha, options) {
|
|
3302
|
+
if (options.colorSpace === "displayP3") {
|
|
3303
|
+
const p3 = toP32(dtcgObjectToCulori(colorObj));
|
|
3304
|
+
const r2 = roundComponent(p3?.r ?? 0);
|
|
3305
|
+
const g2 = roundComponent(p3?.g ?? 0);
|
|
3306
|
+
const b2 = roundComponent(p3?.b ?? 0);
|
|
3307
|
+
return `Color(${r2}f, ${g2}f, ${b2}f, ${roundComponent(alpha)}f, ColorSpaces.DisplayP3)`;
|
|
3308
|
+
}
|
|
3309
|
+
const rgb = toSRGB2(dtcgObjectToCulori(colorObj));
|
|
3310
|
+
const r = roundComponent(rgb?.r ?? 0);
|
|
3311
|
+
const g = roundComponent(rgb?.g ?? 0);
|
|
3312
|
+
const b = roundComponent(rgb?.b ?? 0);
|
|
3313
|
+
return `Color(${r}f, ${g}f, ${b}f, ${roundComponent(alpha)}f)`;
|
|
3314
|
+
}
|
|
3315
|
+
formatHexColor(colorObj, alpha) {
|
|
3316
|
+
const hex = colorObjectToHex(colorObj);
|
|
3317
|
+
const hexClean = hex.replace("#", "");
|
|
3318
|
+
if (hexClean.length === 8) {
|
|
3319
|
+
const rrggbb = hexClean.slice(0, 6);
|
|
3320
|
+
const aa = hexClean.slice(6, 8);
|
|
3321
|
+
return `Color(0x${aa.toUpperCase()}${rrggbb.toUpperCase()})`;
|
|
3322
|
+
}
|
|
3323
|
+
const alphaHex = alpha < 1 ? Math.round(alpha * 255).toString(16).padStart(2, "0").toUpperCase() : "FF";
|
|
3324
|
+
return `Color(0x${alphaHex}${hexClean.toUpperCase()})`;
|
|
3325
|
+
}
|
|
3326
|
+
formatDimensionValue(value) {
|
|
3327
|
+
if (isDimensionObject(value)) {
|
|
3328
|
+
const dim = value;
|
|
3329
|
+
const dpValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
|
|
3330
|
+
return `${dpValue}.dp`;
|
|
3331
|
+
}
|
|
3332
|
+
return typeof value === "number" ? `${value}.dp` : `0.dp`;
|
|
3333
|
+
}
|
|
3334
|
+
formatFontFamilyValue(value) {
|
|
3335
|
+
if (Array.isArray(value)) {
|
|
3336
|
+
const primary = value[0];
|
|
3337
|
+
if (typeof primary === "string") {
|
|
3338
|
+
return this.mapKotlinFontFamily(primary);
|
|
3339
|
+
}
|
|
3340
|
+
return "FontFamily.Default";
|
|
3341
|
+
}
|
|
3342
|
+
return typeof value === "string" ? this.mapKotlinFontFamily(value) : "FontFamily.Default";
|
|
3343
|
+
}
|
|
3344
|
+
mapKotlinFontFamily(family) {
|
|
3345
|
+
const normalized = family.toLowerCase().replace(/['"]/g, "").trim();
|
|
3346
|
+
const builtIn = {
|
|
3347
|
+
"sans-serif": "FontFamily.SansSerif",
|
|
3348
|
+
serif: "FontFamily.Serif",
|
|
3349
|
+
monospace: "FontFamily.Monospace",
|
|
3350
|
+
cursive: "FontFamily.Cursive"
|
|
3351
|
+
};
|
|
3352
|
+
return builtIn[normalized] ?? `FontFamily.Default // TODO: load "${family}" via Font(R.font.${toResourceName(family)})`;
|
|
3353
|
+
}
|
|
3354
|
+
formatFontWeightValue(value) {
|
|
3355
|
+
if (typeof value === "number") {
|
|
3356
|
+
return this.numericFontWeight(value);
|
|
3357
|
+
}
|
|
3358
|
+
if (typeof value === "string") {
|
|
3359
|
+
return this.namedFontWeight(value) ?? "FontWeight.Normal";
|
|
3360
|
+
}
|
|
3361
|
+
return "FontWeight.Normal";
|
|
3362
|
+
}
|
|
3363
|
+
numericFontWeight(weight) {
|
|
3364
|
+
if (weight <= 100) {
|
|
3365
|
+
return "FontWeight.Thin";
|
|
3366
|
+
}
|
|
3367
|
+
if (weight <= 200) {
|
|
3368
|
+
return "FontWeight.ExtraLight";
|
|
3369
|
+
}
|
|
3370
|
+
if (weight <= 300) {
|
|
3371
|
+
return "FontWeight.Light";
|
|
3372
|
+
}
|
|
3373
|
+
if (weight <= 400) {
|
|
3374
|
+
return "FontWeight.Normal";
|
|
3375
|
+
}
|
|
3376
|
+
if (weight <= 500) {
|
|
3377
|
+
return "FontWeight.Medium";
|
|
3378
|
+
}
|
|
3379
|
+
if (weight <= 600) {
|
|
3380
|
+
return "FontWeight.SemiBold";
|
|
3381
|
+
}
|
|
3382
|
+
if (weight <= 700) {
|
|
3383
|
+
return "FontWeight.Bold";
|
|
3384
|
+
}
|
|
3385
|
+
if (weight <= 800) {
|
|
3386
|
+
return "FontWeight.ExtraBold";
|
|
3387
|
+
}
|
|
3388
|
+
return "FontWeight.Black";
|
|
3389
|
+
}
|
|
3390
|
+
namedFontWeight(name) {
|
|
3391
|
+
const map = {
|
|
3392
|
+
thin: "FontWeight.Thin",
|
|
3393
|
+
extralight: "FontWeight.ExtraLight",
|
|
3394
|
+
ultralight: "FontWeight.ExtraLight",
|
|
3395
|
+
light: "FontWeight.Light",
|
|
3396
|
+
regular: "FontWeight.Normal",
|
|
3397
|
+
normal: "FontWeight.Normal",
|
|
3398
|
+
medium: "FontWeight.Medium",
|
|
3399
|
+
semibold: "FontWeight.SemiBold",
|
|
3400
|
+
demibold: "FontWeight.SemiBold",
|
|
3401
|
+
bold: "FontWeight.Bold",
|
|
3402
|
+
extrabold: "FontWeight.ExtraBold",
|
|
3403
|
+
heavy: "FontWeight.ExtraBold",
|
|
3404
|
+
black: "FontWeight.Black",
|
|
3405
|
+
ultrabold: "FontWeight.Black"
|
|
3406
|
+
};
|
|
3407
|
+
return map[name.toLowerCase()];
|
|
3408
|
+
}
|
|
3409
|
+
formatDurationValue(value) {
|
|
3410
|
+
if (isDurationObject(value)) {
|
|
3411
|
+
return value.unit === "ms" ? `${value.value}.milliseconds` : `${value.value}.seconds`;
|
|
3412
|
+
}
|
|
3413
|
+
return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
|
|
3414
|
+
}
|
|
3415
|
+
formatShadowValue(value, options, depth) {
|
|
3416
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
3417
|
+
return this.formatSingleShadow(value[0], options, depth);
|
|
3418
|
+
}
|
|
3419
|
+
if (typeof value === "object" && value !== null) {
|
|
3420
|
+
return this.formatSingleShadow(value, options, depth);
|
|
3421
|
+
}
|
|
3422
|
+
return "ShadowToken(color = Color.Unspecified, elevation = 0.dp, offsetX = 0.dp, offsetY = 0.dp)";
|
|
3423
|
+
}
|
|
3424
|
+
formatSingleShadow(shadow, options, depth) {
|
|
3425
|
+
const color = isColorObject(shadow.color) ? this.formatColorValue(shadow.color, options) : "Color.Black";
|
|
3426
|
+
const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
|
|
3427
|
+
const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
|
|
3428
|
+
const offsetY = isDimensionObject(shadow.offsetY) ? this.formatDimensionValue(shadow.offsetY) : "0.dp";
|
|
3429
|
+
const propIndent = indentStr(options.indent, depth + 1);
|
|
3430
|
+
const closeIndent = indentStr(options.indent, depth);
|
|
3431
|
+
return [
|
|
3432
|
+
"ShadowToken(",
|
|
3433
|
+
`${propIndent}color = ${color},`,
|
|
3434
|
+
`${propIndent}elevation = ${elevation},`,
|
|
3435
|
+
`${propIndent}offsetX = ${offsetX},`,
|
|
3436
|
+
`${propIndent}offsetY = ${offsetY},`,
|
|
3437
|
+
`${closeIndent})`
|
|
3438
|
+
].join("\n");
|
|
3439
|
+
}
|
|
3440
|
+
formatBorderValue(value, options) {
|
|
3441
|
+
if (typeof value !== "object" || value === null) {
|
|
3442
|
+
return "BorderStroke(0.dp, Color.Unspecified)";
|
|
3443
|
+
}
|
|
3444
|
+
const border = value;
|
|
3445
|
+
const width = isDimensionObject(border.width) ? this.formatDimensionValue(border.width) : "0.dp";
|
|
3446
|
+
const color = isColorObject(border.color) ? this.formatColorValue(border.color, options) : "Color.Unspecified";
|
|
3447
|
+
return `BorderStroke(${width}, ${color})`;
|
|
3448
|
+
}
|
|
3449
|
+
formatTypographyValue(value, options, depth) {
|
|
3450
|
+
if (typeof value !== "object" || value === null) {
|
|
3451
|
+
return "TextStyle()";
|
|
3452
|
+
}
|
|
3453
|
+
const typo = value;
|
|
3454
|
+
const parts = [];
|
|
3455
|
+
if (isDimensionObject(typo.fontSize)) {
|
|
3456
|
+
const dim = typo.fontSize;
|
|
3457
|
+
const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
|
|
3458
|
+
parts.push(`fontSize = ${spValue}.sp`);
|
|
3459
|
+
}
|
|
3460
|
+
if (typo.fontWeight != null) {
|
|
3461
|
+
parts.push(`fontWeight = ${this.formatFontWeightValue(typo.fontWeight)}`);
|
|
3462
|
+
}
|
|
3463
|
+
if (typo.lineHeight != null && typeof typo.lineHeight === "number") {
|
|
3464
|
+
if (isDimensionObject(typo.fontSize)) {
|
|
3465
|
+
const dim = typo.fontSize;
|
|
3466
|
+
const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
|
|
3467
|
+
const lineHeightSp = Math.round(spValue * typo.lineHeight * 100) / 100;
|
|
3468
|
+
parts.push(`lineHeight = ${lineHeightSp}.sp`);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
if (isDimensionObject(typo.letterSpacing)) {
|
|
3472
|
+
const dim = typo.letterSpacing;
|
|
3473
|
+
const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
|
|
3474
|
+
parts.push(`letterSpacing = ${spValue}.sp`);
|
|
3475
|
+
}
|
|
3476
|
+
if (parts.length === 0) {
|
|
3477
|
+
return "TextStyle()";
|
|
3478
|
+
}
|
|
3479
|
+
const propIndent = indentStr(options.indent, depth + 1);
|
|
3480
|
+
const closeIndent = indentStr(options.indent, depth);
|
|
3481
|
+
return `TextStyle(
|
|
3482
|
+
${parts.map((p) => `${propIndent}${p}`).join(",\n")},
|
|
3483
|
+
${closeIndent})`;
|
|
3484
|
+
}
|
|
3485
|
+
// -----------------------------------------------------------------------
|
|
3486
|
+
// Output: standalone
|
|
3487
|
+
// -----------------------------------------------------------------------
|
|
3488
|
+
async formatStandalone(context, options) {
|
|
3489
|
+
assertFileRequired(
|
|
3490
|
+
context.buildPath,
|
|
3491
|
+
context.output.file,
|
|
3492
|
+
context.output.name,
|
|
3493
|
+
"standalone Android"
|
|
3494
|
+
);
|
|
3495
|
+
const files = {};
|
|
3496
|
+
for (const { tokens, modifierInputs } of context.permutations) {
|
|
3497
|
+
const processedTokens = stripInternalMetadata(tokens);
|
|
3498
|
+
const content = this.formatTokens(processedTokens, options);
|
|
3499
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
|
|
3500
|
+
outputName: context.output.name,
|
|
3501
|
+
extension: "kt",
|
|
3502
|
+
modifierInputs,
|
|
3503
|
+
resolver: context.resolver,
|
|
3504
|
+
defaults: context.meta.defaults
|
|
3505
|
+
});
|
|
3506
|
+
files[fileName] = content;
|
|
3507
|
+
}
|
|
3508
|
+
return outputTree(files);
|
|
3509
|
+
}
|
|
3510
|
+
// -----------------------------------------------------------------------
|
|
3511
|
+
// Output: bundle
|
|
3512
|
+
// -----------------------------------------------------------------------
|
|
3513
|
+
async formatBundle(context, options) {
|
|
3514
|
+
assertFileRequired(
|
|
3515
|
+
context.buildPath,
|
|
3516
|
+
context.output.file,
|
|
3517
|
+
context.output.name,
|
|
3518
|
+
"bundle Android"
|
|
3519
|
+
);
|
|
3520
|
+
const content = this.formatBundleContent(context, options);
|
|
3521
|
+
const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
|
|
3522
|
+
outputName: context.output.name,
|
|
3523
|
+
extension: "kt",
|
|
3524
|
+
modifierInputs: context.meta.basePermutation,
|
|
3525
|
+
resolver: context.resolver,
|
|
3526
|
+
defaults: context.meta.defaults
|
|
3527
|
+
});
|
|
3528
|
+
return outputTree({ [fileName]: content });
|
|
3529
|
+
}
|
|
3530
|
+
formatBundleContent(context, options) {
|
|
3531
|
+
const allTokenTypes = this.collectAllPermutationTypes(context);
|
|
3532
|
+
return this.buildFile(allTokenTypes, options, (lines) => {
|
|
3533
|
+
const i1 = indentStr(options.indent, 1);
|
|
3534
|
+
lines.push(`@Suppress("unused")`);
|
|
3535
|
+
lines.push(`${options.visPrefix}object ${options.objectName} {`);
|
|
3536
|
+
for (let idx = 0; idx < context.permutations.length; idx++) {
|
|
3537
|
+
const { tokens, modifierInputs } = context.permutations[idx];
|
|
3538
|
+
const processedTokens = stripInternalMetadata(tokens);
|
|
3539
|
+
const permName = this.buildPermutationName(modifierInputs);
|
|
3540
|
+
lines.push(`${i1}${options.visPrefix}object ${permName} {`);
|
|
3541
|
+
this.renderBundleTokens(lines, processedTokens, options, 2);
|
|
3542
|
+
lines.push(`${i1}}`);
|
|
3543
|
+
if (idx < context.permutations.length - 1) {
|
|
3544
|
+
lines.push("");
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
lines.push("}");
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
collectAllPermutationTypes(context) {
|
|
3551
|
+
const types = /* @__PURE__ */ new Set();
|
|
3552
|
+
for (const { tokens } of context.permutations) {
|
|
3553
|
+
for (const t of this.collectTokenTypesFromEntries(stripInternalMetadata(tokens))) {
|
|
3554
|
+
types.add(t);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
return types;
|
|
3558
|
+
}
|
|
3559
|
+
renderBundleTokens(lines, tokens, options, baseDepth) {
|
|
3560
|
+
if (options.structure === "flat") {
|
|
3561
|
+
const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
|
|
3562
|
+
this.renderFlatGroups(lines, groups, baseDepth, options);
|
|
3563
|
+
return;
|
|
3564
|
+
}
|
|
3565
|
+
const tree = this.buildTokenTree(tokens);
|
|
3566
|
+
this.renderTreeChildren(lines, tree, baseDepth, options);
|
|
3567
|
+
}
|
|
3568
|
+
buildPermutationName(modifierInputs) {
|
|
3569
|
+
const values = Object.values(modifierInputs);
|
|
3570
|
+
if (values.length === 0) {
|
|
3571
|
+
return "Default";
|
|
3572
|
+
}
|
|
3573
|
+
return values.map((v) => toSafeIdentifier(v, KOTLIN_KEYWORDS, true)).join("");
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
function androidRenderer() {
|
|
3577
|
+
const rendererInstance = new AndroidRenderer();
|
|
3578
|
+
return {
|
|
3579
|
+
format: (context, options) => rendererInstance.format(
|
|
3580
|
+
context,
|
|
3581
|
+
options ?? context.output.options
|
|
3582
|
+
)
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
// src/outputs/types.ts
|
|
3587
|
+
function defineRenderer(renderer) {
|
|
3588
|
+
return renderer;
|
|
3589
|
+
}
|
|
3590
|
+
function nameKebabCase() {
|
|
3591
|
+
return {
|
|
3592
|
+
transform: (token) => {
|
|
3593
|
+
const name = kebabCase(token.path.join(" "));
|
|
3594
|
+
return {
|
|
3595
|
+
...token,
|
|
3596
|
+
name
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
// src/outputs/builders.ts
|
|
3603
|
+
function css(config) {
|
|
3604
|
+
const {
|
|
3605
|
+
name,
|
|
3606
|
+
file,
|
|
3607
|
+
transforms,
|
|
3608
|
+
filters,
|
|
3609
|
+
hooks,
|
|
3610
|
+
preset = "bundle",
|
|
3611
|
+
...rendererOptions
|
|
3612
|
+
} = config;
|
|
3613
|
+
return {
|
|
3614
|
+
name,
|
|
3615
|
+
file,
|
|
3616
|
+
renderer: cssRenderer(),
|
|
3617
|
+
options: { preset, ...rendererOptions },
|
|
3618
|
+
transforms: [nameKebabCase(), ...transforms ?? []],
|
|
3619
|
+
filters,
|
|
3620
|
+
hooks
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
function json(config) {
|
|
3624
|
+
const {
|
|
3625
|
+
name,
|
|
3626
|
+
file,
|
|
3627
|
+
transforms,
|
|
3628
|
+
filters,
|
|
3629
|
+
hooks,
|
|
3630
|
+
preset = "standalone",
|
|
3631
|
+
...rendererOptions
|
|
3632
|
+
} = config;
|
|
3633
|
+
return {
|
|
3634
|
+
name,
|
|
3635
|
+
file,
|
|
3636
|
+
renderer: jsonRenderer(),
|
|
3637
|
+
options: { preset, ...rendererOptions },
|
|
3638
|
+
transforms,
|
|
3639
|
+
filters,
|
|
3640
|
+
hooks
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
function js(config) {
|
|
3644
|
+
const {
|
|
3645
|
+
name,
|
|
3646
|
+
file,
|
|
3647
|
+
transforms,
|
|
3648
|
+
filters,
|
|
3649
|
+
hooks,
|
|
3650
|
+
preset = "standalone",
|
|
3651
|
+
...rendererOptions
|
|
3652
|
+
} = config;
|
|
3653
|
+
return {
|
|
3654
|
+
name,
|
|
3655
|
+
file,
|
|
3656
|
+
renderer: jsRenderer(),
|
|
3657
|
+
options: { preset, ...rendererOptions },
|
|
3658
|
+
transforms,
|
|
3659
|
+
filters,
|
|
3660
|
+
hooks
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
function tailwind(config) {
|
|
3664
|
+
const { name, file, transforms, filters, hooks, preset = "bundle", ...rendererOptions } = config;
|
|
3665
|
+
return {
|
|
3666
|
+
name,
|
|
3667
|
+
file,
|
|
3668
|
+
renderer: tailwindRenderer(),
|
|
3669
|
+
options: { preset, ...rendererOptions },
|
|
3670
|
+
transforms,
|
|
3671
|
+
filters,
|
|
3672
|
+
hooks
|
|
3673
|
+
};
|
|
3674
|
+
}
|
|
3675
|
+
function ios(config) {
|
|
3676
|
+
const {
|
|
3677
|
+
name,
|
|
3678
|
+
file,
|
|
3679
|
+
transforms,
|
|
3680
|
+
filters,
|
|
3681
|
+
hooks,
|
|
3682
|
+
preset = "standalone",
|
|
3683
|
+
...rendererOptions
|
|
3684
|
+
} = config;
|
|
3685
|
+
return {
|
|
3686
|
+
name,
|
|
3687
|
+
file,
|
|
3688
|
+
renderer: iosRenderer(),
|
|
3689
|
+
options: { preset, ...rendererOptions },
|
|
3690
|
+
transforms,
|
|
3691
|
+
filters,
|
|
3692
|
+
hooks
|
|
3693
|
+
};
|
|
3694
|
+
}
|
|
3695
|
+
function android(config) {
|
|
3696
|
+
const {
|
|
3697
|
+
name,
|
|
3698
|
+
file,
|
|
3699
|
+
transforms,
|
|
3700
|
+
filters,
|
|
3701
|
+
hooks,
|
|
3702
|
+
preset = "standalone",
|
|
3703
|
+
...rendererOptions
|
|
3704
|
+
} = config;
|
|
3705
|
+
return {
|
|
3706
|
+
name,
|
|
3707
|
+
file,
|
|
3708
|
+
renderer: androidRenderer(),
|
|
3709
|
+
options: { preset, ...rendererOptions },
|
|
3710
|
+
transforms,
|
|
3711
|
+
filters,
|
|
3712
|
+
hooks
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3715
|
+
/**
|
|
3716
|
+
* @license MIT
|
|
3717
|
+
* Copyright (c) 2025-present Dispersa Contributors
|
|
3718
|
+
*
|
|
3719
|
+
* This source code is licensed under the MIT license found in the
|
|
3720
|
+
* LICENSE file in the root directory of this source tree.
|
|
3721
|
+
*/
|
|
3722
|
+
/**
|
|
3723
|
+
* @license
|
|
3724
|
+
* Copyright (c) 2025 Dispersa Contributors
|
|
3725
|
+
* SPDX-License-Identifier: MIT
|
|
3726
|
+
*/
|
|
3727
|
+
|
|
3728
|
+
export { android, androidRenderer, css, cssRenderer, defineRenderer, ios, iosRenderer, isOutputTree, js, jsRenderer, json, jsonRenderer, outputTree, tailwind, tailwindRenderer };
|
|
3729
|
+
//# sourceMappingURL=index.js.map
|
|
3730
|
+
//# sourceMappingURL=index.js.map
|