dispersa 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +1 -2
  2. package/dist/{builders.d.cts → builders-B7_pBy58.d.cts} +180 -6
  3. package/dist/{builders.d.ts → builders-BEoMaLal.d.ts} +180 -6
  4. package/dist/{types-8MLtztK3.d.ts → config-schemas-DnEBhIg0.d.cts} +1 -158
  5. package/dist/{types-BHBHRm0a.d.cts → config-schemas-DnEBhIg0.d.ts} +1 -158
  6. package/dist/dispersa-DF2ZkG2O.d.ts +567 -0
  7. package/dist/dispersa-DJeCN0cP.d.cts +567 -0
  8. package/dist/index.cjs +2117 -2098
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +7 -7
  11. package/dist/index.d.ts +7 -7
  12. package/dist/index.js +2117 -2098
  13. package/dist/index.js.map +1 -1
  14. package/dist/{lint.cjs → lint/index.cjs} +2 -2
  15. package/dist/lint/index.cjs.map +1 -0
  16. package/dist/{lint.d.ts → lint/index.d.cts} +8 -7
  17. package/dist/{lint.d.cts → lint/index.d.ts} +8 -7
  18. package/dist/{lint.js → lint/index.js} +2 -2
  19. package/dist/lint/index.js.map +1 -0
  20. package/dist/{renderers.d.ts → output-tree-BRbfWSmG.d.ts} +3 -10
  21. package/dist/{renderers.d.cts → output-tree-Hmi77EMv.d.cts} +3 -10
  22. package/dist/{builders.cjs → outputs/builders.cjs} +30 -30
  23. package/dist/outputs/builders.cjs.map +1 -0
  24. package/dist/outputs/builders.d.cts +7 -0
  25. package/dist/outputs/builders.d.ts +7 -0
  26. package/dist/{builders.js → outputs/builders.js} +30 -30
  27. package/dist/outputs/builders.js.map +1 -0
  28. package/dist/outputs/index.cjs +3750 -0
  29. package/dist/outputs/index.cjs.map +1 -0
  30. package/dist/outputs/index.d.cts +73 -0
  31. package/dist/outputs/index.d.ts +73 -0
  32. package/dist/outputs/index.js +3730 -0
  33. package/dist/outputs/index.js.map +1 -0
  34. package/dist/{filters.cjs → processing/filters/index.cjs} +24 -18
  35. package/dist/processing/filters/index.cjs.map +1 -0
  36. package/dist/processing/filters/index.d.cts +36 -0
  37. package/dist/processing/filters/index.d.ts +36 -0
  38. package/dist/{filters.js → processing/filters/index.js} +24 -18
  39. package/dist/processing/filters/index.js.map +1 -0
  40. package/dist/{preprocessors.cjs → processing/preprocessors/index.cjs} +3 -3
  41. package/dist/processing/preprocessors/index.cjs.map +1 -0
  42. package/dist/processing/preprocessors/index.d.cts +29 -0
  43. package/dist/processing/preprocessors/index.d.ts +29 -0
  44. package/dist/{preprocessors.js → processing/preprocessors/index.js} +3 -3
  45. package/dist/processing/preprocessors/index.js.map +1 -0
  46. package/dist/{transforms.cjs → processing/transforms/index.cjs} +2 -7
  47. package/dist/processing/transforms/index.cjs.map +1 -0
  48. package/dist/{transforms.d.ts → processing/transforms/index.d.cts} +2 -2
  49. package/dist/{transforms.d.cts → processing/transforms/index.d.ts} +2 -2
  50. package/dist/{transforms.js → processing/transforms/index.js} +2 -7
  51. package/dist/processing/transforms/index.js.map +1 -0
  52. package/dist/{errors.cjs → shared/errors/index.cjs} +12 -2
  53. package/dist/shared/errors/index.cjs.map +1 -0
  54. package/dist/{errors-qT4sJgSA.d.ts → shared/errors/index.d.cts} +11 -2
  55. package/dist/{errors-qT4sJgSA.d.cts → shared/errors/index.d.ts} +11 -2
  56. package/dist/{errors.js → shared/errors/index.js} +12 -3
  57. package/dist/shared/errors/index.js.map +1 -0
  58. package/dist/types-B0cI70Bt.d.cts +453 -0
  59. package/dist/types-BxDEUCos.d.ts +453 -0
  60. package/dist/types-DUc4vLZH.d.cts +36 -0
  61. package/dist/types-s3UoDRKl.d.ts +36 -0
  62. package/package.json +26 -36
  63. package/dist/android-CRDfSB3_.d.cts +0 -126
  64. package/dist/android-DANJjjPO.d.ts +0 -126
  65. package/dist/builders.cjs.map +0 -1
  66. package/dist/builders.js.map +0 -1
  67. package/dist/dispersa-BC1kDF5u.d.ts +0 -118
  68. package/dist/dispersa-DL3J_Pmz.d.cts +0 -118
  69. package/dist/errors.cjs.map +0 -1
  70. package/dist/errors.d.cts +0 -1
  71. package/dist/errors.d.ts +0 -1
  72. package/dist/errors.js.map +0 -1
  73. package/dist/filters.cjs.map +0 -1
  74. package/dist/filters.d.cts +0 -83
  75. package/dist/filters.d.ts +0 -83
  76. package/dist/filters.js.map +0 -1
  77. package/dist/index-Dajm5rvM.d.ts +0 -895
  78. package/dist/index-De6SjZYH.d.cts +0 -895
  79. package/dist/lint.cjs.map +0 -1
  80. package/dist/lint.js.map +0 -1
  81. package/dist/preprocessors.cjs.map +0 -1
  82. package/dist/preprocessors.d.cts +0 -17
  83. package/dist/preprocessors.d.ts +0 -17
  84. package/dist/preprocessors.js.map +0 -1
  85. package/dist/renderers.cjs +0 -28
  86. package/dist/renderers.cjs.map +0 -1
  87. package/dist/renderers.js +0 -24
  88. package/dist/renderers.js.map +0 -1
  89. package/dist/transforms.cjs.map +0 -1
  90. package/dist/transforms.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -9,9 +9,9 @@ var module$1 = require('module');
9
9
  var process2 = require('process');
10
10
  var jiti = require('jiti');
11
11
  var jsonPtr = require('json-ptr');
12
- var changeCase = require('change-case');
13
12
  var culori = require('culori');
14
13
  var prettier = require('prettier');
14
+ var changeCase = require('change-case');
15
15
 
16
16
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
17
 
@@ -3369,7 +3369,7 @@ var init_resolver_loader = __esm({
3369
3369
  }
3370
3370
  });
3371
3371
 
3372
- // src/renderers/bundlers/utils.ts
3372
+ // src/outputs/utils.ts
3373
3373
  function sanitizeDataAttributeName(value) {
3374
3374
  return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
3375
3375
  }
@@ -3601,13 +3601,13 @@ function resolveFileName(fileName, modifierInputs) {
3601
3601
  return fileName;
3602
3602
  }
