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