@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.
@@ -0,0 +1,468 @@
1
+ const {
2
+ flattenTokens,
3
+ getTokenValue,
4
+ getTokenType,
5
+ isReference,
6
+ cleanReference,
7
+ toDartIdentifier,
8
+ canonicalTokenKey,
9
+ colorValue,
10
+ dimensionValue,
11
+ dartType,
12
+ } = require('./token-utils');
13
+
14
+ const CATEGORY_CONFIG = {
15
+ spacing: {
16
+ className: 'SpacingTokens',
17
+ importPath: '../spacing/spacing_tokens.dart',
18
+ },
19
+
20
+ radius: {
21
+ className: 'RadiusTokens',
22
+ importPath: '../radius/radius_tokens.dart',
23
+ },
24
+
25
+ dimensions: {
26
+ className: 'DimensionTokens',
27
+ importPath: '../dimensions/dimensions_tokens.dart',
28
+ },
29
+
30
+ typography: {
31
+ className: 'TypographyTokens',
32
+ importPath: '../typography/typography_tokens.dart',
33
+ },
34
+
35
+ shadow: {
36
+ className: 'ShadowTokens',
37
+ importPath: '../shadow/shadow_tokens.dart',
38
+ },
39
+
40
+ alias: {
41
+ className: 'AliasTokens',
42
+ importPath: '../alias/alias_tokens.dart',
43
+ },
44
+
45
+ global: {
46
+ className: 'GlobalTokens',
47
+ importPath: '../global/global_tokens.dart',
48
+ },
49
+ };
50
+
51
+ function generateComponentTokens({
52
+ tokens,
53
+ aliasTokens,
54
+ globalTokens,
55
+ spacingTokens,
56
+ radiusTokens,
57
+ dimensionTokens,
58
+ typographyTokens,
59
+ shadowTokens,
60
+ }) {
61
+ const rawMap =
62
+ flattenTokens(tokens || {});
63
+
64
+ const aliasMap =
65
+ flattenTokens(aliasTokens || {});
66
+
67
+ const globalMap =
68
+ flattenTokens(globalTokens || {});
69
+
70
+ const spacingMap =
71
+ flattenTokens(spacingTokens || {});
72
+
73
+ const radiusMap =
74
+ flattenTokens(radiusTokens || {});
75
+
76
+ const dimensionMap =
77
+ flattenTokens(dimensionTokens || {});
78
+
79
+ const typographyMap =
80
+ flattenTokens(
81
+ typographyTokens || {}
82
+ );
83
+
84
+ const shadowMap =
85
+ flattenTokens(shadowTokens || {});
86
+
87
+ /*
88
+ * Deduplicate by canonical Dart name.
89
+ */
90
+ const componentMap =
91
+ new Map();
92
+
93
+ for (
94
+ const [tokenPath, token]
95
+ of Object.entries(rawMap)
96
+ ) {
97
+ const canonical =
98
+ canonicalTokenKey(
99
+ tokenPath
100
+ );
101
+
102
+ if (
103
+ componentMap.has(canonical)
104
+ ) {
105
+ /*
106
+ * Same logical token exported twice:
107
+ *
108
+ * buttoncomponent
109
+ * buttonComponent
110
+ *
111
+ * Keep the first canonical source.
112
+ */
113
+ continue;
114
+ }
115
+
116
+ componentMap.set(
117
+ canonical,
118
+ {
119
+ tokenPath,
120
+ token,
121
+ }
122
+ );
123
+ }
124
+
125
+ const body = [];
126
+
127
+ const imports =
128
+ new Set();
129
+
130
+ for (
131
+ const { tokenPath, token }
132
+ of componentMap.values()
133
+ ) {
134
+ const type =
135
+ getTokenType(token);
136
+
137
+ const rawValue =
138
+ getTokenValue(token);
139
+
140
+ const resolved =
141
+ resolveReference(
142
+ rawValue,
143
+ type,
144
+ {
145
+ alias: aliasMap,
146
+ global: globalMap,
147
+ spacing: spacingMap,
148
+ radius: radiusMap,
149
+ dimensions: dimensionMap,
150
+ typography: typographyMap,
151
+ shadow: shadowMap,
152
+ },
153
+ []
154
+ );
155
+
156
+ const expr =
157
+ generateValue(
158
+ type,
159
+ resolved,
160
+ tokenPath
161
+ );
162
+
163
+ if (expr.importPath) {
164
+ imports.add(
165
+ expr.importPath
166
+ );
167
+ }
168
+
169
+ body.push(
170
+ ` static const ${expr.type} ` +
171
+ `${toDartIdentifier(tokenPath)} ` +
172
+ `= ${expr.value};`,
173
+ ''
174
+ );
175
+ }
176
+
177
+ const lines = [
178
+ '// GENERATED FILE - DO NOT EDIT',
179
+ '',
180
+ ];
181
+
182
+ if (
183
+ body.some(line =>
184
+ line.includes('Color')
185
+ )
186
+ ) {
187
+ imports.add(
188
+ 'dart:ui'
189
+ );
190
+ }
191
+
192
+ for (
193
+ const importPath
194
+ of [...imports].sort()
195
+ ) {
196
+ if (importPath === 'dart:ui') {
197
+ lines.push(
198
+ "import 'dart:ui';"
199
+ );
200
+ } else {
201
+ lines.push(
202
+ `import '${importPath}';`
203
+ );
204
+ }
205
+ }
206
+
207
+ if (imports.size > 0) {
208
+ lines.push('');
209
+ }
210
+
211
+ lines.push(
212
+ 'class ComponentTokens {',
213
+ ' ComponentTokens._();',
214
+ '',
215
+ ...body,
216
+ '}',
217
+ ''
218
+ );
219
+
220
+ return lines.join('\n');
221
+ }
222
+
223
+ function resolveReference(
224
+ value,
225
+ type,
226
+ maps,
227
+ stack
228
+ ) {
229
+ if (!isReference(value)) {
230
+ return {
231
+ value,
232
+ sourceLayer: 'literal',
233
+ sourcePath: null,
234
+ };
235
+ }
236
+
237
+ const referencePath =
238
+ cleanReference(value);
239
+
240
+ if (
241
+ stack.includes(referencePath)
242
+ ) {
243
+ throw new Error(
244
+ `❌ Circular component reference: ` +
245
+ `${[
246
+ ...stack,
247
+ referencePath,
248
+ ].join(' -> ')}`
249
+ );
250
+ }
251
+
252
+ /*
253
+ * Determine category from reference path.
254
+ */
255
+ const first =
256
+ referencePath.split('.')[0];
257
+
258
+ const categoryAliases = {
259
+ spacing: 'spacing',
260
+ space: 'spacing',
261
+
262
+ radius: 'radius',
263
+ radii: 'radius',
264
+
265
+ dimension: 'dimensions',
266
+ dimensions: 'dimensions',
267
+ size: 'dimensions',
268
+ sizes: 'dimensions',
269
+
270
+ typography: 'typography',
271
+ type: 'typography',
272
+
273
+ shadow: 'shadow',
274
+ shadows: 'shadow',
275
+ elevation: 'shadow',
276
+ };
277
+
278
+ const explicitCategory =
279
+ categoryAliases[first];
280
+
281
+ if (
282
+ explicitCategory &&
283
+ maps[explicitCategory]?.[
284
+ referencePath.substring(
285
+ first.length + 1
286
+ )
287
+ ]
288
+ ) {
289
+ return {
290
+ value: referencePath.substring(
291
+ first.length + 1
292
+ ),
293
+ sourceLayer: explicitCategory,
294
+ sourcePath:
295
+ referencePath.substring(
296
+ first.length + 1
297
+ ),
298
+ };
299
+ }
300
+
301
+ /*
302
+ * Alias has priority over global.
303
+ */
304
+ if (maps.alias[referencePath]) {
305
+ return {
306
+ value: getTokenValue(
307
+ maps.alias[referencePath]
308
+ ),
309
+ sourceLayer: 'alias',
310
+ sourcePath: referencePath,
311
+ };
312
+ }
313
+
314
+ if (maps.global[referencePath]) {
315
+ return {
316
+ value: getTokenValue(
317
+ maps.global[referencePath]
318
+ ),
319
+ sourceLayer: 'global',
320
+ sourcePath: referencePath,
321
+ };
322
+ }
323
+
324
+ throw new Error(
325
+ `❌ Broken component reference: ` +
326
+ `"${value}"\n` +
327
+ `Token not found: ${referencePath}`
328
+ );
329
+ }
330
+
331
+ function generateValue(
332
+ type,
333
+ resolved,
334
+ tokenPath
335
+ ) {
336
+ if (
337
+ resolved.sourceLayer ===
338
+ 'alias'
339
+ ) {
340
+ return {
341
+ type: dartType(type),
342
+ value:
343
+ `AliasTokens.${toDartIdentifier(
344
+ resolved.sourcePath
345
+ )}`,
346
+ importPath:
347
+ CATEGORY_CONFIG.alias.importPath,
348
+ };
349
+ }
350
+
351
+ if (
352
+ resolved.sourceLayer ===
353
+ 'global'
354
+ ) {
355
+ return {
356
+ type: dartType(type),
357
+ value:
358
+ `GlobalTokens.${toDartIdentifier(
359
+ resolved.sourcePath
360
+ )}`,
361
+ importPath:
362
+ CATEGORY_CONFIG.global.importPath,
363
+ };
364
+ }
365
+
366
+ if (
367
+ CATEGORY_CONFIG[
368
+ resolved.sourceLayer
369
+ ]
370
+ ) {
371
+ const config =
372
+ CATEGORY_CONFIG[
373
+ resolved.sourceLayer
374
+ ];
375
+
376
+ return {
377
+ type: dartType(type),
378
+ value:
379
+ `${config.className}.` +
380
+ `${toDartIdentifier(
381
+ resolved.sourcePath
382
+ )}`,
383
+ importPath:
384
+ config.importPath,
385
+ };
386
+ }
387
+
388
+ if (type === 'color') {
389
+ const color =
390
+ colorValue(
391
+ resolved.value
392
+ );
393
+
394
+ if (!color) {
395
+ throw new Error(
396
+ `❌ Invalid component color: ` +
397
+ `${tokenPath}\n` +
398
+ `Value: ${resolved.value}`
399
+ );
400
+ }
401
+
402
+ return {
403
+ type: 'Color',
404
+ value: color,
405
+ importPath: 'dart:ui',
406
+ };
407
+ }
408
+
409
+ if (
410
+ type === 'dimension'
411
+ ) {
412
+ const dimension =
413
+ dimensionValue(
414
+ resolved.value
415
+ );
416
+
417
+ if (dimension === null) {
418
+ throw new Error(
419
+ `❌ Invalid component dimension: ` +
420
+ `${tokenPath}\n` +
421
+ `Value: ${resolved.value}`
422
+ );
423
+ }
424
+
425
+ return {
426
+ type: 'double',
427
+ value: dimension,
428
+ };
429
+ }
430
+
431
+ if (type === 'number') {
432
+ return {
433
+ type: 'double',
434
+ value: String(
435
+ resolved.value
436
+ ),
437
+ };
438
+ }
439
+
440
+ if (type === 'string') {
441
+ return {
442
+ type: 'String',
443
+ value: JSON.stringify(
444
+ String(
445
+ resolved.value
446
+ )
447
+ ),
448
+ };
449
+ }
450
+
451
+ if (type === 'boolean') {
452
+ return {
453
+ type: 'bool',
454
+ value:
455
+ resolved.value
456
+ ? 'true'
457
+ : 'false',
458
+ };
459
+ }
460
+
461
+ throw new Error(
462
+ `❌ Unsupported component token type: ${type}`
463
+ );
464
+ }
465
+
466
+ module.exports = {
467
+ renderComponentTokens: generateComponentTokens,
468
+ };
@@ -0,0 +1,116 @@
1
+ const {
2
+ flattenTokens,
3
+ getTokenValue,
4
+ getTokenType,
5
+ toDartIdentifier,
6
+ colorValue,
7
+ dimensionValue,
8
+ } = require('./token-utils');
9
+
10
+ function renderGlobalTokens({
11
+ tokens,
12
+ }) {
13
+ const tokenMap = flattenTokens(tokens);
14
+
15
+ let output =
16
+ `// GENERATED FILE - DO NOT EDIT\n` +
17
+ `// ignore_for_file: constant_identifier_names\n\n` +
18
+ `import 'dart:ui';\n\n`;
19
+
20
+ output +=
21
+ `class GlobalTokens {\n` +
22
+ ` GlobalTokens._();\n\n`;
23
+
24
+ for (const [tokenPath, token] of Object.entries(tokenMap)) {
25
+ const tokenName = toDartIdentifier(tokenPath);
26
+
27
+ const value = getTokenValue(token);
28
+ const type = getTokenType(token);
29
+
30
+ let dartType;
31
+ let dartValue;
32
+
33
+ // -----------------------------
34
+ // COLOR
35
+ // -----------------------------
36
+
37
+ if (type === 'color') {
38
+ const color = colorValue(value);
39
+
40
+ if (!color) {
41
+ throw new Error(
42
+ `❌ Invalid global color:\n` +
43
+ `Token: ${tokenPath}\n` +
44
+ `Value: ${value}`
45
+ );
46
+ }
47
+
48
+ dartType = 'Color';
49
+ dartValue = color;
50
+ }
51
+
52
+ // -----------------------------
53
+ // DIMENSION
54
+ // -----------------------------
55
+
56
+ else if (type === 'dimension') {
57
+ const dimension = dimensionValue(value);
58
+
59
+ if (dimension === null) {
60
+ throw new Error(
61
+ `❌ Invalid global dimension:\n` +
62
+ `Token: ${tokenPath}\n` +
63
+ `Value: ${value}`
64
+ );
65
+ }
66
+
67
+ dartType = 'double';
68
+ dartValue = dimension;
69
+ }
70
+
71
+ // -----------------------------
72
+ // NUMBER
73
+ // -----------------------------
74
+
75
+ else if (type === 'number') {
76
+ dartType = 'double';
77
+ dartValue = String(value);
78
+ }
79
+
80
+ // -----------------------------
81
+ // STRING
82
+ // -----------------------------
83
+
84
+ else if (type === 'string') {
85
+ dartType = 'String';
86
+ dartValue = JSON.stringify(String(value));
87
+ }
88
+
89
+ // -----------------------------
90
+ // BOOLEAN
91
+ // -----------------------------
92
+
93
+ else if (type === 'boolean') {
94
+ dartType = 'bool';
95
+ dartValue = value ? 'true' : 'false';
96
+ }
97
+
98
+ else {
99
+ throw new Error(
100
+ `❌ Unsupported global token type:\n` +
101
+ `Token: ${tokenPath}\n` +
102
+ `Type: ${type}`
103
+ );
104
+ }
105
+
106
+ output +=
107
+ ` static const ${dartType} ` +
108
+ `${tokenName} = ${dartValue};\n\n`;
109
+ }
110
+
111
+ output += `}\n`;
112
+
113
+ return output;
114
+ }
115
+
116
+ module.exports = { renderGlobalTokens };