@sb1/ffe-core 100.0.0 → 100.0.4

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 (39) hide show
  1. package/README.md +8 -8
  2. package/css/ffe.css +0 -1
  3. package/less/dimensions.less +1 -0
  4. package/less/variables.less +0 -1
  5. package/package.json +6 -4
  6. package/scripts/build-colors/build-colors.cjs +115 -0
  7. package/scripts/build-colors/json/json.cjs +12 -0
  8. package/scripts/build-colors/mobile/android.cjs +99 -0
  9. package/scripts/build-colors/mobile/android.test.js +98 -0
  10. package/scripts/build-colors/mobile/ios.cjs +205 -0
  11. package/scripts/build-colors/web/chevron.cjs +44 -0
  12. package/scripts/build-colors/web/color-names.cjs +24 -0
  13. package/scripts/build-colors/web/css.cjs +78 -0
  14. package/scripts/build-colors/web/js.cjs +44 -0
  15. package/scripts/build-colors/web/tailwind.cjs +36 -0
  16. package/scripts/build-colors/web/tailwind4.cjs +24 -0
  17. package/scripts/build-colors/web/web.cjs +18 -0
  18. package/scripts/build-custom-mq.cjs +45 -0
  19. package/scripts/build.cjs +38 -0
  20. package/scripts/figma-api/color.spec.tsx +126 -0
  21. package/scripts/figma-api/color.ts +52 -0
  22. package/scripts/figma-api/figma_api.ts +41 -0
  23. package/scripts/figma-api/sync_figma_to_tokens.ts +57 -0
  24. package/scripts/figma-api/token_export.spec.tsx +359 -0
  25. package/scripts/figma-api/token_export.ts +99 -0
  26. package/scripts/figma-api/token_import.spec.tsx +1097 -0
  27. package/scripts/figma-api/token_import.ts +430 -0
  28. package/scripts/figma-api/token_types.ts +55 -0
  29. package/scripts/figma-api/utils.ts +11 -0
  30. package/scripts/lib/extractCustomProps.cjs +24 -0
  31. package/scripts/lib/genTypeScript.cjs +7 -0
  32. package/scripts/lib/renderLessVarsToCSSProps.cjs +23 -0
  33. package/scripts/lib/writeToFile.cjs +9 -0
  34. package/scripts/utils.cjs +27 -0
  35. package/tokens/01 Primitive.value.json +4726 -0
  36. package/tokens/02 Semantic.Dark.json +3942 -0
  37. package/tokens/02 Semantic.Light.json +3942 -0
  38. package/tokens/03 Context.Accent.json +2273 -0
  39. package/tokens/03 Context.Default.json +2273 -0
