style-dictionary 4.0.0-prerelease.0 → 4.0.0-prerelease.2

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 (55) hide show
  1. package/bin/{style-dictionary → style-dictionary.js} +13 -13
  2. package/examples/advanced/auto-rebuild-watcher/package.json +1 -1
  3. package/examples/advanced/create-react-app/package.json +2 -1
  4. package/examples/advanced/create-react-native-app/package.json +5 -1
  5. package/examples/advanced/s3/upload.js +1 -1
  6. package/examples/advanced/yaml-tokens/sd.config.js +1 -1
  7. package/lib/StyleDictionary.js +451 -0
  8. package/lib/buildFile.js +46 -40
  9. package/lib/cleanDir.js +1 -1
  10. package/lib/cleanDirs.js +2 -2
  11. package/lib/cleanFile.js +1 -1
  12. package/lib/common/actions.js +12 -7
  13. package/lib/common/formatHelpers/createPropertyFormatter.js +116 -59
  14. package/lib/common/formatHelpers/getTypeScriptType.js +8 -7
  15. package/lib/common/formats.js +6 -6
  16. package/lib/common/templates/compose/object.kt.template.js +1 -1
  17. package/lib/common/templates/css/fonts.css.template.js +1 -1
  18. package/lib/common/templates/ios/singleton.m.template.js +10 -10
  19. package/lib/common/templates/scss/map-deep.template.js +3 -3
  20. package/lib/common/transforms.js +11 -1
  21. package/lib/filterTokens.js +76 -0
  22. package/lib/register/preprocessor.js +39 -0
  23. package/lib/register/transform.js +2 -1
  24. package/lib/register/transformGroup.js +1 -3
  25. package/lib/transform/config.js +26 -21
  26. package/lib/transform/object.js +18 -12
  27. package/lib/transform/{property.js → token.js} +1 -2
  28. package/lib/transform/{propertySetup.js → tokenSetup.js} +17 -17
  29. package/lib/utils/combineJSON.js +22 -12
  30. package/lib/utils/convertToBase64.js +3 -3
  31. package/lib/utils/createDictionary.js +10 -14
  32. package/lib/utils/createFormatArgs.js +1 -5
  33. package/lib/utils/deepExtend.js +15 -18
  34. package/lib/utils/deepmerge.js +14 -0
  35. package/lib/utils/{flattenProperties.js → flattenTokens.js} +10 -10
  36. package/lib/utils/preprocess.js +35 -0
  37. package/lib/utils/references/getName.js +2 -2
  38. package/lib/utils/references/getReferences.js +7 -7
  39. package/lib/utils/resolveObject.js +4 -5
  40. package/package.json +33 -20
  41. package/types/Config.d.ts +3 -2
  42. package/types/Dictionary.d.ts +0 -2
  43. package/types/FormatHelpers.d.ts +12 -23
  44. package/{lib/cleanAllPlatforms.js → types/Preprocessor.d.ts} +6 -17
  45. package/types/index.d.ts +23 -2
  46. package/types/index.test-d.ts +7 -0
  47. package/index-node.js +0 -7
  48. package/index.js +0 -84
  49. package/lib/buildAllPlatforms.js +0 -37
  50. package/lib/buildPlatform.js +0 -67
  51. package/lib/cleanPlatform.js +0 -59
  52. package/lib/exportPlatform.js +0 -131
  53. package/lib/extend.js +0 -162
  54. package/lib/filterProperties.js +0 -92
  55. package/lib/utils/es6_.js +0 -151
package/lib/buildFile.js CHANGED
@@ -13,14 +13,14 @@
13
13
 
14
14
  import path from '@bundled-es-modules/path-browserify';
15
15
  import chalk from 'chalk';
16
- import { fs } from '../fs.js';
17
- import filterProperties from './filterProperties.js';
16
+ import { fs } from 'style-dictionary/fs';
17
+ import filterTokens from './filterTokens.js';
18
18
  import GroupMessages from './utils/groupMessages.js';
19
19
 
20
20
  import createFormatArgs from './utils/createFormatArgs.js';
21
21
 
