style-dictionary 4.0.0-prerelease.1 → 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 (54) 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/getTypeScriptType.js +8 -7
  14. package/lib/common/formats.js +6 -6
  15. package/lib/common/templates/compose/object.kt.template.js +1 -1
  16. package/lib/common/templates/css/fonts.css.template.js +1 -1
  17. package/lib/common/templates/ios/singleton.m.template.js +10 -10
  18. package/lib/common/templates/scss/map-deep.template.js +3 -3
  19. package/lib/common/transforms.js +11 -1
  20. package/lib/filterTokens.js +76 -0
  21. package/lib/register/preprocessor.js +39 -0
  22. package/lib/register/transform.js +2 -1
  23. package/lib/register/transformGroup.js +1 -3
  24. package/lib/transform/config.js +26 -21
  25. package/lib/transform/object.js +18 -12
  26. package/lib/transform/{property.js → token.js} +1 -2
  27. package/lib/transform/{propertySetup.js → tokenSetup.js} +17 -17
  28. package/lib/utils/combineJSON.js +22 -12
  29. package/lib/utils/convertToBase64.js +3 -3
  30. package/lib/utils/createDictionary.js +10 -14
  31. package/lib/utils/createFormatArgs.js +1 -5
  32. package/lib/utils/deepExtend.js +15 -18
  33. package/lib/utils/deepmerge.js +14 -0
  34. package/lib/utils/{flattenProperties.js → flattenTokens.js} +10 -10
  35. package/lib/utils/preprocess.js +35 -0
  36. package/lib/utils/references/getName.js +2 -2
  37. package/lib/utils/references/getReferences.js +7 -7
  38. package/lib/utils/resolveObject.js +4 -5
  39. package/package.json +33 -20
  40. package/types/Config.d.ts +3 -2
  41. package/types/Dictionary.d.ts +0 -2
  42. package/types/FormatHelpers.d.ts +12 -23
  43. package/{lib/cleanAllPlatforms.js → types/Preprocessor.d.ts} +6 -17
  44. package/types/index.d.ts +23 -2
  45. package/types/index.test-d.ts +7 -0
  46. package/index-node.js +0 -7
  47. package/index.js +0 -84
  48. package/lib/buildAllPlatforms.js +0 -37
  49. package/lib/buildPlatform.js +0 -67
  50. package/lib/cleanPlatform.js +0 -59
  51. package/lib/exportPlatform.js +0 -131
  52. package/lib/extend.js +0 -162
  53. package/lib/filterProperties.js +0 -92
  54. 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
  },
@@ -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
  },
