style-dictionary 4.0.1 → 4.1.0

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 (34) hide show
  1. package/README.md +9 -13
  2. package/examples/advanced/assets-base64-embed/package.json +1 -1
  3. package/examples/advanced/create-react-app/package.json +1 -1
  4. package/examples/advanced/create-react-native-app/package.json +1 -1
  5. package/examples/advanced/custom-parser/package.json +1 -1
  6. package/examples/advanced/custom-transforms/package.json +1 -1
  7. package/examples/advanced/font-face-rules/package.json +1 -1
  8. package/examples/advanced/format-helpers/package.json +1 -1
  9. package/examples/advanced/format-helpers/sd.config.js +1 -1
  10. package/examples/advanced/matching-build-files/package.json +1 -1
  11. package/examples/advanced/multi-brand-multi-platform/package.json +1 -1
  12. package/examples/advanced/node-modules-as-config-and-properties/package.json +1 -1
  13. package/examples/advanced/npm-module/package.json +1 -1
  14. package/examples/advanced/referencing_aliasing/package.json +1 -1
  15. package/examples/advanced/s3/package.json +1 -1
  16. package/examples/advanced/tokens-deprecation/package.json +1 -1
  17. package/examples/advanced/transitive-transforms/package.json +1 -1
  18. package/examples/advanced/variables-in-outputs/package.json +1 -1
  19. package/examples/advanced/yaml-tokens/package.json +1 -1
  20. package/lib/StyleDictionary.d.ts +78 -16
  21. package/lib/StyleDictionary.js +96 -43
  22. package/lib/common/formatHelpers/createPropertyFormatter.d.ts +9 -0
  23. package/lib/common/formatHelpers/createPropertyFormatter.js +1 -1
  24. package/lib/common/formatHelpers/fileHeader.js +1 -0
  25. package/lib/common/formats.d.ts +1 -1
  26. package/lib/common/formats.js +5 -3
  27. package/lib/common/templates/scss/map-deep.template.js +12 -6
  28. package/lib/common/templates/scss/map-flat.template.js +22 -10
  29. package/lib/common/transforms.js +1 -0
  30. package/lib/utils/groupMessages.d.ts +1 -1
  31. package/lib/utils/groupMessages.js +1 -1
  32. package/package.json +3 -1
  33. package/types/Config.d.ts +2 -2
  34. package/types/File.d.ts +6 -4
package/README.md CHANGED
@@ -1,12 +1,8 @@
1
1
  <pre>
2
- <a href="https://amzn.github.io/style-dictionary/#/version_3">What's new in Style Dictionary 3.0!</a>
2
+ <a href="https://styledictionary.com/version-4/migration/">What's new in Style Dictionary 4.0!</a>
3
3
  </pre>
4
4
 
5
- <pre>
6
- <a href="https://github.com/amzn/style-dictionary#version-4">What's coming in Style Dictionary 4.0?</a>
7
- </pre>
8
-
9
- <img src="docs/assets/logo.png" alt="Style Dictionary logo and mascot" title="&quot;Pascal&quot;" width="100" align="right" />
5
+ <img src="docs/src/assets/logo.png" alt="Style Dictionary logo and mascot" title="&quot;Pascal&quot;" width="100" align="right" />
10
6
 
