@servicetitan/hammer-token 2.5.2 → 3.0.1

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 (146) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/README.md +332 -0
  3. package/build/web/core/component-variables.scss +1088 -131
  4. package/build/web/core/component.d.ts +558 -0
  5. package/build/web/core/component.js +6685 -249
  6. package/build/web/core/component.scss +557 -69
  7. package/build/web/core/css-utils/a2-border.css +23 -51
  8. package/build/web/core/css-utils/a2-color.css +221 -233
  9. package/build/web/core/css-utils/a2-font.css +1 -29
  10. package/build/web/core/css-utils/a2-spacing.css +238 -483
  11. package/build/web/core/css-utils/a2-utils.css +496 -781
  12. package/build/web/core/css-utils/border.css +23 -51
  13. package/build/web/core/css-utils/color.css +221 -233
  14. package/build/web/core/css-utils/font.css +1 -29
  15. package/build/web/core/css-utils/spacing.css +238 -483
  16. package/build/web/core/css-utils/utils.css +496 -781
  17. package/build/web/core/index.d.ts +6 -0
  18. package/build/web/core/index.js +1 -1
  19. package/build/web/core/primitive-variables.scss +148 -65
  20. package/build/web/core/primitive.d.ts +209 -0
  21. package/build/web/core/primitive.js +779 -61
  22. package/build/web/core/primitive.scss +207 -124
  23. package/build/web/core/semantic-variables.scss +363 -245
  24. package/build/web/core/semantic.d.ts +221 -0
  25. package/build/web/core/semantic.js +1592 -347
  26. package/build/web/core/semantic.scss +219 -140
  27. package/build/web/index.d.ts +3 -4
  28. package/build/web/types.d.ts +17 -0
  29. package/config.js +121 -496
  30. package/eslint.config.mjs +11 -1
  31. package/package.json +15 -5
  32. package/src/global/primitive/breakpoint.tokens.json +54 -0
  33. package/src/global/primitive/color.tokens.json +1092 -0
  34. package/src/global/primitive/duration.tokens.json +44 -0
  35. package/src/global/primitive/font.tokens.json +151 -0
  36. package/src/global/primitive/radius.tokens.json +94 -0
  37. package/src/global/primitive/size.tokens.json +174 -0
  38. package/src/global/primitive/transition.tokens.json +32 -0
  39. package/src/theme/core/background.tokens.json +1312 -0
  40. package/src/theme/core/border.tokens.json +192 -0
  41. package/src/theme/core/chart.tokens.json +982 -0
  42. package/src/theme/core/component/ai-mark.tokens.json +20 -0
  43. package/src/theme/core/component/alert.tokens.json +261 -0
  44. package/src/theme/core/component/announcement.tokens.json +460 -0
  45. package/src/theme/core/component/avatar.tokens.json +137 -0
  46. package/src/theme/core/component/badge.tokens.json +42 -0
  47. package/src/theme/core/component/breadcrumb.tokens.json +42 -0
  48. package/src/theme/core/component/button-toggle.tokens.json +428 -0
  49. package/src/theme/core/component/button.tokens.json +941 -0
  50. package/src/theme/core/component/calendar.tokens.json +391 -0
  51. package/src/theme/core/component/card.tokens.json +107 -0
  52. package/src/theme/core/component/checkbox.tokens.json +631 -0
  53. package/src/theme/core/component/chip.tokens.json +169 -0
  54. package/src/theme/core/component/combobox.tokens.json +269 -0
  55. package/src/theme/core/component/details.tokens.json +152 -0
  56. package/src/theme/core/component/dialog.tokens.json +87 -0
  57. package/src/theme/core/component/divider.tokens.json +23 -0
  58. package/src/theme/core/component/dnd.tokens.json +208 -0
  59. package/src/theme/core/component/drawer.tokens.json +61 -0
  60. package/src/theme/core/component/drilldown.tokens.json +61 -0
  61. package/src/theme/core/component/edit-card.tokens.json +381 -0
  62. package/src/theme/core/component/field-label.tokens.json +42 -0
  63. package/src/theme/core/component/field-message.tokens.json +65 -0
  64. package/src/theme/core/component/icon.tokens.json +42 -0
  65. package/src/theme/core/component/link.tokens.json +108 -0
  66. package/src/theme/core/component/list-view.tokens.json +82 -0
  67. package/src/theme/core/component/listbox.tokens.json +283 -0
  68. package/src/theme/core/component/menu.tokens.json +230 -0
  69. package/src/theme/core/component/overflow.tokens.json +84 -0
  70. package/src/theme/core/component/page.tokens.json +377 -0
  71. package/src/theme/core/component/pagination.tokens.json +63 -0
  72. package/src/theme/core/component/popover.tokens.json +122 -0
  73. package/src/theme/core/component/progress-bar.tokens.json +133 -0
  74. package/src/theme/core/component/radio.tokens.json +631 -0
  75. package/src/theme/core/component/segmented-control.tokens.json +175 -0
  76. package/src/theme/core/component/select-card.tokens.json +943 -0
  77. package/src/theme/core/component/side-nav.tokens.json +349 -0
  78. package/src/theme/core/component/skeleton.tokens.json +42 -0
  79. package/src/theme/core/component/spinner.tokens.json +96 -0
  80. package/src/theme/core/component/status-icon.tokens.json +164 -0
  81. package/src/theme/core/component/stepper.tokens.json +484 -0
  82. package/src/theme/core/component/switch.tokens.json +285 -0
  83. package/src/theme/core/component/tab.tokens.json +192 -0
  84. package/src/theme/core/component/text-field.tokens.json +160 -0
  85. package/src/theme/core/component/text.tokens.json +59 -0
  86. package/src/theme/core/component/toast.tokens.json +343 -0
  87. package/src/theme/core/component/toolbar.tokens.json +114 -0
  88. package/src/theme/core/component/tooltip.tokens.json +61 -0
  89. package/src/theme/core/focus.tokens.json +56 -0
  90. package/src/theme/core/foreground.tokens.json +416 -0
  91. package/src/theme/core/gradient.tokens.json +41 -0
  92. package/src/theme/core/opacity.tokens.json +25 -0
  93. package/src/theme/core/shadow.tokens.json +81 -0
  94. package/src/theme/core/status.tokens.json +74 -0
  95. package/src/theme/core/typography.tokens.json +163 -0
  96. package/src/utils/__tests__/css-utils-format-utils.test.js +312 -0
  97. package/src/utils/__tests__/sd-build-configs.test.js +306 -0
  98. package/src/utils/__tests__/sd-formats.test.js +942 -0
  99. package/src/utils/__tests__/sd-transforms.test.js +336 -0
  100. package/src/utils/__tests__/token-helpers.test.js +1160 -0
  101. package/src/utils/copy-css-utils-cli.js +13 -1
  102. package/src/utils/css-utils-format-utils.js +105 -176
  103. package/src/utils/figma/__tests__/sync-gradient.test.js +561 -0
  104. package/src/utils/figma/__tests__/token-conversion.test.js +117 -0
  105. package/src/utils/figma/__tests__/token-resolution.test.js +231 -0
  106. package/src/utils/figma/auth.js +355 -0
  107. package/src/utils/figma/constants.js +22 -0
  108. package/src/utils/figma/errors.js +80 -0
  109. package/src/utils/figma/figma-api.js +1069 -0
  110. package/src/utils/figma/get-token.js +348 -0
  111. package/src/utils/figma/sync-components.js +909 -0
  112. package/src/utils/figma/sync-main.js +692 -0
  113. package/src/utils/figma/sync-orchestration.js +683 -0
  114. package/src/utils/figma/sync-primitives.js +230 -0
  115. package/src/utils/figma/sync-semantic.js +1056 -0
  116. package/src/utils/figma/token-conversion.js +340 -0
  117. package/src/utils/figma/token-parsing.js +186 -0
  118. package/src/utils/figma/token-resolution.js +569 -0
  119. package/src/utils/figma/utils.js +199 -0
  120. package/src/utils/sd-build-configs.js +305 -0
  121. package/src/utils/sd-formats.js +948 -0
  122. package/src/utils/sd-transforms.js +165 -0
  123. package/src/utils/token-helpers.js +848 -0
  124. package/tsconfig.json +18 -0
  125. package/vitest.config.js +17 -0
  126. package/.turbo/turbo-build.log +0 -37
  127. package/build/web/core/raw.js +0 -234
  128. package/src/global/primitive/breakpoint.js +0 -19
  129. package/src/global/primitive/color.js +0 -231
  130. package/src/global/primitive/duration.js +0 -16
  131. package/src/global/primitive/font.js +0 -60
  132. package/src/global/primitive/radius.js +0 -31
  133. package/src/global/primitive/size.js +0 -55
  134. package/src/global/primitive/transition.js +0 -16
  135. package/src/theme/core/background.js +0 -170
  136. package/src/theme/core/border.js +0 -103
  137. package/src/theme/core/charts.js +0 -464
  138. package/src/theme/core/component/button.js +0 -708
  139. package/src/theme/core/component/checkbox.js +0 -405
  140. package/src/theme/core/focus.js +0 -35
  141. package/src/theme/core/foreground.js +0 -148
  142. package/src/theme/core/overlay.js +0 -137
  143. package/src/theme/core/shadow.js +0 -29
  144. package/src/theme/core/status.js +0 -49
  145. package/src/theme/core/typography.js +0 -82
  146. package/type/types.ts +0 -344
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Primitive Token Sync
5
+ *
6
+ * Functions for building variable requests for primitive tokens.
7
+ */
8
+
9
+ const {
10
+ convertTokenValueToFigma,
11
+ getFigmaVariableType,
12
+ } = require("./token-conversion");
13
+ const { valuesEqual, getVariableIdByName } = require("./figma-api");
14
+ const { getTokenDescription } = require("./token-resolution");
15
+ /**
16
+ * Get Figma variable scopes from token $extensions["com.figma.scopes"]. Missing or empty = ["ALL_SCOPES"].
17
+ * @param {Object} [token] - Leaf token object
18
+ * @returns {string[]} Scopes array
19
+ */
20
+ function getScopesForVariable(token) {
21
+ const fromToken = token?.$extensions?.["com.figma.scopes"];
22
+ return Array.isArray(fromToken) && fromToken.length > 0
23
+ ? fromToken
24
+ : ["ALL_SCOPES"];
25
+ }
26
+
27
+ /**
28
+ * Build bulk request for primitive variable changes only
29
+ * Returns an object with variables array and variableModeValues array
30
+ * @param {Map} resolvedTokens - Map of resolved tokens
31
+ * @param {Array} existingVariables - Array of existing variables
32
+ * @param {string} collectionId - Collection ID
33
+ * @param {Object} modes - Modes object with light and dark
34
+ * @param {Map} tempIdMap - Temporary ID map for tracking created variables
35
+ * @returns {Object} Object with variableChanges, variableModeValues, stats, errors, and tempIdMap
36
+ */
37
+ function buildPrimitiveVariableRequest(
38
+ resolvedTokens,
39
+ existingVariables,
40
+ collectionId,
41
+ modes,
42
+ tempIdMap = new Map(),
43
+ ) {
44
+ const variableChanges = [];
45
+ const variableModeValues = [];
46
+ const stats = { created: 0, updated: 0, skipped: 0, deleted: 0, errors: 0 };
47
+ const errors = [];
48
+
49
+ // Filter to only primitive tokens (exclude font/line-height - Figma doesn't accept string line-height)
50
+ const primitiveTokens = [];
51
+ for (const [tokenPath, tokenData] of resolvedTokens.entries()) {
52
+ if (tokenData.isPrimitive && !tokenPath.startsWith("font/line-height/")) {
53
+ primitiveTokens.push([tokenPath, tokenData]);
54
+ }
55
+ }
56
+
57
+ const keptPrimitiveNames = new Set(
58
+ primitiveTokens.map(([path]) => `primitive/${path}`),
59
+ );
60
+
61
+ // Process primitives
62
+ for (const [tokenPath, tokenData] of primitiveTokens) {
63
+ try {
64
+ const { token, resolvedLight, resolvedDark } = tokenData;
65
+ const tokenType = token.$type || "string";
66
+
67
+ // Add "primitive/" prefix to variable name for organization
68
+ const variableName = `primitive/${tokenPath}`;
69
+
70
+ // Get existing variable (check both with and without prefix for backward compatibility)
71
+ // Optimize: Use getVariableIdByName for better performance
72
+ const existingId =
73
+ getVariableIdByName(variableName, existingVariables) ||
74
+ getVariableIdByName(tokenPath, existingVariables);
75
+ // Optimize: Use Map for O(1) lookup if we have many variables
76
+ let existing = null;
77
+ if (existingId) {
78
+ if (existingVariables.length > 100) {
79
+ const variableIdMap = new Map();
80
+ for (const v of existingVariables) {
81
+ if (v.id) variableIdMap.set(v.id, v);
82
+ }
83
+ existing = variableIdMap.get(existingId) || null;
84
+ } else {
85
+ existing = existingVariables.find((v) => v.id === existingId);
86
+ }
87
+ }
88
+
89
+ // Convert values (pass tokenPath for context e.g. line-height → %)
90
+ const scopes = getScopesForVariable(token);
91
+ const lightFigmaValue = convertTokenValueToFigma(
92
+ tokenType,
93
+ resolvedLight,
94
+ { tokenPath, scopes },
95
+ );
96
+ const darkFigmaValue = convertTokenValueToFigma(tokenType, resolvedDark, {
97
+ tokenPath,
98
+ scopes,
99
+ });
100
+
101
+ if (lightFigmaValue === null || darkFigmaValue === null) {
102
+ errors.push(`Could not convert values for ${tokenPath}`);
103
+ stats.errors++;
104
+ continue;
105
+ }
106
+
107
+ if (existing) {
108
+ // Check if values are the same
109
+ const existingValues = existing.valuesByMode || {};
110
+ const existingLight = existingValues[modes.light.id];
111
+ const existingDark = existingValues[modes.dark.id];
112
+
113
+ const lightSame = valuesEqual(existingLight, lightFigmaValue);
114
+ const darkSame = valuesEqual(existingDark, darkFigmaValue);
115
+
116
+ // Line-height is sent as % string; Figma variable type must be STRING
117
+ const updateScopes = getScopesForVariable(tokenData.token);
118
+ const existingScopes = existing.scopes ?? [];
119
+ const sortedUpdate = [...updateScopes].sort();
120
+ const sortedExisting = [...existingScopes].sort();
121
+ const scopesDiffer =
122
+ sortedUpdate.length !== sortedExisting.length ||
123
+ sortedUpdate.some((s, i) => s !== sortedExisting[i]);
124
+
125
+ const tokenDescription = getTokenDescription(token, resolvedTokens);
126
+ const descriptionSame =
127
+ (existing.description || "") === tokenDescription;
128
+
129
+ if (lightSame && darkSame && !scopesDiffer && descriptionSame) {
130
+ stats.skipped++;
131
+ // Store existing ID for alias resolution (use both paths for lookup)
132
+ tempIdMap.set(tokenPath, existing.id);
133
+ tempIdMap.set(variableName, existing.id);
134
+ continue;
135
+ }
136
+
137
+ // Update variable (scopes and/or values) — always send our scopes so Figma matches code
138
+ const updatePayload = {
139
+ action: "UPDATE",
140
+ id: existing.id,
141
+ name: variableName,
142
+ scopes: updateScopes,
143
+ description: tokenDescription,
144
+ };
145
+ variableChanges.push(updatePayload);
146
+
147
+ if (!lightSame || !darkSame) {
148
+ variableModeValues.push(
149
+ {
150
+ variableId: existing.id,
151
+ modeId: modes.light.id,
152
+ value: lightFigmaValue,
153
+ },
154
+ {
155
+ variableId: existing.id,
156
+ modeId: modes.dark.id,
157
+ value: darkFigmaValue,
158
+ },
159
+ );
160
+ }
161
+
162
+ stats.updated++;
163
+ tempIdMap.set(tokenPath, existing.id);
164
+ tempIdMap.set(variableName, existing.id);
165
+ } else {
166
+ // Create new variable with temporary ID (use a counter to ensure uniqueness)
167
+ const tempIdCounter = tempIdMap.size + variableChanges.length + 1;
168
+ const tempId = `temp_var_${tempIdCounter}_${variableName.replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "_")}`;
169
+ tempIdMap.set(tokenPath, tempId);
170
+ tempIdMap.set(variableName, tempId);
171
+
172
+ // Line-height is sent as % string; Figma variable type must be STRING
173
+ const resolvedType =
174
+ tokenPath.startsWith("font/line-height/") &&
175
+ typeof lightFigmaValue === "string"
176
+ ? "STRING"
177
+ : getFigmaVariableType(tokenType);
178
+ const createScopes = getScopesForVariable(tokenData.token);
179
+ const createPayload = {
180
+ action: "CREATE",
181
+ id: tempId,
182
+ name: variableName,
183
+ variableCollectionId: collectionId,
184
+ resolvedType,
185
+ description: getTokenDescription(token, resolvedTokens),
186
+ };
187
+ createPayload.scopes = createScopes;
188
+ variableChanges.push(createPayload);
189
+
190
+ variableModeValues.push(
191
+ {
192
+ variableId: tempId,
193
+ modeId: modes.light.id,
194
+ value: lightFigmaValue,
195
+ },
196
+ {
197
+ variableId: tempId,
198
+ modeId: modes.dark.id,
199
+ value: darkFigmaValue,
200
+ },
201
+ );
202
+
203
+ stats.created++;
204
+ }
205
+ } catch (error) {
206
+ errors.push(`${tokenPath}: ${error.message}`);
207
+ stats.errors++;
208
+ }
209
+ }
210
+
211
+ // Delete primitive variables that are no longer in token source (or were excluded)
212
+ for (const v of existingVariables) {
213
+ if (v.name?.startsWith("primitive/") && !keptPrimitiveNames.has(v.name)) {
214
+ variableChanges.push({ action: "DELETE", id: v.id });
215
+ stats.deleted++;
216
+ }
217
+ }
218
+
219
+ return {
220
+ variableChanges,
221
+ variableModeValues,
222
+ stats,
223
+ errors,
224
+ tempIdMap,
225
+ };
226
+ }
227
+
228
+ module.exports = {
229
+ buildPrimitiveVariableRequest,
230
+ };