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
@@ -8,8 +8,8 @@ import path from 'path';
8
8
  import { fileURLToPath } from 'url';
9
9
  import node_fs from 'node:fs';
10
10
  // usually also node:fs in this context, but can be customized by user
11
- import { fs } from '../fs.js';
12
- import StyleDictionary from '../index.js';
11
+ import { fs } from 'style-dictionary/fs';
12
+ import StyleDictionary from 'style-dictionary';
13
13
 
14
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
15
 
@@ -34,7 +34,7 @@ function getConfigPath(options) {
34
34
  }
35
35
  }
36
36
 
37
- return configPath;
37
+ return path.resolve(configPath);
38
38
  }
39
39
 
40
40
  program.version(pkg.version).description(pkg.description).usage('[command] [options]');
@@ -98,14 +98,14 @@ async function styleDictionaryBuild(options) {
98
98
  const configPath = getConfigPath(options);
99
99
 
100
100
  // Create a style dictionary object with the config
101
- const styleDictionary = await StyleDictionary.extend(configPath);
101
+ const styleDictionary = new StyleDictionary(configPath);
102
102
 
103
103
  if (options.platform && options.platform.length > 0) {
104
- options.platform.forEach(function (platform) {
105
- styleDictionary.buildPlatform(platform);
106
- });
104
+ return Promise.all(
105
+ options.platforms.map((platform) => styleDictionary.buildPlatform(platform)),
106
+ );
107
107
  } else {
108
- styleDictionary.buildAllPlatforms();
108
+ return styleDictionary.buildAllPlatforms();
109
109
  }
110
110
  }
111
111
 
@@ -114,14 +114,14 @@ async function styleDictionaryClean(options) {
114
114
  const configPath = getConfigPath(options);
115
115
 
116
116
  // Create a style dictionary object with the config
117
- const styleDictionary = await StyleDictionary.extend(configPath);
117
+ const styleDictionary = new StyleDictionary(configPath);
118
118
 
119
119
  if (options.platform && options.platform.length > 0) {
120
- options.platform.forEach(function (platform) {
121
- styleDictionary.cleanPlatform(platform);
122
- });
120
+ return Promise.all(
121
+ options.platforms.map((platform) => styleDictionary.cleanPlatform(platform)),
122
+ );
123
123
  } else {
124
- styleDictionary.cleanAllPlatforms();
124
+ return styleDictionary.cleanAllPlatforms();
125
125
  }
126
126
  }
127
127
 
@@ -19,4 +19,4 @@
19
19
  "chokidar-cli": "^1.2.0",
20
20
  "style-dictionary": "3.8.0"
21
21
  }
22
- }
22
+ }
@@ -10,6 +10,7 @@
10
10
  "styled-components": "^5.3.0"
11
11
  },
12
12
  "devDependencies": {
13
+ "eslint-config-react-app": "^7.0.1",
13
14
  "style-dictionary": "3.8.0"
14
15
  },
15
16
  "resolutions": {
@@ -35,4 +36,4 @@
35
36
  "not op_mini all"
36
37
  ],
37
38
  "license": "Apache-2.0"