11
7
  [![npm version](https://img.shields.io/npm/v/style-dictionary.svg?style=flat-square)](https://badge.fury.io/js/style-dictionary)
12
8
  ![license](https://img.shields.io/npm/l/style-dictionary.svg?style=flat-square)
@@ -136,7 +132,7 @@ A style dictionary is a collection of design tokens, key/value pairs that descri
136
132
 
137
133
  ### config.json
138
134
 
139
- This tells the style dictionary build system how and what to build. The default file path is `config.json` or `config.js` in the root of the project, but you can name it whatever you want by passing in the `--config` flag to the [CLI](https://amzn.github.io/style-dictionary/#/using_the_cli).
135
+ This tells the style dictionary build system how and what to build. The default file path is `config.json` or `config.js` in the root of the project, but you can name it whatever you want by passing in the `--config` flag to the [CLI](https://styledictionary.com/getting-started/using_the_cli/).
140
136
 
141
137
  ```json
142
138
  {
@@ -176,7 +172,7 @@ This tells the style dictionary build system how and what to build. The default
176
172
  | platform.buildPath | String (optional) | Base path to build the files, must end with a trailing slash. |
177
173
  | platform.files | Array (optional) | Files to be generated for this platform. |
178
174
  | platform.file.destination | String (optional) | Location to build the file, will be appended to the buildPath. |
179
- | platform.file.format | String (optional) | Format used to generate the file. Can be a built-in one or you can create your own. [More on formats](https://amzn.github.io/style-dictionary/#/formats) |
175
+ | platform.file.format | String (optional) | Format used to generate the file. Can be a built-in one or you can create your own. [More on formats](https://styledictionary.com/reference/hooks/formats/) |
180
176
  | platform.file.options | Object (optional) | A set of extra options associated with the file. |
181
177
  | platform.file.options.showFileHeader | Boolean | If the generated file should have a "Do not edit + Timestamp" header (where the format supports it). By default is "true". |
182
178
 
@@ -224,7 +220,7 @@ float const SizeFontBase = 16.00f;
224
220
 
225
221
  The style dictionary framework comes with some example code to get you started. Install the node module globally, create a directory and `cd` into it.
226
222
 
227
- ```
223
+ ```bash
228
224
  $ npm i -g style-dictionary
229
225
  $ mkdir MyStyleDictionary
230
226
  $ cd MyStyleDictionary
@@ -232,13 +228,13 @@ $ cd MyStyleDictionary
232
228
 
233
229
  Now run:
234
230
 
235
- ```
231
+ ```bash
236
232
  $ style-dictionary init basic
237
233
  ```
238
234
 
239
235
  This command will copy over the example files found in [example](examples/) in this repo. Now you have an example project set up. You can make changes to the style dictionary and rebuild the project by running:
240
236
 
241
- ```
237
+ ```bash
242
238
  $ style-dictionary build
243
239
  ```
244
240
 
@@ -313,7 +309,7 @@ StyleDictionary.registerTransform({
313
309
  StyleDictionary.buildAllPlatforms();
314
310
  ```
315
311
 
316
- For more information on creating your own transforms and formats, take a look at our [docs](https://amzn.github.io/style-dictionary/).
312
+ For more information on creating your own transforms and formats, take a look at our [docs](https://styledictionary.com).
317
313
 
318
314
  ## Version 4
319
315
 
@@ -340,7 +336,7 @@ We are very open to feedback and collaboration, feel free to reach out to us in
340
336
 
341
337
  The mascot for Style Dictionary is ["Pascal"](https://github.com/amzn/style-dictionary/issues/97) the chameleon (seen below). You can also find them blending in as the logo throughout the documentation.
342
338
 
343
- <img src="docs/assets/logo.png" alt="Style Dictionary logo and mascot" title="&quot;Pascal&quot;" width="240" />
339
+ <img src="docs/src/assets/logo.png" alt="Style Dictionary logo and mascot" title="&quot;Pascal&quot;" width="240" />
344
340
 
345
341
  ## Contributing
346
342
 
@@ -12,6 +12,6 @@
12
12
  "author": "",
13
13
  "license": "Apache-2.0",
14
14
  "devDependencies": {
15
- "style-dictionary": "^4.0.1"
15
+ "style-dictionary": "^4.1.0"
16
16
  }
17
17
  }
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "devDependencies": {
14
14
  "eslint-config-react-app": "^7.0.1",
15
- "style-dictionary": "^4.0.1"
15
+ "style-dictionary": "^4.1.0"
16
16
  },
17
17
  "resolutions": {
18
18
  "immer": "8.0.1",
@@ -29,7 +29,7 @@
29
29
  "eslint-config-react-app": "^7.0.1",
30
30
  "jest": "~25.2.6",
31
31
  "react-test-renderer": "~16.13.1",
32
- "style-dictionary": "^4.0.1"
32
+ "style-dictionary": "^4.1.0"
33
33
  },
34
34
  "jest": {
35
35
  "preset": "react-native"
@@ -10,6 +10,6 @@
10
10
  "author": "",
11
11
  "license": "Apache-2.0",
12
12
  "devDependencies": {
13
- "style-dictionary": "^4.0.1"
13
+ "style-dictionary": "^4.1.0"
14
14
  }
15
15
  }
@@ -16,6 +16,6 @@
16
16
  "author": "",
17
17
  "license": "Apache-2.0",
18
18
  "devDependencies": {
19
- "style-dictionary": "^4.0.1"
19
+ "style-dictionary": "^4.1.0"
20
20
  }
21
21
  }
@@ -9,6 +9,6 @@
9
9
  },
10
10
  "license": "Apache-2.0",
11
11
  "devDependencies": {
12
- "style-dictionary": "^4.0.1"
12
+ "style-dictionary": "^4.1.0"
13
13
  }
14
14
  }
@@ -10,6 +10,6 @@
10
10
  "author": "",
11
11
  "license": "Apache-2.0",
12
12
  "devDependencies": {
13
- "style-dictionary": "^4.0.1"
13
+ "style-dictionary": "^4.1.0"
14
14
  }
15
15
  }
@@ -3,7 +3,7 @@ import {
3
3
  fileHeader,
4
4
  formattedVariables,
5
5
  sortByReference,
6
- } from 'style-dictionary';
6
+ } from 'style-dictionary/utils';
7
7
 
8
8
  export default {
9
9
  hooks: {
@@ -17,6 +17,6 @@
17
17
  "author": "Kelly Harrop <kn.harrop@gmail.com>",
18
18
  "license": "Apache-2.0",
19
19
  "devDependencies": {
20
- "style-dictionary": "^4.0.1"
20
+ "style-dictionary": "^4.1.0"
21
21
  }
22
22
  }
@@ -16,6 +16,6 @@
16
16
  "author": "",
17
17
  "license": "Apache-2.0",
18
18
  "devDependencies": {
19
- "style-dictionary": "^4.0.1"
19
+ "style-dictionary": "^4.1.0"
20
20
  }
21
21
  }
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "homepage": "https://github.com/dbanksdesign/style-dictionary-node#readme",
22
22
  "devDependencies": {
23
- "style-dictionary": "^4.0.1",
23
+ "style-dictionary": "^4.1.0",
24
24
  "tinycolor2": "^1.4.1"
25
25
  }
26
26
  }
@@ -17,6 +17,6 @@
17
17
  "author": "",
18
18
  "license": "Apache-2.0",
19
19
  "devDependencies": {
20
- "style-dictionary": "^4.0.1"
20
+ "style-dictionary": "^4.1.0"
21
21
  }
22
22
  }
