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.
- package/bin/{style-dictionary → style-dictionary.js} +13 -13
- package/examples/advanced/auto-rebuild-watcher/package.json +1 -1
- package/examples/advanced/create-react-app/package.json +2 -1
- package/examples/advanced/create-react-native-app/package.json +5 -1
- package/examples/advanced/s3/upload.js +1 -1
- package/examples/advanced/yaml-tokens/sd.config.js +1 -1
- package/lib/StyleDictionary.js +451 -0
- package/lib/buildFile.js +46 -40
- package/lib/cleanDir.js +1 -1
- package/lib/cleanDirs.js +2 -2
- package/lib/cleanFile.js +1 -1
- package/lib/common/actions.js +12 -7
- package/lib/common/formatHelpers/createPropertyFormatter.js +116 -59
- package/lib/common/formatHelpers/getTypeScriptType.js +8 -7
- package/lib/common/formats.js +6 -6
- package/lib/common/templates/compose/object.kt.template.js +1 -1
- package/lib/common/templates/css/fonts.css.template.js +1 -1
- package/lib/common/templates/ios/singleton.m.template.js +10 -10
- package/lib/common/templates/scss/map-deep.template.js +3 -3
- package/lib/common/transforms.js +11 -1
- package/lib/filterTokens.js +76 -0
- package/lib/register/preprocessor.js +39 -0
- package/lib/register/transform.js +2 -1
- package/lib/register/transformGroup.js +1 -3
- package/lib/transform/config.js +26 -21
- package/lib/transform/object.js +18 -12
- package/lib/transform/{property.js → token.js} +1 -2
- package/lib/transform/{propertySetup.js → tokenSetup.js} +17 -17
- package/lib/utils/combineJSON.js +22 -12
- package/lib/utils/convertToBase64.js +3 -3
- package/lib/utils/createDictionary.js +10 -14
- package/lib/utils/createFormatArgs.js +1 -5
- package/lib/utils/deepExtend.js +15 -18
- package/lib/utils/deepmerge.js +14 -0
- package/lib/utils/{flattenProperties.js → flattenTokens.js} +10 -10
- package/lib/utils/preprocess.js +35 -0
- package/lib/utils/references/getName.js +2 -2
- package/lib/utils/references/getReferences.js +7 -7
- package/lib/utils/resolveObject.js +4 -5
- package/package.json +33 -20
- package/types/Config.d.ts +3 -2
- package/types/Dictionary.d.ts +0 -2
- package/types/FormatHelpers.d.ts +12 -23
- package/{lib/cleanAllPlatforms.js → types/Preprocessor.d.ts} +6 -17
- package/types/index.d.ts +23 -2
- package/types/index.test-d.ts +7 -0
- package/index-node.js +0 -7
- package/index.js +0 -84
- package/lib/buildAllPlatforms.js +0 -37
- package/lib/buildPlatform.js +0 -67
- package/lib/cleanPlatform.js +0 -59
- package/lib/exportPlatform.js +0 -131
- package/lib/extend.js +0 -162
- package/lib/filterProperties.js +0 -92
- 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 '
|
|
12
|
-
import StyleDictionary from '
|
|
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 =
|
|
101
|
+
const styleDictionary = new StyleDictionary(configPath);
|
|
102
102
|
|
|
103
103
|
if (options.platform && options.platform.length > 0) {
|
|
104
|
-
|
|
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 =
|
|
117
|
+
const styleDictionary = new StyleDictionary(configPath);
|
|
118
118
|
|
|
119
119
|
if (options.platform && options.platform.length > 0) {
|
|
120
|
-
|
|
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
|
|
|
@@ -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
|
|
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
|
|
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
|
+
}
|