22
22
  /**
23
- * Takes the style property object and a format and returns a
23
+ * Takes the style token object and a format and returns a
24
24
  * string that can be written to a file.
25
25
  * @memberOf StyleDictionary
26
26
  * @param {Object} file
@@ -50,60 +50,58 @@ export default function buildFile(file = {}, platform = {}, dictionary = {}) {
50
50
  const dirname = path.dirname(fullDestination);
51
51
  if (!fs.existsSync(dirname)) fs.mkdirSync(dirname, { recursive: true });
52
52
 
53
- const filteredProperties = filterProperties(dictionary, filter);
53
+ const filteredTokens = filterTokens(dictionary, filter);
54
54
  const filteredDictionary = Object.assign({}, dictionary, {
55
- properties: filteredProperties.properties,
56
- allProperties: filteredProperties.allProperties,
57
- tokens: filteredProperties.properties,
58
- allTokens: filteredProperties.allProperties,
59
- // keep the unfiltered properties object for reference resolution
60
- _properties: dictionary.properties,
55
+ tokens: filteredTokens.tokens,
56
+ allTokens: filteredTokens.allTokens,
57
+ // keep the unfiltered tokens object for reference resolution
58
+ _tokens: dictionary.tokens,
61
59
  });
62
60
 
63
- // if properties object is empty, return without creating a file
61
+ // if tokens object is empty, return without creating a file
64
62
  if (
65
- filteredProperties.hasOwnProperty('properties') &&
66
- Object.keys(filteredProperties.properties).length === 0 &&
67
- filteredProperties.properties.constructor === Object
63
+ filteredTokens.hasOwnProperty('tokens') &&
64
+ Object.keys(filteredTokens.tokens).length === 0 &&
65
+ filteredTokens.tokens.constructor === Object
68
66
  ) {
69
- let warnNoFile = `No properties for ${destination}. File not created.`;
67
+ let warnNoFile = `No tokens for ${destination}. File not created.`;
70
68
  console.log(chalk.rgb(255, 140, 0)(warnNoFile));
71
69
  return null;
72
70
  }
73
71
 
74
- // Check for property name Collisions
72
+ // Check for token name Collisions
75
73
  const nameCollisionObj = {};
76
- filteredProperties.allTokens &&
77
- filteredProperties.allTokens.forEach((propertyData) => {
78
- let propertyName = propertyData.name;
79
- if (!nameCollisionObj[propertyName]) {
80
- nameCollisionObj[propertyName] = [];
74
+ filteredTokens.allTokens &&
75
+ filteredTokens.allTokens.forEach((tokenData) => {
76
+ let tokenName = tokenData.name;
77
+ if (!nameCollisionObj[tokenName]) {
78
+ nameCollisionObj[tokenName] = [];
81
79
  }
82
- nameCollisionObj[propertyName].push(propertyData);
80
+ nameCollisionObj[tokenName].push(tokenData);
83
81
  });
84
82
 
85
83
  const PROPERTY_NAME_COLLISION_WARNINGS =
86
84
  GroupMessages.GROUP.PropertyNameCollisionWarnings + ':' + destination;
87
85
  GroupMessages.clear(PROPERTY_NAME_COLLISION_WARNINGS);
88
- Object.keys(nameCollisionObj).forEach((propertyName) => {
89
- if (nameCollisionObj[propertyName].length > 1) {
90
- let collisions = nameCollisionObj[propertyName]
91
- .map((properties) => {
92
- let propertyPathText = chalk.rgb(255, 69, 0)(properties.path.join('.'));
93
- let valueText = chalk.rgb(255, 140, 0)(properties.value);
94
- return propertyPathText + ' ' + valueText;
86
+ Object.keys(nameCollisionObj).forEach((tokenName) => {
87
+ if (nameCollisionObj[tokenName].length > 1) {
88
+ let collisions = nameCollisionObj[tokenName]
89
+ .map((tokens) => {
90
+ let tokenPathText = chalk.rgb(255, 69, 0)(tokens.path.join('.'));
91
+ let valueText = chalk.rgb(255, 140, 0)(tokens.value);
92
+ return tokenPathText + ' ' + valueText;
95
93
  })
96
94
  .join('\n ');
97
95
  GroupMessages.add(
98
96
  PROPERTY_NAME_COLLISION_WARNINGS,
99
97
  `Output name ${chalk
100
98
  .rgb(255, 69, 0)
101
- .bold(propertyName)} was generated by:\n ${collisions}`,
99
+ .bold(tokenName)} was generated by:\n ${collisions}`,
102
100
  );
103
101
  }
104
102
  });
105
103
 
106
- let propertyNamesCollisionCount = GroupMessages.count(PROPERTY_NAME_COLLISION_WARNINGS);
104
+ let tokenNamesCollisionCount = GroupMessages.count(PROPERTY_NAME_COLLISION_WARNINGS);
107
105
  fs.writeFileSync(
108
106
  fullDestination,
109
107
  format(
@@ -121,12 +119,12 @@ export default function buildFile(file = {}, platform = {}, dictionary = {}) {
121
119
 
122
120
  // don't show name collision warnings for nested type formats
123
121
  // because they are not relevant.
124
- if ((nested || propertyNamesCollisionCount === 0) && filteredReferencesCount === 0) {
122
+ if ((nested || tokenNamesCollisionCount === 0) && filteredReferencesCount === 0) {
125
123
  console.log(chalk.bold.green(`✔︎ ${fullDestination}`));
126
124
  } else {
127
- console.log(`⚠️ ${fullDestination}`);
128
- if (propertyNamesCollisionCount > 0) {
129
- let propertyNamesCollisionWarnings = GroupMessages.fetchMessages(
125
+ const warnHeader = `⚠️ ${fullDestination}`;
126
+ if (tokenNamesCollisionCount > 0) {
127
+ let tokenNamesCollisionWarnings = GroupMessages.fetchMessages(
130
128
  PROPERTY_NAME_COLLISION_WARNINGS,
131
129
  ).join('\n ');
132
130
  let title = `While building ${chalk
@@ -139,13 +137,17 @@ export default function buildFile(file = {}, platform = {}, dictionary = {}) {
139
137
  )(
140
138
  [
141
139
  'This many-to-one issue is usually caused by some combination of:',
142
- '* conflicting or similar paths/names in property definitions',
140
+ '* conflicting or similar paths/names in token definitions',
143
141
  '* platform transforms/transformGroups affecting names, especially when removing specificity',
144
142
  '* overly inclusive file filters',
145
143
  ].join('\n '),
146
144
  );
147
- let warn = `${title}\n ${propertyNamesCollisionWarnings}\n${help}`;
148
- console.log(chalk.rgb(255, 140, 0).bold(warn));
145
+ let warn = `${warnHeader}\n${title}\n ${tokenNamesCollisionWarnings}\n${help}`;
146
+ if (platform?.log === 'error') {
147
+ throw new Error(warn);
148
+ } else {
149
+ console.log(chalk.rgb(255, 140, 0).bold(warn));
150
+ }
149
151
  }
150
152
 
151
153
  if (filteredReferencesCount > 0) {
@@ -162,8 +164,12 @@ export default function buildFile(file = {}, platform = {}, dictionary = {}) {
162
164
  165,
163
165
  0,
164
166
  )(['This is caused when combining a filter and `outputReferences`.'].join('\n '));
165
- let warn = `${title}\n ${filteredReferencesWarnings}\n${help}`;
166
- console.log(chalk.rgb(255, 140, 0).bold(warn));
167
+ let warn = `${warnHeader}\n${title}\n ${filteredReferencesWarnings}\n${help}`;
168
+ if (platform?.log === 'error') {
169
+ throw new Error(warn);
170
+ } else {
171
+ console.log(chalk.rgb(255, 140, 0).bold(warn));
172
+ }
167
173
  }
168
174
  }
169
175
  }
package/lib/cleanDir.js CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  import chalk from 'chalk';
15
15
  import path from '@bundled-es-modules/path-browserify';
16
- import { fs } from '../fs.js';
16
+ import { fs } from 'style-dictionary/fs';
17
17
 
18
18
  /**
19
19
  * Takes the style property object and a format and returns a
package/lib/cleanDirs.js CHANGED
@@ -14,8 +14,8 @@
14
14
  import cleanDir from './cleanDir.js';
15
15
 
16
16
  /**
17
- * Takes a platform config object and a properties
18
- * object and cleans all the files. Properties object
17
+ * Takes a platform config object and a tokens
18
+ * object and cleans all the files. Tokens object
19
19
  * should have been transformed and resolved before this
20
20
  * point.
21
21
  * @memberOf StyleDictionary
package/lib/cleanFile.js CHANGED
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  import chalk from 'chalk';
15
- import { fs } from '../fs.js';
15
+ import { fs } from 'style-dictionary/fs';
16
16
 
17
17
  /**
18
18
  * Takes the style property object and a format and returns a
@@ -11,7 +11,7 @@
11
11
  * and limitations under the License.
12
12
  */
