dispersa 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17,47 +17,6 @@ var __export = (target, all) => {
17
17
  __defProp(target, name, { get: all[name], enumerable: true });
18
18
  };
19
19
 
20
- // src/shared/utils/token-utils.ts
21
- function formatDeprecationMessage(token, description = "", format = "bracket") {
22
- if (token.$deprecated == null || token.$deprecated === false) {
23
- return description;
24
- }
25
- const deprecationMsg = typeof token.$deprecated === "string" ? token.$deprecated : "";
26
- if (format === "comment") {
27
- const msg = deprecationMsg ? ` ${deprecationMsg}` : "";
28
- return `DEPRECATED${msg}`;
29
- } else {
30
- const msg = deprecationMsg ? `: ${deprecationMsg}` : "";
31
- const prefix = `[DEPRECATED${msg}]`;
32
- return description ? `${prefix} ${description}` : prefix;
33
- }
34
- }
35
- function stripInternalTokenMetadata(tokens) {
36
- const cleaned = {};
37
- for (const [name, token] of Object.entries(tokens)) {
38
- const { _isAlias: _alias, _sourceModifier: _source, _sourceSet, ...rest } = token;
39
- cleaned[name] = rest;
40
- }
41
- return cleaned;
42
- }
43
- function getSortedTokenEntries(tokens) {
44
- return Object.entries(tokens).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
45
- }
46
- function isTokenLike(value) {
47
- return typeof value === "object" && value !== null && ("$value" in value || "$ref" in value);
48
- }
49
- function getPureAliasReferenceName(value) {
50
- if (typeof value !== "string") {
51
- return void 0;
52
- }
53
- const match = /^\{([^}]+)\}$/.exec(value);
54
- return match?.[1]?.trim();
55
- }
56
- var init_token_utils = __esm({
57
- "src/shared/utils/token-utils.ts"() {
58
- }
59
- });
60
-
61
20
  // src/shared/errors/index.ts
62
21
  var DispersaError, TokenReferenceError, CircularReferenceError, ValidationError, FileOperationError, ConfigurationError, BasePermutationError, ModifierError;
63
22
  var init_errors = __esm({
@@ -145,6 +104,70 @@ var init_errors = __esm({
145
104
  }
146
105
  });
147
106
 
107
+ // src/shared/utils/token-utils.ts
108
+ function formatDeprecationMessage(token, description = "", format = "bracket") {
109
+ if (token.$deprecated == null || token.$deprecated === false) {
110
+ return description;
111
+ }
112
+ const deprecationMsg = typeof token.$deprecated === "string" ? token.$deprecated : "";
113
+ if (format === "comment") {
114
+ const msg2 = deprecationMsg ? ` ${deprecationMsg}` : "";
115
+ return `DEPRECATED${msg2}`;
116
+ }
117
+ const msg = deprecationMsg ? `: ${deprecationMsg}` : "";
118
+ const prefix = `[DEPRECATED${msg}]`;
119
+ return description ? `${prefix} ${description}` : prefix;
120
+ }
121
+ function stripInternalTokenMetadata(tokens) {
122
+ const cleaned = {};
123
+ for (const [name, token] of Object.entries(tokens)) {
124
+ const { _isAlias: _alias, _sourceModifier: _source, _sourceSet, ...rest } = token;
125
+ cleaned[name] = rest;
126
+ }
127
+ return cleaned;
128
+ }
129
+ function getSortedTokenEntries(tokens) {
130
+ return Object.entries(tokens).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
131
+ }
132
+ function buildNestedTokenObject(tokens, extractValue) {
133
+ const result = {};
134
+ for (const [, token] of getSortedTokenEntries(tokens)) {
135
+ setNestedValue(result, token.path, extractValue(token));
136
+ }
137
+ return result;
138
+ }
139
+ function setNestedValue(root, path7, value) {
140
+ let current = root;
141
+ for (let i = 0; i < path7.length - 1; i++) {
142
+ const part = path7[i];
143
+ if (part == null) {
144
+ continue;
145
+ }
146
+ if (!(part in current)) {
147
+ current[part] = {};
148
+ }
149
+ current = current[part];
150
+ }
151
+ const lastPart = path7[path7.length - 1];
152
+ if (lastPart != null) {
153
+ current[lastPart] = value;
154
+ }
155
+ }
156
+ function isTokenLike(value) {
157
+ return typeof value === "object" && value !== null && ("$value" in value || "$ref" in value);
158
+ }
159
+ function getPureAliasReferenceName(value) {
160
+ if (typeof value !== "string") {
161
+ return void 0;
162
+ }
163
+ const match = /^\{([^}]+)\}$/.exec(value);
164
+ return match?.[1]?.trim();
165
+ }
166
+ var init_token_utils = __esm({
167
+ "src/shared/utils/token-utils.ts"() {
168
+ }
169
+ });
170
+
148
171
  // src/shared/utils/validation-handler.ts
149
172
  var ValidationHandler;