38
- }
39
+ }
@@ -25,6 +25,7 @@
25
25
  "devDependencies": {
26
26
  "@babel/core": "~7.9.0",
27
27
  "babel-jest": "~25.2.6",
28
+ "eslint-config-react-app": "^7.0.1",
28
29
  "jest": "~25.2.6",
29
30
  "react-test-renderer": "~16.13.1",
30
31
  "style-dictionary": "3.8.0"
@@ -32,5 +33,8 @@
32
33
  "jest": {
33
34
  "preset": "react-native"
34
35
  },
36
+ "eslintConfig": {
37
+ "extends": "react-app"
38
+ },
35
39
  "private": true
36
- }
40
+ }
@@ -18,7 +18,7 @@ s3.createBucket({ Bucket: bucketName }, function () {
18
18
  Key: file,
19
19
  Body: fs.readFileSync(file),
20
20
  };
21
- s3.putObject(options, function (err, data) {
21
+ s3.putObject(options, function (err) {
22
22
  if (err) console.log(err);
23
23
  else console.log('Successfully uploaded data to ' + bucketName + '/' + file);
24
24
  });
@@ -11,7 +11,7 @@ module.exports = {
11
11
  // 2 attributes: contents which is a string of the file contents, and
12
12
  // filePath which is the path of the file.
13
13
  // The function is expected to return a plain object.
14
- parse: ({ contents, filePath }) => yaml.parse(contents),
14
+ parse: ({ contents }) => yaml.parse(contents),
15
15
  },
16
16
  ],
17
17
  source: [`tokens/**/*.yaml`],
@@ -0,0 +1,451 @@
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
+ import JSON5 from 'json5';
15
+ import path from '@bundled-es-modules/path-browserify';
16
+ import { fs } from 'style-dictionary/fs';
17
+ import { deepmerge } from './utils/deepmerge.js';
18
+
19
+ import transform from './common/transforms.js';
20
+ import transformGroup from './common/transformGroups.js';
21
+ import format from './common/formats.js';
22
+ import action from './common/actions.js';
23
+ import * as formatHelpers from './common/formatHelpers/index.js';
24
+ import filter from './common/filters.js';
25
+
26
+ import registerTransform from './register/transform.js';
27
+ import registerTransformGroup from './register/transformGroup.js';
28
+ import registerFormat from './register/format.js';
29
+ import registerAction from './register/action.js';
30
+ import registerFilter from './register/filter.js';
31
+ import registerParser from './register/parser.js';
32
+ import registerPreprocessor from './register/preprocessor.js';
33
+ import registerFileHeader from './register/fileHeader.js';
34
+
35
+ import combineJSON from './utils/combineJSON.js';
36
+ import deepExtend from './utils/deepExtend.js';
37
+ import resolveObject from './utils/resolveObject.js';
38
+ import getName from './utils/references/getName.js';
39
+ import createDictionary from './utils/createDictionary.js';
40
+ import GroupMessages from './utils/groupMessages.js';
41
+ import { preprocess } from './utils/preprocess.js';
42
+
43
+ import transformObject from './transform/object.js';
44
+ import transformConfig from './transform/config.js';
45
+ import performActions from './performActions.js';
46
+ import buildFiles from './buildFiles.js';
47
+ import cleanFiles from './cleanFiles.js';
48
+ import cleanDirs from './cleanDirs.js';
49
+ import cleanActions from './cleanActions.js';
50
+
51
+ const PROPERTY_VALUE_COLLISIONS = GroupMessages.GROUP.PropertyValueCollisions;
52
+ const PROPERTY_REFERENCE_WARNINGS = GroupMessages.GROUP.PropertyReferenceWarnings;
53
+
54
+ /**
55
+ * Style Dictionary module
56
+ *
57
+ * @module style-dictionary
58
+ * @typicalname StyleDictionary
59
+ * @example
60
+ * ```js
61
+ * import StyleDictionary from 'style-dictionary';
62
+ * new StyleDictionary.extend('config.json').buildAllPlatforms();
63
+ * ```
64
+ */
65
+
66
+ export default class StyleDictionary {
67
+ // Placeholder is transformed on prepublish -> see scripts/inject-version.js
68
+ // Another option might be import pkg from './package.json' with { "type": "json" } which would work in both browser and node, but support is not there yet.
69
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
70
+ static VERSION = '4.0.0-prerelease.2';
71
+ static formatHelpers = formatHelpers;
72
+
73
+ /**
74
+ * Below is a ton of boilerplate. Explanation:
75
+ *
76
+ * You can register things on the StyleDictionary class level e.g. StyleDictionary.registerFormat()
77
+ * You can also register these things on StyleDictionary instance (through config) or on StyleDictionary instance's platform property.
78
+ *
79
+ * Therefore, we have to make use of static props vs instance props and use getters and setters to merge these together.
80
+ * In the extend() method is where we merge config props into the SD instance.
81
+ * Platform specific options are handled and merged by exportPlatform -> transformConfig
82
+ */
83
+ static transform = transform;
84
+ static transformGroup = transformGroup;
85
+ static format = format;
86
+ static action = action;
87
+ static filter = filter;
88
+ static fileHeader = {};
89
+ static parsers = []; // we need to initialise the array, since we don't have built-in parsers
90
+ static preprocessors = {};
91
+
92
+ static registerTransform(...args) {
93
+ return registerTransform.call(this, ...args);
94
+ }
95
+
96
+ static registerTransformGroup(...args) {
97
+ return registerTransformGroup.call(this, ...args);
98
+ }
99
+
100
+ static registerFormat(...args) {
101
+ return registerFormat.call(this, ...args);
102
+ }
103
+
104
+ static registerAction(...args) {
105
+ return registerAction.call(this, ...args);
106
+ }
107
+
108
+ static registerFilter(...args) {
109
+ return registerFilter.call(this, ...args);
110
+ }
111
+
112
+ static registerParser(...args) {
113
+ return registerParser.call(this, ...args);
114
+ }
115
+
116
+ static registerPreprocessor(...args) {
117
+ return registerPreprocessor.call(this, ...args);
118
+ }
119
+
120
+ static registerFileHeader(...args) {
121
+ return registerFileHeader.call(this, ...args);
122
+ }
123
+
124
+ constructor(config = {}, { init = true } = {}) {
125
+ // dynamically add these instance methods to delegate to class methods for register<Thing>
126
+ [
127
+ 'transform',
128
+ 'transformGroup',
129
+ 'format',
130
+ 'action',
131
+ 'filter',
132
+ 'fileHeader',
133
+ 'parser',
134
+ 'preprocessor',
135
+ ].forEach((prop) => {
136
+ Object.defineProperty(this, `register${prop.charAt(0).toUpperCase() + prop.slice(1)}`, {
137
+ value: (...args) => {
138
+ this.constructor[`register${prop.charAt(0).toUpperCase() + prop.slice(1)}`](...args);
139
+ },
140
+ });
141
+
142
+ // Dynamically add getter/setter pairs so we can act as a gateway, merging
143
+ // the SD class options with SD instance options
144
+ const _prop = ['parser', 'preprocessor'].includes(prop) ? `${prop}s` : prop;
145
+ Object.defineProperty(this, _prop, {
146
+ get: function () {
147
+ if (prop === 'parser') {
148
+ return [...this.constructor[`${_prop}`], ...this[`_${_prop}`]];
149
+ }
150
+ return { ...this.constructor[`${_prop}`], ...this[`_${_prop}`] };
151
+ },
152
+ set: function (v) {
153
+ this[`_${_prop}`] = v;
154
+ },
155
+ });
156
+ });
157
+
158
+ this.config = config;
159
+ this.options = {};
160
+ this.tokens = {};
161
+ this.allTokens = {};
162
+ this.parsers = [];
163
+ this.preprocessors = {};
164
+ this.hasInitialized = new Promise((resolve) => {
165
+ this.hasInitializedResolve = resolve;
166
+ });
167
+
168
+ // By default, always call async extend function when constructing new SD instance
169
+ // However, for testability and managing error handling,
170
+ // you can call constructor with { init: false }
171
+ // and call SDInstance.extend() manually (and catch the error).
172
+ if (init) {
173
+ this.init(config);
174
+ }
175
+ }
176
+
177
+ async init() {
178
+ return this.extend(undefined, true);
179
+ }
180
+
181
+ async extend(config = this.config, mutateOriginal = false) {
182
+ // by default, if extend is called it means extending the current instance
183
+ // with a new instance without mutating the original
184
+ if (!mutateOriginal) {
185
+ const newSD = new StyleDictionary(deepmerge(this.options, config), { init: false });
186
+ return newSD.init();
187
+ }
188
+
189
+ let options;
190
+ let inlineTokens = {};
191
+ let includeTokens = {};
192
+ let sourceTokens = {};
193
+ // Overloaded method, can accept a string as a path that points to a JS or
194
+ // JSON file or a plain object. Potentially refactor.
195
+ if (typeof config === 'string') {
196
+ // get ext name without leading .
197
+ const ext = path.extname(config).replace(/^\./, '');
198
+ if (['json', 'json5', 'jsonc'].includes(ext)) {
199
+ options = JSON5.parse(fs.readFileSync(config, 'utf-8'));
200
+ } else {
201
+ // import path in Node has to be relative to cwd, in browser to root
202
+ const fileToImport = path.resolve(typeof window === 'object' ? '' : process.cwd(), config);
203
+ options = (await import(/* webpackIgnore: true */ fileToImport)).default;
204
+ }
205
+ } else {
206
+ options = config;
207
+ }
208
+
209
+ // SD Config options should be passed to class instance as well
210
+ Object.entries(options).forEach(([key, val]) => {
211
+ this[key] = val;
212
+ });
213
+ this.options = options;
214
+
215
+ // Creating a new object and copying over the options
216
+ // Also keeping an options object in case
217
+
218
+ // grab the inline tokens, ones either defined in the configuration object
219
+ // or that already exist from extending another style dictionary instance
220
+ // with `tokens` keys
221
+ inlineTokens = deepExtend([{}, this.tokens || {}]);
222
+
223
+ // Update tokens with includes from dependencies
224
+ if (this.options.include) {
225
+ if (!Array.isArray(this.options.include)) throw new Error('include must be an array');
226
+
227
+ includeTokens = await combineJSON(this.options.include, true, null, false, this.parsers);
228
+ this.include = null; // We don't want to carry over include references
229
+ }
230
+
231
+ // Update tokens with current package's source
232
+ // These highest precedence
233
+ if (this.options.source) {
234
+ if (!Array.isArray(this.options.source)) throw new Error('source must be an array');
235
+ sourceTokens = await combineJSON(
236
+ this.options.source,
237
+ true,
238
+ function Collision(prop) {
239
+ GroupMessages.add(
240
+ PROPERTY_VALUE_COLLISIONS,
241
+ `Collision detected at: ${prop.path.join('.')}! Original value: ${
242
+ prop.target[prop.key]
243
+ }, New value: ${prop.copy[prop.key]}`,
244
+ );
245
+ },
246
+ true,
247
+ this.parsers,
248
+ );
249
+
250
+ if (GroupMessages.count(PROPERTY_VALUE_COLLISIONS) > 0) {
251
+ const collisions = GroupMessages.flush(PROPERTY_VALUE_COLLISIONS).join('\n');
252
+ const warn = `\n${PROPERTY_VALUE_COLLISIONS}:\n${collisions}\n\n`;
253
+ if (options.log === 'error') {
254
+ throw new Error(warn);
255
+ } else {
256
+ console.log(warn);
257
+ }
258
+ }
259
+
260
+ this.source = null; // We don't want to carry over the source references
261
+ }
262
+
263
+ // Merge inline, include, and source tokens
264
+ const unprocessedTokens = deepExtend([{}, inlineTokens, includeTokens, sourceTokens]);
265
+ this.tokens = await preprocess(unprocessedTokens, this.preprocessors);
266
+ this.hasInitializedResolve();
267
+
268
+ // For chaining
269
+ return this;
270
+ }
271
+
272
+ async exportPlatform(platform) {
273
+ await this.hasInitialized;
274
+
275
+ if (!platform || !this.options.platforms[platform]) {
276
+ throw new Error('Please supply a valid platform');
277
+ }
278
+
279
+ // We don't want to mutate the original object
280
+ const platformConfig = transformConfig(this.options.platforms[platform], this, platform);
281
+
282
+ let exportableResult = this.tokens;
283
+
284
+ // list keeping paths of props with applied value transformations
285
+ const transformedPropRefs = [];
286
+ // list keeping paths of props that had references in it, and therefore
287
+ // could not (yet) have transformed
288
+ const deferredPropValueTransforms = [];
289
+
290
+ const transformationContext = {
291
+ transformedPropRefs,
292
+ deferredPropValueTransforms,
293
+ };
294
+
295
+ let deferredPropCount = 0;
296
+ let finished;
297
+
298
+ while (typeof finished === 'undefined') {
299
+ // We keep up transforming and resolving until all props are resolved
300
+ // and every defined transformation was executed. Remember: transformations
301
+ // can only be executed, if the value to be transformed, has no references
302
+ // in it. So resolving may lead to enable further transformations, and sub
303
+ // sequent resolving may enable even more transformations - and so on.
304
+ // So we keep this loop running until sub sequent transformations are ineffective.
305
+ //
306
+ // Take the following example:
307
+ //
308
+ // color.brand = {
309
+ // value: "{color.base.green}"
310
+ // }
311
+ //
312
+ // color.background.button.primary.base = {
313
+ // value: "{color.brand.value}",
314
+ // color: {
315
+ // desaturate: 0.5
316
+ // }
317
+ // }
318
+ //
319
+ // color.background.button.primary.hover = {
320
+ // value: "{color.background.button.primary.base}",
321
+ // color: {
322
+ // darken: 0.2
323
+ // }
324
+ // }
325
+ //
326
+ // As you can see 'color.background.button.primary.hover' is a variation
327
+ // of 'color.background.button.primary.base' which is a variation of
328
+ // 'color.base.green'. These transitive references are solved by running
329
+ // this loop until all tokens are transformed and resolved.
330
+
331
+ // We need to transform the object before we resolve the
332
+ // variable names because if a value contains concatenated
333
+ // values like "1px solid {color.border.base}" we want to
334
+ // transform the original value (color.border.base) before
335
+ // replacing that value in the string.
336
+ const transformed = transformObject(exportableResult, platformConfig, transformationContext);
337
+
338
+ // referenced values, that have not (yet) been transformed should be excluded from resolving
339
+ const ignorePathsToResolve = deferredPropValueTransforms.map((p) => getName([p, 'value']));
340
+ exportableResult = resolveObject(transformed, {
341
+ ignorePaths: ignorePathsToResolve,
342
+ });
343
+
344
+ const newDeferredPropCount = deferredPropValueTransforms.length;
345
+
346
+ // nothing left to transform -> ready
347
+ if (newDeferredPropCount === 0) {
348
+ finished = true;
349
+ // or deferred count doesn't go down, that means there
350
+ // is a circular reference -> ready (but errored)
351
+ } else if (deferredPropCount === newDeferredPropCount) {
352
+ // if we didn't resolve any deferred references then we have a circular reference
353
+ // the resolveObject method will find the circular references
354
+ // we do this in case there are multiple circular references
355
+ resolveObject(transformed);
356
+ finished = true;
357
+ } else {
358
+ // neither of these things, keep going.
359
+ deferredPropCount = newDeferredPropCount;
360
+ }
361
+ }
362
+
363
+ if (GroupMessages.count(PROPERTY_REFERENCE_WARNINGS) > 0) {
364
+ const warnings = GroupMessages.flush(PROPERTY_REFERENCE_WARNINGS).join('\n');
365
+ throw new Error(
366
+ `\n${PROPERTY_REFERENCE_WARNINGS}:\n${warnings}\n\nProblems were found when trying to resolve property references`,
367
+ );
368
+ }
369
+
370
+ return exportableResult;
371
+ }
372
+
373
+ async buildPlatform(platform) {
374
+ await this.hasInitialized;
375
+
376
+ console.log('\n' + platform);
377
+
378
+ if (!this.options || !this.options?.platforms[platform]) {
379
+ throw new Error(`Platform "${platform}" does not exist`);
380
+ }
381
+
382
+ let tokens;
383
+ // We don't want to mutate the original object
384
+ const platformConfig = transformConfig(this.options.platforms[platform], this, platform);
385
+
386
+ // We need to transform the object before we resolve the
387
+ // variable names because if a value contains concatenated
388
+ // values like "1px solid {color.border.base}" we want to
389
+ // transform the original value (color.border.base) before
390
+ // replacing that value in the string.
391
+ tokens = await this.exportPlatform(platform);
392
+
393
+ // This is the dictionary object we pass to the file
394
+ // building and action methods.
395
+ const dictionary = createDictionary({ tokens });
396
+
397
+ buildFiles(dictionary, platformConfig);
398
+ performActions(dictionary, platformConfig);
399
+
400
+ // For chaining
401
+ return this;
402
+ }
403
+
404
+ async buildAllPlatforms() {
405
+ await this.hasInitialized;
406
+ await Promise.all(Object.keys(this.options.platforms).map((key) => this.buildPlatform(key)));
407
+
408
+ // For chaining
409
+ return this;
410
+ }
411
+
412
+ async cleanPlatform(platform) {
413
+ await this.hasInitialized;
414
+
415
+ console.log('\n' + platform);
416
+
417
+ if (!this.options || !this.options?.platforms[platform]) {
418
+ throw new Error('Platform ' + platform + " doesn't exist");
419
+ }
420
+
421
+ let tokens;
422
+ // We don't want to mutate the original object
423
+ const platformConfig = transformConfig(this.options.platforms[platform], this, platform);
424
+
425
+ // We need to transform the object before we resolve the
426
+ // variable names because if a value contains concatenated
427
+ // values like "1px solid {color.border.base}" we want to
428
+ // transform the original value (color.border.base) before
429
+ // replacing that value in the string.
430
+ tokens = await this.exportPlatform(platform);
431
+
432
+ // This is the dictionary object we pass to the file
433
+ // cleaning and action methods.
434
+ const dictionary = createDictionary({ tokens });
435
+
436
+ // We clean files first, then actions, ...and then directories?
437
+ cleanFiles(dictionary, platformConfig);
438
+ cleanActions(dictionary, platformConfig);
439
+ cleanDirs(dictionary, platformConfig);
440
+
441
+ // For chaining
442
+ return this;
443
+ }
444
+
445
+ async cleanAllPlatforms() {
446
+ await this.hasInitialized;
447
+ await Promise.all(Object.keys(this.options.platforms).map((key) => this.cleanPlatform(key)));
448
+ // For chaining
449
+ return this;
450
+ }
451
+ }