dispersa 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6,6 +6,7 @@ var fs = require('fs');
6
6
  var promises = require('fs/promises');
7
7
  var path = require('path');
8
8
  var jsonPtr = require('json-ptr');
9
+ var changeCase = require('change-case');
9
10
  var culori = require('culori');
10
11
  var prettier = require('prettier');
11
12
 
@@ -44,47 +45,6 @@ var __export = (target, all) => {
44
45
  __defProp(target, name, { get: all[name], enumerable: true });
45
46
  };
46
47
 
47
- // src/shared/utils/token-utils.ts
48
- function formatDeprecationMessage(token, description = "", format = "bracket") {
49
- if (token.$deprecated == null || token.$deprecated === false) {
50
- return description;
51
- }
52
- const deprecationMsg = typeof token.$deprecated === "string" ? token.$deprecated : "";
53
- if (format === "comment") {
54
- const msg = deprecationMsg ? ` ${deprecationMsg}` : "";
55
- return `DEPRECATED${msg}`;
56
- } else {
57
- const msg = deprecationMsg ? `: ${deprecationMsg}` : "";
58
- const prefix = `[DEPRECATED${msg}]`;
59
- return description ? `${prefix} ${description}` : prefix;
60
- }
61
- }
62
- function stripInternalTokenMetadata(tokens) {
63
- const cleaned = {};
64
- for (const [name, token] of Object.entries(tokens)) {
65
- const { _isAlias: _alias, _sourceModifier: _source, _sourceSet, ...rest } = token;
66
- cleaned[name] = rest;
67
- }
68
- return cleaned;
69
- }
70
- function getSortedTokenEntries(tokens) {
71
- return Object.entries(tokens).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
72
- }
73
- function isTokenLike(value) {
74
- return typeof value === "object" && value !== null && ("$value" in value || "$ref" in value);
75
- }
76
- function getPureAliasReferenceName(value) {
77
- if (typeof value !== "string") {
78
- return void 0;
79
- }
80
- const match = /^\{([^}]+)\}$/.exec(value);
81
- return match?.[1]?.trim();
82
- }
83
- var init_token_utils = __esm({
84
- "src/shared/utils/token-utils.ts"() {
85
- }
86
- });
87
-
88
48
  // src/shared/errors/index.ts
89
49
  exports.DispersaError = void 0; exports.TokenReferenceError = void 0; exports.CircularReferenceError = void 0; exports.ValidationError = void 0; exports.FileOperationError = void 0; exports.ConfigurationError = void 0; exports.BasePermutationError = void 0; exports.ModifierError = void 0;
90
50
  var init_errors = __esm({
@@ -172,6 +132,70 @@ var init_errors = __esm({
172
132
  }
173
133
  });
174
134
 
135
+ // src/shared/utils/token-utils.ts
136
+ function formatDeprecationMessage(token, description = "", format = "bracket") {
137
+ if (token.$deprecated == null || token.$deprecated === false) {
138
+ return description;
139
+ }
140
+ const deprecationMsg = typeof token.$deprecated === "string" ? token.$deprecated : "";
141
+ if (format === "comment") {
142
+ const msg2 = deprecationMsg ? ` ${deprecationMsg}` : "";
143
+ return `DEPRECATED${msg2}`;
144
+ }
145
+ const msg = deprecationMsg ? `: ${deprecationMsg}` : "";
146
+ const prefix = `[DEPRECATED${msg}]`;
147
+ return description ? `${prefix} ${description}` : prefix;
148
+ }
149
+ function stripInternalTokenMetadata(tokens) {
150
+ const cleaned = {};
151
+ for (const [name, token] of Object.entries(tokens)) {
152
+ const { _isAlias: _alias, _sourceModifier: _source, _sourceSet, ...rest } = token;
153
+ cleaned[name] = rest;
154
+ }
155
+ return cleaned;
156
+ }
157
+ function getSortedTokenEntries(tokens) {
158
+ return Object.entries(tokens).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
159
+ }
160
+ function buildNestedTokenObject(tokens, extractValue) {
161
+ const result = {};
162
+ for (const [, token] of getSortedTokenEntries(tokens)) {
163
+ setNestedValue(result, token.path, extractValue(token));
164
+ }
165
+ return result;
166
+ }
167
+ function setNestedValue(root, path7, value) {
168
+ let current = root;
169
+ for (let i = 0; i < path7.length - 1; i++) {
170
+ const part = path7[i];
171
+ if (part == null) {
172
+ continue;
173
+ }
174
+ if (!(part in current)) {
175
+ current[part] = {};
176
+ }
177
+ current = current[part];
178
+ }
179
+ const lastPart = path7[path7.length - 1];
180
+ if (lastPart != null) {
181
+ current[lastPart] = value;
182
+ }
183
+ }
184
+ function isTokenLike(value) {
185
+ return typeof value === "object" && value !== null && ("$value" in value || "$ref" in value);
186
+ }
187
+ function getPureAliasReferenceName(value) {
188
+ if (typeof value !== "string") {
189
+ return void 0;
190
+ }
191
+ const match = /^\{([^}]+)\}$/.exec(value);
192
+ return match?.[1]?.trim();
193
+ }
194
+ var init_token_utils = __esm({
195
+ "src/shared/utils/token-utils.ts"() {
196
+ }
197
+ });
198
+
175
199
  // src/shared/utils/validation-handler.ts
176
200
  var ValidationHandler;