@@ -0,0 +1,76 @@
1
+ /*
2
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
5
+ * the License. A copy of the License is located at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11
+ * and limitations under the License.
12
+ */
13
+ import { isPlainObject } from 'is-plain-object';
14
+
15
+ /**
16
+ * Takes a nested object of tokens and filters them using the provided
17
+ * function.
18
+ *
19
+ * @param {Object|undefined|null} tokens
20
+ * @param {Function} filter - A function that receives a property object and
21
+ * returns `true` if the property should be included in the output or `false`
22
+ * if the property should be excluded from the output.
23
+ * @returns {Object[]} tokens - A new object containing only the tokens
24
+ * that matched the filter.
25
+ */
26
+ function filterTokenObject(tokens, filter) {
27
+ // Use reduce to generate a new object with the unwanted tokens filtered
28
+ // out
29
+ return Object.entries(tokens ?? []).reduce((acc, [key, value]) => {
30
+ // If the value is not an object, we don't know what it is. We return it as-is.
31
+ if (!isPlainObject(value)) {
32
+ return acc;
33
+ // If the value has a `value` member we know it's a property, pass it to
34
+ // the filter function and either include it in the final `acc` object or
35
+ // exclude it (by returning the `acc` object without it added).
36
+ } else if (typeof value.value !== 'undefined') {
37
+ return filter(value) ? { ...acc, [key]: value } : acc;
38
+ // If we got here we have an object that is not a property. We'll assume
39
+ // it's an object containing multiple tokens and recursively filter it
40
+ // using the `filterTokenObject` function.
41
+ } else {
42
+ const filtered = filterTokenObject(value, filter);
43
+ // If the filtered object is not empty then add it to the final `acc`
44
+ // object. If it is empty then every property inside of it was filtered
45
+ // out, then exclude it entirely from the final `acc` object.
46
+ return Object.entries(filtered || {}).length < 1 ? acc : { ...acc, [key]: filtered };
47
+ }
48
+ }, {});
49
+ }
50
+
51
+ /**
52
+ * Takes a dictionary and filters the `allTokens` array and the `tokens`
53
+ * object using a function provided by the user.
54
+ *
55
+ * @param {Object} dictionary
56
+ * @param {Function} filter - A function that receives a token object
57
+ * and returns `true` if the token should be included in the output
58
+ * or `false` if the token should be excluded from the output
59
+ * @returns {Object} dictionary - A new dictionary containing only the
60
+ * tokens that matched the filter (or the original dictionary if no filter
61
+ * function was provided).
62
+ */
63
+ export default function filterTokens(dictionary, filter) {
64
+ if (!filter) {
65
+ return dictionary;
66
+ } else {
67
+ if (typeof filter !== 'function') {
68
+ throw new Error('filter is not a function');
69
+ } else {
70
+ return {
71
+ allTokens: (dictionary.allTokens ?? []).filter(filter),
72
+ tokens: filterTokenObject(dictionary.tokens, filter),
73
+ };
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
5
+ * the License. A copy of the License is located at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11
+ * and limitations under the License.
12
+ */
13
+
14
+ /**
15
+ * @typedef {import('../../types/Preprocessor').Preprocessor} Preprocessor
16
+ * Adds a custom preprocessor to preprocess the parsed dictionary, before transforming individual tokens.
17
+ * @static
18
+ * @memberof module:style-dictionary
19
+ * @param {Object} Preprocessor - Preprocessor object
20
+ * @param {String} Preprocessor.name - Name of the format to be referenced in your config.json
21
+ * @param {function} Preprocessor.preprocessor - Function to preprocess the dictionary. The function should return a plain Javascript object.
22
+ * @returns {module:style-dictionary}
23
+ * @example
24
+ * ```js
25
+ * StyleDictionary.registerPreprocessor((dictionary) => {
26
+ * return dictionary;
27
+ * });
28
+ * ```
29
+ */
30
+ export default function registerPreprocessor(cfg) {
31
+ const errorPrefix = 'Cannot register preprocessor;';
32
+ if (typeof cfg.name !== 'string')
33
+ throw new Error(`${errorPrefix} Preprocessor.name must be a string`);
34
+ if (!(cfg.preprocessor instanceof Function)) {
35
+ throw new Error(`${errorPrefix} Preprocessor.preprocessor must be a function`);
36
+ }
37
+ this.preprocessors[cfg.name] = cfg.preprocessor;
38
+ return this;
39
+ }
@@ -54,12 +54,13 @@ export default function registerTransform(options) {
54
54
  throw new Error('matcher must be a function');
55
55
  if (typeof options.transformer !== 'function') throw new Error('transformer must be a function');
56
56
 
57
- this.transform[options.name] = {
57
+ const transform = {
58
58
  type: options.type,
59
59
  matcher: options.matcher,
60
60
  transitive: !!options.transitive,
61
61
  transformer: options.transformer,
62
62
  };
63
63
 
64
+ this.transform[options.name] = transform;
64
65
  return this;
65
66
  }
@@ -11,8 +11,6 @@
11
11
  * and limitations under the License.
12
12
  */
13
13
 
14
- import { isArray } from '../utils/es6_.js';
15
-
16
14
  /**
17
15
  * Add a custom transformGroup to the Style Dictionary, which is a
18
16
  * group of transforms.
@@ -36,7 +34,7 @@ import { isArray } from '../utils/es6_.js';
36
34
  */
37
35
  export default function registerTransformGroup(options) {
38
36
  if (typeof options.name !== 'string') throw new Error('transform name must be a string');
39
- if (!isArray(options.transforms))
37
+ if (!Array.isArray(options.transforms))
40
38
  throw new Error('transforms must be an array of registered value transforms');
41
39
 
42
40
  options.transforms.forEach(