@@ -16,6 +16,6 @@
16
16
  "author": "",
17
17
  "license": "Apache-2.0",
18
18
  "devDependencies": {
19
- "style-dictionary": "^4.0.1"
19
+ "style-dictionary": "^4.1.0"
20
20
  }
21
21
  }
@@ -16,6 +16,6 @@
16
16
  "devDependencies": {
17
17
  "aws-sdk": "^2.7.21",
18
18
  "fs-extra": "^1.0.0",
19
- "style-dictionary": "^4.0.1"
19
+ "style-dictionary": "^4.1.0"
20
20
  }
21
21
  }
@@ -16,6 +16,6 @@
16
16
  "author": "",
17
17
  "license": "Apache-2.0",
18
18
  "devDependencies": {
19
- "style-dictionary": "^4.0.1"
19
+ "style-dictionary": "^4.1.0"
20
20
  }
21
21
  }
@@ -12,6 +12,6 @@
12
12
  "license": "ISC",
13
13
  "devDependencies": {
14
14
  "colorjs.io": "^0.5.2",
15
- "style-dictionary": "^4.0.1"
15
+ "style-dictionary": "^4.1.0"
16
16
  }
17
17
  }
@@ -11,6 +11,6 @@
11
11
  "author": "",
12
12
  "license": "MIT",
13
13
  "devDependencies": {
14
- "style-dictionary": "^4.0.1"
14
+ "style-dictionary": "^4.1.0"
15
15
  }
16
16
  }
@@ -10,7 +10,7 @@
10
10
  "author": "",
11
11
  "license": "Apache-2.0",
12
12
  "devDependencies": {
13
- "style-dictionary": "^4.0.1",
13
+ "style-dictionary": "^4.1.0",
14
14
  "yaml": "^1.10.0"
15
15
  }
16
16
  }