150
173
  var init_validation_handler = __esm({
@@ -2884,30 +2907,29 @@ var init_validator = __esm({
2884
2907
  validateTokenOrGroup(obj) {
2885
2908
  const hasValue = isTokenLike(obj);
2886
2909
  if (hasValue) {
2887
- const tokenErrors = this.validateToken(obj);
2888
- if (tokenErrors.length === 0) {
2910
+ const tokenErrors2 = this.validateToken(obj);
2911
+ if (tokenErrors2.length === 0) {
2889
2912
  return { type: "token", errors: [] };
2890
2913
  }
2891
2914
  return {
2892
2915
  type: "invalid",
2893
- errors: tokenErrors,
2916
+ errors: tokenErrors2,
2894
2917
  message: "Object has $value/$ref but failed token validation"
2895
2918
  };
2896
- } else {
2897
- const groupErrors = this.validateGroup(obj);
2898
- if (groupErrors.length === 0) {
2899
- return { type: "group", errors: [] };
2900
- }
2901
- const tokenErrors = this.validateToken(obj);
2902
- if (tokenErrors.length === 0) {
2903
- return { type: "token", errors: [] };
2904
- }
2905
- return {
2906
- type: "invalid",
2907
- errors: groupErrors.length < tokenErrors.length ? groupErrors : tokenErrors,
2908
- message: groupErrors.length < tokenErrors.length ? "Object appears to be a group but failed validation" : "Object appears to be a token but failed validation"
2909
- };
2910
2919
  }
2920
+ const groupErrors = this.validateGroup(obj);
2921
+ if (groupErrors.length === 0) {
2922
+ return { type: "group", errors: [] };
2923
+ }
2924
+ const tokenErrors = this.validateToken(obj);
2925
+ if (tokenErrors.length === 0) {
2926
+ return { type: "token", errors: [] };
2927
+ }
2928
+ return {
2929
+ type: "invalid",
2930
+ errors: groupErrors.length < tokenErrors.length ? groupErrors : tokenErrors,
2931
+ message: groupErrors.length < tokenErrors.length ? "Object appears to be a group but failed validation" : "Object appears to be a token but failed validation"
2932
+ };
2911
2933
  }
2912
2934
  /**
2913
2935
  * Format AJV errors into readable ValidationError objects
@@ -3102,35 +3124,34 @@ var init_resolver_parser = __esm({
3102
3124
  this.validateSourceReferences(set.sources, `set "${setName}"`, contextMsg);
3103
3125
  }
3104
3126
  }
3105
- if (resolver.modifiers) {
3106
- for (const [modifierName, modifier] of Object.entries(resolver.modifiers)) {
3107
- for (const [contextName, sources] of Object.entries(modifier.contexts)) {
3108
- this.validateSourceReferences(
3109
- sources,
3110
- `modifier "${modifierName}" context "${contextName}"`,
3111
- contextMsg
3112
- );
3113
- }
3127
+ if (!resolver.modifiers) {
3128
+ return;
3129
+ }
3130
+ for (const [modifierName, modifier] of Object.entries(resolver.modifiers)) {
3131
+ for (const [contextName, sources] of Object.entries(modifier.contexts)) {
3132
+ this.validateSourceReferences(
3133
+ sources,
3134
+ `modifier "${modifierName}" context "${contextName}"`,
3135
+ contextMsg
3136
+ );
3114
3137
  }
3115
3138
  }
3116
3139
  }
3117
- /**
3118
- * Validate source references in an array
3119
- */
3120
3140
  validateSourceReferences(sources, location, contextMsg) {
3121
3141
  for (const source of sources) {
3122
- if (typeof source === "object" && source !== null && "$ref" in source) {
3123
- const ref = source.$ref;
3124
- if (ref.startsWith("#/modifiers/")) {
3125
- this.handleValidationIssue(
3126
- `Invalid reference in ${location}: "${ref}". Sets and modifier contexts MUST NOT reference modifiers${contextMsg}`
3127
- );
3128
- }
3129
- if (ref.startsWith("#/resolutionOrder/")) {
3130
- this.handleValidationIssue(
3131
- `Invalid reference in ${location}: "${ref}". References MUST NOT point to resolutionOrder array items${contextMsg}`
3132
- );
3133
- }
3142
+ if (typeof source !== "object" || source === null || !("$ref" in source)) {
3143
+ continue;
3144
+ }
3145
+ const ref = source.$ref;
3146
+ if (ref.startsWith("#/modifiers/")) {
3147
+ this.handleValidationIssue(
3148
+ `Invalid reference in ${location}: "${ref}". Sets and modifier contexts MUST NOT reference modifiers${contextMsg}`
3149
+ );
3150
+ }
3151
+ if (ref.startsWith("#/resolutionOrder/")) {
3152
+ this.handleValidationIssue(
3153
+ `Invalid reference in ${location}: "${ref}". References MUST NOT point to resolutionOrder array items${contextMsg}`
3154
+ );
3134
3155
  }
3135
3156
  }
3136
3157
  }
@@ -3210,15 +3231,14 @@ var init_resolver_loader = __esm({
3210
3231
  * ```
3211
3232
  */
3212
3233
  async load(resolver) {
3213
- if (typeof resolver === "string") {
3214
- const absolutePath = path.resolve(this.options.baseDir, resolver);
3215
- const resolverDoc = await this.parser.parseFile(absolutePath);
3216
- const baseDir = path.dirname(absolutePath);
3217
- return { resolverDoc, baseDir };
3218
- } else {
3219
- const resolverDoc = this.parser.parseInline(resolver);
3220
- return { resolverDoc, baseDir: this.options.baseDir };
3234
+ if (typeof resolver !== "string") {
3235
+ const resolverDoc2 = this.parser.parseInline(resolver);
3236
+ return { resolverDoc: resolverDoc2, baseDir: this.options.baseDir };
3221
3237
  }
3238
+ const absolutePath = path.resolve(this.options.baseDir, resolver);
3239
+ const resolverDoc = await this.parser.parseFile(absolutePath);
3240
+ const baseDir = path.dirname(absolutePath);
3241
+ return { resolverDoc, baseDir };
3222
3242
  }
3223
3243
  /**
3224
3244
  * Load only the resolver document (without base directory)
@@ -3251,6 +3271,35 @@ function sanitizeDataAttributeName(value) {
3251
3271
  function escapeCssString(value) {
3252
3272
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, " ");
3253
3273
  }
3274
+ function groupTokensByType(tokens, typeGroupMap) {
3275
+ const groupMap = /* @__PURE__ */ new Map();
3276
+ for (const [, token] of getSortedTokenEntries(tokens)) {
3277
+ const groupName = typeGroupMap[token.$type ?? ""] ?? "Other";
3278
+ const existing = groupMap.get(groupName) ?? [];
3279
+ existing.push(token);
3280
+ groupMap.set(groupName, existing);
3281
+ }
3282
+ return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
3283
+ name,
3284
+ tokens: groupTokens
3285
+ }));
3286
+ }
3287
+ function indentStr(width, level) {
3288
+ return " ".repeat(width * level);
3289
+ }
3290
+ function buildGeneratedFileHeader() {
3291
+ return [
3292
+ "// Generated by Dispersa - do not edit manually",
3293
+ "// https://github.com/timges/dispersa"
3294
+ ].join("\n");
3295
+ }
3296
+ function toSafeIdentifier(name, keywords, capitalize) {
3297
+ const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
3298
+ const cased = capitalize ? camel.charAt(0).toUpperCase() + camel.slice(1) : camel.charAt(0).toLowerCase() + camel.slice(1);
3299
+ const safe = /^\d/.test(cased) ? `_${cased}` : cased;
3300
+ const keyCheck = capitalize ? safe.charAt(0).toLowerCase() + safe.slice(1) : safe;
3301
+ return keywords.has(keyCheck) ? `\`${safe}\`` : safe;
3302
+ }
3254
3303
  function normalizeModifierInputs(inputs) {
3255
3304
  const normalized = {};
3256
3305
  for (const [key, value] of Object.entries(inputs)) {
@@ -3258,6 +3307,14 @@ function normalizeModifierInputs(inputs) {
3258
3307
  }
3259
3308
  return normalized;
3260
3309
  }
3310
+ function assertFileRequired(buildPath, outputFile, outputName, presetLabel) {
3311
+ const requiresFile = buildPath !== void 0 && buildPath !== "";
3312
+ if (!outputFile && requiresFile) {
3313
+ throw new ConfigurationError(
3314
+ `Output "${outputName}": file is required for ${presetLabel} output`
3315
+ );
3316
+ }
3317
+ }
3261
3318
  function buildStablePermutationKey(modifierInputs, dimensions) {
3262
3319
  return dimensions.map((dimension) => `${dimension}=${modifierInputs[dimension] ?? ""}`).join("|");
3263
3320
  }
@@ -3307,14 +3364,18 @@ function generatePermutationKey(modifierInputs, resolver, isBase) {
3307
3364
  }
3308
3365
  return buildStablePermutationKey(normalizedInputs, metadata.dimensions);
3309
3366
  }
3310
- function buildInMemoryOutputKey(params) {
3311
- const { outputName, extension, modifierInputs, resolver, defaults } = params;
3367
+ function isBasePermutation(modifierInputs, defaults) {
3312
3368
  const normalizedInputs = normalizeModifierInputs(modifierInputs);
3313
3369
  const normalizedDefaults = normalizeModifierInputs(defaults);
3314
- const isBase = Object.entries(normalizedDefaults).every(
3315
- ([key, value]) => normalizedInputs[key] === value
3370
+ return Object.entries(normalizedDefaults).every(([key, value]) => normalizedInputs[key] === value);
3371
+ }
3372
+ function buildInMemoryOutputKey(params) {
3373
+ const { outputName, extension, modifierInputs, resolver, defaults } = params;
3374
+ const permutationKey = generatePermutationKey(
3375
+ modifierInputs,
3376
+ resolver,
3377
+ isBasePermutation(modifierInputs, defaults)
3316
3378
  );
3317
- const permutationKey = generatePermutationKey(modifierInputs, resolver, isBase);
3318
3379
  return `${outputName}-${permutationKey}.${extension}`;
3319
3380
  }
3320
3381
  function buildMetadata(resolver) {
@@ -3436,6 +3497,7 @@ function resolveFileName(fileName, modifierInputs) {
3436
3497
  }
3437
3498
  var init_utils = __esm({
3438
3499
  "src/renderers/bundlers/utils.ts"() {
3500
+ init_errors();
3439
3501
  init_token_utils();
3440
3502
  }
3441
3503
  });
@@ -3445,36 +3507,38 @@ var js_exports = {};
3445
3507
  __export(js_exports, {
3446
3508
  bundleAsJsModule: () => bundleAsJsModule
3447
3509
  });
3510
+ function updateStringTracking(state, char) {
3511
+ if (!state.escaped && (char === '"' || char === "'" || char === "`")) {
3512
+ if (!state.inString) {
3513
+ state.inString = true;
3514
+ state.stringChar = char;
3515
+ } else if (char === state.stringChar) {
3516
+ state.inString = false;
3517
+ state.stringChar = "";
3518
+ }
3519
+ }
3520
+ state.escaped = !state.escaped && char === "\\";
3521
+ }
3448
3522
  function extractObjectFromJsModule(formattedJs) {
3449
3523
  const assignmentMatch = /const\s+\w+\s*=\s*\{/.exec(formattedJs);
3450
3524
  if (!assignmentMatch) {
3451
3525
  return "{}";
3452
3526
  }
3453
3527
  const startIndex = assignmentMatch.index + assignmentMatch[0].length - 1;
3528
+ const state = { inString: false, stringChar: "", escaped: false };
3454
3529
  let braceCount = 0;
3455
- let inString = false;
3456
- let stringChar = "";
3457
- let escaped = false;
3458
3530
  for (let i = startIndex; i < formattedJs.length; i++) {
3459
3531
  const char = formattedJs[i];
3460
- if (!escaped && (char === '"' || char === "'" || char === "`")) {
3461
- if (!inString) {
3462
- inString = true;
3463
- stringChar = char;
3464
- } else if (char === stringChar) {
3465
- inString = false;
3466
- stringChar = "";
3467
- }
3468
- }
3469
- escaped = !escaped && char === "\\";
3470
- if (!inString) {
3471
- if (char === "{") {
3472
- braceCount++;
3473
- } else if (char === "}") {
3474
- braceCount--;
3475
- if (braceCount === 0) {
3476
- return formattedJs.substring(startIndex, i + 1);
3477
- }
3532
+ updateStringTracking(state, char);
3533
+ if (state.inString) {
3534
+ continue;
3535
+ }
3536
+ if (char === "{") {
3537
+ braceCount++;
3538
+ } else if (char === "}") {
3539
+ braceCount--;
3540
+ if (braceCount === 0) {
3541
+ return formattedJs.substring(startIndex, i + 1);
3478
3542
  }
3479
3543
  }
3480
3544
  }
@@ -3571,22 +3635,19 @@ __export(json_exports, {
3571
3635
  bundleAsJson: () => bundleAsJson
3572
3636
  });
3573
3637
  async function bundleAsJson(bundleData, resolver, formatTokens) {
3638
+ if (!formatTokens) {
3639
+ throw new ConfigurationError("JSON formatter was not provided");
3640
+ }
3574
3641
  const metadata = buildMetadata(resolver);
3575
3642
  const tokens = {};
3576
3643
  for (const { tokens: tokenSet, modifierInputs } of bundleData) {
3577
3644
  const cleanTokens = stripInternalMetadata(tokenSet);
3578
- if (!formatTokens) {
3579
- throw new ConfigurationError("JSON formatter was not provided");
3580
- }
3581
3645
  const normalizedInputs = normalizeModifierInputs(modifierInputs);
3582
3646
  const key = buildStablePermutationKey(normalizedInputs, metadata.dimensions);
3583
3647
  const themeJson = await formatTokens(cleanTokens);
3584
3648
  tokens[key] = JSON.parse(themeJson);
3585
3649
  }
3586
- const bundle = {
3587
- _meta: metadata,
3588
- tokens
3589
- };
3650
+ const bundle = { _meta: metadata, tokens };
3590
3651
  return JSON.stringify(bundle, null, 2);
3591
3652
  }
3592
3653
  var init_json = __esm({
@@ -3652,15 +3713,15 @@ var TypeGenerator = class {
3652
3713
  const lines = [];
3653
3714
  if (names.length === 0) {
3654
3715
  lines.push(`export type ${typeName} = never`);
3655
- } else {
3656
- lines.push(`export type ${typeName} =`);
3657
- for (let i = 0; i < names.length; i++) {
3658
- const name = names[i];
3659
- if (name == null) {
3660
- continue;
3661
- }
3662
- lines.push(` | "${name}"`);
3716
+ return lines;
3717
+ }
3718
+ lines.push(`export type ${typeName} =`);
3719
+ for (let i = 0; i < names.length; i++) {
3720
+ const name = names[i];
3721
+ if (name == null) {
3722
+ continue;
3663
3723
  }
3724
+ lines.push(` | "${name}"`);
3664
3725
  }
3665
3726
  return lines;
3666
3727
  }
@@ -3686,15 +3747,10 @@ var TypeGenerator = class {
3686
3747
  generateStructureType(tokens, options) {
3687
3748
  const lines = [];
3688
3749
  const structure = this.buildNestedStructure(tokens);
3689
- if (options.exportType === "type") {
3690
- lines.push(`export type ${options.moduleName} = {`);
3691
- this.addStructureProperties(lines, structure, 1);
3692
- lines.push("}");
3693
- } else {
3694
- lines.push(`export interface ${options.moduleName} {`);
3695
- this.addStructureProperties(lines, structure, 1);
3696
- lines.push("}");
3697
- }
3750
+ const opener = options.exportType === "type" ? `export type ${options.moduleName} = {` : `export interface ${options.moduleName} {`;
3751
+ lines.push(opener);
3752
+ this.addStructureProperties(lines, structure, 1);
3753
+ lines.push("}");
3698
3754
  return lines;
3699
3755
  }
3700
3756
  /**
@@ -3728,20 +3784,20 @@ var TypeGenerator = class {
3728
3784
  /**
3729
3785
  * Add structure properties to lines
3730
3786
  */
3731
- addStructureProperties(lines, structure, indent2) {
3732
- const indentStr = " ".repeat(indent2);
3787
+ addStructureProperties(lines, structure, indent) {
3788
+ const indentStr2 = " ".repeat(indent);
3733
3789
  for (const [key, value] of Object.entries(structure)) {
3734
3790
  if (this.isToken(value)) {
3735
3791
  const token = value;
3736
3792
  if (token.$description) {
3737
- lines.push(`${indentStr}/** ${token.$description} */`);
3793
+ lines.push(`${indentStr2}/** ${token.$description} */`);
3738
3794
  }
3739
3795
  const valueType = this.inferValueType(token);
3740
- lines.push(`${indentStr}${this.quoteKey(key)}: ${valueType}`);
3796
+ lines.push(`${indentStr2}${this.quoteKey(key)}: ${valueType}`);
3741
3797
  } else {
3742
- lines.push(`${indentStr}${this.quoteKey(key)}: {`);
3743
- this.addStructureProperties(lines, value, indent2 + 1);
3744
- lines.push(`${indentStr}}`);
3798
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
3799
+ this.addStructureProperties(lines, value, indent + 1);
3800
+ lines.push(`${indentStr2}}`);
3745
3801
  }
3746
3802
  }
3747
3803
  }
@@ -3933,28 +3989,27 @@ var BuildOrchestrator = class {
3933
3989
  if (config.hooks?.onBuildStart) {
3934
3990
  await config.hooks.onBuildStart({ config, resolver });
3935
3991
  }
3936
- let permutations;
3937
- if (config.permutations && config.permutations.length > 0) {
3938
- permutations = await Promise.all(
3939
- config.permutations.map(async (modifierInputs) => {
3940
- const { tokens, modifierInputs: resolvedInputs } = await this.pipeline.resolve(
3941
- resolver,
3942
- modifierInputs,
3943
- config.transforms,
3944
- config.preprocessors,
3945
- config.filters
3946
- );
3947
- return { tokens, modifierInputs: resolvedInputs };
3948
- })
3949
- );
3950
- } else {
3951
- permutations = await this.pipeline.resolveAllPermutations(
3992
+ if (!config.permutations || config.permutations.length === 0) {
3993
+ const permutations2 = await this.pipeline.resolveAllPermutations(
3952
3994
  resolver,
3953
3995
  config.transforms,
3954
3996
  config.preprocessors,
3955
3997
  config.filters
3956
3998
  );
3999
+ return this.executeBuild(buildPath, config, permutations2, resolver);
3957
4000
  }
4001
+ const permutations = await Promise.all(
4002
+ config.permutations.map(async (modifierInputs) => {
4003
+ const { tokens, modifierInputs: resolvedInputs } = await this.pipeline.resolve(
4004
+ resolver,
4005
+ modifierInputs,
4006
+ config.transforms,
4007
+ config.preprocessors,
4008
+ config.filters
4009
+ );
4010
+ return { tokens, modifierInputs: resolvedInputs };
4011
+ })
4012
+ );
3958
4013
  return this.executeBuild(buildPath, config, permutations, resolver);
3959
4014
  }
3960
4015
  /**
@@ -3976,44 +4031,15 @@ var BuildOrchestrator = class {
3976
4031
  * @returns Build result with success status, outputs, and any errors
3977
4032
  */
3978
4033
  async executeBuild(buildPath, config, permutations, resolver) {
3979
- const outputs = [];
3980
- const errors = [];
3981
4034
  try {
3982
4035
  const resolverDoc = await resolveResolverDocument(resolver);
3983
4036
  const metadata = buildMetadata(resolverDoc);
3984
- const basePermutation = metadata.defaults;
3985
4037
  const settled = await Promise.allSettled(
3986
- config.outputs.map(async (output) => {
3987
- if (output.hooks?.onBuildStart) {
3988
- await output.hooks.onBuildStart({ config, resolver });
3989
- }
3990
- try {
3991
- const results = await this.processOutput(output, permutations, resolverDoc, metadata, basePermutation, buildPath);
3992
- if (output.hooks?.onBuildEnd) {
3993
- await output.hooks.onBuildEnd({ success: true, outputs: results });
3994
- }
3995
- return results;
3996
- } catch (error) {
3997
- if (output.hooks?.onBuildEnd) {
3998
- await output.hooks.onBuildEnd({ success: false, outputs: [], errors: [toBuildError(error, output.name)] });
3999
- }
4000
- throw error;
4001
- }
4002
- })
4038
+ config.outputs.map(
4039
+ (output) => this.buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver)
4040
+ )
4003
4041
  );
4004
- for (let i = 0; i < settled.length; i++) {
4005
- const outcome = settled[i];
4006
- if (outcome.status === "fulfilled") {
4007
- outputs.push(...outcome.value);
4008
- } else {
4009
- errors.push(toBuildError(outcome.reason, config.outputs[i].name));
4010
- }
4011
- }
4012
- const result = {
4013
- success: errors.length === 0,
4014
- outputs,
4015
- errors: errors.length > 0 ? errors : void 0
4016
- };
4042
+ const result = this.collectSettledResults(settled, config);
4017
4043
  if (config.hooks?.onBuildEnd) {
4018
4044
  await config.hooks.onBuildEnd(result);
4019
4045
  }
@@ -4030,6 +4056,40 @@ var BuildOrchestrator = class {
4030
4056
  return result;
4031
4057
  }
4032
4058
  }
4059
+ async buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver) {
4060
+ if (output.hooks?.onBuildStart) {
4061
+ await output.hooks.onBuildStart({ config, resolver });
4062
+ }
4063
+ try {
4064
+ const results = await this.processOutput(output, permutations, resolverDoc, metadata, metadata.defaults, buildPath);
4065
+ if (output.hooks?.onBuildEnd) {
4066
+ await output.hooks.onBuildEnd({ success: true, outputs: results });
4067
+ }
4068
+ return results;
4069
+ } catch (error) {
4070
+ if (output.hooks?.onBuildEnd) {
4071
+ await output.hooks.onBuildEnd({ success: false, outputs: [], errors: [toBuildError(error, output.name)] });
4072
+ }
4073
+ throw error;
4074
+ }
4075
+ }
4076
+ collectSettledResults(settled, config) {
4077
+ const outputs = [];
4078
+ const errors = [];
4079
+ for (let i = 0; i < settled.length; i++) {
4080
+ const outcome = settled[i];
4081
+ if (outcome.status === "fulfilled") {
4082
+ outputs.push(...outcome.value);
4083
+ } else {
4084
+ errors.push(toBuildError(outcome.reason, config.outputs[i].name));
4085
+ }
4086
+ }
4087
+ return {
4088
+ success: errors.length === 0,
4089
+ outputs,
4090
+ errors: errors.length > 0 ? errors : void 0
4091
+ };
4092
+ }
4033
4093
  async processOutput(output, permutations, resolverDoc, metadata, basePermutation, buildPath) {
4034
4094
  if (!output.renderer.format) {
4035
4095
  throw new ConfigurationError("Renderer does not implement format()");
@@ -4079,7 +4139,7 @@ async function writeOutputFile(fileName, content, encoding = "utf-8") {
4079
4139
  }
4080
4140
  }
4081
4141
 
4082
- // src/processing/token-modifier.ts
4142
+ // src/processing/apply.ts
4083
4143
  function applyTransforms(tokens, transformList) {
4084
4144
  const result = {};
4085
4145
  for (const [name, token] of Object.entries(tokens)) {
@@ -5357,14 +5417,15 @@ var ResolutionEngine = class {
5357
5417
  mergeTokens(target, source) {
5358
5418
  const result = { ...target };
5359
5419
  for (const [key, value] of Object.entries(source)) {
5360
- if (typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value)) {
5361
- result[key] = this.mergeTokens(
5362
- result[key] ?? {},
5363
- value
5364
- );
5365
- } else {
5420
+ const isGroup = typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value);
5421
+ if (!isGroup) {
5366
5422
  result[key] = value;
5423
+ continue;
5367
5424
  }
5425
+ result[key] = this.mergeTokens(
5426
+ result[key] ?? {},
5427
+ value
5428
+ );
5368
5429
  }
5369
5430
  return result;
5370
5431
  }
@@ -6510,7 +6571,7 @@ function colorObjectToHex(color) {
6510
6571
  return formatHex(culoriColor);
6511
6572
  }
6512
6573
 
6513
- // src/processing/processors/transforms/built-in/dimension-converter.ts
6574
+ // src/processing/transforms/built-in/dimension-converter.ts
6514
6575
  function isDimensionObject(value) {
6515
6576
  return typeof value === "object" && value !== null && "value" in value && "unit" in value;
6516
6577
  }
@@ -6518,6 +6579,14 @@ function dimensionObjectToString(dimension) {
6518
6579
  return `${dimension.value}${dimension.unit}`;
6519
6580
  }
6520
6581
 
6582
+ // src/processing/transforms/built-in/duration-converter.ts
6583
+ function isDurationObject(value) {
6584
+ return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
6585
+ }
6586
+ function durationObjectToString(duration) {
6587
+ return `${duration.value}${duration.unit}`;
6588
+ }
6589
+
6521
6590
  // src/renderers/android.ts
6522
6591
  init_errors();
6523
6592
  init_token_utils();
@@ -6569,9 +6638,6 @@ function resolveColorFormat(format) {
6569
6638
  }
6570
6639
  return "argb_hex";
6571
6640
  }
6572
- function indent(width, level) {
6573
- return " ".repeat(width * level);
6574
- }
6575
6641
  function escapeKotlinString(str) {
6576
6642
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
6577
6643
  }
@@ -6587,22 +6653,6 @@ function roundComponent(value) {
6587
6653
  function toResourceName(family) {
6588
6654
  return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
6589
6655
  }
6590
- function toPascalCase(name) {
6591
- const pascal = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
6592
- const result = pascal.charAt(0).toUpperCase() + pascal.slice(1);
6593
- if (/^\d/.test(result)) {
6594
- return `_${result}`;
6595
- }
6596
- return KOTLIN_KEYWORDS.has(result.charAt(0).toLowerCase() + result.slice(1)) ? `\`${result}\`` : result;
6597
- }
6598
- function toKotlinIdentifier(name) {
6599
- const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
6600
- const identifier = camel.charAt(0).toLowerCase() + camel.slice(1);
6601
- if (/^\d/.test(identifier)) {
6602
- return `_${identifier}`;
6603
- }
6604
- return KOTLIN_KEYWORDS.has(identifier) ? `\`${identifier}\`` : identifier;
6605
- }
6606
6656
  var AndroidRenderer = class {
6607
6657
  async format(context, options) {
6608
6658
  if (!options?.packageName) {
@@ -6610,6 +6660,7 @@ var AndroidRenderer = class {
6610
6660
  `Output "${context.output.name}": packageName is required for Android output`
6611
6661
  );
6612
6662
  }
6663
+ const visibility = options?.visibility;
6613
6664
  const opts = {
6614
6665
  preset: options?.preset ?? "standalone",
6615
6666
  packageName: options.packageName,
@@ -6617,7 +6668,8 @@ var AndroidRenderer = class {
6617
6668
  colorFormat: resolveColorFormat(options?.colorFormat),
6618
6669
  colorSpace: options?.colorSpace ?? "sRGB",
6619
6670
  structure: options?.structure ?? "nested",
6620
- visibility: options?.visibility,
6671
+ visibility,
6672
+ visPrefix: visibility ? `${visibility} ` : "",
6621
6673
  indent: options?.indent ?? 4
6622
6674
  };
6623
6675
  if (opts.preset === "bundle") {
@@ -6650,19 +6702,6 @@ var AndroidRenderer = class {
6650
6702
  // -----------------------------------------------------------------------
6651
6703
  // Flat structure grouping
6652
6704
  // -----------------------------------------------------------------------
6653
- groupTokensByType(tokens) {
6654
- const groupMap = /* @__PURE__ */ new Map();
6655
- for (const [, token] of getSortedTokenEntries(tokens)) {
6656
- const groupName = KOTLIN_TYPE_GROUP_MAP[token.$type ?? ""] ?? "Other";
6657
- const existing = groupMap.get(groupName) ?? [];
6658
- existing.push(token);
6659
- groupMap.set(groupName, existing);
6660
- }
6661
- return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
6662
- name,
6663
- tokens: groupTokens
6664
- }));
6665
- }
6666
6705
  /**
6667
6706
  * Builds a flattened camelCase name from a token's path, stripping the
6668
6707
  * type prefix segment (which is already represented by the group object).
@@ -6671,7 +6710,7 @@ var AndroidRenderer = class {
6671
6710
  const path7 = token.path;
6672
6711
  const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
6673
6712
  const joined = withoutTypePrefix.join("_");
6674
- return toKotlinIdentifier(joined);
6713
+ return toSafeIdentifier(joined, KOTLIN_KEYWORDS, false);
6675
6714
  }
6676
6715
  // -----------------------------------------------------------------------
6677
6716
  // Rendering
@@ -6683,22 +6722,21 @@ var AndroidRenderer = class {
6683
6722
  return this.formatAsNested(tokens, options);
6684
6723
  }
6685
6724
  formatAsNested(tokens, options) {
6725
+ const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6686
6726
  const tree = this.buildTokenTree(tokens);
6687
- const tokenTypes = /* @__PURE__ */ new Set();
6688
- this.collectTokenTypes(tree, tokenTypes);
6689
- return this.buildFile(tokenTypes, options, (lines, vis) => {
6727
+ return this.buildFile(tokenTypes, options, (lines) => {
6690
6728
  lines.push(`@Suppress("unused")`);
6691
- lines.push(`${vis}object ${options.objectName} {`);
6729
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
6692
6730
  this.renderTreeChildren(lines, tree, 1, options);
6693
6731
  lines.push("}");
6694
6732
  });
6695
6733
  }
6696
6734
  formatAsFlat(tokens, options) {
6697
- const groups = this.groupTokensByType(tokens);
6735
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
6698
6736
  const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6699
- return this.buildFile(tokenTypes, options, (lines, vis) => {
6737
+ return this.buildFile(tokenTypes, options, (lines) => {
6700
6738
  lines.push(`@Suppress("unused")`);
6701
- lines.push(`${vis}object ${options.objectName} {`);
6739
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
6702
6740
  this.renderFlatGroups(lines, groups, 1, options);
6703
6741
  lines.push("}");
6704
6742
  });
@@ -6709,9 +6747,8 @@ var AndroidRenderer = class {
6709
6747
  */
6710
6748
  buildFile(tokenTypes, options, renderBody) {
6711
6749
  const imports = this.collectImports(tokenTypes, options);
6712
- const vis = options.visibility ? `${options.visibility} ` : "";
6713
6750
  const lines = [];
6714
- lines.push(this.buildFileHeader());
6751
+ lines.push(buildGeneratedFileHeader());
6715
6752
  lines.push("");
6716
6753
  lines.push(`package ${options.packageName}`);
6717
6754
  lines.push("");
@@ -6722,19 +6759,18 @@ var AndroidRenderer = class {
6722
6759
  lines.push("");
6723
6760
  }
6724
6761
  if (tokenTypes.has("shadow")) {
6725
- lines.push(...this.buildShadowTokenClass(vis, options));
6762
+ lines.push(...this.buildShadowTokenClass(options));
6726
6763
  lines.push("");
6727
6764
  }
6728
- renderBody(lines, vis);
6765
+ renderBody(lines);
6729
6766
  lines.push("");
6730
6767
  return lines.join("\n");
6731
6768
  }
6732
6769
  renderFlatGroups(lines, groups, baseDepth, options) {
6733
- const vis = options.visibility ? `${options.visibility} ` : "";
6734
- const groupIndent = indent(options.indent, baseDepth);
6735
- const valIndent = indent(options.indent, baseDepth + 1);
6770
+ const groupIndent = indentStr(options.indent, baseDepth);
6771
+ const valIndent = indentStr(options.indent, baseDepth + 1);
6736
6772
  for (const group of groups) {
6737
- lines.push(`${groupIndent}${vis}object ${group.name} {`);
6773
+ lines.push(`${groupIndent}${options.visPrefix}object ${group.name} {`);
6738
6774
  for (const token of group.tokens) {
6739
6775
  const kotlinName = this.buildFlatKotlinName(token);
6740
6776
  const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
@@ -6742,23 +6778,24 @@ var AndroidRenderer = class {
6742
6778
  if (token.$description) {
6743
6779
  lines.push(`${valIndent}/** ${escapeKDoc(token.$description)} */`);
6744
6780
  }
6745
- lines.push(`${valIndent}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6781
+ lines.push(
6782
+ `${valIndent}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`
6783
+ );
6746
6784
  }
6747
6785
  lines.push(`${groupIndent}}`);
6748
6786
  lines.push("");
6749
6787
  }
6750
6788
  }
6751
6789
  renderTreeChildren(lines, node, depth, options) {
6752
- const vis = options.visibility ? `${options.visibility} ` : "";
6753
- const pad = indent(options.indent, depth);
6790
+ const pad = indentStr(options.indent, depth);
6754
6791
  const entries = Array.from(node.children.entries());
6755
6792
  for (let idx = 0; idx < entries.length; idx++) {
6756
6793
  const [key, child] = entries[idx];
6757
6794
  if (child.token && child.children.size === 0) {
6758
6795
  this.renderLeaf(lines, key, child.token, depth, options);
6759
6796
  } else if (child.children.size > 0 && !child.token) {
6760
- const objectName = toPascalCase(key);
6761
- lines.push(`${pad}${vis}object ${objectName} {`);
6797
+ const objectName = toSafeIdentifier(key, KOTLIN_KEYWORDS, true);
6798
+ lines.push(`${pad}${options.visPrefix}object ${objectName} {`);
6762
6799
  this.renderTreeChildren(lines, child, depth + 1, options);
6763
6800
  lines.push(`${pad}}`);
6764
6801
  if (idx < entries.length - 1) {
@@ -6771,30 +6808,23 @@ var AndroidRenderer = class {
6771
6808
  }
6772
6809
  }
6773
6810
  renderLeaf(lines, key, token, depth, options) {
6774
- const vis = options.visibility ? `${options.visibility} ` : "";
6775
- const pad = indent(options.indent, depth);
6776
- const kotlinName = toKotlinIdentifier(key);
6811
+ const pad = indentStr(options.indent, depth);
6812
+ const kotlinName = toSafeIdentifier(key, KOTLIN_KEYWORDS, false);
6777
6813
  const kotlinValue = this.formatKotlinValue(token, options, depth);
6778
6814
  const annotation = this.typeAnnotationSuffix(token);
6779
6815
  if (token.$description) {
6780
6816
  lines.push(`${pad}/** ${escapeKDoc(token.$description)} */`);
6781
6817
  }
6782
- lines.push(`${pad}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6783
- }
6784
- buildFileHeader() {
6785
- return [
6786
- "// Generated by Dispersa - do not edit manually",
6787
- "// https://github.com/timges/dispersa"
6788
- ].join("\n");
6818
+ lines.push(`${pad}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`);
6789
6819
  }
6790
6820
  // -----------------------------------------------------------------------
6791
6821
  // Shadow data class
6792
6822
  // -----------------------------------------------------------------------
6793
- buildShadowTokenClass(vis, options) {
6794
- const i1 = indent(options.indent, 1);
6823
+ buildShadowTokenClass(options) {
6824
+ const i1 = indentStr(options.indent, 1);
6795
6825
  return [
6796
6826
  "@Immutable",
6797
- `${vis}data class ShadowToken(`,
6827
+ `${options.visPrefix}data class ShadowToken(`,
6798
6828
  `${i1}val color: Color,`,
6799
6829
  `${i1}val elevation: Dp,`,
6800
6830
  `${i1}val offsetX: Dp,`,
@@ -6845,14 +6875,6 @@ var AndroidRenderer = class {
6845
6875
  }
6846
6876
  return Array.from(imports).sort();
6847
6877
  }
6848
- collectTokenTypes(node, types) {
6849
- if (node.token?.$type) {
6850
- types.add(node.token.$type);
6851
- }
6852
- for (const child of node.children.values()) {
6853
- this.collectTokenTypes(child, types);
6854
- }
6855
- }
6856
6878
  collectTokenTypesFromEntries(tokens) {
6857
6879
  const types = /* @__PURE__ */ new Set();
6858
6880
  for (const [, token] of Object.entries(tokens)) {
@@ -7079,9 +7101,8 @@ var AndroidRenderer = class {
7079
7101
  return map[name.toLowerCase()];
7080
7102
  }
7081
7103
  formatDurationValue(value) {
7082
- if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
7083
- const dur = value;
7084
- return dur.unit === "ms" ? `${dur.value}.milliseconds` : `${dur.value}.seconds`;
7104
+ if (isDurationObject(value)) {
7105
+ return value.unit === "ms" ? `${value.value}.milliseconds` : `${value.value}.seconds`;
7085
7106
  }
7086
7107
  return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
7087
7108
  }
@@ -7099,8 +7120,8 @@ var AndroidRenderer = class {
7099
7120
  const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
7100
7121
  const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
7101
7122
  const offsetY = isDimensionObject(shadow.offsetY) ? this.formatDimensionValue(shadow.offsetY) : "0.dp";
7102
- const propIndent = indent(options.indent, depth + 1);
7103
- const closeIndent = indent(options.indent, depth);
7123
+ const propIndent = indentStr(options.indent, depth + 1);
7124
+ const closeIndent = indentStr(options.indent, depth);
7104
7125
  return [
7105
7126
  "ShadowToken(",
7106
7127
  `${propIndent}color = ${color},`,
@@ -7149,8 +7170,8 @@ var AndroidRenderer = class {
7149
7170
  if (parts.length === 0) {
7150
7171
  return "TextStyle()";
7151
7172
  }
7152
- const propIndent = indent(options.indent, depth + 1);
7153
- const closeIndent = indent(options.indent, depth);
7173
+ const propIndent = indentStr(options.indent, depth + 1);
7174
+ const closeIndent = indentStr(options.indent, depth);
7154
7175
  return `TextStyle(
7155
7176
  ${parts.map((p) => `${propIndent}${p}`).join(",\n")},
7156
7177
  ${closeIndent})`;
@@ -7159,12 +7180,12 @@ ${closeIndent})`;
7159
7180
  // Output: standalone
7160
7181
  // -----------------------------------------------------------------------
7161
7182
  async formatStandalone(context, options) {
7162
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
7163
- if (!context.output.file && requiresFile) {
7164
- throw new ConfigurationError(
7165
- `Output "${context.output.name}": file is required for standalone Android output`
7166
- );
7167
- }
7183
+ assertFileRequired(
7184
+ context.buildPath,
7185
+ context.output.file,
7186
+ context.output.name,
7187
+ "standalone Android"
7188
+ );
7168
7189
  const files = {};
7169
7190
  for (const { tokens, modifierInputs } of context.permutations) {
7170
7191
  const processedTokens = stripInternalMetadata(tokens);
@@ -7184,12 +7205,12 @@ ${closeIndent})`;
7184
7205
  // Output: bundle
7185
7206
  // -----------------------------------------------------------------------
7186
7207
  async formatBundle(context, options) {
7187
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
7188
- if (!context.output.file && requiresFile) {
7189
- throw new ConfigurationError(
7190
- `Output "${context.output.name}": file is required for bundle Android output`
7191
- );
7192
- }
7208
+ assertFileRequired(
7209
+ context.buildPath,
7210
+ context.output.file,
7211
+ context.output.name,
7212
+ "bundle Android"
7213
+ );
7193
7214
  const content = this.formatBundleContent(context, options);
7194
7215
  const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
7195
7216
  outputName: context.output.name,
@@ -7202,15 +7223,15 @@ ${closeIndent})`;
7202
7223
  }
7203
7224
  formatBundleContent(context, options) {
7204
7225
  const allTokenTypes = this.collectAllPermutationTypes(context);
7205
- return this.buildFile(allTokenTypes, options, (lines, vis) => {
7206
- const i1 = indent(options.indent, 1);
7226
+ return this.buildFile(allTokenTypes, options, (lines) => {
7227
+ const i1 = indentStr(options.indent, 1);
7207
7228
  lines.push(`@Suppress("unused")`);
7208
- lines.push(`${vis}object ${options.objectName} {`);
7229
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
7209
7230
  for (let idx = 0; idx < context.permutations.length; idx++) {
7210
7231
  const { tokens, modifierInputs } = context.permutations[idx];
7211
7232
  const processedTokens = stripInternalMetadata(tokens);
7212
7233
  const permName = this.buildPermutationName(modifierInputs);
7213
- lines.push(`${i1}${vis}object ${permName} {`);
7234
+ lines.push(`${i1}${options.visPrefix}object ${permName} {`);
7214
7235
  this.renderBundleTokens(lines, processedTokens, options, 2);
7215
7236
  lines.push(`${i1}}`);
7216
7237
  if (idx < context.permutations.length - 1) {
@@ -7221,20 +7242,17 @@ ${closeIndent})`;
7221
7242
  });
7222
7243
  }
7223
7244
  collectAllPermutationTypes(context) {
7224
- const allTokenTypes = /* @__PURE__ */ new Set();
7245
+ const types = /* @__PURE__ */ new Set();
7225
7246
  for (const { tokens } of context.permutations) {
7226
- const processed = stripInternalMetadata(tokens);
7227
- for (const [, token] of Object.entries(processed)) {
7228
- if (token.$type) {
7229
- allTokenTypes.add(token.$type);
7230
- }
7247
+ for (const t of this.collectTokenTypesFromEntries(stripInternalMetadata(tokens))) {
7248
+ types.add(t);
7231
7249
  }
7232
7250
  }
7233
- return allTokenTypes;
7251
+ return types;
7234
7252
  }
7235
7253
  renderBundleTokens(lines, tokens, options, baseDepth) {
7236
7254
  if (options.structure === "flat") {
7237
- const groups = this.groupTokensByType(tokens);
7255
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
7238
7256
  this.renderFlatGroups(lines, groups, baseDepth, options);
7239
7257
  return;
7240
7258
  }
@@ -7246,7 +7264,7 @@ ${closeIndent})`;
7246
7264
  if (values.length === 0) {
7247
7265
  return "Default";
7248
7266
  }
7249
- return values.map((v) => toPascalCase(v)).join("");
7267
+ return values.map((v) => toSafeIdentifier(v, KOTLIN_KEYWORDS, true)).join("");
7250
7268
  }
7251
7269
  };
7252
7270
  function androidRenderer() {
@@ -7266,19 +7284,19 @@ init_token_utils();
7266
7284
  // src/renderers/bundlers/css.ts
7267
7285
  init_errors();
7268
7286
  init_utils();
7287
+ var REF_PREFIX_SETS = "#/sets/";
7288
+ var REF_PREFIX_MODIFIERS = "#/modifiers/";
7269
7289
  var getSourceSet = (token) => {
7270
7290
  if (typeof token !== "object" || token === null) {
7271
7291
  return void 0;
7272
7292
  }
7273
- const maybe = token;
7274
- return typeof maybe._sourceSet === "string" ? maybe._sourceSet : void 0;
7293
+ return "_sourceSet" in token && typeof token._sourceSet === "string" ? token._sourceSet : void 0;
7275
7294
  };
7276
7295
  var getSourceModifier = (token) => {
7277
7296
  if (typeof token !== "object" || token === null) {
7278
7297
  return void 0;
7279
7298
  }
7280
- const maybe = token;
7281
- return typeof maybe._sourceModifier === "string" ? maybe._sourceModifier : void 0;
7299
+ return "_sourceModifier" in token && typeof token._sourceModifier === "string" ? token._sourceModifier : void 0;
7282
7300
  };
7283
7301
  async function bundleAsCss(bundleData, resolver, options, formatTokens) {
7284
7302
  const baseItem = bundleData.find((item) => item.isBase);
@@ -7363,6 +7381,15 @@ async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, o
7363
7381
  return `/* Modifier: ${modifier}=${context} */
7364
7382
  ${css2}`;
7365
7383
  }
7384
+ function addLayerBlock(blocks, included, key, blockTokens, description) {
7385
+ if (Object.keys(blockTokens).length === 0) {
7386
+ return;
7387
+ }
7388
+ for (const k of Object.keys(blockTokens)) {
7389
+ included.add(k);
7390
+ }
7391
+ blocks.push({ key, description, tokens: blockTokens });
7392
+ }
7366
7393
  function collectSetTokens(tokens, setName, included) {
7367
7394
  const result = {};
7368
7395
  for (const [name, token] of Object.entries(tokens)) {
@@ -7393,75 +7420,67 @@ function collectRemainder(tokens, included) {
7393
7420
  function buildSetLayerBlocks(tokens, resolver) {
7394
7421
  const blocks = [];
7395
7422
  const included = /* @__PURE__ */ new Set();
7396
- const addBlock = (key, blockTokens, description) => {
7397
- if (Object.keys(blockTokens).length === 0) {
7398
- return;
7399
- }
7400
- for (const k of Object.keys(blockTokens)) {
7401
- included.add(k);
7402
- }
7403
- blocks.push({ key, description, tokens: blockTokens });
7404
- };
7405
7423
  for (const item of resolver.resolutionOrder) {
7406
7424
  const ref = item.$ref;
7407
- if (typeof ref !== "string" || !ref.startsWith("#/sets/")) {
7425
+ if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX_SETS)) {
7408
7426
  continue;
7409
7427
  }
7410
- const setName = ref.slice("#/sets/".length);
7411
- addBlock(
7428
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7429
+ addLayerBlock(
7430
+ blocks,
7431
+ included,
7412
7432
  `Set: ${setName}`,
7413
7433
  collectSetTokens(tokens, setName, included),
7414
7434
  resolver.sets?.[setName]?.description
7415
7435
  );
7416
7436
  }
7417
- addBlock("Unattributed", collectRemainder(tokens, included));
7437
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7418
7438
  return blocks;
7419
7439
  }
7420
7440
  function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
7421
7441
  const blocks = [];
7422
7442
  const included = /* @__PURE__ */ new Set();
7423
7443
  const baseInputs = normalizeModifierInputs(baseModifierInputs);
7424
- const addBlock = (key, blockTokens, description) => {
7425
- if (Object.keys(blockTokens).length === 0) {
7426
- return;
7427
- }
7428
- for (const k of Object.keys(blockTokens)) {
7429
- included.add(k);
7430
- }
7431
- blocks.push({ key, description, tokens: blockTokens });
7432
- };
7433
7444
  for (const item of resolver.resolutionOrder) {
7434
7445
  const ref = item.$ref;
7435
7446
  if (typeof ref !== "string") {
7436
7447
  continue;
7437
7448
  }
7438
- if (ref.startsWith("#/sets/")) {
7439
- const setName = ref.slice("#/sets/".length);
7440
- addBlock(
7441
- `Set: ${setName}`,
7442
- collectSetTokens(tokens, setName, included),
7443
- resolver.sets?.[setName]?.description
7444
- );
7445
- continue;
7446
- }
7447
- if (ref.startsWith("#/modifiers/")) {
7448
- const modifierName = ref.slice("#/modifiers/".length);
7449
- const modifier = resolver.modifiers?.[modifierName];
7450
- const selectedContext = baseInputs[modifierName.toLowerCase()];
7451
- if (!modifier || !selectedContext) {
7452
- continue;
7453
- }
7454
- const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7455
- addBlock(
7456
- `Modifier: ${modifierName}=${selectedContext} (default)`,
7457
- collectModifierTokens(tokens, expectedSource, included),
7458
- modifier.description
7459
- );
7460
- }
7449
+ processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver);
7461
7450
  }
7462
- addBlock("Unattributed", collectRemainder(tokens, included));
7451
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7463
7452
  return blocks;
7464
7453
  }
7454
+ function processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver) {
7455
+ if (ref.startsWith(REF_PREFIX_SETS)) {
7456
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7457
+ addLayerBlock(
7458
+ blocks,
7459
+ included,
7460
+ `Set: ${setName}`,
7461
+ collectSetTokens(tokens, setName, included),
7462
+ resolver.sets?.[setName]?.description
7463
+ );
7464
+ return;
7465
+ }
7466
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7467
+ return;
7468
+ }
7469
+ const modifierName = ref.slice(REF_PREFIX_MODIFIERS.length);
7470
+ const modifier = resolver.modifiers?.[modifierName];
7471
+ const selectedContext = baseInputs[modifierName.toLowerCase()];
7472
+ if (!modifier || !selectedContext) {
7473
+ return;
7474
+ }
7475
+ const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7476
+ addLayerBlock(
7477
+ blocks,
7478
+ included,
7479
+ `Modifier: ${modifierName}=${selectedContext} (default)`,
7480
+ collectModifierTokens(tokens, expectedSource, included),
7481
+ modifier.description
7482
+ );
7483
+ }
7465
7484
  function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs) {
7466
7485
  const normalizedModifier = modifierName.toLowerCase();
7467
7486
  const normalizedContext = context.toLowerCase();
@@ -7476,6 +7495,36 @@ function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs
7476
7495
  return Object.entries(baseInputs).every(([k, v]) => k === normalizedModifier || inputs[k] === v);
7477
7496
  });
7478
7497
  }
7498
+ function pushUniqueBundleItem(ordered, includedKeys, item) {
7499
+ if (!item) {
7500
+ return;
7501
+ }
7502
+ const key = stableInputsKey(item.modifierInputs);
7503
+ if (includedKeys.has(key)) {
7504
+ return;
7505
+ }
7506
+ includedKeys.add(key);
7507
+ ordered.push(item);
7508
+ }
7509
+ function appendModifierPermutations(bundleData, modifiers, orderedNames, baseInputs, ordered, includedKeys) {
7510
+ for (const modifierName of orderedNames) {
7511
+ const modifierDef = modifiers[modifierName];
7512
+ if (!modifierDef) {
7513
+ continue;
7514
+ }
7515
+ const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
7516
+ for (const ctx of Object.keys(modifierDef.contexts)) {
7517
+ if (defaultValue === ctx.toLowerCase()) {
7518
+ continue;
7519
+ }
7520
+ pushUniqueBundleItem(
7521
+ ordered,
7522
+ includedKeys,
7523
+ findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs)
7524
+ );
7525
+ }
7526
+ }
7527
+ }
7479
7528
  function orderBundleData(bundleData, resolver, baseItem) {
7480
7529
  const modifiers = resolver.modifiers;
7481
7530
  if (!modifiers) {
@@ -7492,31 +7541,15 @@ function orderBundleData(bundleData, resolver, baseItem) {
7492
7541
  }
7493
7542
  const includedKeys = /* @__PURE__ */ new Set();
7494
7543
  const ordered = [];
7495
- const pushUnique = (item) => {
7496
- if (!item) {
7497
- return;
7498
- }
7499
- const key = stableInputsKey(item.modifierInputs);
7500
- if (includedKeys.has(key)) {
7501
- return;
7502
- }
7503
- includedKeys.add(key);
7504
- ordered.push(item);
7505
- };
7506
- pushUnique(baseItem);
7507
- for (const modifierName of orderedModifierNames) {
7508
- const modifierDef = modifiers[modifierName];
7509
- if (!modifierDef) {
7510
- continue;
7511
- }
7512
- const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
7513
- for (const ctx of Object.keys(modifierDef.contexts)) {
7514
- if (defaultValue === ctx.toLowerCase()) {
7515
- continue;
7516
- }
7517
- pushUnique(findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs));
7518
- }
7519
- }
7544
+ pushUniqueBundleItem(ordered, includedKeys, baseItem);
7545
+ appendModifierPermutations(
7546
+ bundleData,
7547
+ modifiers,
7548
+ orderedModifierNames,
7549
+ baseInputs,
7550
+ ordered,
7551
+ includedKeys
7552
+ );
7520
7553
  return ordered.length > 0 ? ordered : bundleData;
7521
7554
  }
7522
7555
  function getOrderedModifierNames(resolver) {
@@ -7528,10 +7561,10 @@ function getOrderedModifierNames(resolver) {
7528
7561
  if (typeof ref !== "string") {
7529
7562
  continue;
7530
7563
  }
7531
- if (!ref.startsWith("#/modifiers/")) {
7564
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7532
7565
  continue;
7533
7566
  }
7534
- const name = ref.slice("#/modifiers/".length);
7567
+ const name = ref.slice(REF_PREFIX_MODIFIERS.length);
7535
7568
  if (seen.has(name)) {
7536
7569
  continue;
7537
7570
  }
@@ -7602,24 +7635,22 @@ var CssRenderer = class _CssRenderer {
7602
7635
  ...options,
7603
7636
  referenceTokens: options?.referenceTokens ?? tokens
7604
7637
  };
7605
- const groups = this.groupTokens(tokens, opts);
7638
+ const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
7606
7639
  const referenceTokens = opts.referenceTokens;
7607
7640
  const lines = [];
7608
- for (const [selector, groupTokens] of Object.entries(groups)) {
7609
- this.buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts);
7610
- }
7641
+ this.buildCssBlock(lines, sortedTokens, opts.selector, tokens, referenceTokens, opts);
7611
7642
  const cssString = lines.join("");
7612
7643
  return opts.minify ? cssString : await this.formatWithPrettier(cssString);
7613
7644
  }
7614
7645
  buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
7615
- const indent2 = opts.minify ? "" : " ";
7646
+ const indent = opts.minify ? "" : " ";
7616
7647
  const newline = opts.minify ? "" : "\n";
7617
7648
  const space = opts.minify ? "" : " ";
7618
7649
  const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
7619
- const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
7650
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
7620
7651
  if (hasMediaQuery) {
7621
7652
  lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
7622
- lines.push(`${indent2}${selector}${space}{${newline}`);
7653
+ lines.push(`${indent}${selector}${space}{${newline}`);
7623
7654
  } else {
7624
7655
  lines.push(`${selector}${space}{${newline}`);
7625
7656
  }
@@ -7636,21 +7667,21 @@ var CssRenderer = class _CssRenderer {
7636
7667
  );
7637
7668
  }
7638
7669
  if (hasMediaQuery) {
7639
- lines.push(`${indent2}}${newline}`);
7670
+ lines.push(`${indent}}${newline}`);
7640
7671
  }
7641
7672
  lines.push(`}${newline}${newline}`);
7642
7673
  }
7643
- pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent2, newline, space) {
7674
+ pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
7644
7675
  const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
7645
7676
  if (token.$deprecated != null && token.$deprecated !== false) {
7646
7677
  const deprecationMsg = formatDeprecationMessage(token, "", "comment");
7647
- lines.push(`${indent2}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7678
+ lines.push(`${indent}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7648
7679
  }
7649
7680
  if (token.$description && token.$description !== "") {
7650
- lines.push(`${indent2}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7681
+ lines.push(`${indent}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7651
7682
  }
7652
7683
  for (const entry of entries) {
7653
- lines.push(`${indent2}--${entry.name}:${space}${entry.value};${newline}`);
7684
+ lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
7654
7685
  }
7655
7686
  }
7656
7687
  async formatWithPrettier(css2) {
@@ -7665,15 +7696,6 @@ var CssRenderer = class _CssRenderer {
7665
7696
  return css2;
7666
7697
  }
7667
7698
  }
7668
- /**
7669
- * Group tokens by selector (for theme support)
7670
- */
7671
- groupTokens(tokens, options) {
7672
- const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
7673
- return {
7674
- [options.selector]: sortedTokens
7675
- };
7676
- }
7677
7699
  buildCssEntries(token, tokens, referenceTokens, preserveReferences) {
7678
7700
  if (preserveReferences) {
7679
7701
  const refName = getPureAliasReferenceName(token.originalValue);
@@ -7815,7 +7837,7 @@ var CssRenderer = class _CssRenderer {
7815
7837
  leaves.push({ path: path7, value });
7816
7838
  return;
7817
7839
  }
7818
- if (isColorObject(value) || isDimensionObject(value) || this.isDurationObject(value)) {
7840
+ if (isColorObject(value) || isDimensionObject(value) || isDurationObject(value)) {
7819
7841
  leaves.push({ path: path7, value });
7820
7842
  return;
7821
7843
  }
@@ -7858,8 +7880,8 @@ var CssRenderer = class _CssRenderer {
7858
7880
  if (isDimensionObject(value)) {
7859
7881
  return dimensionObjectToString(value);
7860
7882
  }
7861
- if (this.isDurationObject(value)) {
7862
- return this.formatDurationValue(value);
7883
+ if (isDurationObject(value)) {
7884
+ return durationObjectToString(value);
7863
7885
  }
7864
7886
  if (typeof value === "string") {
7865
7887
  return value;
@@ -7922,15 +7944,6 @@ var CssRenderer = class _CssRenderer {
7922
7944
  isPrimitiveValue(value) {
7923
7945
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
7924
7946
  }
7925
- isDurationObject(value) {
7926
- return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
7927
- }
7928
- formatDurationValue(value) {
7929
- if (typeof value === "string") {
7930
- return value;
7931
- }
7932
- return `${value.value}${value.unit}`;
7933
- }
7934
7947
  /**
7935
7948
  * Format token value for CSS
7936
7949
  * Handles DTCG 2025.10 object formats for colors and dimensions
@@ -7951,8 +7964,8 @@ var CssRenderer = class _CssRenderer {
7951
7964
  return typeof value === "string" ? value : dimensionObjectToString(value);
7952
7965
  }
7953
7966
  if (type === "duration") {
7954
- if (this.isDurationObject(value)) {
7955
- return this.formatDurationValue(value);
7967
+ if (isDurationObject(value)) {
7968
+ return durationObjectToString(value);
7956
7969
  }
7957
7970
  if (typeof value === "string") {
7958
7971
  return value;
@@ -8042,16 +8055,16 @@ var CssRenderer = class _CssRenderer {
8042
8055
  */
8043
8056
  formatTransition(value) {
8044
8057
  const parts = [];
8045
- if (this.isDurationObject(value.duration)) {
8046
- parts.push(this.formatDurationValue(value.duration));
8058
+ if (isDurationObject(value.duration)) {
8059
+ parts.push(durationObjectToString(value.duration));
8047
8060
  } else if (value.duration != null) {
8048
8061
  parts.push(String(value.duration));
8049
8062
  }
8050
8063
  if (Array.isArray(value.timingFunction) && value.timingFunction.length === 4) {
8051
8064
  parts.push(`cubic-bezier(${value.timingFunction.join(", ")})`);
8052
8065
  }
8053
- if (this.isDurationObject(value.delay)) {
8054
- parts.push(this.formatDurationValue(value.delay));
8066
+ if (isDurationObject(value.delay)) {
8067
+ parts.push(durationObjectToString(value.delay));
8055
8068
  } else if (value.delay != null) {
8056
8069
  parts.push(String(value.delay));
8057
8070
  }
@@ -8061,7 +8074,7 @@ var CssRenderer = class _CssRenderer {
8061
8074
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8062
8075
  tokens,
8063
8076
  modifierInputs,
8064
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
8077
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8065
8078
  }));
8066
8079
  return await bundleAsCss(bundleData, context.resolver, options, async (tokens, resolved) => {
8067
8080
  return await this.formatTokens(tokens, {
@@ -8071,12 +8084,12 @@ var CssRenderer = class _CssRenderer {
8071
8084
  });
8072
8085
  }
8073
8086
  async formatStandalone(context, options) {
8074
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8075
- if (!context.output.file && requiresFile) {
8076
- throw new ConfigurationError(
8077
- `Output "${context.output.name}": file is required for standalone CSS output`
8078
- );
8079
- }
8087
+ assertFileRequired(
8088
+ context.buildPath,
8089
+ context.output.file,
8090
+ context.output.name,
8091
+ "standalone CSS"
8092
+ );
8080
8093
  const files = {};
8081
8094
  for (const { tokens, modifierInputs } of context.permutations) {
8082
8095
  const { fileName, content } = await this.buildStandaloneFile(
@@ -8090,7 +8103,7 @@ var CssRenderer = class _CssRenderer {
8090
8103
  return { kind: "outputTree", files };
8091
8104
  }
8092
8105
  async buildStandaloneFile(tokens, modifierInputs, context, options) {
8093
- const isBase = this.isBasePermutation(modifierInputs, context.meta.defaults);
8106
+ const isBase = isBasePermutation(modifierInputs, context.meta.defaults);
8094
8107
  const { modifierName, modifierContext } = this.resolveModifierContext(
8095
8108
  modifierInputs,
8096
8109
  context,
@@ -8127,12 +8140,7 @@ var CssRenderer = class _CssRenderer {
8127
8140
  return { fileName, content };
8128
8141
  }
8129
8142
  async formatModifier(context, options) {
8130
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8131
- if (!context.output.file && requiresFile) {
8132
- throw new ConfigurationError(
8133
- `Output "${context.output.name}": file is required for modifier CSS output`
8134
- );
8135
- }
8143
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "modifier CSS");
8136
8144
  if (!context.resolver.modifiers) {
8137
8145
  throw new ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
8138
8146
  }
@@ -8158,7 +8166,7 @@ var CssRenderer = class _CssRenderer {
8158
8166
  }
8159
8167
  async buildModifierBaseFile(context, options) {
8160
8168
  const basePermutation = context.permutations.find(
8161
- ({ modifierInputs }) => this.isBasePermutation(modifierInputs, context.meta.defaults)
8169
+ ({ modifierInputs }) => isBasePermutation(modifierInputs, context.meta.defaults)
8162
8170
  );
8163
8171
  if (!basePermutation) {
8164
8172
  return void 0;
@@ -8171,25 +8179,40 @@ var CssRenderer = class _CssRenderer {
8171
8179
  if (setBlocks.length === 0) {
8172
8180
  return void 0;
8173
8181
  }
8182
+ const { selector, mediaQuery } = this.resolveBaseModifierContext(context, options);
8183
+ const content = await this.formatSetBlocksCss(
8184
+ setBlocks,
8185
+ basePermutation.tokens,
8186
+ selector,
8187
+ mediaQuery,
8188
+ options
8189
+ );
8190
+ const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8191
+ return { fileName, content };
8192
+ }
8193
+ resolveBaseModifierContext(context, options) {
8174
8194
  const modifiers = context.resolver.modifiers;
8175
8195
  const firstModifierName = Object.keys(modifiers)[0] ?? "";
8176
8196
  const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
8177
8197
  const baseModifierInputs = { ...context.meta.defaults };
8178
- const selector = resolveSelector(
8179
- options.selector,
8180
- firstModifierName,
8181
- firstModifierContext,
8182
- true,
8183
- baseModifierInputs
8184
- );
8185
- const mediaQuery = resolveMediaQuery(
8186
- options.mediaQuery,
8187
- firstModifierName,
8188
- firstModifierContext,
8189
- true,
8190
- baseModifierInputs
8191
- );
8192
- const referenceTokens = basePermutation.tokens;
8198
+ return {
8199
+ selector: resolveSelector(
8200
+ options.selector,
8201
+ firstModifierName,
8202
+ firstModifierContext,
8203
+ true,
8204
+ baseModifierInputs
8205
+ ),
8206
+ mediaQuery: resolveMediaQuery(
8207
+ options.mediaQuery,
8208
+ firstModifierName,
8209
+ firstModifierContext,
8210
+ true,
8211
+ baseModifierInputs
8212
+ )
8213
+ };
8214
+ }
8215
+ async formatSetBlocksCss(setBlocks, referenceTokens, selector, mediaQuery, options) {
8193
8216
  const cssBlocks = [];
8194
8217
  for (const block of setBlocks) {
8195
8218
  const cleanTokens = stripInternalMetadata(block.tokens);
@@ -8205,9 +8228,7 @@ var CssRenderer = class _CssRenderer {
8205
8228
  cssBlocks.push(`${header}
8206
8229
  ${css2}`);
8207
8230
  }
8208
- const content = cssBlocks.join("\n");
8209
- const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8210
- return { fileName, content };
8231
+ return cssBlocks.join("\n");
8211
8232
  }
8212
8233
  collectTokensForModifierContext(modifierName, contextValue, permutations) {
8213
8234
  const expectedSource = `${modifierName}-${contextValue}`;
@@ -8284,13 +8305,6 @@ ${css2}`);
8284
8305
  }
8285
8306
  return { modifierName: "", modifierContext: "" };
8286
8307
  }
8287
- isBasePermutation(modifierInputs, defaults) {
8288
- const normalizedInputs = normalizeModifierInputs(modifierInputs);
8289
- const normalizedDefaults = normalizeModifierInputs(defaults);
8290
- return Object.entries(normalizedDefaults).every(
8291
- ([key, value]) => normalizedInputs[key] === value
8292
- );
8293
- }
8294
8308
  };
8295
8309
  function cssRenderer() {
8296
8310
  const rendererInstance = new CssRenderer();
@@ -8303,8 +8317,6 @@ function cssRenderer() {
8303
8317
  }
8304
8318
 
8305
8319
  // src/renderers/ios.ts
8306
- init_errors();
8307
- init_token_utils();
8308
8320
  init_utils();
8309
8321
  var toSRGB2 = converter("rgb");
8310
8322
  var toP32 = converter("p3");
@@ -8393,94 +8405,68 @@ var IosRenderer = class {
8393
8405
  return await this.formatStandalone(context, opts);
8394
8406
  }
8395
8407
  formatTokens(tokens, options) {
8396
- if (options.structure === "grouped") {
8397
- return this.formatAsGrouped(tokens, options);
8398
- }
8399
- return this.formatAsEnum(tokens, options);
8400
- }
8401
- formatAsEnum(tokens, options) {
8402
8408
  const access3 = options.accessLevel;
8403
- const groups = this.groupTokensByType(tokens);
8409
+ const groups = groupTokensByType(tokens, SWIFT_TYPE_GROUP_MAP);
8404
8410
  const imports = this.collectImports(tokens);
8405
- const i1 = this.indentStr(options.indent, 1);
8406
- const i2 = this.indentStr(options.indent, 2);
8407
8411
  const staticPrefix = this.staticLetPrefix(options);
8408
8412
  const frozen = this.frozenPrefix(options);
8409
8413
  const lines = [];
8410
- lines.push(this.buildFileHeader());
8414
+ lines.push(buildGeneratedFileHeader());
8411
8415
  lines.push("");
8412
8416
  for (const imp of imports) {
8413
8417
  lines.push(`import ${imp}`);
8414
8418
  }
8415
8419
  lines.push(...this.buildStructDefinitions(tokens, access3, options));
8420
+ this.pushTokenLayout(lines, groups, options, access3, staticPrefix, frozen);
8421
+ lines.push(...this.buildViewExtensions(tokens, access3, options));
8422
+ if (options.structure !== "grouped") {
8423
+ lines.push("");
8424
+ }
8425
+ return lines.join("\n");
8426
+ }
8427
+ pushTokenLayout(lines, groups, options, access3, staticPrefix, frozen) {
8428
+ const i1 = indentStr(options.indent, 1);
8429
+ const i2 = indentStr(options.indent, 2);
8430
+ if (options.structure === "grouped") {
8431
+ this.pushGroupedLayout(lines, groups, options, access3, i1, i2, staticPrefix, frozen);
8432
+ return;
8433
+ }
8416
8434
  lines.push("");
8417
8435
  lines.push(`${frozen}${access3} enum ${options.enumName} {`);
8418
8436
  for (const group of groups) {
8419
8437
  lines.push(`${i1}${frozen}${access3} enum ${group.name} {`);
8420
- for (const token of group.tokens) {
8421
- const swiftName = this.buildQualifiedSwiftName(token);
8422
- const swiftValue = this.formatSwiftValue(token, options);
8423
- const typeAnnotation = this.getTypeAnnotation(token);
8424
- const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8425
- const docComment = this.buildDocComment(token, i2);
8426
- if (docComment) {
8427
- lines.push(docComment);
8428
- }
8429
- lines.push(`${i2}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8430
- }
8438
+ this.pushTokenDeclarations(lines, group.tokens, options, access3, i2, staticPrefix);
8431
8439
  lines.push(`${i1}}`);
8432
8440
  lines.push("");
8433
8441
  }
8434
8442
  lines.push("}");
8435
- lines.push(...this.buildViewExtensions(tokens, access3, options));
8436
- lines.push("");
8437
- return lines.join("\n");
8438
8443
  }
8439
- formatAsGrouped(tokens, options) {
8440
- const access3 = options.accessLevel;
8444
+ pushGroupedLayout(lines, groups, options, access3, i1, i2, staticPrefix, frozen) {
8441
8445
  const namespace = options.extensionNamespace;
8442
- const groups = this.groupTokensByType(tokens);
8443
- const imports = this.collectImports(tokens);
8444
- const i1 = this.indentStr(options.indent, 1);
8445
- const i2 = this.indentStr(options.indent, 2);
8446
- const staticPrefix = this.staticLetPrefix(options);
8447
- const frozen = this.frozenPrefix(options);
8448
- const lines = [];
8449
- lines.push(this.buildFileHeader());
8450
- lines.push("");
8451
- for (const imp of imports) {
8452
- lines.push(`import ${imp}`);
8453
- }
8454
- lines.push(...this.buildStructDefinitions(tokens, access3, options));
8455
8446
  lines.push("");
8456
8447
  lines.push(`${frozen}${access3} enum ${namespace} {}`);
8457
8448
  lines.push("");
8458
8449
  for (const group of groups) {
8459
8450
  lines.push(`${access3} extension ${namespace} {`);
8460
8451
  lines.push(`${i1}${frozen}enum ${group.name} {`);
8461
- for (const token of group.tokens) {
8462
- const swiftName = this.buildQualifiedSwiftName(token);
8463
- const swiftValue = this.formatSwiftValue(token, options);
8464
- const typeAnnotation = this.getTypeAnnotation(token);
8465
- const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8466
- const docComment = this.buildDocComment(token, i2);
8467
- if (docComment) {
8468
- lines.push(docComment);
8469
- }
8470
- lines.push(`${i2}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8471
- }
8452
+ this.pushTokenDeclarations(lines, group.tokens, options, access3, i2, staticPrefix);
8472
8453
  lines.push(`${i1}}`);
8473
8454
  lines.push("}");
8474
8455
  lines.push("");
8475
8456
  }
8476
- lines.push(...this.buildViewExtensions(tokens, access3, options));
8477
- return lines.join("\n");
8478
8457
  }
8479
- buildFileHeader() {
8480
- return [
8481
- "// Generated by Dispersa - do not edit manually",
8482
- "// https://github.com/timges/dispersa"
8483
- ].join("\n");
8458
+ pushTokenDeclarations(lines, tokens, options, access3, indent, staticPrefix) {
8459
+ for (const token of tokens) {
8460
+ const swiftName = this.buildQualifiedSwiftName(token);
8461
+ const swiftValue = this.formatSwiftValue(token, options);
8462
+ const typeAnnotation = this.getTypeAnnotation(token);
8463
+ const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8464
+ const docComment = this.buildDocComment(token, indent);
8465
+ if (docComment) {
8466
+ lines.push(docComment);
8467
+ }
8468
+ lines.push(`${indent}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8469
+ }
8484
8470
  }
8485
8471
  collectImports(tokens) {
8486
8472
  const imports = /* @__PURE__ */ new Set();
@@ -8495,24 +8481,11 @@ var IosRenderer = class {
8495
8481
  /**
8496
8482
  * Builds a `///` doc comment from a token's `$description`, if present.
8497
8483
  */
8498
- buildDocComment(token, indent2) {
8484
+ buildDocComment(token, indent) {
8499
8485
  if (!token.$description) {
8500
8486
  return void 0;
8501
8487
  }
8502
- return `${indent2}/// ${token.$description}`;
8503
- }
8504
- groupTokensByType(tokens) {
8505
- const groupMap = /* @__PURE__ */ new Map();
8506
- for (const [, token] of getSortedTokenEntries(tokens)) {
8507
- const groupName = SWIFT_TYPE_GROUP_MAP[token.$type ?? ""] ?? "Other";
8508
- const existing = groupMap.get(groupName) ?? [];
8509
- existing.push(token);
8510
- groupMap.set(groupName, existing);
8511
- }
8512
- return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
8513
- name,
8514
- tokens: groupTokens
8515
- }));
8488
+ return `${indent}/// ${token.$description}`;
8516
8489
  }
8517
8490
  /**
8518
8491
  * Builds a qualified Swift name from a token's path, preserving parent
@@ -8525,43 +8498,40 @@ var IosRenderer = class {
8525
8498
  const path7 = token.path;
8526
8499
  const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
8527
8500
  const joined = withoutTypePrefix.join("_");
8528
- return this.toSwiftIdentifier(joined);
8501
+ return toSafeIdentifier(joined, SWIFT_KEYWORDS, false);
8529
8502
  }
8530
8503
  formatSwiftValue(token, options) {
8531
- const value = token.$value;
8532
- if (token.$type === "color") {
8533
- return this.formatColorValue(value, options);
8534
- }
8535
- if (token.$type === "dimension") {
8536
- return this.formatDimensionValue(value);
8537
- }
8538
- if (token.$type === "fontFamily") {
8539
- return this.formatFontFamilyValue(value);
8540
- }
8541
- if (token.$type === "fontWeight") {
8542
- return this.formatFontWeightValue(value);
8543
- }
8544
- if (token.$type === "duration") {
8545
- return this.formatDurationValue(value);
8546
- }
8547
- if (token.$type === "shadow") {
8548
- return this.formatShadowValue(value, options);
8549
- }
8550
- if (token.$type === "typography") {
8551
- return this.formatTypographyValue(value);
8552
- }
8553
- if (token.$type === "border") {
8554
- return this.formatBorderValue(value, options);
8555
- }
8556
- if (token.$type === "gradient") {
8557
- return this.formatGradientValue(value, options);
8558
- }
8559
- if (token.$type === "number") {
8560
- return String(value);
8561
- }
8562
- if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
8563
- return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
8504
+ const { $type, $value: value } = token;
8505
+ switch ($type) {
8506
+ case "color":
8507
+ return this.formatColorValue(value, options);
8508
+ case "dimension":
8509
+ return this.formatDimensionValue(value);
8510
+ case "fontFamily":
8511
+ return this.formatFontFamilyValue(value);
8512
+ case "fontWeight":
8513
+ return this.formatFontWeightValue(value);
8514
+ case "duration":
8515
+ return this.formatDurationValue(value);
8516
+ case "shadow":
8517
+ return this.formatShadowValue(value, options);
8518
+ case "typography":
8519
+ return this.formatTypographyValue(value);
8520
+ case "border":
8521
+ return this.formatBorderValue(value, options);
8522
+ case "gradient":
8523
+ return this.formatGradientValue(value, options);
8524
+ case "number":
8525
+ return String(value);
8526
+ case "cubicBezier":
8527
+ if (Array.isArray(value) && value.length === 4) {
8528
+ return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
8529
+ }
8530
+ break;
8564
8531
  }
8532
+ return this.formatSwiftPrimitive(value);
8533
+ }
8534
+ formatSwiftPrimitive(value) {
8565
8535
  if (typeof value === "string") {
8566
8536
  return `"${this.escapeSwiftString(value)}"`;
8567
8537
  }
@@ -8594,9 +8564,7 @@ var IosRenderer = class {
8594
8564
  }
8595
8565
  formatDimensionValue(value) {
8596
8566
  if (isDimensionObject(value)) {
8597
- const dim = value;
8598
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8599
- return String(ptValue);
8567
+ return this.dimensionToPoints(value);
8600
8568
  }
8601
8569
  return String(value);
8602
8570
  }
@@ -8663,7 +8631,7 @@ var IosRenderer = class {
8663
8631
  return map[name.toLowerCase()];
8664
8632
  }
8665
8633
  formatDurationValue(value) {
8666
- if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
8634
+ if (isDurationObject(value)) {
8667
8635
  const dur = value;
8668
8636
  const seconds = dur.unit === "ms" ? dur.value / 1e3 : dur.value;
8669
8637
  return String(seconds);
@@ -8712,9 +8680,7 @@ var IosRenderer = class {
8712
8680
  if (!isDimensionObject(typo.letterSpacing)) {
8713
8681
  return "0";
8714
8682
  }
8715
- const dim = typo.letterSpacing;
8716
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8717
- return String(ptValue);
8683
+ return this.dimensionToPoints(typo.letterSpacing);
8718
8684
  }
8719
8685
  extractLineSpacing(typo) {
8720
8686
  if (typo.lineHeight == null || typeof typo.lineHeight !== "number") {
@@ -8723,18 +8689,19 @@ var IosRenderer = class {
8723
8689
  if (!isDimensionObject(typo.fontSize)) {
8724
8690
  return "0";
8725
8691
  }
8726
- const dim = typo.fontSize;
8727
- const basePt = dim.unit === "rem" ? dim.value * 16 : dim.value;
8692
+ const basePt = this.dimensionToNumericPoints(typo.fontSize);
8728
8693
  const lineHeightPt = Math.round(basePt * typo.lineHeight * 100) / 100;
8729
8694
  return String(lineHeightPt - basePt);
8730
8695
  }
8696
+ dimensionToNumericPoints(dim) {
8697
+ return dim.unit === "rem" ? dim.value * 16 : dim.value;
8698
+ }
8731
8699
  dimensionToPoints(dim) {
8732
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8733
- return String(ptValue);
8700
+ return String(this.dimensionToNumericPoints(dim));
8734
8701
  }
8735
8702
  /** Formats a dimension as a CGFloat literal (appends `.0` for integers). */
8736
8703
  dimensionToCGFloat(dim) {
8737
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8704
+ const ptValue = this.dimensionToNumericPoints(dim);
8738
8705
  return Number.isInteger(ptValue) ? `${ptValue}.0` : String(ptValue);
8739
8706
  }
8740
8707
  getTypeAnnotation(token) {
@@ -8753,21 +8720,12 @@ var IosRenderer = class {
8753
8720
  return void 0;
8754
8721
  }
8755
8722
  }
8756
- toSwiftIdentifier(name) {
8757
- const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
8758
- const identifier = camel.charAt(0).toLowerCase() + camel.slice(1);
8759
- const safe = /^\d/.test(identifier) ? `_${identifier}` : identifier;
8760
- return SWIFT_KEYWORDS.has(safe) ? `\`${safe}\`` : safe;
8761
- }
8762
8723
  escapeSwiftString(str) {
8763
8724
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
8764
8725
  }
8765
8726
  roundComponent(value) {
8766
8727
  return Math.round(value * 1e4) / 1e4;
8767
8728
  }
8768
- indentStr(width, level) {
8769
- return " ".repeat(width * level);
8770
- }
8771
8729
  /**
8772
8730
  * Returns the prefix for `static let` declarations.
8773
8731
  * Swift 6 requires `nonisolated(unsafe)` on global stored properties.
@@ -8783,34 +8741,25 @@ var IosRenderer = class {
8783
8741
  structConformances(options) {
8784
8742
  return options.swiftVersion === "6.0" ? ": Sendable" : "";
8785
8743
  }
8786
- hasShadowTokens(tokens) {
8787
- return Object.values(tokens).some((t) => t.$type === "shadow");
8788
- }
8789
- hasTypographyTokens(tokens) {
8790
- return Object.values(tokens).some((t) => t.$type === "typography");
8791
- }
8792
- hasBorderTokens(tokens) {
8793
- return Object.values(tokens).some((t) => t.$type === "border");
8794
- }
8795
8744
  /** Emits all struct definitions needed by the token set. */
8796
8745
  buildStructDefinitions(tokens, access3, options) {
8797
8746
  const lines = [];
8798
- if (this.hasShadowTokens(tokens)) {
8747
+ if (Object.values(tokens).some(isShadowToken)) {
8799
8748
  lines.push("");
8800
8749
  lines.push(...this.buildShadowStyleStruct(access3, options));
8801
8750
  }
8802
- if (this.hasTypographyTokens(tokens)) {
8751
+ if (Object.values(tokens).some(isTypographyToken)) {
8803
8752
  lines.push("");
8804
8753
  lines.push(...this.buildTypographyStyleStruct(access3, options));
8805
8754
  }
8806
- if (this.hasBorderTokens(tokens)) {
8755
+ if (Object.values(tokens).some(isBorderToken)) {
8807
8756
  lines.push("");
8808
8757
  lines.push(...this.buildBorderStyleStruct(access3, options));
8809
8758
  }
8810
8759
  return lines;
8811
8760
  }
8812
8761
  buildShadowStyleStruct(access3, options) {
8813
- const i1 = this.indentStr(options.indent, 1);
8762
+ const i1 = indentStr(options.indent, 1);
8814
8763
  const conformances = this.structConformances(options);
8815
8764
  const frozen = this.frozenPrefix(options);
8816
8765
  return [
@@ -8824,7 +8773,7 @@ var IosRenderer = class {
8824
8773
  ];
8825
8774
  }
8826
8775
  buildTypographyStyleStruct(access3, options) {
8827
- const i1 = this.indentStr(options.indent, 1);
8776
+ const i1 = indentStr(options.indent, 1);
8828
8777
  const conformances = this.structConformances(options);
8829
8778
  const frozen = this.frozenPrefix(options);
8830
8779
  return [
@@ -8836,7 +8785,7 @@ var IosRenderer = class {
8836
8785
  ];
8837
8786
  }
8838
8787
  buildBorderStyleStruct(access3, options) {
8839
- const i1 = this.indentStr(options.indent, 1);
8788
+ const i1 = indentStr(options.indent, 1);
8840
8789
  const conformances = this.structConformances(options);
8841
8790
  const frozen = this.frozenPrefix(options);
8842
8791
  return [
@@ -8849,9 +8798,9 @@ var IosRenderer = class {
8849
8798
  /** Emits convenience View extensions for shadow and typography application. */
8850
8799
  buildViewExtensions(tokens, access3, options) {
8851
8800
  const lines = [];
8852
- const i1 = this.indentStr(options.indent, 1);
8853
- const i2 = this.indentStr(options.indent, 2);
8854
- if (this.hasShadowTokens(tokens)) {
8801
+ const i1 = indentStr(options.indent, 1);
8802
+ const i2 = indentStr(options.indent, 2);
8803
+ if (Object.values(tokens).some(isShadowToken)) {
8855
8804
  lines.push("");
8856
8805
  lines.push(`${access3} extension View {`);
8857
8806
  lines.push(`${i1}func shadowStyle(_ style: ShadowStyle) -> some View {`);
@@ -8861,7 +8810,7 @@ var IosRenderer = class {
8861
8810
  lines.push(`${i1}}`);
8862
8811
  lines.push("}");
8863
8812
  }
8864
- if (this.hasTypographyTokens(tokens)) {
8813
+ if (Object.values(tokens).some(isTypographyToken)) {
8865
8814
  lines.push("");
8866
8815
  lines.push(`${access3} extension View {`);
8867
8816
  lines.push(`${i1}func typographyStyle(_ style: TypographyStyle) -> some View {`);
@@ -8893,12 +8842,12 @@ var IosRenderer = class {
8893
8842
  return `Gradient(stops: [${stops.join(", ")}])`;
8894
8843
  }
8895
8844
  async formatStandalone(context, options) {
8896
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8897
- if (!context.output.file && requiresFile) {
8898
- throw new ConfigurationError(
8899
- `Output "${context.output.name}": file is required for standalone iOS output`
8900
- );
8901
- }
8845
+ assertFileRequired(
8846
+ context.buildPath,
8847
+ context.output.file,
8848
+ context.output.name,
8849
+ "standalone iOS"
8850
+ );
8902
8851
  const files = {};
8903
8852
  for (const { tokens, modifierInputs } of context.permutations) {
8904
8853
  const processedTokens = stripInternalMetadata(tokens);
@@ -8927,7 +8876,6 @@ function iosRenderer() {
8927
8876
 
8928
8877
  // src/renderers/js-module.ts
8929
8878
  init_utils();
8930
- init_errors();
8931
8879
  init_token_utils();
8932
8880
  var JsModuleRenderer = class {
8933
8881
  async format(context, options) {
@@ -8943,18 +8891,13 @@ var JsModuleRenderer = class {
8943
8891
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8944
8892
  tokens: stripInternalMetadata(tokens),
8945
8893
  modifierInputs,
8946
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
8894
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8947
8895
  }));
8948
8896
  return await bundleAsJsModule2(bundleData, context.resolver, opts, async (tokens) => {
8949
8897
  return await this.formatTokens(tokens, opts);
8950
8898
  });
8951
8899
  }
8952
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8953
- if (!context.output.file && requiresFile) {
8954
- throw new ConfigurationError(
8955
- `Output "${context.output.name}": file is required for JS module output`
8956
- );
8957
- }
8900
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JS module");
8958
8901
  const files = {};
8959
8902
  for (const { tokens, modifierInputs } of context.permutations) {
8960
8903
  const cleanTokens = stripInternalMetadata(tokens);
@@ -9008,42 +8951,18 @@ var JsModuleRenderer = class {
9008
8951
  lines.push(`export default ${varName}`);
9009
8952
  return lines;
9010
8953
  }
9011
- /**
9012
- * Convert tokens to plain object with flat or nested structure
9013
- */
9014
8954
  tokensToPlainObject(tokens, structure) {
8955
+ if (structure === "nested") {
8956
+ return buildNestedTokenObject(tokens, (token) => token.$value);
8957
+ }
9015
8958
  const result = {};
9016
- if (structure === "flat") {
9017
- for (const [name, token] of getSortedTokenEntries(tokens)) {
9018
- result[name] = token.$value;
9019
- }
9020
- } else {
9021
- for (const [, token] of getSortedTokenEntries(tokens)) {
9022
- const parts = token.path;
9023
- let current = result;
9024
- for (let i = 0; i < parts.length - 1; i++) {
9025
- const part = parts[i];
9026
- if (part == null) {
9027
- continue;
9028
- }
9029
- if (!(part in current)) {
9030
- current[part] = {};
9031
- }
9032
- current = current[part];
9033
- }
9034
- const lastPart = parts[parts.length - 1];
9035
- if (lastPart != null) {
9036
- current[lastPart] = token.$value;
9037
- }
9038
- }
8959
+ for (const [name, token] of getSortedTokenEntries(tokens)) {
8960
+ result[name] = token.$value;
9039
8961
  }
9040
8962
  return result;
9041
8963
  }
9042
- /**
9043
- * Add object properties to lines
9044
- */
9045
- addObjectProperties(lines, obj, indent2) {
9046
- const indentStr = " ".repeat(indent2);
8964
+ addObjectProperties(lines, obj, indent) {
8965
+ const indentStr2 = " ".repeat(indent);
9047
8966
  const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
9048
8967
  for (let i = 0; i < entries.length; i++) {
9049
8968
  const entry = entries[i];
@@ -9052,14 +8971,16 @@ var JsModuleRenderer = class {
9052
8971
  }
9053
8972
  const [key, value] = entry;
9054
8973
  const isLast = i === entries.length - 1;
9055
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
9056
- lines.push(`${indentStr}${this.quoteKey(key)}: {`);
9057
- this.addObjectProperties(lines, value, indent2 + 1);
9058
- lines.push(`${indentStr}}${isLast ? "" : ","}`);
9059
- } else {
9060
- const valueStr = JSON.stringify(value);
9061
- lines.push(`${indentStr}${this.quoteKey(key)}: ${valueStr}${isLast ? "" : ","}`);
8974
+ const isNestedObject = typeof value === "object" && value !== null && !Array.isArray(value);
8975
+ if (!isNestedObject) {
8976
+ lines.push(
8977
+ `${indentStr2}${this.quoteKey(key)}: ${JSON.stringify(value)}${isLast ? "" : ","}`
8978
+ );
8979
+ continue;
9062
8980
  }
8981
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
8982
+ this.addObjectProperties(lines, value, indent + 1);
8983
+ lines.push(`${indentStr2}}${isLast ? "" : ","}`);
9063
8984
  }
9064
8985
  }
9065
8986
  /**
@@ -9071,9 +8992,6 @@ var JsModuleRenderer = class {
9071
8992
  }
9072
8993
  return `"${key}"`;
9073
8994
  }
9074
- isBasePermutation(modifierInputs, defaults) {
9075
- return Object.entries(modifierInputs).every(([key, value]) => value === defaults[key]);
9076
- }
9077
8995
  };
9078
8996
  function jsRenderer() {
9079
8997
  const rendererInstance = new JsModuleRenderer();
@@ -9087,7 +9005,6 @@ function jsRenderer() {
9087
9005
 
9088
9006
  // src/renderers/json.ts
9089
9007
  init_utils();
9090
- init_errors();
9091
9008
  init_token_utils();
9092
9009
  var JsonRenderer = class {
9093
9010
  async format(context, options) {
@@ -9102,18 +9019,13 @@ var JsonRenderer = class {
9102
9019
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9103
9020
  tokens: stripInternalMetadata(tokens),
9104
9021
  modifierInputs,
9105
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9022
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9106
9023
  }));
9107
9024
  return await bundleAsJson2(bundleData, context.resolver, async (tokens) => {
9108
9025
  return await this.formatTokens(tokens, opts);
9109
9026
  });
9110
9027
  }
9111
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
9112
- if (!context.output.file && requiresFile) {
9113
- throw new ConfigurationError(
9114
- `Output "${context.output.name}": file is required for JSON output`
9115
- );
9116
- }
9028
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JSON");
9117
9029
  const files = {};
9118
9030
  for (const { tokens, modifierInputs } of context.permutations) {
9119
9031
  const processedTokens = stripInternalMetadata(tokens);
@@ -9173,55 +9085,11 @@ var JsonRenderer = class {
9173
9085
  }
9174
9086
  return result;
9175
9087
  }
9176
- /**
9177
- * Nest tokens by path (values only)
9178
- */
9179
9088
  nestValues(tokens) {
9180
- const result = {};
9181
- for (const [, token] of getSortedTokenEntries(tokens)) {
9182
- const parts = token.path;
9183
- let current = result;
9184
- for (let i = 0; i < parts.length - 1; i++) {
9185
- const part = parts[i];
9186
- if (part === null || part === void 0) {
9187
- continue;
9188
- }
9189
- if (!(part in current)) {
9190
- current[part] = {};
9191
- }
9192
- current = current[part];
9193
- }
9194
- const lastPart = parts[parts.length - 1];
9195
- if (lastPart !== null && lastPart !== void 0) {
9196
- current[lastPart] = token.$value;
9197
- }
9198
- }
9199
- return result;
9089
+ return buildNestedTokenObject(tokens, (token) => token.$value);
9200
9090
  }
9201
- /**
9202
- * Nest tokens by path (with metadata)
9203
- */
9204
9091
  nestTokens(tokens) {
9205
- const result = {};
9206
- for (const [, token] of getSortedTokenEntries(tokens)) {
9207
- const parts = token.path;
9208
- let current = result;
9209
- for (let i = 0; i < parts.length - 1; i++) {
9210
- const part = parts[i];
9211
- if (part === null || part === void 0) {
9212
- continue;
9213
- }
9214
- if (!(part in current)) {
9215
- current[part] = {};
9216
- }
9217
- current = current[part];
9218
- }
9219
- const lastPart = parts[parts.length - 1];
9220
- if (lastPart !== null && lastPart !== void 0) {
9221
- current[lastPart] = this.serializeToken(token);
9222
- }
9223
- }
9224
- return result;
9092
+ return buildNestedTokenObject(tokens, (token) => this.serializeToken(token));
9225
9093
  }
9226
9094
  serializeToken(token) {
9227
9095
  return {
@@ -9232,9 +9100,6 @@ var JsonRenderer = class {
9232
9100
  ...token.$extensions != null && { $extensions: token.$extensions }
9233
9101
  };
9234
9102
  }
9235
- isBasePermutation(modifierInputs, defaults) {
9236
- return Object.entries(modifierInputs).every(([key, value]) => value === defaults[key]);
9237
- }
9238
9103
  };
9239
9104
  function jsonRenderer() {
9240
9105
  const rendererInstance = new JsonRenderer();
@@ -9247,7 +9112,6 @@ function jsonRenderer() {
9247
9112
  }
9248
9113
 
9249
9114
  // src/renderers/tailwind.ts
9250
- init_errors();
9251
9115
  init_token_utils();
9252
9116
 
9253
9117
  // src/renderers/bundlers/tailwind.ts
@@ -9276,6 +9140,13 @@ async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOv
9276
9140
  }
9277
9141
  return cssBlocks.join("\n");
9278
9142
  }
9143
+ function resolveModifierSelectorAndMedia(options, modifier, context, modifierInputs) {
9144
+ const normalized = normalizeModifierInputs(modifierInputs);
9145
+ return {
9146
+ selector: resolveSelector(options.selector, modifier, context, false, normalized),
9147
+ mediaQuery: resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized)
9148
+ };
9149
+ }
9279
9150
  async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
9280
9151
  const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
9281
9152
  if (differenceCount > 1) {
@@ -9288,19 +9159,11 @@ async function formatModifierOverride({ tokens, modifierInputs }, baseItem, opti
9288
9159
  const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
9289
9160
  const [modifier, context] = parseModifierSource(expectedSource);
9290
9161
  const cleanTokens = stripInternalMetadata(tokensToInclude);
9291
- const selector = resolveSelector(
9292
- options.selector,
9293
- modifier,
9294
- context,
9295
- false,
9296
- normalizeModifierInputs(modifierInputs)
9297
- );
9298
- const mediaQuery = resolveMediaQuery(
9299
- options.mediaQuery,
9162
+ const { selector, mediaQuery } = resolveModifierSelectorAndMedia(
9163
+ options,
9300
9164
  modifier,
9301
9165
  context,
9302
- false,
9303
- normalizeModifierInputs(modifierInputs)
9166
+ modifierInputs
9304
9167
  );
9305
9168
  const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
9306
9169
  return `/* Modifier: ${modifier}=${context} */
@@ -9389,7 +9252,7 @@ var TailwindRenderer = class {
9389
9252
  */
9390
9253
  async formatTokens(tokens, options) {
9391
9254
  const lines = [];
9392
- const indent2 = options.minify ? "" : " ";
9255
+ const indent = options.minify ? "" : " ";
9393
9256
  const newline = options.minify ? "" : "\n";
9394
9257
  const space = options.minify ? "" : " ";
9395
9258
  if (options.includeImport) {
@@ -9411,7 +9274,7 @@ var TailwindRenderer = class {
9411
9274
  for (const [, token] of getSortedTokenEntries(tokens)) {
9412
9275
  const varName = this.buildVariableName(token);
9413
9276
  const varValue = this.formatValue(token);
9414
- lines.push(`${indent2}--${varName}:${space}${varValue};${newline}`);
9277
+ lines.push(`${indent}--${varName}:${space}${varValue};${newline}`);
9415
9278
  }
9416
9279
  lines.push(`}${newline}`);
9417
9280
  const cssString = lines.join("");
@@ -9422,15 +9285,15 @@ var TailwindRenderer = class {
9422
9285
  * Used for modifier overrides (e.g., dark mode) appended after the @theme block.
9423
9286
  */
9424
9287
  async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
9425
- const indent2 = minify ? "" : " ";
9288
+ const indent = minify ? "" : " ";
9426
9289
  const newline = minify ? "" : "\n";
9427
9290
  const space = minify ? "" : " ";
9428
9291
  const hasMediaQuery = mediaQuery !== "";
9429
- const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
9292
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
9430
9293
  const lines = [];
9431
9294
  if (hasMediaQuery) {
9432
9295
  lines.push(`@media ${mediaQuery}${space}{${newline}`);
9433
- lines.push(`${indent2}${selector}${space}{${newline}`);
9296
+ lines.push(`${indent}${selector}${space}{${newline}`);
9434
9297
  } else {
9435
9298
  lines.push(`${selector}${space}{${newline}`);
9436
9299
  }
@@ -9440,7 +9303,7 @@ var TailwindRenderer = class {
9440
9303
  lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
9441
9304
  }
9442
9305
  if (hasMediaQuery) {
9443
- lines.push(`${indent2}}${newline}`);
9306
+ lines.push(`${indent}}${newline}`);
9444
9307
  lines.push(`}${newline}`);
9445
9308
  } else {
9446
9309
  lines.push(`}${newline}`);
@@ -9467,8 +9330,8 @@ var TailwindRenderer = class {
9467
9330
  if (token.$type === "dimension" && isDimensionObject(value)) {
9468
9331
  return dimensionObjectToString(value);
9469
9332
  }
9470
- if (token.$type === "duration" && this.isDurationObject(value)) {
9471
- return `${value.value}${value.unit}`;
9333
+ if (token.$type === "duration" && isDurationObject(value)) {
9334
+ return durationObjectToString(value);
9472
9335
  }
9473
9336
  if (token.$type === "fontFamily") {
9474
9337
  if (Array.isArray(value)) {
@@ -9523,9 +9386,6 @@ var TailwindRenderer = class {
9523
9386
  }
9524
9387
  return parts.join(" ");
9525
9388
  }
9526
- isDurationObject(value) {
9527
- return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
9528
- }
9529
9389
  async formatWithPrettier(css2) {
9530
9390
  try {
9531
9391
  return await prettier.format(css2, {
@@ -9542,7 +9402,7 @@ var TailwindRenderer = class {
9542
9402
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9543
9403
  tokens,
9544
9404
  modifierInputs,
9545
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9405
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9546
9406
  }));
9547
9407
  return await bundleAsTailwind(
9548
9408
  bundleData,
@@ -9552,12 +9412,12 @@ var TailwindRenderer = class {
9552
9412
  );
9553
9413
  }
9554
9414
  async formatStandalone(context, options) {
9555
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
9556
- if (!context.output.file && requiresFile) {
9557
- throw new ConfigurationError(
9558
- `Output "${context.output.name}": file is required for standalone Tailwind output`
9559
- );
9560
- }
9415
+ assertFileRequired(
9416
+ context.buildPath,
9417
+ context.output.file,
9418
+ context.output.name,
9419
+ "standalone Tailwind"
9420
+ );
9561
9421
  const files = {};
9562
9422
  for (const { tokens, modifierInputs } of context.permutations) {
9563
9423
  const processedTokens = stripInternalMetadata(tokens);
@@ -9573,11 +9433,6 @@ var TailwindRenderer = class {
9573
9433
  }
9574
9434
  return outputTree(files);
9575
9435
  }
9576
- isBasePermutation(modifierInputs, defaults) {
9577
- return Object.entries(defaults).every(
9578
- ([key, value]) => modifierInputs[key]?.toLowerCase() === value.toLowerCase()
9579
- );
9580
- }
9581
9436
  };
9582
9437
  function tailwindRenderer() {
9583
9438
  const rendererInstance = new TailwindRenderer();
@@ -9710,11 +9565,6 @@ function defineRenderer(renderer) {
9710
9565
 
9711
9566
  // src/index.ts
9712
9567
  init_errors();
9713
- /**
9714
- * @license
9715
- * Copyright (c) 2025 Dispersa Contributors
9716
- * SPDX-License-Identifier: MIT
9717
- */
9718
9568
  /**
9719
9569
  * @license MIT
9720
9570
  * Copyright (c) 2025-present Dispersa Contributors
@@ -9722,6 +9572,11 @@ init_errors();
9722
9572
  * This source code is licensed under the MIT license found in the
9723
9573
  * LICENSE file in the root directory of this source tree.
9724
9574
  */
9575
+ /**
9576
+ * @license
9577
+ * Copyright (c) 2025 Dispersa Contributors
9578
+ * SPDX-License-Identifier: MIT
9579
+ */
9725
9580
 
9726
9581
  export { BasePermutationError, CircularReferenceError, ConfigurationError, Dispersa, DispersaError, FileOperationError, ModifierError, TokenReferenceError, ValidationError, android, css, defineRenderer, ios, isBorderToken, isColorToken, isDimensionToken, isDurationToken, isGradientToken, isOutputTree, isShadowToken, isTransitionToken, isTypographyToken, js, json, outputTree, tailwind };
9727
9582
  //# sourceMappingURL=index.js.map