dispersa 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -44,47 +44,6 @@ var __export = (target, all) => {
44
44
  __defProp(target, name, { get: all[name], enumerable: true });
45
45
  };
46
46
 
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
47
  // src/shared/errors/index.ts
89
48
  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
49
  var init_errors = __esm({
@@ -172,6 +131,70 @@ var init_errors = __esm({
172
131
  }
173
132
  });
174
133
 
134
+ // src/shared/utils/token-utils.ts
135
+ function formatDeprecationMessage(token, description = "", format = "bracket") {
136
+ if (token.$deprecated == null || token.$deprecated === false) {
137
+ return description;
138
+ }
139
+ const deprecationMsg = typeof token.$deprecated === "string" ? token.$deprecated : "";
140
+ if (format === "comment") {
141
+ const msg2 = deprecationMsg ? ` ${deprecationMsg}` : "";
142
+ return `DEPRECATED${msg2}`;
143
+ }
144
+ const msg = deprecationMsg ? `: ${deprecationMsg}` : "";
145
+ const prefix = `[DEPRECATED${msg}]`;
146
+ return description ? `${prefix} ${description}` : prefix;
147
+ }
148
+ function stripInternalTokenMetadata(tokens) {
149
+ const cleaned = {};
150
+ for (const [name, token] of Object.entries(tokens)) {
151
+ const { _isAlias: _alias, _sourceModifier: _source, _sourceSet, ...rest } = token;
152
+ cleaned[name] = rest;
153
+ }
154
+ return cleaned;
155
+ }
156
+ function getSortedTokenEntries(tokens) {
157
+ return Object.entries(tokens).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
158
+ }
159
+ function buildNestedTokenObject(tokens, extractValue) {
160
+ const result = {};
161
+ for (const [, token] of getSortedTokenEntries(tokens)) {
162
+ setNestedValue(result, token.path, extractValue(token));
163
+ }
164
+ return result;
165
+ }
166
+ function setNestedValue(root, path7, value) {
167
+ let current = root;
168
+ for (let i = 0; i < path7.length - 1; i++) {
169
+ const part = path7[i];
170
+ if (part == null) {
171
+ continue;
172
+ }
173
+ if (!(part in current)) {
174
+ current[part] = {};
175
+ }
176
+ current = current[part];
177
+ }
178
+ const lastPart = path7[path7.length - 1];
179
+ if (lastPart != null) {
180
+ current[lastPart] = value;
181
+ }
182
+ }
183
+ function isTokenLike(value) {
184
+ return typeof value === "object" && value !== null && ("$value" in value || "$ref" in value);
185
+ }
186
+ function getPureAliasReferenceName(value) {
187
+ if (typeof value !== "string") {
188
+ return void 0;
189
+ }
190
+ const match = /^\{([^}]+)\}$/.exec(value);
191
+ return match?.[1]?.trim();
192
+ }
193
+ var init_token_utils = __esm({
194
+ "src/shared/utils/token-utils.ts"() {
195
+ }
196
+ });
197
+
175
198
  // src/shared/utils/validation-handler.ts
176
199
  var ValidationHandler;