@@ -0,0 +1,359 @@
1
+ import { GetLocalVariablesResponse } from '@figma/rest-api-spec';
2
+ import { tokenFilesFromLocalVariables } from './token_export';
3
+
4
+ describe('tokenFilesFromLocalVariables', () => {
5
+ it('ignores remote variables', () => {
6
+ const localVariablesResponse: GetLocalVariablesResponse = {
7
+ status: 200,
8
+ error: false,
9
+ meta: {
10
+ variableCollections: {
11
+ 'VariableCollectionId:1:1': {
12
+ id: 'VariableCollectionId:1:1',
13
+ name: 'primitives',
14
+ modes: [{ modeId: '1:0', name: 'mode1' }],
15
+ defaultModeId: '1:0',
16
+ remote: true,
17
+ key: 'variableKey',
18
+ hiddenFromPublishing: false,
19
+ variableIds: ['VariableID:2:1'],
20
+ },
21
+ },
22
+ variables: {
23
+ 'VariableID:2:1': {
24
+ id: 'VariableID:2:1',
25
+ name: 'spacing/1',
26
+ key: 'variable_key',
27
+ variableCollectionId: 'VariableCollectionId:1:1',
28
+ resolvedType: 'FLOAT',
29
+ valuesByMode: {
30
+ '1:0': 8,
31
+ },
32
+ remote: true,
33
+ description: '',
34
+ hiddenFromPublishing: false,
35
+ scopes: ['ALL_SCOPES'],
36
+ codeSyntax: {},
37
+ },
38
+ },
39
+ },
40
+ };
41
+
42
+ const tokenFiles = tokenFilesFromLocalVariables(localVariablesResponse);
43
+ expect(tokenFiles).toEqual({});
44
+ });
45
+
46
+ it('returns token files', () => {
47
+ const localVariablesResponse: GetLocalVariablesResponse = {
48
+ status: 200,
49
+ error: false,
50
+ meta: {
51
+ variableCollections: {
52
+ 'VariableCollectionId:1:1': {
53
+ id: 'VariableCollectionId:1:1',
54
+ name: 'primitives',
55
+ modes: [
56
+ { modeId: '1:0', name: 'mode1' },
57
+ { modeId: '1:1', name: 'mode2' },
58
+ ],
59
+ defaultModeId: '1:0',
60
+ remote: false,
61
+ key: 'variableKey',
62
+ hiddenFromPublishing: false,
63
+ variableIds: [
64
+ 'VariableID:2:1',
65
+ 'VariableID:2:2',
66
+ 'VariableID:2:3',
67
+ 'VariableID:2:4',
68
+ ],
69
+ },
70
+ },
71
+ variables: {
72
+ 'VariableID:2:1': {
73
+ id: 'VariableID:2:1',
74
+ name: 'spacing/1',
75
+ key: 'variable_key',
76
+ variableCollectionId: 'VariableCollectionId:1:1',
77
+ resolvedType: 'FLOAT',
78
+ valuesByMode: {
79
+ '1:0': 8,
80
+ '1:1': 8,
81
+ },
82
+ remote: false,
83
+ description: '8px spacing',
84
+ hiddenFromPublishing: true,
85
+ scopes: ['TEXT_CONTENT'],
86
+ codeSyntax: { WEB: 'web', ANDROID: 'android' },
87
+ },
88
+ 'VariableID:2:2': {
89
+ id: 'VariableID:2:2',
90
+ name: 'spacing/2',
91
+ key: 'variable_key2',
92
+ variableCollectionId: 'VariableCollectionId:1:1',
93
+ resolvedType: 'FLOAT',
94
+ valuesByMode: {
95
+ '1:0': 16,
96
+ '1:1': 16,
97
+ },
98
+ remote: false,
99
+ description: '16px spacing',
100
+ hiddenFromPublishing: false,
101
+ scopes: ['ALL_SCOPES'],
102
+ codeSyntax: {},
103
+ },
104
+ 'VariableID:2:3': {
105
+ id: 'VariableID:2:3',
106
+ name: 'color/brand/radish',
107
+ key: 'variable_key3',
108
+ variableCollectionId: 'VariableCollectionId:1:1',
109
+ resolvedType: 'COLOR',
110
+ valuesByMode: {
111
+ '1:0': {
112
+ r: 1,
113
+ g: 0.7450980392156863,
114
+ b: 0.08627450980392157,
115
+ a: 1,
116
+ },
117
+ '1:1': {
118
+ r: 1,
119
+ g: 0.796078431372549,
120
+ b: 0.7176470588235294,
121
+ a: 1,
122
+ },
123
+ },
124
+ remote: false,
125
+ description: 'Radish color',
126
+ hiddenFromPublishing: false,
127
+ scopes: ['ALL_SCOPES'],
128
+ codeSyntax: {},
129
+ },
130
+ 'VariableID:2:4': {
131
+ id: 'VariableID:2:4',
132
+ name: 'color/brand/pear',
133
+ key: 'variable_key4',
134
+ variableCollectionId: 'VariableCollectionId:1:1',
135
+ resolvedType: 'COLOR',
136
+ valuesByMode: {
137
+ '1:0': { r: 1, g: 0, b: 0.08627450980392157, a: 1 },
138
+ '1:1': {
139
+ r: 0.8705882352941177,
140
+ g: 0.9529411764705882,
141
+ b: 0.34509803921568627,
142
+ a: 1,
143
+ },
144
+ },
145
+ remote: false,
146
+ description: 'Pear color',
147
+ hiddenFromPublishing: false,
148
+ scopes: ['ALL_SCOPES'],
149
+ codeSyntax: {},
150
+ },
151
+ },
152
+ },
153
+ };
154
+
155
+ const tokenFiles = tokenFilesFromLocalVariables(localVariablesResponse);
156
+
157
+ expect(tokenFiles['primitives.mode1.json']).toEqual({
158
+ spacing: {
159
+ '1': {
160
+ $type: 'number',
161
+ $value: 8,
162
+ $description: '8px spacing',
163
+ $extensions: {
164
+ 'com.figma': {
165
+ hiddenFromPublishing: true,
166
+ scopes: ['TEXT_CONTENT'],
167
+ codeSyntax: { WEB: 'web', ANDROID: 'android' },
168
+ },
169
+ },
170
+ },
171
+ '2': {
172
+ $type: 'number',
173
+ $value: 16,
174
+ $description: '16px spacing',
175
+ $extensions: {
176
+ 'com.figma': {
177
+ hiddenFromPublishing: false,
178
+ scopes: ['ALL_SCOPES'],
179
+ codeSyntax: {},
180
+ },
181
+ },
182
+ },
183
+ },
184
+ color: {
185
+ brand: {
186
+ radish: {
187
+ $type: 'color',
188
+ $value: '#ffbe16',
189
+ $description: 'Radish color',
190
+ $extensions: {
191
+ 'com.figma': {
192
+ hiddenFromPublishing: false,
193
+ scopes: ['ALL_SCOPES'],
194
+ codeSyntax: {},
195
+ },
196
+ },
197
+ },
198
+ pear: {
199
+ $type: 'color',
200
+ $value: '#ff0016',
201
+ $description: 'Pear color',
202
+ $extensions: {
203
+ 'com.figma': {
204
+ hiddenFromPublishing: false,
205
+ scopes: ['ALL_SCOPES'],
206
+ codeSyntax: {},
207
+ },
208
+ },
209
+ },
210
+ },
211
+ },
212
+ });
213
+
214
+ expect(tokenFiles['primitives.mode2.json']).toEqual({
215
+ spacing: {
216
+ '1': {
217
+ $type: 'number',
218
+ $value: 8,
219
+ $description: '8px spacing',
220
+ $extensions: {
221
+ 'com.figma': {
222
+ hiddenFromPublishing: true,
223
+ scopes: ['TEXT_CONTENT'],
224
+ codeSyntax: { WEB: 'web', ANDROID: 'android' },
225
+ },
226
+ },
227
+ },
228
+ '2': {
229
+ $type: 'number',
230
+ $value: 16,
231
+ $description: '16px spacing',
232
+ $extensions: {
233
+ 'com.figma': {
234
+ hiddenFromPublishing: false,
235
+ scopes: ['ALL_SCOPES'],
236
+ codeSyntax: {},
237
+ },
238
+ },
239
+ },
240
+ },
241
+ color: {
242
+ brand: {
243
+ radish: {
244
+ $type: 'color',
245
+ $value: '#ffcbb7',
246
+ $description: 'Radish color',
247
+ $extensions: {
248
+ 'com.figma': {
249
+ hiddenFromPublishing: false,
250
+ scopes: ['ALL_SCOPES'],
251
+ codeSyntax: {},
252
+ },
253
+ },
254
+ },
255
+ pear: {
256
+ $type: 'color',
257
+ $value: '#def358',
258
+ $description: 'Pear color',
259
+ $extensions: {
260
+ 'com.figma': {
261
+ hiddenFromPublishing: false,
262
+ scopes: ['ALL_SCOPES'],
263
+ codeSyntax: {},
264
+ },
265
+ },
266
+ },
267
+ },
268
+ },
269
+ });
270
+ });
271
+
272
+ it('handles aliases', () => {
273
+ const localVariablesResponse: GetLocalVariablesResponse = {
274
+ status: 200,
275
+ error: false,
276
+ meta: {
277
+ variableCollections: {
278
+ 'VariableCollectionId:1:1': {
279
+ id: 'VariableCollectionId:1:1',
280
+ name: 'collection1',
281
+ modes: [
282
+ { modeId: '1:0', name: 'mode1' },
283
+ { modeId: '1:1', name: 'mode2' },
284
+ ],
285
+ defaultModeId: '1:0',
286
+ remote: false,
287
+ key: 'variableKey',
288
+ hiddenFromPublishing: false,
289
+ variableIds: ['VariableID:2:1', 'VariableID:2:2'],
290
+ },
291
+ },
292
+ variables: {
293
+ 'VariableID:2:1': {
294
+ id: 'VariableID:2:1',
295
+ name: 'var1',
296
+ key: 'variable_key1',
297
+ variableCollectionId: 'VariableCollectionId:1:1',
298
+ resolvedType: 'FLOAT',
299
+ valuesByMode: {
300
+ '1:0': 1,
301
+ },
302
+ remote: false,
303
+ description: 'var1 description',
304
+ hiddenFromPublishing: false,
305
+ scopes: ['ALL_SCOPES'],
306
+ codeSyntax: {},
307
+ },
308
+ 'VariableID:2:2': {
309
+ id: 'VariableID:2:2',
310
+ name: 'var2',
311
+ key: 'variable_key2',
312
+ variableCollectionId: 'VariableCollectionId:1:1',
313
+ resolvedType: 'FLOAT',
314
+ valuesByMode: {
315
+ '1:0': {
316
+ type: 'VARIABLE_ALIAS',
317
+ id: 'VariableID:2:1',
318
+ },
319
+ },
320
+ remote: false,
321
+ description: 'var2 description',
322
+ hiddenFromPublishing: false,
323
+ scopes: ['ALL_SCOPES'],
324
+ codeSyntax: {},
325
+ },
326
+ },
327
+ },
328
+ };
329
+
330
+ const tokenFiles = tokenFilesFromLocalVariables(localVariablesResponse);
331
+
332
+ expect(tokenFiles['collection1.mode1.json']).toEqual({
333
+ var1: {
334
+ $type: 'number',
335
+ $value: 1,
336
+ $description: 'var1 description',
337
+ $extensions: {
338
+ 'com.figma': {
339
+ hiddenFromPublishing: false,
340
+ scopes: ['ALL_SCOPES'],
341
+ codeSyntax: {},
342
+ },
343
+ },
344
+ },
345
+ var2: {
346
+ $type: 'number',
347
+ $value: '{var1}',
348
+ $description: 'var2 description',
349
+ $extensions: {
350
+ 'com.figma': {
351
+ hiddenFromPublishing: false,
352
+ scopes: ['ALL_SCOPES'],
353
+ codeSyntax: {},
354
+ },
355
+ },
356
+ },
357
+ });
358
+ });
359
+ });
@@ -0,0 +1,99 @@
1
+ import { GetLocalVariablesResponse, LocalVariable } from '@figma/rest-api-spec';
2
+ import { rgbToHex } from './color';
3
+ import { Token, TokensFile } from './token_types';
4
+
5
+ function tokenTypeFromVariable(variable: LocalVariable) {
6
+ switch (variable.resolvedType) {
7
+ case 'BOOLEAN':
8
+ return 'boolean';
9
+ case 'COLOR':
10
+ return 'color';
11
+ case 'FLOAT':
12
+ return 'number';
13
+ case 'STRING':
14
+ return 'string';
15
+ }
16
+ }
17
+
18
+ function tokenValueFromVariable(
19
+ variable: LocalVariable,
20
+ modeId: string,
21
+ localVariables: { [id: string]: LocalVariable },
22
+ ) {
23
+ const value = variable.valuesByMode[modeId];
24
+ if (typeof value === 'object') {
25
+ if ('type' in value && value.type === 'VARIABLE_ALIAS') {
26
+ const aliasedVariable = localVariables[value.id];
27
+ return `{${aliasedVariable.name.replace(/\//g, '.')}}`;
28
+ } else if ('r' in value) {
29
+ return rgbToHex(value);
30
+ }
31
+
32
+ throw new Error(`Format of variable value is invalid: ${value}`);
33
+ } else {
34
+ return value;
35
+ }
36
+ }
37
+
38
+ export function tokenFilesFromLocalVariables(
39
+ localVariablesResponse: GetLocalVariablesResponse,
40
+ ) {
41
+ const tokenFiles: { [fileName: string]: TokensFile } = {};
42
+ const localVariableCollections =
43
+ localVariablesResponse.meta.variableCollections;
44
+ const localVariables = localVariablesResponse.meta.variables;
45
+
46
+ Object.values(localVariables).forEach(variable => {
47
+ // Skip remote variables because we only want to generate tokens for local variables
48
+ if (variable.remote) {
49
+ return;
50
+ }
51
+
52
+ // Skip variables that are deleted but still referenced as they are not in use
53
+ if (
54
+ 'deletedButReferenced' in variable &&
55
+ variable.deletedButReferenced
56
+ ) {
57
+ return;
58
+ }
59
+
60
+ const collection =
61
+ localVariableCollections[variable.variableCollectionId];
62
+
63
+ collection.modes.forEach(mode => {
64
+ const fileName = `${collection.name}.${mode.name}.json`;
65
+
66
+ if (!tokenFiles[fileName]) {
67
+ tokenFiles[fileName] = {};
68
+ }
69
+
70
+ let obj: any = tokenFiles[fileName];
71
+
72
+ variable.name.split('/').forEach(groupName => {
73
+ obj[groupName] = obj[groupName] || {};
74
+ obj = obj[groupName];
75
+ });
76
+
77
+ const token: Token = {
78
+ $type: tokenTypeFromVariable(variable),
79
+ $value: tokenValueFromVariable(
80
+ variable,
81
+ mode.modeId,
82
+ localVariables,
83
+ ),
84
+ $description: variable.description,
85
+ $extensions: {
86
+ 'com.figma': {
87
+ hiddenFromPublishing: variable.hiddenFromPublishing,
88
+ scopes: variable.scopes,
89
+ codeSyntax: variable.codeSyntax,
90
+ },
91
+ },
92
+ };
93
+
94
+ Object.assign(obj, token);
95
+ });
96
+ });
97
+
98
+ return tokenFiles;
99
+ }