figma-tokens-flattener 1.0.12 → 1.0.14

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 (2) hide show
  1. package/index.js +68 -161
  2. package/package.json +3 -2
package/index.js CHANGED
@@ -49,11 +49,15 @@ function flattenSeedTokens(seedTokens) {
49
49
 
50
50
  let valueToUse;
51
51
 
52
- if (tokenObj.hasOwnProperty('value')) {
52
+ if (Object.hasOwn(tokenObj, 'value') && typeof tokenObj.value === 'object' && tokenObj.value !== null && Object.hasOwn(tokenObj.value, 'style')) {
53
+ valueToUse = tokenObj.value.style;
54
+ }
55
+
56
+ else if (Object.hasOwn(tokenObj, 'value')) {
53
57
  valueToUse = tokenObj.value;
54
58
  }
55
59
 
56
- else if (tokenObj.hasOwnProperty('style') && tokenObj.style && typeof tokenObj.style === 'object' && tokenObj.style.hasOwnProperty('value')) {
60
+ else if (Object.hasOwn(tokenObj, 'style') && tokenObj.style && typeof tokenObj.style === 'object' && Object.hasOwn(tokenObj.style, 'value')) {
57
61
  valueToUse = tokenObj.style.value;
58
62
  }
59
63
 
@@ -72,6 +76,7 @@ function flattenSeedTokens(seedTokens) {
72
76
 
73
77
  flattened[tokenName] = valueToUse;
74
78
  })
79
+
75
80
  return flattened;
76
81
  }
77
82
 
@@ -190,138 +195,62 @@ function flattenMapTokensWrapper(mapTokens, seedContext) {
190
195
  }
191
196
 
192
197
  /* Alias */
193
- function isDropShadowStructure(obj) {
194
- if (!obj || typeof obj !== 'object') {
195
- return false;
196
- }
197
-
198
- const keys = Object.keys(obj);
199
- if (keys.length === 0) {
200
- return false;
201
- }
202
-
203
- for (const key of keys) {
204
- if (isNaN(key)) {
205
- return false;
206
- }
207
- }
208
-
209
- const firstShadow = obj[keys[0]];
210
- if (!firstShadow || typeof firstShadow !== 'object') {
211
- return false;
212
- }
213
-
214
- const expectedProps = ['x', 'y', 'blur', 'spread', 'color', 'type'];
215
- for (const prop of expectedProps) {
216
- if (!firstShadow.hasOwnProperty(prop)) {
217
- return false;
218
- }
219
- }
220
198
 
221
- for (const coordProp of ['x', 'y', 'blur', 'spread']) {
222
- if (!firstShadow[coordProp] || typeof firstShadow[coordProp] !== 'object' || !firstShadow[coordProp].hasOwnProperty('value')) {
223
- return false;
224
- }
199
+ function processSingleShadow(shadowDef, contextTokens) {
200
+ if (!shadowDef || typeof shadowDef !== 'object') {
201
+ return undefined;
225
202
  }
226
203
 
227
- if (!firstShadow.color || typeof firstShadow.color !== 'object' || !firstShadow.color.hasOwnProperty('value')) {
228
- return false;
204
+ if (typeof shadowDef.x === 'undefined' || typeof shadowDef.y === 'undefined' || typeof shadowDef.color === 'undefined') {
205
+ console.warn('Invalid shadow structure, missing x, y or color:', shadowDef);
206
+ return undefined;
229
207
  }
230
208
 
231
- if (firstShadow.type?.value !== 'dropShadow' && firstShadow.type !== 'dropShadow') {
232
- return false;
233
- }
209
+ const x = flattenMapTokens(shadowDef.x, contextTokens);
210
+ const y = flattenMapTokens(shadowDef.y, contextTokens);
211
+ const blur = flattenMapTokens(shadowDef.blur || '0', contextTokens);
212
+ const spread = flattenMapTokens(shadowDef.spread || '0', contextTokens);
213
+ const color = flattenMapTokens(shadowDef.color, contextTokens);
234
214
 
235
- // If the first shadow has passed the test, we will check the rest.
236
- for (const key of keys) {
237
- const shadow = obj[key];
238
- if (!shadow || typeof shadow !== 'object') {
239
- return false;
240
- }
241
- for (const coordProp of ['x', 'y', 'blur', 'spread']) {
242
- if (!shadow[coordProp] || typeof shadow[coordProp] !== 'object' || !shadow[coordProp].hasOwnProperty('value')) {
243
- return false;
244
- }
245
- }
246
- if (!shadow.color || typeof shadow.color !== 'object' || !shadow.color.hasOwnProperty('value')) {
247
- return false;
248
- }
249
- if (shadow.type?.value !== 'dropShadow' && shadow.type !== 'dropShadow') {
250
- return false;
251
- }
215
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof blur !== 'number' || typeof spread !== 'number' || typeof color !== 'string') {
216
+ console.warn(`Invalid value type in shadow: x=${x}(${typeof x}), y=${y}(${typeof y}), blur=${blur}(${typeof blur}), spread=${spread}(${typeof spread}), color=${color}(${typeof color})`);
217
+ return undefined;
252
218
  }
253
219
 
254
- return true;
220
+ // Forming a line for one shadow
221
+ return `${x}px ${y}px ${blur}px ${spread}px ${color}`;
255
222
  }
256
223
 
257
- /* Checks whether the object is a single dropShadow structure. */
258
- function isSingleDropShadowStructure(obj) {
259
- if (!obj || typeof obj !== 'object') {
260
- return false;
261
- }
262
-
263
- const expectedProps = ['x', 'y', 'blur', 'spread', 'color', 'type'];
264
- for (const prop of expectedProps) {
265
- if (!obj.hasOwnProperty(prop)) {
266
- return false;
267
- }
268
- }
269
-
270
- for (const coordProp of ['x', 'y', 'blur', 'spread']) {
271
- if (!obj[coordProp] || typeof obj[coordProp] !== 'object' || !obj[coordProp].hasOwnProperty('value')) {
272
- return false;
273
- }
274
- }
275
-
276
- if (!obj.color || typeof obj.color !== 'object' || !obj.color.hasOwnProperty('value')) {
277
- return false;
224
+ function processBoxShadow(shadowData, contextTokens) {
225
+ let shadowsArray;
226
+
227
+ if (Array.isArray(shadowData)) {
228
+ // This is an array of shadows
229
+ shadowsArray = shadowData;
230
+ } else if (typeof shadowData === 'object' && shadowData !== null) {
231
+ // This is a single shadow object
232
+ shadowsArray = [shadowData];
233
+ } else {
234
+ console.warn('Expected array or shadow object, received::', shadowData);
235
+ return undefined;
278
236
  }
279
237
 
280
- if (obj.type?.value !== 'dropShadow' && obj.type !== 'dropShadow') {
281
- return false;
282
- }
283
-
284
- return true;
285
- }
286
-
287
- /* Collects the boxShadow string from the dropShadow structure (multiple). */
288
- function buildBoxShadowString(shadowStructure, contextTokens) {
289
- const shadowParts = [];
290
- const keys = Object.keys(shadowStructure).sort((a, b) => parseInt(a) - parseInt(b));
291
-
292
- for (const key of keys) {
293
- const shadowDef = shadowStructure[key];
294
- const shadowString = buildSingleShadowString(shadowDef, contextTokens);
238
+ const processedShadows = [];
239
+ for (const shadowDef of shadowsArray) {
240
+ const shadowString = processSingleShadow(shadowDef, contextTokens);
295
241
  if (shadowString) {
296
- shadowParts.push(shadowString);
242
+ processedShadows.push(shadowString);
297
243
  }
298
244
  }
299
245
 
300
- // Combine all the shadows into one line
301
- return shadowParts.join(', ');
302
- }
303
-
304
- /* Collects a row for one shadow from the dropShadow structure. */
305
- function buildSingleShadowString(singleShadowDef, contextTokens) {
306
- // Extracting and calculating the values
307
- const x = flattenMapTokens(singleShadowDef.x.value, contextTokens);
308
- const y = flattenMapTokens(singleShadowDef.y.value, contextTokens);
309
- const blur = flattenMapTokens(singleShadowDef.blur.value, contextTokens);
310
- const spread = flattenMapTokens(singleShadowDef.spread.value, contextTokens);
311
- const color = flattenMapTokens(singleShadowDef.color.value, contextTokens);
312
-
313
- // Checking if all values are numeric (or color)
314
- if (typeof x !== 'number' || typeof y !== 'number' || typeof blur !== 'number' || typeof spread !== 'number' || typeof color !== 'string') {
315
- console.warn(`Invalid value type in dropShadow: x=${x}(${typeof x}), y=${y}(${typeof y}), blur=${blur}(${typeof blur}), spread=${spread}(${typeof spread}), color=${color}(${typeof color})`);
316
- return undefined; // Returning undefined to skip this shadow
246
+ if (processedShadows.length === 0) {
247
+ return undefined;
317
248
  }
318
249
 
319
- // Forming a row for one shadow
320
- return `${x}px ${y}px ${blur}px ${spread}px ${color}`;
250
+ // Combining all the shadow lines into one boxShadow line
251
+ return processedShadows.join(', ');
321
252
  }
322
253
 
323
-
324
- /* An auxiliary function for converting the alias token structure. */
325
254
  function flattenAliasTokens(aliasTokens, contextTokens) {
326
255
  const flattened = {};
327
256
  if (!aliasTokens || typeof aliasTokens !== 'object') {
@@ -336,36 +265,30 @@ function flattenAliasTokens(aliasTokens, contextTokens) {
336
265
  return;
337
266
  }
338
267
 
339
- if (tokenContent && typeof tokenContent === 'object' && tokenContent.hasOwnProperty('value')) {
340
- const rawValue = tokenContent.value;
341
- const processedValue = flattenMapTokens(rawValue, contextTokens);
342
-
343
- flattened[tokenName] = processedValue;
344
- } else {
345
- // It can be a complex token, for example, a boxShadow (multiple or single)
346
- if (isDropShadowStructure(tokenContent)) {
347
-
348
- // boxShadow structure processing (multiple)
349
- const boxShadowValue = buildBoxShadowString(tokenContent, contextTokens);
350
- flattened[tokenName] = boxShadowValue;
351
-
352
- } else if (isSingleDropShadowStructure(tokenContent)) {
353
- // Processing the structure of a single shadow
354
- const singleShadowValue = buildSingleShadowString(tokenContent, contextTokens);
355
-
356
- if (singleShadowValue) {
357
- flattened[tokenName] = singleShadowValue;
268
+ if (tokenContent && typeof tokenContent === 'object' && Object.hasOwn(tokenContent, 'value')) {
269
+ if (
270
+ (Array.isArray(tokenContent.value) && tokenContent.value.length > 0) ||
271
+ (typeof tokenContent.value === 'object' && tokenContent.value !== null && Object.hasOwn(tokenContent.value, 'x'))
272
+ ) {
273
+ const boxShadowValue = processBoxShadow(tokenContent.value, contextTokens);
274
+ if (boxShadowValue !== undefined) {
275
+ flattened[tokenName] = boxShadowValue;
358
276
  } else {
359
- console.warn(`Couldn't form a line for a single shadow ${tokenName}:`, tokenContent);
277
+ console.warn(`${tokenName}: The boxShadow structure could not be processed.`, tokenContent.value);
360
278
  }
361
- } else {
362
- console.warn(`Unsupported structure for alias token ${tokenName}:`, tokenContent);
363
279
  }
280
+ else {
281
+ const rawValue = tokenContent.value;
282
+ const processedValue = flattenMapTokens(rawValue, contextTokens);
283
+ flattened[tokenName] = processedValue;
284
+ }
285
+ } else {
286
+ console.warn(`Unsupported structure for alias token ${tokenName}:`, tokenContent);
364
287
  }
365
- })
288
+ });
366
289
 
367
290
  return flattened;
368
- };
291
+ }
369
292
 
370
293
  function checkAndResolveVarValues(contextTokens) {
371
294
  const resolved = {};
@@ -405,8 +328,8 @@ function flattenDefaultValueTokens(defaultTokens) {
405
328
 
406
329
  const extractRefKey = (value) => {
407
330
  if (typeof value !== 'string') return null;
408
- const tokenMatch = value.match(/^\s*\{([^}]+)\}\s*$/);
409
- return tokenMatch ? tokenMatch [1].trim() : null;
331
+ const tokenMatch = value.match(/^\s*\{([^}]+)\}\s*$/);
332
+ return tokenMatch ? tokenMatch[1].trim() : null;
410
333
  };
411
334
 
412
335
  // 2) Iterative resolution of "{key}" links
@@ -443,11 +366,11 @@ function flattenDefaultValueTokens(defaultTokens) {
443
366
  }
444
367
 
445
368
  // 3) Returning numeric values only
446
- const numericTokens = {};
369
+ const numericTokens = {};
447
370
  for (const [tokenName, tokenValue] of Object.entries(resolved)) {
448
- if (typeof tokenValue === 'number' && Number.isFinite(tokenValue)) numericTokens [tokenName] = tokenValue;
371
+ if (typeof tokenValue === 'number' && Number.isFinite(tokenValue)) numericTokens[tokenName] = tokenValue;
449
372
  }
450
- return numericTokens ;
373
+ return numericTokens;
451
374
  }
452
375
 
453
376
  /* Components */
@@ -476,23 +399,7 @@ function flattenComponentsTokens(componentsTokens, contextTokens) {
476
399
  const processedValue = flattenMapTokens(rawValue, contextTokens);
477
400
  processedComponentTokens[tokenName] = processedValue;
478
401
  } else {
479
- // It can be a token with a nested structure, for example, lineType.style.value
480
- if (tokenDefinition && typeof tokenDefinition === 'object' && tokenDefinition.style && typeof tokenDefinition.style === 'object' && Object.hasOwn(tokenDefinition.style, 'value')) {
481
- const rawValue = tokenDefinition.style.value;
482
-
483
- const processedValue = flattenMapTokens(rawValue, contextTokens);
484
- processedComponentTokens[tokenName] = processedValue;
485
-
486
- } else {
487
- if (isDropShadowStructure(tokenDefinition)) {
488
- // Processing of the boxShadowSecondary structure (multiple)
489
- const boxShadowValue = buildBoxShadowString(tokenDefinition, contextTokens);
490
- processedComponentTokens[tokenName] = boxShadowValue;
491
-
492
- } else {
493
- console.warn(`Unsupported token structure ${componentName}.${tokenName}:`, tokenDefinition);
494
- }
495
- }
402
+ console.warn(`Unsupported token structure ${componentName}.${tokenName}:`, tokenDefinition);
496
403
  }
497
404
  })
498
405
 
@@ -512,7 +419,7 @@ function flatten() {
512
419
  config = JSON.parse(configContent);
513
420
  } catch (configError) {
514
421
  if (configError.code === 'ENOENT') {
515
- console.log('The configuration file is simple-token-transformer-config.json was not found. We use the path - the root directory.');
422
+ console.log('The configuration file is figma-tokens-flattener-config.json was not found. We use the path - the root directory.');
516
423
  } else {
517
424
  console.error('Error when reading or parsing the configuration file:', configError.message);
518
425
  // Continue with an empty configuration, by default
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "figma-tokens-flattener",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "A tool for transforming Ant Design tokens from Tokens Studio for Figma (Single file) into flat style mappings for light and dark themes.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "figma-tokens-flattener": "index.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "test:local": "node index.js"
11
12
  },
12
13
  "keywords": ["figma", "design-tokens", "tokens", "json", "flattener", "transformer", "parser"],
13
14
  "author": "Yurii Sudarskii",