177
200
  var init_validation_handler = __esm({
@@ -2911,30 +2934,29 @@ var init_validator = __esm({
2911
2934
  validateTokenOrGroup(obj) {
2912
2935
  const hasValue = isTokenLike(obj);
2913
2936
  if (hasValue) {
2914
- const tokenErrors = this.validateToken(obj);
2915
- if (tokenErrors.length === 0) {
2937
+ const tokenErrors2 = this.validateToken(obj);
2938
+ if (tokenErrors2.length === 0) {
2916
2939
  return { type: "token", errors: [] };
2917
2940
  }
2918
2941
  return {
2919
2942
  type: "invalid",
2920
- errors: tokenErrors,
2943
+ errors: tokenErrors2,
2921
2944
  message: "Object has $value/$ref but failed token validation"
2922
2945
  };
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
2946
  }
2947
+ const groupErrors = this.validateGroup(obj);
2948
+ if (groupErrors.length === 0) {
2949
+ return { type: "group", errors: [] };
2950
+ }
2951
+ const tokenErrors = this.validateToken(obj);
2952
+ if (tokenErrors.length === 0) {
2953
+ return { type: "token", errors: [] };
2954
+ }
2955
+ return {
2956
+ type: "invalid",
2957
+ errors: groupErrors.length < tokenErrors.length ? groupErrors : tokenErrors,
2958
+ message: groupErrors.length < tokenErrors.length ? "Object appears to be a group but failed validation" : "Object appears to be a token but failed validation"
2959
+ };
2938
2960
  }
2939
2961
  /**
2940
2962
  * Format AJV errors into readable ValidationError objects
@@ -3129,35 +3151,34 @@ var init_resolver_parser = __esm({
3129
3151
  this.validateSourceReferences(set.sources, `set "${setName}"`, contextMsg);
3130
3152
  }
3131
3153
  }
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
- }
3154
+ if (!resolver.modifiers) {
3155
+ return;
3156
+ }
3157
+ for (const [modifierName, modifier] of Object.entries(resolver.modifiers)) {
3158
+ for (const [contextName, sources] of Object.entries(modifier.contexts)) {
3159
+ this.validateSourceReferences(
3160
+ sources,
3161
+ `modifier "${modifierName}" context "${contextName}"`,
3162
+ contextMsg
3163
+ );
3141
3164
  }
3142
3165
  }
3143
3166
  }
3144
- /**
3145
- * Validate source references in an array
3146
- */
3147
3167
  validateSourceReferences(sources, location, contextMsg) {
3148
3168
  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
- }
3169
+ if (typeof source !== "object" || source === null || !("$ref" in source)) {
3170
+ continue;
3171
+ }
3172
+ const ref = source.$ref;
3173
+ if (ref.startsWith("#/modifiers/")) {
3174
+ this.handleValidationIssue(
3175
+ `Invalid reference in ${location}: "${ref}". Sets and modifier contexts MUST NOT reference modifiers${contextMsg}`
3176
+ );
3177
+ }
3178
+ if (ref.startsWith("#/resolutionOrder/")) {
3179
+ this.handleValidationIssue(
3180
+ `Invalid reference in ${location}: "${ref}". References MUST NOT point to resolutionOrder array items${contextMsg}`
3181
+ );
3161
3182
  }
3162
3183
  }
3163
3184
  }
@@ -3237,15 +3258,14 @@ var init_resolver_loader = __esm({
3237
3258
  * ```
3238
3259
  */
3239
3260
  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 };
3261
+ if (typeof resolver !== "string") {
3262
+ const resolverDoc2 = this.parser.parseInline(resolver);
3263
+ return { resolverDoc: resolverDoc2, baseDir: this.options.baseDir };
3248
3264
  }
3265
+ const absolutePath = path__namespace.resolve(this.options.baseDir, resolver);
3266
+ const resolverDoc = await this.parser.parseFile(absolutePath);
3267
+ const baseDir = path__namespace.dirname(absolutePath);
3268
+ return { resolverDoc, baseDir };
3249
3269
  }
3250
3270
  /**
3251
3271
  * Load only the resolver document (without base directory)
@@ -3278,6 +3298,35 @@ function sanitizeDataAttributeName(value) {
3278
3298
  function escapeCssString(value) {
3279
3299
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, " ");
3280
3300
  }
3301
+ function groupTokensByType(tokens, typeGroupMap) {
3302
+ const groupMap = /* @__PURE__ */ new Map();
3303
+ for (const [, token] of getSortedTokenEntries(tokens)) {
3304
+ const groupName = typeGroupMap[token.$type ?? ""] ?? "Other";
3305
+ const existing = groupMap.get(groupName) ?? [];
3306
+ existing.push(token);
3307
+ groupMap.set(groupName, existing);
3308
+ }
3309
+ return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
3310
+ name,
3311
+ tokens: groupTokens
3312
+ }));
3313
+ }
3314
+ function indentStr(width, level) {
3315
+ return " ".repeat(width * level);
3316
+ }
3317
+ function buildGeneratedFileHeader() {
3318
+ return [
3319
+ "// Generated by Dispersa - do not edit manually",
3320
+ "// https://github.com/timges/dispersa"
3321
+ ].join("\n");
3322
+ }
3323
+ function toSafeIdentifier(name, keywords, capitalize) {
3324
+ const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
3325
+ const cased = capitalize ? camel.charAt(0).toUpperCase() + camel.slice(1) : camel.charAt(0).toLowerCase() + camel.slice(1);
3326
+ const safe = /^\d/.test(cased) ? `_${cased}` : cased;
3327
+ const keyCheck = capitalize ? safe.charAt(0).toLowerCase() + safe.slice(1) : safe;
3328
+ return keywords.has(keyCheck) ? `\`${safe}\`` : safe;
3329
+ }
3281
3330
  function normalizeModifierInputs(inputs) {
3282
3331
  const normalized = {};
3283
3332
  for (const [key, value] of Object.entries(inputs)) {
@@ -3285,6 +3334,14 @@ function normalizeModifierInputs(inputs) {
3285
3334
  }
3286
3335
  return normalized;
3287
3336
  }
3337
+ function assertFileRequired(buildPath, outputFile, outputName, presetLabel) {
3338
+ const requiresFile = buildPath !== void 0 && buildPath !== "";
3339
+ if (!outputFile && requiresFile) {
3340
+ throw new exports.ConfigurationError(
3341
+ `Output "${outputName}": file is required for ${presetLabel} output`
3342
+ );
3343
+ }
3344
+ }
3288
3345
  function buildStablePermutationKey(modifierInputs, dimensions) {
3289
3346
  return dimensions.map((dimension) => `${dimension}=${modifierInputs[dimension] ?? ""}`).join("|");
3290
3347
  }
@@ -3334,14 +3391,18 @@ function generatePermutationKey(modifierInputs, resolver, isBase) {
3334
3391
  }
3335
3392
  return buildStablePermutationKey(normalizedInputs, metadata.dimensions);
3336
3393
  }
3337
- function buildInMemoryOutputKey(params) {
3338
- const { outputName, extension, modifierInputs, resolver, defaults } = params;
3394
+ function isBasePermutation(modifierInputs, defaults) {
3339
3395
  const normalizedInputs = normalizeModifierInputs(modifierInputs);
3340
3396
  const normalizedDefaults = normalizeModifierInputs(defaults);
3341
- const isBase = Object.entries(normalizedDefaults).every(
3342
- ([key, value]) => normalizedInputs[key] === value
3397
+ return Object.entries(normalizedDefaults).every(([key, value]) => normalizedInputs[key] === value);
3398
+ }
3399
+ function buildInMemoryOutputKey(params) {
3400
+ const { outputName, extension, modifierInputs, resolver, defaults } = params;
3401
+ const permutationKey = generatePermutationKey(
3402
+ modifierInputs,
3403
+ resolver,
3404
+ isBasePermutation(modifierInputs, defaults)
3343
3405
  );
3344
- const permutationKey = generatePermutationKey(modifierInputs, resolver, isBase);
3345
3406
  return `${outputName}-${permutationKey}.${extension}`;
3346
3407
  }
3347
3408
  function buildMetadata(resolver) {
@@ -3463,6 +3524,7 @@ function resolveFileName(fileName, modifierInputs) {
3463
3524
  }
3464
3525
  var init_utils = __esm({
3465
3526
  "src/renderers/bundlers/utils.ts"() {
3527
+ init_errors();
3466
3528
  init_token_utils();
3467
3529
  }
3468
3530
  });
@@ -3472,36 +3534,38 @@ var js_exports = {};
3472
3534
  __export(js_exports, {
3473
3535
  bundleAsJsModule: () => bundleAsJsModule
3474
3536
  });
3537
+ function updateStringTracking(state, char) {
3538
+ if (!state.escaped && (char === '"' || char === "'" || char === "`")) {
3539
+ if (!state.inString) {
3540
+ state.inString = true;
3541
+ state.stringChar = char;
3542
+ } else if (char === state.stringChar) {
3543
+ state.inString = false;
3544
+ state.stringChar = "";
3545
+ }
3546
+ }
3547
+ state.escaped = !state.escaped && char === "\\";
3548
+ }
3475
3549
  function extractObjectFromJsModule(formattedJs) {
3476
3550
  const assignmentMatch = /const\s+\w+\s*=\s*\{/.exec(formattedJs);
3477
3551
  if (!assignmentMatch) {
3478
3552
  return "{}";
3479
3553
  }
3480
3554
  const startIndex = assignmentMatch.index + assignmentMatch[0].length - 1;
3555
+ const state = { inString: false, stringChar: "", escaped: false };
3481
3556
  let braceCount = 0;
3482
- let inString = false;
3483
- let stringChar = "";
3484
- let escaped = false;
3485
3557
  for (let i = startIndex; i < formattedJs.length; i++) {
3486
3558
  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
- }
3559
+ updateStringTracking(state, char);
3560
+ if (state.inString) {
3561
+ continue;
3562
+ }
3563
+ if (char === "{") {
3564
+ braceCount++;
3565
+ } else if (char === "}") {
3566
+ braceCount--;
3567
+ if (braceCount === 0) {
3568
+ return formattedJs.substring(startIndex, i + 1);
3505
3569
  }
3506
3570
  }
3507
3571
  }
@@ -3598,22 +3662,19 @@ __export(json_exports, {
3598
3662
  bundleAsJson: () => bundleAsJson
3599
3663
  });
3600
3664
  async function bundleAsJson(bundleData, resolver, formatTokens) {
3665
+ if (!formatTokens) {
3666
+ throw new exports.ConfigurationError("JSON formatter was not provided");
3667
+ }
3601
3668
  const metadata = buildMetadata(resolver);
3602
3669
  const tokens = {};
3603
3670
  for (const { tokens: tokenSet, modifierInputs } of bundleData) {
3604
3671
  const cleanTokens = stripInternalMetadata(tokenSet);
3605
- if (!formatTokens) {
3606
- throw new exports.ConfigurationError("JSON formatter was not provided");
3607
- }
3608
3672
  const normalizedInputs = normalizeModifierInputs(modifierInputs);
3609
3673
  const key = buildStablePermutationKey(normalizedInputs, metadata.dimensions);
3610
3674
  const themeJson = await formatTokens(cleanTokens);
3611
3675
  tokens[key] = JSON.parse(themeJson);
3612
3676
  }
3613
- const bundle = {
3614
- _meta: metadata,
3615
- tokens
3616
- };
3677
+ const bundle = { _meta: metadata, tokens };
3617
3678
  return JSON.stringify(bundle, null, 2);
3618
3679
  }
3619
3680
  var init_json = __esm({
@@ -3679,15 +3740,15 @@ var TypeGenerator = class {
3679
3740
  const lines = [];
3680
3741
  if (names.length === 0) {
3681
3742
  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}"`);
3743
+ return lines;
3744
+ }
3745
+ lines.push(`export type ${typeName} =`);
3746
+ for (let i = 0; i < names.length; i++) {
3747
+ const name = names[i];
3748
+ if (name == null) {
3749
+ continue;
3690
3750
  }
3751
+ lines.push(` | "${name}"`);
3691
3752
  }
3692
3753
  return lines;
3693
3754
  }
@@ -3713,15 +3774,10 @@ var TypeGenerator = class {
3713
3774
  generateStructureType(tokens, options) {
3714
3775
  const lines = [];
3715
3776
  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
- }
3777
+ const opener = options.exportType === "type" ? `export type ${options.moduleName} = {` : `export interface ${options.moduleName} {`;
3778
+ lines.push(opener);
3779
+ this.addStructureProperties(lines, structure, 1);
3780
+ lines.push("}");
3725
3781
  return lines;
3726
3782
  }
3727
3783
  /**
@@ -3755,20 +3811,20 @@ var TypeGenerator = class {
3755
3811
  /**
3756
3812
  * Add structure properties to lines
3757
3813
  */
3758
- addStructureProperties(lines, structure, indent2) {
3759
- const indentStr = " ".repeat(indent2);
3814
+ addStructureProperties(lines, structure, indent) {
3815
+ const indentStr2 = " ".repeat(indent);
3760
3816
  for (const [key, value] of Object.entries(structure)) {
3761
3817
  if (this.isToken(value)) {
3762
3818
  const token = value;
3763
3819
  if (token.$description) {
3764
- lines.push(`${indentStr}/** ${token.$description} */`);
3820
+ lines.push(`${indentStr2}/** ${token.$description} */`);
3765
3821
  }
3766
3822
  const valueType = this.inferValueType(token);
3767
- lines.push(`${indentStr}${this.quoteKey(key)}: ${valueType}`);
3823
+ lines.push(`${indentStr2}${this.quoteKey(key)}: ${valueType}`);
3768
3824
  } else {
3769
- lines.push(`${indentStr}${this.quoteKey(key)}: {`);
3770
- this.addStructureProperties(lines, value, indent2 + 1);
3771
- lines.push(`${indentStr}}`);
3825
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
3826
+ this.addStructureProperties(lines, value, indent + 1);
3827
+ lines.push(`${indentStr2}}`);
3772
3828
  }
3773
3829
  }
3774
3830
  }
@@ -3960,28 +4016,27 @@ var BuildOrchestrator = class {
3960
4016
  if (config.hooks?.onBuildStart) {
3961
4017
  await config.hooks.onBuildStart({ config, resolver });
3962
4018
  }
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(
4019
+ if (!config.permutations || config.permutations.length === 0) {
4020
+ const permutations2 = await this.pipeline.resolveAllPermutations(
3979
4021
  resolver,
3980
4022
  config.transforms,
3981
4023
  config.preprocessors,
3982
4024
  config.filters
3983
4025
  );
4026
+ return this.executeBuild(buildPath, config, permutations2, resolver);
3984
4027
  }
4028
+ const permutations = await Promise.all(
4029
+ config.permutations.map(async (modifierInputs) => {
4030
+ const { tokens, modifierInputs: resolvedInputs } = await this.pipeline.resolve(
4031
+ resolver,
4032
+ modifierInputs,
4033
+ config.transforms,
4034
+ config.preprocessors,
4035
+ config.filters
4036
+ );
4037
+ return { tokens, modifierInputs: resolvedInputs };
4038
+ })
4039
+ );
3985
4040
  return this.executeBuild(buildPath, config, permutations, resolver);
3986
4041
  }
3987
4042
  /**
@@ -4003,44 +4058,15 @@ var BuildOrchestrator = class {
4003
4058
  * @returns Build result with success status, outputs, and any errors
4004
4059
  */
4005
4060
  async executeBuild(buildPath, config, permutations, resolver) {
4006
- const outputs = [];
4007
- const errors = [];
4008
4061
  try {
4009
4062
  const resolverDoc = await resolveResolverDocument(resolver);
4010
4063
  const metadata = buildMetadata(resolverDoc);
4011
- const basePermutation = metadata.defaults;
4012
4064
  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
- })
4065
+ config.outputs.map(
4066
+ (output) => this.buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver)
4067
+ )
4030
4068
  );
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
- };
4069
+ const result = this.collectSettledResults(settled, config);
4044
4070
  if (config.hooks?.onBuildEnd) {
4045
4071
  await config.hooks.onBuildEnd(result);
4046
4072
  }
@@ -4057,6 +4083,40 @@ var BuildOrchestrator = class {
4057
4083
  return result;
4058
4084
  }
4059
4085
  }
4086
+ async buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver) {
4087
+ if (output.hooks?.onBuildStart) {
4088
+ await output.hooks.onBuildStart({ config, resolver });
4089
+ }
4090
+ try {
4091
+ const results = await this.processOutput(output, permutations, resolverDoc, metadata, metadata.defaults, buildPath);
4092
+ if (output.hooks?.onBuildEnd) {
4093
+ await output.hooks.onBuildEnd({ success: true, outputs: results });
4094
+ }
4095
+ return results;
4096
+ } catch (error) {
4097
+ if (output.hooks?.onBuildEnd) {
4098
+ await output.hooks.onBuildEnd({ success: false, outputs: [], errors: [toBuildError(error, output.name)] });
4099
+ }
4100
+ throw error;
4101
+ }
4102
+ }
4103
+ collectSettledResults(settled, config) {
4104
+ const outputs = [];
4105
+ const errors = [];
4106
+ for (let i = 0; i < settled.length; i++) {
4107
+ const outcome = settled[i];
4108
+ if (outcome.status === "fulfilled") {
4109
+ outputs.push(...outcome.value);
4110
+ } else {
4111
+ errors.push(toBuildError(outcome.reason, config.outputs[i].name));
4112
+ }
4113
+ }
4114
+ return {
4115
+ success: errors.length === 0,
4116
+ outputs,
4117
+ errors: errors.length > 0 ? errors : void 0
4118
+ };
4119
+ }
4060
4120
  async processOutput(output, permutations, resolverDoc, metadata, basePermutation, buildPath) {
4061
4121
  if (!output.renderer.format) {
4062
4122
  throw new exports.ConfigurationError("Renderer does not implement format()");
@@ -4106,7 +4166,7 @@ async function writeOutputFile(fileName, content, encoding = "utf-8") {
4106
4166
  }
4107
4167
  }
4108
4168
 
4109
- // src/processing/token-modifier.ts
4169
+ // src/processing/apply.ts
4110
4170
  function applyTransforms(tokens, transformList) {
4111
4171
  const result = {};
4112
4172
  for (const [name, token] of Object.entries(tokens)) {
@@ -5384,14 +5444,15 @@ var ResolutionEngine = class {
5384
5444
  mergeTokens(target, source) {
5385
5445
  const result = { ...target };
5386
5446
  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 {
5447
+ const isGroup = typeof value === "object" && value !== null && !Array.isArray(value) && !("$value" in value);
5448
+ if (!isGroup) {
5393
5449
  result[key] = value;
5450
+ continue;
5394
5451
  }
5452
+ result[key] = this.mergeTokens(
5453
+ result[key] ?? {},
5454
+ value
5455
+ );
5395
5456
  }
5396
5457
  return result;
5397
5458
  }
@@ -6537,7 +6598,7 @@ function colorObjectToHex(color) {
6537
6598
  return culori.formatHex(culoriColor);
6538
6599
  }
6539
6600
 
6540
- // src/processing/processors/transforms/built-in/dimension-converter.ts
6601
+ // src/processing/transforms/built-in/dimension-converter.ts
6541
6602
  function isDimensionObject(value) {
6542
6603
  return typeof value === "object" && value !== null && "value" in value && "unit" in value;
6543
6604
  }
@@ -6545,6 +6606,14 @@ function dimensionObjectToString(dimension) {
6545
6606
  return `${dimension.value}${dimension.unit}`;
6546
6607
  }
6547
6608
 
6609
+ // src/processing/transforms/built-in/duration-converter.ts
6610
+ function isDurationObject(value) {
6611
+ return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
6612
+ }
6613
+ function durationObjectToString(duration) {
6614
+ return `${duration.value}${duration.unit}`;
6615
+ }
6616
+
6548
6617
  // src/renderers/android.ts
6549
6618
  init_errors();
6550
6619
  init_token_utils();
@@ -6596,9 +6665,6 @@ function resolveColorFormat(format) {
6596
6665
  }
6597
6666
  return "argb_hex";
6598
6667
  }
6599
- function indent(width, level) {
6600
- return " ".repeat(width * level);
6601
- }
6602
6668
  function escapeKotlinString(str) {
6603
6669
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
6604
6670
  }
@@ -6614,22 +6680,6 @@ function roundComponent(value) {
6614
6680
  function toResourceName(family) {
6615
6681
  return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
6616
6682
  }
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
6683
  var AndroidRenderer = class {
6634
6684
  async format(context, options) {
6635
6685
  if (!options?.packageName) {
@@ -6637,6 +6687,7 @@ var AndroidRenderer = class {
6637
6687
  `Output "${context.output.name}": packageName is required for Android output`
6638
6688
  );
6639
6689
  }
6690
+ const visibility = options?.visibility;
6640
6691
  const opts = {
6641
6692
  preset: options?.preset ?? "standalone",
6642
6693
  packageName: options.packageName,
@@ -6644,7 +6695,8 @@ var AndroidRenderer = class {
6644
6695
  colorFormat: resolveColorFormat(options?.colorFormat),
6645
6696
  colorSpace: options?.colorSpace ?? "sRGB",
6646
6697
  structure: options?.structure ?? "nested",
6647
- visibility: options?.visibility,
6698
+ visibility,
6699
+ visPrefix: visibility ? `${visibility} ` : "",
6648
6700
  indent: options?.indent ?? 4
6649
6701
  };
6650
6702
  if (opts.preset === "bundle") {
@@ -6677,19 +6729,6 @@ var AndroidRenderer = class {
6677
6729
  // -----------------------------------------------------------------------
6678
6730
  // Flat structure grouping
6679
6731
  // -----------------------------------------------------------------------
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
6732
  /**
6694
6733
  * Builds a flattened camelCase name from a token's path, stripping the
6695
6734
  * type prefix segment (which is already represented by the group object).
@@ -6698,7 +6737,7 @@ var AndroidRenderer = class {
6698
6737
  const path7 = token.path;
6699
6738
  const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
6700
6739
  const joined = withoutTypePrefix.join("_");
6701
- return toKotlinIdentifier(joined);
6740
+ return toSafeIdentifier(joined, KOTLIN_KEYWORDS, false);
6702
6741
  }
6703
6742
  // -----------------------------------------------------------------------
6704
6743
  // Rendering
@@ -6710,22 +6749,21 @@ var AndroidRenderer = class {
6710
6749
  return this.formatAsNested(tokens, options);
6711
6750
  }
6712
6751
  formatAsNested(tokens, options) {
6752
+ const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6713
6753
  const tree = this.buildTokenTree(tokens);
6714
- const tokenTypes = /* @__PURE__ */ new Set();
6715
- this.collectTokenTypes(tree, tokenTypes);
6716
- return this.buildFile(tokenTypes, options, (lines, vis) => {
6754
+ return this.buildFile(tokenTypes, options, (lines) => {
6717
6755
  lines.push(`@Suppress("unused")`);
6718
- lines.push(`${vis}object ${options.objectName} {`);
6756
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
6719
6757
  this.renderTreeChildren(lines, tree, 1, options);
6720
6758
  lines.push("}");
6721
6759
  });
6722
6760
  }
6723
6761
  formatAsFlat(tokens, options) {
6724
- const groups = this.groupTokensByType(tokens);
6762
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
6725
6763
  const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6726
- return this.buildFile(tokenTypes, options, (lines, vis) => {
6764
+ return this.buildFile(tokenTypes, options, (lines) => {
6727
6765
  lines.push(`@Suppress("unused")`);
6728
- lines.push(`${vis}object ${options.objectName} {`);
6766
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
6729
6767
  this.renderFlatGroups(lines, groups, 1, options);
6730
6768
  lines.push("}");
6731
6769
  });
@@ -6736,9 +6774,8 @@ var AndroidRenderer = class {
6736
6774
  */
6737
6775
  buildFile(tokenTypes, options, renderBody) {
6738
6776
  const imports = this.collectImports(tokenTypes, options);
6739
- const vis = options.visibility ? `${options.visibility} ` : "";
6740
6777
  const lines = [];
6741
- lines.push(this.buildFileHeader());
6778
+ lines.push(buildGeneratedFileHeader());
6742
6779
  lines.push("");
6743
6780
  lines.push(`package ${options.packageName}`);
6744
6781
  lines.push("");
@@ -6749,19 +6786,18 @@ var AndroidRenderer = class {
6749
6786
  lines.push("");
6750
6787
  }
6751
6788
  if (tokenTypes.has("shadow")) {
6752
- lines.push(...this.buildShadowTokenClass(vis, options));
6789
+ lines.push(...this.buildShadowTokenClass(options));
6753
6790
  lines.push("");
6754
6791
  }
6755
- renderBody(lines, vis);
6792
+ renderBody(lines);
6756
6793
  lines.push("");
6757
6794
  return lines.join("\n");
6758
6795
  }
6759
6796
  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);
6797
+ const groupIndent = indentStr(options.indent, baseDepth);
6798
+ const valIndent = indentStr(options.indent, baseDepth + 1);
6763
6799
  for (const group of groups) {
6764
- lines.push(`${groupIndent}${vis}object ${group.name} {`);
6800
+ lines.push(`${groupIndent}${options.visPrefix}object ${group.name} {`);
6765
6801
  for (const token of group.tokens) {
6766
6802
  const kotlinName = this.buildFlatKotlinName(token);
6767
6803
  const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
@@ -6769,23 +6805,24 @@ var AndroidRenderer = class {
6769
6805
  if (token.$description) {
6770
6806
  lines.push(`${valIndent}/** ${escapeKDoc(token.$description)} */`);
6771
6807
  }
6772
- lines.push(`${valIndent}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6808
+ lines.push(
6809
+ `${valIndent}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`
6810
+ );
6773
6811
  }
6774
6812
  lines.push(`${groupIndent}}`);
6775
6813
  lines.push("");
6776
6814
  }
6777
6815
  }
6778
6816
  renderTreeChildren(lines, node, depth, options) {
6779
- const vis = options.visibility ? `${options.visibility} ` : "";
6780
- const pad = indent(options.indent, depth);
6817
+ const pad = indentStr(options.indent, depth);
6781
6818
  const entries = Array.from(node.children.entries());
6782
6819
  for (let idx = 0; idx < entries.length; idx++) {
6783
6820
  const [key, child] = entries[idx];
6784
6821
  if (child.token && child.children.size === 0) {
6785
6822
  this.renderLeaf(lines, key, child.token, depth, options);
6786
6823
  } else if (child.children.size > 0 && !child.token) {
6787
- const objectName = toPascalCase(key);
6788
- lines.push(`${pad}${vis}object ${objectName} {`);
6824
+ const objectName = toSafeIdentifier(key, KOTLIN_KEYWORDS, true);
6825
+ lines.push(`${pad}${options.visPrefix}object ${objectName} {`);
6789
6826
  this.renderTreeChildren(lines, child, depth + 1, options);
6790
6827
  lines.push(`${pad}}`);
6791
6828
  if (idx < entries.length - 1) {
@@ -6798,30 +6835,23 @@ var AndroidRenderer = class {
6798
6835
  }
6799
6836
  }
6800
6837
  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);
6838
+ const pad = indentStr(options.indent, depth);
6839
+ const kotlinName = toSafeIdentifier(key, KOTLIN_KEYWORDS, false);
6804
6840
  const kotlinValue = this.formatKotlinValue(token, options, depth);
6805
6841
  const annotation = this.typeAnnotationSuffix(token);
6806
6842
  if (token.$description) {
6807
6843
  lines.push(`${pad}/** ${escapeKDoc(token.$description)} */`);
6808
6844
  }
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");
6845
+ lines.push(`${pad}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`);
6816
6846
  }
6817
6847
  // -----------------------------------------------------------------------
6818
6848
  // Shadow data class
6819
6849
  // -----------------------------------------------------------------------
6820
- buildShadowTokenClass(vis, options) {
6821
- const i1 = indent(options.indent, 1);
6850
+ buildShadowTokenClass(options) {
6851
+ const i1 = indentStr(options.indent, 1);
6822
6852
  return [
6823
6853
  "@Immutable",
6824
- `${vis}data class ShadowToken(`,
6854
+ `${options.visPrefix}data class ShadowToken(`,
6825
6855
  `${i1}val color: Color,`,
6826
6856
  `${i1}val elevation: Dp,`,
6827
6857
  `${i1}val offsetX: Dp,`,
@@ -6872,14 +6902,6 @@ var AndroidRenderer = class {
6872
6902
  }
6873
6903
  return Array.from(imports).sort();
6874
6904
  }
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
6905
  collectTokenTypesFromEntries(tokens) {
6884
6906
  const types = /* @__PURE__ */ new Set();
6885
6907
  for (const [, token] of Object.entries(tokens)) {
@@ -7106,9 +7128,8 @@ var AndroidRenderer = class {
7106
7128
  return map[name.toLowerCase()];
7107
7129
  }
7108
7130
  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`;
7131
+ if (isDurationObject(value)) {
7132
+ return value.unit === "ms" ? `${value.value}.milliseconds` : `${value.value}.seconds`;
7112
7133
  }
7113
7134
  return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
7114
7135
  }
@@ -7126,8 +7147,8 @@ var AndroidRenderer = class {
7126
7147
  const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
7127
7148
  const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
7128
7149
  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);
7150
+ const propIndent = indentStr(options.indent, depth + 1);
7151
+ const closeIndent = indentStr(options.indent, depth);
7131
7152
  return [
7132
7153
  "ShadowToken(",
7133
7154
  `${propIndent}color = ${color},`,
@@ -7176,8 +7197,8 @@ var AndroidRenderer = class {
7176
7197
  if (parts.length === 0) {
7177
7198
  return "TextStyle()";
7178
7199
  }
7179
- const propIndent = indent(options.indent, depth + 1);
7180
- const closeIndent = indent(options.indent, depth);
7200
+ const propIndent = indentStr(options.indent, depth + 1);
7201
+ const closeIndent = indentStr(options.indent, depth);
7181
7202
  return `TextStyle(
7182
7203
  ${parts.map((p) => `${propIndent}${p}`).join(",\n")},
7183
7204
  ${closeIndent})`;
@@ -7186,12 +7207,12 @@ ${closeIndent})`;
7186
7207
  // Output: standalone
7187
7208
  // -----------------------------------------------------------------------
7188
7209
  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
- }
7210
+ assertFileRequired(
7211
+ context.buildPath,
7212
+ context.output.file,
7213
+ context.output.name,
7214
+ "standalone Android"
7215
+ );
7195
7216
  const files = {};
7196
7217
  for (const { tokens, modifierInputs } of context.permutations) {
7197
7218
  const processedTokens = stripInternalMetadata(tokens);
@@ -7211,12 +7232,12 @@ ${closeIndent})`;
7211
7232
  // Output: bundle
7212
7233
  // -----------------------------------------------------------------------
7213
7234
  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
- }
7235
+ assertFileRequired(
7236
+ context.buildPath,
7237
+ context.output.file,
7238
+ context.output.name,
7239
+ "bundle Android"
7240
+ );
7220
7241
  const content = this.formatBundleContent(context, options);
7221
7242
  const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
7222
7243
  outputName: context.output.name,
@@ -7229,15 +7250,15 @@ ${closeIndent})`;
7229
7250
  }
7230
7251
  formatBundleContent(context, options) {
7231
7252
  const allTokenTypes = this.collectAllPermutationTypes(context);
7232
- return this.buildFile(allTokenTypes, options, (lines, vis) => {
7233
- const i1 = indent(options.indent, 1);
7253
+ return this.buildFile(allTokenTypes, options, (lines) => {
7254
+ const i1 = indentStr(options.indent, 1);
7234
7255
  lines.push(`@Suppress("unused")`);
7235
- lines.push(`${vis}object ${options.objectName} {`);
7256
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
7236
7257
  for (let idx = 0; idx < context.permutations.length; idx++) {
7237
7258
  const { tokens, modifierInputs } = context.permutations[idx];
7238
7259
  const processedTokens = stripInternalMetadata(tokens);
7239
7260
  const permName = this.buildPermutationName(modifierInputs);
7240
- lines.push(`${i1}${vis}object ${permName} {`);
7261
+ lines.push(`${i1}${options.visPrefix}object ${permName} {`);
7241
7262
  this.renderBundleTokens(lines, processedTokens, options, 2);
7242
7263
  lines.push(`${i1}}`);
7243
7264
  if (idx < context.permutations.length - 1) {
@@ -7248,20 +7269,17 @@ ${closeIndent})`;
7248
7269
  });
7249
7270
  }
7250
7271
  collectAllPermutationTypes(context) {
7251
- const allTokenTypes = /* @__PURE__ */ new Set();
7272
+ const types = /* @__PURE__ */ new Set();
7252
7273
  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
- }
7274
+ for (const t of this.collectTokenTypesFromEntries(stripInternalMetadata(tokens))) {
7275
+ types.add(t);
7258
7276
  }
7259
7277
  }
7260
- return allTokenTypes;
7278
+ return types;
7261
7279
  }
7262
7280
  renderBundleTokens(lines, tokens, options, baseDepth) {
7263
7281
  if (options.structure === "flat") {
7264
- const groups = this.groupTokensByType(tokens);
7282
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
7265
7283
  this.renderFlatGroups(lines, groups, baseDepth, options);
7266
7284
  return;
7267
7285
  }
@@ -7273,7 +7291,7 @@ ${closeIndent})`;
7273
7291
  if (values.length === 0) {
7274
7292
  return "Default";
7275
7293
  }
7276
- return values.map((v) => toPascalCase(v)).join("");
7294
+ return values.map((v) => toSafeIdentifier(v, KOTLIN_KEYWORDS, true)).join("");
7277
7295
  }
7278
7296
  };
7279
7297
  function androidRenderer() {
@@ -7293,19 +7311,19 @@ init_token_utils();
7293
7311
  // src/renderers/bundlers/css.ts
7294
7312
  init_errors();
7295
7313
  init_utils();
7314
+ var REF_PREFIX_SETS = "#/sets/";
7315
+ var REF_PREFIX_MODIFIERS = "#/modifiers/";
7296
7316
  var getSourceSet = (token) => {
7297
7317
  if (typeof token !== "object" || token === null) {
7298
7318
  return void 0;
7299
7319
  }
7300
- const maybe = token;
7301
- return typeof maybe._sourceSet === "string" ? maybe._sourceSet : void 0;
7320
+ return "_sourceSet" in token && typeof token._sourceSet === "string" ? token._sourceSet : void 0;
7302
7321
  };
7303
7322
  var getSourceModifier = (token) => {
7304
7323
  if (typeof token !== "object" || token === null) {
7305
7324
  return void 0;
7306
7325
  }
7307
- const maybe = token;
7308
- return typeof maybe._sourceModifier === "string" ? maybe._sourceModifier : void 0;
7326
+ return "_sourceModifier" in token && typeof token._sourceModifier === "string" ? token._sourceModifier : void 0;
7309
7327
  };
7310
7328
  async function bundleAsCss(bundleData, resolver, options, formatTokens) {
7311
7329
  const baseItem = bundleData.find((item) => item.isBase);
@@ -7390,6 +7408,15 @@ async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, o
7390
7408
  return `/* Modifier: ${modifier}=${context} */
7391
7409
  ${css2}`;
7392
7410
  }
7411
+ function addLayerBlock(blocks, included, key, blockTokens, description) {
7412
+ if (Object.keys(blockTokens).length === 0) {
7413
+ return;
7414
+ }
7415
+ for (const k of Object.keys(blockTokens)) {
7416
+ included.add(k);
7417
+ }
7418
+ blocks.push({ key, description, tokens: blockTokens });
7419
+ }
7393
7420
  function collectSetTokens(tokens, setName, included) {
7394
7421
  const result = {};
7395
7422
  for (const [name, token] of Object.entries(tokens)) {
@@ -7420,75 +7447,67 @@ function collectRemainder(tokens, included) {
7420
7447
  function buildSetLayerBlocks(tokens, resolver) {
7421
7448
  const blocks = [];
7422
7449
  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
7450
  for (const item of resolver.resolutionOrder) {
7433
7451
  const ref = item.$ref;
7434
- if (typeof ref !== "string" || !ref.startsWith("#/sets/")) {
7452
+ if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX_SETS)) {
7435
7453
  continue;
7436
7454
  }
7437
- const setName = ref.slice("#/sets/".length);
7438
- addBlock(
7455
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7456
+ addLayerBlock(
7457
+ blocks,
7458
+ included,
7439
7459
  `Set: ${setName}`,
7440
7460
  collectSetTokens(tokens, setName, included),
7441
7461
  resolver.sets?.[setName]?.description
7442
7462
  );
7443
7463
  }
7444
- addBlock("Unattributed", collectRemainder(tokens, included));
7464
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7445
7465
  return blocks;
7446
7466
  }
7447
7467
  function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
7448
7468
  const blocks = [];
7449
7469
  const included = /* @__PURE__ */ new Set();
7450
7470
  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
7471
  for (const item of resolver.resolutionOrder) {
7461
7472
  const ref = item.$ref;
7462
7473
  if (typeof ref !== "string") {
7463
7474
  continue;
7464
7475
  }
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
- }
7476
+ processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver);
7488
7477
  }
7489
- addBlock("Unattributed", collectRemainder(tokens, included));
7478
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7490
7479
  return blocks;
7491
7480
  }
7481
+ function processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver) {
7482
+ if (ref.startsWith(REF_PREFIX_SETS)) {
7483
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7484
+ addLayerBlock(
7485
+ blocks,
7486
+ included,
7487
+ `Set: ${setName}`,
7488
+ collectSetTokens(tokens, setName, included),
7489
+ resolver.sets?.[setName]?.description
7490
+ );
7491
+ return;
7492
+ }
7493
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7494
+ return;
7495
+ }
7496
+ const modifierName = ref.slice(REF_PREFIX_MODIFIERS.length);
7497
+ const modifier = resolver.modifiers?.[modifierName];
7498
+ const selectedContext = baseInputs[modifierName.toLowerCase()];
7499
+ if (!modifier || !selectedContext) {
7500
+ return;
7501
+ }
7502
+ const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7503
+ addLayerBlock(
7504
+ blocks,
7505
+ included,
7506
+ `Modifier: ${modifierName}=${selectedContext} (default)`,
7507
+ collectModifierTokens(tokens, expectedSource, included),
7508
+ modifier.description
7509
+ );
7510
+ }
7492
7511
  function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs) {
7493
7512
  const normalizedModifier = modifierName.toLowerCase();
7494
7513
  const normalizedContext = context.toLowerCase();
@@ -7503,6 +7522,36 @@ function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs
7503
7522
  return Object.entries(baseInputs).every(([k, v]) => k === normalizedModifier || inputs[k] === v);
7504
7523
  });
7505
7524
  }
7525
+ function pushUniqueBundleItem(ordered, includedKeys, item) {
7526
+ if (!item) {
7527
+ return;
7528
+ }
7529
+ const key = stableInputsKey(item.modifierInputs);
7530
+ if (includedKeys.has(key)) {
7531
+ return;
7532
+ }
7533
+ includedKeys.add(key);
7534
+ ordered.push(item);
7535
+ }
7536
+ function appendModifierPermutations(bundleData, modifiers, orderedNames, baseInputs, ordered, includedKeys) {
7537
+ for (const modifierName of orderedNames) {
7538
+ const modifierDef = modifiers[modifierName];
7539
+ if (!modifierDef) {
7540
+ continue;
7541
+ }
7542
+ const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
7543
+ for (const ctx of Object.keys(modifierDef.contexts)) {
7544
+ if (defaultValue === ctx.toLowerCase()) {
7545
+ continue;
7546
+ }
7547
+ pushUniqueBundleItem(
7548
+ ordered,
7549
+ includedKeys,
7550
+ findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs)
7551
+ );
7552
+ }
7553
+ }
7554
+ }
7506
7555
  function orderBundleData(bundleData, resolver, baseItem) {
7507
7556
  const modifiers = resolver.modifiers;
7508
7557
  if (!modifiers) {
@@ -7519,31 +7568,15 @@ function orderBundleData(bundleData, resolver, baseItem) {
7519
7568
  }
7520
7569
  const includedKeys = /* @__PURE__ */ new Set();
7521
7570
  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) {
7535
- const modifierDef = modifiers[modifierName];
7536
- if (!modifierDef) {
7537
- continue;
7538
- }
7539
- const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
7540
- for (const ctx of Object.keys(modifierDef.contexts)) {
7541
- if (defaultValue === ctx.toLowerCase()) {
7542
- continue;
7543
- }
7544
- pushUnique(findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs));
7545
- }
7546
- }
7571
+ pushUniqueBundleItem(ordered, includedKeys, baseItem);
7572
+ appendModifierPermutations(
7573
+ bundleData,
7574
+ modifiers,
7575
+ orderedModifierNames,
7576
+ baseInputs,
7577
+ ordered,
7578
+ includedKeys
7579
+ );
7547
7580
  return ordered.length > 0 ? ordered : bundleData;
7548
7581
  }
7549
7582
  function getOrderedModifierNames(resolver) {
@@ -7555,10 +7588,10 @@ function getOrderedModifierNames(resolver) {
7555
7588
  if (typeof ref !== "string") {
7556
7589
  continue;
7557
7590
  }
7558
- if (!ref.startsWith("#/modifiers/")) {
7591
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7559
7592
  continue;
7560
7593
  }
7561
- const name = ref.slice("#/modifiers/".length);
7594
+ const name = ref.slice(REF_PREFIX_MODIFIERS.length);
7562
7595
  if (seen.has(name)) {
7563
7596
  continue;
7564
7597
  }
@@ -7629,24 +7662,22 @@ var CssRenderer = class _CssRenderer {
7629
7662
  ...options,
7630
7663
  referenceTokens: options?.referenceTokens ?? tokens
7631
7664
  };
7632
- const groups = this.groupTokens(tokens, opts);
7665
+ const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
7633
7666
  const referenceTokens = opts.referenceTokens;
7634
7667
  const lines = [];
7635
- for (const [selector, groupTokens] of Object.entries(groups)) {
7636
- this.buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts);
7637
- }
7668
+ this.buildCssBlock(lines, sortedTokens, opts.selector, tokens, referenceTokens, opts);
7638
7669
  const cssString = lines.join("");
7639
7670
  return opts.minify ? cssString : await this.formatWithPrettier(cssString);
7640
7671
  }
7641
7672
  buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
7642
- const indent2 = opts.minify ? "" : " ";
7673
+ const indent = opts.minify ? "" : " ";
7643
7674
  const newline = opts.minify ? "" : "\n";
7644
7675
  const space = opts.minify ? "" : " ";
7645
7676
  const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
7646
- const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
7677
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
7647
7678
  if (hasMediaQuery) {
7648
7679
  lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
7649
- lines.push(`${indent2}${selector}${space}{${newline}`);
7680
+ lines.push(`${indent}${selector}${space}{${newline}`);
7650
7681
  } else {
7651
7682
  lines.push(`${selector}${space}{${newline}`);
7652
7683
  }
@@ -7663,21 +7694,21 @@ var CssRenderer = class _CssRenderer {
7663
7694
  );
7664
7695
  }
7665
7696
  if (hasMediaQuery) {
7666
- lines.push(`${indent2}}${newline}`);
7697
+ lines.push(`${indent}}${newline}`);
7667
7698
  }
7668
7699
  lines.push(`}${newline}${newline}`);
7669
7700
  }
7670
- pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent2, newline, space) {
7701
+ pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
7671
7702
  const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
7672
7703
  if (token.$deprecated != null && token.$deprecated !== false) {
7673
7704
  const deprecationMsg = formatDeprecationMessage(token, "", "comment");
7674
- lines.push(`${indent2}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7705
+ lines.push(`${indent}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7675
7706
  }
7676
7707
  if (token.$description && token.$description !== "") {
7677
- lines.push(`${indent2}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7708
+ lines.push(`${indent}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7678
7709
  }
7679
7710
  for (const entry of entries) {
7680
- lines.push(`${indent2}--${entry.name}:${space}${entry.value};${newline}`);
7711
+ lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
7681
7712
  }
7682
7713
  }
7683
7714
  async formatWithPrettier(css2) {
@@ -7692,15 +7723,6 @@ var CssRenderer = class _CssRenderer {
7692
7723
  return css2;
7693
7724
  }
7694
7725
  }
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
7726
  buildCssEntries(token, tokens, referenceTokens, preserveReferences) {
7705
7727
  if (preserveReferences) {
7706
7728
  const refName = getPureAliasReferenceName(token.originalValue);
@@ -7842,7 +7864,7 @@ var CssRenderer = class _CssRenderer {
7842
7864
  leaves.push({ path: path7, value });
7843
7865
  return;
7844
7866
  }
7845
- if (isColorObject(value) || isDimensionObject(value) || this.isDurationObject(value)) {
7867
+ if (isColorObject(value) || isDimensionObject(value) || isDurationObject(value)) {
7846
7868
  leaves.push({ path: path7, value });
7847
7869
  return;
7848
7870
  }
@@ -7885,8 +7907,8 @@ var CssRenderer = class _CssRenderer {
7885
7907
  if (isDimensionObject(value)) {
7886
7908
  return dimensionObjectToString(value);
7887
7909
  }
7888
- if (this.isDurationObject(value)) {
7889
- return this.formatDurationValue(value);
7910
+ if (isDurationObject(value)) {
7911
+ return durationObjectToString(value);
7890
7912
  }
7891
7913
  if (typeof value === "string") {
7892
7914
  return value;
@@ -7949,15 +7971,6 @@ var CssRenderer = class _CssRenderer {
7949
7971
  isPrimitiveValue(value) {
7950
7972
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
7951
7973
  }
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
7974
  /**
7962
7975
  * Format token value for CSS
7963
7976
  * Handles DTCG 2025.10 object formats for colors and dimensions
@@ -7978,8 +7991,8 @@ var CssRenderer = class _CssRenderer {
7978
7991
  return typeof value === "string" ? value : dimensionObjectToString(value);
7979
7992
  }
7980
7993
  if (type === "duration") {
7981
- if (this.isDurationObject(value)) {
7982
- return this.formatDurationValue(value);
7994
+ if (isDurationObject(value)) {
7995
+ return durationObjectToString(value);
7983
7996
  }
7984
7997
  if (typeof value === "string") {
7985
7998
  return value;
@@ -8069,16 +8082,16 @@ var CssRenderer = class _CssRenderer {
8069
8082
  */
8070
8083
  formatTransition(value) {
8071
8084
  const parts = [];
8072
- if (this.isDurationObject(value.duration)) {
8073
- parts.push(this.formatDurationValue(value.duration));
8085
+ if (isDurationObject(value.duration)) {
8086
+ parts.push(durationObjectToString(value.duration));
8074
8087
  } else if (value.duration != null) {
8075
8088
  parts.push(String(value.duration));
8076
8089
  }
8077
8090
  if (Array.isArray(value.timingFunction) && value.timingFunction.length === 4) {
8078
8091
  parts.push(`cubic-bezier(${value.timingFunction.join(", ")})`);
8079
8092
  }
8080
- if (this.isDurationObject(value.delay)) {
8081
- parts.push(this.formatDurationValue(value.delay));
8093
+ if (isDurationObject(value.delay)) {
8094
+ parts.push(durationObjectToString(value.delay));
8082
8095
  } else if (value.delay != null) {
8083
8096
  parts.push(String(value.delay));
8084
8097
  }
@@ -8088,7 +8101,7 @@ var CssRenderer = class _CssRenderer {
8088
8101
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8089
8102
  tokens,
8090
8103
  modifierInputs,
8091
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
8104
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8092
8105
  }));
8093
8106
  return await bundleAsCss(bundleData, context.resolver, options, async (tokens, resolved) => {
8094
8107
  return await this.formatTokens(tokens, {
@@ -8098,12 +8111,12 @@ var CssRenderer = class _CssRenderer {
8098
8111
  });
8099
8112
  }
8100
8113
  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
- }
8114
+ assertFileRequired(
8115
+ context.buildPath,
8116
+ context.output.file,
8117
+ context.output.name,
8118
+ "standalone CSS"
8119
+ );
8107
8120
  const files = {};
8108
8121
  for (const { tokens, modifierInputs } of context.permutations) {
8109
8122
  const { fileName, content } = await this.buildStandaloneFile(
@@ -8117,7 +8130,7 @@ var CssRenderer = class _CssRenderer {
8117
8130
  return { kind: "outputTree", files };
8118
8131
  }
8119
8132
  async buildStandaloneFile(tokens, modifierInputs, context, options) {
8120
- const isBase = this.isBasePermutation(modifierInputs, context.meta.defaults);
8133
+ const isBase = isBasePermutation(modifierInputs, context.meta.defaults);
8121
8134
  const { modifierName, modifierContext } = this.resolveModifierContext(
8122
8135
  modifierInputs,
8123
8136
  context,
@@ -8154,12 +8167,7 @@ var CssRenderer = class _CssRenderer {
8154
8167
  return { fileName, content };
8155
8168
  }
8156
8169
  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
- }
8170
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "modifier CSS");
8163
8171
  if (!context.resolver.modifiers) {
8164
8172
  throw new exports.ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
8165
8173
  }
@@ -8185,7 +8193,7 @@ var CssRenderer = class _CssRenderer {
8185
8193
  }
8186
8194
  async buildModifierBaseFile(context, options) {
8187
8195
  const basePermutation = context.permutations.find(
8188
- ({ modifierInputs }) => this.isBasePermutation(modifierInputs, context.meta.defaults)
8196
+ ({ modifierInputs }) => isBasePermutation(modifierInputs, context.meta.defaults)
8189
8197
  );
8190
8198
  if (!basePermutation) {
8191
8199
  return void 0;
@@ -8198,25 +8206,40 @@ var CssRenderer = class _CssRenderer {
8198
8206
  if (setBlocks.length === 0) {
8199
8207
  return void 0;
8200
8208
  }
8209
+ const { selector, mediaQuery } = this.resolveBaseModifierContext(context, options);
8210
+ const content = await this.formatSetBlocksCss(
8211
+ setBlocks,
8212
+ basePermutation.tokens,
8213
+ selector,
8214
+ mediaQuery,
8215
+ options
8216
+ );
8217
+ const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8218
+ return { fileName, content };
8219
+ }
8220
+ resolveBaseModifierContext(context, options) {
8201
8221
  const modifiers = context.resolver.modifiers;
8202
8222
  const firstModifierName = Object.keys(modifiers)[0] ?? "";
8203
8223
  const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
8204
8224
  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;
8225
+ return {
8226
+ selector: resolveSelector(
8227
+ options.selector,
8228
+ firstModifierName,
8229
+ firstModifierContext,
8230
+ true,
8231
+ baseModifierInputs
8232
+ ),
8233
+ mediaQuery: resolveMediaQuery(
8234
+ options.mediaQuery,
8235
+ firstModifierName,
8236
+ firstModifierContext,
8237
+ true,
8238
+ baseModifierInputs
8239
+ )
8240
+ };
8241
+ }
8242
+ async formatSetBlocksCss(setBlocks, referenceTokens, selector, mediaQuery, options) {
8220
8243
  const cssBlocks = [];
8221
8244
  for (const block of setBlocks) {
8222
8245
  const cleanTokens = stripInternalMetadata(block.tokens);
@@ -8232,9 +8255,7 @@ var CssRenderer = class _CssRenderer {
8232
8255
  cssBlocks.push(`${header}
8233
8256
  ${css2}`);
8234
8257
  }
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 };
8258
+ return cssBlocks.join("\n");
8238
8259
  }
8239
8260
  collectTokensForModifierContext(modifierName, contextValue, permutations) {
8240
8261
  const expectedSource = `${modifierName}-${contextValue}`;
@@ -8311,13 +8332,6 @@ ${css2}`);
8311
8332
  }
8312
8333
  return { modifierName: "", modifierContext: "" };
8313
8334
  }
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
8335
  };
8322
8336
  function cssRenderer() {
8323
8337
  const rendererInstance = new CssRenderer();
@@ -8330,8 +8344,6 @@ function cssRenderer() {
8330
8344
  }
8331
8345
 
8332
8346
  // src/renderers/ios.ts
8333
- init_errors();
8334
- init_token_utils();
8335
8347
  init_utils();
8336
8348
  var toSRGB2 = culori.converter("rgb");
8337
8349
  var toP32 = culori.converter("p3");
@@ -8420,94 +8432,68 @@ var IosRenderer = class {
8420
8432
  return await this.formatStandalone(context, opts);
8421
8433
  }
8422
8434
  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
8435
  const access3 = options.accessLevel;
8430
- const groups = this.groupTokensByType(tokens);
8436
+ const groups = groupTokensByType(tokens, SWIFT_TYPE_GROUP_MAP);
8431
8437
  const imports = this.collectImports(tokens);
8432
- const i1 = this.indentStr(options.indent, 1);
8433
- const i2 = this.indentStr(options.indent, 2);
8434
8438
  const staticPrefix = this.staticLetPrefix(options);
8435
8439
  const frozen = this.frozenPrefix(options);
8436
8440
  const lines = [];
8437
- lines.push(this.buildFileHeader());
8441
+ lines.push(buildGeneratedFileHeader());
8438
8442
  lines.push("");
8439
8443
  for (const imp of imports) {
8440
8444
  lines.push(`import ${imp}`);
8441
8445
  }
8442
8446
  lines.push(...this.buildStructDefinitions(tokens, access3, options));
8447
+ this.pushTokenLayout(lines, groups, options, access3, staticPrefix, frozen);
8448
+ lines.push(...this.buildViewExtensions(tokens, access3, options));
8449
+ if (options.structure !== "grouped") {
8450
+ lines.push("");
8451
+ }
8452
+ return lines.join("\n");
8453
+ }
8454
+ pushTokenLayout(lines, groups, options, access3, staticPrefix, frozen) {
8455
+ const i1 = indentStr(options.indent, 1);
8456
+ const i2 = indentStr(options.indent, 2);
8457
+ if (options.structure === "grouped") {
8458
+ this.pushGroupedLayout(lines, groups, options, access3, i1, i2, staticPrefix, frozen);
8459
+ return;
8460
+ }
8443
8461
  lines.push("");
8444
8462
  lines.push(`${frozen}${access3} enum ${options.enumName} {`);
8445
8463
  for (const group of groups) {
8446
8464
  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
- }
8465
+ this.pushTokenDeclarations(lines, group.tokens, options, access3, i2, staticPrefix);
8458
8466
  lines.push(`${i1}}`);
8459
8467
  lines.push("");
8460
8468
  }
8461
8469
  lines.push("}");
8462
- lines.push(...this.buildViewExtensions(tokens, access3, options));
8463
- lines.push("");
8464
- return lines.join("\n");
8465
8470
  }
8466
- formatAsGrouped(tokens, options) {
8467
- const access3 = options.accessLevel;
8471
+ pushGroupedLayout(lines, groups, options, access3, i1, i2, staticPrefix, frozen) {
8468
8472
  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
8473
  lines.push("");
8483
8474
  lines.push(`${frozen}${access3} enum ${namespace} {}`);
8484
8475
  lines.push("");
8485
8476
  for (const group of groups) {
8486
8477
  lines.push(`${access3} extension ${namespace} {`);
8487
8478
  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
- }
8479
+ this.pushTokenDeclarations(lines, group.tokens, options, access3, i2, staticPrefix);
8499
8480
  lines.push(`${i1}}`);
8500
8481
  lines.push("}");
8501
8482
  lines.push("");
8502
8483
  }
8503
- lines.push(...this.buildViewExtensions(tokens, access3, options));
8504
- return lines.join("\n");
8505
8484
  }
8506
- buildFileHeader() {
8507
- return [
8508
- "// Generated by Dispersa - do not edit manually",
8509
- "// https://github.com/timges/dispersa"
8510
- ].join("\n");
8485
+ pushTokenDeclarations(lines, tokens, options, access3, indent, staticPrefix) {
8486
+ for (const token of tokens) {
8487
+ const swiftName = this.buildQualifiedSwiftName(token);
8488
+ const swiftValue = this.formatSwiftValue(token, options);
8489
+ const typeAnnotation = this.getTypeAnnotation(token);
8490
+ const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8491
+ const docComment = this.buildDocComment(token, indent);
8492
+ if (docComment) {
8493
+ lines.push(docComment);
8494
+ }
8495
+ lines.push(`${indent}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8496
+ }
8511
8497
  }
8512
8498
  collectImports(tokens) {
8513
8499
  const imports = /* @__PURE__ */ new Set();
@@ -8522,24 +8508,11 @@ var IosRenderer = class {
8522
8508
  /**
8523
8509
  * Builds a `///` doc comment from a token's `$description`, if present.
8524
8510
  */
8525
- buildDocComment(token, indent2) {
8511
+ buildDocComment(token, indent) {
8526
8512
  if (!token.$description) {
8527
8513
  return void 0;
8528
8514
  }
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
- }));
8515
+ return `${indent}/// ${token.$description}`;
8543
8516
  }
8544
8517
  /**
8545
8518
  * Builds a qualified Swift name from a token's path, preserving parent
@@ -8552,43 +8525,40 @@ var IosRenderer = class {
8552
8525
  const path7 = token.path;
8553
8526
  const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
8554
8527
  const joined = withoutTypePrefix.join("_");
8555
- return this.toSwiftIdentifier(joined);
8528
+ return toSafeIdentifier(joined, SWIFT_KEYWORDS, false);
8556
8529
  }
8557
8530
  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]}))`;
8531
+ const { $type, $value: value } = token;
8532
+ switch ($type) {
8533
+ case "color":
8534
+ return this.formatColorValue(value, options);
8535
+ case "dimension":
8536
+ return this.formatDimensionValue(value);
8537
+ case "fontFamily":
8538
+ return this.formatFontFamilyValue(value);
8539
+ case "fontWeight":
8540
+ return this.formatFontWeightValue(value);
8541
+ case "duration":
8542
+ return this.formatDurationValue(value);
8543
+ case "shadow":
8544
+ return this.formatShadowValue(value, options);
8545
+ case "typography":
8546
+ return this.formatTypographyValue(value);
8547
+ case "border":
8548
+ return this.formatBorderValue(value, options);
8549
+ case "gradient":
8550
+ return this.formatGradientValue(value, options);
8551
+ case "number":
8552
+ return String(value);
8553
+ case "cubicBezier":
8554
+ if (Array.isArray(value) && value.length === 4) {
8555
+ return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
8556
+ }
8557
+ break;
8591
8558
  }
8559
+ return this.formatSwiftPrimitive(value);
8560
+ }
8561
+ formatSwiftPrimitive(value) {
8592
8562
  if (typeof value === "string") {
8593
8563
  return `"${this.escapeSwiftString(value)}"`;
8594
8564
  }
@@ -8621,9 +8591,7 @@ var IosRenderer = class {
8621
8591
  }
8622
8592
  formatDimensionValue(value) {
8623
8593
  if (isDimensionObject(value)) {
8624
- const dim = value;
8625
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8626
- return String(ptValue);
8594
+ return this.dimensionToPoints(value);
8627
8595
  }
8628
8596
  return String(value);
8629
8597
  }
@@ -8690,7 +8658,7 @@ var IosRenderer = class {
8690
8658
  return map[name.toLowerCase()];
8691
8659
  }
8692
8660
  formatDurationValue(value) {
8693
- if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
8661
+ if (isDurationObject(value)) {
8694
8662
  const dur = value;
8695
8663
  const seconds = dur.unit === "ms" ? dur.value / 1e3 : dur.value;
8696
8664
  return String(seconds);
@@ -8739,9 +8707,7 @@ var IosRenderer = class {
8739
8707
  if (!isDimensionObject(typo.letterSpacing)) {
8740
8708
  return "0";
8741
8709
  }
8742
- const dim = typo.letterSpacing;
8743
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8744
- return String(ptValue);
8710
+ return this.dimensionToPoints(typo.letterSpacing);
8745
8711
  }
8746
8712
  extractLineSpacing(typo) {
8747
8713
  if (typo.lineHeight == null || typeof typo.lineHeight !== "number") {
@@ -8750,18 +8716,19 @@ var IosRenderer = class {
8750
8716
  if (!isDimensionObject(typo.fontSize)) {
8751
8717
  return "0";
8752
8718
  }
8753
- const dim = typo.fontSize;
8754
- const basePt = dim.unit === "rem" ? dim.value * 16 : dim.value;
8719
+ const basePt = this.dimensionToNumericPoints(typo.fontSize);
8755
8720
  const lineHeightPt = Math.round(basePt * typo.lineHeight * 100) / 100;
8756
8721
  return String(lineHeightPt - basePt);
8757
8722
  }
8723
+ dimensionToNumericPoints(dim) {
8724
+ return dim.unit === "rem" ? dim.value * 16 : dim.value;
8725
+ }
8758
8726
  dimensionToPoints(dim) {
8759
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8760
- return String(ptValue);
8727
+ return String(this.dimensionToNumericPoints(dim));
8761
8728
  }
8762
8729
  /** Formats a dimension as a CGFloat literal (appends `.0` for integers). */
8763
8730
  dimensionToCGFloat(dim) {
8764
- const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8731
+ const ptValue = this.dimensionToNumericPoints(dim);
8765
8732
  return Number.isInteger(ptValue) ? `${ptValue}.0` : String(ptValue);
8766
8733
  }
8767
8734
  getTypeAnnotation(token) {
@@ -8780,21 +8747,12 @@ var IosRenderer = class {
8780
8747
  return void 0;
8781
8748
  }
8782
8749
  }
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
8750
  escapeSwiftString(str) {
8790
8751
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
8791
8752
  }
8792
8753
  roundComponent(value) {
8793
8754
  return Math.round(value * 1e4) / 1e4;
8794
8755
  }
8795
- indentStr(width, level) {
8796
- return " ".repeat(width * level);
8797
- }
8798
8756
  /**
8799
8757
  * Returns the prefix for `static let` declarations.
8800
8758
  * Swift 6 requires `nonisolated(unsafe)` on global stored properties.
@@ -8810,34 +8768,25 @@ var IosRenderer = class {
8810
8768
  structConformances(options) {
8811
8769
  return options.swiftVersion === "6.0" ? ": Sendable" : "";
8812
8770
  }
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
8771
  /** Emits all struct definitions needed by the token set. */
8823
8772
  buildStructDefinitions(tokens, access3, options) {
8824
8773
  const lines = [];
8825
- if (this.hasShadowTokens(tokens)) {
8774
+ if (Object.values(tokens).some(isShadowToken)) {
8826
8775
  lines.push("");
8827
8776
  lines.push(...this.buildShadowStyleStruct(access3, options));
8828
8777
  }
8829
- if (this.hasTypographyTokens(tokens)) {
8778
+ if (Object.values(tokens).some(isTypographyToken)) {
8830
8779
  lines.push("");
8831
8780
  lines.push(...this.buildTypographyStyleStruct(access3, options));
8832
8781
  }
8833
- if (this.hasBorderTokens(tokens)) {
8782
+ if (Object.values(tokens).some(isBorderToken)) {
8834
8783
  lines.push("");
8835
8784
  lines.push(...this.buildBorderStyleStruct(access3, options));
8836
8785
  }
8837
8786
  return lines;
8838
8787
  }
8839
8788
  buildShadowStyleStruct(access3, options) {
8840
- const i1 = this.indentStr(options.indent, 1);
8789
+ const i1 = indentStr(options.indent, 1);
8841
8790
  const conformances = this.structConformances(options);
8842
8791
  const frozen = this.frozenPrefix(options);
8843
8792
  return [
@@ -8851,7 +8800,7 @@ var IosRenderer = class {
8851
8800
  ];
8852
8801
  }
8853
8802
  buildTypographyStyleStruct(access3, options) {
8854
- const i1 = this.indentStr(options.indent, 1);
8803
+ const i1 = indentStr(options.indent, 1);
8855
8804
  const conformances = this.structConformances(options);
8856
8805
  const frozen = this.frozenPrefix(options);
8857
8806
  return [
@@ -8863,7 +8812,7 @@ var IosRenderer = class {
8863
8812
  ];
8864
8813
  }
8865
8814
  buildBorderStyleStruct(access3, options) {
8866
- const i1 = this.indentStr(options.indent, 1);
8815
+ const i1 = indentStr(options.indent, 1);
8867
8816
  const conformances = this.structConformances(options);
8868
8817
  const frozen = this.frozenPrefix(options);
8869
8818
  return [
@@ -8876,9 +8825,9 @@ var IosRenderer = class {
8876
8825
  /** Emits convenience View extensions for shadow and typography application. */
8877
8826
  buildViewExtensions(tokens, access3, options) {
8878
8827
  const lines = [];
8879
- const i1 = this.indentStr(options.indent, 1);
8880
- const i2 = this.indentStr(options.indent, 2);
8881
- if (this.hasShadowTokens(tokens)) {
8828
+ const i1 = indentStr(options.indent, 1);
8829
+ const i2 = indentStr(options.indent, 2);
8830
+ if (Object.values(tokens).some(isShadowToken)) {
8882
8831
  lines.push("");
8883
8832
  lines.push(`${access3} extension View {`);
8884
8833
  lines.push(`${i1}func shadowStyle(_ style: ShadowStyle) -> some View {`);
@@ -8888,7 +8837,7 @@ var IosRenderer = class {
8888
8837
  lines.push(`${i1}}`);
8889
8838
  lines.push("}");
8890
8839
  }
8891
- if (this.hasTypographyTokens(tokens)) {
8840
+ if (Object.values(tokens).some(isTypographyToken)) {
8892
8841
  lines.push("");
8893
8842
  lines.push(`${access3} extension View {`);
8894
8843
  lines.push(`${i1}func typographyStyle(_ style: TypographyStyle) -> some View {`);
@@ -8920,12 +8869,12 @@ var IosRenderer = class {
8920
8869
  return `Gradient(stops: [${stops.join(", ")}])`;
8921
8870
  }
8922
8871
  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
- }
8872
+ assertFileRequired(
8873
+ context.buildPath,
8874
+ context.output.file,
8875
+ context.output.name,
8876
+ "standalone iOS"
8877
+ );
8929
8878
  const files = {};
8930
8879
  for (const { tokens, modifierInputs } of context.permutations) {
8931
8880
  const processedTokens = stripInternalMetadata(tokens);
@@ -8954,7 +8903,6 @@ function iosRenderer() {
8954
8903
 
8955
8904
  // src/renderers/js-module.ts
8956
8905
  init_utils();
8957
- init_errors();
8958
8906
  init_token_utils();
8959
8907
  var JsModuleRenderer = class {
8960
8908
  async format(context, options) {
@@ -8970,18 +8918,13 @@ var JsModuleRenderer = class {
8970
8918
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8971
8919
  tokens: stripInternalMetadata(tokens),
8972
8920
  modifierInputs,
8973
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
8921
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8974
8922
  }));
8975
8923
  return await bundleAsJsModule2(bundleData, context.resolver, opts, async (tokens) => {
8976
8924
  return await this.formatTokens(tokens, opts);
8977
8925
  });
8978
8926
  }
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
- }
8927
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JS module");
8985
8928
  const files = {};
8986
8929
  for (const { tokens, modifierInputs } of context.permutations) {
8987
8930
  const cleanTokens = stripInternalMetadata(tokens);
@@ -9035,42 +8978,18 @@ var JsModuleRenderer = class {
9035
8978
  lines.push(`export default ${varName}`);
9036
8979
  return lines;
9037
8980
  }
9038
- /**
9039
- * Convert tokens to plain object with flat or nested structure
9040
- */
9041
8981
  tokensToPlainObject(tokens, structure) {
8982
+ if (structure === "nested") {
8983
+ return buildNestedTokenObject(tokens, (token) => token.$value);
8984
+ }
9042
8985
  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
- }
8986
+ for (const [name, token] of getSortedTokenEntries(tokens)) {
8987
+ result[name] = token.$value;
9066
8988
  }
9067
8989
  return result;
9068
8990
  }
9069
- /**
9070
- * Add object properties to lines
9071
- */
9072
- addObjectProperties(lines, obj, indent2) {
9073
- const indentStr = " ".repeat(indent2);
8991
+ addObjectProperties(lines, obj, indent) {
8992
+ const indentStr2 = " ".repeat(indent);
9074
8993
  const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
9075
8994
  for (let i = 0; i < entries.length; i++) {
9076
8995
  const entry = entries[i];
@@ -9079,14 +8998,16 @@ var JsModuleRenderer = class {
9079
8998
  }
9080
8999
  const [key, value] = entry;
9081
9000
  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 ? "" : ","}`);
9001
+ const isNestedObject = typeof value === "object" && value !== null && !Array.isArray(value);
9002
+ if (!isNestedObject) {
9003
+ lines.push(
9004
+ `${indentStr2}${this.quoteKey(key)}: ${JSON.stringify(value)}${isLast ? "" : ","}`
9005
+ );
9006
+ continue;
9089
9007
  }
9008
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
9009
+ this.addObjectProperties(lines, value, indent + 1);
9010
+ lines.push(`${indentStr2}}${isLast ? "" : ","}`);
9090
9011
  }
9091
9012
  }
9092
9013
  /**
@@ -9098,9 +9019,6 @@ var JsModuleRenderer = class {
9098
9019
  }
9099
9020
  return `"${key}"`;
9100
9021
  }
9101
- isBasePermutation(modifierInputs, defaults) {
9102
- return Object.entries(modifierInputs).every(([key, value]) => value === defaults[key]);
9103
- }
9104
9022
  };
9105
9023
  function jsRenderer() {
9106
9024
  const rendererInstance = new JsModuleRenderer();
@@ -9114,7 +9032,6 @@ function jsRenderer() {
9114
9032
 
9115
9033
  // src/renderers/json.ts
9116
9034
  init_utils();
9117
- init_errors();
9118
9035
  init_token_utils();
9119
9036
  var JsonRenderer = class {
9120
9037
  async format(context, options) {
@@ -9129,18 +9046,13 @@ var JsonRenderer = class {
9129
9046
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9130
9047
  tokens: stripInternalMetadata(tokens),
9131
9048
  modifierInputs,
9132
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9049
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9133
9050
  }));
9134
9051
  return await bundleAsJson2(bundleData, context.resolver, async (tokens) => {
9135
9052
  return await this.formatTokens(tokens, opts);
9136
9053
  });
9137
9054
  }
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
- }
9055
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JSON");
9144
9056
  const files = {};
9145
9057
  for (const { tokens, modifierInputs } of context.permutations) {
9146
9058
  const processedTokens = stripInternalMetadata(tokens);
@@ -9200,55 +9112,11 @@ var JsonRenderer = class {
9200
9112
  }
9201
9113
  return result;
9202
9114
  }
9203
- /**
9204
- * Nest tokens by path (values only)
9205
- */
9206
9115
  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;
9116
+ return buildNestedTokenObject(tokens, (token) => token.$value);
9227
9117
  }
9228
- /**
9229
- * Nest tokens by path (with metadata)
9230
- */
9231
9118
  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;
9119
+ return buildNestedTokenObject(tokens, (token) => this.serializeToken(token));
9252
9120
  }
9253
9121
  serializeToken(token) {
9254
9122
  return {
@@ -9259,9 +9127,6 @@ var JsonRenderer = class {
9259
9127
  ...token.$extensions != null && { $extensions: token.$extensions }
9260
9128
  };
9261
9129
  }
9262
- isBasePermutation(modifierInputs, defaults) {
9263
- return Object.entries(modifierInputs).every(([key, value]) => value === defaults[key]);
9264
- }
9265
9130
  };
9266
9131
  function jsonRenderer() {
9267
9132
  const rendererInstance = new JsonRenderer();
@@ -9274,7 +9139,6 @@ function jsonRenderer() {
9274
9139
  }
9275
9140
 
9276
9141
  // src/renderers/tailwind.ts
9277
- init_errors();
9278
9142
  init_token_utils();
9279
9143
 
9280
9144
  // src/renderers/bundlers/tailwind.ts
@@ -9303,6 +9167,13 @@ async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOv
9303
9167
  }
9304
9168
  return cssBlocks.join("\n");
9305
9169
  }
9170
+ function resolveModifierSelectorAndMedia(options, modifier, context, modifierInputs) {
9171
+ const normalized = normalizeModifierInputs(modifierInputs);
9172
+ return {
9173
+ selector: resolveSelector(options.selector, modifier, context, false, normalized),
9174
+ mediaQuery: resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized)
9175
+ };
9176
+ }
9306
9177
  async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
9307
9178
  const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
9308
9179
  if (differenceCount > 1) {
@@ -9315,19 +9186,11 @@ async function formatModifierOverride({ tokens, modifierInputs }, baseItem, opti
9315
9186
  const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
9316
9187
  const [modifier, context] = parseModifierSource(expectedSource);
9317
9188
  const cleanTokens = stripInternalMetadata(tokensToInclude);
9318
- const selector = resolveSelector(
9319
- options.selector,
9320
- modifier,
9321
- context,
9322
- false,
9323
- normalizeModifierInputs(modifierInputs)
9324
- );
9325
- const mediaQuery = resolveMediaQuery(
9326
- options.mediaQuery,
9189
+ const { selector, mediaQuery } = resolveModifierSelectorAndMedia(
9190
+ options,
9327
9191
  modifier,
9328
9192
  context,
9329
- false,
9330
- normalizeModifierInputs(modifierInputs)
9193
+ modifierInputs
9331
9194
  );
9332
9195
  const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
9333
9196
  return `/* Modifier: ${modifier}=${context} */
@@ -9416,7 +9279,7 @@ var TailwindRenderer = class {
9416
9279
  */
9417
9280
  async formatTokens(tokens, options) {
9418
9281
  const lines = [];
9419
- const indent2 = options.minify ? "" : " ";
9282
+ const indent = options.minify ? "" : " ";
9420
9283
  const newline = options.minify ? "" : "\n";
9421
9284
  const space = options.minify ? "" : " ";
9422
9285
  if (options.includeImport) {
@@ -9438,7 +9301,7 @@ var TailwindRenderer = class {
9438
9301
  for (const [, token] of getSortedTokenEntries(tokens)) {
9439
9302
  const varName = this.buildVariableName(token);
9440
9303
  const varValue = this.formatValue(token);
9441
- lines.push(`${indent2}--${varName}:${space}${varValue};${newline}`);
9304
+ lines.push(`${indent}--${varName}:${space}${varValue};${newline}`);
9442
9305
  }
9443
9306
  lines.push(`}${newline}`);
9444
9307
  const cssString = lines.join("");
@@ -9449,15 +9312,15 @@ var TailwindRenderer = class {
9449
9312
  * Used for modifier overrides (e.g., dark mode) appended after the @theme block.
9450
9313
  */
9451
9314
  async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
9452
- const indent2 = minify ? "" : " ";
9315
+ const indent = minify ? "" : " ";
9453
9316
  const newline = minify ? "" : "\n";
9454
9317
  const space = minify ? "" : " ";
9455
9318
  const hasMediaQuery = mediaQuery !== "";
9456
- const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
9319
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
9457
9320
  const lines = [];
9458
9321
  if (hasMediaQuery) {
9459
9322
  lines.push(`@media ${mediaQuery}${space}{${newline}`);
9460
- lines.push(`${indent2}${selector}${space}{${newline}`);
9323
+ lines.push(`${indent}${selector}${space}{${newline}`);
9461
9324
  } else {
9462
9325
  lines.push(`${selector}${space}{${newline}`);
9463
9326
  }
@@ -9467,7 +9330,7 @@ var TailwindRenderer = class {
9467
9330
  lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
9468
9331
  }
9469
9332
  if (hasMediaQuery) {
9470
- lines.push(`${indent2}}${newline}`);
9333
+ lines.push(`${indent}}${newline}`);
9471
9334
  lines.push(`}${newline}`);
9472
9335
  } else {
9473
9336
  lines.push(`}${newline}`);
@@ -9494,8 +9357,8 @@ var TailwindRenderer = class {
9494
9357
  if (token.$type === "dimension" && isDimensionObject(value)) {
9495
9358
  return dimensionObjectToString(value);
9496
9359
  }
9497
- if (token.$type === "duration" && this.isDurationObject(value)) {
9498
- return `${value.value}${value.unit}`;
9360
+ if (token.$type === "duration" && isDurationObject(value)) {
9361
+ return durationObjectToString(value);
9499
9362
  }
9500
9363
  if (token.$type === "fontFamily") {
9501
9364
  if (Array.isArray(value)) {
@@ -9550,9 +9413,6 @@ var TailwindRenderer = class {
9550
9413
  }
9551
9414
  return parts.join(" ");
9552
9415
  }
9553
- isDurationObject(value) {
9554
- return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
9555
- }
9556
9416
  async formatWithPrettier(css2) {
9557
9417
  try {
9558
9418
  return await prettier__default.default.format(css2, {
@@ -9569,7 +9429,7 @@ var TailwindRenderer = class {
9569
9429
  const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9570
9430
  tokens,
9571
9431
  modifierInputs,
9572
- isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9432
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9573
9433
  }));
9574
9434
  return await bundleAsTailwind(
9575
9435
  bundleData,
@@ -9579,12 +9439,12 @@ var TailwindRenderer = class {
9579
9439
  );
9580
9440
  }
9581
9441
  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
- }
9442
+ assertFileRequired(
9443
+ context.buildPath,
9444
+ context.output.file,
9445
+ context.output.name,
9446
+ "standalone Tailwind"
9447
+ );
9588
9448
  const files = {};
9589
9449
  for (const { tokens, modifierInputs } of context.permutations) {
9590
9450
  const processedTokens = stripInternalMetadata(tokens);
@@ -9600,11 +9460,6 @@ var TailwindRenderer = class {
9600
9460
  }
9601
9461
  return outputTree(files);
9602
9462
  }
9603
- isBasePermutation(modifierInputs, defaults) {
9604
- return Object.entries(defaults).every(
9605
- ([key, value]) => modifierInputs[key]?.toLowerCase() === value.toLowerCase()
9606
- );
9607
- }
9608
9463
  };
9609
9464
  function tailwindRenderer() {
9610
9465
  const rendererInstance = new TailwindRenderer();
@@ -9737,11 +9592,6 @@ function defineRenderer(renderer) {
9737
9592
 
9738
9593
  // src/index.ts
9739
9594
  init_errors();
9740
- /**
9741
- * @license
9742
- * Copyright (c) 2025 Dispersa Contributors
9743
- * SPDX-License-Identifier: MIT
9744
- */
9745
9595
  /**
9746
9596
  * @license MIT
9747
9597
  * Copyright (c) 2025-present Dispersa Contributors
@@ -9749,6 +9599,11 @@ init_errors();
9749
9599
  * This source code is licensed under the MIT license found in the
9750
9600
  * LICENSE file in the root directory of this source tree.
9751
9601
  */
9602
+ /**
9603
+ * @license
9604
+ * Copyright (c) 2025 Dispersa Contributors
9605
+ * SPDX-License-Identifier: MIT
9606
+ */
9752
9607
 
9753
9608
  exports.Dispersa = Dispersa;
9754
9609
  exports.android = android;