@@ -32,10 +32,10 @@ export default class StyleDictionary extends Register {
32
32
  get options(): import("../types/Config.ts").Config;
33
33
  _options: import("../types/Config.ts").Config | undefined;
34
34
  config: string | import("../types/Config.ts").Config;
35
- /** @type {Tokens|TransformedTokens|PreprocessedTokens} */
36
- tokens: Tokens | TransformedTokens | PreprocessedTokens;
37
- /** @type {TransformedToken[]} */
38
- allTokens: TransformedToken[];
35
+ /** @type {PreprocessedTokens} */
36
+ tokens: PreprocessedTokens;
37
+ /** @type {PreprocessedTokens[]} */
38
+ allTokens: PreprocessedTokens[];
39
39
  /** @type {boolean | undefined} */
40
40
  usesDtcg: boolean | undefined;
41
41
  /** @type {LogConfig} */
@@ -64,6 +64,14 @@ export default class StyleDictionary extends Register {
64
64
  unfilteredAllTokens: TransformedToken[];
65
65
  hasInitialized: Promise<any>;
66
66
  hasInitializedResolve: (value: any) => void;
67
+ /**
68
+ * Storing the platform specific transformed tokens so we can prevent re-running exportPlatform when we already know the outcome
69
+ * Same thing for platform specific configs, we don't need to call transformConfig again if we already know the outcome
70
+ */
71
+ /** @type {Record<string,Dictionary>} */
72
+ _dictionaries: Record<string, Dictionary>;
73
+ /** @type {Record<string,PlatformConfig>} */
74
+ _platformConfigs: Record<string, PlatformConfig>;
67
75
  /**
68
76
  * @param {{verbosity?: LogConfig['verbosity'], warnings?: LogConfig['warnings']}} [opts]
69
77
  * @returns
@@ -95,20 +103,47 @@ export default class StyleDictionary extends Register {
95
103
  shouldRunExpansion(expandCfg?: import("../types/Config.ts").ExpandConfig | undefined): boolean;
96
104
  /**
97
105
  * @param {string} platform
106
+ * @param {{ cache?: boolean }} [opts]
107
+ */
108
+ getPlatformConfig(platform: string, opts?: {
109
+ cache?: boolean;
110
+ } | undefined): import("../types/Config.ts").PlatformConfig;
111
+ /**
112
+ * @param {string} platform
113
+ * @param {{ cache?: boolean }} [opts]
114
+ */
115
+ getPlatformTokens(platform: string, opts?: {
116
+ cache?: boolean;
117
+ } | undefined): Promise<import("../types/DesignToken.ts").Dictionary>;
118
+ /**
119
+ * Public wrapper around _exportPlatform, returns only tokens object
120
+ * Here for backwards compatibility.
121
+ * @deprecated use getPlatformTokens instead
122
+ *
123
+ * @param {string} platform
124
+ * @param {{ cache?: boolean }} [opts]
98
125
  * @returns {Promise<TransformedTokens>}
99
126
  */
100
- exportPlatform(platform: string): Promise<TransformedTokens>;
127
+ exportPlatform(platform: string, opts?: {
128
+ cache?: boolean;
129
+ } | undefined): Promise<TransformedTokens>;
130
+ /**
131
+ * @param {string} platform
132
+ * @returns {Promise<Dictionary>}
133
+ */
134
+ _exportPlatform(platform: string): Promise<Dictionary>;
101
135
  /**
102
136
  * This will get the dictionary / platformConfig for specified platform name
103
137
  * Runs transforms, reference resolutions
138
+ * @deprecated use getPlatformConfig / getPlatformTokens instead
104
139
  * @param {string} platform
140
+ * @param {{ cache?: boolean }} [opts]
105
141
  * @returns
106
142
  */
107
- getPlatform(platform: string): Promise<{
108
- dictionary: {
109
- tokens: import("../types/DesignToken.ts").TransformedTokens;
110
- allTokens: import("../types/DesignToken.ts").TransformedToken[];
111
- };
143
+ getPlatform(platform: string, opts?: {
144
+ cache?: boolean;
145
+ } | undefined): Promise<{
146
+ dictionary: import("../types/DesignToken.ts").Dictionary;
112
147
  platformConfig: import("../types/Config.ts").PlatformConfig;
113
148
  }>;
114
149
  /**
@@ -134,24 +169,51 @@ export default class StyleDictionary extends Register {
134
169
  }>;
135
170
  /**
136
171
  * @param {string} platform
172
+ * @param {{ cache?: boolean }} [opts]
137
173
  */
138
- formatPlatform(platform: string): Promise<{
174
+ formatPlatform(platform: string, opts?: {
175
+ cache?: boolean;
176
+ } | undefined): Promise<{
139
177
  output: unknown;
140
178
  destination: string | undefined;
141
179
  }[]>;
142
- formatAllPlatforms(): Promise<{}>;
180
+ /**
181
+ * @param {{ cache?: boolean }} [opts]
182
+ * @returns
183
+ */
184
+ formatAllPlatforms(opts?: {
185
+ cache?: boolean;
186
+ } | undefined): Promise<{}>;
143
187
  /**
144
188
  * @param {string} platform
189
+ * @param {{ cache?: boolean }} [opts]
145
190
  * @returns
146
191
  */
147
- buildPlatform(platform: string): Promise<this>;
148
- buildAllPlatforms(): Promise<this>;
192
+ buildPlatform(platform: string, opts?: {
193
+ cache?: boolean;
194
+ } | undefined): Promise<this>;
195
+ /**
196
+ * @param {{ cache?: boolean }} [opts]
197
+ * @returns
198
+ */
199
+ buildAllPlatforms(opts?: {
200
+ cache?: boolean;
201
+ } | undefined): Promise<this>;
149
202
  /**
150
203
  * @param {string} platform
204
+ * @param {{ cache?: boolean }} [opts]
205
+ * @returns
206
+ */
207
+ cleanPlatform(platform: string, opts?: {
208
+ cache?: boolean;
209
+ } | undefined): Promise<this>;
210
+ /**
211
+ * @param {{ cache?: boolean }} [opts]
151
212
  * @returns
152
213
  */
153
- cleanPlatform(platform: string): Promise<this>;
154
- cleanAllPlatforms(): Promise<this>;
214
+ cleanAllPlatforms(opts?: {
215
+ cache?: boolean;
216
+ } | undefined): Promise<this>;
155
217
  }
156
218
  export type Volume = import("../types/Volume.ts").Volume;
157
219
  export type Config = import("../types/Config.ts").Config;
@@ -78,7 +78,7 @@ export default class StyleDictionary extends Register {
78
78
  // Placeholder is transformed on prepublish -> see scripts/inject-version.js
79
79
  // 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.
80
80
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
81
- static VERSION = '4.0.1';
81
+ static VERSION = '4.1.0';
82
82
 
83
83
  /** @returns {Config} */
84
84
  get options() {
@@ -110,9 +110,9 @@ export default class StyleDictionary extends Register {
110
110
  super();
111
111
  this.config = config;
112
112
  this.options = {};
113
- /** @type {Tokens|TransformedTokens|PreprocessedTokens} */
113
+ /** @type {PreprocessedTokens} */
114
114
  this.tokens = {};
115
- /** @type {TransformedToken[]} */
115
+ /** @type {PreprocessedTokens[]} */
116
116
  this.allTokens = [];
117
117
  /** @type {boolean | undefined} */
118
118
  this.usesDtcg = undefined;
@@ -156,6 +156,15 @@ export default class StyleDictionary extends Register {
156
156
  this.hasInitializedResolve = resolve;
157
157
  });
158
158
 
159
+ /**
160
+ * Storing the platform specific transformed tokens so we can prevent re-running exportPlatform when we already know the outcome
161
+ * Same thing for platform specific configs, we don't need to call transformConfig again if we already know the outcome
162
+ */
163
+ /** @type {Record<string,Dictionary>} */
164
+ this._dictionaries = {};
165
+ /** @type {Record<string,PlatformConfig>} */
166
+ this._platformConfigs = {};
167
+
159
168
  // By default, always call async extend function when constructing new SD instance
160
169
  // However, for testability and managing error handling,
161
170
  // you can call constructor with { init: false }
@@ -352,6 +361,7 @@ export default class StyleDictionary extends Register {
352
361
  preprocessedTokens = expandTokens(preprocessedTokens, this.options);
353
362
  }
354
363
  this.tokens = preprocessedTokens;
364
+ this.allTokens = flattenTokens(/** @type {PreprocessedTokens} */ (this.tokens), this.usesDtcg);
355
365
  this.hasInitializedResolve(null);
356
366
 
357
367
  // For chaining
@@ -380,17 +390,51 @@ export default class StyleDictionary extends Register {
380
390
 
381
391
  /**
382
392
  * @param {string} platform
383
- * @returns {Promise<TransformedTokens>}
393
+ * @param {{ cache?: boolean }} [opts]
384
394
  */
385
- async exportPlatform(platform) {
386
- await this.hasInitialized;
387
-
395
+ getPlatformConfig(platform, opts) {
388
396
  if (!platform || !this.platforms?.[platform]) {
389
- throw new Error('Please supply a valid platform');
397
+ throw new Error(`Please supply a valid platform, "${platform}" does not exist`);
398
+ }
399
+ if (!this._platformConfigs[platform] || opts?.cache === false) {
400
+ this._platformConfigs[platform] = transformConfig(this.platforms[platform], this, platform);
401
+ }
402
+ return this._platformConfigs[platform];
403
+ }
404
+
405
+ /**
406
+ * @param {string} platform
407
+ * @param {{ cache?: boolean }} [opts]
408
+ */
409
+ async getPlatformTokens(platform, opts) {
410
+ if (!this._dictionaries[platform] || opts?.cache === false) {
411
+ const dictionary = await this._exportPlatform(platform);
412
+ this._dictionaries[platform] = dictionary;
390
413
  }
414
+ return this._dictionaries[platform];
415
+ }
416
+
417
+ /**
418
+ * Public wrapper around _exportPlatform, returns only tokens object
419
+ * Here for backwards compatibility.
420
+ * @deprecated use getPlatformTokens instead
421
+ *
422
+ * @param {string} platform
423
+ * @param {{ cache?: boolean }} [opts]
424
+ * @returns {Promise<TransformedTokens>}
425
+ */
426
+ async exportPlatform(platform, opts) {
427
+ const dictionary = await this.getPlatformTokens(platform, opts);
428
+ return dictionary.tokens;
429
+ }
391
430
 
392
- // We don't want to mutate the original object
393
- const platformConfig = transformConfig(this.platforms[platform], this, platform);
431
+ /**
432
+ * @param {string} platform
433
+ * @returns {Promise<Dictionary>}
434
+ */
435
+ async _exportPlatform(platform) {
436
+ await this.hasInitialized;
437
+ const platformConfig = this.getPlatformConfig(platform);
394
438
 
395
439
  let platformProcessedTokens = /** @type {PreprocessedTokens} */ (this.tokens);
396
440
 
@@ -541,35 +585,25 @@ export default class StyleDictionary extends Register {
541
585
  console.log(chalk.rgb(255, 140, 0).bold(err));
542
586
  }
543
587
  }
544
-
545
- return exportableResult;
588
+ return { tokens: exportableResult, allTokens: flattenTokens(exportableResult, this.usesDtcg) };
546
589
  }
547
590
 
548
591
  /**
549
592
  * This will get the dictionary / platformConfig for specified platform name
550
593
  * Runs transforms, reference resolutions
594
+ * @deprecated use getPlatformConfig / getPlatformTokens instead
551
595
  * @param {string} platform
596
+ * @param {{ cache?: boolean }} [opts]
552
597
  * @returns
553
598
  */
554
- async getPlatform(platform) {
599
+ async getPlatform(platform, opts) {
555
600
  await this.hasInitialized;
556
- if (!this.platforms?.[platform]) {
557
- throw new Error(`Platform "${platform}" does not exist`);
558
- }
559
-
560
- // We don't want to mutate the original object
561
- const platformConfig = transformConfig(this.platforms[platform], this, platform);
562
-
563
- // We need to transform the object before we resolve the
564
- // variable names because if a value contains concatenated
565
- // values like "1px solid {color.border.base}" we want to
566
- // transform the original value (color.border.base) before
567
- // replacing that value in the string.
568
- const tokens = await this.exportPlatform(platform);
569
- this.allTokens = /** @type {TransformedToken[]} */ (flattenTokens(tokens, this.usesDtcg));
570
- // This is the dictionary object we pass to the file
571
- // building and action methods.
572
- return { dictionary: { tokens, allTokens: this.allTokens }, platformConfig };
601
+ const platformConfig = this.getPlatformConfig(platform, opts);
602
+ const dictionary = await this.getPlatformTokens(platform, opts);
603
+ return {
604
+ dictionary,
605
+ platformConfig,
606
+ };
573
607
  }
574
608
 
575
609
  /**
@@ -758,10 +792,13 @@ export default class StyleDictionary extends Register {
758
792
 
759
793
  /**
760
794
  * @param {string} platform
795
+ * @param {{ cache?: boolean }} [opts]
761
796
  */
762
- async formatPlatform(platform) {
797
+ async formatPlatform(platform, opts) {
763
798
  await this.hasInitialized;
764
- const { dictionary, platformConfig } = await this.getPlatform(platform);
799
+ const platformConfig = this.getPlatformConfig(platform, opts);
800
+ const dictionary = await this.getPlatformTokens(platform, opts);
801
+
765
802
  if (
766
803
  platformConfig.buildPath &&
767
804
  platformConfig.buildPath.slice(-1) !== '/' &&
@@ -805,7 +842,11 @@ export default class StyleDictionary extends Register {
805
842
  return formattedFiles.map(({ output, destination }) => ({ output, destination }));
806
843
  }
807
844
 
808
- async formatAllPlatforms() {
845
+ /**
846
+ * @param {{ cache?: boolean }} [opts]
847
+ * @returns
848
+ */
849
+ async formatAllPlatforms(opts) {
809
850
  await this.hasInitialized;
810
851
  if (!this.platforms) {
811
852
  throw new Error('Cannot format platforms due to missing property "platforms" on config');
@@ -815,7 +856,7 @@ export default class StyleDictionary extends Register {
815
856
  * @param {string} platformKey
816
857
  */
817
858
  const getOutputsForPlatform = async (platformKey) => {
818
- const outputs = await this.formatPlatform(platformKey);
859
+ const outputs = await this.formatPlatform(platformKey, opts);
819
860
  return { platform: platformKey, outputs };
820
861
  };
821
862
 
@@ -833,11 +874,13 @@ export default class StyleDictionary extends Register {
833
874
 
834
875
  /**
835
876
  * @param {string} platform
877
+ * @param {{ cache?: boolean }} [opts]
836
878
  * @returns
837
879
  */
838
- async buildPlatform(platform) {
880
+ async buildPlatform(platform, opts) {
839
881
  await this.hasInitialized;
840
- const { dictionary, platformConfig } = await this.getPlatform(platform);
882
+ const platformConfig = this.getPlatformConfig(platform, opts);
883
+ const dictionary = await this.getPlatformTokens(platform, opts);
841
884
 
842
885
  /**
843
886
  * @param {string} destination
@@ -853,7 +896,7 @@ export default class StyleDictionary extends Register {
853
896
  return this.volume.promises.writeFile(destination, output);
854
897
  };
855
898
 
856
- const files = await this.formatPlatform(platform);
899
+ const files = await this.formatPlatform(platform, opts);
857
900
  if (files) {
858
901
  await Promise.all(
859
902
  files.map(({ destination, output }) => {
@@ -879,10 +922,14 @@ export default class StyleDictionary extends Register {
879
922
  return this;
880
923
  }
881
924
 
882
- async buildAllPlatforms() {
925
+ /**
926
+ * @param {{ cache?: boolean }} [opts]
927
+ * @returns
928
+ */
929
+ async buildAllPlatforms(opts) {
883
930
  await this.hasInitialized;
884
931
  if (this.platforms) {
885
- await Promise.all(Object.keys(this.platforms).map((key) => this.buildPlatform(key)));
932
+ await Promise.all(Object.keys(this.platforms).map((key) => this.buildPlatform(key, opts)));
886
933
  }
887
934
  // For chaining
888
935
  return this;
@@ -890,10 +937,12 @@ export default class StyleDictionary extends Register {
890
937
 
891
938
  /**
892
939
  * @param {string} platform
940
+ * @param {{ cache?: boolean }} [opts]
893
941
  * @returns
894
942
  */
895
- async cleanPlatform(platform) {
896
- const { dictionary, platformConfig } = await this.getPlatform(platform);
943
+ async cleanPlatform(platform, opts) {
944
+ const platformConfig = this.getPlatformConfig(platform, opts);
945
+ const dictionary = await this.getPlatformTokens(platform, opts);
897
946
  // collect logs, cleanFiles happens in parallel but we want to log in sequence
898
947
  const logs = await cleanFiles(platformConfig, this.volume);
899
948
  if (logs) {
@@ -918,10 +967,14 @@ export default class StyleDictionary extends Register {
918
967
  return this;
919
968
  }
920
969
 
921
- async cleanAllPlatforms() {
970
+ /**
971
+ * @param {{ cache?: boolean }} [opts]
972
+ * @returns
973
+ */
974
+ async cleanAllPlatforms(opts) {
922
975
  await this.hasInitialized;
923
976
  if (this.platforms) {
924
- await Promise.all(Object.keys(this.platforms).map((key) => this.cleanPlatform(key)));
977
+ await Promise.all(Object.keys(this.platforms).map((key) => this.cleanPlatform(key, opts)));
925
978
  }
926
979
  // For chaining
927
980
  return this;
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Split a string comment by newlines and
3
+ * convert to multi-line comment if necessary
4
+ * @param {string} to_ret_token
5
+ * @param {string} comment
6
+ * @param {Formatting} options
7
+ * @returns {string}
8
+ */
9
+ export function addComment(to_ret_token: string, comment: string, options: Formatting): string;
1
10
  /**
2
11
  * Creates a function that can be used to format a token. This can be useful
3
12
  * to use as the function on `dictionary.allTokens.map`. The formatting
@@ -40,7 +40,7 @@ const defaultFormatting = {
40
40
  * @param {Formatting} options
41
41
  * @returns {string}
42
42
  */
43
- function addComment(to_ret_token, comment, options) {
43
+ export function addComment(to_ret_token, comment, options) {
44
44
  const { commentStyle, indentation } = options;
45
45
  let { commentPosition } = options;
46
46
 
@@ -12,6 +12,7 @@
12
12
  */
13
13
 
14
14
  /**
15
+ *
15
16
  * @typedef {import('../../../types/File.ts').File} File
16
17
  * @typedef {import('../../../types/File.ts').FileHeader} FileHeader
17
18
  * @typedef {import('../../../types/File.ts').FormattingOptions} Formatting
@@ -1,7 +1,7 @@
1
1
  export default formats;
2
2
  export type Format = import("../../types/Format.ts").Format;
3
3
  export type FormatArgs = import("../../types/Format.ts").FormatFnArguments;
4
- export type FormattingOptions = import("../../types/File").FormattingOptions;
4
+ export type FormattingOverrides = import("../../types/File").FormattingOverrides;
5
5
  export type OutputReferences = import("../../types/Format.ts").OutputReferences;
6
6
  export type Token = import("../../types/DesignToken.ts").TransformedToken;
7
7
  export type Tokens = import("../../types/DesignToken.ts").TransformedTokens;
@@ -49,7 +49,7 @@ import plistTemplate from './templates/ios/plist.template.js';
49
49
  /**
50
50
  * @typedef {import('../../types/Format.ts').Format} Format
51
51
  * @typedef {import('../../types/Format.ts').FormatFnArguments} FormatArgs
52
- * @typedef {import('../../types/File').FormattingOptions} FormattingOptions
52
+ * @typedef {import('../../types/File').FormattingOverrides} FormattingOverrides
53
53
  * @typedef {import('../../types/Format.ts').OutputReferences} OutputReferences
54
54
  * @typedef {import('../../types/DesignToken.ts').TransformedToken} Token
55
55
  * @typedef {import('../../types/DesignToken.ts').TransformedTokens} Tokens
@@ -60,11 +60,13 @@ import plistTemplate from './templates/ios/plist.template.js';
60
60
  */
61
61
 
62
62
  /**
63
- * remove prefix because the prefix option for createPropertyFormatting is not the same as the prefix inside header comment
64
- * @param {FormattingOptions} [formatting]
63
+ * Remove prefix because the prefix option for createPropertyFormatter
64
+ * is not the same as the prefix inside header comment
65
+ * @param {FormattingOverrides} [formatting]
65
66
  */
66
67
  function getFormattingCloneWithoutPrefix(formatting) {
67
68
  const formattingWithoutPrefix = structuredClone(formatting) ?? {};
69
+ // @ts-expect-error users are not supposed to pass "prefix" but they might because it used to be supported
68
70
  delete formattingWithoutPrefix.prefix;
69
71
  return formattingWithoutPrefix;
70
72
  }
@@ -12,6 +12,7 @@
12
12
  * @param {number} depth
13
13
  */
14
14
  function processJsonNode(obj, options, depth = 0) {
15
+ const indent = options.formatting?.indentation ?? ' ';
15
16
  let output = '';
16
17
  if (obj === null) {
17
18
  output += `''`;
@@ -22,15 +23,18 @@ function processJsonNode(obj, options, depth = 0) {
22
23
  output += `$${obj.name}`;
23
24
  } else {
24
25
  // if we have found a group of tokens, use the Sass group "(...)" syntax and loop -recursively- on the children
25
- output += '(\n';
26
+ output += `(\n`;
26
27
  output += Object.keys(obj)
27
28
  .map(function (newKey) {
28
29
  const newProp = obj[newKey];
29
- const indent = ' '.repeat(depth + 1);
30
- return `${indent}'${newKey}': ${processJsonNode(newProp, options, depth + 1)}`;
30
+ return `${indent.repeat(depth + 1)}'${newKey}': ${processJsonNode(
31
+ newProp,
32
+ options,
33
+ depth + 1,
34
+ )}`;
31
35
  })
32
- .join(',\n');
33
- output += '\n' + ' '.repeat(depth) + ')';
36
+ .join(`,\n`);
37
+ output += `\n` + indent.repeat(depth) + ')';
34
38
  }
35
39
  return output;
36
40
  }
@@ -41,6 +45,8 @@ function processJsonNode(obj, options, depth = 0) {
41
45
  * options: Config & LocalOptions
42
46
  * }} opts
43
47
  */
44
- export default ({ dictionary, options }) => `
48
+ export default ({ dictionary, options }) => {
49
+ return `
45
50
  $${options.mapName ?? 'tokens'}: ${processJsonNode(dictionary.tokens, options)};
46
51
  `;
52
+ };
@@ -1,3 +1,5 @@
1
+ import { addComment } from '../../formatHelpers/createPropertyFormatter.js';
2
+
1
3
  /**
2
4
  * @typedef {import('../../../../types/DesignToken.ts').TransformedToken} TransformedToken
3
5
  * @typedef {import('../../../../types/Config.ts').Config} Config
@@ -11,14 +13,24 @@
11
13
  * header: string
12
14
  * }} opts
13
15
  */
14
- export default ({ allTokens, options, header }) => `
15
- ${header}$${options.mapName ?? 'tokens'}: (
16
- ${allTokens
17
- .map(
18
- (token) =>
19
- `${token.comment ? ` // ${token.comment}\n` : ''} '${token.name}': ${
16
+ export default ({ allTokens, options, header }) => {
17
+ const _f = options.formatting ?? {};
18
+ const f = {
19
+ ..._f,
20
+ indentation: _f.indentation ?? ' ',
21
+ commentStyle: _f.commentStyle ?? 'short',
22
+ commentPosition: _f.commentPosition ?? 'above',
23
+ };
24
+ return `
25
+ ${header}$${options.mapName ?? 'tokens'}: (\n${allTokens
26
+ .map((token, i, arr) => {
27
+ const tokenString = `${f.indentation}'${token.name}': ${
20
28
  options.usesDtcg ? token.$value : token.value
21
- }`,
22
- )
23
- .join(',\n')}
24
- );`;
29
+ }${i !== arr.length - 1 ? ',' : ''}`;
30
+ if (token.comment && f.commentStyle !== 'none') {
31
+ return addComment(tokenString, token.comment, f);
32
+ }
33
+ return tokenString;
34
+ })
35
+ .join(`\n`)}\n);`;
36
+ };
@@ -832,6 +832,7 @@ export default {
832
832
  }
833
833
  const parsedVal = parseFloat(nonParsed);
834
834
  if (isNaN(parsedVal)) throwSizeError(token.name, nonParsed, 'rem');
835
+ if (parsedVal === 0) return Number.isInteger(nonParsed) ? 0 : '0';
835
836
  return parsedVal + 'rem';
836
837
  },
837
838
  },
@@ -1,4 +1,4 @@
1
- export const verbosityInfo: "Use log.verbosity \"verbose\" or use CLI option --verbose for more details.";
1
+ export const verbosityInfo: "Use log.verbosity \"verbose\" or use CLI option --verbose for more details.\nRefer to: https://styledictionary.com/reference/logging/";
2
2
  export class GroupMessages {
3
3
  /** @type {{[key: string]: string[]}} */
4
4
  groupedMessages: {
@@ -11,7 +11,7 @@
11
11
  * and limitations under the License.
12
12
  */
13
13
 
14
- export const verbosityInfo = `Use log.verbosity "verbose" or use CLI option --verbose for more details.`;
14
+ export const verbosityInfo = `Use log.verbosity "verbose" or use CLI option --verbose for more details.\nRefer to: https://styledictionary.com/reference/logging/`;
15
15
 
16
16
  export class GroupMessages {
17
17
  constructor() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "style-dictionary",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Style once, use everywhere. A build system for creating cross-platform styles.",
5
5
  "keywords": [
6
6
  "style dictionary",
@@ -59,6 +59,8 @@
59
59
  "test:coverage": "cd coverage/lcov-report && npx http-server -o -c-1",
60
60
  "test:update-snapshots": "web-test-runner --update-snapshots",
61
61
  "test:node": "mocha -r mocha-hooks.mjs './__integration__/**/*.test.js' './__tests__/**/*.test.js' './__node_tests__/**/*.test.js'",
62
+ "test:perf": "mocha -r mocha-hooks.mjs './__perf_tests__/**/*.test.js'",
63
+ "test:perf:debug": "web-test-runner --config wtr-perf.config.mjs --watch",
62
64
  "install-cli": "npm install -g $(npm pack)",
63
65
  "release": "npm run build && changeset publish",
64
66
  "prepare": "husky install",
package/types/Config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { DesignToken, DesignTokens, PreprocessedTokens } from './DesignToken.ts';
2
2
  import type { Filter } from './Filter.ts';
3
- import type { FileHeader, File, FormattingOptions } from './File.ts';
3
+ import type { FileHeader, File, FormattingOverrides } from './File.ts';
4
4
  import type { Parser } from './Parser.ts';
5
5
  import type { Preprocessor } from './Preprocessor.ts';
6
6
  import type { Transform } from './Transform.ts';
@@ -21,7 +21,7 @@ export interface LocalOptions {
21
21
  fileHeader?: string | FileHeader;
22
22
  outputReferences?: OutputReferences;
23
23
  outputReferenceFallbacks?: boolean;
24
- formatting?: FormattingOptions;
24
+ formatting?: FormattingOverrides;
25
25
  [key: string]: any;
26
26
  }
27
27
  export interface RegexOptions {
package/types/File.d.ts CHANGED
@@ -2,16 +2,18 @@ import type { TransformedToken } from './DesignToken.ts';
2
2
  import type { FormatFn } from './Format.ts';
3
3
  import type { LocalOptions, Config } from './Config.ts';
4
4
  import type { Filter } from './Filter.ts';
5
- export interface FormattingOptions {
5
+ export interface FormattingOptions extends FormattingOverrides {
6
6
  prefix?: string;
7
7
  suffix?: string;
8
8
  lineSeparator?: string;
9
- header?: string;
10
- footer?: string;
9
+ separator?: string;
10
+ }
11
+ export interface FormattingOverrides {
11
12
  commentStyle?: 'short' | 'long' | 'none';
12
13
  commentPosition?: 'above' | 'inline';
13
14
  indentation?: string;
14
- separator?: string;
15
+ header?: string;
16
+ footer?: string;
15
17
  fileHeaderTimestamp?: boolean;
16
18
  }
17
19
  export type FileHeader = (defaultMessage: string[], options?: Config) => Promise<string[]> | string[];