@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.
- package/CHANGELOG.md +52 -2
- package/README.md +332 -0
- package/build/web/core/component-variables.scss +1088 -131
- package/build/web/core/component.d.ts +558 -0
- package/build/web/core/component.js +6685 -249
- package/build/web/core/component.scss +557 -69
- package/build/web/core/css-utils/a2-border.css +23 -51
- package/build/web/core/css-utils/a2-color.css +221 -233
- package/build/web/core/css-utils/a2-font.css +1 -29
- package/build/web/core/css-utils/a2-spacing.css +238 -483
- package/build/web/core/css-utils/a2-utils.css +496 -781
- package/build/web/core/css-utils/border.css +23 -51
- package/build/web/core/css-utils/color.css +221 -233
- package/build/web/core/css-utils/font.css +1 -29
- package/build/web/core/css-utils/spacing.css +238 -483
- package/build/web/core/css-utils/utils.css +496 -781
- package/build/web/core/index.d.ts +6 -0
- package/build/web/core/index.js +1 -1
- package/build/web/core/primitive-variables.scss +148 -65
- package/build/web/core/primitive.d.ts +209 -0
- package/build/web/core/primitive.js +779 -61
- package/build/web/core/primitive.scss +207 -124
- package/build/web/core/semantic-variables.scss +363 -245
- package/build/web/core/semantic.d.ts +221 -0
- package/build/web/core/semantic.js +1592 -347
- package/build/web/core/semantic.scss +219 -140
- package/build/web/index.d.ts +3 -4
- package/build/web/types.d.ts +17 -0
- package/config.js +121 -496
- package/eslint.config.mjs +11 -1
- package/package.json +15 -5
- package/src/global/primitive/breakpoint.tokens.json +54 -0
- package/src/global/primitive/color.tokens.json +1092 -0
- package/src/global/primitive/duration.tokens.json +44 -0
- package/src/global/primitive/font.tokens.json +151 -0
- package/src/global/primitive/radius.tokens.json +94 -0
- package/src/global/primitive/size.tokens.json +174 -0
- package/src/global/primitive/transition.tokens.json +32 -0
- package/src/theme/core/background.tokens.json +1312 -0
- package/src/theme/core/border.tokens.json +192 -0
- package/src/theme/core/chart.tokens.json +982 -0
- package/src/theme/core/component/ai-mark.tokens.json +20 -0
- package/src/theme/core/component/alert.tokens.json +261 -0
- package/src/theme/core/component/announcement.tokens.json +460 -0
- package/src/theme/core/component/avatar.tokens.json +137 -0
- package/src/theme/core/component/badge.tokens.json +42 -0
- package/src/theme/core/component/breadcrumb.tokens.json +42 -0
- package/src/theme/core/component/button-toggle.tokens.json +428 -0
- package/src/theme/core/component/button.tokens.json +941 -0
- package/src/theme/core/component/calendar.tokens.json +391 -0
- package/src/theme/core/component/card.tokens.json +107 -0
- package/src/theme/core/component/checkbox.tokens.json +631 -0
- package/src/theme/core/component/chip.tokens.json +169 -0
- package/src/theme/core/component/combobox.tokens.json +269 -0
- package/src/theme/core/component/details.tokens.json +152 -0
- package/src/theme/core/component/dialog.tokens.json +87 -0
- package/src/theme/core/component/divider.tokens.json +23 -0
- package/src/theme/core/component/dnd.tokens.json +208 -0
- package/src/theme/core/component/drawer.tokens.json +61 -0
- package/src/theme/core/component/drilldown.tokens.json +61 -0
- package/src/theme/core/component/edit-card.tokens.json +381 -0
- package/src/theme/core/component/field-label.tokens.json +42 -0
- package/src/theme/core/component/field-message.tokens.json +65 -0
- package/src/theme/core/component/icon.tokens.json +42 -0
- package/src/theme/core/component/link.tokens.json +108 -0
- package/src/theme/core/component/list-view.tokens.json +82 -0
- package/src/theme/core/component/listbox.tokens.json +283 -0
- package/src/theme/core/component/menu.tokens.json +230 -0
- package/src/theme/core/component/overflow.tokens.json +84 -0
- package/src/theme/core/component/page.tokens.json +377 -0
- package/src/theme/core/component/pagination.tokens.json +63 -0
- package/src/theme/core/component/popover.tokens.json +122 -0
- package/src/theme/core/component/progress-bar.tokens.json +133 -0
- package/src/theme/core/component/radio.tokens.json +631 -0
- package/src/theme/core/component/segmented-control.tokens.json +175 -0
- package/src/theme/core/component/select-card.tokens.json +943 -0
- package/src/theme/core/component/side-nav.tokens.json +349 -0
- package/src/theme/core/component/skeleton.tokens.json +42 -0
- package/src/theme/core/component/spinner.tokens.json +96 -0
- package/src/theme/core/component/status-icon.tokens.json +164 -0
- package/src/theme/core/component/stepper.tokens.json +484 -0
- package/src/theme/core/component/switch.tokens.json +285 -0
- package/src/theme/core/component/tab.tokens.json +192 -0
- package/src/theme/core/component/text-field.tokens.json +160 -0
- package/src/theme/core/component/text.tokens.json +59 -0
- package/src/theme/core/component/toast.tokens.json +343 -0
- package/src/theme/core/component/toolbar.tokens.json +114 -0
- package/src/theme/core/component/tooltip.tokens.json +61 -0
- package/src/theme/core/focus.tokens.json +56 -0
- package/src/theme/core/foreground.tokens.json +416 -0
- package/src/theme/core/gradient.tokens.json +41 -0
- package/src/theme/core/opacity.tokens.json +25 -0
- package/src/theme/core/shadow.tokens.json +81 -0
- package/src/theme/core/status.tokens.json +74 -0
- package/src/theme/core/typography.tokens.json +163 -0
- package/src/utils/__tests__/css-utils-format-utils.test.js +312 -0
- package/src/utils/__tests__/sd-build-configs.test.js +306 -0
- package/src/utils/__tests__/sd-formats.test.js +942 -0
- package/src/utils/__tests__/sd-transforms.test.js +336 -0
- package/src/utils/__tests__/token-helpers.test.js +1160 -0
- package/src/utils/copy-css-utils-cli.js +13 -1
- package/src/utils/css-utils-format-utils.js +105 -176
- package/src/utils/figma/__tests__/sync-gradient.test.js +561 -0
- package/src/utils/figma/__tests__/token-conversion.test.js +117 -0
- package/src/utils/figma/__tests__/token-resolution.test.js +231 -0
- package/src/utils/figma/auth.js +355 -0
- package/src/utils/figma/constants.js +22 -0
- package/src/utils/figma/errors.js +80 -0
- package/src/utils/figma/figma-api.js +1069 -0
- package/src/utils/figma/get-token.js +348 -0
- package/src/utils/figma/sync-components.js +909 -0
- package/src/utils/figma/sync-main.js +692 -0
- package/src/utils/figma/sync-orchestration.js +683 -0
- package/src/utils/figma/sync-primitives.js +230 -0
- package/src/utils/figma/sync-semantic.js +1056 -0
- package/src/utils/figma/token-conversion.js +340 -0
- package/src/utils/figma/token-parsing.js +186 -0
- package/src/utils/figma/token-resolution.js +569 -0
- package/src/utils/figma/utils.js +199 -0
- package/src/utils/sd-build-configs.js +305 -0
- package/src/utils/sd-formats.js +948 -0
- package/src/utils/sd-transforms.js +165 -0
- package/src/utils/token-helpers.js +848 -0
- package/tsconfig.json +18 -0
- package/vitest.config.js +17 -0
- package/.turbo/turbo-build.log +0 -37
- package/build/web/core/raw.js +0 -234
- package/src/global/primitive/breakpoint.js +0 -19
- package/src/global/primitive/color.js +0 -231
- package/src/global/primitive/duration.js +0 -16
- package/src/global/primitive/font.js +0 -60
- package/src/global/primitive/radius.js +0 -31
- package/src/global/primitive/size.js +0 -55
- package/src/global/primitive/transition.js +0 -16
- package/src/theme/core/background.js +0 -170
- package/src/theme/core/border.js +0 -103
- package/src/theme/core/charts.js +0 -464
- package/src/theme/core/component/button.js +0 -708
- package/src/theme/core/component/checkbox.js +0 -405
- package/src/theme/core/focus.js +0 -35
- package/src/theme/core/foreground.js +0 -148
- package/src/theme/core/overlay.js +0 -137
- package/src/theme/core/shadow.js +0 -29
- package/src/theme/core/status.js +0 -49
- package/src/theme/core/typography.js +0 -82
- 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
|
+
};
|