@khaledeltohamy14/token-pipeline-engine 1.0.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.
- package/README.md +12 -0
- package/generators/alias-generator.js +257 -0
- package/generators/brand-generator.js +68 -0
- package/generators/category-generator.js +44 -0
- package/generators/component-generator.js +468 -0
- package/generators/global-generator.js +116 -0
- package/generators/theme-generator.js +1168 -0
- package/generators/token-utils.js +355 -0
- package/index.d.ts +34 -0
- package/index.js +148 -0
- package/package.json +35 -0
- package/split/split-token-document.js +58 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
function readJSON(filePath) {
|
|
4
|
+
if (!fs.existsSync(filePath)) {
|
|
5
|
+
throw new Error(`❌ File not found: ${filePath}`);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
10
|
+
} catch (error) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`❌ Invalid JSON: ${filePath}\n${error.message}`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ensureDir(dir) {
|
|
18
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isObject(value) {
|
|
22
|
+
return value !== null &&
|
|
23
|
+
typeof value === 'object' &&
|
|
24
|
+
!Array.isArray(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isToken(value) {
|
|
28
|
+
return isObject(value) &&
|
|
29
|
+
('$value' in value || 'value' in value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getTokenValue(token) {
|
|
33
|
+
return '$value' in token
|
|
34
|
+
? token.$value
|
|
35
|
+
: token.value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getTokenType(token) {
|
|
39
|
+
return token.$type || token.type;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isReference(value) {
|
|
43
|
+
return typeof value === 'string' &&
|
|
44
|
+
value.startsWith('{') &&
|
|
45
|
+
value.endsWith('}');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function cleanReference(value) {
|
|
49
|
+
return value
|
|
50
|
+
.replace(/^\{/, '')
|
|
51
|
+
.replace(/\}$/, '')
|
|
52
|
+
.trim();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
|--------------------------------------------------------------------------
|
|
57
|
+
| Dart naming
|
|
58
|
+
|--------------------------------------------------------------------------
|
|
59
|
+
|
|
|
60
|
+
| JSON naming stays untouched.
|
|
61
|
+
|
|
|
62
|
+
| Dart:
|
|
63
|
+
|
|
|
64
|
+
| red_500 -> red500
|
|
65
|
+
| card_background -> cardBackground
|
|
66
|
+
| textfieldcomponent_padding -> textfieldComponentPadding
|
|
67
|
+
| topRightButtonsComponent -> topRightButtonsComponent
|
|
68
|
+
| toprightbuttonscomponent -> topRightButtonsComponent
|
|
69
|
+
|
|
|
70
|
+
|--------------------------------------------------------------------------
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
const KNOWN_COMPOUND_NAMES = {
|
|
74
|
+
toprightbuttons: 'topRightButtons',
|
|
75
|
+
textfield: 'textfield',
|
|
76
|
+
button: 'button',
|
|
77
|
+
radio: 'radio',
|
|
78
|
+
card: 'card',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function normalizeCompoundWord(word) {
|
|
82
|
+
const lower = word.toLowerCase();
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* Handle:
|
|
86
|
+
*
|
|
87
|
+
* textfieldcomponent
|
|
88
|
+
* buttoncomponent
|
|
89
|
+
* radiocomponent
|
|
90
|
+
* cardcomponent
|
|
91
|
+
* toprightbuttonscomponent
|
|
92
|
+
*/
|
|
93
|
+
if (lower.endsWith('component')) {
|
|
94
|
+
const base = lower.substring(
|
|
95
|
+
0,
|
|
96
|
+
lower.length - 'component'.length
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const normalizedBase =
|
|
100
|
+
KNOWN_COMPOUND_NAMES[base] || base;
|
|
101
|
+
|
|
102
|
+
return `${normalizedBase}Component`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return KNOWN_COMPOUND_NAMES[lower] || lower;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function splitIdentifier(value) {
|
|
109
|
+
return String(value)
|
|
110
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
111
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
112
|
+
.replace(/[_./\\\-]+/g, ' ')
|
|
113
|
+
.trim()
|
|
114
|
+
.split(/\s+/)
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function toDartIdentifier(value) {
|
|
119
|
+
const words = splitIdentifier(value);
|
|
120
|
+
|
|
121
|
+
if (!words.length) {
|
|
122
|
+
return '_token';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/*
|
|
126
|
+
* First normalize compound component words.
|
|
127
|
+
*/
|
|
128
|
+
const normalizedWords = [];
|
|
129
|
+
|
|
130
|
+
for (finalWord of words) {
|
|
131
|
+
const normalized = normalizeCompoundWord(finalWord);
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
* A word such as:
|
|
135
|
+
*
|
|
136
|
+
* topRightButtonsComponent
|
|
137
|
+
*
|
|
138
|
+
* can already contain camel casing.
|
|
139
|
+
*/
|
|
140
|
+
const nested = normalized
|
|
141
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
142
|
+
.split(/\s+/);
|
|
143
|
+
|
|
144
|
+
normalizedWords.push(...nested);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const first = normalizedWords[0].toLowerCase();
|
|
148
|
+
|
|
149
|
+
const rest = normalizedWords
|
|
150
|
+
.slice(1)
|
|
151
|
+
.map(word => {
|
|
152
|
+
const lower = word.toLowerCase();
|
|
153
|
+
|
|
154
|
+
return lower.charAt(0).toUpperCase() +
|
|
155
|
+
lower.slice(1);
|
|
156
|
+
})
|
|
157
|
+
.join('');
|
|
158
|
+
|
|
159
|
+
let result = first + rest;
|
|
160
|
+
|
|
161
|
+
if (/^\d/.test(result)) {
|
|
162
|
+
result = `_${result}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/*
|
|
169
|
+
* Same semantic key for duplicate detection.
|
|
170
|
+
*
|
|
171
|
+
* buttonComponent
|
|
172
|
+
* buttoncomponent
|
|
173
|
+
*
|
|
174
|
+
* => buttoncomponent
|
|
175
|
+
*/
|
|
176
|
+
function canonicalTokenKey(value) {
|
|
177
|
+
return String(value)
|
|
178
|
+
.replace(/[^a-zA-Z0-9]/g, '')
|
|
179
|
+
.toLowerCase();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const toSafeName = toDartIdentifier;
|
|
183
|
+
|
|
184
|
+
function flattenTokens(
|
|
185
|
+
object,
|
|
186
|
+
prefix = '',
|
|
187
|
+
result = {}
|
|
188
|
+
) {
|
|
189
|
+
for (const [key, value] of Object.entries(object || {})) {
|
|
190
|
+
const currentPath =
|
|
191
|
+
prefix
|
|
192
|
+
? `${prefix}.${key}`
|
|
193
|
+
: key;
|
|
194
|
+
|
|
195
|
+
if (isToken(value)) {
|
|
196
|
+
result[currentPath] = value;
|
|
197
|
+
} else if (isObject(value)) {
|
|
198
|
+
flattenTokens(
|
|
199
|
+
value,
|
|
200
|
+
currentPath,
|
|
201
|
+
result
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function colorValue(value) {
|
|
210
|
+
if (
|
|
211
|
+
typeof value !== 'string' ||
|
|
212
|
+
!value.startsWith('#')
|
|
213
|
+
) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let hex = value
|
|
218
|
+
.replace('#', '')
|
|
219
|
+
.toUpperCase();
|
|
220
|
+
|
|
221
|
+
if (hex.length === 3) {
|
|
222
|
+
hex = hex
|
|
223
|
+
.split('')
|
|
224
|
+
.map(x => x + x)
|
|
225
|
+
.join('');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (hex.length === 6) {
|
|
229
|
+
return `Color(0xFF${hex})`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (hex.length === 8) {
|
|
233
|
+
return `Color(0x${hex})`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function dimensionValue(value) {
|
|
240
|
+
if (typeof value !== 'string') {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const match = value.match(
|
|
245
|
+
/^(-?\d+(?:\.\d+)?)px$/
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return match ? match[1] : null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function dartType(type) {
|
|
252
|
+
const types = {
|
|
253
|
+
color: 'Color',
|
|
254
|
+
dimension: 'double',
|
|
255
|
+
number: 'double',
|
|
256
|
+
string: 'String',
|
|
257
|
+
boolean: 'bool',
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (!types[type]) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Unsupported type: ${type}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return types[type];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function dartLiteral(
|
|
270
|
+
value,
|
|
271
|
+
type,
|
|
272
|
+
tokenPath = 'token'
|
|
273
|
+
) {
|
|
274
|
+
if (type === 'color') {
|
|
275
|
+
const color = colorValue(value);
|
|
276
|
+
|
|
277
|
+
if (!color) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`❌ Invalid color token: ${tokenPath}\n` +
|
|
280
|
+
`Value: ${value}`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
type: 'Color',
|
|
286
|
+
value: color,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (type === 'dimension') {
|
|
291
|
+
const dimension =
|
|
292
|
+
dimensionValue(value);
|
|
293
|
+
|
|
294
|
+
if (dimension === null) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`❌ Invalid dimension token: ${tokenPath}\n` +
|
|
297
|
+
`Value: ${value}`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
type: 'double',
|
|
303
|
+
value: dimension,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (type === 'number') {
|
|
308
|
+
return {
|
|
309
|
+
type: 'double',
|
|
310
|
+
value: String(value),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (type === 'string') {
|
|
315
|
+
return {
|
|
316
|
+
type: 'String',
|
|
317
|
+
value: JSON.stringify(
|
|
318
|
+
String(value)
|
|
319
|
+
),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (type === 'boolean') {
|
|
324
|
+
return {
|
|
325
|
+
type: 'bool',
|
|
326
|
+
value: value
|
|
327
|
+
? 'true'
|
|
328
|
+
: 'false',
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
throw new Error(
|
|
333
|
+
`❌ Unsupported token type: ${type}\n` +
|
|
334
|
+
`Token: ${tokenPath}`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = {
|
|
339
|
+
readJSON,
|
|
340
|
+
ensureDir,
|
|
341
|
+
isObject,
|
|
342
|
+
isToken,
|
|
343
|
+
getTokenValue,
|
|
344
|
+
getTokenType,
|
|
345
|
+
isReference,
|
|
346
|
+
cleanReference,
|
|
347
|
+
toSafeName,
|
|
348
|
+
toDartIdentifier,
|
|
349
|
+
canonicalTokenKey,
|
|
350
|
+
flattenTokens,
|
|
351
|
+
colorValue,
|
|
352
|
+
dimensionValue,
|
|
353
|
+
dartType,
|
|
354
|
+
dartLiteral,
|
|
355
|
+
};
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type TokenPipelineGeneratedFile = {
|
|
2
|
+
path: string;
|
|
3
|
+
content: string;
|
|
4
|
+
mode: '100644' | '100755';
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type GenerateTokenFilesOptions = {
|
|
8
|
+
document: Record<string, unknown>;
|
|
9
|
+
outputRoot?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type GenerateRepositoryFilesOptions = {
|
|
13
|
+
document: Record<string, unknown>;
|
|
14
|
+
outputRoot?: string;
|
|
15
|
+
tokenRoot?: string;
|
|
16
|
+
sourceTokenPath?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function splitTokenDocument(
|
|
20
|
+
document: Record<string, unknown>,
|
|
21
|
+
): Record<string, unknown>;
|
|
22
|
+
|
|
23
|
+
export function splitTokenFiles(
|
|
24
|
+
document: Record<string, unknown>,
|
|
25
|
+
root?: string,
|
|
26
|
+
): TokenPipelineGeneratedFile[];
|
|
27
|
+
|
|
28
|
+
export function generateTokenFiles(
|
|
29
|
+
options: GenerateTokenFilesOptions,
|
|
30
|
+
): TokenPipelineGeneratedFile[];
|
|
31
|
+
|
|
32
|
+
export function generateRepositoryFiles(
|
|
33
|
+
options: GenerateRepositoryFilesOptions,
|
|
34
|
+
): TokenPipelineGeneratedFile[];
|
package/index.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const { splitTokenDocument, splitTokenFiles } = require('./split/split-token-document');
|
|
2
|
+
const { renderGlobalTokens } = require('./generators/global-generator');
|
|
3
|
+
const { renderAliasTokens } = require('./generators/alias-generator');
|
|
4
|
+
const { renderCategoryTokens } = require('./generators/category-generator');
|
|
5
|
+
const { renderComponentTokens } = require('./generators/component-generator');
|
|
6
|
+
const { renderBrandTokens } = require('./generators/brand-generator');
|
|
7
|
+
const { renderTheme, renderThemeTokens } = require('./generators/theme-generator');
|
|
8
|
+
|
|
9
|
+
const CATEGORY_CONFIG = [
|
|
10
|
+
['spacing', 'SpacingTokens'],
|
|
11
|
+
['typography', 'TypographyTokens'],
|
|
12
|
+
['radius', 'RadiusTokens'],
|
|
13
|
+
['shadow', 'ShadowTokens'],
|
|
14
|
+
['dimensions', 'DimensionTokens'],
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
function asRecord(value) {
|
|
18
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeRelativePath(value) {
|
|
22
|
+
const normalized = String(value || '').trim().replace(/^\/+/, '').replace(/\\/g, '/');
|
|
23
|
+
if (!normalized || normalized.includes('..') || normalized.split('/').some((part) => !part)) {
|
|
24
|
+
throw new Error(`Invalid generated output path: ${value}`);
|
|
25
|
+
}
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function joinPath(root, file) {
|
|
30
|
+
const cleanRoot = String(root || '').trim().replace(/^\/+|\/+$/g, '');
|
|
31
|
+
return cleanRoot ? `${cleanRoot}/${file}` : file;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function brandClassName(brandName) {
|
|
35
|
+
return String(brandName).split(/[_-]/).map((value) => value.charAt(0).toUpperCase() + value.slice(1)).join('') + 'Tokens';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function brandThemeClassName(brandName) {
|
|
39
|
+
return String(brandName).split(/[_-]/).map((value) => value.charAt(0).toUpperCase() + value.slice(1)).join('') + 'Theme';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function generateTokenFiles({
|
|
43
|
+
document,
|
|
44
|
+
outputRoot = 'packages/tokens/lib',
|
|
45
|
+
}) {
|
|
46
|
+
const source = asRecord(document);
|
|
47
|
+
const tokens = asRecord(source.tokens);
|
|
48
|
+
const globalTokens = asRecord(tokens.global);
|
|
49
|
+
const aliasTokens = asRecord(tokens.alias);
|
|
50
|
+
const componentTokens = asRecord(tokens.components);
|
|
51
|
+
const spacingTokens = asRecord(tokens.spacing);
|
|
52
|
+
const typographyTokens = asRecord(tokens.typography);
|
|
53
|
+
const radiusTokens = asRecord(tokens.radius);
|
|
54
|
+
const shadowTokens = asRecord(tokens.shadow);
|
|
55
|
+
const dimensionTokens = asRecord(tokens.dimensions);
|
|
56
|
+
const brands = asRecord(tokens.brands);
|
|
57
|
+
|
|
58
|
+
const root = normalizeRelativePath(outputRoot);
|
|
59
|
+
const files = [];
|
|
60
|
+
const add = (path, content) => files.push({ path: joinPath(root, path), content, mode: '100644' });
|
|
61
|
+
|
|
62
|
+
add('global/global_tokens.dart', renderGlobalTokens({ tokens: globalTokens }));
|
|
63
|
+
add('alias/alias_tokens.dart', renderAliasTokens({ tokens: aliasTokens, globalTokens }));
|
|
64
|
+
|
|
65
|
+
for (const [name, className] of CATEGORY_CONFIG) {
|
|
66
|
+
add(`${name}/${name}_tokens.dart`, renderCategoryTokens({
|
|
67
|
+
tokens: asRecord(tokens[name]),
|
|
68
|
+
globalTokens,
|
|
69
|
+
aliasTokens,
|
|
70
|
+
className,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
add('components/component_tokens.dart', renderComponentTokens({
|
|
75
|
+
tokens: componentTokens,
|
|
76
|
+
aliasTokens,
|
|
77
|
+
globalTokens,
|
|
78
|
+
spacingTokens,
|
|
79
|
+
radiusTokens,
|
|
80
|
+
dimensionTokens,
|
|
81
|
+
typographyTokens,
|
|
82
|
+
shadowTokens,
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
const brandNames = Object.keys(brands).sort();
|
|
86
|
+
const brandMap = {};
|
|
87
|
+
for (const brandName of brandNames) {
|
|
88
|
+
const brandTokens = asRecord(brands[brandName]);
|
|
89
|
+
brandMap[brandName] = brandTokens;
|
|
90
|
+
add(`brands/${brandName}_tokens.dart`, renderBrandTokens({
|
|
91
|
+
brandTokens,
|
|
92
|
+
aliasTokens,
|
|
93
|
+
globalTokens,
|
|
94
|
+
className: brandClassName(brandName),
|
|
95
|
+
}));
|
|
96
|
+
add(`themes/${brandName}_theme.dart`, renderTheme({
|
|
97
|
+
aliasTokens,
|
|
98
|
+
globalTokens,
|
|
99
|
+
componentTokens,
|
|
100
|
+
spacingTokens,
|
|
101
|
+
radiusTokens,
|
|
102
|
+
dimensionTokens,
|
|
103
|
+
typographyTokens,
|
|
104
|
+
shadowTokens,
|
|
105
|
+
brandTokens,
|
|
106
|
+
className: brandThemeClassName(brandName),
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
add('themes/theme_tokens.dart', renderThemeTokens({
|
|
111
|
+
aliasTokens,
|
|
112
|
+
globalTokens,
|
|
113
|
+
componentTokens,
|
|
114
|
+
spacingTokens,
|
|
115
|
+
radiusTokens,
|
|
116
|
+
dimensionTokens,
|
|
117
|
+
typographyTokens,
|
|
118
|
+
shadowTokens,
|
|
119
|
+
brands: brandMap,
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
return files;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function generateRepositoryFiles({
|
|
126
|
+
document,
|
|
127
|
+
outputRoot = 'packages/tokens/lib',
|
|
128
|
+
tokenRoot = 'token_pipeline/tokens',
|
|
129
|
+
sourceTokenPath = 'token_pipeline/tokens.json',
|
|
130
|
+
}) {
|
|
131
|
+
const files = [];
|
|
132
|
+
const sourcePath = normalizeRelativePath(sourceTokenPath);
|
|
133
|
+
files.push({
|
|
134
|
+
path: sourcePath,
|
|
135
|
+
content: `${JSON.stringify(document, null, 2)}\n`,
|
|
136
|
+
mode: '100644',
|
|
137
|
+
});
|
|
138
|
+
files.push(...splitTokenFiles(document, tokenRoot));
|
|
139
|
+
files.push(...generateTokenFiles({ document, outputRoot }));
|
|
140
|
+
return files;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
splitTokenDocument,
|
|
145
|
+
splitTokenFiles,
|
|
146
|
+
generateTokenFiles,
|
|
147
|
+
generateRepositoryFiles,
|
|
148
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@khaledeltohamy14/token-pipeline-engine",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reusable Galaxy design token pipeline engine for splitting design tokens and generating token/theme files.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"files": [
|
|
9
|
+
"index.js",
|
|
10
|
+
"index.d.ts",
|
|
11
|
+
"generators",
|
|
12
|
+
"split",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/YOUR_GITHUB_USERNAME/galaxy-token-pipeline-engine.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"figma",
|
|
21
|
+
"design-tokens",
|
|
22
|
+
"flutter",
|
|
23
|
+
"dart",
|
|
24
|
+
"atomic-design",
|
|
25
|
+
"galaxy"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "node test/engine.test.js",
|
|
33
|
+
"pack:check": "npm pack --dry-run"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const CATEGORY_NAMES = [
|
|
2
|
+
'global',
|
|
3
|
+
'alias',
|
|
4
|
+
'components',
|
|
5
|
+
'spacing',
|
|
6
|
+
'typography',
|
|
7
|
+
'radius',
|
|
8
|
+
'shadow',
|
|
9
|
+
'dimensions',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
function asRecord(value) {
|
|
13
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
14
|
+
? value
|
|
15
|
+
: {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function splitTokenDocument(document) {
|
|
19
|
+
const source = asRecord(document);
|
|
20
|
+
const tokens = asRecord(source.tokens);
|
|
21
|
+
const result = {};
|
|
22
|
+
|
|
23
|
+
for (const category of CATEGORY_NAMES) {
|
|
24
|
+
result[category] = asRecord(tokens[category]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
result.brands = asRecord(tokens.brands);
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function splitTokenFiles(document, root = 'token_pipeline/tokens') {
|
|
33
|
+
const split = splitTokenDocument(document);
|
|
34
|
+
const files = [];
|
|
35
|
+
|
|
36
|
+
for (const category of CATEGORY_NAMES) {
|
|
37
|
+
files.push({
|
|
38
|
+
path: `${root}/${category}.json`,
|
|
39
|
+
content: `${JSON.stringify(split[category], null, 2)}\n`,
|
|
40
|
+
mode: '100644',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const [brandName, brandTokens] of Object.entries(split.brands)) {
|
|
45
|
+
files.push({
|
|
46
|
+
path: `token_pipeline/brands/${brandName}.json`,
|
|
47
|
+
content: `${JSON.stringify(asRecord(brandTokens), null, 2)}\n`,
|
|
48
|
+
mode: '100644',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return files;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
splitTokenDocument,
|
|
57
|
+
splitTokenFiles,
|
|
58
|
+
};
|