3603
3603
  var init_utils = __esm({
3604
- "src/renderers/bundlers/utils.ts"() {
3604
+ "src/outputs/utils.ts"() {
3605
3605
  init_errors();
3606
3606
  init_token_utils();
3607
3607
  }
3608
3608
  });
3609
3609
 
3610
- // src/renderers/metadata.ts
3610
+ // src/outputs/metadata.ts
3611
3611
  function sanitizeText(text, format) {
3612
3612
  switch (format) {
3613
3613
  case "css":
@@ -3691,13 +3691,13 @@ function buildKotlinDeprecationAnnotation(token) {
3691
3691
  return "@Deprecated";
3692
3692
  }
3693
3693
  var init_metadata = __esm({
3694
- "src/renderers/metadata.ts"() {
3694
+ "src/outputs/metadata.ts"() {
3695
3695
  }
3696
3696
  });
3697
3697
 
3698
- // src/renderers/bundlers/js.ts
3699
- var js_exports = {};
3700
- __export(js_exports, {
3698
+ // src/outputs/js/presets/bundle.ts
3699
+ var bundle_exports = {};
3700
+ __export(bundle_exports, {
3701
3701
  bundleAsJsModule: () => bundleAsJsModule
3702
3702
  });
3703
3703
  function updateStringTracking(state, char) {
@@ -3835,17 +3835,17 @@ async function bundleAsJsModule(bundleData, resolver, options, formatTokens) {
3835
3835
  }
3836
3836
  return assembleJsBundle(metadata, jsBlocks, options?.generateHelper ?? false);
3837
3837
  }
3838
- var init_js = __esm({
3839
- "src/renderers/bundlers/js.ts"() {
3838
+ var init_bundle = __esm({
3839
+ "src/outputs/js/presets/bundle.ts"() {
3840
3840
  init_errors();
3841
3841
  init_metadata();
3842
3842
  init_utils();
3843
3843
  }
3844
3844
  });
3845
3845
 
3846
- // src/renderers/bundlers/json.ts
3847
- var json_exports = {};
3848
- __export(json_exports, {
3846
+ // src/outputs/json/presets/bundle.ts
3847
+ var bundle_exports2 = {};
3848
+ __export(bundle_exports2, {
3849
3849
  bundleAsJson: () => bundleAsJson
3850
3850
  });
3851
3851
  async function bundleAsJson(bundleData, resolver, formatTokens) {
@@ -3864,8 +3864,8 @@ async function bundleAsJson(bundleData, resolver, formatTokens) {
3864
3864
  const bundle = { _meta: metadata, tokens };
3865
3865
  return JSON.stringify(bundle, null, 2);
3866
3866
  }
3867
- var init_json = __esm({
3868
- "src/renderers/bundlers/json.ts"() {
3867
+ var init_bundle2 = __esm({
3868
+ "src/outputs/json/presets/bundle.ts"() {
3869
3869
  init_errors();
3870
3870
  init_utils();
3871
3871
  }
@@ -4109,7 +4109,7 @@ var TypeWriter = class {
4109
4109
  }
4110
4110
  };
4111
4111
 
4112
- // src/build/build-orchestrator.ts
4112
+ // src/engine/build-orchestrator.ts
4113
4113
  init_utils();
4114
4114
  init_errors();
4115
4115
 
@@ -4151,7 +4151,7 @@ function toBuildError(error, outputName) {
4151
4151
  return { message, code: "UNKNOWN", severity: "error" };
4152
4152
  }
4153
4153
 
4154
- // src/build/build-orchestrator.ts
4154
+ // src/engine/build-orchestrator.ts
4155
4155
  var BuildOrchestrator = class {
4156
4156
  constructor(pipeline, outputProcessor) {
4157
4157
  this.pipeline = pipeline;
@@ -4258,7 +4258,15 @@ var BuildOrchestrator = class {
4258
4258
  const metadata = buildMetadata(resolverDoc);
4259
4259
  const settled = await Promise.allSettled(
4260
4260
  config.outputs.map(
4261
- (output) => this.buildSingleOutput(output, permutations, resolverDoc, metadata, buildPath, config, resolver)
4261
+ (output) => this.buildSingleOutput(
4262
+ output,
4263
+ permutations,
4264
+ resolverDoc,
4265
+ metadata,
4266
+ buildPath,
4267
+ config,
4268
+ resolver
4269
+ )
4262
4270
  )
4263
4271
  );
4264
4272
  const result = this.collectSettledResults(settled, config);
@@ -4283,14 +4291,25 @@ var BuildOrchestrator = class {
4283
4291
  await output.hooks.onBuildStart({ config, resolver });
4284
4292
  }
4285
4293
  try {
4286
- const results = await this.processOutput(output, permutations, resolverDoc, metadata, metadata.defaults, buildPath);
4294
+ const results = await this.processOutput(
4295
+ output,
4296
+ permutations,
4297
+ resolverDoc,
4298
+ metadata,
4299
+ metadata.defaults,
4300
+ buildPath
4301
+ );
4287
4302
  if (output.hooks?.onBuildEnd) {
4288
4303
  await output.hooks.onBuildEnd({ success: true, outputs: results });
4289
4304
  }
4290
4305
  return results;
4291
4306
  } catch (error) {
4292
4307
  if (output.hooks?.onBuildEnd) {
4293
- await output.hooks.onBuildEnd({ success: false, outputs: [], errors: [toBuildError(error, output.name)] });
4308
+ await output.hooks.onBuildEnd({
4309
+ success: false,
4310
+ outputs: [],
4311
+ errors: [toBuildError(error, output.name)]
4312
+ });
4294
4313
  }
4295
4314
  throw error;
4296
4315
  }
@@ -4392,10 +4411,10 @@ function applyFilters(tokens, filterList) {
4392
4411
  return result;
4393
4412
  }
4394
4413
 
4395
- // src/build/output-processor.ts
4414
+ // src/engine/output-processor.ts
4396
4415
  init_utils();
4397
4416
 
4398
- // src/renderers/output-tree.ts
4417
+ // src/outputs/output-tree.ts
4399
4418
  var outputTree = (files) => {
4400
4419
  return {
4401
4420
  kind: "outputTree",
@@ -4406,7 +4425,7 @@ var isOutputTree = (value) => {
4406
4425
  return typeof value === "object" && value !== null && value.kind === "outputTree";
4407
4426
  };
4408
4427
 
4409
- // src/build/output-processor.ts
4428
+ // src/engine/output-processor.ts
4410
4429
  init_errors();
4411
4430
  var OutputProcessor = class {
4412
4431
  /**
@@ -4486,7 +4505,7 @@ var OutputProcessor = class {
4486
4505
  }
4487
4506
  };
4488
4507
 
4489
- // src/build/pipeline/token-pipeline.ts
4508
+ // src/engine/pipeline/token-pipeline.ts
4490
4509
  init_resolver_loader();
4491
4510
 
4492
4511
  // src/lint/plugin-loader.ts
@@ -6081,7 +6100,7 @@ var ResolutionEngine = class {
6081
6100
  }
6082
6101
  };
6083
6102
 
6084
- // src/build/pipeline/token-pipeline.ts
6103
+ // src/engine/pipeline/token-pipeline.ts
6085
6104
  init_errors();
6086
6105
  init_token_utils();
6087
6106
  init_validation_handler();
@@ -6095,12 +6114,12 @@ function formatTokenPath(parentPath, name) {
6095
6114
  return parentPath.length > 0 ? `${parentPath.join(".")}.${name}` : name;
6096
6115
  }
6097
6116
 
6098
- // src/tokens/token-parser.ts
6117
+ // src/engine/token-parser.ts
6099
6118
  init_token_utils();
6100
6119
  init_validation_handler();
6101
6120
  init_validator();
6102
6121
 
6103
- // src/tokens/group-extension-resolver.ts
6122
+ // src/engine/group-extension-resolver.ts
6104
6123
  init_errors();
6105
6124
  init_token_utils();
6106
6125
  var GroupExtensionResolver = class {
@@ -6262,7 +6281,7 @@ var GroupExtensionResolver = class {
6262
6281
  }
6263
6282
  };
6264
6283
 
6265
- // src/tokens/token-parser.ts
6284
+ // src/engine/token-parser.ts
6266
6285
  var INVALID_NAME_CHARS_REGEX = /[{}.]/;
6267
6286
  var TokenParser = class {
6268
6287
  validator;
@@ -6544,7 +6563,7 @@ var TokenParser = class {
6544
6563
  }
6545
6564
  };
6546
6565
 
6547
- // src/build/pipeline/token-pipeline.ts
6566
+ // src/engine/pipeline/token-pipeline.ts
6548
6567
  var TokenPipeline = class {
6549
6568
  options;
6550
6569
  validationHandler;
@@ -6945,7 +6964,7 @@ async function generateTypes(tokens, fileName, options) {
6945
6964
  });
6946
6965
  }
6947
6966
 
6948
- // src/tokens/types.ts
6967
+ // src/shared/token-types/types.ts
6949
6968
  function isColorToken(token) {
6950
6969
  return token.$type === "color";
6951
6970
  }
@@ -6970,17 +6989,6 @@ function isTransitionToken(token) {
6970
6989
  function isGradientToken(token) {
6971
6990
  return token.$type === "gradient";
6972
6991
  }
6973
- function nameKebabCase() {
6974
- return {
6975
- transform: (token) => {
6976
- const name = changeCase.kebabCase(token.path.join(" "));
6977
- return {
6978
- ...token,
6979
- name
6980
- };
6981
- }
6982
- };
6983
- }
6984
6992
  function isColorObject(value) {
6985
6993
  return typeof value === "object" && value !== null && "colorSpace" in value && "components" in value;
6986
6994
  }
@@ -7054,1734 +7062,1651 @@ function durationObjectToString(duration) {
7054
7062
  return `${duration.value}${duration.unit}`;
7055
7063
  }
7056
7064
 
7057
- // src/renderers/android.ts
7065
+ // src/outputs/css/renderer.ts
7058
7066
  init_errors();
7059
7067
  init_token_utils();
7068
+
7069
+ // src/outputs/css/presets/bundle.ts
7070
+ init_errors();
7060
7071
  init_utils();
7061
- init_metadata();
7062
- var toSRGB = culori.converter("rgb");
7063
- var toP3 = culori.converter("p3");
7064
- var KOTLIN_KEYWORDS = /* @__PURE__ */ new Set([
7065
- "val",
7066
- "var",
7067
- "fun",
7068
- "class",
7069
- "object",
7070
- "when",
7071
- "is",
7072
- "in",
7073
- "return",
7074
- "break",
7075
- "continue",
7076
- "do",
7077
- "while",
7078
- "for",
7079
- "if",
7080
- "else",
7081
- "try",
7082
- "catch",
7083
- "throw",
7084
- "as",
7085
- "this",
7086
- "super",
7087
- "null",
7088
- "true",
7089
- "false"
7090
- ]);
7091
- var KOTLIN_TYPE_GROUP_MAP = {
7092
- color: "Colors",
7093
- dimension: "Spacing",
7094
- fontFamily: "Fonts",
7095
- fontWeight: "FontWeights",
7096
- duration: "Durations",
7097
- shadow: "Shadows",
7098
- typography: "Typography",
7099
- number: "Numbers",
7100
- cubicBezier: "Animations",
7101
- border: "Borders"
7072
+ var REF_PREFIX_SETS = "#/sets/";
7073
+ var REF_PREFIX_MODIFIERS = "#/modifiers/";
7074
+ var getSourceSet = (token) => {
7075
+ if (typeof token !== "object" || token === null) {
7076
+ return void 0;
7077
+ }
7078
+ return "_sourceSet" in token && typeof token._sourceSet === "string" ? token._sourceSet : void 0;
7102
7079
  };
7103
- function resolveColorFormat(format) {
7104
- if (format === "argb_floats" || format === "argb_float") {
7105
- return "argb_float";
7080
+ var getSourceModifier = (token) => {
7081
+ if (typeof token !== "object" || token === null) {
7082
+ return void 0;
7106
7083
  }
7107
- return "argb_hex";
7108
- }
7109
- function escapeKotlinString(str) {
7110
- return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
7084
+ return "_sourceModifier" in token && typeof token._sourceModifier === "string" ? token._sourceModifier : void 0;
7085
+ };
7086
+ async function bundleAsCss(bundleData, resolver, options, formatTokens) {
7087
+ const baseItem = bundleData.find((item) => item.isBase);
7088
+ if (!baseItem) {
7089
+ throw new exports.BasePermutationError("Base permutation not found in bundle data");
7090
+ }
7091
+ if (!formatTokens) {
7092
+ throw new exports.ConfigurationError("CSS formatter was not provided");
7093
+ }
7094
+ const orderedBundleData = orderBundleData(bundleData, resolver, baseItem);
7095
+ const cssBlocks = [];
7096
+ for (const item of orderedBundleData) {
7097
+ if (item.isBase) {
7098
+ const blocks = await formatBasePermutation(item, resolver, options, formatTokens);
7099
+ cssBlocks.push(...blocks);
7100
+ continue;
7101
+ }
7102
+ const block = await formatModifierPermutation(item, baseItem, options, formatTokens);
7103
+ if (block) {
7104
+ cssBlocks.push(block);
7105
+ }
7106
+ }
7107
+ return cssBlocks.join("\n\n");
7111
7108
  }
7112
- function formatKotlinNumber(value) {
7113
- return Number.isInteger(value) ? `${value}.0` : String(value);
7109
+ async function formatBasePermutation({ tokens, modifierInputs }, resolver, options, formatTokens) {
7110
+ const firstModifierName = resolver.modifiers ? Object.keys(resolver.modifiers)[0] : "";
7111
+ const modifier = firstModifierName ?? "";
7112
+ const context = modifierInputs[modifier] ?? "";
7113
+ const selector = resolveSelector(options?.selector, modifier, context, true, modifierInputs);
7114
+ const mediaQuery = resolveMediaQuery(options?.mediaQuery, modifier, context, true, modifierInputs);
7115
+ const referenceTokens = stripInternalMetadata(tokens);
7116
+ const defaultBlocks = buildDefaultLayerBlocks(tokens, modifierInputs, resolver);
7117
+ const cssBlocks = [];
7118
+ for (const block of defaultBlocks) {
7119
+ const cleanTokens = stripInternalMetadata(block.tokens);
7120
+ const css2 = await formatTokens(cleanTokens, {
7121
+ selector,
7122
+ mediaQuery,
7123
+ minify: options?.minify,
7124
+ referenceTokens
7125
+ });
7126
+ const header = block.description ? `/* ${block.key} */
7127
+ /* ${block.description} */` : `/* ${block.key} */`;
7128
+ cssBlocks.push(`${header}
7129
+ ${css2}`);
7130
+ }
7131
+ return cssBlocks;
7114
7132
  }
7115
- function roundComponent(value) {
7116
- return Math.round(value * 1e3) / 1e3;
7133
+ async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, options, formatTokens) {
7134
+ const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
7135
+ if (differenceCount > 1) {
7136
+ return void 0;
7137
+ }
7138
+ const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
7139
+ let tokensToInclude = filterTokensBySource(tokens, expectedSource);
7140
+ const hasSourceMetadata = Object.values(tokens).some(
7141
+ (token) => token != null && getSourceModifier(token) !== void 0
7142
+ );
7143
+ if (Object.keys(tokensToInclude).length === 0 && !hasSourceMetadata) {
7144
+ tokensToInclude = tokens;
7145
+ }
7146
+ if (Object.keys(tokensToInclude).length === 0) {
7147
+ return void 0;
7148
+ }
7149
+ const [modifier, context] = parseModifierSource(expectedSource);
7150
+ const cleanTokens = stripInternalMetadata(tokensToInclude);
7151
+ const referenceTokens = stripInternalMetadata(tokens);
7152
+ const selector = resolveSelector(options?.selector, modifier, context, false, modifierInputs);
7153
+ const mediaQuery = resolveMediaQuery(
7154
+ options?.mediaQuery,
7155
+ modifier,
7156
+ context,
7157
+ false,
7158
+ modifierInputs
7159
+ );
7160
+ const css2 = await formatTokens(cleanTokens, {
7161
+ selector,
7162
+ mediaQuery,
7163
+ minify: options?.minify,
7164
+ referenceTokens
7165
+ });
7166
+ return `/* Modifier: ${modifier}=${context} */
7167
+ ${css2}`;
7117
7168
  }
7118
- function toResourceName(family) {
7119
- return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
7169
+ function addLayerBlock(blocks, included, key, blockTokens, description) {
7170
+ if (Object.keys(blockTokens).length === 0) {
7171
+ return;
7172
+ }
7173
+ for (const k of Object.keys(blockTokens)) {
7174
+ included.add(k);
7175
+ }
7176
+ blocks.push({ key, description, tokens: blockTokens });
7120
7177
  }
7121
- var AndroidRenderer = class {
7122
- async format(context, options) {
7123
- if (!options?.packageName) {
7124
- throw new exports.ConfigurationError(
7125
- `Output "${context.output.name}": packageName is required for Android output`
7126
- );
7127
- }
7128
- const visibility = options?.visibility;
7129
- const opts = {
7130
- preset: options?.preset ?? "standalone",
7131
- packageName: options.packageName,
7132
- objectName: options?.objectName ?? "DesignTokens",
7133
- colorFormat: resolveColorFormat(options?.colorFormat),
7134
- colorSpace: options?.colorSpace ?? "sRGB",
7135
- structure: options?.structure ?? "nested",
7136
- visibility,
7137
- visPrefix: visibility ? `${visibility} ` : "",
7138
- indent: options?.indent ?? 4
7139
- };
7140
- if (opts.preset === "bundle") {
7141
- return await this.formatBundle(context, opts);
7178
+ function collectSetTokens(tokens, setName, included) {
7179
+ const result = {};
7180
+ for (const [name, token] of Object.entries(tokens)) {
7181
+ if (!included.has(name) && getSourceSet(token) === setName) {
7182
+ result[name] = token;
7142
7183
  }
7143
- return await this.formatStandalone(context, opts);
7144
7184
  }
7145
- // -----------------------------------------------------------------------
7146
- // Token tree (nested mode)
7147
- // -----------------------------------------------------------------------
7148
- buildTokenTree(tokens) {
7149
- const root = { children: /* @__PURE__ */ new Map() };
7150
- for (const [, token] of getSortedTokenEntries(tokens)) {
7151
- let current = root;
7152
- const segments = token.path;
7153
- for (let i = 0; i < segments.length - 1; i++) {
7154
- const seg = segments[i];
7155
- if (!current.children.has(seg)) {
7156
- current.children.set(seg, { children: /* @__PURE__ */ new Map() });
7157
- }
7158
- current = current.children.get(seg);
7159
- }
7160
- const leafName = segments[segments.length - 1] ?? token.name;
7161
- const leaf = current.children.get(leafName) ?? { children: /* @__PURE__ */ new Map() };
7162
- leaf.token = token;
7163
- current.children.set(leafName, leaf);
7185
+ return result;
7186
+ }
7187
+ function collectModifierTokens(tokens, expectedSource, included) {
7188
+ const result = {};
7189
+ for (const [name, token] of Object.entries(tokens)) {
7190
+ if (!included.has(name) && (getSourceModifier(token) ?? "").toLowerCase() === expectedSource) {
7191
+ result[name] = token;
7164
7192
  }
7165
- return root;
7166
- }
7167
- // -----------------------------------------------------------------------
7168
- // Flat structure grouping
7169
- // -----------------------------------------------------------------------
7170
- /**
7171
- * Builds a flattened camelCase name from a token's path, stripping the
7172
- * type prefix segment (which is already represented by the group object).
7173
- */
7174
- buildFlatKotlinName(token) {
7175
- const path7 = token.path;
7176
- const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
7177
- const joined = withoutTypePrefix.join("_");
7178
- return toSafeIdentifier(joined, KOTLIN_KEYWORDS, false);
7179
7193
  }
7180
- // -----------------------------------------------------------------------
7181
- // Rendering
7182
- // -----------------------------------------------------------------------
7183
- formatTokens(tokens, options) {
7184
- if (options.structure === "flat") {
7185
- return this.formatAsFlat(tokens, options);
7194
+ return result;
7195
+ }
7196
+ function collectRemainder(tokens, included) {
7197
+ const result = {};
7198
+ for (const [name, token] of Object.entries(tokens)) {
7199
+ if (!included.has(name)) {
7200
+ result[name] = token;
7186
7201
  }
7187
- return this.formatAsNested(tokens, options);
7188
7202
  }
7189
- formatAsNested(tokens, options) {
7190
- const tokenTypes = this.collectTokenTypesFromEntries(tokens);
7191
- const tree = this.buildTokenTree(tokens);
7192
- return this.buildFile(tokenTypes, options, (lines) => {
7193
- lines.push(`@Suppress("unused")`);
7194
- lines.push(`${options.visPrefix}object ${options.objectName} {`);
7195
- this.renderTreeChildren(lines, tree, 1, options);
7196
- lines.push("}");
7197
- });
7198
- }
7199
- formatAsFlat(tokens, options) {
7200
- const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
7201
- const tokenTypes = this.collectTokenTypesFromEntries(tokens);
7202
- return this.buildFile(tokenTypes, options, (lines) => {
7203
- lines.push(`@Suppress("unused")`);
7204
- lines.push(`${options.visPrefix}object ${options.objectName} {`);
7205
- this.renderFlatGroups(lines, groups, 1, options);
7206
- lines.push("}");
7207
- });
7208
- }
7209
- /**
7210
- * Shared file preamble: header, package, imports, optional ShadowToken class.
7211
- * The `renderBody` callback appends the main object(s) to `lines`.
7212
- */
7213
- buildFile(tokenTypes, options, renderBody) {
7214
- const imports = this.collectImports(tokenTypes, options);
7215
- const lines = [];
7216
- lines.push(buildGeneratedFileHeader());
7217
- lines.push("");
7218
- lines.push(`package ${options.packageName}`);
7219
- lines.push("");
7220
- for (const imp of imports) {
7221
- lines.push(`import ${imp}`);
7222
- }
7223
- if (imports.length > 0) {
7224
- lines.push("");
7225
- }
7226
- if (tokenTypes.has("shadow")) {
7227
- lines.push(...this.buildShadowTokenClass(options));
7228
- lines.push("");
7229
- }
7230
- renderBody(lines);
7231
- lines.push("");
7232
- return lines.join("\n");
7233
- }
7234
- renderFlatGroups(lines, groups, baseDepth, options) {
7235
- const groupIndent = indentStr(options.indent, baseDepth);
7236
- const valIndent = indentStr(options.indent, baseDepth + 1);
7237
- for (const group of groups) {
7238
- lines.push(`${groupIndent}${options.visPrefix}object ${group.name} {`);
7239
- for (const token of group.tokens) {
7240
- const kotlinName = this.buildFlatKotlinName(token);
7241
- const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
7242
- const annotation = this.typeAnnotationSuffix(token);
7243
- const descriptionComment = buildTokenDescriptionComment(token, "kotlin");
7244
- if (descriptionComment) {
7245
- lines.push(`${valIndent}${descriptionComment}`);
7246
- }
7247
- const deprecation = buildKotlinDeprecationAnnotation(token);
7248
- if (deprecation) {
7249
- lines.push(`${valIndent}${deprecation}`);
7250
- }
7251
- lines.push(
7252
- `${valIndent}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`
7253
- );
7254
- }
7255
- lines.push(`${groupIndent}}`);
7256
- lines.push("");
7257
- }
7258
- }
7259
- renderTreeChildren(lines, node, depth, options) {
7260
- const pad = indentStr(options.indent, depth);
7261
- const entries = Array.from(node.children.entries());
7262
- for (let idx = 0; idx < entries.length; idx++) {
7263
- const [key, child] = entries[idx];
7264
- if (child.token && child.children.size === 0) {
7265
- this.renderLeaf(lines, key, child.token, depth, options);
7266
- } else if (child.children.size > 0 && !child.token) {
7267
- const objectName = toSafeIdentifier(key, KOTLIN_KEYWORDS, true);
7268
- lines.push(`${pad}${options.visPrefix}object ${objectName} {`);
7269
- this.renderTreeChildren(lines, child, depth + 1, options);
7270
- lines.push(`${pad}}`);
7271
- if (idx < entries.length - 1) {
7272
- lines.push("");
7273
- }
7274
- } else {
7275
- this.renderLeaf(lines, key, child.token, depth, options);
7276
- this.renderTreeChildren(lines, child, depth, options);
7277
- }
7278
- }
7279
- }
7280
- renderLeaf(lines, key, token, depth, options) {
7281
- const pad = indentStr(options.indent, depth);
7282
- const kotlinName = toSafeIdentifier(key, KOTLIN_KEYWORDS, false);
7283
- const kotlinValue = this.formatKotlinValue(token, options, depth);
7284
- const annotation = this.typeAnnotationSuffix(token);
7285
- const descriptionComment = buildTokenDescriptionComment(token, "kotlin");
7286
- if (descriptionComment) {
7287
- lines.push(`${pad}${descriptionComment}`);
7288
- }
7289
- const deprecation = buildKotlinDeprecationAnnotation(token);
7290
- if (deprecation) {
7291
- lines.push(`${pad}${deprecation}`);
7292
- }
7293
- lines.push(`${pad}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`);
7294
- }
7295
- // -----------------------------------------------------------------------
7296
- // Shadow data class
7297
- // -----------------------------------------------------------------------
7298
- buildShadowTokenClass(options) {
7299
- const i1 = indentStr(options.indent, 1);
7300
- return [
7301
- "@Immutable",
7302
- `${options.visPrefix}data class ShadowToken(`,
7303
- `${i1}val color: Color,`,
7304
- `${i1}val elevation: Dp,`,
7305
- `${i1}val offsetX: Dp,`,
7306
- `${i1}val offsetY: Dp,`,
7307
- ")"
7308
- ];
7309
- }
7310
- // -----------------------------------------------------------------------
7311
- // Imports (tree-shaken)
7312
- // -----------------------------------------------------------------------
7313
- collectImports(tokenTypes, options) {
7314
- const imports = /* @__PURE__ */ new Set();
7315
- const ns = "androidx.compose";
7316
- const hasColors = tokenTypes.has("color") || tokenTypes.has("shadow") || tokenTypes.has("border");
7317
- if (hasColors) {
7318
- imports.add(`${ns}.ui.graphics.Color`);
7319
- }
7320
- if (tokenTypes.has("dimension") || tokenTypes.has("shadow") || tokenTypes.has("border")) {
7321
- imports.add(`${ns}.ui.unit.Dp`);
7322
- imports.add(`${ns}.ui.unit.dp`);
7323
- }
7324
- if (tokenTypes.has("typography") || tokenTypes.has("fontFamily")) {
7325
- imports.add(`${ns}.ui.text.TextStyle`);
7326
- imports.add(`${ns}.ui.unit.sp`);
7327
- }
7328
- if (tokenTypes.has("typography") || tokenTypes.has("fontWeight")) {
7329
- imports.add(`${ns}.ui.text.font.FontWeight`);
7330
- }
7331
- if (tokenTypes.has("fontFamily")) {
7332
- imports.add(`${ns}.ui.text.font.FontFamily`);
7333
- }
7334
- if (tokenTypes.has("duration")) {
7335
- imports.add("kotlin.time.Duration");
7336
- imports.add("kotlin.time.Duration.Companion.milliseconds");
7337
- imports.add("kotlin.time.Duration.Companion.seconds");
7338
- }
7339
- if (tokenTypes.has("cubicBezier")) {
7340
- imports.add(`${ns}.animation.core.CubicBezierEasing`);
7341
- }
7342
- if (tokenTypes.has("shadow")) {
7343
- imports.add(`${ns}.runtime.Immutable`);
7344
- }
7345
- if (tokenTypes.has("border")) {
7346
- imports.add(`${ns}.foundation.BorderStroke`);
7347
- }
7348
- if (options.colorSpace === "displayP3" && hasColors) {
7349
- imports.add(`${ns}.ui.graphics.colorspace.ColorSpaces`);
7350
- }
7351
- return Array.from(imports).sort();
7352
- }
7353
- collectTokenTypesFromEntries(tokens) {
7354
- const types = /* @__PURE__ */ new Set();
7355
- for (const [, token] of Object.entries(tokens)) {
7356
- if (token.$type) {
7357
- types.add(token.$type);
7358
- }
7359
- }
7360
- return types;
7361
- }
7362
- // -----------------------------------------------------------------------
7363
- // Type annotations
7364
- // -----------------------------------------------------------------------
7365
- getTypeAnnotation(token) {
7366
- switch (token.$type) {
7367
- case "color":
7368
- return "Color";
7369
- case "dimension":
7370
- return "Dp";
7371
- case "fontFamily":
7372
- return "FontFamily";
7373
- case "fontWeight":
7374
- return "FontWeight";
7375
- case "duration":
7376
- return "Duration";
7377
- case "shadow":
7378
- return "ShadowToken";
7379
- case "cubicBezier":
7380
- return "CubicBezierEasing";
7381
- case "number":
7382
- return "Double";
7383
- case "typography":
7384
- return "TextStyle";
7385
- case "border":
7386
- return "BorderStroke";
7387
- default: {
7388
- const value = token.$value;
7389
- if (typeof value === "string") {
7390
- return "String";
7391
- }
7392
- if (typeof value === "boolean") {
7393
- return "Boolean";
7394
- }
7395
- if (typeof value === "number") {
7396
- return "Double";
7397
- }
7398
- return void 0;
7399
- }
7400
- }
7401
- }
7402
- typeAnnotationSuffix(token) {
7403
- const type = this.getTypeAnnotation(token);
7404
- return type ? `: ${type}` : "";
7405
- }
7406
- // -----------------------------------------------------------------------
7407
- // Value formatting
7408
- // -----------------------------------------------------------------------
7409
- formatKotlinValue(token, options, depth) {
7410
- const value = token.$value;
7411
- if (token.$type === "color") {
7412
- return this.formatColorValue(value, options);
7413
- }
7414
- if (token.$type === "dimension") {
7415
- return this.formatDimensionValue(value);
7416
- }
7417
- if (token.$type === "fontFamily") {
7418
- return this.formatFontFamilyValue(value);
7419
- }
7420
- if (token.$type === "fontWeight") {
7421
- return this.formatFontWeightValue(value);
7422
- }
7423
- if (token.$type === "duration") {
7424
- return this.formatDurationValue(value);
7425
- }
7426
- if (token.$type === "shadow") {
7427
- return this.formatShadowValue(value, options, depth);
7428
- }
7429
- if (token.$type === "typography") {
7430
- return this.formatTypographyValue(value, options, depth);
7431
- }
7432
- if (token.$type === "border") {
7433
- return this.formatBorderValue(value, options);
7434
- }
7435
- if (token.$type === "number") {
7436
- return typeof value === "number" ? formatKotlinNumber(value) : String(value);
7437
- }
7438
- if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
7439
- return `CubicBezierEasing(${value[0]}f, ${value[1]}f, ${value[2]}f, ${value[3]}f)`;
7440
- }
7441
- if (typeof value === "string") {
7442
- return `"${escapeKotlinString(value)}"`;
7443
- }
7444
- if (typeof value === "number") {
7445
- return formatKotlinNumber(value);
7446
- }
7447
- if (typeof value === "boolean") {
7448
- return value ? "true" : "false";
7449
- }
7450
- return `"${escapeKotlinString(String(value))}"`;
7451
- }
7452
- formatColorValue(value, options) {
7453
- if (!isColorObject(value)) {
7454
- if (typeof value === "string") {
7455
- const hex = value.replace("#", "");
7456
- if (/^[0-9a-fA-F]{6,8}$/.test(hex)) {
7457
- const argb = hex.length === 8 ? hex : `FF${hex}`;
7458
- return `Color(0x${argb.toUpperCase()})`;
7459
- }
7460
- }
7461
- return "Color.Unspecified";
7462
- }
7463
- const colorObj = value;
7464
- const alpha = colorObj.alpha ?? 1;
7465
- if (options.colorFormat === "argb_float" || options.colorSpace === "displayP3") {
7466
- return this.formatFloatColor(colorObj, alpha, options);
7467
- }
7468
- return this.formatHexColor(colorObj, alpha);
7469
- }
7470
- formatFloatColor(colorObj, alpha, options) {
7471
- if (options.colorSpace === "displayP3") {
7472
- const p3 = toP3(dtcgObjectToCulori(colorObj));
7473
- const r2 = roundComponent(p3?.r ?? 0);
7474
- const g2 = roundComponent(p3?.g ?? 0);
7475
- const b2 = roundComponent(p3?.b ?? 0);
7476
- return `Color(${r2}f, ${g2}f, ${b2}f, ${roundComponent(alpha)}f, ColorSpaces.DisplayP3)`;
7203
+ return result;
7204
+ }
7205
+ function buildSetLayerBlocks(tokens, resolver) {
7206
+ const blocks = [];
7207
+ const included = /* @__PURE__ */ new Set();
7208
+ for (const item of resolver.resolutionOrder) {
7209
+ const ref = item.$ref;
7210
+ if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX_SETS)) {
7211
+ continue;
7477
7212
  }
7478
- const rgb = toSRGB(dtcgObjectToCulori(colorObj));
7479
- const r = roundComponent(rgb?.r ?? 0);
7480
- const g = roundComponent(rgb?.g ?? 0);
7481
- const b = roundComponent(rgb?.b ?? 0);
7482
- return `Color(${r}f, ${g}f, ${b}f, ${roundComponent(alpha)}f)`;
7213
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7214
+ addLayerBlock(
7215
+ blocks,
7216
+ included,
7217
+ `Set: ${setName}`,
7218
+ collectSetTokens(tokens, setName, included),
7219
+ resolver.sets?.[setName]?.description
7220
+ );
7483
7221
  }
7484
- formatHexColor(colorObj, alpha) {
7485
- const hex = colorObjectToHex(colorObj);
7486
- const hexClean = hex.replace("#", "");
7487
- if (hexClean.length === 8) {
7488
- const rrggbb = hexClean.slice(0, 6);
7489
- const aa = hexClean.slice(6, 8);
7490
- return `Color(0x${aa.toUpperCase()}${rrggbb.toUpperCase()})`;
7222
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7223
+ return blocks;
7224
+ }
7225
+ function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
7226
+ const blocks = [];
7227
+ const included = /* @__PURE__ */ new Set();
7228
+ const baseInputs = normalizeModifierInputs(baseModifierInputs);
7229
+ for (const item of resolver.resolutionOrder) {
7230
+ const ref = item.$ref;
7231
+ if (typeof ref !== "string") {
7232
+ continue;
7491
7233
  }
7492
- const alphaHex = alpha < 1 ? Math.round(alpha * 255).toString(16).padStart(2, "0").toUpperCase() : "FF";
7493
- return `Color(0x${alphaHex}${hexClean.toUpperCase()})`;
7234
+ processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver);
7494
7235
  }
7495
- formatDimensionValue(value) {
7496
- if (isDimensionObject(value)) {
7497
- const dim = value;
7498
- const dpValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7499
- return `${dpValue}.dp`;
7500
- }
7501
- return typeof value === "number" ? `${value}.dp` : `0.dp`;
7236
+ addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7237
+ return blocks;
7238
+ }
7239
+ function processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver) {
7240
+ if (ref.startsWith(REF_PREFIX_SETS)) {
7241
+ const setName = ref.slice(REF_PREFIX_SETS.length);
7242
+ addLayerBlock(
7243
+ blocks,
7244
+ included,
7245
+ `Set: ${setName}`,
7246
+ collectSetTokens(tokens, setName, included),
7247
+ resolver.sets?.[setName]?.description
7248
+ );
7249
+ return;
7502
7250
  }
7503
- formatFontFamilyValue(value) {
7504
- if (Array.isArray(value)) {
7505
- const primary = value[0];
7506
- if (typeof primary === "string") {
7507
- return this.mapKotlinFontFamily(primary);
7508
- }
7509
- return "FontFamily.Default";
7510
- }
7511
- return typeof value === "string" ? this.mapKotlinFontFamily(value) : "FontFamily.Default";
7251
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7252
+ return;
7512
7253
  }
7513
- mapKotlinFontFamily(family) {
7514
- const normalized = family.toLowerCase().replace(/['"]/g, "").trim();
7515
- const builtIn = {
7516
- "sans-serif": "FontFamily.SansSerif",
7517
- serif: "FontFamily.Serif",
7518
- monospace: "FontFamily.Monospace",
7519
- cursive: "FontFamily.Cursive"
7520
- };
7521
- return builtIn[normalized] ?? `FontFamily.Default // TODO: load "${family}" via Font(R.font.${toResourceName(family)})`;
7254
+ const modifierName = ref.slice(REF_PREFIX_MODIFIERS.length);
7255
+ const modifier = resolver.modifiers?.[modifierName];
7256
+ const selectedContext = baseInputs[modifierName.toLowerCase()];
7257
+ if (!modifier || !selectedContext) {
7258
+ return;
7522
7259
  }
7523
- formatFontWeightValue(value) {
7524
- if (typeof value === "number") {
7525
- return this.numericFontWeight(value);
7260
+ const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7261
+ addLayerBlock(
7262
+ blocks,
7263
+ included,
7264
+ `Modifier: ${modifierName}=${selectedContext} (default)`,
7265
+ collectModifierTokens(tokens, expectedSource, included),
7266
+ modifier.description
7267
+ );
7268
+ }
7269
+ function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs) {
7270
+ const normalizedModifier = modifierName.toLowerCase();
7271
+ const normalizedContext = context.toLowerCase();
7272
+ return bundleData.find((item) => {
7273
+ if (item.isBase) {
7274
+ return false;
7526
7275
  }
7527
- if (typeof value === "string") {
7528
- return this.namedFontWeight(value) ?? "FontWeight.Normal";
7276
+ const inputs = normalizeModifierInputs(item.modifierInputs);
7277
+ if (inputs[normalizedModifier] !== normalizedContext) {
7278
+ return false;
7529
7279
  }
7530
- return "FontWeight.Normal";
7280
+ return Object.entries(baseInputs).every(([k, v]) => k === normalizedModifier || inputs[k] === v);
7281
+ });
7282
+ }
7283
+ function pushUniqueBundleItem(ordered, includedKeys, item) {
7284
+ if (!item) {
7285
+ return;
7531
7286
  }
7532
- numericFontWeight(weight) {
7533
- if (weight <= 100) {
7534
- return "FontWeight.Thin";
7535
- }
7536
- if (weight <= 200) {
7537
- return "FontWeight.ExtraLight";
7287
+ const key = stableInputsKey(item.modifierInputs);
7288
+ if (includedKeys.has(key)) {
7289
+ return;
7290
+ }
7291
+ includedKeys.add(key);
7292
+ ordered.push(item);
7293
+ }
7294
+ function appendModifierPermutations(bundleData, modifiers, orderedNames, baseInputs, ordered, includedKeys) {
7295
+ for (const modifierName of orderedNames) {
7296
+ const modifierDef = modifiers[modifierName];
7297
+ if (!modifierDef) {
7298
+ continue;
7538
7299
  }
7539
- if (weight <= 300) {
7540
- return "FontWeight.Light";
7300
+ const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
7301
+ for (const ctx of Object.keys(modifierDef.contexts)) {
7302
+ if (defaultValue === ctx.toLowerCase()) {
7303
+ continue;
7304
+ }
7305
+ pushUniqueBundleItem(
7306
+ ordered,
7307
+ includedKeys,
7308
+ findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs)
7309
+ );
7541
7310
  }
7542
- if (weight <= 400) {
7543
- return "FontWeight.Normal";
7311
+ }
7312
+ }
7313
+ function orderBundleData(bundleData, resolver, baseItem) {
7314
+ const modifiers = resolver.modifiers;
7315
+ if (!modifiers) {
7316
+ return bundleData;
7317
+ }
7318
+ const baseInputs = normalizeModifierInputs(baseItem.modifierInputs);
7319
+ const orderedModifierNames = getOrderedModifierNames(resolver);
7320
+ if (orderedModifierNames.length === 0) {
7321
+ return bundleData;
7322
+ }
7323
+ const firstModifierDef = modifiers[orderedModifierNames[0] ?? ""];
7324
+ if (!firstModifierDef) {
7325
+ return bundleData;
7326
+ }
7327
+ const includedKeys = /* @__PURE__ */ new Set();
7328
+ const ordered = [];
7329
+ pushUniqueBundleItem(ordered, includedKeys, baseItem);
7330
+ appendModifierPermutations(
7331
+ bundleData,
7332
+ modifiers,
7333
+ orderedModifierNames,
7334
+ baseInputs,
7335
+ ordered,
7336
+ includedKeys
7337
+ );
7338
+ return ordered.length > 0 ? ordered : bundleData;
7339
+ }
7340
+ function getOrderedModifierNames(resolver) {
7341
+ const modifiers = resolver.modifiers ?? {};
7342
+ const seen = /* @__PURE__ */ new Set();
7343
+ const ordered = [];
7344
+ for (const item of resolver.resolutionOrder) {
7345
+ const ref = item.$ref;
7346
+ if (typeof ref !== "string") {
7347
+ continue;
7544
7348
  }
7545
- if (weight <= 500) {
7546
- return "FontWeight.Medium";
7349
+ if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7350
+ continue;
7547
7351
  }
7548
- if (weight <= 600) {
7549
- return "FontWeight.SemiBold";
7352
+ const name = ref.slice(REF_PREFIX_MODIFIERS.length);
7353
+ if (seen.has(name)) {
7354
+ continue;
7550
7355
  }
7551
- if (weight <= 700) {
7552
- return "FontWeight.Bold";
7356
+ if (!(name in modifiers)) {
7357
+ continue;
7553
7358
  }
7554
- if (weight <= 800) {
7555
- return "FontWeight.ExtraBold";
7359
+ ordered.push(name);
7360
+ seen.add(name);
7361
+ }
7362
+ for (const name of Object.keys(modifiers)) {
7363
+ if (seen.has(name)) {
7364
+ continue;
7556
7365
  }
7557
- return "FontWeight.Black";
7366
+ ordered.push(name);
7367
+ seen.add(name);
7558
7368
  }
7559
- namedFontWeight(name) {
7560
- const map = {
7561
- thin: "FontWeight.Thin",
7562
- extralight: "FontWeight.ExtraLight",
7563
- ultralight: "FontWeight.ExtraLight",
7564
- light: "FontWeight.Light",
7565
- regular: "FontWeight.Normal",
7566
- normal: "FontWeight.Normal",
7567
- medium: "FontWeight.Medium",
7568
- semibold: "FontWeight.SemiBold",
7569
- demibold: "FontWeight.SemiBold",
7570
- bold: "FontWeight.Bold",
7571
- extrabold: "FontWeight.ExtraBold",
7572
- heavy: "FontWeight.ExtraBold",
7573
- black: "FontWeight.Black",
7574
- ultrabold: "FontWeight.Black"
7369
+ return ordered;
7370
+ }
7371
+ function stableInputsKey(modifierInputs) {
7372
+ const normalized = normalizeModifierInputs(modifierInputs);
7373
+ return Object.keys(normalized).sort().map((k) => `${k}=${normalized[k]}`).join("|");
7374
+ }
7375
+
7376
+ // src/outputs/css/renderer.ts
7377
+ init_utils();
7378
+ init_metadata();
7379
+ var CssRenderer = class _CssRenderer {
7380
+ async format(context, options) {
7381
+ const opts = {
7382
+ preset: options?.preset ?? "bundle",
7383
+ selector: options?.selector,
7384
+ mediaQuery: options?.mediaQuery,
7385
+ minify: options?.minify ?? false,
7386
+ preserveReferences: options?.preserveReferences ?? false
7575
7387
  };
7576
- return map[name.toLowerCase()];
7577
- }
7578
- formatDurationValue(value) {
7579
- if (isDurationObject(value)) {
7580
- return value.unit === "ms" ? `${value.value}.milliseconds` : `${value.value}.seconds`;
7581
- }
7582
- return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
7583
- }
7584
- formatShadowValue(value, options, depth) {
7585
- if (Array.isArray(value) && value.length > 0) {
7586
- return this.formatSingleShadow(value[0], options, depth);
7388
+ if (opts.preset === "bundle") {
7389
+ return await this.formatBundle(context, opts);
7587
7390
  }
7588
- if (typeof value === "object" && value !== null) {
7589
- return this.formatSingleShadow(value, options, depth);
7391
+ if (opts.preset === "modifier") {
7392
+ return await this.formatModifier(context, opts);
7590
7393
  }
7591
- return "ShadowToken(color = Color.Unspecified, elevation = 0.dp, offsetX = 0.dp, offsetY = 0.dp)";
7592
- }
7593
- formatSingleShadow(shadow, options, depth) {
7594
- const color = isColorObject(shadow.color) ? this.formatColorValue(shadow.color, options) : "Color.Black";
7595
- const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
7596
- const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
7597
- const offsetY = isDimensionObject(shadow.offsetY) ? this.formatDimensionValue(shadow.offsetY) : "0.dp";
7598
- const propIndent = indentStr(options.indent, depth + 1);
7599
- const closeIndent = indentStr(options.indent, depth);
7600
- return [
7601
- "ShadowToken(",
7602
- `${propIndent}color = ${color},`,
7603
- `${propIndent}elevation = ${elevation},`,
7604
- `${propIndent}offsetX = ${offsetX},`,
7605
- `${propIndent}offsetY = ${offsetY},`,
7606
- `${closeIndent})`
7607
- ].join("\n");
7394
+ return await this.formatStandalone(context, opts);
7608
7395
  }
7609
- formatBorderValue(value, options) {
7610
- if (typeof value !== "object" || value === null) {
7611
- return "BorderStroke(0.dp, Color.Unspecified)";
7612
- }
7613
- const border = value;
7614
- const width = isDimensionObject(border.width) ? this.formatDimensionValue(border.width) : "0.dp";
7615
- const color = isColorObject(border.color) ? this.formatColorValue(border.color, options) : "Color.Unspecified";
7616
- return `BorderStroke(${width}, ${color})`;
7396
+ static PRETTIER_PRINT_WIDTH = 80;
7397
+ static PRETTIER_TAB_WIDTH = 2;
7398
+ /**
7399
+ * Format tokens as CSS custom properties
7400
+ *
7401
+ * Converts resolved design tokens into CSS format with configurable selector,
7402
+ * media queries, and formatting options. Supports DTCG color and dimension objects.
7403
+ *
7404
+ * Note: This method expects selector and mediaQuery to be resolved strings.
7405
+ * Function-based selectors should be resolved before calling format().
7406
+ *
7407
+ * @param tokens - Resolved tokens to format
7408
+ * @param options - CSS formatting options (selector, media query, minify, etc.)
7409
+ * @returns Formatted CSS string with custom properties
7410
+ */
7411
+ async formatTokens(tokens, options) {
7412
+ const opts = {
7413
+ preset: "bundle",
7414
+ selector: ":root",
7415
+ mediaQuery: "",
7416
+ minify: false,
7417
+ preserveReferences: false,
7418
+ ...options,
7419
+ referenceTokens: options?.referenceTokens ?? tokens
7420
+ };
7421
+ const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
7422
+ const referenceTokens = opts.referenceTokens;
7423
+ const lines = [];
7424
+ this.buildCssBlock(lines, sortedTokens, opts.selector, tokens, referenceTokens, opts);
7425
+ const cssString = lines.join("");
7426
+ return opts.minify ? cssString : await this.formatWithPrettier(cssString);
7617
7427
  }
7618
- formatTypographyValue(value, options, depth) {
7619
- if (typeof value !== "object" || value === null) {
7620
- return "TextStyle()";
7428
+ buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
7429
+ const indent = opts.minify ? "" : " ";
7430
+ const newline = opts.minify ? "" : "\n";
7431
+ const space = opts.minify ? "" : " ";
7432
+ const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
7433
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
7434
+ if (hasMediaQuery) {
7435
+ lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
7436
+ lines.push(`${indent}${selector}${space}{${newline}`);
7437
+ } else {
7438
+ lines.push(`${selector}${space}{${newline}`);
7621
7439
  }
7622
- const typo = value;
7623
- const parts = [];
7624
- if (isDimensionObject(typo.fontSize)) {
7625
- const dim = typo.fontSize;
7626
- const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7627
- parts.push(`fontSize = ${spValue}.sp`);
7440
+ for (const token of groupTokens) {
7441
+ this.pushTokenLines(
7442
+ lines,
7443
+ token,
7444
+ tokens,
7445
+ referenceTokens,
7446
+ opts.preserveReferences ?? false,
7447
+ tokenIndent,
7448
+ newline,
7449
+ space
7450
+ );
7628
7451
  }
7629
- if (typo.fontWeight != null) {
7630
- parts.push(`fontWeight = ${this.formatFontWeightValue(typo.fontWeight)}`);
7452
+ if (hasMediaQuery) {
7453
+ lines.push(`${indent}}${newline}`);
7631
7454
  }
7632
- if (typo.lineHeight != null && typeof typo.lineHeight === "number") {
7633
- if (isDimensionObject(typo.fontSize)) {
7634
- const dim = typo.fontSize;
7635
- const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7636
- const lineHeightSp = Math.round(spValue * typo.lineHeight * 100) / 100;
7637
- parts.push(`lineHeight = ${lineHeightSp}.sp`);
7638
- }
7455
+ lines.push(`}${newline}${newline}`);
7456
+ }
7457
+ pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
7458
+ const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
7459
+ const deprecationComment = buildTokenDeprecationComment(token, "css");
7460
+ if (deprecationComment) {
7461
+ lines.push(`${indent}${deprecationComment}${newline}`);
7639
7462
  }
7640
- if (isDimensionObject(typo.letterSpacing)) {
7641
- const dim = typo.letterSpacing;
7642
- const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7643
- parts.push(`letterSpacing = ${spValue}.sp`);
7463
+ const descriptionComment = buildTokenDescriptionComment(token, "css");
7464
+ if (descriptionComment) {
7465
+ lines.push(`${indent}${descriptionComment}${newline}`);
7644
7466
  }
7645
- if (parts.length === 0) {
7646
- return "TextStyle()";
7467
+ for (const entry of entries) {
7468
+ lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
7647
7469
  }
7648
- const propIndent = indentStr(options.indent, depth + 1);
7649
- const closeIndent = indentStr(options.indent, depth);
7650
- return `TextStyle(
7651
- ${parts.map((p) => `${propIndent}${p}`).join(",\n")},
7652
- ${closeIndent})`;
7653
7470
  }
7654
- // -----------------------------------------------------------------------
7655
- // Output: standalone
7656
- // -----------------------------------------------------------------------
7657
- async formatStandalone(context, options) {
7658
- assertFileRequired(
7659
- context.buildPath,
7660
- context.output.file,
7661
- context.output.name,
7662
- "standalone Android"
7663
- );
7664
- const files = {};
7665
- for (const { tokens, modifierInputs } of context.permutations) {
7666
- const processedTokens = stripInternalMetadata(tokens);
7667
- const content = this.formatTokens(processedTokens, options);
7668
- const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
7669
- outputName: context.output.name,
7670
- extension: "kt",
7671
- modifierInputs,
7672
- resolver: context.resolver,
7673
- defaults: context.meta.defaults
7471
+ async formatWithPrettier(css2) {
7472
+ try {
7473
+ return await prettier__default.default.format(css2, {
7474
+ parser: "css",
7475
+ printWidth: _CssRenderer.PRETTIER_PRINT_WIDTH,
7476
+ tabWidth: _CssRenderer.PRETTIER_TAB_WIDTH,
7477
+ useTabs: false
7674
7478
  });
7675
- files[fileName] = content;
7479
+ } catch {
7480
+ return css2;
7676
7481
  }
7677
- return outputTree(files);
7678
- }
7679
- // -----------------------------------------------------------------------
7680
- // Output: bundle
7681
- // -----------------------------------------------------------------------
7682
- async formatBundle(context, options) {
7683
- assertFileRequired(
7684
- context.buildPath,
7685
- context.output.file,
7686
- context.output.name,
7687
- "bundle Android"
7688
- );
7689
- const content = this.formatBundleContent(context, options);
7690
- const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
7691
- outputName: context.output.name,
7692
- extension: "kt",
7693
- modifierInputs: context.meta.basePermutation,
7694
- resolver: context.resolver,
7695
- defaults: context.meta.defaults
7696
- });
7697
- return outputTree({ [fileName]: content });
7698
7482
  }
7699
- formatBundleContent(context, options) {
7700
- const allTokenTypes = this.collectAllPermutationTypes(context);
7701
- return this.buildFile(allTokenTypes, options, (lines) => {
7702
- const i1 = indentStr(options.indent, 1);
7703
- lines.push(`@Suppress("unused")`);
7704
- lines.push(`${options.visPrefix}object ${options.objectName} {`);
7705
- for (let idx = 0; idx < context.permutations.length; idx++) {
7706
- const { tokens, modifierInputs } = context.permutations[idx];
7707
- const processedTokens = stripInternalMetadata(tokens);
7708
- const permName = this.buildPermutationName(modifierInputs);
7709
- lines.push(`${i1}${options.visPrefix}object ${permName} {`);
7710
- this.renderBundleTokens(lines, processedTokens, options, 2);
7711
- lines.push(`${i1}}`);
7712
- if (idx < context.permutations.length - 1) {
7713
- lines.push("");
7714
- }
7483
+ buildCssEntries(token, tokens, referenceTokens, preserveReferences) {
7484
+ if (preserveReferences) {
7485
+ const refName = getPureAliasReferenceName(token.originalValue);
7486
+ if (refName !== void 0) {
7487
+ return [
7488
+ {
7489
+ name: token.name,
7490
+ value: this.buildCssVarReference(refName, referenceTokens, tokens)
7491
+ }
7492
+ ];
7715
7493
  }
7716
- lines.push("}");
7717
- });
7494
+ }
7495
+ if (!this.isCompositeToken(token)) {
7496
+ return [{ name: token.name, value: this.formatValue(token) }];
7497
+ }
7498
+ const leaves = this.collectCompositeLeaves(token.$value);
7499
+ if (leaves.length === 0) {
7500
+ return [{ name: token.name, value: this.formatLeafValue(token.$value) }];
7501
+ }
7502
+ const leafEntries = leaves.map((leaf) => ({
7503
+ name: this.buildCompositeName(token.name, leaf.path),
7504
+ value: this.formatLeafValue(
7505
+ this.resolveCompositeLeafValue(
7506
+ leaf,
7507
+ token.originalValue,
7508
+ tokens,
7509
+ referenceTokens,
7510
+ preserveReferences
7511
+ )
7512
+ )
7513
+ }));
7514
+ const wholeValue = this.buildCompositeWholeValue(token, preserveReferences);
7515
+ if (!wholeValue) {
7516
+ return leafEntries;
7517
+ }
7518
+ return [{ name: token.name, value: wholeValue }, ...leafEntries];
7718
7519
  }
7719
- collectAllPermutationTypes(context) {
7720
- const types = /* @__PURE__ */ new Set();
7721
- for (const { tokens } of context.permutations) {
7722
- for (const t of this.collectTokenTypesFromEntries(stripInternalMetadata(tokens))) {
7723
- types.add(t);
7724
- }
7520
+ isCompositeToken(token) {
7521
+ const isCompositeType = [
7522
+ "shadow",
7523
+ "typography",
7524
+ "border",
7525
+ "strokeStyle",
7526
+ "transition",
7527
+ "gradient"
7528
+ ].includes(token.$type ?? "");
7529
+ if (!isCompositeType) {
7530
+ return false;
7725
7531
  }
7726
- return types;
7532
+ const value = token.$value;
7533
+ return typeof value === "object" && value !== null || Array.isArray(value);
7727
7534
  }
7728
- renderBundleTokens(lines, tokens, options, baseDepth) {
7729
- if (options.structure === "flat") {
7730
- const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
7731
- this.renderFlatGroups(lines, groups, baseDepth, options);
7732
- return;
7535
+ buildCompositeWholeValue(token, preserveReferences) {
7536
+ if (token.$type === "shadow") {
7537
+ return preserveReferences ? this.buildShadowWholeValue(token) : this.formatValue(token);
7733
7538
  }
7734
- const tree = this.buildTokenTree(tokens);
7735
- this.renderTreeChildren(lines, tree, baseDepth, options);
7736
- }
7737
- buildPermutationName(modifierInputs) {
7738
- const values = Object.values(modifierInputs);
7739
- if (values.length === 0) {
7740
- return "Default";
7539
+ if (token.$type === "border") {
7540
+ if (!this.hasBorderShorthandStyle(token)) {
7541
+ return void 0;
7542
+ }
7543
+ return preserveReferences ? this.buildBorderWholeValue(token) : this.formatValue(token);
7544
+ }
7545
+ if (token.$type === "transition") {
7546
+ return preserveReferences ? this.buildTransitionWholeValue(token) : this.formatValue(token);
7741
7547
  }
7742
- return values.map((v) => toSafeIdentifier(v, KOTLIN_KEYWORDS, true)).join("");
7743
- }
7744
- };
7745
- function androidRenderer() {
7746
- const rendererInstance = new AndroidRenderer();
7747
- return {
7748
- format: (context, options) => rendererInstance.format(
7749
- context,
7750
- options ?? context.output.options
7751
- )
7752
- };
7753
- }
7754
-
7755
- // src/renderers/css.ts
7756
- init_errors();
7757
- init_token_utils();
7758
-
7759
- // src/renderers/bundlers/css.ts
7760
- init_errors();
7761
- init_utils();
7762
- var REF_PREFIX_SETS = "#/sets/";
7763
- var REF_PREFIX_MODIFIERS = "#/modifiers/";
7764
- var getSourceSet = (token) => {
7765
- if (typeof token !== "object" || token === null) {
7766
- return void 0;
7767
- }
7768
- return "_sourceSet" in token && typeof token._sourceSet === "string" ? token._sourceSet : void 0;
7769
- };
7770
- var getSourceModifier = (token) => {
7771
- if (typeof token !== "object" || token === null) {
7772
7548
  return void 0;
7773
7549
  }
7774
- return "_sourceModifier" in token && typeof token._sourceModifier === "string" ? token._sourceModifier : void 0;
7775
- };
7776
- async function bundleAsCss(bundleData, resolver, options, formatTokens) {
7777
- const baseItem = bundleData.find((item) => item.isBase);
7778
- if (!baseItem) {
7779
- throw new exports.BasePermutationError("Base permutation not found in bundle data");
7780
- }
7781
- if (!formatTokens) {
7782
- throw new exports.ConfigurationError("CSS formatter was not provided");
7550
+ hasBorderShorthandStyle(token) {
7551
+ const value = token.$value;
7552
+ if (typeof value !== "object" || value === null) {
7553
+ return false;
7554
+ }
7555
+ return typeof value.style === "string";
7783
7556
  }
7784
- const orderedBundleData = orderBundleData(bundleData, resolver, baseItem);
7785
- const cssBlocks = [];
7786
- for (const item of orderedBundleData) {
7787
- if (item.isBase) {
7788
- const blocks = await formatBasePermutation(item, resolver, options, formatTokens);
7789
- cssBlocks.push(...blocks);
7790
- continue;
7557
+ buildShadowWholeValue(token) {
7558
+ const value = token.$value;
7559
+ if (Array.isArray(value)) {
7560
+ return value.map((shadow, index) => this.buildShadowLayerValue(token.name, shadow, [String(index)])).join(", ");
7791
7561
  }
7792
- const block = await formatModifierPermutation(item, baseItem, options, formatTokens);
7793
- if (block) {
7794
- cssBlocks.push(block);
7562
+ if (typeof value === "object" && value !== null) {
7563
+ return this.buildShadowLayerValue(token.name, value, []);
7795
7564
  }
7796
- }
7797
- return cssBlocks.join("\n\n");
7798
- }
7799
- async function formatBasePermutation({ tokens, modifierInputs }, resolver, options, formatTokens) {
7800
- const firstModifierName = resolver.modifiers ? Object.keys(resolver.modifiers)[0] : "";
7801
- const modifier = firstModifierName ?? "";
7802
- const context = modifierInputs[modifier] ?? "";
7803
- const selector = resolveSelector(options?.selector, modifier, context, true, modifierInputs);
7804
- const mediaQuery = resolveMediaQuery(options?.mediaQuery, modifier, context, true, modifierInputs);
7805
- const referenceTokens = stripInternalMetadata(tokens);
7806
- const defaultBlocks = buildDefaultLayerBlocks(tokens, modifierInputs, resolver);
7807
- const cssBlocks = [];
7808
- for (const block of defaultBlocks) {
7809
- const cleanTokens = stripInternalMetadata(block.tokens);
7810
- const css2 = await formatTokens(cleanTokens, {
7811
- selector,
7812
- mediaQuery,
7813
- minify: options?.minify,
7814
- referenceTokens
7815
- });
7816
- const header = block.description ? `/* ${block.key} */
7817
- /* ${block.description} */` : `/* ${block.key} */`;
7818
- cssBlocks.push(`${header}
7819
- ${css2}`);
7820
- }
7821
- return cssBlocks;
7822
- }
7823
- async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, options, formatTokens) {
7824
- const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
7825
- if (differenceCount > 1) {
7826
- return void 0;
7827
- }
7828
- const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
7829
- let tokensToInclude = filterTokensBySource(tokens, expectedSource);
7830
- const hasSourceMetadata = Object.values(tokens).some(
7831
- (token) => token != null && getSourceModifier(token) !== void 0
7832
- );
7833
- if (Object.keys(tokensToInclude).length === 0 && !hasSourceMetadata) {
7834
- tokensToInclude = tokens;
7835
- }
7836
- if (Object.keys(tokensToInclude).length === 0) {
7837
7565
  return void 0;
7838
7566
  }
7839
- const [modifier, context] = parseModifierSource(expectedSource);
7840
- const cleanTokens = stripInternalMetadata(tokensToInclude);
7841
- const referenceTokens = stripInternalMetadata(tokens);
7842
- const selector = resolveSelector(options?.selector, modifier, context, false, modifierInputs);
7843
- const mediaQuery = resolveMediaQuery(
7844
- options?.mediaQuery,
7845
- modifier,
7846
- context,
7847
- false,
7848
- modifierInputs
7849
- );
7850
- const css2 = await formatTokens(cleanTokens, {
7851
- selector,
7852
- mediaQuery,
7853
- minify: options?.minify,
7854
- referenceTokens
7855
- });
7856
- return `/* Modifier: ${modifier}=${context} */
7857
- ${css2}`;
7858
- }
7859
- function addLayerBlock(blocks, included, key, blockTokens, description) {
7860
- if (Object.keys(blockTokens).length === 0) {
7861
- return;
7862
- }
7863
- for (const k of Object.keys(blockTokens)) {
7864
- included.add(k);
7865
- }
7866
- blocks.push({ key, description, tokens: blockTokens });
7867
- }
7868
- function collectSetTokens(tokens, setName, included) {
7869
- const result = {};
7870
- for (const [name, token] of Object.entries(tokens)) {
7871
- if (!included.has(name) && getSourceSet(token) === setName) {
7872
- result[name] = token;
7567
+ buildShadowLayerValue(baseName, shadow, prefix) {
7568
+ if (typeof shadow !== "object" || shadow === null) {
7569
+ return String(shadow);
7873
7570
  }
7874
- }
7875
- return result;
7876
- }
7877
- function collectModifierTokens(tokens, expectedSource, included) {
7878
- const result = {};
7879
- for (const [name, token] of Object.entries(tokens)) {
7880
- if (!included.has(name) && (getSourceModifier(token) ?? "").toLowerCase() === expectedSource) {
7881
- result[name] = token;
7571
+ const shadowObj = shadow;
7572
+ const parts = [];
7573
+ if (shadowObj.inset === true) {
7574
+ parts.push("inset");
7882
7575
  }
7883
- }
7884
- return result;
7885
- }
7886
- function collectRemainder(tokens, included) {
7887
- const result = {};
7888
- for (const [name, token] of Object.entries(tokens)) {
7889
- if (!included.has(name)) {
7890
- result[name] = token;
7576
+ parts.push(this.buildCompositeVar(baseName, [...prefix, "offsetX"]));
7577
+ parts.push(this.buildCompositeVar(baseName, [...prefix, "offsetY"]));
7578
+ parts.push(this.buildCompositeVar(baseName, [...prefix, "blur"]));
7579
+ if (shadowObj.spread != null) {
7580
+ parts.push(this.buildCompositeVar(baseName, [...prefix, "spread"]));
7891
7581
  }
7582
+ parts.push(this.buildCompositeVar(baseName, [...prefix, "color"]));
7583
+ return parts.join(" ");
7892
7584
  }
7893
- return result;
7894
- }
7895
- function buildSetLayerBlocks(tokens, resolver) {
7896
- const blocks = [];
7897
- const included = /* @__PURE__ */ new Set();
7898
- for (const item of resolver.resolutionOrder) {
7899
- const ref = item.$ref;
7900
- if (typeof ref !== "string" || !ref.startsWith(REF_PREFIX_SETS)) {
7901
- continue;
7585
+ buildBorderWholeValue(token) {
7586
+ const value = token.$value;
7587
+ if (typeof value !== "object" || value === null) {
7588
+ return void 0;
7902
7589
  }
7903
- const setName = ref.slice(REF_PREFIX_SETS.length);
7904
- addLayerBlock(
7905
- blocks,
7906
- included,
7907
- `Set: ${setName}`,
7908
- collectSetTokens(tokens, setName, included),
7909
- resolver.sets?.[setName]?.description
7910
- );
7911
- }
7912
- addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7913
- return blocks;
7914
- }
7915
- function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
7916
- const blocks = [];
7917
- const included = /* @__PURE__ */ new Set();
7918
- const baseInputs = normalizeModifierInputs(baseModifierInputs);
7919
- for (const item of resolver.resolutionOrder) {
7920
- const ref = item.$ref;
7921
- if (typeof ref !== "string") {
7922
- continue;
7590
+ const border = value;
7591
+ if (typeof border.style !== "string") {
7592
+ return void 0;
7923
7593
  }
7924
- processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver);
7594
+ return [
7595
+ this.buildCompositeVar(token.name, ["width"]),
7596
+ this.buildCompositeVar(token.name, ["style"]),
7597
+ this.buildCompositeVar(token.name, ["color"])
7598
+ ].join(" ");
7925
7599
  }
7926
- addLayerBlock(blocks, included, "Unattributed", collectRemainder(tokens, included));
7927
- return blocks;
7928
- }
7929
- function processResolutionOrderRef(ref, tokens, blocks, included, baseInputs, resolver) {
7930
- if (ref.startsWith(REF_PREFIX_SETS)) {
7931
- const setName = ref.slice(REF_PREFIX_SETS.length);
7932
- addLayerBlock(
7933
- blocks,
7934
- included,
7935
- `Set: ${setName}`,
7936
- collectSetTokens(tokens, setName, included),
7937
- resolver.sets?.[setName]?.description
7938
- );
7939
- return;
7600
+ buildTransitionWholeValue(token) {
7601
+ const value = token.$value;
7602
+ if (typeof value !== "object" || value === null) {
7603
+ return void 0;
7604
+ }
7605
+ return [
7606
+ this.buildCompositeVar(token.name, ["duration"]),
7607
+ `cubic-bezier(${this.buildCompositeVar(token.name, ["timingFunction", "0"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "1"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "2"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "3"])})`,
7608
+ this.buildCompositeVar(token.name, ["delay"])
7609
+ ].join(" ");
7940
7610
  }
7941
- if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
7942
- return;
7611
+ buildCompositeVar(baseName, path7) {
7612
+ return `var(--${this.buildCompositeName(baseName, path7)})`;
7943
7613
  }
7944
- const modifierName = ref.slice(REF_PREFIX_MODIFIERS.length);
7945
- const modifier = resolver.modifiers?.[modifierName];
7946
- const selectedContext = baseInputs[modifierName.toLowerCase()];
7947
- if (!modifier || !selectedContext) {
7948
- return;
7614
+ collectCompositeLeaves(value) {
7615
+ const leaves = [];
7616
+ this.collectLeafEntries(value, [], leaves);
7617
+ return leaves;
7949
7618
  }
7950
- const expectedSource = `${modifierName}-${selectedContext}`.toLowerCase();
7951
- addLayerBlock(
7952
- blocks,
7953
- included,
7954
- `Modifier: ${modifierName}=${selectedContext} (default)`,
7955
- collectModifierTokens(tokens, expectedSource, included),
7956
- modifier.description
7957
- );
7958
- }
7959
- function findSingleDiffPermutation(bundleData, modifierName, context, baseInputs) {
7960
- const normalizedModifier = modifierName.toLowerCase();
7961
- const normalizedContext = context.toLowerCase();
7962
- return bundleData.find((item) => {
7963
- if (item.isBase) {
7964
- return false;
7619
+ collectLeafEntries(value, path7, leaves) {
7620
+ if (this.isPrimitiveValue(value)) {
7621
+ leaves.push({ path: path7, value });
7622
+ return;
7965
7623
  }
7966
- const inputs = normalizeModifierInputs(item.modifierInputs);
7967
- if (inputs[normalizedModifier] !== normalizedContext) {
7968
- return false;
7624
+ if (isColorObject(value) || isDimensionObject(value) || isDurationObject(value)) {
7625
+ leaves.push({ path: path7, value });
7626
+ return;
7969
7627
  }
7970
- return Object.entries(baseInputs).every(([k, v]) => k === normalizedModifier || inputs[k] === v);
7971
- });
7972
- }
7973
- function pushUniqueBundleItem(ordered, includedKeys, item) {
7974
- if (!item) {
7975
- return;
7976
- }
7977
- const key = stableInputsKey(item.modifierInputs);
7978
- if (includedKeys.has(key)) {
7979
- return;
7980
- }
7981
- includedKeys.add(key);
7982
- ordered.push(item);
7983
- }
7984
- function appendModifierPermutations(bundleData, modifiers, orderedNames, baseInputs, ordered, includedKeys) {
7985
- for (const modifierName of orderedNames) {
7986
- const modifierDef = modifiers[modifierName];
7987
- if (!modifierDef) {
7988
- continue;
7628
+ if (Array.isArray(value)) {
7629
+ if (value.length === 0) {
7630
+ leaves.push({ path: path7, value });
7631
+ return;
7632
+ }
7633
+ value.forEach((item, index) => {
7634
+ this.collectLeafEntries(item, [...path7, String(index)], leaves);
7635
+ });
7636
+ return;
7989
7637
  }
7990
- const defaultValue = baseInputs[modifierName.toLowerCase()] ?? "";
7991
- for (const ctx of Object.keys(modifierDef.contexts)) {
7992
- if (defaultValue === ctx.toLowerCase()) {
7993
- continue;
7638
+ if (typeof value === "object" && value !== null) {
7639
+ const entries = Object.entries(value);
7640
+ if (entries.length === 0) {
7641
+ leaves.push({ path: path7, value });
7642
+ return;
7994
7643
  }
7995
- pushUniqueBundleItem(
7996
- ordered,
7997
- includedKeys,
7998
- findSingleDiffPermutation(bundleData, modifierName, ctx, baseInputs)
7999
- );
7644
+ for (const [key, child] of entries) {
7645
+ this.collectLeafEntries(child, [...path7, this.normalizePathSegment(key)], leaves);
7646
+ }
7647
+ return;
8000
7648
  }
7649
+ leaves.push({ path: path7, value });
8001
7650
  }
8002
- }
8003
- function orderBundleData(bundleData, resolver, baseItem) {
8004
- const modifiers = resolver.modifiers;
8005
- if (!modifiers) {
8006
- return bundleData;
7651
+ normalizePathSegment(segment) {
7652
+ return segment.trim().replace(/\s+/g, "-");
8007
7653
  }
8008
- const baseInputs = normalizeModifierInputs(baseItem.modifierInputs);
8009
- const orderedModifierNames = getOrderedModifierNames(resolver);
8010
- if (orderedModifierNames.length === 0) {
8011
- return bundleData;
7654
+ buildCompositeName(base, path7) {
7655
+ if (path7.length === 0) {
7656
+ return base;
7657
+ }
7658
+ return `${base}-${path7.join("-")}`;
8012
7659
  }
8013
- const firstModifierDef = modifiers[orderedModifierNames[0] ?? ""];
8014
- if (!firstModifierDef) {
8015
- return bundleData;
7660
+ formatLeafValue(value) {
7661
+ if (isColorObject(value)) {
7662
+ return colorObjectToHex(value);
7663
+ }
7664
+ if (isDimensionObject(value)) {
7665
+ return dimensionObjectToString(value);
7666
+ }
7667
+ if (isDurationObject(value)) {
7668
+ return durationObjectToString(value);
7669
+ }
7670
+ if (typeof value === "string") {
7671
+ return value;
7672
+ }
7673
+ if (typeof value === "number" || typeof value === "boolean") {
7674
+ return String(value);
7675
+ }
7676
+ if (Array.isArray(value)) {
7677
+ return JSON.stringify(value);
7678
+ }
7679
+ if (typeof value === "object" && value != null) {
7680
+ return JSON.stringify(value);
7681
+ }
7682
+ return String(value);
8016
7683
  }
8017
- const includedKeys = /* @__PURE__ */ new Set();
8018
- const ordered = [];
8019
- pushUniqueBundleItem(ordered, includedKeys, baseItem);
8020
- appendModifierPermutations(
8021
- bundleData,
8022
- modifiers,
8023
- orderedModifierNames,
8024
- baseInputs,
8025
- ordered,
8026
- includedKeys
8027
- );
8028
- return ordered.length > 0 ? ordered : bundleData;
8029
- }
8030
- function getOrderedModifierNames(resolver) {
8031
- const modifiers = resolver.modifiers ?? {};
8032
- const seen = /* @__PURE__ */ new Set();
8033
- const ordered = [];
8034
- for (const item of resolver.resolutionOrder) {
8035
- const ref = item.$ref;
8036
- if (typeof ref !== "string") {
8037
- continue;
7684
+ resolveCompositeLeafValue(leaf, originalValue, tokens, referenceTokens, preserveReferences) {
7685
+ if (!preserveReferences) {
7686
+ return leaf.value;
8038
7687
  }
8039
- if (!ref.startsWith(REF_PREFIX_MODIFIERS)) {
8040
- continue;
7688
+ const originalLeafValue = this.getOriginalLeafValue(originalValue, leaf.path);
7689
+ const refName = getPureAliasReferenceName(originalLeafValue);
7690
+ if (refName === void 0) {
7691
+ return leaf.value;
8041
7692
  }
8042
- const name = ref.slice(REF_PREFIX_MODIFIERS.length);
8043
- if (seen.has(name)) {
8044
- continue;
7693
+ return this.buildCssVarReference(refName, referenceTokens, tokens);
7694
+ }
7695
+ buildCssVarReference(refName, referenceTokens, tokens) {
7696
+ const referencedToken = referenceTokens[refName] ?? tokens[refName];
7697
+ if (!referencedToken) {
7698
+ throw new exports.ConfigurationError(
7699
+ `CSS reference "{${refName}}" could not be resolved. The referenced token is not present in the current output's token set. This usually means a filter (e.g. isAlias()) excluded the referenced token while preserveReferences is true. Either remove the filter, include the referenced token, or set preserveReferences to false.`
7700
+ );
8045
7701
  }
8046
- if (!(name in modifiers)) {
8047
- continue;
7702
+ return `var(--${referencedToken.name})`;
7703
+ }
7704
+ getOriginalLeafValue(value, path7) {
7705
+ let current = value;
7706
+ for (const segment of path7) {
7707
+ if (Array.isArray(current)) {
7708
+ const index = Number(segment);
7709
+ if (!Number.isInteger(index)) {
7710
+ return void 0;
7711
+ }
7712
+ current = current[index];
7713
+ continue;
7714
+ }
7715
+ if (typeof current === "object" && current !== null) {
7716
+ const entries = Object.entries(current);
7717
+ const matched = entries.find(([key]) => this.normalizePathSegment(key) === segment);
7718
+ if (!matched) {
7719
+ return void 0;
7720
+ }
7721
+ current = matched[1];
7722
+ continue;
7723
+ }
7724
+ return void 0;
8048
7725
  }
8049
- ordered.push(name);
8050
- seen.add(name);
7726
+ return current;
8051
7727
  }
8052
- for (const name of Object.keys(modifiers)) {
8053
- if (seen.has(name)) {
8054
- continue;
7728
+ isPrimitiveValue(value) {
7729
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
7730
+ }
7731
+ /**
7732
+ * Format token value for CSS
7733
+ * Handles DTCG 2025.10 object formats for colors and dimensions
7734
+ */
7735
+ formatValue(token) {
7736
+ const value = token.$value;
7737
+ const typed = this.formatTypedValue(token.$type, value);
7738
+ if (typed !== void 0) {
7739
+ return typed;
8055
7740
  }
8056
- ordered.push(name);
8057
- seen.add(name);
7741
+ return this.formatPrimitiveOrStructured(value, token.$type);
8058
7742
  }
8059
- return ordered;
8060
- }
8061
- function stableInputsKey(modifierInputs) {
8062
- const normalized = normalizeModifierInputs(modifierInputs);
8063
- return Object.keys(normalized).sort().map((k) => `${k}=${normalized[k]}`).join("|");
8064
- }
8065
-
8066
- // src/renderers/css.ts
8067
- init_utils();
8068
- init_metadata();
8069
- var CssRenderer = class _CssRenderer {
8070
- async format(context, options) {
8071
- const opts = {
8072
- preset: options?.preset ?? "bundle",
8073
- selector: options?.selector,
8074
- mediaQuery: options?.mediaQuery,
8075
- minify: options?.minify ?? false,
8076
- preserveReferences: options?.preserveReferences ?? false
8077
- };
8078
- if (opts.preset === "bundle") {
8079
- return await this.formatBundle(context, opts);
7743
+ formatTypedValue(type, value) {
7744
+ if (type === "color" && isColorObject(value)) {
7745
+ return colorObjectToHex(value);
7746
+ }
7747
+ if (type === "dimension") {
7748
+ return typeof value === "string" ? value : dimensionObjectToString(value);
7749
+ }
7750
+ if (type === "duration") {
7751
+ if (isDurationObject(value)) {
7752
+ return durationObjectToString(value);
7753
+ }
7754
+ if (typeof value === "string") {
7755
+ return value;
7756
+ }
7757
+ }
7758
+ return void 0;
7759
+ }
7760
+ formatPrimitiveOrStructured(value, tokenType) {
7761
+ if (typeof value === "string") {
7762
+ return value;
8080
7763
  }
8081
- if (opts.preset === "modifier") {
8082
- return await this.formatModifier(context, opts);
7764
+ if (typeof value === "number") {
7765
+ return String(value);
8083
7766
  }
8084
- return await this.formatStandalone(context, opts);
7767
+ if (Array.isArray(value)) {
7768
+ return this.formatArrayValue(value, tokenType);
7769
+ }
7770
+ if (typeof value === "object" && value != null) {
7771
+ return this.formatCompositeValue(value, tokenType);
7772
+ }
7773
+ return String(value);
7774
+ }
7775
+ formatArrayValue(value, tokenType) {
7776
+ if (tokenType === "shadow" && value.length > 0 && typeof value[0] === "object") {
7777
+ return value.map((shadowObj) => this.formatShadow(shadowObj)).join(", ");
7778
+ }
7779
+ return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
8085
7780
  }
8086
- static PRETTIER_PRINT_WIDTH = 80;
8087
- static PRETTIER_TAB_WIDTH = 2;
8088
7781
  /**
8089
- * Format tokens as CSS custom properties
8090
- *
8091
- * Converts resolved design tokens into CSS format with configurable selector,
8092
- * media queries, and formatting options. Supports DTCG color and dimension objects.
8093
- *
8094
- * Note: This method expects selector and mediaQuery to be resolved strings.
8095
- * Function-based selectors should be resolved before calling format().
8096
- *
8097
- * @param tokens - Resolved tokens to format
8098
- * @param options - CSS formatting options (selector, media query, minify, etc.)
8099
- * @returns Formatted CSS string with custom properties
7782
+ * Format composite token values
8100
7783
  */
8101
- async formatTokens(tokens, options) {
8102
- const opts = {
8103
- preset: "bundle",
8104
- selector: ":root",
8105
- mediaQuery: "",
8106
- minify: false,
8107
- preserveReferences: false,
8108
- ...options,
8109
- referenceTokens: options?.referenceTokens ?? tokens
8110
- };
8111
- const sortedTokens = getSortedTokenEntries(tokens).map(([, token]) => token);
8112
- const referenceTokens = opts.referenceTokens;
8113
- const lines = [];
8114
- this.buildCssBlock(lines, sortedTokens, opts.selector, tokens, referenceTokens, opts);
8115
- const cssString = lines.join("");
8116
- return opts.minify ? cssString : await this.formatWithPrettier(cssString);
8117
- }
8118
- buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
8119
- const indent = opts.minify ? "" : " ";
8120
- const newline = opts.minify ? "" : "\n";
8121
- const space = opts.minify ? "" : " ";
8122
- const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
8123
- const tokenIndent = hasMediaQuery ? indent + indent : indent;
8124
- if (hasMediaQuery) {
8125
- lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
8126
- lines.push(`${indent}${selector}${space}{${newline}`);
8127
- } else {
8128
- lines.push(`${selector}${space}{${newline}`);
7784
+ formatCompositeValue(value, tokenType) {
7785
+ if (tokenType === "shadow") {
7786
+ return this.formatShadow(value);
8129
7787
  }
8130
- for (const token of groupTokens) {
8131
- this.pushTokenLines(
8132
- lines,
8133
- token,
8134
- tokens,
8135
- referenceTokens,
8136
- opts.preserveReferences ?? false,
8137
- tokenIndent,
8138
- newline,
8139
- space
8140
- );
7788
+ if (tokenType === "border") {
7789
+ return this.formatBorder(value);
8141
7790
  }
8142
- if (hasMediaQuery) {
8143
- lines.push(`${indent}}${newline}`);
7791
+ if (tokenType === "transition") {
7792
+ return this.formatTransition(value);
8144
7793
  }
8145
- lines.push(`}${newline}${newline}`);
7794
+ return JSON.stringify(value);
8146
7795
  }
8147
- pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
8148
- const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
8149
- const deprecationComment = buildTokenDeprecationComment(token, "css");
8150
- if (deprecationComment) {
8151
- lines.push(`${indent}${deprecationComment}${newline}`);
7796
+ /**
7797
+ * Format a single shadow object to CSS box-shadow syntax
7798
+ */
7799
+ formatShadow(shadow) {
7800
+ const parts = [];
7801
+ if (shadow.inset === true) {
7802
+ parts.push("inset");
8152
7803
  }
8153
- const descriptionComment = buildTokenDescriptionComment(token, "css");
8154
- if (descriptionComment) {
8155
- lines.push(`${indent}${descriptionComment}${newline}`);
7804
+ parts.push(dimensionObjectToString(shadow.offsetX));
7805
+ parts.push(dimensionObjectToString(shadow.offsetY));
7806
+ parts.push(dimensionObjectToString(shadow.blur));
7807
+ if (shadow.spread != null) {
7808
+ parts.push(dimensionObjectToString(shadow.spread));
8156
7809
  }
8157
- for (const entry of entries) {
8158
- lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
7810
+ if (isColorObject(shadow.color)) {
7811
+ parts.push(colorObjectToHex(shadow.color));
7812
+ } else {
7813
+ parts.push(String(shadow.color));
8159
7814
  }
7815
+ return parts.join(" ");
8160
7816
  }
8161
- async formatWithPrettier(css2) {
8162
- try {
8163
- return await prettier__default.default.format(css2, {
8164
- parser: "css",
8165
- printWidth: _CssRenderer.PRETTIER_PRINT_WIDTH,
8166
- tabWidth: _CssRenderer.PRETTIER_TAB_WIDTH,
8167
- useTabs: false
8168
- });
8169
- } catch {
8170
- return css2;
7817
+ /**
7818
+ * Format a border object to CSS border shorthand syntax
7819
+ */
7820
+ formatBorder(value) {
7821
+ const parts = [];
7822
+ if (isDimensionObject(value.width)) {
7823
+ parts.push(dimensionObjectToString(value.width));
7824
+ } else if (value.width != null) {
7825
+ parts.push(String(value.width));
7826
+ }
7827
+ if (typeof value.style === "string") {
7828
+ parts.push(value.style);
7829
+ }
7830
+ if (isColorObject(value.color)) {
7831
+ parts.push(colorObjectToHex(value.color));
7832
+ } else if (value.color != null) {
7833
+ parts.push(String(value.color));
8171
7834
  }
7835
+ return parts.join(" ");
8172
7836
  }
8173
- buildCssEntries(token, tokens, referenceTokens, preserveReferences) {
8174
- if (preserveReferences) {
8175
- const refName = getPureAliasReferenceName(token.originalValue);
8176
- if (refName !== void 0) {
8177
- return [
8178
- {
8179
- name: token.name,
8180
- value: this.buildCssVarReference(refName, referenceTokens, tokens)
8181
- }
8182
- ];
8183
- }
7837
+ /**
7838
+ * Format a transition object to CSS transition shorthand syntax
7839
+ */
7840
+ formatTransition(value) {
7841
+ const parts = [];
7842
+ if (isDurationObject(value.duration)) {
7843
+ parts.push(durationObjectToString(value.duration));
7844
+ } else if (value.duration != null) {
7845
+ parts.push(String(value.duration));
8184
7846
  }
8185
- if (!this.isCompositeToken(token)) {
8186
- return [{ name: token.name, value: this.formatValue(token) }];
7847
+ if (Array.isArray(value.timingFunction) && value.timingFunction.length === 4) {
7848
+ parts.push(`cubic-bezier(${value.timingFunction.join(", ")})`);
8187
7849
  }
8188
- const leaves = this.collectCompositeLeaves(token.$value);
8189
- if (leaves.length === 0) {
8190
- return [{ name: token.name, value: this.formatLeafValue(token.$value) }];
7850
+ if (isDurationObject(value.delay)) {
7851
+ parts.push(durationObjectToString(value.delay));
7852
+ } else if (value.delay != null) {
7853
+ parts.push(String(value.delay));
8191
7854
  }
8192
- const leafEntries = leaves.map((leaf) => ({
8193
- name: this.buildCompositeName(token.name, leaf.path),
8194
- value: this.formatLeafValue(
8195
- this.resolveCompositeLeafValue(
8196
- leaf,
8197
- token.originalValue,
8198
- tokens,
8199
- referenceTokens,
8200
- preserveReferences
8201
- )
8202
- )
7855
+ return parts.join(" ");
7856
+ }
7857
+ async formatBundle(context, options) {
7858
+ const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
7859
+ tokens,
7860
+ modifierInputs,
7861
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8203
7862
  }));
8204
- const wholeValue = this.buildCompositeWholeValue(token, preserveReferences);
8205
- if (!wholeValue) {
8206
- return leafEntries;
8207
- }
8208
- return [{ name: token.name, value: wholeValue }, ...leafEntries];
7863
+ return await bundleAsCss(bundleData, context.resolver, options, async (tokens, resolved) => {
7864
+ return await this.formatTokens(tokens, {
7865
+ ...resolved,
7866
+ preserveReferences: options.preserveReferences ?? false
7867
+ });
7868
+ });
8209
7869
  }
8210
- isCompositeToken(token) {
8211
- const isCompositeType = [
8212
- "shadow",
8213
- "typography",
8214
- "border",
8215
- "strokeStyle",
8216
- "transition",
8217
- "gradient"
8218
- ].includes(token.$type ?? "");
8219
- if (!isCompositeType) {
8220
- return false;
7870
+ async formatStandalone(context, options) {
7871
+ assertFileRequired(
7872
+ context.buildPath,
7873
+ context.output.file,
7874
+ context.output.name,
7875
+ "standalone CSS"
7876
+ );
7877
+ const files = {};
7878
+ for (const { tokens, modifierInputs } of context.permutations) {
7879
+ const { fileName, content } = await this.buildStandaloneFile(
7880
+ tokens,
7881
+ modifierInputs,
7882
+ context,
7883
+ options
7884
+ );
7885
+ files[fileName] = content;
8221
7886
  }
8222
- const value = token.$value;
8223
- return typeof value === "object" && value !== null || Array.isArray(value);
7887
+ return { kind: "outputTree", files };
7888
+ }
7889
+ async buildStandaloneFile(tokens, modifierInputs, context, options) {
7890
+ const isBase = isBasePermutation(modifierInputs, context.meta.defaults);
7891
+ const { modifierName, modifierContext } = this.resolveModifierContext(
7892
+ modifierInputs,
7893
+ context,
7894
+ isBase
7895
+ );
7896
+ const selector = resolveSelector(
7897
+ options.selector,
7898
+ modifierName,
7899
+ modifierContext,
7900
+ isBase,
7901
+ modifierInputs
7902
+ );
7903
+ const mediaQuery = resolveMediaQuery(
7904
+ options.mediaQuery,
7905
+ modifierName,
7906
+ modifierContext,
7907
+ isBase,
7908
+ modifierInputs
7909
+ );
7910
+ const content = await this.formatTokens(tokens, {
7911
+ selector,
7912
+ mediaQuery,
7913
+ minify: options.minify ?? false,
7914
+ preserveReferences: options.preserveReferences ?? false,
7915
+ referenceTokens: tokens
7916
+ });
7917
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
7918
+ outputName: context.output.name,
7919
+ extension: "css",
7920
+ modifierInputs,
7921
+ resolver: context.resolver,
7922
+ defaults: context.meta.defaults
7923
+ });
7924
+ return { fileName, content };
8224
7925
  }
8225
- buildCompositeWholeValue(token, preserveReferences) {
8226
- if (token.$type === "shadow") {
8227
- return preserveReferences ? this.buildShadowWholeValue(token) : this.formatValue(token);
7926
+ async formatModifier(context, options) {
7927
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "modifier CSS");
7928
+ if (!context.resolver.modifiers) {
7929
+ throw new exports.ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
8228
7930
  }
8229
- if (token.$type === "border") {
8230
- if (!this.hasBorderShorthandStyle(token)) {
8231
- return void 0;
8232
- }
8233
- return preserveReferences ? this.buildBorderWholeValue(token) : this.formatValue(token);
7931
+ const files = {};
7932
+ const baseResult = await this.buildModifierBaseFile(context, options);
7933
+ if (baseResult) {
7934
+ files[baseResult.fileName] = baseResult.content;
8234
7935
  }
8235
- if (token.$type === "transition") {
8236
- return preserveReferences ? this.buildTransitionWholeValue(token) : this.formatValue(token);
7936
+ for (const [modifierName, modifierDef] of Object.entries(context.resolver.modifiers)) {
7937
+ for (const contextValue of Object.keys(modifierDef.contexts)) {
7938
+ const result = await this.buildModifierContextFile(
7939
+ modifierName,
7940
+ contextValue,
7941
+ context,
7942
+ options
7943
+ );
7944
+ if (result) {
7945
+ files[result.fileName] = result.content;
7946
+ }
7947
+ }
8237
7948
  }
8238
- return void 0;
7949
+ return { kind: "outputTree", files };
8239
7950
  }
8240
- hasBorderShorthandStyle(token) {
8241
- const value = token.$value;
8242
- if (typeof value !== "object" || value === null) {
8243
- return false;
7951
+ async buildModifierBaseFile(context, options) {
7952
+ const basePermutation = context.permutations.find(
7953
+ ({ modifierInputs }) => isBasePermutation(modifierInputs, context.meta.defaults)
7954
+ );
7955
+ if (!basePermutation) {
7956
+ return void 0;
8244
7957
  }
8245
- return typeof value.style === "string";
8246
- }
8247
- buildShadowWholeValue(token) {
8248
- const value = token.$value;
8249
- if (Array.isArray(value)) {
8250
- return value.map((shadow, index) => this.buildShadowLayerValue(token.name, shadow, [String(index)])).join(", ");
7958
+ const setTokens = filterTokensFromSets(basePermutation.tokens);
7959
+ if (Object.keys(setTokens).length === 0) {
7960
+ return void 0;
8251
7961
  }
8252
- if (typeof value === "object" && value !== null) {
8253
- return this.buildShadowLayerValue(token.name, value, []);
7962
+ const setBlocks = buildSetLayerBlocks(setTokens, context.resolver);
7963
+ if (setBlocks.length === 0) {
7964
+ return void 0;
8254
7965
  }
8255
- return void 0;
7966
+ const { selector, mediaQuery } = this.resolveBaseModifierContext(context, options);
7967
+ const content = await this.formatSetBlocksCss(
7968
+ setBlocks,
7969
+ basePermutation.tokens,
7970
+ selector,
7971
+ mediaQuery,
7972
+ options
7973
+ );
7974
+ const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
7975
+ return { fileName, content };
8256
7976
  }
8257
- buildShadowLayerValue(baseName, shadow, prefix) {
8258
- if (typeof shadow !== "object" || shadow === null) {
8259
- return String(shadow);
8260
- }
8261
- const shadowObj = shadow;
8262
- const parts = [];
8263
- if (shadowObj.inset === true) {
8264
- parts.push("inset");
8265
- }
8266
- parts.push(this.buildCompositeVar(baseName, [...prefix, "offsetX"]));
8267
- parts.push(this.buildCompositeVar(baseName, [...prefix, "offsetY"]));
8268
- parts.push(this.buildCompositeVar(baseName, [...prefix, "blur"]));
8269
- if (shadowObj.spread != null) {
8270
- parts.push(this.buildCompositeVar(baseName, [...prefix, "spread"]));
8271
- }
8272
- parts.push(this.buildCompositeVar(baseName, [...prefix, "color"]));
8273
- return parts.join(" ");
7977
+ resolveBaseModifierContext(context, options) {
7978
+ const modifiers = context.resolver.modifiers;
7979
+ const firstModifierName = Object.keys(modifiers)[0] ?? "";
7980
+ const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
7981
+ const baseModifierInputs = { ...context.meta.defaults };
7982
+ return {
7983
+ selector: resolveSelector(
7984
+ options.selector,
7985
+ firstModifierName,
7986
+ firstModifierContext,
7987
+ true,
7988
+ baseModifierInputs
7989
+ ),
7990
+ mediaQuery: resolveMediaQuery(
7991
+ options.mediaQuery,
7992
+ firstModifierName,
7993
+ firstModifierContext,
7994
+ true,
7995
+ baseModifierInputs
7996
+ )
7997
+ };
8274
7998
  }
8275
- buildBorderWholeValue(token) {
8276
- const value = token.$value;
8277
- if (typeof value !== "object" || value === null) {
8278
- return void 0;
7999
+ async formatSetBlocksCss(setBlocks, referenceTokens, selector, mediaQuery, options) {
8000
+ const cssBlocks = [];
8001
+ for (const block of setBlocks) {
8002
+ const cleanTokens = stripInternalMetadata(block.tokens);
8003
+ const css2 = await this.formatTokens(cleanTokens, {
8004
+ selector,
8005
+ mediaQuery,
8006
+ minify: options.minify ?? false,
8007
+ preserveReferences: options.preserveReferences ?? false,
8008
+ referenceTokens
8009
+ });
8010
+ const header = block.description ? `/* ${block.key} */
8011
+ /* ${block.description} */` : `/* ${block.key} */`;
8012
+ cssBlocks.push(`${header}
8013
+ ${css2}`);
8279
8014
  }
8280
- const border = value;
8281
- if (typeof border.style !== "string") {
8282
- return void 0;
8015
+ return cssBlocks.join("\n");
8016
+ }
8017
+ collectTokensForModifierContext(modifierName, contextValue, permutations) {
8018
+ const expectedSource = `${modifierName}-${contextValue}`;
8019
+ let tokensFromSource = {};
8020
+ let referenceTokens = {};
8021
+ for (const { tokens, modifierInputs } of permutations) {
8022
+ if (modifierInputs[modifierName] !== contextValue) {
8023
+ continue;
8024
+ }
8025
+ tokensFromSource = { ...tokensFromSource, ...filterTokensBySource(tokens, expectedSource) };
8026
+ referenceTokens = { ...referenceTokens, ...tokens };
8283
8027
  }
8284
- return [
8285
- this.buildCompositeVar(token.name, ["width"]),
8286
- this.buildCompositeVar(token.name, ["style"]),
8287
- this.buildCompositeVar(token.name, ["color"])
8288
- ].join(" ");
8028
+ return { tokensFromSource, referenceTokens };
8289
8029
  }
8290
- buildTransitionWholeValue(token) {
8291
- const value = token.$value;
8292
- if (typeof value !== "object" || value === null) {
8030
+ async buildModifierContextFile(modifierName, contextValue, context, options) {
8031
+ const { tokensFromSource, referenceTokens } = this.collectTokensForModifierContext(
8032
+ modifierName,
8033
+ contextValue,
8034
+ context.permutations
8035
+ );
8036
+ if (Object.keys(tokensFromSource).length === 0) {
8293
8037
  return void 0;
8294
8038
  }
8295
- return [
8296
- this.buildCompositeVar(token.name, ["duration"]),
8297
- `cubic-bezier(${this.buildCompositeVar(token.name, ["timingFunction", "0"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "1"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "2"])}, ${this.buildCompositeVar(token.name, ["timingFunction", "3"])})`,
8298
- this.buildCompositeVar(token.name, ["delay"])
8299
- ].join(" ");
8300
- }
8301
- buildCompositeVar(baseName, path7) {
8302
- return `var(--${this.buildCompositeName(baseName, path7)})`;
8303
- }
8304
- collectCompositeLeaves(value) {
8305
- const leaves = [];
8306
- this.collectLeafEntries(value, [], leaves);
8307
- return leaves;
8039
+ const defaults = context.meta.defaults;
8040
+ const isBase = contextValue === defaults[modifierName];
8041
+ const modifierInputs = { ...defaults, [modifierName]: contextValue };
8042
+ const selector = resolveSelector(
8043
+ options.selector,
8044
+ modifierName,
8045
+ contextValue,
8046
+ isBase,
8047
+ modifierInputs
8048
+ );
8049
+ const mediaQuery = resolveMediaQuery(
8050
+ options.mediaQuery,
8051
+ modifierName,
8052
+ contextValue,
8053
+ isBase,
8054
+ modifierInputs
8055
+ );
8056
+ const content = await this.formatTokens(tokensFromSource, {
8057
+ selector,
8058
+ mediaQuery,
8059
+ minify: options.minify ?? false,
8060
+ preserveReferences: options.preserveReferences ?? false,
8061
+ referenceTokens
8062
+ });
8063
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8064
+ outputName: context.output.name,
8065
+ extension: "css",
8066
+ modifierInputs,
8067
+ resolver: context.resolver,
8068
+ defaults: context.meta.defaults
8069
+ });
8070
+ return { fileName, content };
8308
8071
  }
8309
- collectLeafEntries(value, path7, leaves) {
8310
- if (this.isPrimitiveValue(value)) {
8311
- leaves.push({ path: path7, value });
8312
- return;
8072
+ resolveModifierContext(modifierInputs, context, isBase) {
8073
+ if (!context.resolver.modifiers) {
8074
+ return { modifierName: "", modifierContext: "" };
8313
8075
  }
8314
- if (isColorObject(value) || isDimensionObject(value) || isDurationObject(value)) {
8315
- leaves.push({ path: path7, value });
8316
- return;
8076
+ const normalizedInputs = normalizeModifierInputs(modifierInputs);
8077
+ const normalizedDefaults = normalizeModifierInputs(context.meta.defaults);
8078
+ if (isBase) {
8079
+ const firstModifier = Object.keys(context.resolver.modifiers)[0] ?? "";
8080
+ return {
8081
+ modifierName: firstModifier,
8082
+ modifierContext: normalizedInputs[firstModifier] ?? ""
8083
+ };
8317
8084
  }
8318
- if (Array.isArray(value)) {
8319
- if (value.length === 0) {
8320
- leaves.push({ path: path7, value });
8321
- return;
8085
+ for (const [name, value] of Object.entries(normalizedInputs)) {
8086
+ if (value !== normalizedDefaults[name]) {
8087
+ return { modifierName: name, modifierContext: value };
8322
8088
  }
8323
- value.forEach((item, index) => {
8324
- this.collectLeafEntries(item, [...path7, String(index)], leaves);
8089
+ }
8090
+ return { modifierName: "", modifierContext: "" };
8091
+ }
8092
+ };
8093
+ function cssRenderer() {
8094
+ const rendererInstance = new CssRenderer();
8095
+ return {
8096
+ format: (context, options) => rendererInstance.format(
8097
+ context,
8098
+ options ?? context.output.options
8099
+ )
8100
+ };
8101
+ }
8102
+
8103
+ // src/outputs/js/renderer.ts
8104
+ init_utils();
8105
+ init_token_utils();
8106
+ init_metadata();
8107
+ var JsModuleRenderer = class {
8108
+ async format(context, options) {
8109
+ const opts = {
8110
+ preset: options?.preset ?? "standalone",
8111
+ structure: options?.structure ?? "nested",
8112
+ minify: options?.minify ?? false,
8113
+ moduleName: options?.moduleName ?? "tokens",
8114
+ generateHelper: options?.generateHelper ?? false
8115
+ };
8116
+ if (opts.preset === "bundle") {
8117
+ const { bundleAsJsModule: bundleAsJsModule2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
8118
+ const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8119
+ tokens: stripInternalMetadata(tokens),
8120
+ modifierInputs,
8121
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8122
+ }));
8123
+ return await bundleAsJsModule2(bundleData, context.resolver, opts, async (tokens) => {
8124
+ return await this.formatTokens(tokens, opts);
8325
8125
  });
8326
- return;
8327
8126
  }
8328
- if (typeof value === "object" && value !== null) {
8329
- const entries = Object.entries(value);
8330
- if (entries.length === 0) {
8331
- leaves.push({ path: path7, value });
8332
- return;
8333
- }
8334
- for (const [key, child] of entries) {
8335
- this.collectLeafEntries(child, [...path7, this.normalizePathSegment(key)], leaves);
8336
- }
8337
- return;
8127
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JS module");
8128
+ const files = {};
8129
+ for (const { tokens, modifierInputs } of context.permutations) {
8130
+ const cleanTokens = stripInternalMetadata(tokens);
8131
+ const content = await this.formatTokens(cleanTokens, opts);
8132
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8133
+ outputName: context.output.name,
8134
+ extension: "js",
8135
+ modifierInputs,
8136
+ resolver: context.resolver,
8137
+ defaults: context.meta.defaults
8138
+ });
8139
+ files[fileName] = content;
8338
8140
  }
8339
- leaves.push({ path: path7, value });
8340
- }
8341
- normalizePathSegment(segment) {
8342
- return segment.trim().replace(/\s+/g, "-");
8141
+ return outputTree(files);
8343
8142
  }
8344
- buildCompositeName(base, path7) {
8345
- if (path7.length === 0) {
8346
- return base;
8143
+ async formatTokens(tokens, options) {
8144
+ const lines = [];
8145
+ lines.push(...this.formatAsObject(tokens, options));
8146
+ const code = lines.join("\n");
8147
+ if (options.minify) {
8148
+ return code;
8347
8149
  }
8348
- return `${base}-${path7.join("-")}`;
8150
+ return await prettier__default.default.format(code, {
8151
+ parser: "babel",
8152
+ printWidth: 80,
8153
+ tabWidth: 2,
8154
+ useTabs: false,
8155
+ semi: false,
8156
+ singleQuote: true,
8157
+ trailingComma: "es5"
8158
+ });
8349
8159
  }
8350
- formatLeafValue(value) {
8351
- if (isColorObject(value)) {
8352
- return colorObjectToHex(value);
8353
- }
8354
- if (isDimensionObject(value)) {
8355
- return dimensionObjectToString(value);
8356
- }
8357
- if (isDurationObject(value)) {
8358
- return durationObjectToString(value);
8359
- }
8360
- if (typeof value === "string") {
8361
- return value;
8362
- }
8363
- if (typeof value === "number" || typeof value === "boolean") {
8364
- return String(value);
8160
+ formatAsObject(tokens, options) {
8161
+ const lines = [];
8162
+ const tokenMap = this.buildTokenMap(tokens);
8163
+ const varName = options.moduleName !== "" ? options.moduleName : "tokens";
8164
+ if (options.structure === "flat") {
8165
+ lines.push(`const ${varName} = {`);
8166
+ this.addFlatProperties(lines, tokens, 1);
8167
+ lines.push("}");
8168
+ } else {
8169
+ lines.push(`const ${varName} = {`);
8170
+ const tokenObj = this.tokensToPlainObject(tokens, "nested");
8171
+ this.addNestedProperties(lines, tokenObj, tokenMap, 1);
8172
+ lines.push("}");
8365
8173
  }
8366
- if (Array.isArray(value)) {
8367
- return JSON.stringify(value);
8174
+ lines.push("");
8175
+ lines.push(`export default ${varName}`);
8176
+ return lines;
8177
+ }
8178
+ buildTokenMap(tokens) {
8179
+ const map = /* @__PURE__ */ new Map();
8180
+ for (const [name, token] of Object.entries(tokens)) {
8181
+ map.set(name, token);
8368
8182
  }
8369
- if (typeof value === "object" && value != null) {
8370
- return JSON.stringify(value);
8183
+ return map;
8184
+ }
8185
+ addFlatProperties(lines, tokens, indent) {
8186
+ const indentStr2 = " ".repeat(indent);
8187
+ const sortedEntries = getSortedTokenEntries(tokens);
8188
+ for (let i = 0; i < sortedEntries.length; i++) {
8189
+ const [name, token] = sortedEntries[i];
8190
+ const isLast = i === sortedEntries.length - 1;
8191
+ this.pushTokenComments(lines, token, indentStr2);
8192
+ lines.push(
8193
+ `${indentStr2}${this.quoteKey(name)}: ${JSON.stringify(token.$value)}${isLast ? "" : ","}`
8194
+ );
8371
8195
  }
8372
- return String(value);
8373
8196
  }
8374
- resolveCompositeLeafValue(leaf, originalValue, tokens, referenceTokens, preserveReferences) {
8375
- if (!preserveReferences) {
8376
- return leaf.value;
8197
+ pushTokenComments(lines, token, indent) {
8198
+ const deprecationComment = buildTokenDeprecationComment(token, "js");
8199
+ if (deprecationComment) {
8200
+ lines.push(`${indent}${deprecationComment}`);
8377
8201
  }
8378
- const originalLeafValue = this.getOriginalLeafValue(originalValue, leaf.path);
8379
- const refName = getPureAliasReferenceName(originalLeafValue);
8380
- if (refName === void 0) {
8381
- return leaf.value;
8202
+ const descriptionComment = buildTokenDescriptionComment(token, "js");
8203
+ if (descriptionComment) {
8204
+ lines.push(`${indent}${descriptionComment}`);
8382
8205
  }
8383
- return this.buildCssVarReference(refName, referenceTokens, tokens);
8384
8206
  }
8385
- buildCssVarReference(refName, referenceTokens, tokens) {
8386
- const referencedToken = referenceTokens[refName] ?? tokens[refName];
8387
- if (!referencedToken) {
8388
- throw new exports.ConfigurationError(
8389
- `CSS reference "{${refName}}" could not be resolved. The referenced token is not present in the current output's token set. This usually means a filter (e.g. isAlias()) excluded the referenced token while preserveReferences is true. Either remove the filter, include the referenced token, or set preserveReferences to false.`
8390
- );
8207
+ tokensToPlainObject(tokens, structure) {
8208
+ if (structure === "nested") {
8209
+ return buildNestedTokenObject(tokens, (token) => token.$value);
8391
8210
  }
8392
- return `var(--${referencedToken.name})`;
8211
+ const result = {};
8212
+ for (const [name, token] of getSortedTokenEntries(tokens)) {
8213
+ result[name] = token.$value;
8214
+ }
8215
+ return result;
8393
8216
  }
8394
- getOriginalLeafValue(value, path7) {
8395
- let current = value;
8396
- for (const segment of path7) {
8397
- if (Array.isArray(current)) {
8398
- const index = Number(segment);
8399
- if (!Number.isInteger(index)) {
8400
- return void 0;
8401
- }
8402
- current = current[index];
8217
+ addNestedProperties(lines, obj, tokenMap, indent) {
8218
+ const indentStr2 = " ".repeat(indent);
8219
+ const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
8220
+ for (let i = 0; i < entries.length; i++) {
8221
+ const entry = entries[i];
8222
+ if (entry == null) {
8403
8223
  continue;
8404
8224
  }
8405
- if (typeof current === "object" && current !== null) {
8406
- const entries = Object.entries(current);
8407
- const matched = entries.find(([key]) => this.normalizePathSegment(key) === segment);
8408
- if (!matched) {
8409
- return void 0;
8225
+ const [key, value] = entry;
8226
+ const isLast = i === entries.length - 1;
8227
+ const isNestedObject = typeof value === "object" && value !== null && !Array.isArray(value);
8228
+ if (!isNestedObject) {
8229
+ const token = tokenMap.get(key);
8230
+ if (token) {
8231
+ this.pushTokenComments(lines, token, indentStr2);
8410
8232
  }
8411
- current = matched[1];
8233
+ lines.push(
8234
+ `${indentStr2}${this.quoteKey(key)}: ${JSON.stringify(value)}${isLast ? "" : ","}`
8235
+ );
8412
8236
  continue;
8413
8237
  }
8414
- return void 0;
8238
+ lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
8239
+ this.addNestedProperties(lines, value, tokenMap, indent + 1);
8240
+ lines.push(`${indentStr2}}${isLast ? "" : ","}`);
8241
+ }
8242
+ }
8243
+ quoteKey(key) {
8244
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) {
8245
+ return key;
8246
+ }
8247
+ return `"${key}"`;
8248
+ }
8249
+ };
8250
+ function jsRenderer() {
8251
+ const rendererInstance = new JsModuleRenderer();
8252
+ return {
8253
+ format: (context, options) => rendererInstance.format(
8254
+ context,
8255
+ options ?? context.output.options
8256
+ )
8257
+ };
8258
+ }
8259
+
8260
+ // src/outputs/json/renderer.ts
8261
+ init_utils();
8262
+ init_token_utils();
8263
+ var JsonRenderer = class {
8264
+ async format(context, options) {
8265
+ const opts = {
8266
+ preset: options?.preset ?? "standalone",
8267
+ structure: options?.structure ?? "nested",
8268
+ minify: options?.minify ?? false,
8269
+ includeMetadata: options?.includeMetadata ?? false
8270
+ };
8271
+ if (opts.preset === "bundle") {
8272
+ const { bundleAsJson: bundleAsJson2 } = await Promise.resolve().then(() => (init_bundle2(), bundle_exports2));
8273
+ const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8274
+ tokens: stripInternalMetadata(tokens),
8275
+ modifierInputs,
8276
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8277
+ }));
8278
+ return await bundleAsJson2(bundleData, context.resolver, async (tokens) => {
8279
+ return await this.formatTokens(tokens, opts);
8280
+ });
8281
+ }
8282
+ assertFileRequired(context.buildPath, context.output.file, context.output.name, "JSON");
8283
+ const files = {};
8284
+ for (const { tokens, modifierInputs } of context.permutations) {
8285
+ const processedTokens = stripInternalMetadata(tokens);
8286
+ const content = await this.formatTokens(processedTokens, opts);
8287
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8288
+ outputName: context.output.name,
8289
+ extension: "json",
8290
+ modifierInputs,
8291
+ resolver: context.resolver,
8292
+ defaults: context.meta.defaults
8293
+ });
8294
+ files[fileName] = content;
8295
+ }
8296
+ return outputTree(files);
8297
+ }
8298
+ async formatTokens(tokens, options) {
8299
+ const opts = {
8300
+ preset: options.preset ?? "standalone",
8301
+ structure: options.structure ?? "nested",
8302
+ minify: options.minify ?? false,
8303
+ includeMetadata: options.includeMetadata ?? false
8304
+ };
8305
+ let output;
8306
+ if (opts.structure === "flat") {
8307
+ output = opts.includeMetadata ? this.flattenTokens(tokens) : this.flattenValues(tokens);
8308
+ } else {
8309
+ output = opts.includeMetadata ? this.nestTokens(tokens) : this.nestValues(tokens);
8310
+ }
8311
+ const jsonString = JSON.stringify(output);
8312
+ if (!opts.minify) {
8313
+ return await prettier__default.default.format(jsonString, {
8314
+ parser: "json",
8315
+ printWidth: 80,
8316
+ tabWidth: 2,
8317
+ useTabs: false
8318
+ });
8415
8319
  }
8416
- return current;
8417
- }
8418
- isPrimitiveValue(value) {
8419
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
8320
+ return jsonString;
8420
8321
  }
8421
8322
  /**
8422
- * Format token value for CSS
8423
- * Handles DTCG 2025.10 object formats for colors and dimensions
8323
+ * Flatten tokens to simple key-value pairs
8424
8324
  */
8425
- formatValue(token) {
8426
- const value = token.$value;
8427
- const typed = this.formatTypedValue(token.$type, value);
8428
- if (typed !== void 0) {
8429
- return typed;
8325
+ flattenValues(tokens) {
8326
+ const result = {};
8327
+ for (const [name, token] of getSortedTokenEntries(tokens)) {
8328
+ result[name] = token.$value;
8430
8329
  }
8431
- return this.formatPrimitiveOrStructured(value, token.$type);
8330
+ return result;
8432
8331
  }
8433
- formatTypedValue(type, value) {
8434
- if (type === "color" && isColorObject(value)) {
8435
- return colorObjectToHex(value);
8332
+ /**
8333
+ * Flatten tokens to metadata objects
8334
+ */
8335
+ flattenTokens(tokens) {
8336
+ const result = {};
8337
+ for (const [name, token] of getSortedTokenEntries(tokens)) {
8338
+ result[name] = this.serializeToken(token);
8436
8339
  }
8437
- if (type === "dimension") {
8438
- return typeof value === "string" ? value : dimensionObjectToString(value);
8340
+ return result;
8341
+ }
8342
+ nestValues(tokens) {
8343
+ return buildNestedTokenObject(tokens, (token) => token.$value);
8344
+ }
8345
+ nestTokens(tokens) {
8346
+ return buildNestedTokenObject(tokens, (token) => this.serializeToken(token));
8347
+ }
8348
+ serializeToken(token) {
8349
+ return {
8350
+ $value: token.$value,
8351
+ ...typeof token.$type === "string" && { $type: token.$type },
8352
+ ...token.$description != null && token.$description !== "" && { $description: token.$description },
8353
+ ...token.$deprecated != null && token.$deprecated !== false && { $deprecated: token.$deprecated },
8354
+ ...token.$extensions != null && { $extensions: token.$extensions }
8355
+ };
8356
+ }
8357
+ };
8358
+ function jsonRenderer() {
8359
+ const rendererInstance = new JsonRenderer();
8360
+ return {
8361
+ format: (context, options) => rendererInstance.format(
8362
+ context,
8363
+ options ?? context.output.options
8364
+ )
8365
+ };
8366
+ }
8367
+
8368
+ // src/outputs/tailwind/renderer.ts
8369
+ init_token_utils();
8370
+
8371
+ // src/outputs/tailwind/presets/bundle.ts
8372
+ init_errors();
8373
+ init_utils();
8374
+ async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOverrideBlock) {
8375
+ const baseItem = bundleData.find((item) => item.isBase);
8376
+ if (!baseItem) {
8377
+ throw new exports.BasePermutationError("Base permutation not found in bundle data");
8378
+ }
8379
+ const resolvedOpts = resolveOptions(options);
8380
+ const cssBlocks = [];
8381
+ const variantDeclarations = collectVariantDeclarations(bundleData, baseItem, resolvedOpts);
8382
+ const themeOpts = { ...resolvedOpts, variantDeclarations };
8383
+ const baseTokens = stripInternalMetadata(baseItem.tokens);
8384
+ const themeBlock = await formatThemeTokens(baseTokens, themeOpts);
8385
+ cssBlocks.push(themeBlock);
8386
+ for (const item of bundleData) {
8387
+ if (item.isBase) {
8388
+ continue;
8439
8389
  }
8440
- if (type === "duration") {
8441
- if (isDurationObject(value)) {
8442
- return durationObjectToString(value);
8443
- }
8444
- if (typeof value === "string") {
8445
- return value;
8446
- }
8390
+ const block = await formatModifierOverride(item, baseItem, resolvedOpts, formatOverrideBlock);
8391
+ if (block) {
8392
+ cssBlocks.push(block);
8447
8393
  }
8394
+ }
8395
+ return cssBlocks.join("\n");
8396
+ }
8397
+ function resolveModifierSelectorAndMedia(options, modifier, context, modifierInputs) {
8398
+ const normalized = normalizeModifierInputs(modifierInputs);
8399
+ return {
8400
+ selector: resolveSelector(options.selector, modifier, context, false, normalized),
8401
+ mediaQuery: resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized)
8402
+ };
8403
+ }
8404
+ async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
8405
+ const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
8406
+ if (differenceCount > 1) {
8448
8407
  return void 0;
8449
8408
  }
8450
- formatPrimitiveOrStructured(value, tokenType) {
8451
- if (typeof value === "string") {
8452
- return value;
8453
- }
8454
- if (typeof value === "number") {
8455
- return String(value);
8456
- }
8457
- if (Array.isArray(value)) {
8458
- return this.formatArrayValue(value, tokenType);
8459
- }
8460
- if (typeof value === "object" && value != null) {
8461
- return this.formatCompositeValue(value, tokenType);
8462
- }
8463
- return String(value);
8409
+ const tokensToInclude = filterTokensByValueChange(tokens, baseItem.tokens);
8410
+ if (Object.keys(tokensToInclude).length === 0) {
8411
+ return void 0;
8464
8412
  }
8465
- formatArrayValue(value, tokenType) {
8466
- if (tokenType === "shadow" && value.length > 0 && typeof value[0] === "object") {
8467
- return value.map((shadowObj) => this.formatShadow(shadowObj)).join(", ");
8413
+ const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
8414
+ const [modifier, context] = parseModifierSource(expectedSource);
8415
+ const cleanTokens = stripInternalMetadata(tokensToInclude);
8416
+ const { selector, mediaQuery } = resolveModifierSelectorAndMedia(
8417
+ options,
8418
+ modifier,
8419
+ context,
8420
+ modifierInputs
8421
+ );
8422
+ const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
8423
+ return `/* Modifier: ${modifier}=${context} */
8424
+ ${css2}`;
8425
+ }
8426
+ function filterTokensByValueChange(currentTokens, baseTokens) {
8427
+ const changed = {};
8428
+ for (const [name, token] of Object.entries(currentTokens)) {
8429
+ const baseToken = baseTokens[name];
8430
+ if (!baseToken) {
8431
+ changed[name] = token;
8432
+ continue;
8433
+ }
8434
+ if (JSON.stringify(token.$value) !== JSON.stringify(baseToken.$value)) {
8435
+ changed[name] = token;
8468
8436
  }
8469
- return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
8470
8437
  }
8471
- /**
8472
- * Format composite token values
8473
- */
8474
- formatCompositeValue(value, tokenType) {
8475
- if (tokenType === "shadow") {
8476
- return this.formatShadow(value);
8438
+ return changed;
8439
+ }
8440
+ function collectVariantDeclarations(bundleData, baseItem, options) {
8441
+ const declarations = [];
8442
+ for (const item of bundleData) {
8443
+ if (item.isBase) {
8444
+ continue;
8477
8445
  }
8478
- if (tokenType === "border") {
8479
- return this.formatBorder(value);
8446
+ const differenceCount = countModifierDifferences(item.modifierInputs, baseItem.modifierInputs);
8447
+ if (differenceCount > 1) {
8448
+ continue;
8480
8449
  }
8481
- if (tokenType === "transition") {
8482
- return this.formatTransition(value);
8450
+ const expectedSource = getExpectedSource(item.modifierInputs, baseItem.modifierInputs);
8451
+ const [modifier, context] = parseModifierSource(expectedSource);
8452
+ const variantName = `${modifier}-${context}`;
8453
+ const normalized = normalizeModifierInputs(item.modifierInputs);
8454
+ const mediaQuery = resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized);
8455
+ if (mediaQuery !== "") {
8456
+ declarations.push(`@custom-variant ${variantName} (@media ${mediaQuery});`);
8457
+ continue;
8483
8458
  }
8484
- return JSON.stringify(value);
8459
+ const selector = resolveSelector(options.selector, modifier, context, false, normalized);
8460
+ declarations.push(`@custom-variant ${variantName} (&:where(${selector}, ${selector} *));`);
8485
8461
  }
8486
- /**
8487
- * Format a single shadow object to CSS box-shadow syntax
8488
- */
8489
- formatShadow(shadow) {
8490
- const parts = [];
8491
- if (shadow.inset === true) {
8492
- parts.push("inset");
8493
- }
8494
- parts.push(dimensionObjectToString(shadow.offsetX));
8495
- parts.push(dimensionObjectToString(shadow.offsetY));
8496
- parts.push(dimensionObjectToString(shadow.blur));
8497
- if (shadow.spread != null) {
8498
- parts.push(dimensionObjectToString(shadow.spread));
8499
- }
8500
- if (isColorObject(shadow.color)) {
8501
- parts.push(colorObjectToHex(shadow.color));
8502
- } else {
8503
- parts.push(String(shadow.color));
8462
+ return declarations;
8463
+ }
8464
+ function resolveOptions(options) {
8465
+ return {
8466
+ preset: options.preset ?? "bundle",
8467
+ includeImport: options.includeImport ?? true,
8468
+ namespace: options.namespace ?? "",
8469
+ minify: options.minify ?? false,
8470
+ selector: options.selector,
8471
+ mediaQuery: options.mediaQuery,
8472
+ variantDeclarations: []
8473
+ };
8474
+ }
8475
+
8476
+ // src/outputs/tailwind/renderer.ts
8477
+ init_utils();
8478
+ init_metadata();
8479
+ var TAILWIND_NAMESPACE_MAP = {
8480
+ color: "color",
8481
+ dimension: "spacing",
8482
+ fontFamily: "font",
8483
+ fontWeight: "font-weight",
8484
+ duration: "duration",
8485
+ shadow: "shadow",
8486
+ number: "number",
8487
+ cubicBezier: "ease"
8488
+ };
8489
+ var TailwindRenderer = class {
8490
+ async format(context, options) {
8491
+ const opts = {
8492
+ preset: options?.preset ?? "bundle",
8493
+ includeImport: options?.includeImport ?? true,
8494
+ namespace: options?.namespace ?? "",
8495
+ minify: options?.minify ?? false,
8496
+ selector: options?.selector,
8497
+ mediaQuery: options?.mediaQuery,
8498
+ variantDeclarations: []
8499
+ };
8500
+ if (opts.preset === "bundle") {
8501
+ return await this.formatBundle(context, opts);
8504
8502
  }
8505
- return parts.join(" ");
8503
+ return await this.formatStandalone(context, opts);
8506
8504
  }
8507
8505
  /**
8508
- * Format a border object to CSS border shorthand syntax
8506
+ * Format tokens as Tailwind v4 @theme CSS variables
8509
8507
  */
8510
- formatBorder(value) {
8511
- const parts = [];
8512
- if (isDimensionObject(value.width)) {
8513
- parts.push(dimensionObjectToString(value.width));
8514
- } else if (value.width != null) {
8515
- parts.push(String(value.width));
8508
+ async formatTokens(tokens, options) {
8509
+ const lines = [];
8510
+ const indent = options.minify ? "" : " ";
8511
+ const newline = options.minify ? "" : "\n";
8512
+ const space = options.minify ? "" : " ";
8513
+ if (options.includeImport) {
8514
+ lines.push(`@import "tailwindcss";${newline}`);
8515
+ }
8516
+ if (options.variantDeclarations.length > 0) {
8517
+ if (options.includeImport) {
8518
+ lines.push(newline);
8519
+ }
8520
+ for (const declaration of options.variantDeclarations) {
8521
+ lines.push(`${declaration}${newline}`);
8522
+ }
8516
8523
  }
8517
- if (typeof value.style === "string") {
8518
- parts.push(value.style);
8524
+ if (options.includeImport || options.variantDeclarations.length > 0) {
8525
+ lines.push(newline);
8519
8526
  }
8520
- if (isColorObject(value.color)) {
8521
- parts.push(colorObjectToHex(value.color));
8522
- } else if (value.color != null) {
8523
- parts.push(String(value.color));
8527
+ const themeDirective = options.namespace ? `@theme namespace(${options.namespace})` : "@theme";
8528
+ lines.push(`${themeDirective}${space}{${newline}`);
8529
+ for (const [, token] of getSortedTokenEntries(tokens)) {
8530
+ const varName = this.buildVariableName(token);
8531
+ const varValue = this.formatValue(token);
8532
+ const deprecationComment = buildTokenDeprecationComment(token, "tailwind");
8533
+ if (deprecationComment) {
8534
+ lines.push(`${indent}${deprecationComment}${newline}`);
8535
+ }
8536
+ const descriptionComment = buildTokenDescriptionComment(token, "tailwind");
8537
+ if (descriptionComment) {
8538
+ lines.push(`${indent}${descriptionComment}${newline}`);
8539
+ }
8540
+ lines.push(`${indent}--${varName}:${space}${varValue};${newline}`);
8524
8541
  }
8525
- return parts.join(" ");
8542
+ lines.push(`}${newline}`);
8543
+ const cssString = lines.join("");
8544
+ return options.minify ? cssString : await this.formatWithPrettier(cssString);
8526
8545
  }
8527
8546
  /**
8528
- * Format a transition object to CSS transition shorthand syntax
8547
+ * Format tokens as plain CSS custom property overrides inside a selector block.
8548
+ * Used for modifier overrides (e.g., dark mode) appended after the @theme block.
8529
8549
  */
8530
- formatTransition(value) {
8531
- const parts = [];
8532
- if (isDurationObject(value.duration)) {
8533
- parts.push(durationObjectToString(value.duration));
8534
- } else if (value.duration != null) {
8535
- parts.push(String(value.duration));
8536
- }
8537
- if (Array.isArray(value.timingFunction) && value.timingFunction.length === 4) {
8538
- parts.push(`cubic-bezier(${value.timingFunction.join(", ")})`);
8550
+ async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
8551
+ const indent = minify ? "" : " ";
8552
+ const newline = minify ? "" : "\n";
8553
+ const space = minify ? "" : " ";
8554
+ const hasMediaQuery = mediaQuery !== "";
8555
+ const tokenIndent = hasMediaQuery ? indent + indent : indent;
8556
+ const lines = [];
8557
+ if (hasMediaQuery) {
8558
+ lines.push(`@media ${mediaQuery}${space}{${newline}`);
8559
+ lines.push(`${indent}${selector}${space}{${newline}`);
8560
+ } else {
8561
+ lines.push(`${selector}${space}{${newline}`);
8539
8562
  }
8540
- if (isDurationObject(value.delay)) {
8541
- parts.push(durationObjectToString(value.delay));
8542
- } else if (value.delay != null) {
8543
- parts.push(String(value.delay));
8563
+ for (const [, token] of getSortedTokenEntries(tokens)) {
8564
+ const varName = this.buildVariableName(token);
8565
+ const varValue = this.formatValue(token);
8566
+ const deprecationComment = buildTokenDeprecationComment(token, "tailwind");
8567
+ if (deprecationComment) {
8568
+ lines.push(`${tokenIndent}${deprecationComment}${newline}`);
8569
+ }
8570
+ const descriptionComment = buildTokenDescriptionComment(token, "tailwind");
8571
+ if (descriptionComment) {
8572
+ lines.push(`${tokenIndent}${descriptionComment}${newline}`);
8573
+ }
8574
+ lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
8544
8575
  }
8545
- return parts.join(" ");
8546
- }
8547
- async formatBundle(context, options) {
8548
- const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8549
- tokens,
8550
- modifierInputs,
8551
- isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8552
- }));
8553
- return await bundleAsCss(bundleData, context.resolver, options, async (tokens, resolved) => {
8554
- return await this.formatTokens(tokens, {
8555
- ...resolved,
8556
- preserveReferences: options.preserveReferences ?? false
8557
- });
8558
- });
8559
- }
8560
- async formatStandalone(context, options) {
8561
- assertFileRequired(
8562
- context.buildPath,
8563
- context.output.file,
8564
- context.output.name,
8565
- "standalone CSS"
8566
- );
8567
- const files = {};
8568
- for (const { tokens, modifierInputs } of context.permutations) {
8569
- const { fileName, content } = await this.buildStandaloneFile(
8570
- tokens,
8571
- modifierInputs,
8572
- context,
8573
- options
8574
- );
8575
- files[fileName] = content;
8576
+ if (hasMediaQuery) {
8577
+ lines.push(`${indent}}${newline}`);
8578
+ lines.push(`}${newline}`);
8579
+ } else {
8580
+ lines.push(`}${newline}`);
8576
8581
  }
8577
- return { kind: "outputTree", files };
8578
- }
8579
- async buildStandaloneFile(tokens, modifierInputs, context, options) {
8580
- const isBase = isBasePermutation(modifierInputs, context.meta.defaults);
8581
- const { modifierName, modifierContext } = this.resolveModifierContext(
8582
- modifierInputs,
8583
- context,
8584
- isBase
8585
- );
8586
- const selector = resolveSelector(
8587
- options.selector,
8588
- modifierName,
8589
- modifierContext,
8590
- isBase,
8591
- modifierInputs
8592
- );
8593
- const mediaQuery = resolveMediaQuery(
8594
- options.mediaQuery,
8595
- modifierName,
8596
- modifierContext,
8597
- isBase,
8598
- modifierInputs
8599
- );
8600
- const content = await this.formatTokens(tokens, {
8601
- selector,
8602
- mediaQuery,
8603
- minify: options.minify ?? false,
8604
- preserveReferences: options.preserveReferences ?? false,
8605
- referenceTokens: tokens
8606
- });
8607
- const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8608
- outputName: context.output.name,
8609
- extension: "css",
8610
- modifierInputs,
8611
- resolver: context.resolver,
8612
- defaults: context.meta.defaults
8613
- });
8614
- return { fileName, content };
8582
+ return lines.join("");
8615
8583
  }
8616
- async formatModifier(context, options) {
8617
- assertFileRequired(context.buildPath, context.output.file, context.output.name, "modifier CSS");
8618
- if (!context.resolver.modifiers) {
8619
- throw new exports.ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
8620
- }
8621
- const files = {};
8622
- const baseResult = await this.buildModifierBaseFile(context, options);
8623
- if (baseResult) {
8624
- files[baseResult.fileName] = baseResult.content;
8584
+ buildVariableName(token) {
8585
+ const prefix = TAILWIND_NAMESPACE_MAP[token.$type ?? ""];
8586
+ if (!prefix) {
8587
+ return token.name;
8625
8588
  }
8626
- for (const [modifierName, modifierDef] of Object.entries(context.resolver.modifiers)) {
8627
- for (const contextValue of Object.keys(modifierDef.contexts)) {
8628
- const result = await this.buildModifierContextFile(
8629
- modifierName,
8630
- contextValue,
8631
- context,
8632
- options
8633
- );
8634
- if (result) {
8635
- files[result.fileName] = result.content;
8636
- }
8637
- }
8589
+ const nameLower = token.name.toLowerCase();
8590
+ const prefixLower = prefix.toLowerCase();
8591
+ if (nameLower.startsWith(`${prefixLower}-`) || nameLower.startsWith(`${prefixLower}.`)) {
8592
+ return token.name;
8638
8593
  }
8639
- return { kind: "outputTree", files };
8594
+ return `${prefix}-${token.name}`;
8640
8595
  }
8641
- async buildModifierBaseFile(context, options) {
8642
- const basePermutation = context.permutations.find(
8643
- ({ modifierInputs }) => isBasePermutation(modifierInputs, context.meta.defaults)
8644
- );
8645
- if (!basePermutation) {
8646
- return void 0;
8647
- }
8648
- const setTokens = filterTokensFromSets(basePermutation.tokens);
8649
- if (Object.keys(setTokens).length === 0) {
8650
- return void 0;
8596
+ formatValue(token) {
8597
+ const value = token.$value;
8598
+ if (token.$type === "color" && isColorObject(value)) {
8599
+ return colorObjectToHex(value);
8651
8600
  }
8652
- const setBlocks = buildSetLayerBlocks(setTokens, context.resolver);
8653
- if (setBlocks.length === 0) {
8654
- return void 0;
8601
+ if (token.$type === "dimension" && isDimensionObject(value)) {
8602
+ return dimensionObjectToString(value);
8655
8603
  }
8656
- const { selector, mediaQuery } = this.resolveBaseModifierContext(context, options);
8657
- const content = await this.formatSetBlocksCss(
8658
- setBlocks,
8659
- basePermutation.tokens,
8660
- selector,
8661
- mediaQuery,
8662
- options
8663
- );
8664
- const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8665
- return { fileName, content };
8666
- }
8667
- resolveBaseModifierContext(context, options) {
8668
- const modifiers = context.resolver.modifiers;
8669
- const firstModifierName = Object.keys(modifiers)[0] ?? "";
8670
- const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
8671
- const baseModifierInputs = { ...context.meta.defaults };
8672
- return {
8673
- selector: resolveSelector(
8674
- options.selector,
8675
- firstModifierName,
8676
- firstModifierContext,
8677
- true,
8678
- baseModifierInputs
8679
- ),
8680
- mediaQuery: resolveMediaQuery(
8681
- options.mediaQuery,
8682
- firstModifierName,
8683
- firstModifierContext,
8684
- true,
8685
- baseModifierInputs
8686
- )
8687
- };
8688
- }
8689
- async formatSetBlocksCss(setBlocks, referenceTokens, selector, mediaQuery, options) {
8690
- const cssBlocks = [];
8691
- for (const block of setBlocks) {
8692
- const cleanTokens = stripInternalMetadata(block.tokens);
8693
- const css2 = await this.formatTokens(cleanTokens, {
8694
- selector,
8695
- mediaQuery,
8696
- minify: options.minify ?? false,
8697
- preserveReferences: options.preserveReferences ?? false,
8698
- referenceTokens
8699
- });
8700
- const header = block.description ? `/* ${block.key} */
8701
- /* ${block.description} */` : `/* ${block.key} */`;
8702
- cssBlocks.push(`${header}
8703
- ${css2}`);
8604
+ if (token.$type === "duration" && isDurationObject(value)) {
8605
+ return durationObjectToString(value);
8704
8606
  }
8705
- return cssBlocks.join("\n");
8706
- }
8707
- collectTokensForModifierContext(modifierName, contextValue, permutations) {
8708
- const expectedSource = `${modifierName}-${contextValue}`;
8709
- let tokensFromSource = {};
8710
- let referenceTokens = {};
8711
- for (const { tokens, modifierInputs } of permutations) {
8712
- if (modifierInputs[modifierName] !== contextValue) {
8713
- continue;
8607
+ if (token.$type === "fontFamily") {
8608
+ if (Array.isArray(value)) {
8609
+ return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
8714
8610
  }
8715
- tokensFromSource = { ...tokensFromSource, ...filterTokensBySource(tokens, expectedSource) };
8716
- referenceTokens = { ...referenceTokens, ...tokens };
8611
+ return typeof value === "string" ? value : String(value);
8717
8612
  }
8718
- return { tokensFromSource, referenceTokens };
8613
+ if (token.$type === "shadow") {
8614
+ return this.formatShadowValue(value);
8615
+ }
8616
+ if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
8617
+ return `cubic-bezier(${value.join(", ")})`;
8618
+ }
8619
+ if (typeof value === "string") {
8620
+ return value;
8621
+ }
8622
+ if (typeof value === "number") {
8623
+ return String(value);
8624
+ }
8625
+ return String(value);
8719
8626
  }
8720
- async buildModifierContextFile(modifierName, contextValue, context, options) {
8721
- const { tokensFromSource, referenceTokens } = this.collectTokensForModifierContext(
8722
- modifierName,
8723
- contextValue,
8724
- context.permutations
8725
- );
8726
- if (Object.keys(tokensFromSource).length === 0) {
8727
- return void 0;
8627
+ formatShadowValue(value) {
8628
+ if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object") {
8629
+ return value.map((s) => this.formatSingleShadow(s)).join(", ");
8728
8630
  }
8729
- const defaults = context.meta.defaults;
8730
- const isBase = contextValue === defaults[modifierName];
8731
- const modifierInputs = { ...defaults, [modifierName]: contextValue };
8732
- const selector = resolveSelector(
8733
- options.selector,
8734
- modifierName,
8735
- contextValue,
8736
- isBase,
8737
- modifierInputs
8738
- );
8739
- const mediaQuery = resolveMediaQuery(
8740
- options.mediaQuery,
8741
- modifierName,
8742
- contextValue,
8743
- isBase,
8744
- modifierInputs
8745
- );
8746
- const content = await this.formatTokens(tokensFromSource, {
8747
- selector,
8748
- mediaQuery,
8749
- minify: options.minify ?? false,
8750
- preserveReferences: options.preserveReferences ?? false,
8751
- referenceTokens
8752
- });
8753
- const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8754
- outputName: context.output.name,
8755
- extension: "css",
8756
- modifierInputs,
8757
- resolver: context.resolver,
8758
- defaults: context.meta.defaults
8759
- });
8760
- return { fileName, content };
8631
+ if (typeof value === "object" && value !== null) {
8632
+ return this.formatSingleShadow(value);
8633
+ }
8634
+ return String(value);
8761
8635
  }
8762
- resolveModifierContext(modifierInputs, context, isBase) {
8763
- if (!context.resolver.modifiers) {
8764
- return { modifierName: "", modifierContext: "" };
8636
+ formatSingleShadow(shadow) {
8637
+ const parts = [];
8638
+ if (shadow.inset === true) {
8639
+ parts.push("inset");
8765
8640
  }
8766
- const normalizedInputs = normalizeModifierInputs(modifierInputs);
8767
- const normalizedDefaults = normalizeModifierInputs(context.meta.defaults);
8768
- if (isBase) {
8769
- const firstModifier = Object.keys(context.resolver.modifiers)[0] ?? "";
8770
- return {
8771
- modifierName: firstModifier,
8772
- modifierContext: normalizedInputs[firstModifier] ?? ""
8773
- };
8641
+ if (isDimensionObject(shadow.offsetX)) {
8642
+ parts.push(dimensionObjectToString(shadow.offsetX));
8774
8643
  }
8775
- for (const [name, value] of Object.entries(normalizedInputs)) {
8776
- if (value !== normalizedDefaults[name]) {
8777
- return { modifierName: name, modifierContext: value };
8778
- }
8644
+ if (isDimensionObject(shadow.offsetY)) {
8645
+ parts.push(dimensionObjectToString(shadow.offsetY));
8779
8646
  }
8780
- return { modifierName: "", modifierContext: "" };
8647
+ if (isDimensionObject(shadow.blur)) {
8648
+ parts.push(dimensionObjectToString(shadow.blur));
8649
+ }
8650
+ if (shadow.spread != null && isDimensionObject(shadow.spread)) {
8651
+ parts.push(dimensionObjectToString(shadow.spread));
8652
+ }
8653
+ if (isColorObject(shadow.color)) {
8654
+ parts.push(colorObjectToHex(shadow.color));
8655
+ } else if (shadow.color != null) {
8656
+ parts.push(String(shadow.color));
8657
+ }
8658
+ return parts.join(" ");
8659
+ }
8660
+ async formatWithPrettier(css2) {
8661
+ try {
8662
+ return await prettier__default.default.format(css2, {
8663
+ parser: "css",
8664
+ printWidth: 80,
8665
+ tabWidth: 2,
8666
+ useTabs: false
8667
+ });
8668
+ } catch {
8669
+ return css2;
8670
+ }
8671
+ }
8672
+ async formatBundle(context, options) {
8673
+ const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
8674
+ tokens,
8675
+ modifierInputs,
8676
+ isBase: isBasePermutation(modifierInputs, context.meta.defaults)
8677
+ }));
8678
+ return await bundleAsTailwind(
8679
+ bundleData,
8680
+ options,
8681
+ async (tokens, opts) => await this.formatTokens(tokens, opts),
8682
+ async (tokens, selector, mediaQuery, minify) => await this.formatOverrideBlock(tokens, selector, mediaQuery, minify)
8683
+ );
8684
+ }
8685
+ async formatStandalone(context, options) {
8686
+ assertFileRequired(
8687
+ context.buildPath,
8688
+ context.output.file,
8689
+ context.output.name,
8690
+ "standalone Tailwind"
8691
+ );
8692
+ const files = {};
8693
+ for (const { tokens, modifierInputs } of context.permutations) {
8694
+ const processedTokens = stripInternalMetadata(tokens);
8695
+ const content = await this.formatTokens(processedTokens, options);
8696
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8697
+ outputName: context.output.name,
8698
+ extension: "css",
8699
+ modifierInputs,
8700
+ resolver: context.resolver,
8701
+ defaults: context.meta.defaults
8702
+ });
8703
+ files[fileName] = content;
8704
+ }
8705
+ return outputTree(files);
8781
8706
  }
8782
8707
  };
8783
- function cssRenderer() {
8784
- const rendererInstance = new CssRenderer();
8708
+ function tailwindRenderer() {
8709
+ const rendererInstance = new TailwindRenderer();
8785
8710
  return {
8786
8711
  format: (context, options) => rendererInstance.format(
8787
8712
  context,
@@ -8790,11 +8715,11 @@ function cssRenderer() {
8790
8715
  };
8791
8716
  }
8792
8717
 
8793
- // src/renderers/ios.ts
8718
+ // src/outputs/ios/renderer.ts
8794
8719
  init_utils();
8795
8720
  init_metadata();
8796
- var toSRGB2 = culori.converter("rgb");
8797
- var toP32 = culori.converter("p3");
8721
+ var toSRGB = culori.converter("rgb");
8722
+ var toP3 = culori.converter("p3");
8798
8723
  var SWIFT_TYPE_GROUP_MAP = {
8799
8724
  color: "Colors",
8800
8725
  dimension: "Spacing",
@@ -9020,13 +8945,13 @@ var IosRenderer = class {
9020
8945
  const colorObj = value;
9021
8946
  const alpha = colorObj.alpha ?? 1;
9022
8947
  if (options.colorSpace === "displayP3") {
9023
- const p3 = toP32(dtcgObjectToCulori(colorObj));
8948
+ const p3 = toP3(dtcgObjectToCulori(colorObj));
9024
8949
  const r2 = this.roundComponent(p3?.r ?? 0);
9025
8950
  const g2 = this.roundComponent(p3?.g ?? 0);
9026
8951
  const b2 = this.roundComponent(p3?.b ?? 0);
9027
8952
  return alpha < 1 ? `Color(.displayP3, red: ${r2}, green: ${g2}, blue: ${b2}, opacity: ${this.roundComponent(alpha)})` : `Color(.displayP3, red: ${r2}, green: ${g2}, blue: ${b2})`;
9028
8953
  }
9029
- const rgb = toSRGB2(dtcgObjectToCulori(colorObj));
8954
+ const rgb = toSRGB(dtcgObjectToCulori(colorObj));
9030
8955
  const r = this.roundComponent(rgb?.r ?? 0);
9031
8956
  const g = this.roundComponent(rgb?.g ?? 0);
9032
8957
  const b = this.roundComponent(rgb?.b ?? 0);
@@ -9344,602 +9269,620 @@ function iosRenderer() {
9344
9269
  };
9345
9270
  }
9346
9271
 
9347
- // src/renderers/js-module.ts
9348
- init_utils();
9272
+ // src/outputs/android/renderer.ts
9273
+ init_errors();
9349
9274
  init_token_utils();
9275
+ init_utils();
9350
9276
  init_metadata();
9351
- var JsModuleRenderer = class {
9277
+ var toSRGB2 = culori.converter("rgb");
9278
+ var toP32 = culori.converter("p3");
9279
+ var KOTLIN_KEYWORDS = /* @__PURE__ */ new Set([
9280
+ "val",
9281
+ "var",
9282
+ "fun",
9283
+ "class",
9284
+ "object",
9285
+ "when",
9286
+ "is",
9287
+ "in",
9288
+ "return",
9289
+ "break",
9290
+ "continue",
9291
+ "do",
9292
+ "while",
9293
+ "for",
9294
+ "if",
9295
+ "else",
9296
+ "try",
9297
+ "catch",
9298
+ "throw",
9299
+ "as",
9300
+ "this",
9301
+ "super",
9302
+ "null",
9303
+ "true",
9304
+ "false"
9305
+ ]);
9306
+ var KOTLIN_TYPE_GROUP_MAP = {
9307
+ color: "Colors",
9308
+ dimension: "Spacing",
9309
+ fontFamily: "Fonts",
9310
+ fontWeight: "FontWeights",
9311
+ duration: "Durations",
9312
+ shadow: "Shadows",
9313
+ typography: "Typography",
9314
+ number: "Numbers",
9315
+ cubicBezier: "Animations",
9316
+ border: "Borders"
9317
+ };
9318
+ function resolveColorFormat(format) {
9319
+ if (format === "argb_floats" || format === "argb_float") {
9320
+ return "argb_float";
9321
+ }
9322
+ return "argb_hex";
9323
+ }
9324
+ function escapeKotlinString(str) {
9325
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
9326
+ }
9327
+ function formatKotlinNumber(value) {
9328
+ return Number.isInteger(value) ? `${value}.0` : String(value);
9329
+ }
9330
+ function roundComponent(value) {
9331
+ return Math.round(value * 1e3) / 1e3;
9332
+ }
9333
+ function toResourceName(family) {
9334
+ return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
9335
+ }
9336
+ var AndroidRenderer = class {
9352
9337
  async format(context, options) {
9353
- const opts = {
9354
- preset: options?.preset ?? "standalone",
9355
- structure: options?.structure ?? "nested",
9356
- minify: options?.minify ?? false,
9357
- moduleName: options?.moduleName ?? "tokens",
9358
- generateHelper: options?.generateHelper ?? false
9359
- };
9360
- if (opts.preset === "bundle") {
9361
- const { bundleAsJsModule: bundleAsJsModule2 } = await Promise.resolve().then(() => (init_js(), js_exports));
9362
- const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9363
- tokens: stripInternalMetadata(tokens),
9364
- modifierInputs,
9365
- isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9366
- }));
9367
- return await bundleAsJsModule2(bundleData, context.resolver, opts, async (tokens) => {
9368
- return await this.formatTokens(tokens, opts);
9369
- });
9370
- }
9371
- assertFileRequired(context.buildPath, context.output.file, context.output.name, "JS module");
9372
- const files = {};
9373
- for (const { tokens, modifierInputs } of context.permutations) {
9374
- const cleanTokens = stripInternalMetadata(tokens);
9375
- const content = await this.formatTokens(cleanTokens, opts);
9376
- const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
9377
- outputName: context.output.name,
9378
- extension: "js",
9379
- modifierInputs,
9380
- resolver: context.resolver,
9381
- defaults: context.meta.defaults
9382
- });
9383
- files[fileName] = content;
9338
+ if (!options?.packageName) {
9339
+ throw new exports.ConfigurationError(
9340
+ `Output "${context.output.name}": packageName is required for Android output`
9341
+ );
9384
9342
  }
9385
- return outputTree(files);
9343
+ const visibility = options?.visibility;
9344
+ const opts = {
9345
+ preset: options?.preset ?? "standalone",
9346
+ packageName: options.packageName,
9347
+ objectName: options?.objectName ?? "DesignTokens",
9348
+ colorFormat: resolveColorFormat(options?.colorFormat),
9349
+ colorSpace: options?.colorSpace ?? "sRGB",
9350
+ structure: options?.structure ?? "nested",
9351
+ visibility,
9352
+ visPrefix: visibility ? `${visibility} ` : "",
9353
+ indent: options?.indent ?? 4
9354
+ };
9355
+ if (opts.preset === "bundle") {
9356
+ return await this.formatBundle(context, opts);
9357
+ }
9358
+ return await this.formatStandalone(context, opts);
9386
9359
  }
9387
- async formatTokens(tokens, options) {
9388
- const lines = [];
9389
- lines.push(...this.formatAsObject(tokens, options));
9390
- const code = lines.join("\n");
9391
- if (options.minify) {
9392
- return code;
9360
+ // -----------------------------------------------------------------------
9361
+ // Token tree (nested mode)
9362
+ // -----------------------------------------------------------------------
9363
+ buildTokenTree(tokens) {
9364
+ const root = { children: /* @__PURE__ */ new Map() };
9365
+ for (const [, token] of getSortedTokenEntries(tokens)) {
9366
+ let current = root;
9367
+ const segments = token.path;
9368
+ for (let i = 0; i < segments.length - 1; i++) {
9369
+ const seg = segments[i];
9370
+ if (!current.children.has(seg)) {
9371
+ current.children.set(seg, { children: /* @__PURE__ */ new Map() });
9372
+ }
9373
+ current = current.children.get(seg);
9374
+ }
9375
+ const leafName = segments[segments.length - 1] ?? token.name;
9376
+ const leaf = current.children.get(leafName) ?? { children: /* @__PURE__ */ new Map() };
9377
+ leaf.token = token;
9378
+ current.children.set(leafName, leaf);
9393
9379
  }
9394
- return await prettier__default.default.format(code, {
9395
- parser: "babel",
9396
- printWidth: 80,
9397
- tabWidth: 2,
9398
- useTabs: false,
9399
- semi: false,
9400
- singleQuote: true,
9401
- trailingComma: "es5"
9402
- });
9380
+ return root;
9403
9381
  }
9404
- formatAsObject(tokens, options) {
9405
- const lines = [];
9406
- const tokenMap = this.buildTokenMap(tokens);
9407
- const varName = options.moduleName !== "" ? options.moduleName : "tokens";
9382
+ // -----------------------------------------------------------------------
9383
+ // Flat structure grouping
9384
+ // -----------------------------------------------------------------------
9385
+ /**
9386
+ * Builds a flattened camelCase name from a token's path, stripping the
9387
+ * type prefix segment (which is already represented by the group object).
9388
+ */
9389
+ buildFlatKotlinName(token) {
9390
+ const path7 = token.path;
9391
+ const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
9392
+ const joined = withoutTypePrefix.join("_");
9393
+ return toSafeIdentifier(joined, KOTLIN_KEYWORDS, false);
9394
+ }
9395
+ // -----------------------------------------------------------------------
9396
+ // Rendering
9397
+ // -----------------------------------------------------------------------
9398
+ formatTokens(tokens, options) {
9408
9399
  if (options.structure === "flat") {
9409
- lines.push(`const ${varName} = {`);
9410
- this.addFlatProperties(lines, tokens, 1);
9400
+ return this.formatAsFlat(tokens, options);
9401
+ }
9402
+ return this.formatAsNested(tokens, options);
9403
+ }
9404
+ formatAsNested(tokens, options) {
9405
+ const tokenTypes = this.collectTokenTypesFromEntries(tokens);
9406
+ const tree = this.buildTokenTree(tokens);
9407
+ return this.buildFile(tokenTypes, options, (lines) => {
9408
+ lines.push(`@Suppress("unused")`);
9409
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
9410
+ this.renderTreeChildren(lines, tree, 1, options);
9411
9411
  lines.push("}");
9412
- } else {
9413
- lines.push(`const ${varName} = {`);
9414
- const tokenObj = this.tokensToPlainObject(tokens, "nested");
9415
- this.addNestedProperties(lines, tokenObj, tokenMap, 1);
9412
+ });
9413
+ }
9414
+ formatAsFlat(tokens, options) {
9415
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
9416
+ const tokenTypes = this.collectTokenTypesFromEntries(tokens);
9417
+ return this.buildFile(tokenTypes, options, (lines) => {
9418
+ lines.push(`@Suppress("unused")`);
9419
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
9420
+ this.renderFlatGroups(lines, groups, 1, options);
9416
9421
  lines.push("}");
9422
+ });
9423
+ }
9424
+ /**
9425
+ * Shared file preamble: header, package, imports, optional ShadowToken class.
9426
+ * The `renderBody` callback appends the main object(s) to `lines`.
9427
+ */
9428
+ buildFile(tokenTypes, options, renderBody) {
9429
+ const imports = this.collectImports(tokenTypes, options);
9430
+ const lines = [];
9431
+ lines.push(buildGeneratedFileHeader());
9432
+ lines.push("");
9433
+ lines.push(`package ${options.packageName}`);
9434
+ lines.push("");
9435
+ for (const imp of imports) {
9436
+ lines.push(`import ${imp}`);
9437
+ }
9438
+ if (imports.length > 0) {
9439
+ lines.push("");
9440
+ }
9441
+ if (tokenTypes.has("shadow")) {
9442
+ lines.push(...this.buildShadowTokenClass(options));
9443
+ lines.push("");
9417
9444
  }
9445
+ renderBody(lines);
9418
9446
  lines.push("");
9419
- lines.push(`export default ${varName}`);
9420
- return lines;
9447
+ return lines.join("\n");
9421
9448
  }
9422
- buildTokenMap(tokens) {
9423
- const map = /* @__PURE__ */ new Map();
9424
- for (const [name, token] of Object.entries(tokens)) {
9425
- map.set(name, token);
9449
+ renderFlatGroups(lines, groups, baseDepth, options) {
9450
+ const groupIndent = indentStr(options.indent, baseDepth);
9451
+ const valIndent = indentStr(options.indent, baseDepth + 1);
9452
+ for (const group of groups) {
9453
+ lines.push(`${groupIndent}${options.visPrefix}object ${group.name} {`);
9454
+ for (const token of group.tokens) {
9455
+ const kotlinName = this.buildFlatKotlinName(token);
9456
+ const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
9457
+ const annotation = this.typeAnnotationSuffix(token);
9458
+ const descriptionComment = buildTokenDescriptionComment(token, "kotlin");
9459
+ if (descriptionComment) {
9460
+ lines.push(`${valIndent}${descriptionComment}`);
9461
+ }
9462
+ const deprecation = buildKotlinDeprecationAnnotation(token);
9463
+ if (deprecation) {
9464
+ lines.push(`${valIndent}${deprecation}`);
9465
+ }
9466
+ lines.push(
9467
+ `${valIndent}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`
9468
+ );
9469
+ }
9470
+ lines.push(`${groupIndent}}`);
9471
+ lines.push("");
9426
9472
  }
9427
- return map;
9428
9473
  }
9429
- addFlatProperties(lines, tokens, indent) {
9430
- const indentStr2 = " ".repeat(indent);
9431
- const sortedEntries = getSortedTokenEntries(tokens);
9432
- for (let i = 0; i < sortedEntries.length; i++) {
9433
- const [name, token] = sortedEntries[i];
9434
- const isLast = i === sortedEntries.length - 1;
9435
- this.pushTokenComments(lines, token, indentStr2);
9436
- lines.push(
9437
- `${indentStr2}${this.quoteKey(name)}: ${JSON.stringify(token.$value)}${isLast ? "" : ","}`
9438
- );
9474
+ renderTreeChildren(lines, node, depth, options) {
9475
+ const pad = indentStr(options.indent, depth);
9476
+ const entries = Array.from(node.children.entries());
9477
+ for (let idx = 0; idx < entries.length; idx++) {
9478
+ const [key, child] = entries[idx];
9479
+ if (child.token && child.children.size === 0) {
9480
+ this.renderLeaf(lines, key, child.token, depth, options);
9481
+ } else if (child.children.size > 0 && !child.token) {
9482
+ const objectName = toSafeIdentifier(key, KOTLIN_KEYWORDS, true);
9483
+ lines.push(`${pad}${options.visPrefix}object ${objectName} {`);
9484
+ this.renderTreeChildren(lines, child, depth + 1, options);
9485
+ lines.push(`${pad}}`);
9486
+ if (idx < entries.length - 1) {
9487
+ lines.push("");
9488
+ }
9489
+ } else {
9490
+ this.renderLeaf(lines, key, child.token, depth, options);
9491
+ this.renderTreeChildren(lines, child, depth, options);
9492
+ }
9439
9493
  }
9440
9494
  }
9441
- pushTokenComments(lines, token, indent) {
9442
- const deprecationComment = buildTokenDeprecationComment(token, "js");
9443
- if (deprecationComment) {
9444
- lines.push(`${indent}${deprecationComment}`);
9445
- }
9446
- const descriptionComment = buildTokenDescriptionComment(token, "js");
9495
+ renderLeaf(lines, key, token, depth, options) {
9496
+ const pad = indentStr(options.indent, depth);
9497
+ const kotlinName = toSafeIdentifier(key, KOTLIN_KEYWORDS, false);
9498
+ const kotlinValue = this.formatKotlinValue(token, options, depth);
9499
+ const annotation = this.typeAnnotationSuffix(token);
9500
+ const descriptionComment = buildTokenDescriptionComment(token, "kotlin");
9447
9501
  if (descriptionComment) {
9448
- lines.push(`${indent}${descriptionComment}`);
9449
- }
9450
- }
9451
- tokensToPlainObject(tokens, structure) {
9452
- if (structure === "nested") {
9453
- return buildNestedTokenObject(tokens, (token) => token.$value);
9502
+ lines.push(`${pad}${descriptionComment}`);
9454
9503
  }
9455
- const result = {};
9456
- for (const [name, token] of getSortedTokenEntries(tokens)) {
9457
- result[name] = token.$value;
9504
+ const deprecation = buildKotlinDeprecationAnnotation(token);
9505
+ if (deprecation) {
9506
+ lines.push(`${pad}${deprecation}`);
9458
9507
  }
9459
- return result;
9508
+ lines.push(`${pad}${options.visPrefix}val ${kotlinName}${annotation} = ${kotlinValue}`);
9460
9509
  }
9461
- addNestedProperties(lines, obj, tokenMap, indent) {
9462
- const indentStr2 = " ".repeat(indent);
9463
- const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
9464
- for (let i = 0; i < entries.length; i++) {
9465
- const entry = entries[i];
9466
- if (entry == null) {
9467
- continue;
9468
- }
9469
- const [key, value] = entry;
9470
- const isLast = i === entries.length - 1;
9471
- const isNestedObject = typeof value === "object" && value !== null && !Array.isArray(value);
9472
- if (!isNestedObject) {
9473
- const token = tokenMap.get(key);
9474
- if (token) {
9475
- this.pushTokenComments(lines, token, indentStr2);
9476
- }
9477
- lines.push(
9478
- `${indentStr2}${this.quoteKey(key)}: ${JSON.stringify(value)}${isLast ? "" : ","}`
9479
- );
9480
- continue;
9481
- }
9482
- lines.push(`${indentStr2}${this.quoteKey(key)}: {`);
9483
- this.addNestedProperties(lines, value, tokenMap, indent + 1);
9484
- lines.push(`${indentStr2}}${isLast ? "" : ","}`);
9510
+ // -----------------------------------------------------------------------
9511
+ // Shadow data class
9512
+ // -----------------------------------------------------------------------
9513
+ buildShadowTokenClass(options) {
9514
+ const i1 = indentStr(options.indent, 1);
9515
+ return [
9516
+ "@Immutable",
9517
+ `${options.visPrefix}data class ShadowToken(`,
9518
+ `${i1}val color: Color,`,
9519
+ `${i1}val elevation: Dp,`,
9520
+ `${i1}val offsetX: Dp,`,
9521
+ `${i1}val offsetY: Dp,`,
9522
+ ")"
9523
+ ];
9524
+ }
9525
+ // -----------------------------------------------------------------------
9526
+ // Imports (tree-shaken)
9527
+ // -----------------------------------------------------------------------
9528
+ collectImports(tokenTypes, options) {
9529
+ const imports = /* @__PURE__ */ new Set();
9530
+ const ns = "androidx.compose";
9531
+ const hasColors = tokenTypes.has("color") || tokenTypes.has("shadow") || tokenTypes.has("border");
9532
+ if (hasColors) {
9533
+ imports.add(`${ns}.ui.graphics.Color`);
9485
9534
  }
9486
- }
9487
- quoteKey(key) {
9488
- if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) {
9489
- return key;
9535
+ if (tokenTypes.has("dimension") || tokenTypes.has("shadow") || tokenTypes.has("border")) {
9536
+ imports.add(`${ns}.ui.unit.Dp`);
9537
+ imports.add(`${ns}.ui.unit.dp`);
9490
9538
  }
9491
- return `"${key}"`;
9492
- }
9493
- };
9494
- function jsRenderer() {
9495
- const rendererInstance = new JsModuleRenderer();
9496
- return {
9497
- format: (context, options) => rendererInstance.format(
9498
- context,
9499
- options ?? context.output.options
9500
- )
9501
- };
9502
- }
9503
-
9504
- // src/renderers/json.ts
9505
- init_utils();
9506
- init_token_utils();
9507
- var JsonRenderer = class {
9508
- async format(context, options) {
9509
- const opts = {
9510
- preset: options?.preset ?? "standalone",
9511
- structure: options?.structure ?? "nested",
9512
- minify: options?.minify ?? false,
9513
- includeMetadata: options?.includeMetadata ?? false
9514
- };
9515
- if (opts.preset === "bundle") {
9516
- const { bundleAsJson: bundleAsJson2 } = await Promise.resolve().then(() => (init_json(), json_exports));
9517
- const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9518
- tokens: stripInternalMetadata(tokens),
9519
- modifierInputs,
9520
- isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9521
- }));
9522
- return await bundleAsJson2(bundleData, context.resolver, async (tokens) => {
9523
- return await this.formatTokens(tokens, opts);
9524
- });
9539
+ if (tokenTypes.has("typography") || tokenTypes.has("fontFamily")) {
9540
+ imports.add(`${ns}.ui.text.TextStyle`);
9541
+ imports.add(`${ns}.ui.unit.sp`);
9525
9542
  }
9526
- assertFileRequired(context.buildPath, context.output.file, context.output.name, "JSON");
9527
- const files = {};
9528
- for (const { tokens, modifierInputs } of context.permutations) {
9529
- const processedTokens = stripInternalMetadata(tokens);
9530
- const content = await this.formatTokens(processedTokens, opts);
9531
- const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
9532
- outputName: context.output.name,
9533
- extension: "json",
9534
- modifierInputs,
9535
- resolver: context.resolver,
9536
- defaults: context.meta.defaults
9537
- });
9538
- files[fileName] = content;
9543
+ if (tokenTypes.has("typography") || tokenTypes.has("fontWeight")) {
9544
+ imports.add(`${ns}.ui.text.font.FontWeight`);
9539
9545
  }
9540
- return outputTree(files);
9541
- }
9542
- async formatTokens(tokens, options) {
9543
- const opts = {
9544
- preset: options.preset ?? "standalone",
9545
- structure: options.structure ?? "nested",
9546
- minify: options.minify ?? false,
9547
- includeMetadata: options.includeMetadata ?? false
9548
- };
9549
- let output;
9550
- if (opts.structure === "flat") {
9551
- output = opts.includeMetadata ? this.flattenTokens(tokens) : this.flattenValues(tokens);
9552
- } else {
9553
- output = opts.includeMetadata ? this.nestTokens(tokens) : this.nestValues(tokens);
9546
+ if (tokenTypes.has("fontFamily")) {
9547
+ imports.add(`${ns}.ui.text.font.FontFamily`);
9554
9548
  }
9555
- const jsonString = JSON.stringify(output);
9556
- if (!opts.minify) {
9557
- return await prettier__default.default.format(jsonString, {
9558
- parser: "json",
9559
- printWidth: 80,
9560
- tabWidth: 2,
9561
- useTabs: false
9562
- });
9549
+ if (tokenTypes.has("duration")) {
9550
+ imports.add("kotlin.time.Duration");
9551
+ imports.add("kotlin.time.Duration.Companion.milliseconds");
9552
+ imports.add("kotlin.time.Duration.Companion.seconds");
9563
9553
  }
9564
- return jsonString;
9565
- }
9566
- /**
9567
- * Flatten tokens to simple key-value pairs
9568
- */
9569
- flattenValues(tokens) {
9570
- const result = {};
9571
- for (const [name, token] of getSortedTokenEntries(tokens)) {
9572
- result[name] = token.$value;
9554
+ if (tokenTypes.has("cubicBezier")) {
9555
+ imports.add(`${ns}.animation.core.CubicBezierEasing`);
9573
9556
  }
9574
- return result;
9575
- }
9576
- /**
9577
- * Flatten tokens to metadata objects
9578
- */
9579
- flattenTokens(tokens) {
9580
- const result = {};
9581
- for (const [name, token] of getSortedTokenEntries(tokens)) {
9582
- result[name] = this.serializeToken(token);
9557
+ if (tokenTypes.has("shadow")) {
9558
+ imports.add(`${ns}.runtime.Immutable`);
9583
9559
  }
9584
- return result;
9585
- }
9586
- nestValues(tokens) {
9587
- return buildNestedTokenObject(tokens, (token) => token.$value);
9588
- }
9589
- nestTokens(tokens) {
9590
- return buildNestedTokenObject(tokens, (token) => this.serializeToken(token));
9591
- }
9592
- serializeToken(token) {
9593
- return {
9594
- $value: token.$value,
9595
- ...typeof token.$type === "string" && { $type: token.$type },
9596
- ...token.$description != null && token.$description !== "" && { $description: token.$description },
9597
- ...token.$deprecated != null && token.$deprecated !== false && { $deprecated: token.$deprecated },
9598
- ...token.$extensions != null && { $extensions: token.$extensions }
9599
- };
9600
- }
9601
- };
9602
- function jsonRenderer() {
9603
- const rendererInstance = new JsonRenderer();
9604
- return {
9605
- format: (context, options) => rendererInstance.format(
9606
- context,
9607
- options ?? context.output.options
9608
- )
9609
- };
9610
- }
9611
-
9612
- // src/renderers/tailwind.ts
9613
- init_token_utils();
9614
-
9615
- // src/renderers/bundlers/tailwind.ts
9616
- init_errors();
9617
- init_utils();
9618
- async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOverrideBlock) {
9619
- const baseItem = bundleData.find((item) => item.isBase);
9620
- if (!baseItem) {
9621
- throw new exports.BasePermutationError("Base permutation not found in bundle data");
9622
- }
9623
- const resolvedOpts = resolveOptions(options);
9624
- const cssBlocks = [];
9625
- const variantDeclarations = collectVariantDeclarations(bundleData, baseItem, resolvedOpts);
9626
- const themeOpts = { ...resolvedOpts, variantDeclarations };
9627
- const baseTokens = stripInternalMetadata(baseItem.tokens);
9628
- const themeBlock = await formatThemeTokens(baseTokens, themeOpts);
9629
- cssBlocks.push(themeBlock);
9630
- for (const item of bundleData) {
9631
- if (item.isBase) {
9632
- continue;
9560
+ if (tokenTypes.has("border")) {
9561
+ imports.add(`${ns}.foundation.BorderStroke`);
9633
9562
  }
9634
- const block = await formatModifierOverride(item, baseItem, resolvedOpts, formatOverrideBlock);
9635
- if (block) {
9636
- cssBlocks.push(block);
9563
+ if (options.colorSpace === "displayP3" && hasColors) {
9564
+ imports.add(`${ns}.ui.graphics.colorspace.ColorSpaces`);
9637
9565
  }
9566
+ return Array.from(imports).sort();
9638
9567
  }
9639
- return cssBlocks.join("\n");
9640
- }
9641
- function resolveModifierSelectorAndMedia(options, modifier, context, modifierInputs) {
9642
- const normalized = normalizeModifierInputs(modifierInputs);
9643
- return {
9644
- selector: resolveSelector(options.selector, modifier, context, false, normalized),
9645
- mediaQuery: resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized)
9646
- };
9647
- }
9648
- async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
9649
- const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
9650
- if (differenceCount > 1) {
9651
- return void 0;
9568
+ collectTokenTypesFromEntries(tokens) {
9569
+ const types = /* @__PURE__ */ new Set();
9570
+ for (const [, token] of Object.entries(tokens)) {
9571
+ if (token.$type) {
9572
+ types.add(token.$type);
9573
+ }
9574
+ }
9575
+ return types;
9652
9576
  }
9653
- const tokensToInclude = filterTokensByValueChange(tokens, baseItem.tokens);
9654
- if (Object.keys(tokensToInclude).length === 0) {
9655
- return void 0;
9577
+ // -----------------------------------------------------------------------
9578
+ // Type annotations
9579
+ // -----------------------------------------------------------------------
9580
+ getTypeAnnotation(token) {
9581
+ switch (token.$type) {
9582
+ case "color":
9583
+ return "Color";
9584
+ case "dimension":
9585
+ return "Dp";
9586
+ case "fontFamily":
9587
+ return "FontFamily";
9588
+ case "fontWeight":
9589
+ return "FontWeight";
9590
+ case "duration":
9591
+ return "Duration";
9592
+ case "shadow":
9593
+ return "ShadowToken";
9594
+ case "cubicBezier":
9595
+ return "CubicBezierEasing";
9596
+ case "number":
9597
+ return "Double";
9598
+ case "typography":
9599
+ return "TextStyle";
9600
+ case "border":
9601
+ return "BorderStroke";
9602
+ default: {
9603
+ const value = token.$value;
9604
+ if (typeof value === "string") {
9605
+ return "String";
9606
+ }
9607
+ if (typeof value === "boolean") {
9608
+ return "Boolean";
9609
+ }
9610
+ if (typeof value === "number") {
9611
+ return "Double";
9612
+ }
9613
+ return void 0;
9614
+ }
9615
+ }
9656
9616
  }
9657
- const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
9658
- const [modifier, context] = parseModifierSource(expectedSource);
9659
- const cleanTokens = stripInternalMetadata(tokensToInclude);
9660
- const { selector, mediaQuery } = resolveModifierSelectorAndMedia(
9661
- options,
9662
- modifier,
9663
- context,
9664
- modifierInputs
9665
- );
9666
- const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
9667
- return `/* Modifier: ${modifier}=${context} */
9668
- ${css2}`;
9669
- }
9670
- function filterTokensByValueChange(currentTokens, baseTokens) {
9671
- const changed = {};
9672
- for (const [name, token] of Object.entries(currentTokens)) {
9673
- const baseToken = baseTokens[name];
9674
- if (!baseToken) {
9675
- changed[name] = token;
9676
- continue;
9617
+ typeAnnotationSuffix(token) {
9618
+ const type = this.getTypeAnnotation(token);
9619
+ return type ? `: ${type}` : "";
9620
+ }
9621
+ // -----------------------------------------------------------------------
9622
+ // Value formatting
9623
+ // -----------------------------------------------------------------------
9624
+ formatKotlinValue(token, options, depth) {
9625
+ const value = token.$value;
9626
+ if (token.$type === "color") {
9627
+ return this.formatColorValue(value, options);
9677
9628
  }
9678
- if (JSON.stringify(token.$value) !== JSON.stringify(baseToken.$value)) {
9679
- changed[name] = token;
9629
+ if (token.$type === "dimension") {
9630
+ return this.formatDimensionValue(value);
9680
9631
  }
9681
- }
9682
- return changed;
9683
- }
9684
- function collectVariantDeclarations(bundleData, baseItem, options) {
9685
- const declarations = [];
9686
- for (const item of bundleData) {
9687
- if (item.isBase) {
9688
- continue;
9632
+ if (token.$type === "fontFamily") {
9633
+ return this.formatFontFamilyValue(value);
9689
9634
  }
9690
- const differenceCount = countModifierDifferences(item.modifierInputs, baseItem.modifierInputs);
9691
- if (differenceCount > 1) {
9692
- continue;
9635
+ if (token.$type === "fontWeight") {
9636
+ return this.formatFontWeightValue(value);
9693
9637
  }
9694
- const expectedSource = getExpectedSource(item.modifierInputs, baseItem.modifierInputs);
9695
- const [modifier, context] = parseModifierSource(expectedSource);
9696
- const variantName = `${modifier}-${context}`;
9697
- const normalized = normalizeModifierInputs(item.modifierInputs);
9698
- const mediaQuery = resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized);
9699
- if (mediaQuery !== "") {
9700
- declarations.push(`@custom-variant ${variantName} (@media ${mediaQuery});`);
9701
- continue;
9638
+ if (token.$type === "duration") {
9639
+ return this.formatDurationValue(value);
9702
9640
  }
9703
- const selector = resolveSelector(options.selector, modifier, context, false, normalized);
9704
- declarations.push(`@custom-variant ${variantName} (&:where(${selector}, ${selector} *));`);
9705
- }
9706
- return declarations;
9707
- }
9708
- function resolveOptions(options) {
9709
- return {
9710
- preset: options.preset ?? "bundle",
9711
- includeImport: options.includeImport ?? true,
9712
- namespace: options.namespace ?? "",
9713
- minify: options.minify ?? false,
9714
- selector: options.selector,
9715
- mediaQuery: options.mediaQuery,
9716
- variantDeclarations: []
9717
- };
9718
- }
9719
-
9720
- // src/renderers/tailwind.ts
9721
- init_utils();
9722
- init_metadata();
9723
- var TAILWIND_NAMESPACE_MAP = {
9724
- color: "color",
9725
- dimension: "spacing",
9726
- fontFamily: "font",
9727
- fontWeight: "font-weight",
9728
- duration: "duration",
9729
- shadow: "shadow",
9730
- number: "number",
9731
- cubicBezier: "ease"
9732
- };
9733
- var TailwindRenderer = class {
9734
- async format(context, options) {
9735
- const opts = {
9736
- preset: options?.preset ?? "bundle",
9737
- includeImport: options?.includeImport ?? true,
9738
- namespace: options?.namespace ?? "",
9739
- minify: options?.minify ?? false,
9740
- selector: options?.selector,
9741
- mediaQuery: options?.mediaQuery,
9742
- variantDeclarations: []
9743
- };
9744
- if (opts.preset === "bundle") {
9745
- return await this.formatBundle(context, opts);
9641
+ if (token.$type === "shadow") {
9642
+ return this.formatShadowValue(value, options, depth);
9746
9643
  }
9747
- return await this.formatStandalone(context, opts);
9748
- }
9749
- /**
9750
- * Format tokens as Tailwind v4 @theme CSS variables
9751
- */
9752
- async formatTokens(tokens, options) {
9753
- const lines = [];
9754
- const indent = options.minify ? "" : " ";
9755
- const newline = options.minify ? "" : "\n";
9756
- const space = options.minify ? "" : " ";
9757
- if (options.includeImport) {
9758
- lines.push(`@import "tailwindcss";${newline}`);
9644
+ if (token.$type === "typography") {
9645
+ return this.formatTypographyValue(value, options, depth);
9759
9646
  }
9760
- if (options.variantDeclarations.length > 0) {
9761
- if (options.includeImport) {
9762
- lines.push(newline);
9763
- }
9764
- for (const declaration of options.variantDeclarations) {
9765
- lines.push(`${declaration}${newline}`);
9766
- }
9647
+ if (token.$type === "border") {
9648
+ return this.formatBorderValue(value, options);
9767
9649
  }
9768
- if (options.includeImport || options.variantDeclarations.length > 0) {
9769
- lines.push(newline);
9650
+ if (token.$type === "number") {
9651
+ return typeof value === "number" ? formatKotlinNumber(value) : String(value);
9770
9652
  }
9771
- const themeDirective = options.namespace ? `@theme namespace(${options.namespace})` : "@theme";
9772
- lines.push(`${themeDirective}${space}{${newline}`);
9773
- for (const [, token] of getSortedTokenEntries(tokens)) {
9774
- const varName = this.buildVariableName(token);
9775
- const varValue = this.formatValue(token);
9776
- const deprecationComment = buildTokenDeprecationComment(token, "tailwind");
9777
- if (deprecationComment) {
9778
- lines.push(`${indent}${deprecationComment}${newline}`);
9779
- }
9780
- const descriptionComment = buildTokenDescriptionComment(token, "tailwind");
9781
- if (descriptionComment) {
9782
- lines.push(`${indent}${descriptionComment}${newline}`);
9653
+ if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
9654
+ return `CubicBezierEasing(${value[0]}f, ${value[1]}f, ${value[2]}f, ${value[3]}f)`;
9655
+ }
9656
+ if (typeof value === "string") {
9657
+ return `"${escapeKotlinString(value)}"`;
9658
+ }
9659
+ if (typeof value === "number") {
9660
+ return formatKotlinNumber(value);
9661
+ }
9662
+ if (typeof value === "boolean") {
9663
+ return value ? "true" : "false";
9664
+ }
9665
+ return `"${escapeKotlinString(String(value))}"`;
9666
+ }
9667
+ formatColorValue(value, options) {
9668
+ if (!isColorObject(value)) {
9669
+ if (typeof value === "string") {
9670
+ const hex = value.replace("#", "");
9671
+ if (/^[0-9a-fA-F]{6,8}$/.test(hex)) {
9672
+ const argb = hex.length === 8 ? hex : `FF${hex}`;
9673
+ return `Color(0x${argb.toUpperCase()})`;
9674
+ }
9783
9675
  }
9784
- lines.push(`${indent}--${varName}:${space}${varValue};${newline}`);
9676
+ return "Color.Unspecified";
9785
9677
  }
9786
- lines.push(`}${newline}`);
9787
- const cssString = lines.join("");
9788
- return options.minify ? cssString : await this.formatWithPrettier(cssString);
9678
+ const colorObj = value;
9679
+ const alpha = colorObj.alpha ?? 1;
9680
+ if (options.colorFormat === "argb_float" || options.colorSpace === "displayP3") {
9681
+ return this.formatFloatColor(colorObj, alpha, options);
9682
+ }
9683
+ return this.formatHexColor(colorObj, alpha);
9789
9684
  }
9790
- /**
9791
- * Format tokens as plain CSS custom property overrides inside a selector block.
9792
- * Used for modifier overrides (e.g., dark mode) appended after the @theme block.
9793
- */
9794
- async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
9795
- const indent = minify ? "" : " ";
9796
- const newline = minify ? "" : "\n";
9797
- const space = minify ? "" : " ";
9798
- const hasMediaQuery = mediaQuery !== "";
9799
- const tokenIndent = hasMediaQuery ? indent + indent : indent;
9800
- const lines = [];
9801
- if (hasMediaQuery) {
9802
- lines.push(`@media ${mediaQuery}${space}{${newline}`);
9803
- lines.push(`${indent}${selector}${space}{${newline}`);
9804
- } else {
9805
- lines.push(`${selector}${space}{${newline}`);
9685
+ formatFloatColor(colorObj, alpha, options) {
9686
+ if (options.colorSpace === "displayP3") {
9687
+ const p3 = toP32(dtcgObjectToCulori(colorObj));
9688
+ const r2 = roundComponent(p3?.r ?? 0);
9689
+ const g2 = roundComponent(p3?.g ?? 0);
9690
+ const b2 = roundComponent(p3?.b ?? 0);
9691
+ return `Color(${r2}f, ${g2}f, ${b2}f, ${roundComponent(alpha)}f, ColorSpaces.DisplayP3)`;
9806
9692
  }
9807
- for (const [, token] of getSortedTokenEntries(tokens)) {
9808
- const varName = this.buildVariableName(token);
9809
- const varValue = this.formatValue(token);
9810
- const deprecationComment = buildTokenDeprecationComment(token, "tailwind");
9811
- if (deprecationComment) {
9812
- lines.push(`${tokenIndent}${deprecationComment}${newline}`);
9813
- }
9814
- const descriptionComment = buildTokenDescriptionComment(token, "tailwind");
9815
- if (descriptionComment) {
9816
- lines.push(`${tokenIndent}${descriptionComment}${newline}`);
9817
- }
9818
- lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
9693
+ const rgb = toSRGB2(dtcgObjectToCulori(colorObj));
9694
+ const r = roundComponent(rgb?.r ?? 0);
9695
+ const g = roundComponent(rgb?.g ?? 0);
9696
+ const b = roundComponent(rgb?.b ?? 0);
9697
+ return `Color(${r}f, ${g}f, ${b}f, ${roundComponent(alpha)}f)`;
9698
+ }
9699
+ formatHexColor(colorObj, alpha) {
9700
+ const hex = colorObjectToHex(colorObj);
9701
+ const hexClean = hex.replace("#", "");
9702
+ if (hexClean.length === 8) {
9703
+ const rrggbb = hexClean.slice(0, 6);
9704
+ const aa = hexClean.slice(6, 8);
9705
+ return `Color(0x${aa.toUpperCase()}${rrggbb.toUpperCase()})`;
9819
9706
  }
9820
- if (hasMediaQuery) {
9821
- lines.push(`${indent}}${newline}`);
9822
- lines.push(`}${newline}`);
9823
- } else {
9824
- lines.push(`}${newline}`);
9707
+ const alphaHex = alpha < 1 ? Math.round(alpha * 255).toString(16).padStart(2, "0").toUpperCase() : "FF";
9708
+ return `Color(0x${alphaHex}${hexClean.toUpperCase()})`;
9709
+ }
9710
+ formatDimensionValue(value) {
9711
+ if (isDimensionObject(value)) {
9712
+ const dim = value;
9713
+ const dpValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
9714
+ return `${dpValue}.dp`;
9825
9715
  }
9826
- return lines.join("");
9716
+ return typeof value === "number" ? `${value}.dp` : `0.dp`;
9827
9717
  }
9828
- buildVariableName(token) {
9829
- const prefix = TAILWIND_NAMESPACE_MAP[token.$type ?? ""];
9830
- if (!prefix) {
9831
- return token.name;
9718
+ formatFontFamilyValue(value) {
9719
+ if (Array.isArray(value)) {
9720
+ const primary = value[0];
9721
+ if (typeof primary === "string") {
9722
+ return this.mapKotlinFontFamily(primary);
9723
+ }
9724
+ return "FontFamily.Default";
9832
9725
  }
9833
- const nameLower = token.name.toLowerCase();
9834
- const prefixLower = prefix.toLowerCase();
9835
- if (nameLower.startsWith(`${prefixLower}-`) || nameLower.startsWith(`${prefixLower}.`)) {
9836
- return token.name;
9726
+ return typeof value === "string" ? this.mapKotlinFontFamily(value) : "FontFamily.Default";
9727
+ }
9728
+ mapKotlinFontFamily(family) {
9729
+ const normalized = family.toLowerCase().replace(/['"]/g, "").trim();
9730
+ const builtIn = {
9731
+ "sans-serif": "FontFamily.SansSerif",
9732
+ serif: "FontFamily.Serif",
9733
+ monospace: "FontFamily.Monospace",
9734
+ cursive: "FontFamily.Cursive"
9735
+ };
9736
+ return builtIn[normalized] ?? `FontFamily.Default // TODO: load "${family}" via Font(R.font.${toResourceName(family)})`;
9737
+ }
9738
+ formatFontWeightValue(value) {
9739
+ if (typeof value === "number") {
9740
+ return this.numericFontWeight(value);
9837
9741
  }
9838
- return `${prefix}-${token.name}`;
9742
+ if (typeof value === "string") {
9743
+ return this.namedFontWeight(value) ?? "FontWeight.Normal";
9744
+ }
9745
+ return "FontWeight.Normal";
9839
9746
  }
9840
- formatValue(token) {
9841
- const value = token.$value;
9842
- if (token.$type === "color" && isColorObject(value)) {
9843
- return colorObjectToHex(value);
9747
+ numericFontWeight(weight) {
9748
+ if (weight <= 100) {
9749
+ return "FontWeight.Thin";
9750
+ }
9751
+ if (weight <= 200) {
9752
+ return "FontWeight.ExtraLight";
9844
9753
  }
9845
- if (token.$type === "dimension" && isDimensionObject(value)) {
9846
- return dimensionObjectToString(value);
9754
+ if (weight <= 300) {
9755
+ return "FontWeight.Light";
9847
9756
  }
9848
- if (token.$type === "duration" && isDurationObject(value)) {
9849
- return durationObjectToString(value);
9757
+ if (weight <= 400) {
9758
+ return "FontWeight.Normal";
9850
9759
  }
9851
- if (token.$type === "fontFamily") {
9852
- if (Array.isArray(value)) {
9853
- return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
9854
- }
9855
- return typeof value === "string" ? value : String(value);
9760
+ if (weight <= 500) {
9761
+ return "FontWeight.Medium";
9856
9762
  }
9857
- if (token.$type === "shadow") {
9858
- return this.formatShadowValue(value);
9763
+ if (weight <= 600) {
9764
+ return "FontWeight.SemiBold";
9859
9765
  }
9860
- if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
9861
- return `cubic-bezier(${value.join(", ")})`;
9766
+ if (weight <= 700) {
9767
+ return "FontWeight.Bold";
9862
9768
  }
9863
- if (typeof value === "string") {
9864
- return value;
9769
+ if (weight <= 800) {
9770
+ return "FontWeight.ExtraBold";
9865
9771
  }
9866
- if (typeof value === "number") {
9867
- return String(value);
9772
+ return "FontWeight.Black";
9773
+ }
9774
+ namedFontWeight(name) {
9775
+ const map = {
9776
+ thin: "FontWeight.Thin",
9777
+ extralight: "FontWeight.ExtraLight",
9778
+ ultralight: "FontWeight.ExtraLight",
9779
+ light: "FontWeight.Light",
9780
+ regular: "FontWeight.Normal",
9781
+ normal: "FontWeight.Normal",
9782
+ medium: "FontWeight.Medium",
9783
+ semibold: "FontWeight.SemiBold",
9784
+ demibold: "FontWeight.SemiBold",
9785
+ bold: "FontWeight.Bold",
9786
+ extrabold: "FontWeight.ExtraBold",
9787
+ heavy: "FontWeight.ExtraBold",
9788
+ black: "FontWeight.Black",
9789
+ ultrabold: "FontWeight.Black"
9790
+ };
9791
+ return map[name.toLowerCase()];
9792
+ }
9793
+ formatDurationValue(value) {
9794
+ if (isDurationObject(value)) {
9795
+ return value.unit === "ms" ? `${value.value}.milliseconds` : `${value.value}.seconds`;
9868
9796
  }
9869
- return String(value);
9797
+ return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
9870
9798
  }
9871
- formatShadowValue(value) {
9872
- if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object") {
9873
- return value.map((s) => this.formatSingleShadow(s)).join(", ");
9799
+ formatShadowValue(value, options, depth) {
9800
+ if (Array.isArray(value) && value.length > 0) {
9801
+ return this.formatSingleShadow(value[0], options, depth);
9874
9802
  }
9875
9803
  if (typeof value === "object" && value !== null) {
9876
- return this.formatSingleShadow(value);
9804
+ return this.formatSingleShadow(value, options, depth);
9877
9805
  }
9878
- return String(value);
9806
+ return "ShadowToken(color = Color.Unspecified, elevation = 0.dp, offsetX = 0.dp, offsetY = 0.dp)";
9879
9807
  }
9880
- formatSingleShadow(shadow) {
9881
- const parts = [];
9882
- if (shadow.inset === true) {
9883
- parts.push("inset");
9808
+ formatSingleShadow(shadow, options, depth) {
9809
+ const color = isColorObject(shadow.color) ? this.formatColorValue(shadow.color, options) : "Color.Black";
9810
+ const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
9811
+ const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
9812
+ const offsetY = isDimensionObject(shadow.offsetY) ? this.formatDimensionValue(shadow.offsetY) : "0.dp";
9813
+ const propIndent = indentStr(options.indent, depth + 1);
9814
+ const closeIndent = indentStr(options.indent, depth);
9815
+ return [
9816
+ "ShadowToken(",
9817
+ `${propIndent}color = ${color},`,
9818
+ `${propIndent}elevation = ${elevation},`,
9819
+ `${propIndent}offsetX = ${offsetX},`,
9820
+ `${propIndent}offsetY = ${offsetY},`,
9821
+ `${closeIndent})`
9822
+ ].join("\n");
9823
+ }
9824
+ formatBorderValue(value, options) {
9825
+ if (typeof value !== "object" || value === null) {
9826
+ return "BorderStroke(0.dp, Color.Unspecified)";
9884
9827
  }
9885
- if (isDimensionObject(shadow.offsetX)) {
9886
- parts.push(dimensionObjectToString(shadow.offsetX));
9828
+ const border = value;
9829
+ const width = isDimensionObject(border.width) ? this.formatDimensionValue(border.width) : "0.dp";
9830
+ const color = isColorObject(border.color) ? this.formatColorValue(border.color, options) : "Color.Unspecified";
9831
+ return `BorderStroke(${width}, ${color})`;
9832
+ }
9833
+ formatTypographyValue(value, options, depth) {
9834
+ if (typeof value !== "object" || value === null) {
9835
+ return "TextStyle()";
9887
9836
  }
9888
- if (isDimensionObject(shadow.offsetY)) {
9889
- parts.push(dimensionObjectToString(shadow.offsetY));
9837
+ const typo = value;
9838
+ const parts = [];
9839
+ if (isDimensionObject(typo.fontSize)) {
9840
+ const dim = typo.fontSize;
9841
+ const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
9842
+ parts.push(`fontSize = ${spValue}.sp`);
9890
9843
  }
9891
- if (isDimensionObject(shadow.blur)) {
9892
- parts.push(dimensionObjectToString(shadow.blur));
9844
+ if (typo.fontWeight != null) {
9845
+ parts.push(`fontWeight = ${this.formatFontWeightValue(typo.fontWeight)}`);
9893
9846
  }
9894
- if (shadow.spread != null && isDimensionObject(shadow.spread)) {
9895
- parts.push(dimensionObjectToString(shadow.spread));
9847
+ if (typo.lineHeight != null && typeof typo.lineHeight === "number") {
9848
+ if (isDimensionObject(typo.fontSize)) {
9849
+ const dim = typo.fontSize;
9850
+ const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
9851
+ const lineHeightSp = Math.round(spValue * typo.lineHeight * 100) / 100;
9852
+ parts.push(`lineHeight = ${lineHeightSp}.sp`);
9853
+ }
9896
9854
  }
9897
- if (isColorObject(shadow.color)) {
9898
- parts.push(colorObjectToHex(shadow.color));
9899
- } else if (shadow.color != null) {
9900
- parts.push(String(shadow.color));
9855
+ if (isDimensionObject(typo.letterSpacing)) {
9856
+ const dim = typo.letterSpacing;
9857
+ const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
9858
+ parts.push(`letterSpacing = ${spValue}.sp`);
9901
9859
  }
9902
- return parts.join(" ");
9903
- }
9904
- async formatWithPrettier(css2) {
9905
- try {
9906
- return await prettier__default.default.format(css2, {
9907
- parser: "css",
9908
- printWidth: 80,
9909
- tabWidth: 2,
9910
- useTabs: false
9911
- });
9912
- } catch {
9913
- return css2;
9860
+ if (parts.length === 0) {
9861
+ return "TextStyle()";
9914
9862
  }
9863
+ const propIndent = indentStr(options.indent, depth + 1);
9864
+ const closeIndent = indentStr(options.indent, depth);
9865
+ return `TextStyle(
9866
+ ${parts.map((p) => `${propIndent}${p}`).join(",\n")},
9867
+ ${closeIndent})`;
9915
9868
  }
9916
- async formatBundle(context, options) {
9917
- const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9918
- tokens,
9919
- modifierInputs,
9920
- isBase: isBasePermutation(modifierInputs, context.meta.defaults)
9921
- }));
9922
- return await bundleAsTailwind(
9923
- bundleData,
9924
- options,
9925
- async (tokens, opts) => await this.formatTokens(tokens, opts),
9926
- async (tokens, selector, mediaQuery, minify) => await this.formatOverrideBlock(tokens, selector, mediaQuery, minify)
9927
- );
9928
- }
9869
+ // -----------------------------------------------------------------------
9870
+ // Output: standalone
9871
+ // -----------------------------------------------------------------------
9929
9872
  async formatStandalone(context, options) {
9930
9873
  assertFileRequired(
9931
9874
  context.buildPath,
9932
9875
  context.output.file,
9933
9876
  context.output.name,
9934
- "standalone Tailwind"
9877
+ "standalone Android"
9935
9878
  );
9936
9879
  const files = {};
9937
9880
  for (const { tokens, modifierInputs } of context.permutations) {
9938
9881
  const processedTokens = stripInternalMetadata(tokens);
9939
- const content = await this.formatTokens(processedTokens, options);
9882
+ const content = this.formatTokens(processedTokens, options);
9940
9883
  const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
9941
9884
  outputName: context.output.name,
9942
- extension: "css",
9885
+ extension: "kt",
9943
9886
  modifierInputs,
9944
9887
  resolver: context.resolver,
9945
9888
  defaults: context.meta.defaults
@@ -9948,9 +9891,74 @@ var TailwindRenderer = class {
9948
9891
  }
9949
9892
  return outputTree(files);
9950
9893
  }
9894
+ // -----------------------------------------------------------------------
9895
+ // Output: bundle
9896
+ // -----------------------------------------------------------------------
9897
+ async formatBundle(context, options) {
9898
+ assertFileRequired(
9899
+ context.buildPath,
9900
+ context.output.file,
9901
+ context.output.name,
9902
+ "bundle Android"
9903
+ );
9904
+ const content = this.formatBundleContent(context, options);
9905
+ const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
9906
+ outputName: context.output.name,
9907
+ extension: "kt",
9908
+ modifierInputs: context.meta.basePermutation,
9909
+ resolver: context.resolver,
9910
+ defaults: context.meta.defaults
9911
+ });
9912
+ return outputTree({ [fileName]: content });
9913
+ }
9914
+ formatBundleContent(context, options) {
9915
+ const allTokenTypes = this.collectAllPermutationTypes(context);
9916
+ return this.buildFile(allTokenTypes, options, (lines) => {
9917
+ const i1 = indentStr(options.indent, 1);
9918
+ lines.push(`@Suppress("unused")`);
9919
+ lines.push(`${options.visPrefix}object ${options.objectName} {`);
9920
+ for (let idx = 0; idx < context.permutations.length; idx++) {
9921
+ const { tokens, modifierInputs } = context.permutations[idx];
9922
+ const processedTokens = stripInternalMetadata(tokens);
9923
+ const permName = this.buildPermutationName(modifierInputs);
9924
+ lines.push(`${i1}${options.visPrefix}object ${permName} {`);
9925
+ this.renderBundleTokens(lines, processedTokens, options, 2);
9926
+ lines.push(`${i1}}`);
9927
+ if (idx < context.permutations.length - 1) {
9928
+ lines.push("");
9929
+ }
9930
+ }
9931
+ lines.push("}");
9932
+ });
9933
+ }
9934
+ collectAllPermutationTypes(context) {
9935
+ const types = /* @__PURE__ */ new Set();
9936
+ for (const { tokens } of context.permutations) {
9937
+ for (const t of this.collectTokenTypesFromEntries(stripInternalMetadata(tokens))) {
9938
+ types.add(t);
9939
+ }
9940
+ }
9941
+ return types;
9942
+ }
9943
+ renderBundleTokens(lines, tokens, options, baseDepth) {
9944
+ if (options.structure === "flat") {
9945
+ const groups = groupTokensByType(tokens, KOTLIN_TYPE_GROUP_MAP);
9946
+ this.renderFlatGroups(lines, groups, baseDepth, options);
9947
+ return;
9948
+ }
9949
+ const tree = this.buildTokenTree(tokens);
9950
+ this.renderTreeChildren(lines, tree, baseDepth, options);
9951
+ }
9952
+ buildPermutationName(modifierInputs) {
9953
+ const values = Object.values(modifierInputs);
9954
+ if (values.length === 0) {
9955
+ return "Default";
9956
+ }
9957
+ return values.map((v) => toSafeIdentifier(v, KOTLIN_KEYWORDS, true)).join("");
9958
+ }
9951
9959
  };
9952
- function tailwindRenderer() {
9953
- const rendererInstance = new TailwindRenderer();
9960
+ function androidRenderer() {
9961
+ const rendererInstance = new AndroidRenderer();
9954
9962
  return {
9955
9963
  format: (context, options) => rendererInstance.format(
9956
9964
  context,
@@ -9959,7 +9967,23 @@ function tailwindRenderer() {
9959
9967
  };
9960
9968
  }
9961
9969
 
9962
- // src/builders.ts
9970
+ // src/outputs/types.ts
9971
+ function defineRenderer(renderer) {
9972
+ return renderer;
9973
+ }
9974
+ function nameKebabCase() {
9975
+ return {
9976
+ transform: (token) => {
9977
+ const name = changeCase.kebabCase(token.path.join(" "));
9978
+ return {
9979
+ ...token,
9980
+ name
9981
+ };
9982
+ }
9983
+ };
9984
+ }
9985
+
9986
+ // src/outputs/builders.ts
9963
9987
  function css(config) {
9964
9988
  const {
9965
9989
  name,
@@ -10073,11 +10097,6 @@ function android(config) {
10073
10097
  };
10074
10098
  }
10075
10099
 
10076
- // src/renderers/types.ts
10077
- function defineRenderer(renderer) {
10078
- return renderer;
10079
- }
10080
-
10081
10100
  // src/index.ts
10082
10101
  init_errors();
10083
10102
  /**