13
13
 
14
- import { fs } from '../../fs.js';
14
+ import { fs } from 'style-dictionary/fs';
15
15
 
16
16
  /**
17
17
  * @namespace Actions
@@ -25,20 +25,25 @@ export default {
25
25
  */
26
26
  'android/copyImages': {
27
27
  do: function (dictionary, config) {
28
- var imagesDir = config.buildPath + 'android/main/res/drawable-';
28
+ const imagesDir = `${config.buildPath}android/main/res/drawable-`;
29
29
  dictionary.allTokens.forEach(function (token) {
30
30
  if (token.attributes.category === 'asset' && token.attributes.type === 'image') {
31
- var name = token.path.slice(2, 4).join('_');
32
- fs.copyFileSync(token.value, imagesDir + token.attributes.state + '/' + name + '.png');
31
+ const name = token.path.slice(2, 4).join('_');
32
+ const dir = `${imagesDir}${token.attributes.state}`;
33
+ const path = `${dir}/${name}.png`;
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ fs.copyFileSync(token.value, path);
33
36
  }
34
37
  });
35
38
  },
36
39
  undo: function (dictionary, config) {
37
- var imagesDir = config.buildPath + 'android/main/res/drawable-';
40
+ const imagesDir = `${config.buildPath}android/main/res/drawable-`;
38
41
  dictionary.allTokens.forEach(function (token) {
39
42
  if (token.attributes.category === 'asset' && token.attributes.type === 'image') {
40
- var name = token.path.slice(2, 4).join('_');
41
- fs.unlinkSync(imagesDir + token.attributes.state + '/' + name + '.png');
43
+ const name = token.path.slice(2, 4).join('_');
44
+ const dir = `${imagesDir}${token.attributes.state}`;
45
+ const path = `${dir}/${name}.png`;
46
+ fs.unlinkSync(path);
42
47
  }
43
48
  });
44
49
  },
@@ -14,11 +14,65 @@
14
14
  const defaultFormatting = {
15
15
  prefix: '',
16
16
  commentStyle: 'long',
17
+ commentPosition: 'inline',
17
18
  indentation: '',
18
19
  separator: ' =',
19
20
  suffix: ';',
20
21
  };
21
22
 
23
+ /**
24
+ * Split a string comment by newlines and
25
+ * convert to multi-line comment if necessary
26
+ * @param {string} to_ret_prop
27
+ * @param {{comment: string; style: 'short' | 'long'; position: 'above' | 'inline'; indentation: string}} options
28
+ * @returns {string}
29
+ */
30
+ function addComment(to_ret_prop, options) {
31
+ const { comment, style, indentation } = options;
32
+ let { position } = options;
33
+
34
+ const commentsByNewLine = comment.split('\n');
35
+ if (commentsByNewLine.length > 1) {
36
+ position = 'above';
37
+ }
38
+
39
+ let processedComment;
40
+ switch (style) {
41
+ case 'short':
42
+ if (position === 'inline') {
43
+ processedComment = `// ${comment}`;
44
+ } else {
45
+ processedComment = commentsByNewLine.reduce(
46
+ (acc, curr) => `${acc}${indentation}// ${curr}\n`,
47
+ '',
48
+ );
49
+ // remove trailing newline
50
+ processedComment = processedComment.replace(/\n$/g, '');
51
+ }
52
+ break;
53
+ case 'long':
54
+ if (commentsByNewLine.length > 1) {
55
+ processedComment = commentsByNewLine.reduce(
56
+ (acc, curr) => `${acc}${indentation} * ${curr}\n`,
57
+ `${indentation}/**\n`,
58
+ );
59
+ processedComment += `${indentation} */`;
60
+ } else {
61
+ processedComment = `${position === 'above' ? indentation : ''}/* ${comment} */`;
62
+ }
63
+ break;
64
+ }
65
+
66
+ if (position === 'above') {
67
+ // put the comment above the prop if it's multi-line or if commentStyle ended with -above
68
+ to_ret_prop = `${processedComment}\n${to_ret_prop}`;
69
+ } else {
70
+ to_ret_prop = `${to_ret_prop} ${processedComment}`;
71
+ }
72
+
73
+ return to_ret_prop;
74
+ }
75
+
22
76
  /**
23
77
  * Creates a function that can be used to format a property. This can be useful
24
78
  * to use as the function on `dictionary.allTokens.map`. The formatting
@@ -57,37 +111,37 @@ export default function createPropertyFormatter({
57
111
  formatting = {},
58
112
  themeable = false,
59
113
  }) {
60
- let { prefix, commentStyle, indentation, separator, suffix } = Object.assign(
61
- {},
62
- defaultFormatting,
63
- formatting,
64
- );
65
-
114
+ const formatDefaults = {};
66
115
  switch (format) {
67
116
  case 'css':
68
- prefix = '--';
69
- indentation = ' ';
70
- separator = ':';
117
+ formatDefaults.prefix = '--';
118
+ formatDefaults.indentation = ' ';
119
+ formatDefaults.separator = ':';
71
120
  break;
72
121
  case 'sass':
73
- prefix = '$';
74
- commentStyle = 'short';
75
- indentation = '';
76
- separator = ':';
122
+ formatDefaults.prefix = '$';
123
+ formatDefaults.commentStyle = 'short';
124
+ formatDefaults.indentation = '';
125
+ formatDefaults.separator = ':';
77
126
  break;
78
127
  case 'less':
79
- prefix = '@';
80
- commentStyle = 'short';
81
- indentation = '';
82
- separator = ':';
128
+ formatDefaults.prefix = '@';
129
+ formatDefaults.commentStyle = 'short';
130
+ formatDefaults.indentation = '';
131
+ formatDefaults.separator = ':';
83
132
  break;
84
133
  case 'stylus':
85
- prefix = '$';
86
- commentStyle = 'short';
87
- indentation = '';
88
- separator = '=';
134
+ formatDefaults.prefix = '$';
135
+ formatDefaults.commentStyle = 'short';
136
+ formatDefaults.indentation = '';
137
+ formatDefaults.separator = '=';
89
138
  break;
90
139
  }
140
+ let { prefix, commentStyle, commentPosition, indentation, separator, suffix } = {
141
+ ...defaultFormatting,
142
+ ...formatDefaults,
143
+ ...formatting,
144
+ };
91
145
 
92
146
  return function (prop) {
93
147
  let to_ret_prop = `${indentation}${prefix}${prop.name}${separator} `;
@@ -108,44 +162,46 @@ export default function createPropertyFormatter({
108
162
  if (outputReferences && dictionary.usesReference(prop.original.value)) {
109
163
  // Formats that use this function expect `value` to be a string
110
164
  // or else you will get '[object Object]' in the output
111
- if (typeof value === 'string') {
112
- const refs = dictionary.getReferences(prop.original.value);
165
+ const refs = dictionary.getReferences(prop.original.value);
113
166
 
114
- // original can either be string value or an object value
115
- const originalIsString = typeof prop.original.value === 'string';
167
+ // original can either be an object value, which requires transitive value transformation in web CSS formats
168
+ // or a different (primitive) type, meaning it can be stringified.
169
+ const originalIsObject =
170
+ typeof prop.original.value === 'object' && prop.original.value !== null;
116
171
 
117
- // Set the value to the original value with refs first, undoing value-changing transitive transforms
118
- if (originalIsString) {
119
- value = prop.original.value;
120
- }
172
+ if (!originalIsObject) {
173
+ // when original is object value, we replace value by matching ref.value and putting a var instead.
174
+ // Due to the original.value being an object, it requires transformation, so undoing the transformation
175
+ // by replacing value with original.value is not possible.
121
176
 
122
- refs.forEach((ref) => {
123
- // value should be a string that contains the resolved reference
124
- // because Style Dictionary resolved this in the resolution step.
125
- // Here we are undoing that by replacing the value with
126
- // the reference's name
127
- if (ref.value && ref.name) {
128
- const replaceFunc = function () {
129
- if (format === 'css') {
130
- if (outputReferenceFallbacks) {
131
- return `var(${prefix}${ref.name}, ${ref.value})`;
132
- } else {
133
- return `var(${prefix}${ref.name})`;
134
- }
177
+ // when original is string value, we replace value by matching original.value and putting a var instead
178
+ // this is more friendly to transitive transforms that transform the string values
179
+ value = prop.original.value;
180
+ }
181
+
182
+ refs.forEach((ref) => {
183
+ // value should be a string that contains the resolved reference
184
+ // because Style Dictionary resolved this in the resolution step.
185
+ // Here we are undoing that by replacing the value with
186
+ // the reference's name
187
+ if (Object.hasOwn(ref, 'value') && Object.hasOwn(ref, 'name')) {
188
+ const replaceFunc = function () {
189
+ if (format === 'css') {
190
+ if (outputReferenceFallbacks) {
191
+ return `var(${prefix}${ref.name}, ${ref.value})`;
135
192
  } else {
136
- return `${prefix}${ref.name}`;
193
+ return `var(${prefix}${ref.name})`;
137
194
  }
138
- };
139
- // when original is object value, we replace value by matching ref.value and putting a var instead
140
- // when original is string value, we replace value by matching original.value and putting a var instead
141
- // this is more friendly to transitive transforms that transform the string values
142
- value = value.replace(
143
- originalIsString ? new RegExp(`{${ref.path.join('.')}(.value)?}`, 'g') : ref.value,
144
- replaceFunc,
145
- );
146
- }
147
- });
148
- }
195
+ } else {
196
+ return `${prefix}${ref.name}`;
197
+ }
198
+ };
199
+ value = value.replace(
200
+ originalIsObject ? ref.value : new RegExp(`{${ref.path.join('.')}(.value)?}`, 'g'),
201
+ replaceFunc,
202
+ );
203
+ }
204
+ });
149
205
  }
150
206
 
151
207
  to_ret_prop += prop.attributes.category === 'asset' ? `"${value}"` : value;
@@ -158,11 +214,12 @@ export default function createPropertyFormatter({
158
214
  to_ret_prop += suffix;
159
215
 
160
216
  if (prop.comment && commentStyle !== 'none') {
161
- if (commentStyle === 'short') {
162
- to_ret_prop = to_ret_prop.concat(` // ${prop.comment}`);
163
- } else {
164
- to_ret_prop = to_ret_prop.concat(` /* ${prop.comment} */`);
165
- }
217
+ to_ret_prop = addComment(to_ret_prop, {
218
+ comment: prop.comment,
219
+ style: commentStyle,
220
+ position: commentPosition,
221
+ indentation,
222
+ });
166
223
  }
167
224
 
168
225
  return to_ret_prop;
@@ -10,7 +10,6 @@
10
10
  * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11
11
  * and limitations under the License.
12
12
  */
13
- import { unique } from '../../utils/es6_.js';
14
13
 
15
14
  /**
16
15
  * Given some value, returns a basic valid TypeScript type for that value.
@@ -22,7 +21,7 @@ import { unique } from '../../utils/es6_.js';
22
21
  * StyleDictionary.registerFormat({
23
22
  * name: 'myCustomFormat',
24
23
  * formatter: function({ dictionary, options }) {
25
- * return dictionary.allProperties.map(function(prop) {
24
+ * return dictionary.allTokens.map(function(prop) {
26
25
  * var to_ret_prop = 'export const ' + prop.name + ' : ' + getTypeScriptType(prop.value) + ';';
27
26
  * if (prop.comment)
28
27
  * to_ret_prop = to_ret_prop.concat(' // ' + prop.comment);
@@ -72,11 +71,13 @@ function getArrayType(passedArray) {
72
71
  if (passedArray.every((v) => getTypeScriptType(v) === firstValueType)) {
73
72
  return firstValueType + '[]';
74
73
  } else {
75
- return `(${unique(
76
- passedArray.map((item, index) => {
77
- const isLast = passedArray.length === index + 1;
78
- return `${getTypeScriptType(item)}${!isLast ? ' | ' : ''}`;
79
- }),
74
+ return `(${Array.from(
75
+ new Set(
76
+ passedArray.map((item, index) => {
77
+ const isLast = passedArray.length === index + 1;
78
+ return `${getTypeScriptType(item)}${!isLast ? ' | ' : ''}`;
79
+ }),
80
+ ),
80
81
  ).join('')})[]`;
81
82
  }
82
83
  }
@@ -468,7 +468,7 @@ const formats = {
468
468
  'typescript/es6-declarations': function ({ dictionary, file, options }) {
469
469
  return (
470
470
  fileHeader({ file }) +
471
- dictionary.allProperties
471
+ dictionary.allTokens
472
472
  .map(function (prop) {
473
473
  let to_ret_prop = '';
474
474
  if (prop.comment) to_ret_prop += '/** ' + prop.comment + ' */\n';
@@ -529,7 +529,7 @@ const formats = {
529
529
  * formatter: function({ dictionary }) {
530
530
  * return 'declare const root: RootObject\n' +
531
531
  * 'export default root\n' +
532
- * JsonToTS(dictionary.properties).join('\n');
532
+ * JsonToTS(dictionary.tokens).join('\n');
533
533
  * },
534
534
  * });
535
535
  * ```
@@ -788,7 +788,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul
788
788
  */
789
789
  'compose/object': function ({ dictionary, options, file }) {
790
790
  const template = _template(composeObject);
791
- let allProperties;
791
+ let allTokens;
792
792
  const { outputReferences } = options;
793
793
  const formatProperty = createPropertyFormatter({
794
794
  outputReferences,
@@ -800,14 +800,14 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul
800
800
  });
801
801
 
802
802
  if (outputReferences) {
803
- allProperties = [...dictionary.allProperties].sort(sortByReference(dictionary));
803
+ allTokens = [...dictionary.allTokens].sort(sortByReference(dictionary));
804
804
  } else {
805
- allProperties = [...dictionary.allProperties].sort(sortByName);
805
+ allTokens = [...dictionary.allTokens].sort(sortByName);
806
806
  }
807
807
 
808
808
  options = setComposeObjectProperties(options);
809
809
 
810
- return template({ allProperties, file, options, formatProperty, fileHeader });
810
+ return template({ allTokens, file, options, formatProperty, fileHeader });
811
811
  },
812
812
 
813
813
  // iOS templates
@@ -23,7 +23,7 @@ package <%= file.packageName %>;
23
23
  %>
24
24
 
25
25
  object <%= file.className %> {
26
- <%= allProperties.map(function(prop) {
26
+ <%= allTokens.map(function(prop) {
27
27
  let comment = "";
28
28
  if (prop.comment) {
29
29
  comment = \`/** \${prop.comment} */\\n \`
@@ -1,4 +1,4 @@
1
- export default `<% Object.values(properties && properties.asset && properties.asset.font || {}).forEach(function(font) {
1
+ export default `<% Object.values(tokens && tokens.asset && tokens.asset.font || {}).forEach(function(font) {
2
2
  var fileFormatArr = [];
3
3
  if (font.eot) {
4
4
  fileFormatArr.push("url('../" + font.eot.value + "');\\n\\tsrc: url('../" + font.eot.value + "?#iefix') format('embedded-opentype')");
@@ -34,7 +34,7 @@ export default `<%
34
34
  static dispatch_once_t onceToken;
35
35
 
36
36
  dispatch_once(&onceToken, ^{
37
- dictionary = <%= buildDictionary(dictionary.properties) %>;
37
+ dictionary = <%= buildDictionary(dictionary.tokens) %>;
38
38
  });
39
39
 
40
40
  return dictionary;
@@ -42,25 +42,25 @@ export default `<%
42
42
 
43
43
  @end
44
44
 
45
- <% function buildDictionary(props, indent) {
45
+ <% function buildDictionary(tokens, indent) {
46
46
  indent = indent || ' ';
47
47
  var to_ret = '@{\\n';
48
- if (props.hasOwnProperty('value')) {
49
- var value = props.attributes.category === 'size' || props.attributes.category === 'time' ? '@' + props.value : props.value;
48
+ if (tokens.hasOwnProperty('value')) {
49
+ var value = tokens.attributes.category === 'size' || tokens.attributes.category === 'time' ? '@' + tokens.value : tokens.value;
50
50
  to_ret += indent + '@"value": ' + value + ',\\n';
51
- to_ret += indent + '@"name": @"' + props.name + '",\\n';
51
+ to_ret += indent + '@"name": @"' + tokens.name + '",\\n';
52
52
 
53
- for(var name in props.attributes) {
54
- if (props.attributes[name]) {
55
- to_ret += indent + '@"' + name + '": @"' + props.attributes[name] + '",\\n';
53
+ for(var name in tokens.attributes) {
54
+ if (tokens.attributes[name]) {
55
+ to_ret += indent + '@"' + name + '": @"' + tokens.attributes[name] + '",\\n';
56
56
  }
57
57
  }
58
58
 
59
59
  // remove last comma
60
60
  return to_ret.slice(0, -2) + '\\n' + indent + '}';
61
61
  } else {
62
- for(var name in props) {
63
- to_ret += indent + '@"' + name + '": ' + buildDictionary(props[name], indent + ' ') + ',\\n';
62
+ for(var name in tokens) {
63
+ to_ret += indent + '@"' + name + '": ' + buildDictionary(tokens[name], indent + ' ') + ',\\n';
64
64
  }
65
65
  // remove last comma
66
66
  return to_ret.slice(0, -2) + '\\n' + indent + '}';
@@ -17,9 +17,9 @@ export default `<%
17
17
  // output the list of tokens as a Sass nested map
18
18
  // (the values are pointing to the variables)
19
19
  //
20
- print(\`$\${file.mapName||'tokens'}: \${processJsonNode(dictionary.properties, 0)};\\n\`);
20
+ print(\`$\${file.mapName||'tokens'}: \${processJsonNode(dictionary.tokens, 0)};\\n\`);
21
21
 
22
- // recursive function to process a properties JSON node
22
+ // recursive function to process a tokens JSON node
23
23
  //
24
24
  function processJsonNode(obj, depth) {
25
25
  var output = '';
@@ -31,7 +31,7 @@ export default `<%
31
31
  // if we have found a leaf (a property with a value) append the value
32
32
  output += \`$\${obj.name}\`;
33
33
  } else {
34
- // if we have found a group of properties, use the Sass group "(...)" syntax and loop -recursively- on the children
34
+ // if we have found a group of tokens, use the Sass group "(...)" syntax and loop -recursively- on the children
35
35
  output += '(\\n'
36
36
  output += Object.keys(obj).map(function(newKey) {
37
37
  var newProp = obj[newKey];
@@ -13,9 +13,16 @@
13
13
 
14
14
  import Color from 'tinycolor2';
15
15
  import path from '@bundled-es-modules/path-browserify';
16
- import { kebabCase, camelCase, snakeCase, upperFirst } from '../utils/es6_.js';
16
+ import {
17
+ camelCaseTransformMerge,
18
+ snakeCase,
19
+ paramCase as kebabCase,
20
+ camelCase as _camelCase,
21
+ } from 'change-case';
17
22
  import convertToBase64 from '../utils/convertToBase64.js';
18
23
 
24
+ const camelCase = (str) => _camelCase(str, { transform: camelCaseTransformMerge });
25
+
19
26
  const UNICODE_PATTERN = /&#x([^;]+);/g;
20
27
 
21
28
  function isColor(token) {
@@ -281,6 +288,9 @@ export default {
281
288
  'name/cti/pascal': {
282
289
  type: 'name',
283
290
  transformer: function (token, options) {
291
+ const upperFirst = function (str) {
292
+ return str ? str[0].toUpperCase() + str.substr(1) : '';
293
+ };
284
294
  return upperFirst(camelCase([options.prefix].concat(token.path).join(' ')));
285
295
  },
286
296
  },