177
201
  var init_validation_handler = __esm({
@@ -2911,30 +2935,29 @@ var init_validator = __esm({
2911
2935
  validateTokenOrGroup(obj) {
2912
2936
  const hasValue = isTokenLike(obj);
2913
2937
  if (hasValue) {
2914
- const tokenErrors = this.validateToken(obj);
2915
- if (tokenErrors.length === 0) {
2938
+ const tokenErrors2 = this.validateToken(obj);
2939
+ if (tokenErrors2.length === 0) {
2916
2940
  return { type: "token", errors: [] };
2917
2941
  }
2918
2942
  return {
2919
2943
  type: "invalid",
2920
- errors: tokenErrors,
2944
+ errors: tokenErrors2,
2921
2945
  message: "Object has $value/$ref but failed token validation"
2922
2946
  };
2923
- } else {
2924
- const groupErrors = this.validateGroup(obj);
2925
- if (groupErrors.length === 0) {
2926
- return { type: "group", errors: [] };
2927
- }
2928
- const tokenErrors = this.validateToken(obj);
2929
- if (tokenErrors.length === 0) {
2930
- return { type: "token", errors: [] };
2931
- }
2932
- return {
2933
- type: "invalid",
2934
- errors: groupErrors.length < tokenErrors.length ? groupErrors : tokenErrors,
2935
- message: groupErrors.length < tokenErrors.length ? "Object appears to be a group but failed validation" : "Object appears to be a token but failed validation"
2936
- };
2937
2947
  }
2948
+ const groupErrors = this.validateGroup(obj);
2949
+ if (groupErrors.length === 0) {
2950
+ return { type: "group", errors: [] };
2951
+ }
2952
+ const tokenErrors = this.validateToken(obj);
2953
+ if (tokenErrors.length === 0) {
2954
+ return { type: "token", errors: [] };
2955
+ }
2956
+ return {
2957
+ type: "invalid",
2958
+ errors: groupErrors.length < tokenErrors.length ? groupErrors : tokenErrors,
2959
+ message: groupErrors.length < tokenErrors.length ? "Object appears to be a group but failed validation" : "Object appears to be a token but failed validation"
2960
+ };
2938
2961
  }
2939
2962
  /**
2940
2963
  * Format AJV errors into readable ValidationError objects
@@ -3129,35 +3152,34 @@ var init_resolver_parser = __esm({
3129
3152
  this.validateSourceReferences(set.sources, `set "${setName}"`, contextMsg);
3130
3153
  }
3131
3154
  }
3132
- if (resolver.modifiers) {
3133
- for (const [modifierName, modifier] of Object.entries(resolver.modifiers)) {
3134
- for (const [contextName, sources] of Object.entries(modifier.contexts)) {
3135
- this.validateSourceReferences(
3136
- sources,
3137
- `modifier "${modifierName}" context "${contextName}"`,
3138
- contextMsg
3139
- );
3140
- }
3155
+ if (!resolver.modifiers) {
3156
+ return;
3157
+ }
3158
+ for (const [modifierName, modifier] of Object.entries(resolver.modifiers)) {
3159
+ for (const [contextName, sources] of Object.entries(modifier.contexts)) {
3160
+ this.validateSourceReferences(
3161
+ sources,
3162
+ `modifier "${modifierName}" context "${contextName}"`,
3163
+ contextMsg
3164
+ );
3141
3165
  }
3142
3166
  }
3143
3167
  }
3144
- /**
3145
- * Validate source references in an array
3146
- */
3147
3168
  validateSourceReferences(sources, location, contextMsg) {
3148
3169
  for (const source of sources) {
3149
- if (typeof source === "object" && source !== null && "$ref" in source) {
3150
- const ref = source.$ref;
3151
- if (ref.startsWith("#/modifiers/")) {
3152
- this.handleValidationIssue(
3153
- `Invalid reference in ${location}: "${ref}". Sets and modifier contexts MUST NOT reference modifiers${contextMsg}`
3154
- );
3155
- }
3156
- if (ref.startsWith("#/resolutionOrder/")) {
3157
- this.handleValidationIssue(
3158
- `Invalid reference in ${location}: "${ref}". References MUST NOT point to resolutionOrder array items${contextMsg}`
3159
- );
3160
- }
3170
+ if (typeof source !== "object" || source === null || !("$ref" in source)) {
3171
+ continue;
3172
+ }
3173
+ const ref = source.$ref;
3174
+ if (ref.startsWith("#/modifiers/")) {
3175
+ this.handleValidationIssue(
3176
+ `Invalid reference in ${location}: "${ref}". Sets and modifier contexts MUST NOT reference modifiers${contextMsg}`
3177
+ );
3178
+ }
3179
+ if (ref.startsWith("#/resolutionOrder/")) {
3180
+ this.handleValidationIssue(
3181
+ `Invalid reference in ${location}: "${ref}". References MUST NOT point to resolutionOrder array items${contextMsg}`
3182
+ );
3161
3183
  }
3162
3184
  }
3163
3185
  }
@@ -3237,15 +3259,14 @@ var init_resolver_loader = __esm({
3237
3259
  * ```
3238
3260
  */
3239
3261
  async load(resolver) {
3240
- if (typeof resolver === "string") {
3241
- const absolutePath = path__namespace.resolve(this.options.baseDir, resolver);
3242
- const resolverDoc = await this.parser.parseFile(absolutePath);
3243
- const baseDir = path__namespace.dirname(absolutePath);
3244
- return { resolverDoc, baseDir };
3245
- } else {
3246
- const resolverDoc = this.parser.parseInline(resolver);
3247
- return { resolverDoc, baseDir: this.options.baseDir };
3262
+ if (typeof resolver !== "string") {
3263
+ const resolverDoc2 = this.parser.parseInline(resolver);
3264
+ return { resolverDoc: resolverDoc2, baseDir: this.options.baseDir };
3248
3265
  }
3266
+ const absolutePath = path__namespace.resolve(this.options.baseDir, resolver);
3267
+ const resolverDoc = await this.parser.parseFile(absolutePath);
3268
+ const baseDir = path__namespace.dirname(absolutePath);
3269
+ return { resolverDoc, baseDir };
3249
3270
  }
3250
3271
  /**
3251
3272
  * Load only the resolver document (without base directory)
@@ -3278,6 +3299,35 @@ function sanitizeDataAttributeName(value) {
3278
3299
  function escapeCssString(value) {
3279
3300
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, " ");
3280
3301
  }
3302
+ function groupTokensByType(tokens, typeGroupMap) {
3303
+ const groupMap = /* @__PURE__ */ new Map();
3304
+ for (const [, token] of getSortedTokenEntries(tokens)) {
3305
+ const groupName = typeGroupMap[token.$type ?? ""] ?? "Other";
3306
+ const existing = groupMap.get(groupName) ?? [];
3307
+ existing.push(token);
3308
+ groupMap.set(groupName, existing);
3309
+ }
3310
+ return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
3311
+ name,
3312
+ tokens: groupTokens
3313
+ }));
3314
+ }
3315
+ function indentStr(width, level) {
3316
+ return " ".repeat(width * level);
3317
+ }
3318
+ function buildGeneratedFileHeader() {
3319
+ return [
3320
+ "// Generated by Dispersa - do not edit manually",
3321
+ "// https://github.com/dispersa-core/dispersa"
3322
+ ].join("\n");
3323
+ }
3324
+ function toSafeIdentifier(name, keywords, capitalize) {
3325
+ const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
3326
+ const cased = capitalize ? camel.charAt(0).toUpperCase() + camel.slice(1) : camel.charAt(0).toLowerCase() + camel.slice(1);
3327
+ const safe = /^\d/.test(cased) ? `_${cased}` : cased;
3328
+ const keyCheck = capitalize ? safe.charAt(0).toLowerCase() + safe.slice(1) : safe;
3329
+ return keywords.has(keyCheck) ? `\`${safe}\`` : safe;
3330
+ }
3281
3331
  function normalizeModifierInputs(inputs) {
3282
3332
  const normalized = {};
3283
3333
  for (const [key, value] of Object.entries(inputs)) {
@@ -3285,6 +3335,14 @@ function normalizeModifierInputs(inputs) {
3285
3335
  }
3286
3336
  return normalized;
3287
3337
  }
3338
+ function assertFileRequired(buildPath, outputFile, outputName, presetLabel) {
3339
+ const requiresFile = buildPath !== void 0 && buildPath !== "";
3340
+ if (!outputFile && requiresFile) {
3341
+ throw new exports.ConfigurationError(
3342
+ `Output "${outputName}": file is required for ${presetLabel} output`
3343
+ );
3344
+ }
3345
+ }
3288
3346
  function buildStablePermutationKey(modifierInputs, dimensions) {
3289
3347
  return dimensions.map((dimension) => `${dimension}=${modifierInputs[dimension] ?? ""}`).join("|");
3290
3348
  }
@@ -3334,14 +3392,18 @@ function generatePermutationKey(modifierInputs, resolver, isBase) {
3334
3392
  }
3335
3393
  return buildStablePermutationKey(normalizedInputs, metadata.dimensions);
3336
3394
  }
3337
- function buildInMemoryOutputKey(params) {
3338
- const { outputName, extension, modifierInputs, resolver, defaults } = params;
3395
+ function isBasePermutation(modifierInputs, defaults) {
3339
3396
  const normalizedInputs = normalizeModifierInputs(modifierInputs);
3340
3397
  const normalizedDefaults = normalizeModifierInputs(defaults);
3341
- const isBase = Object.entries(normalizedDefaults).every(
3342
- ([key, value]) => normalizedInputs[key] === value
3398
+ return Object.entries(normalizedDefaults).every(([key, value]) => normalizedInputs[key] === value);
3399
+ }
3400
+ function buildInMemoryOutputKey(params) {
3401
+ const { outputName, extension, modifierInputs, resolver, defaults } = params;
3402
+ const permutationKey = generatePermutationKey(
3403
+ modifierInputs,
3404
+ resolver,
3405
+ isBasePermutation(modifierInputs, defaults)
3343
3406
  );
3344
- const permutationKey = generatePermutationKey(modifierInputs, resolver, isBase);
3345
3407
  return `${outputName}-${permutationKey}.${extension}`;
3346
3408
  }
3347
3409
  function buildMetadata(resolver) {
@@ -3463,6 +3525,7 @@ function resolveFileName(fileName, modifierInputs) {
3463
3525
  }
3464
3526
  var init_utils = __esm({
3465
3527
  "src/renderers/bundlers/utils.ts"() {
3528
+ init_errors();
3466
3529
  init_token_utils();
3467
3530
  }
3468
3531
  });
@@ -3472,36 +3535,38 @@ var js_exports = {};
3472
3535
  __export(js_exports, {
3473
3536
  bundleAsJsModule: () => bundleAsJsModule
3474
3537
  });
3538
+ function updateStringTracking(state, char) {
3539
+ if (!state.escaped && (char === '"' || char === "'" || char === "`")) {
3540
+ if (!state.inString) {
3541
+ state.inString = true;
3542
+ state.stringChar = char;
3543
+ } else if (char === state.stringChar) {
3544
+ state.inString = false;
3545
+ state.stringChar = "";
3546
+ }
3547
+ }
3548
+ state.escaped = !state.escaped && char === "\\";
3549
+ }
3475
3550
  function extractObjectFromJsModule(formattedJs) {
3476
3551
  const assignmentMatch = /const\s+\w+\s*=\s*\{/.exec(formattedJs);
3477
3552
  if (!assignmentMatch) {
3478
3553
  return "{}";
3479
3554
  }
3480
3555
  const startIndex = assignmentMatch.index + assignmentMatch[0].length - 1;
3556
+ const state = { inString: false, stringChar: "", escaped: false };
3481
3557
  let braceCount = 0;
3482
- let inString = false;
3483
- let stringChar = "";
3484
- let escaped = false;
3485
3558
  for (let i = startIndex; i < formattedJs.length; i++) {
3486
3559
  const char = formattedJs[i];
3487
- if (!escaped && (char === '"' || char === "'" || char === "`")) {
3488
- if (!inString) {
3489
- inString = true;
3490
- stringChar = char;
3491
- } else if (char === stringChar) {
3492
- inString = false;
3493
- stringChar = "";
3494
- }
3495
- }
3496
- escaped = !escaped && char === "\\";
3497
- if (!inString) {
3498
- if (char === "{") {
3499
- braceCount++;
3500
- } else if (char === "}") {
3501
- braceCount--;
3502
- if (braceCount === 0) {
3503
- return formattedJs.substring(startIndex, i + 1);
3504
- }
3560
+ updateStringTracking(state, char);
3561
+ if (state.inString) {
3562
+ continue;
3563
+ }
3564
+ if (char === "{") {
3565
+ braceCount++;
3566
+ } else if (char === "}") {
3567
+ braceCount--;
3568
+ if (braceCount === 0) {
3569
+ return formattedJs.substring(startIndex, i + 1);
3505
3570
  }
3506
3571
  }
3507
3572
  }
@@ -3598,22 +3663,19 @@ __export(json_exports, {
3598
3663
  bundleAsJson: () => bundleAsJson
3599
3664
  });
3600
3665
  async function bundleAsJson(bundleData, resolver, formatTokens) {
3666
+ if (!formatTokens) {
3667
+ throw new exports.ConfigurationError("JSON formatter was not provided");
3668
+ }
3601
3669
  const metadata = buildMetadata(resolver);
3602
3670
  const tokens = {};
3603
3671
  for (const { tokens: tokenSet, modifierInputs } of bundleData) {
3604
3672
  const cleanTokens = stripInternalMetadata(tokenSet);
3605
- if (!formatTokens) {
3606
- throw new exports.ConfigurationError("JSON formatter was not provided");
3607
- }
3608
3673
  const normalizedInputs = normalizeModifierInputs(modifierInputs);
3609
3674
  const key = buildStablePermutationKey(normalizedInputs, metadata.dimensions);
3610
3675
  const themeJson = await formatTokens(cleanTokens);
3611
3676
  tokens[key] = JSON.parse(themeJson);
3612
3677
  }
3613
- const bundle = {
3614
- _meta: metadata,
3615
- tokens
3616
- };
3678
+ const bundle = { _meta: metadata, tokens };
3617
3679
  return JSON.stringify(bundle, null, 2);
3618
3680
  }
3619
3681
  var init_json = __esm({
@@ -3679,15 +3741,15 @@ var TypeGenerator = class {
3679
3741
  const lines = [];
3680
3742
  if (names.length === 0) {
3681
3743
  lines.push(`export type ${typeName} = never`);
3682
- } else {
3683
- lines.push(`export type ${typeName} =`);
3684
- for (let i = 0; i < names.length; i++) {
3685
- const name = names[i];
3686
- if (name == null) {
3687
- continue;
3688
- }
3689
- lines.push(` | "${name}"`);
3744
+ return lines;
3745
+ }
3746
+ lines.push(`export type ${typeName} =`);
3747
+ for (let i = 0; i < names.length; i++) {
3748
+ const name = names[i];
3749
+ if (name == null) {
3750
+ continue;
3690
3751
  }
3752
+ lines.push(` | "${name}"`);
3691
3753
  }
3692
3754
  return lines;
3693
3755
  }
@@ -3713,15 +3775,10 @@ var TypeGenerator = class {
3713
3775
  generateStructureType(tokens, options) {
3714
3776
  const lines = [];
3715
3777
  const structure = this.buildNestedStructure(tokens);
3716
- if (options.exportType === "type") {
3717
- lines.push(`export type ${options.moduleName} = {`);
3718
- this.addStructureProperties(lines, structure, 1);
3719
- lines.push("}");
3720
- } else {
3721
- lines.push(`export interface ${options.moduleName} {`);
3722
- this.addStructureProperties(lines, structure, 1);
3723
- lines.push("}");
3724
- }
3778
+ const opener = options.exportType === "type" ? `export type ${options.moduleName} = {` : `export interface ${options.moduleName} {`;
3779
+ lines.push(opener);
3780
+ this.addStructureProperties(lines, structure, 1);
3781
+ lines.push("}");
3725
3782
  return lines;
3726
3783
  }
3727
3784
  /**
@@ -3755,20 +3812,20 @@ var TypeGenerator = class {
3755
3812
  /**
3756
3813
  * Add structure properties to lines
3757
3814
  */
3758
- addStructureProperties(lines, structure, indent2) {
3759
- const indentStr = " ".repeat(indent2);
3815
+ addStructureProperties(lines, structure, indent) {
3816
+ const indentStr2 = " ".repeat(indent);
3760
3817
  for (const [key, value] of Object.entries(structure)) {
3761
3818
  if (this.isToken(value)) {
3762
3819
  const token = value;
3763
3820
  if (token.$description) {
3764
- lines.push(`${indentStr}/** ${token.$description} */`);
3821
+ lines.push(`${indentStr2}/** ${token.$description} */`);
3765
3822
  }
3766
3823
  const valueType = this.inferValueType(token);
3767
- lines.push(`${indentStr}${this.quoteKey(key)}: ${valueType}`);
3824
+ lines.push(`${indentStr2}${this.quoteKey(key)}: ${valueType}`);
3768
3825
  } else {
3769
- lines.push(`${indentStr}${this.quoteKey(key)}: {`);
3770
- this.addStructureProperties(lines, value, indent2 + 1);
3771
- lines.push(`${indentStr}}`);
3826
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
3827
+ this.addStructureProperties(lines, value, indent + 1);
3828
+ lines.push(`${indentStr2}}`);
3772
3829
  }
3773
3830
  }
3774
3831
  }
@@ -3960,28 +4017,27 @@ var BuildOrchestrator = class {
3960
4017
  if (config.hooks?.onBuildStart) {
3961
4018
  await config.hooks.onBuildStart({ config, resolver });
3962
4019
  }
3963
- let permutations;
3964
- if (config.permutations && config.permutations.length > 0) {
3965
- permutations = await Promise.all(
3966
- config.permutations.map(async (modifierInputs) => {
3967
- const { tokens, modifierInputs: resolvedInputs } = await this.pipeline.resolve(
3968
- resolver,
3969
- modifierInputs,
3970
- config.transforms,
3971
- config.preprocessors,
3972
- config.filters
3973
- );
3974
- return { tokens, modifierInputs: resolvedInputs };
3975
- })
3976
- );
3977
- } else {
3978
- permutations = await this.pipeline.resolveAllPermutations(
4020
+ if (!config.permutations || config.permutations.length === 0) {
4021
+ const permutations2 = await this.pipeline.resolveAllPermutations(
3979
4022
  resolver,
3980
4023
  config.transforms,
3981
4024
  config.preprocessors,
3982
4025
  config.filters
3983
4026
  );
4027
+ return this.executeBuild(buildPath, config, permutations2, resolver);
3984
4028
  }
4029
+ const permutations = await Promise.all(
4030
+ config.permutations.map(async (modifierInputs) => {
4031
+ const { tokens, modifierInputs: resolvedInputs } = await this.pipeline.resolve(
4032
+ resolver,
4033
+ modifierInputs,
4034
+ config.transforms,
4035
+ config.preprocessors,
4036
+ config.filters
4037
+ );
4038
+ return { tokens, modifierInputs: resolvedInputs };
4039
+ })
4040
+ );
3985
4041
  return this.executeBuild(buildPath, config, permutations, resolver);
3986
4042
  }
3987
4043
  /**
@@ -4003,44 +4059,15 @@ var BuildOrchestrator = class {
4003
4059
  * @returns Build result with success status, outputs, and any errors
4004
4060
  */
4005
4061
  async executeBuild(buildPath, config, permutations, resolver) {
4006
- const outputs = [];
4007
- const errors = [];
4008
4062
  try {
4009
4063
  const resolverDoc = await resolveResolverDocument(resolver);
4010
4064
  const metadata = buildMetadata(resolverDoc);
4011
- const basePermutation = metadata.defaults;
4012
4065
  const settled = await Promise.allSettled(
4013
- config.outputs.map(async (output) => {
4014
- if (output.hooks?.onBuildStart) {
4015
- await output.hooks.onBuildStart({ config, resolver });
4016
- }
4017
- try {
4018
- const results = await this.processOutput(output, permutations, resolverDoc, metadata, basePermutation, buildPath);
4019
- if (output.hooks?.onBuildEnd) {
4020
- await output.hooks.onBuildEnd({ success: true, outputs: results });
4021
- }
4022
- return results;
4023
- } catch (error) {
4024
- if (output.hooks?.onBuildEnd) {
4025
- await output.hooks.onBuildEnd({ success: false, outputs: [], errors: [toBuildError(error, output.name)] });
4026
- }
4027
- throw error;
4028
- }
4029
- })
4066
+ config.outputs.map(
4067
+ (output) => this.buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver)
4068
+ )
4030
4069
  );
4031
- for (let i = 0; i < settled.length; i++) {
4032
- const outcome = settled[i];
4033
- if (outcome.status === "fulfilled") {
4034
- outputs.push(...outcome.value);
4035
- } else {
4036
- errors.push(toBuildError(outcome.reason, config.outputs[i].name));
4037
- }
4038
- }
4039
- const result = {
4040
- success: errors.length === 0,
4041
- outputs,
4042
- errors: errors.length > 0 ? errors : void 0
4043
- };
4070
+ const result = this.collectSettledResults(settled, config);
4044
4071
  if (config.hooks?.onBuildEnd) {
4045
4072
  await config.hooks.onBuildEnd(result);
4046
4073
  }
@@ -4057,6 +4084,40 @@ var BuildOrchestrator = class {
4057
4084
  return result;
4058
4085
  }
4059
4086
  }
4087
+ async buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver) {
4088
+ if (output.hooks?.onBuildStart) {
4089
+ await output.hooks.onBuildStart({ config, resolver });
4090
+ }
4091
+ try {
4092
+ const results = await this.processOutput(output, permutations, resolverDoc, metadata, metadata.defaults, buildPath);
4093
+ if (output.hooks?.onBuildEnd) {
4094
+ await output.hooks.onBuildEnd({ success: true, outputs: results });
4095
+ }
4096
+ return results;
4097
+ } catch (error) {
4098
+ if (output.hooks?.onBuildEnd) {
4099
+ await output.hooks.onBuildEnd({ success: false, outputs: [], errors: [toBuildError(error, output.name)] });
4100
+ }
4101
+ throw error;
4102
+ }
4103
+ }
4104
+ collectSettledResults(settled, config) {
4105
+ const outputs = [];
4106
+ const errors = [];
4107
+ for (let i = 0; i < settled.length; i++) {
4108
+ const outcome = settled[i];
4109
+ if (outcome.status === "fulfilled") {
4110
+ outputs.push(...outcome.value);
4111
+ } else {
4112
+ errors.push(toBuildError(outcome.reason, config.outputs[i].name));
4113
+ }
4114
+ }
4115
+ return {
4116
+ success: errors.length === 0,
4117
+ outputs,
4118
+ errors: errors.length > 0 ? errors : void 0
4119
+ };
4120
+ }
4060
4121
  async processOutput(output, permutations, resolverDoc, metadata, basePermutation, buildPath) {
4061
4122
  if (!output.renderer.format) {
4062
4123
  throw new exports.ConfigurationError("Renderer does not implement format()");
@@ -4106,7 +4167,7 @@ async function writeOutputFile(fileName, content, encoding = "utf-8") {
4106
4167
  }
4107
4168
  }
4108
4169
 
4109
- // src/processing/token-modifier.ts
4170
+ // src/processing/apply.ts
4110
4171
  function applyTransforms(tokens, transformList) {
4111
4172
  const result = {};
4112
4173
  for (const [name, token] of Object.entries(tokens)) {
@@ -5384,14 +5445,15 @@ var ResolutionEngine = class {
5384
5445
  mergeTokens(target, source) {
5385
5446
  const result = { ...target };
5386
5447
  for (const [key, value] of Object.entries(source)) {
5387
- if (typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value)) {
5388
- result[key] = this.mergeTokens(
5389
- result[key] ?? {},
5390
- value
5391
- );
5392
- } else {
5448
+ const isGroup = typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value);
5449
+ if (!isGroup) {
5393
5450
  result[key] = value;
5451
+ continue;
5394
5452
  }
5453
+ result[key] = this.mergeTokens(
5454
+ result[key] ?? {},
5455
+ value
5456
+ );
5395
5457
  }
5396
5458
  return result;
5397
5459
  }
@@ -6480,6 +6542,17 @@ function isTransitionToken(token) {
6480
6542
  function isGradientToken(token) {
6481
6543
  return token.$type === "gradient";
6482
6544
  }
6545
+ function nameKebabCase() {
6546
+ return {
6547
+ transform: (token) => {
6548
+ const name = changeCase.kebabCase(token.path.join(" "));
6549
+ return {
6550
+ ...token,
6551
+ name
6552
+ };
6553
+ }
6554
+ };
6555
+ }
6483
6556
  function isColorObject(value) {
6484
6557
  return typeof value === "object" && value !== null && "colorSpace" in value && "components" in value;
6485
6558
  }
@@ -6537,7 +6610,7 @@ function colorObjectToHex(color) {
6537
6610
  return culori.formatHex(culoriColor);
6538
6611
  }
6539
6612
 
6540
- // src/processing/processors/transforms/built-in/dimension-converter.ts
6613
+ // src/processing/transforms/built-in/dimension-converter.ts
6541
6614
  function isDimensionObject(value) {
6542
6615
  return typeof value === "object" && value !== null && "value" in value && "unit" in value;
6543
6616
  }
@@ -6545,6 +6618,14 @@ function dimensionObjectToString(dimension) {
6545
6618
  return `${dimension.value}${dimension.unit}`;
6546
6619
  }
6547
6620
 
6621
+ // src/processing/transforms/built-in/duration-converter.ts
6622
+ function isDurationObject(value) {
6623
+ return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
6624
+ }
6625
+ function durationObjectToString(duration) {
6626
+ return `${duration.value}${duration.unit}`;
6627
+ }
6628
+
6548
6629
  // src/renderers/android.ts
6549
6630
  init_errors();
6550
6631
  init_token_utils();
@@ -6596,9 +6677,6 @@ function resolveColorFormat(format) {
6596
6677
  }
6597
6678
  return "argb_hex";
6598
6679
  }
6599
- function indent(width, level) {
6600
- return " ".repeat(width * level);
6601
- }
6602
6680
  function escapeKotlinString(str) {
6603
6681
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
6604
6682
  }
@@ -6614,22 +6692,6 @@ function roundComponent(value) {
6614
6692
  function toResourceName(family) {
6615
6693
  return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
6616
6694
  }
6617
- function toPascalCase(name) {
6618
- const pascal = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
6619
- const result = pascal.charAt(0).toUpperCase() + pascal.slice(1);
6620
- if (/^\d/.test(result)) {
6621
- return `_${result}`;
6622
- }
6623
- return KOTLIN_KEYWORDS.has(result.charAt(0).toLowerCase() + result.slice(1)) ? `\`${result}\`` : result;
6624
- }
6625
- function toKotlinIdentifier(name) {
6626
- const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
6627
- const identifier = camel.charAt(0).toLowerCase() + camel.slice(1);
6628
- if (/^\d/.test(identifier)) {
6629
- return `_${identifier}`;
6630
- }
6631
- return KOTLIN_KEYWORDS.has(identifier) ? `\`${identifier}\`` : identifier;
6632
- }
6633
6695
  var AndroidRenderer = class {
6634
6696
  async format(context, options) {
6635
6697
  if (!options?.packageName) {
@@ -6637,6 +6699,7 @@ var AndroidRenderer = class {
6637
6699
  `Output "${context.output.name}": packageName is required for Android output`
6638
6700
  );
6639
6701
  }
6702
+ const visibility = options?.visibility;
6640
6703
  const opts = {
6641
6704
  preset: options?.preset ?? "standalone",
6642
6705
  packageName: options.packageName,
@@ -6644,7 +6707,8 @@ var AndroidRenderer = class {
6644
6707
  colorFormat: resolveColorFormat(options?.colorFormat),
6645
6708
  colorSpace: options?.colorSpace ?? "sRGB",
6646
6709
  structure: options?.structure ?? "nested",
6647
- visibility: options?.visibility,
6710
+ visibility,
6711
+ visPrefix: visibility ? `${visibility} ` : "",
6648
6712
  indent: options?.indent ?? 4
6649
6713
  };
6650
6714
  if (opts.preset === "bundle") {
@@ -6677,19 +6741,6 @@ var AndroidRenderer = class {
6677
6741
  // -----------------------------------------------------------------------
6678
6742
  // Flat structure grouping
6679
6743
  // -----------------------------------------------------------------------
6680
- groupTokensByType(tokens) {
6681
- const groupMap = /* @__PURE__ */ new Map();
6682
- for (const [, token] of getSortedTokenEntries(tokens)) {
6683
- const groupName = KOTLIN_TYPE_GROUP_MAP[token.$type ?? ""] ?? "Other";
6684
- const existing = groupMap.get(groupName) ?? [];
6685
- existing.push(token);
6686
- groupMap.set(groupName, existing);
6687
- }
6688
- return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
6689
- name,
6690
- tokens: groupTokens
6691
- }));
6692
- }
6693
6744
  /**
6694
6745
  * Builds a flattened camelCase name from a token's path, stripping the
6695
6746
  * type prefix segment (which is already represented by the group object).
@@ -6698,7 +6749,7 @@ var AndroidRenderer = class {
6698
6749
  const path7 = token.path;
6699
6750
  const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
6700
6751
  const joined = withoutTypePrefix.join("_");
6701
- return toKotlinIdentifier(joined);
6752
+ return toSafeIdentifier(joined, KOTLIN_KEYWORDS, false);
6702
6753
  }
6703
6754
  // -----------------------------------------------------------------------
6704
6755
  // Rendering
@@ -6710,22 +6761,21 @@ var AndroidRenderer = class {
6710
6761
  return this.formatAsNested(tokens, options);
6711
6762
  }
6712
6763
  formatAsNested(tokens, options) {
6764
+ const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6713
6765
  const tree = this.buildTokenTree(tokens);
6714
- const tokenTypes = /* @__PURE__ */ new Set();
6715
- this.collectTokenTypes(tree, tokenTypes);
6716
- return this.buildFile(tokenTypes, options, (lines, vis) => {
6766
+ return this.buildFile(tokenTypes, options, (lines) => {
6717
6767
  lines.push(`@Suppress("unused")`);
6718
- lines.push(`${vis}object ${options.objectName} {`);
6768
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
6719
6769
  this.renderTreeChildren(lines, tree, 1, options);
6720
6770
  lines.push("}");
6721
6771
  });
6722
6772
  }
6723
6773
  formatAsFlat(tokens, options) {
6724
- const groups = this.groupTokensByType(tokens);
6774
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
6725
6775
  const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6726
- return this.buildFile(tokenTypes, options, (lines, vis) => {
6776
+ return this.buildFile(tokenTypes, options, (lines) => {
6727
6777
  lines.push(`@Suppress("unused")`);
6728
- lines.push(`${vis}object ${options.objectName} {`);
6778
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
6729
6779
  this.renderFlatGroups(lines, groups, 1, options);
6730
6780
  lines.push("}");
6731
6781
  });
@@ -6736,9 +6786,8 @@ var AndroidRenderer = class {
6736
6786
  */
6737
6787
  buildFile(tokenTypes, options, renderBody) {
6738
6788
  const imports = this.collectImports(tokenTypes, options);
6739
- const vis = options.visibility ? `${options.visibility} ` : "";
6740
6789
  const lines = [];
6741
- lines.push(this.buildFileHeader());
6790
+ lines.push(buildGeneratedFileHeader());
6742
6791
  lines.push("");
6743
6792
  lines.push(`package ${options.packageName}`);
6744
6793
  lines.push("");
@@ -6749,19 +6798,18 @@ var AndroidRenderer = class {
6749
6798
  lines.push("");
6750
6799
  }
6751
6800
  if (tokenTypes.has("shadow")) {
6752
- lines.push(...this.buildShadowTokenClass(vis, options));
6801
+ lines.push(...this.buildShadowTokenClass(options));
6753
6802
  lines.push("");
6754
6803
  }
6755
- renderBody(lines, vis);
6804
+ renderBody(lines);
6756
6805
  lines.push("");
6757
6806
  return lines.join("\n");
6758
6807
  }
6759
6808
  renderFlatGroups(lines, groups, baseDepth, options) {
6760
- const vis = options.visibility ? `${options.visibility} ` : "";
6761
- const groupIndent = indent(options.indent, baseDepth);
6762
- const valIndent = indent(options.indent, baseDepth + 1);
6809
+ const groupIndent = indentStr(options.indent, baseDepth);
6810
+ const valIndent = indentStr(options.indent, baseDepth + 1);
6763
6811
  for (const group of groups) {
6764
- lines.push(`${groupIndent}${vis}object ${group.name} {`);
6812
+ lines.push(`${groupIndent}${options.visPrefix}object ${group.name} {`);
6765
6813
  for (const token of group.tokens) {
6766
6814
  const kotlinName = this.buildFlatKotlinName(token);
6767
6815
  const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
@@ -6769,23 +6817,24 @@ var AndroidRenderer = class {
6769
6817
  if (token.$description) {
6770
6818
  lines.push(`${valIndent}/** ${escapeKDoc(token.$description)} */`);
6771
6819
  }
6772
- lines.push(`${valIndent}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6820
+ lines.push(
6821
+ `${valIndent}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`
6822
+ );
6773
6823
  }
6774
6824
  lines.push(`${groupIndent}}`);
6775
6825
  lines.push("");
6776
6826
  }
6777
6827
  }
6778
6828
  renderTreeChildren(lines, node, depth, options) {
6779
- const vis = options.visibility ? `${options.visibility} ` : "";
6780
- const pad = indent(options.indent, depth);
6829
+ const pad = indentStr(options.indent, depth);
6781
6830
  const entries = Array.from(node.children.entries());
6782
6831
  for (let idx = 0; idx < entries.length; idx++) {
6783
6832
  const [key, child] = entries[idx];
6784
6833
  if (child.token && child.children.size === 0) {
6785
6834
  this.renderLeaf(lines, key, child.token, depth, options);
6786
6835
  } else if (child.children.size > 0 && !child.token) {
6787
- const objectName = toPascalCase(key);
6788
- lines.push(`${pad}${vis}object ${objectName} {`);
6836
+ const objectName = toSafeIdentifier(key, KOTLIN_KEYWORDS, true);
6837
+ lines.push(`${pad}${options.visPrefix}object ${objectName} {`);
6789
6838
  this.renderTreeChildren(lines, child, depth + 1, options);
6790
6839
  lines.push(`${pad}}`);
6791
6840
  if (idx < entries.length - 1) {
@@ -6798,30 +6847,23 @@ var AndroidRenderer = class {
6798
6847
  }
6799
6848
  }
6800
6849
  renderLeaf(lines, key, token, depth, options) {
6801
- const vis = options.visibility ? `${options.visibility} ` : "";
6802
- const pad = indent(options.indent, depth);
6803
- const kotlinName = toKotlinIdentifier(key);
6850
+ const pad = indentStr(options.indent, depth);
6851
+ const kotlinName = toSafeIdentifier(key, KOTLIN_KEYWORDS, false);
6804
6852
  const kotlinValue = this.formatKotlinValue(token, options, depth);
6805
6853
  const annotation = this.typeAnnotationSuffix(token);
6806
6854
  if (token.$description) {
6807
6855
  lines.push(`${pad}/** ${escapeKDoc(token.$description)} */`);
6808
6856
  }
6809
- lines.push(`${pad}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6810
- }
6811
- buildFileHeader() {
6812
- return [
6813
- "// Generated by Dispersa - do not edit manually",
6814
- "// https://github.com/timges/dispersa"
6815
- ].join("\n");
6857
+ lines.push(`${pad}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`);
6816
6858
  }
6817
6859
  // -----------------------------------------------------------------------
6818
6860
  // Shadow data class
6819
6861
  // -----------------------------------------------------------------------
6820
- buildShadowTokenClass(vis, options) {
6821
- const i1 = indent(options.indent, 1);
6862
+ buildShadowTokenClass(options) {
6863
+ const i1 = indentStr(options.indent, 1);
6822
6864
  return [
6823
6865
  "@Immutable",
6824
- `${vis}data class ShadowToken(`,
6866
+ `${options.visPrefix}data class ShadowToken(`,
6825
6867
  `${i1}val color: Color,`,
6826
6868
  `${i1}val elevation: Dp,`,
6827
6869
  `${i1}val offsetX: Dp,`,
@@ -6872,14 +6914,6 @@ var AndroidRenderer = class {
6872
6914
  }
6873
6915
  return Array.from(imports).sort();
6874
6916
  }
6875
- collectTokenTypes(node, types) {
6876
- if (node.token?.$type) {
6877
- types.add(node.token.$type);
6878
- }
6879
- for (const child of node.children.values()) {
6880
- this.collectTokenTypes(child, types);
6881
- }
6882
- }
6883
6917
  collectTokenTypesFromEntries(tokens) {
6884
6918
  const types = /* @__PURE__ */ new Set();
6885
6919
  for (const [, token] of Object.entries(tokens)) {
@@ -7106,9 +7140,8 @@ var AndroidRenderer = class {
7106
7140
  return map[name.toLowerCase()];
7107
7141
  }
7108
7142
  formatDurationValue(value) {
7109
- if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
7110
- const dur = value;
7111
- return dur.unit === "ms" ? `${dur.value}.milliseconds` : `${dur.value}.seconds`;
7143
+ if (isDurationObject(value)) {
7144
+ return value.unit === "ms" ? `${value.value}.milliseconds` : `${value.value}.seconds`;
7112
7145
  }
7113
7146
  return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
7114
7147
  }
@@ -7126,8 +7159,8 @@ var AndroidRenderer = class {
7126
7159
  const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
7127
7160
  const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
7128
7161
  const offsetY = isDimensionObject(shadow.offsetY) ? this.formatDimensionValue(shadow.offsetY) : "0.dp";
7129
- const propIndent = indent(options.indent, depth + 1);
7130
- const closeIndent = indent(options.indent, depth);
7162
+ const propIndent = indentStr(options.indent, depth + 1);
7163
+ const closeIndent = indentStr(options.indent, depth);
7131
7164
  return [
7132
7165
  "ShadowToken(",
7133
7166
  `${propIndent}color = ${color},`,
@@ -7176,8 +7209,8 @@ var AndroidRenderer = class {
7176
7209
  if (parts.length === 0) {
7177
7210
  return "TextStyle()";
7178
7211
  }
7179
- const propIndent = indent(options.indent, depth + 1);
7180
- const closeIndent = indent(options.indent, depth);
7212
+ const propIndent = indentStr(options.indent, depth + 1);
7213
+ const closeIndent = indentStr(options.indent, depth);
7181
7214
  return `TextStyle(
7182
7215
  ${parts.map((p) => `${propIndent}${p}`).join(",\n")},
7183
7216
  ${closeIndent})`;
@@ -7186,12 +7219,12 @@ ${closeIndent})`;
7186
7219
  // Output: standalone
7187
7220
  // -----------------------------------------------------------------------
7188
7221
  async formatStandalone(context, options) {
7189
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
7190
- if (!context.output.file && requiresFile) {
7191
- throw new exports.ConfigurationError(
7192
- `Output "${context.output.name}": file is required for standalone Android output`
7193
- );
7194
- }
7222
+ assertFileRequired(
7223
+ context.buildPath,
7224
+ context.output.file,
7225
+ context.output.name,
7226
+ "standalone Android"
7227
+ );
7195
7228
  const files = {};
7196
7229
  for (const { tokens, modifierInputs } of context.permutations) {
7197
7230
  const processedTokens = stripInternalMetadata(tokens);
@@ -7211,12 +7244,12 @@ ${closeIndent})`;
7211
7244
  // Output: bundle
7212
7245
  // -----------------------------------------------------------------------
7213
7246
  async formatBundle(context, options) {
7214
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
7215
- if (!context.output.file && requiresFile) {
7216
- throw new exports.ConfigurationError(
7217
- `Output "${context.output.name}": file is required for bundle Android output`
7218
- );
7219
- }
7247
+ assertFileRequired(
7248
+ context.buildPath,
7249
+ context.output.file,
7250
+ context.output.name,
7251
+ "bundle Android"
7252
+ );
7220
7253
  const content = this.formatBundleContent(context, options);
7221
7254
  const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
7222
7255
  outputName: context.output.name,
@@ -7229,15 +7262,15 @@ ${closeIndent})`;
7229
7262
  }
7230
7263
  formatBundleContent(context, options) {
7231
7264
  const allTokenTypes = this.collectAllPermutationTypes(context);
7232
- return this.buildFile(allTokenTypes, options, (lines, vis) => {
7233
- const i1 = indent(options.indent, 1);
7265
+ return this.buildFile(allTokenTypes, options, (lines) => {
7266
+ const i1 = indentStr(options.indent, 1);
7234
7267
  lines.push(`@Suppress("unused")`);
7235
- lines.push(`${vis}object ${options.objectName} {`);
7268
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
7236
7269
  for (let idx = 0; idx < context.permutations.length; idx++) {
7237
7270
  const { tokens, modifierInputs } = context.permutations[idx];
7238
7271
  const processedTokens = stripInternalMetadata(tokens);
7239
7272
  const permName = this.buildPermutationName(modifierInputs);
7240
- lines.push(`${i1}${vis}object ${permName} {`);
7273
+ lines.push(`${i1}${options.visPrefix}object ${permName} {`);
7241
7274
  this.renderBundleTokens(lines, processedTokens, options, 2);
7242
7275
  lines.push(`${i1}}`);
7243
7276
  if (idx < context.permutations.length - 1) {
@@ -7248,20 +7281,17 @@ ${closeIndent})`;
7248
7281
  });
7249
7282
  }
7250
7283
  collectAllPermutationTypes(context) {
7251
- const allTokenTypes = /* @__PURE__ */ new Set();
7284
+ const types = /* @__PURE__ */ new Set();
7252
7285
  for (const { tokens } of context.permutations) {
7253
- const processed = stripInternalMetadata(tokens);
7254
- for (const [, token] of Object.entries(processed)) {
7255
- if (token.$type) {
7256
- allTokenTypes.add(token.$type);
7257
- }
7286
+ for (const t of this.collectTokenTypesFromEntries(stripInternalMetadata(tokens))) {
7287
+ types.add(t);
7258
7288
  }
7259
7289
  }
7260
- return allTokenTypes;
7290
+ return types;
7261
7291
  }
7262
7292
  renderBundleTokens(lines, tokens, options, baseDepth) {
7263
7293
  if (options.structure === "flat") {
7264
- const groups = this.groupTokensByType(tokens);
7294
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
7265
7295
  this.renderFlatGroups(lines, groups, baseDepth, options);
7266
7296
  return;
7267
7297
  }
@@ -7273,7 +7303,7 @@ ${closeIndent})`;
7273
7303
  if (values.length === 0) {
7274
7304
  return "Default";
7275
7305
  }
7276
- return values.map((v) => toPascalCase(v)).join("");
7306
+ return values.map((v) => toSafeIdentifier(v, KOTLIN_KEYWORDS, true)).join("");
7277
7307
  }
7278
7308
  };
7279
7309
  function androidRenderer() {
@@ -7293,19 +7323,19 @@ init_token_utils();
7293
7323
  // src/renderers/bundlers/css.ts
7294
7324
  init_errors();
7295
7325
  init_utils();
7326
+ var REF_PREFIX_SETS = "#/sets/";
7327
+ var REF_PREFIX_MODIFIERS = "#/modifiers/";
7296
7328
  var getSourceSet = (token) => {
7297
7329
  if (typeof token !== "object" || token === null) {
7298
7330
  return void 0;
7299
7331
  }
7300
- const maybe = token;
7301
- return typeof maybe._sourceSet === "string" ? maybe._sourceSet : void 0;
7332
+ return "_sourceSet" in token && typeof token._sourceSet === "string" ? token._sourceSet : void 0;
7302
7333
  };
7303
7334
  var getSourceModifier = (token) => {
7304
7335
  if (typeof token !== "object" || token === null) {
7305
7336
  return void 0;
7306
7337
  }
7307
- const maybe = token;
7308
- return typeof maybe._sourceModifier === "string" ? maybe._sourceModifier : void 0;
7338
+ return "_sourceModifier" in token && typeof token._sourceModifier === "string" ? token._sourceModifier : void 0;
7309
7339
  };
7310
7340
  async function bundleAsCss(bundleData, resolver, options, formatTokens) {
7311
7341
  const baseItem = bundleData.find((item) => item.isBase);
@@ -7390,6 +7420,15 @@ async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, o
7390
7420
  return `/* Modifier: ${modifier}=${context} */
7391
7421
  ${css2}`;
7392
7422
  }
7423
+ function addLayerBlock(blocks, included, key, blockTokens, description) {
7424
+ if (Object.keys(blockTokens).length === 0) {
7425
+ return;
7426
+ }
7427
+ for (const k of Object.keys(blockTokens)) {
7428
+ included.add(k);
7429
+ }
7430
+ blocks.push({ key, description, tokens: blockTokens });
7431
+ }
7393
7432
  function collectSetTokens(tokens, setName, included) {
7394
7433
  const result = {};
7395
7434
  for (const [name, token] of Object.entries(tokens)) {
@@ -7420,75 +7459,67 @@ function collectRemainder(tokens, included) {
7420
7459
  function buildSetLayerBlocks(tokens, resolver) {
7421
7460
  const blocks = [];
7422
7461
  const included = /* @__PURE__ */ new Set();
7423
- const addBlock = (key, blockTokens, description) => {
7424
- if (Object.keys(blockTokens).length === 0) {
7425
- return;
7426
- }
7427
- for (const k of Object.keys(blockTokens)) {
7428
- included.add(k);
7429
- }
7430
- blocks.push({ key, description, tokens: blockTokens });
7431
- };
7432
7462
  for (const item of resolver.resolutionOrder) {
7433
7463
  const ref = item.$ref;
7434
- if (typeof ref !== "string" || !ref.startsWith("#/sets/")) {
7464
+ if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX_SETS)) {
7435
7465
  continue;
7436
7466
  }
7437
- const setName = ref.slice("#/sets/".length);
7438
- addBlock(
7467
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7468
+ addLayerBlock(
7469
+ blocks,
7470
+ included,
7439
7471
  `Set: ${setName}`,
7440
7472
  collectSetTokens(tokens, setName, included),
7441
7473
  resolver.sets?.[setName]?.description
7442
7474
  );
7443
7475
  }
7444
- addBlock("Unattributed", collectRemainder(tokens, included));
7476
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7445
7477
  return blocks;
7446
7478
  }
7447
7479
  function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
7448
7480
  const blocks = [];
7449
7481
  const included = /* @__PURE__ */ new Set();
7450
7482
  const baseInputs = normalizeModifierInputs(baseModifierInputs);
7451
- const addBlock = (key, blockTokens, description) => {
7452
- if (Object.keys(blockTokens).length === 0) {
7453
- return;
7454
- }
7455
- for (const k of Object.keys(blockTokens)) {
7456
- included.add(k);
7457
- }
7458
- blocks.push({ key, description, tokens: blockTokens });
7459
- };
7460
7483
  for (const item of resolver.resolutionOrder) {
7461
7484
  const ref = item.$ref;
7462
7485
  if (typeof ref !== "string") {
7463
7486
  continue;
7464
7487
  }
7465
- if (ref.startsWith("#/sets/")) {
7466
- const setName = ref.slice("#/sets/".length);
7467
- addBlock(
7468
- `Set: ${setName}`,
7469
- collectSetTokens(tokens, setName, included),
7470
- resolver.sets?.[setName]?.description
7471
- );
7472
- continue;
7473
- }
7474
- if (ref.startsWith("#/modifiers/")) {
7475
- const modifierName = ref.slice("#/modifiers/".length);
7476
- const modifier = resolver.modifiers?.[modifierName];
7477
- const selectedContext = baseInputs[modifierName.toLowerCase()];
7478
- if (!modifier || !selectedContext) {
7479
- continue;
7480
- }
7481
- const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7482
- addBlock(
7483
- `Modifier: ${modifierName}=${selectedContext} (default)`,
7484
- collectModifierTokens(tokens, expectedSource, included),
7485
- modifier.description
7486
- );
7487
- }
7488
+ processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver);
7488
7489
  }
7489
- addBlock("Unattributed", collectRemainder(tokens, included));
7490
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7490
7491
  return blocks;
7491
7492
  }
7493
+ function processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver) {
7494
+ if (ref.startsWith(REF_PREFIX_SETS)) {
7495
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7496
+ addLayerBlock(
7497
+ blocks,
7498
+ included,
7499
+ `Set: ${setName}`,
7500
+ collectSetTokens(tokens, setName, included),
7501
+ resolver.sets?.[setName]?.description
7502
+ );
7503
+ return;
7504
+ }
7505
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7506
+ return;
7507
+ }
7508
+ const modifierName = ref.slice(REF_PREFIX_MODIFIERS.length);
7509
+ const modifier = resolver.modifiers?.[modifierName];
7510
+ const selectedContext = baseInputs[modifierName.toLowerCase()];
7511
+ if (!modifier || !selectedContext) {
7512
+ return;
7513
+ }
7514
+ const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7515
+ addLayerBlock(
7516
+ blocks,
7517
+ included,
7518
+ `Modifier: ${modifierName}=${selectedContext} (default)`,
7519
+ collectModifierTokens(tokens, expectedSource, included),
7520
+ modifier.description
7521
+ );
7522
+ }
7492
7523
  function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs) {
7493
7524
  const normalizedModifier = modifierName.toLowerCase();
7494
7525
  const normalizedContext = context.toLowerCase();
@@ -7503,35 +7534,19 @@ function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs
7503
7534
  return Object.entries(baseInputs).every(([k, v]) => k === normalizedModifier || inputs[k] === v);
7504
7535
  });
7505
7536
  }
7506
- function orderBundleData(bundleData, resolver, baseItem) {
7507
- const modifiers = resolver.modifiers;
7508
- if (!modifiers) {
7509
- return bundleData;
7537
+ function pushUniqueBundleItem(ordered, includedKeys, item) {
7538
+ if (!item) {
7539
+ return;
7510
7540
  }
7511
- const baseInputs = normalizeModifierInputs(baseItem.modifierInputs);
7512
- const orderedModifierNames = getOrderedModifierNames(resolver);
7513
- if (orderedModifierNames.length === 0) {
7514
- return bundleData;
7515
- }
7516
- const firstModifierDef = modifiers[orderedModifierNames[0] ?? ""];
7517
- if (!firstModifierDef) {
7518
- return bundleData;
7541
+ const key = stableInputsKey(item.modifierInputs);
7542
+ if (includedKeys.has(key)) {
7543
+ return;
7519
7544
  }
7520
- const includedKeys = /* @__PURE__ */ new Set();
7521
- const ordered = [];
7522
- const pushUnique = (item) => {
7523
- if (!item) {
7524
- return;
7525
- }
7526
- const key = stableInputsKey(item.modifierInputs);
7527
- if (includedKeys.has(key)) {
7528
- return;
7529
- }
7530
- includedKeys.add(key);
7531
- ordered.push(item);
7532
- };
7533
- pushUnique(baseItem);
7534
- for (const modifierName of orderedModifierNames) {
7545
+ includedKeys.add(key);
7546
+ ordered.push(item);
7547
+ }
7548
+ function appendModifierPermutations(bundleData, modifiers, orderedNames, baseInputs, ordered, includedKeys) {
7549
+ for (const modifierName of orderedNames) {
7535
7550
  const modifierDef = modifiers[modifierName];
7536
7551
  if (!modifierDef) {
7537
7552
  continue;
@@ -7541,9 +7556,39 @@ function orderBundleData(bundleData, resolver, baseItem) {
7541
7556
  if (defaultValue === ctx.toLowerCase()) {
7542
7557
  continue;
7543
7558
  }
7544
- pushUnique(findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs));
7559
+ pushUniqueBundleItem(
7560
+ ordered,
7561
+ includedKeys,
7562
+ findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs)
7563
+ );
7545
7564
  }
7546
7565
  }
7566
+ }
7567
+ function orderBundleData(bundleData, resolver, baseItem) {
7568
+ const modifiers = resolver.modifiers;
7569
+ if (!modifiers) {
7570
+ return bundleData;
7571
+ }
7572
+ const baseInputs = normalizeModifierInputs(baseItem.modifierInputs);
7573
+ const orderedModifierNames = getOrderedModifierNames(resolver);
7574
+ if (orderedModifierNames.length === 0) {
7575
+ return bundleData;
7576
+ }
7577
+ const firstModifierDef = modifiers[orderedModifierNames[0] ?? ""];
7578
+ if (!firstModifierDef) {
7579
+ return bundleData;
7580
+ }
7581
+ const includedKeys = /* @__PURE__ */ new Set();
7582
+ const ordered = [];
7583
+ pushUniqueBundleItem(ordered, includedKeys, baseItem);
7584
+ appendModifierPermutations(
7585
+ bundleData,
7586
+ modifiers,
7587
+ orderedModifierNames,
7588
+ baseInputs,
7589
+ ordered,
7590
+ includedKeys
7591
+ );
7547
7592
  return ordered.length > 0 ? ordered : bundleData;
7548
7593
  }
7549
7594
  function getOrderedModifierNames(resolver) {
@@ -7555,10 +7600,10 @@ function getOrderedModifierNames(resolver) {
7555
7600
  if (typeof ref !== "string") {
7556
7601
  continue;
7557
7602
  }
7558
- if (!ref.startsWith("#/modifiers/")) {
7603
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7559
7604
  continue;
7560
7605
  }
7561
- const name = ref.slice("#/modifiers/".length);
7606
+ const name = ref.slice(REF_PREFIX_MODIFIERS.length);
7562
7607
  if (seen.has(name)) {
7563
7608
  continue;
7564
7609
  }
@@ -7629,24 +7674,22 @@ var CssRenderer = class _CssRenderer {
7629
7674
  ...options,
7630
7675
  referenceTokens: options?.referenceTokens ?? tokens
7631
7676
  };
7632
- const groups = this.groupTokens(tokens, opts);
7677
+ const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
7633
7678
  const referenceTokens = opts.referenceTokens;
7634
7679
  const lines = [];
7635
- for (const [selector, groupTokens] of Object.entries(groups)) {
7636
- this.buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts);
7637
- }
7680
+ this.buildCssBlock(lines, sortedTokens, opts.selector, tokens, referenceTokens, opts);
7638
7681
  const cssString = lines.join("");
7639
7682
  return opts.minify ? cssString : await this.formatWithPrettier(cssString);
7640
7683
  }
7641
7684
  buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
7642
- const indent2 = opts.minify ? "" : " ";
7685
+ const indent = opts.minify ? "" : " ";
7643
7686
  const newline = opts.minify ? "" : "\n";
7644
7687
  const space = opts.minify ? "" : " ";
7645
7688
  const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
7646
- const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
7689
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
7647
7690
  if (hasMediaQuery) {
7648
7691
  lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
7649
- lines.push(`${indent2}${selector}${space}{${newline}`);
7692
+ lines.push(`${indent}${selector}${space}{${newline}`);
7650
7693
  } else {
7651
7694
  lines.push(`${selector}${space}{${newline}`);
7652
7695
  }
@@ -7663,21 +7706,21 @@ var CssRenderer = class _CssRenderer {
7663
7706
  );
7664
7707
  }
7665
7708
  if (hasMediaQuery) {
7666
- lines.push(`${indent2}}${newline}`);
7709
+ lines.push(`${indent}}${newline}`);
7667
7710
  }
7668
7711
  lines.push(`}${newline}${newline}`);
7669
7712
  }
7670
- pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent2, newline, space) {
7713
+ pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
7671
7714
  const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
7672
7715
  if (token.$deprecated != null && token.$deprecated !== false) {
7673
7716
  const deprecationMsg = formatDeprecationMessage(token, "", "comment");
7674
- lines.push(`${indent2}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7717
+ lines.push(`${indent}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7675
7718
  }
7676
7719
  if (token.$description && token.$description !== "") {
7677
- lines.push(`${indent2}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7720
+ lines.push(`${indent}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7678
7721
  }
7679
7722
  for (const entry of entries) {
7680
- lines.push(`${indent2}--${entry.name}:${space}${entry.value};${newline}`);
7723
+ lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
7681
7724
  }
7682
7725
  }
7683
7726
  async formatWithPrettier(css2) {
@@ -7692,15 +7735,6 @@ var CssRenderer = class _CssRenderer {
7692
7735
  return css2;
7693
7736
  }
7694
7737
  }
7695
- /**
7696
- * Group tokens by selector (for theme support)
7697
- */
7698
- groupTokens(tokens, options) {
7699
- const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
7700
- return {
7701
- [options.selector]: sortedTokens
7702
- };
7703
- }
7704
7738
  buildCssEntries(token, tokens, referenceTokens, preserveReferences) {
7705
7739
  if (preserveReferences) {
7706
7740
  const refName = getPureAliasReferenceName(token.originalValue);
@@ -7842,7 +7876,7 @@ var CssRenderer = class _CssRenderer {
7842
7876
  leaves.push({ path: path7, value });
7843
7877
  return;
7844
7878
  }
7845
- if (isColorObject(value) || isDimensionObject(value) || this.isDurationObject(value)) {
7879
+ if (isColorObject(value) || isDimensionObject(value) || isDurationObject(value)) {
7846
7880
  leaves.push({ path: path7, value });
7847
7881
  return;
7848
7882
  }
@@ -7885,8 +7919,8 @@ var CssRenderer = class _CssRenderer {
7885
7919
  if (isDimensionObject(value)) {
7886
7920
  return dimensionObjectToString(value);
7887
7921
  }
7888
- if (this.isDurationObject(value)) {
7889
- return this.formatDurationValue(value);
7922
+ if (isDurationObject(value)) {
7923
+ return durationObjectToString(value);
7890
7924
  }
7891
7925
  if (typeof value === "string") {
7892
7926
  return value;
@@ -7949,15 +7983,6 @@ var CssRenderer = class _CssRenderer {
7949
7983
  isPrimitiveValue(value) {
7950
7984
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
7951
7985
  }
7952
- isDurationObject(value) {
7953
- return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
7954
- }
7955
- formatDurationValue(value) {
7956
- if (typeof value === "string") {
7957
- return value;
7958
- }
7959
- return `${value.value}${value.unit}`;
7960
- }
7961
7986
  /**
7962
7987
  * Format token value for CSS
7963
7988
  * Handles DTCG 2025.10 object formats for colors and dimensions
@@ -7978,8 +8003,8 @@ var CssRenderer = class _CssRenderer {
7978
8003
  return typeof value === "string" ? value : dimensionObjectToString(value);
7979
8004
  }
7980
8005
  if (type === "duration") {
7981
- if (this.isDurationObject(value)) {
7982
- return this.formatDurationValue(value);
8006
+ if (isDurationObject(value)) {
8007
+ return durationObjectToString(value);
7983
8008
  }
7984
8009
  if (typeof value === "string") {
7985
8010
  return value;
@@ -8069,16 +8094,16 @@ var CssRenderer = class _CssRenderer {
8069
8094
  */
8070
8095
  formatTransition(value) {
8071
8096
  const parts = [];
8072
- if (this.isDurationObject(value.duration)) {
8073
- parts.push(this.formatDurationValue(value.duration));
8097
+ if (isDurationObject(value.duration)) {
8098
+ parts.push(durationObjectToString(value.duration));
8074
8099
  } else if (value.duration != null) {
8075
8100
  parts.push(String(value.duration));
8076
8101
  }
8077
8102
  if (Array.isArray(value.timingFunction) && value.timingFunction.length === 4) {
8078
8103
  parts.push(`cubic-bezier(${value.timingFunction.join(", ")})`);
8079
8104
  }
8080
- if (this.isDurationObject(value.delay)) {
8081
- parts.push(this.formatDurationValue(value.delay));
8105
+ if (isDurationObject(value.delay)) {
8106
+ parts.push(durationObjectToString(value.delay));
8082
8107
  } else if (value.delay != null) {
8083
8108
  parts.push(String(value.delay));
8084
8109
  }
@@ -8088,7 +8113,7 @@ var CssRenderer = class _CssRenderer {
8088
8113
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8089
8114
  tokens,
8090
8115
  modifierInputs,
8091
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
8116
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8092
8117
  }));
8093
8118
  return await bundleAsCss(bundleData, context.resolver, options, async (tokens, resolved) => {
8094
8119
  return await this.formatTokens(tokens, {
@@ -8098,12 +8123,12 @@ var CssRenderer = class _CssRenderer {
8098
8123
  });
8099
8124
  }
8100
8125
  async formatStandalone(context, options) {
8101
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8102
- if (!context.output.file && requiresFile) {
8103
- throw new exports.ConfigurationError(
8104
- `Output "${context.output.name}": file is required for standalone CSS output`
8105
- );
8106
- }
8126
+ assertFileRequired(
8127
+ context.buildPath,
8128
+ context.output.file,
8129
+ context.output.name,
8130
+ "standalone CSS"
8131
+ );
8107
8132
  const files = {};
8108
8133
  for (const { tokens, modifierInputs } of context.permutations) {
8109
8134
  const { fileName, content } = await this.buildStandaloneFile(
@@ -8117,7 +8142,7 @@ var CssRenderer = class _CssRenderer {
8117
8142
  return { kind: "outputTree", files };
8118
8143
  }
8119
8144
  async buildStandaloneFile(tokens, modifierInputs, context, options) {
8120
- const isBase = this.isBasePermutation(modifierInputs, context.meta.defaults);
8145
+ const isBase = isBasePermutation(modifierInputs, context.meta.defaults);
8121
8146
  const { modifierName, modifierContext } = this.resolveModifierContext(
8122
8147
  modifierInputs,
8123
8148
  context,
@@ -8154,12 +8179,7 @@ var CssRenderer = class _CssRenderer {
8154
8179
  return { fileName, content };
8155
8180
  }
8156
8181
  async formatModifier(context, options) {
8157
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8158
- if (!context.output.file && requiresFile) {
8159
- throw new exports.ConfigurationError(
8160
- `Output "${context.output.name}": file is required for modifier CSS output`
8161
- );
8162
- }
8182
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "modifier CSS");
8163
8183
  if (!context.resolver.modifiers) {
8164
8184
  throw new exports.ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
8165
8185
  }
@@ -8185,7 +8205,7 @@ var CssRenderer = class _CssRenderer {
8185
8205
  }
8186
8206
  async buildModifierBaseFile(context, options) {
8187
8207
  const basePermutation = context.permutations.find(
8188
- ({ modifierInputs }) => this.isBasePermutation(modifierInputs, context.meta.defaults)
8208
+ ({ modifierInputs }) => isBasePermutation(modifierInputs, context.meta.defaults)
8189
8209
  );
8190
8210
  if (!basePermutation) {
8191
8211
  return void 0;
@@ -8198,25 +8218,40 @@ var CssRenderer = class _CssRenderer {
8198
8218
  if (setBlocks.length === 0) {
8199
8219
  return void 0;
8200
8220
  }
8221
+ const { selector, mediaQuery } = this.resolveBaseModifierContext(context, options);
8222
+ const content = await this.formatSetBlocksCss(
8223
+ setBlocks,
8224
+ basePermutation.tokens,
8225
+ selector,
8226
+ mediaQuery,
8227
+ options
8228
+ );
8229
+ const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8230
+ return { fileName, content };
8231
+ }
8232
+ resolveBaseModifierContext(context, options) {
8201
8233
  const modifiers = context.resolver.modifiers;
8202
8234
  const firstModifierName = Object.keys(modifiers)[0] ?? "";
8203
8235
  const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
8204
8236
  const baseModifierInputs = { ...context.meta.defaults };
8205
- const selector = resolveSelector(
8206
- options.selector,
8207
- firstModifierName,
8208
- firstModifierContext,
8209
- true,
8210
- baseModifierInputs
8211
- );
8212
- const mediaQuery = resolveMediaQuery(
8213
- options.mediaQuery,
8214
- firstModifierName,
8215
- firstModifierContext,
8216
- true,
8217
- baseModifierInputs
8218
- );
8219
- const referenceTokens = basePermutation.tokens;
8237
+ return {
8238
+ selector: resolveSelector(
8239
+ options.selector,
8240
+ firstModifierName,
8241
+ firstModifierContext,
8242
+ true,
8243
+ baseModifierInputs
8244
+ ),
8245
+ mediaQuery: resolveMediaQuery(
8246
+ options.mediaQuery,
8247
+ firstModifierName,
8248
+ firstModifierContext,
8249
+ true,
8250
+ baseModifierInputs
8251
+ )
8252
+ };
8253
+ }
8254
+ async formatSetBlocksCss(setBlocks, referenceTokens, selector, mediaQuery, options) {
8220
8255
  const cssBlocks = [];
8221
8256
  for (const block of setBlocks) {
8222
8257
  const cleanTokens = stripInternalMetadata(block.tokens);
@@ -8232,9 +8267,7 @@ var CssRenderer = class _CssRenderer {
8232
8267
  cssBlocks.push(`${header}
8233
8268
  ${css2}`);
8234
8269
  }
8235
- const content = cssBlocks.join("\n");
8236
- const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8237
- return { fileName, content };
8270
+ return cssBlocks.join("\n");
8238
8271
  }
8239
8272
  collectTokensForModifierContext(modifierName, contextValue, permutations) {
8240
8273
  const expectedSource = `${modifierName}-${contextValue}`;
@@ -8311,13 +8344,6 @@ ${css2}`);
8311
8344
  }
8312
8345
  return { modifierName: "", modifierContext: "" };
8313
8346
  }
8314
- isBasePermutation(modifierInputs, defaults) {
8315
- const normalizedInputs = normalizeModifierInputs(modifierInputs);
8316
- const normalizedDefaults = normalizeModifierInputs(defaults);
8317
- return Object.entries(normalizedDefaults).every(
8318
- ([key, value]) => normalizedInputs[key] === value
8319
- );
8320
- }
8321
8347
  };
8322
8348
  function cssRenderer() {
8323
8349
  const rendererInstance = new CssRenderer();
@@ -8330,8 +8356,6 @@ function cssRenderer() {
8330
8356
  }
8331
8357
 
8332
8358
  // src/renderers/ios.ts
8333
- init_errors();
8334
- init_token_utils();
8335
8359
  init_utils();
8336
8360
  var toSRGB2 = culori.converter("rgb");
8337
8361
  var toP32 = culori.converter("p3");
@@ -8420,94 +8444,68 @@ var IosRenderer = class {
8420
8444
  return await this.formatStandalone(context, opts);
8421
8445
  }
8422
8446
  formatTokens(tokens, options) {
8423
- if (options.structure === "grouped") {
8424
- return this.formatAsGrouped(tokens, options);
8425
- }
8426
- return this.formatAsEnum(tokens, options);
8427
- }
8428
- formatAsEnum(tokens, options) {
8429
8447
  const access3 = options.accessLevel;
8430
- const groups = this.groupTokensByType(tokens);
8448
+ const groups = groupTokensByType(tokens, SWIFT_TYPE_GROUP_MAP);
8431
8449
  const imports = this.collectImports(tokens);
8432
- const i1 = this.indentStr(options.indent, 1);
8433
- const i2 = this.indentStr(options.indent, 2);
8434
8450
  const staticPrefix = this.staticLetPrefix(options);
8435
8451
  const frozen = this.frozenPrefix(options);
8436
8452
  const lines = [];
8437
- lines.push(this.buildFileHeader());
8453
+ lines.push(buildGeneratedFileHeader());
8438
8454
  lines.push("");
8439
8455
  for (const imp of imports) {
8440
8456
  lines.push(`import ${imp}`);
8441
8457
  }
8442
8458
  lines.push(...this.buildStructDefinitions(tokens, access3, options));
8459
+ this.pushTokenLayout(lines, groups, options, access3, staticPrefix, frozen);
8460
+ lines.push(...this.buildViewExtensions(tokens, access3, options));
8461
+ if (options.structure !== "grouped") {
8462
+ lines.push("");
8463
+ }
8464
+ return lines.join("\n");
8465
+ }
8466
+ pushTokenLayout(lines, groups, options, access3, staticPrefix, frozen) {
8467
+ const i1 = indentStr(options.indent, 1);
8468
+ const i2 = indentStr(options.indent, 2);
8469
+ if (options.structure === "grouped") {
8470
+ this.pushGroupedLayout(lines, groups, options, access3, i1, i2, staticPrefix, frozen);
8471
+ return;
8472
+ }
8443
8473
  lines.push("");
8444
8474
  lines.push(`${frozen}${access3} enum ${options.enumName} {`);
8445
8475
  for (const group of groups) {
8446
8476
  lines.push(`${i1}${frozen}${access3} enum ${group.name} {`);
8447
- for (const token of group.tokens) {
8448
- const swiftName = this.buildQualifiedSwiftName(token);
8449
- const swiftValue = this.formatSwiftValue(token, options);
8450
- const typeAnnotation = this.getTypeAnnotation(token);
8451
- const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8452
- const docComment = this.buildDocComment(token, i2);
8453
- if (docComment) {
8454
- lines.push(docComment);
8455
- }
8456
- lines.push(`${i2}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8457
- }
8477
+ this.pushTokenDeclarations(lines, group.tokens, options, access3, i2, staticPrefix);
8458
8478
  lines.push(`${i1}}`);
8459
8479
  lines.push("");
8460
8480
  }
8461
8481
  lines.push("}");
8462
- lines.push(...this.buildViewExtensions(tokens, access3, options));
8463
- lines.push("");
8464
- return lines.join("\n");
8465
8482
  }
8466
- formatAsGrouped(tokens, options) {
8467
- const access3 = options.accessLevel;
8483
+ pushGroupedLayout(lines, groups, options, access3, i1, i2, staticPrefix, frozen) {
8468
8484
  const namespace = options.extensionNamespace;
8469
- const groups = this.groupTokensByType(tokens);
8470
- const imports = this.collectImports(tokens);
8471
- const i1 = this.indentStr(options.indent, 1);
8472
- const i2 = this.indentStr(options.indent, 2);
8473
- const staticPrefix = this.staticLetPrefix(options);
8474
- const frozen = this.frozenPrefix(options);
8475
- const lines = [];
8476
- lines.push(this.buildFileHeader());
8477
- lines.push("");
8478
- for (const imp of imports) {
8479
- lines.push(`import ${imp}`);
8480
- }
8481
- lines.push(...this.buildStructDefinitions(tokens, access3, options));
8482
8485
  lines.push("");
8483
8486
  lines.push(`${frozen}${access3} enum ${namespace} {}`);
8484
8487
  lines.push("");
8485
8488
  for (const group of groups) {
8486
8489
  lines.push(`${access3} extension ${namespace} {`);
8487
8490
  lines.push(`${i1}${frozen}enum ${group.name} {`);
8488
- for (const token of group.tokens) {
8489
- const swiftName = this.buildQualifiedSwiftName(token);
8490
- const swiftValue = this.formatSwiftValue(token, options);
8491
- const typeAnnotation = this.getTypeAnnotation(token);
8492
- const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8493
- const docComment = this.buildDocComment(token, i2);
8494
- if (docComment) {
8495
- lines.push(docComment);
8496
- }
8497
- lines.push(`${i2}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8498
- }
8491
+ this.pushTokenDeclarations(lines, group.tokens, options, access3, i2, staticPrefix);
8499
8492
  lines.push(`${i1}}`);
8500
8493
  lines.push("}");
8501
8494
  lines.push("");
8502
8495
  }
8503
- lines.push(...this.buildViewExtensions(tokens, access3, options));
8504
- return lines.join("\n");
8505
8496
  }
8506
- buildFileHeader() {
8507
- return [
8508
- "// Generated by Dispersa - do not edit manually",
8509
- "// https://github.com/timges/dispersa"
8510
- ].join("\n");
8497
+ pushTokenDeclarations(lines, tokens, options, access3, indent, staticPrefix) {
8498
+ for (const token of tokens) {
8499
+ const swiftName = this.buildQualifiedSwiftName(token);
8500
+ const swiftValue = this.formatSwiftValue(token, options);
8501
+ const typeAnnotation = this.getTypeAnnotation(token);
8502
+ const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8503
+ const docComment = this.buildDocComment(token, indent);
8504
+ if (docComment) {
8505
+ lines.push(docComment);
8506
+ }
8507
+ lines.push(`${indent}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8508
+ }
8511
8509
  }
8512
8510
  collectImports(tokens) {
8513
8511
  const imports = /* @__PURE__ */ new Set();
@@ -8522,24 +8520,11 @@ var IosRenderer = class {
8522
8520
  /**
8523
8521
  * Builds a `///` doc comment from a token's `$description`, if present.
8524
8522
  */
8525
- buildDocComment(token, indent2) {
8523
+ buildDocComment(token, indent) {
8526
8524
  if (!token.$description) {
8527
8525
  return void 0;
8528
8526
  }
8529
- return `${indent2}/// ${token.$description}`;
8530
- }
8531
- groupTokensByType(tokens) {
8532
- const groupMap = /* @__PURE__ */ new Map();
8533
- for (const [, token] of getSortedTokenEntries(tokens)) {
8534
- const groupName = SWIFT_TYPE_GROUP_MAP[token.$type ?? ""] ?? "Other";
8535
- const existing = groupMap.get(groupName) ?? [];
8536
- existing.push(token);
8537
- groupMap.set(groupName, existing);
8538
- }
8539
- return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
8540
- name,
8541
- tokens: groupTokens
8542
- }));
8527
+ return `${indent}/// ${token.$description}`;
8543
8528
  }
8544
8529
  /**
8545
8530
  * Builds a qualified Swift name from a token's path, preserving parent
@@ -8552,43 +8537,40 @@ var IosRenderer = class {
8552
8537
  const path7 = token.path;
8553
8538
  const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
8554
8539
  const joined = withoutTypePrefix.join("_");
8555
- return this.toSwiftIdentifier(joined);
8540
+ return toSafeIdentifier(joined, SWIFT_KEYWORDS, false);
8556
8541
  }
8557
8542
  formatSwiftValue(token, options) {
8558
- const value = token.$value;
8559
- if (token.$type === "color") {
8560
- return this.formatColorValue(value, options);
8561
- }
8562
- if (token.$type === "dimension") {
8563
- return this.formatDimensionValue(value);
8564
- }
8565
- if (token.$type === "fontFamily") {
8566
- return this.formatFontFamilyValue(value);
8567
- }
8568
- if (token.$type === "fontWeight") {
8569
- return this.formatFontWeightValue(value);
8570
- }
8571
- if (token.$type === "duration") {
8572
- return this.formatDurationValue(value);
8573
- }
8574
- if (token.$type === "shadow") {
8575
- return this.formatShadowValue(value, options);
8576
- }
8577
- if (token.$type === "typography") {
8578
- return this.formatTypographyValue(value);
8579
- }
8580
- if (token.$type === "border") {
8581
- return this.formatBorderValue(value, options);
8582
- }
8583
- if (token.$type === "gradient") {
8584
- return this.formatGradientValue(value, options);
8585
- }
8586
- if (token.$type === "number") {
8587
- return String(value);
8588
- }
8589
- if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
8590
- return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
8543
+ const { $type, $value: value } = token;
8544
+ switch ($type) {
8545
+ case "color":
8546
+ return this.formatColorValue(value, options);
8547
+ case "dimension":
8548
+ return this.formatDimensionValue(value);
8549
+ case "fontFamily":
8550
+ return this.formatFontFamilyValue(value);
8551
+ case "fontWeight":
8552
+ return this.formatFontWeightValue(value);
8553
+ case "duration":
8554
+ return this.formatDurationValue(value);
8555
+ case "shadow":
8556
+ return this.formatShadowValue(value, options);
8557
+ case "typography":
8558
+ return this.formatTypographyValue(value);
8559
+ case "border":
8560
+ return this.formatBorderValue(value, options);
8561
+ case "gradient":
8562
+ return this.formatGradientValue(value, options);
8563
+ case "number":
8564
+ return String(value);
8565
+ case "cubicBezier":
8566
+ if (Array.isArray(value) && value.length === 4) {
8567
+ return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
8568
+ }
8569
+ break;
8591
8570
  }
8571
+ return this.formatSwiftPrimitive(value);
8572
+ }
8573
+ formatSwiftPrimitive(value) {
8592
8574
  if (typeof value === "string") {
8593
8575
  return `"${this.escapeSwiftString(value)}"`;
8594
8576
  }
@@ -8621,9 +8603,7 @@ var IosRenderer = class {
8621
8603
  }
8622
8604
  formatDimensionValue(value) {
8623
8605
  if (isDimensionObject(value)) {
8624
- const dim = value;
8625
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8626
- return String(ptValue);
8606
+ return this.dimensionToPoints(value);
8627
8607
  }
8628
8608
  return String(value);
8629
8609
  }
@@ -8690,7 +8670,7 @@ var IosRenderer = class {
8690
8670
  return map[name.toLowerCase()];
8691
8671
  }
8692
8672
  formatDurationValue(value) {
8693
- if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
8673
+ if (isDurationObject(value)) {
8694
8674
  const dur = value;
8695
8675
  const seconds = dur.unit === "ms" ? dur.value / 1e3 : dur.value;
8696
8676
  return String(seconds);
@@ -8739,9 +8719,7 @@ var IosRenderer = class {
8739
8719
  if (!isDimensionObject(typo.letterSpacing)) {
8740
8720
  return "0";
8741
8721
  }
8742
- const dim = typo.letterSpacing;
8743
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8744
- return String(ptValue);
8722
+ return this.dimensionToPoints(typo.letterSpacing);
8745
8723
  }
8746
8724
  extractLineSpacing(typo) {
8747
8725
  if (typo.lineHeight == null || typeof typo.lineHeight !== "number") {
@@ -8750,18 +8728,19 @@ var IosRenderer = class {
8750
8728
  if (!isDimensionObject(typo.fontSize)) {
8751
8729
  return "0";
8752
8730
  }
8753
- const dim = typo.fontSize;
8754
- const basePt = dim.unit === "rem" ? dim.value * 16 : dim.value;
8731
+ const basePt = this.dimensionToNumericPoints(typo.fontSize);
8755
8732
  const lineHeightPt = Math.round(basePt * typo.lineHeight * 100) / 100;
8756
8733
  return String(lineHeightPt - basePt);
8757
8734
  }
8735
+ dimensionToNumericPoints(dim) {
8736
+ return dim.unit === "rem" ? dim.value * 16 : dim.value;
8737
+ }
8758
8738
  dimensionToPoints(dim) {
8759
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8760
- return String(ptValue);
8739
+ return String(this.dimensionToNumericPoints(dim));
8761
8740
  }
8762
8741
  /** Formats a dimension as a CGFloat literal (appends `.0` for integers). */
8763
8742
  dimensionToCGFloat(dim) {
8764
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8743
+ const ptValue = this.dimensionToNumericPoints(dim);
8765
8744
  return Number.isInteger(ptValue) ? `${ptValue}.0` : String(ptValue);
8766
8745
  }
8767
8746
  getTypeAnnotation(token) {
@@ -8780,21 +8759,12 @@ var IosRenderer = class {
8780
8759
  return void 0;
8781
8760
  }
8782
8761
  }
8783
- toSwiftIdentifier(name) {
8784
- const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
8785
- const identifier = camel.charAt(0).toLowerCase() + camel.slice(1);
8786
- const safe = /^\d/.test(identifier) ? `_${identifier}` : identifier;
8787
- return SWIFT_KEYWORDS.has(safe) ? `\`${safe}\`` : safe;
8788
- }
8789
8762
  escapeSwiftString(str) {
8790
8763
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
8791
8764
  }
8792
8765
  roundComponent(value) {
8793
8766
  return Math.round(value * 1e4) / 1e4;
8794
8767
  }
8795
- indentStr(width, level) {
8796
- return " ".repeat(width * level);
8797
- }
8798
8768
  /**
8799
8769
  * Returns the prefix for `static let` declarations.
8800
8770
  * Swift 6 requires `nonisolated(unsafe)` on global stored properties.
@@ -8810,34 +8780,25 @@ var IosRenderer = class {
8810
8780
  structConformances(options) {
8811
8781
  return options.swiftVersion === "6.0" ? ": Sendable" : "";
8812
8782
  }
8813
- hasShadowTokens(tokens) {
8814
- return Object.values(tokens).some((t) => t.$type === "shadow");
8815
- }
8816
- hasTypographyTokens(tokens) {
8817
- return Object.values(tokens).some((t) => t.$type === "typography");
8818
- }
8819
- hasBorderTokens(tokens) {
8820
- return Object.values(tokens).some((t) => t.$type === "border");
8821
- }
8822
8783
  /** Emits all struct definitions needed by the token set. */
8823
8784
  buildStructDefinitions(tokens, access3, options) {
8824
8785
  const lines = [];
8825
- if (this.hasShadowTokens(tokens)) {
8786
+ if (Object.values(tokens).some(isShadowToken)) {
8826
8787
  lines.push("");
8827
8788
  lines.push(...this.buildShadowStyleStruct(access3, options));
8828
8789
  }
8829
- if (this.hasTypographyTokens(tokens)) {
8790
+ if (Object.values(tokens).some(isTypographyToken)) {
8830
8791
  lines.push("");
8831
8792
  lines.push(...this.buildTypographyStyleStruct(access3, options));
8832
8793
  }
8833
- if (this.hasBorderTokens(tokens)) {
8794
+ if (Object.values(tokens).some(isBorderToken)) {
8834
8795
  lines.push("");
8835
8796
  lines.push(...this.buildBorderStyleStruct(access3, options));
8836
8797
  }
8837
8798
  return lines;
8838
8799
  }
8839
8800
  buildShadowStyleStruct(access3, options) {
8840
- const i1 = this.indentStr(options.indent, 1);
8801
+ const i1 = indentStr(options.indent, 1);
8841
8802
  const conformances = this.structConformances(options);
8842
8803
  const frozen = this.frozenPrefix(options);
8843
8804
  return [
@@ -8851,7 +8812,7 @@ var IosRenderer = class {
8851
8812
  ];
8852
8813
  }
8853
8814
  buildTypographyStyleStruct(access3, options) {
8854
- const i1 = this.indentStr(options.indent, 1);
8815
+ const i1 = indentStr(options.indent, 1);
8855
8816
  const conformances = this.structConformances(options);
8856
8817
  const frozen = this.frozenPrefix(options);
8857
8818
  return [
@@ -8863,7 +8824,7 @@ var IosRenderer = class {
8863
8824
  ];
8864
8825
  }
8865
8826
  buildBorderStyleStruct(access3, options) {
8866
- const i1 = this.indentStr(options.indent, 1);
8827
+ const i1 = indentStr(options.indent, 1);
8867
8828
  const conformances = this.structConformances(options);
8868
8829
  const frozen = this.frozenPrefix(options);
8869
8830
  return [
@@ -8876,9 +8837,9 @@ var IosRenderer = class {
8876
8837
  /** Emits convenience View extensions for shadow and typography application. */
8877
8838
  buildViewExtensions(tokens, access3, options) {
8878
8839
  const lines = [];
8879
- const i1 = this.indentStr(options.indent, 1);
8880
- const i2 = this.indentStr(options.indent, 2);
8881
- if (this.hasShadowTokens(tokens)) {
8840
+ const i1 = indentStr(options.indent, 1);
8841
+ const i2 = indentStr(options.indent, 2);
8842
+ if (Object.values(tokens).some(isShadowToken)) {
8882
8843
  lines.push("");
8883
8844
  lines.push(`${access3} extension View {`);
8884
8845
  lines.push(`${i1}func shadowStyle(_ style: ShadowStyle) -> some View {`);
@@ -8888,7 +8849,7 @@ var IosRenderer = class {
8888
8849
  lines.push(`${i1}}`);
8889
8850
  lines.push("}");
8890
8851
  }
8891
- if (this.hasTypographyTokens(tokens)) {
8852
+ if (Object.values(tokens).some(isTypographyToken)) {
8892
8853
  lines.push("");
8893
8854
  lines.push(`${access3} extension View {`);
8894
8855
  lines.push(`${i1}func typographyStyle(_ style: TypographyStyle) -> some View {`);
@@ -8920,12 +8881,12 @@ var IosRenderer = class {
8920
8881
  return `Gradient(stops: [${stops.join(", ")}])`;
8921
8882
  }
8922
8883
  async formatStandalone(context, options) {
8923
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8924
- if (!context.output.file && requiresFile) {
8925
- throw new exports.ConfigurationError(
8926
- `Output "${context.output.name}": file is required for standalone iOS output`
8927
- );
8928
- }
8884
+ assertFileRequired(
8885
+ context.buildPath,
8886
+ context.output.file,
8887
+ context.output.name,
8888
+ "standalone iOS"
8889
+ );
8929
8890
  const files = {};
8930
8891
  for (const { tokens, modifierInputs } of context.permutations) {
8931
8892
  const processedTokens = stripInternalMetadata(tokens);
@@ -8954,7 +8915,6 @@ function iosRenderer() {
8954
8915
 
8955
8916
  // src/renderers/js-module.ts
8956
8917
  init_utils();
8957
- init_errors();
8958
8918
  init_token_utils();
8959
8919
  var JsModuleRenderer = class {
8960
8920
  async format(context, options) {
@@ -8970,18 +8930,13 @@ var JsModuleRenderer = class {
8970
8930
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8971
8931
  tokens: stripInternalMetadata(tokens),
8972
8932
  modifierInputs,
8973
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
8933
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8974
8934
  }));
8975
8935
  return await bundleAsJsModule2(bundleData, context.resolver, opts, async (tokens) => {
8976
8936
  return await this.formatTokens(tokens, opts);
8977
8937
  });
8978
8938
  }
8979
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8980
- if (!context.output.file && requiresFile) {
8981
- throw new exports.ConfigurationError(
8982
- `Output "${context.output.name}": file is required for JS module output`
8983
- );
8984
- }
8939
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JS module");
8985
8940
  const files = {};
8986
8941
  for (const { tokens, modifierInputs } of context.permutations) {
8987
8942
  const cleanTokens = stripInternalMetadata(tokens);
@@ -9035,42 +8990,18 @@ var JsModuleRenderer = class {
9035
8990
  lines.push(`export default ${varName}`);
9036
8991
  return lines;
9037
8992
  }
9038
- /**
9039
- * Convert tokens to plain object with flat or nested structure
9040
- */
9041
8993
  tokensToPlainObject(tokens, structure) {
8994
+ if (structure === "nested") {
8995
+ return buildNestedTokenObject(tokens, (token) => token.$value);
8996
+ }
9042
8997
  const result = {};
9043
- if (structure === "flat") {
9044
- for (const [name, token] of getSortedTokenEntries(tokens)) {
9045
- result[name] = token.$value;
9046
- }
9047
- } else {
9048
- for (const [, token] of getSortedTokenEntries(tokens)) {
9049
- const parts = token.path;
9050
- let current = result;
9051
- for (let i = 0; i < parts.length - 1; i++) {
9052
- const part = parts[i];
9053
- if (part == null) {
9054
- continue;
9055
- }
9056
- if (!(part in current)) {
9057
- current[part] = {};
9058
- }
9059
- current = current[part];
9060
- }
9061
- const lastPart = parts[parts.length - 1];
9062
- if (lastPart != null) {
9063
- current[lastPart] = token.$value;
9064
- }
9065
- }
8998
+ for (const [name, token] of getSortedTokenEntries(tokens)) {
8999
+ result[name] = token.$value;
9066
9000
  }
9067
9001
  return result;
9068
9002
  }
9069
- /**
9070
- * Add object properties to lines
9071
- */
9072
- addObjectProperties(lines, obj, indent2) {
9073
- const indentStr = " ".repeat(indent2);
9003
+ addObjectProperties(lines, obj, indent) {
9004
+ const indentStr2 = " ".repeat(indent);
9074
9005
  const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
9075
9006
  for (let i = 0; i < entries.length; i++) {
9076
9007
  const entry = entries[i];
@@ -9079,14 +9010,16 @@ var JsModuleRenderer = class {
9079
9010
  }
9080
9011
  const [key, value] = entry;
9081
9012
  const isLast = i === entries.length - 1;
9082
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
9083
- lines.push(`${indentStr}${this.quoteKey(key)}: {`);
9084
- this.addObjectProperties(lines, value, indent2 + 1);
9085
- lines.push(`${indentStr}}${isLast ? "" : ","}`);
9086
- } else {
9087
- const valueStr = JSON.stringify(value);
9088
- lines.push(`${indentStr}${this.quoteKey(key)}: ${valueStr}${isLast ? "" : ","}`);
9013
+ const isNestedObject = typeof value === "object" && value !== null && !Array.isArray(value);
9014
+ if (!isNestedObject) {
9015
+ lines.push(
9016
+ `${indentStr2}${this.quoteKey(key)}: ${JSON.stringify(value)}${isLast ? "" : ","}`
9017
+ );
9018
+ continue;
9089
9019
  }
9020
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
9021
+ this.addObjectProperties(lines, value, indent + 1);
9022
+ lines.push(`${indentStr2}}${isLast ? "" : ","}`);
9090
9023
  }
9091
9024
  }
9092
9025
  /**
@@ -9098,9 +9031,6 @@ var JsModuleRenderer = class {
9098
9031
  }
9099
9032
  return `"${key}"`;
9100
9033
  }
9101
- isBasePermutation(modifierInputs, defaults) {
9102
- return Object.entries(modifierInputs).every(([key, value]) => value === defaults[key]);
9103
- }
9104
9034
  };
9105
9035
  function jsRenderer() {
9106
9036
  const rendererInstance = new JsModuleRenderer();
@@ -9114,7 +9044,6 @@ function jsRenderer() {
9114
9044
 
9115
9045
  // src/renderers/json.ts
9116
9046
  init_utils();
9117
- init_errors();
9118
9047
  init_token_utils();
9119
9048
  var JsonRenderer = class {
9120
9049
  async format(context, options) {
@@ -9129,18 +9058,13 @@ var JsonRenderer = class {
9129
9058
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9130
9059
  tokens: stripInternalMetadata(tokens),
9131
9060
  modifierInputs,
9132
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9061
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9133
9062
  }));
9134
9063
  return await bundleAsJson2(bundleData, context.resolver, async (tokens) => {
9135
9064
  return await this.formatTokens(tokens, opts);
9136
9065
  });
9137
9066
  }
9138
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
9139
- if (!context.output.file && requiresFile) {
9140
- throw new exports.ConfigurationError(
9141
- `Output "${context.output.name}": file is required for JSON output`
9142
- );
9143
- }
9067
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JSON");
9144
9068
  const files = {};
9145
9069
  for (const { tokens, modifierInputs } of context.permutations) {
9146
9070
  const processedTokens = stripInternalMetadata(tokens);
@@ -9200,55 +9124,11 @@ var JsonRenderer = class {
9200
9124
  }
9201
9125
  return result;
9202
9126
  }
9203
- /**
9204
- * Nest tokens by path (values only)
9205
- */
9206
9127
  nestValues(tokens) {
9207
- const result = {};
9208
- for (const [, token] of getSortedTokenEntries(tokens)) {
9209
- const parts = token.path;
9210
- let current = result;
9211
- for (let i = 0; i < parts.length - 1; i++) {
9212
- const part = parts[i];
9213
- if (part === null || part === void 0) {
9214
- continue;
9215
- }
9216
- if (!(part in current)) {
9217
- current[part] = {};
9218
- }
9219
- current = current[part];
9220
- }
9221
- const lastPart = parts[parts.length - 1];
9222
- if (lastPart !== null && lastPart !== void 0) {
9223
- current[lastPart] = token.$value;
9224
- }
9225
- }
9226
- return result;
9128
+ return buildNestedTokenObject(tokens, (token) => token.$value);
9227
9129
  }
9228
- /**
9229
- * Nest tokens by path (with metadata)
9230
- */
9231
9130
  nestTokens(tokens) {
9232
- const result = {};
9233
- for (const [, token] of getSortedTokenEntries(tokens)) {
9234
- const parts = token.path;
9235
- let current = result;
9236
- for (let i = 0; i < parts.length - 1; i++) {
9237
- const part = parts[i];
9238
- if (part === null || part === void 0) {
9239
- continue;
9240
- }
9241
- if (!(part in current)) {
9242
- current[part] = {};
9243
- }
9244
- current = current[part];
9245
- }
9246
- const lastPart = parts[parts.length - 1];
9247
- if (lastPart !== null && lastPart !== void 0) {
9248
- current[lastPart] = this.serializeToken(token);
9249
- }
9250
- }
9251
- return result;
9131
+ return buildNestedTokenObject(tokens, (token) => this.serializeToken(token));
9252
9132
  }
9253
9133
  serializeToken(token) {
9254
9134
  return {
@@ -9259,9 +9139,6 @@ var JsonRenderer = class {
9259
9139
  ...token.$extensions != null && { $extensions: token.$extensions }
9260
9140
  };
9261
9141
  }
9262
- isBasePermutation(modifierInputs, defaults) {
9263
- return Object.entries(modifierInputs).every(([key, value]) => value === defaults[key]);
9264
- }
9265
9142
  };
9266
9143
  function jsonRenderer() {
9267
9144
  const rendererInstance = new JsonRenderer();
@@ -9274,7 +9151,6 @@ function jsonRenderer() {
9274
9151
  }
9275
9152
 
9276
9153
  // src/renderers/tailwind.ts
9277
- init_errors();
9278
9154
  init_token_utils();
9279
9155
 
9280
9156
  // src/renderers/bundlers/tailwind.ts
@@ -9303,6 +9179,13 @@ async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOv
9303
9179
  }
9304
9180
  return cssBlocks.join("\n");
9305
9181
  }
9182
+ function resolveModifierSelectorAndMedia(options, modifier, context, modifierInputs) {
9183
+ const normalized = normalizeModifierInputs(modifierInputs);
9184
+ return {
9185
+ selector: resolveSelector(options.selector, modifier, context, false, normalized),
9186
+ mediaQuery: resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized)
9187
+ };
9188
+ }
9306
9189
  async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
9307
9190
  const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
9308
9191
  if (differenceCount > 1) {
@@ -9315,19 +9198,11 @@ async function formatModifierOverride({ tokens, modifierInputs }, baseItem, opti
9315
9198
  const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
9316
9199
  const [modifier, context] = parseModifierSource(expectedSource);
9317
9200
  const cleanTokens = stripInternalMetadata(tokensToInclude);
9318
- const selector = resolveSelector(
9319
- options.selector,
9201
+ const { selector, mediaQuery } = resolveModifierSelectorAndMedia(
9202
+ options,
9320
9203
  modifier,
9321
9204
  context,
9322
- false,
9323
- normalizeModifierInputs(modifierInputs)
9324
- );
9325
- const mediaQuery = resolveMediaQuery(
9326
- options.mediaQuery,
9327
- modifier,
9328
- context,
9329
- false,
9330
- normalizeModifierInputs(modifierInputs)
9205
+ modifierInputs
9331
9206
  );
9332
9207
  const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
9333
9208
  return `/* Modifier: ${modifier}=${context} */
@@ -9416,7 +9291,7 @@ var TailwindRenderer = class {
9416
9291
  */
9417
9292
  async formatTokens(tokens, options) {
9418
9293
  const lines = [];
9419
- const indent2 = options.minify ? "" : " ";
9294
+ const indent = options.minify ? "" : " ";
9420
9295
  const newline = options.minify ? "" : "\n";
9421
9296
  const space = options.minify ? "" : " ";
9422
9297
  if (options.includeImport) {
@@ -9438,7 +9313,7 @@ var TailwindRenderer = class {
9438
9313
  for (const [, token] of getSortedTokenEntries(tokens)) {
9439
9314
  const varName = this.buildVariableName(token);
9440
9315
  const varValue = this.formatValue(token);
9441
- lines.push(`${indent2}--${varName}:${space}${varValue};${newline}`);
9316
+ lines.push(`${indent}--${varName}:${space}${varValue};${newline}`);
9442
9317
  }
9443
9318
  lines.push(`}${newline}`);
9444
9319
  const cssString = lines.join("");
@@ -9449,15 +9324,15 @@ var TailwindRenderer = class {
9449
9324
  * Used for modifier overrides (e.g., dark mode) appended after the @theme block.
9450
9325
  */
9451
9326
  async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
9452
- const indent2 = minify ? "" : " ";
9327
+ const indent = minify ? "" : " ";
9453
9328
  const newline = minify ? "" : "\n";
9454
9329
  const space = minify ? "" : " ";
9455
9330
  const hasMediaQuery = mediaQuery !== "";
9456
- const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
9331
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
9457
9332
  const lines = [];
9458
9333
  if (hasMediaQuery) {
9459
9334
  lines.push(`@media ${mediaQuery}${space}{${newline}`);
9460
- lines.push(`${indent2}${selector}${space}{${newline}`);
9335
+ lines.push(`${indent}${selector}${space}{${newline}`);
9461
9336
  } else {
9462
9337
  lines.push(`${selector}${space}{${newline}`);
9463
9338
  }
@@ -9467,7 +9342,7 @@ var TailwindRenderer = class {
9467
9342
  lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
9468
9343
  }
9469
9344
  if (hasMediaQuery) {
9470
- lines.push(`${indent2}}${newline}`);
9345
+ lines.push(`${indent}}${newline}`);
9471
9346
  lines.push(`}${newline}`);
9472
9347
  } else {
9473
9348
  lines.push(`}${newline}`);
@@ -9494,8 +9369,8 @@ var TailwindRenderer = class {
9494
9369
  if (token.$type === "dimension" && isDimensionObject(value)) {
9495
9370
  return dimensionObjectToString(value);
9496
9371
  }
9497
- if (token.$type === "duration" && this.isDurationObject(value)) {
9498
- return `${value.value}${value.unit}`;
9372
+ if (token.$type === "duration" && isDurationObject(value)) {
9373
+ return durationObjectToString(value);
9499
9374
  }
9500
9375
  if (token.$type === "fontFamily") {
9501
9376
  if (Array.isArray(value)) {
@@ -9550,9 +9425,6 @@ var TailwindRenderer = class {
9550
9425
  }
9551
9426
  return parts.join(" ");
9552
9427
  }
9553
- isDurationObject(value) {
9554
- return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
9555
- }
9556
9428
  async formatWithPrettier(css2) {
9557
9429
  try {
9558
9430
  return await prettier__default.default.format(css2, {
@@ -9569,7 +9441,7 @@ var TailwindRenderer = class {
9569
9441
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9570
9442
  tokens,
9571
9443
  modifierInputs,
9572
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9444
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9573
9445
  }));
9574
9446
  return await bundleAsTailwind(
9575
9447
  bundleData,
@@ -9579,12 +9451,12 @@ var TailwindRenderer = class {
9579
9451
  );
9580
9452
  }
9581
9453
  async formatStandalone(context, options) {
9582
- const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
9583
- if (!context.output.file && requiresFile) {
9584
- throw new exports.ConfigurationError(
9585
- `Output "${context.output.name}": file is required for standalone Tailwind output`
9586
- );
9587
- }
9454
+ assertFileRequired(
9455
+ context.buildPath,
9456
+ context.output.file,
9457
+ context.output.name,
9458
+ "standalone Tailwind"
9459
+ );
9588
9460
  const files = {};
9589
9461
  for (const { tokens, modifierInputs } of context.permutations) {
9590
9462
  const processedTokens = stripInternalMetadata(tokens);
@@ -9600,11 +9472,6 @@ var TailwindRenderer = class {
9600
9472
  }
9601
9473
  return outputTree(files);
9602
9474
  }
9603
- isBasePermutation(modifierInputs, defaults) {
9604
- return Object.entries(defaults).every(
9605
- ([key, value]) => modifierInputs[key]?.toLowerCase() === value.toLowerCase()
9606
- );
9607
- }
9608
9475
  };
9609
9476
  function tailwindRenderer() {
9610
9477
  const rendererInstance = new TailwindRenderer();
@@ -9632,7 +9499,7 @@ function css(config) {
9632
9499
  file,
9633
9500
  renderer: cssRenderer(),
9634
9501
  options: { preset, ...rendererOptions },
9635
- transforms,
9502
+ transforms: [nameKebabCase(), ...transforms ?? []],
9636
9503
  filters,
9637
9504
  hooks
9638
9505
  };
@@ -9737,11 +9604,6 @@ function defineRenderer(renderer) {
9737
9604
 
9738
9605
  // src/index.ts
9739
9606
  init_errors();
9740
- /**
9741
- * @license
9742
- * Copyright (c) 2025 Dispersa Contributors
9743
- * SPDX-License-Identifier: MIT
9744
- */
9745
9607
  /**
9746
9608
  * @license MIT
9747
9609
  * Copyright (c) 2025-present Dispersa Contributors
@@ -9749,6 +9611,11 @@ init_errors();
9749
9611
  * This source code is licensed under the MIT license found in the
9750
9612
  * LICENSE file in the root directory of this source tree.
9751
9613
  */
9614
+ /**
9615
+ * @license
9616
+ * Copyright (c) 2025 Dispersa Contributors
9617
+ * SPDX-License-Identifier: MIT
9618
+ */
9752
9619
 
9753
9620
  exports.Dispersa = Dispersa;
9754
9621
  exports.android = android;