@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
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @khaledeltohamy14/token-pipeline-engine
|
|
2
|
+
|
|
3
|
+
Shared token pipeline used by Galaxy and generated repositories.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
- `splitTokenDocument(document)`
|
|
8
|
+
- `splitTokenFiles(document)`
|
|
9
|
+
- `generateTokenFiles({ document })`
|
|
10
|
+
- `generateRepositoryFiles({ document })`
|
|
11
|
+
|
|
12
|
+
The engine is filesystem-free. It returns `{ path, content, mode }` objects so Galaxy can send them directly to GitHub and Node CI can write them to disk.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const {
|
|
2
|
+
flattenTokens,
|
|
3
|
+
getTokenValue,
|
|
4
|
+
getTokenType,
|
|
5
|
+
isReference,
|
|
6
|
+
cleanReference,
|
|
7
|
+
toDartIdentifier,
|
|
8
|
+
colorValue,
|
|
9
|
+
dimensionValue,
|
|
10
|
+
} = require('./token-utils');
|
|
11
|
+
|
|
12
|
+
function renderAliasTokens({
|
|
13
|
+
tokens,
|
|
14
|
+
globalTokens,
|
|
15
|
+
}) {
|
|
16
|
+
const aliasMap = flattenTokens(tokens);
|
|
17
|
+
const globalMap = flattenTokens(globalTokens);
|
|
18
|
+
|
|
19
|
+
let output =
|
|
20
|
+
`// GENERATED FILE - DO NOT EDIT\n` +
|
|
21
|
+
`// ignore_for_file: constant_identifier_names\n\n` +
|
|
22
|
+
`import 'dart:ui';\n` +
|
|
23
|
+
`import '../global/global_tokens.dart';\n\n`;
|
|
24
|
+
|
|
25
|
+
output +=
|
|
26
|
+
`class AliasTokens {\n` +
|
|
27
|
+
` AliasTokens._();\n\n`;
|
|
28
|
+
|
|
29
|
+
for (const [tokenPath, token] of Object.entries(aliasMap)) {
|
|
30
|
+
const tokenName = toDartIdentifier(tokenPath);
|
|
31
|
+
|
|
32
|
+
const rawValue = getTokenValue(token);
|
|
33
|
+
const type = getTokenType(token);
|
|
34
|
+
|
|
35
|
+
const resolved = resolveAliasReference({
|
|
36
|
+
value: rawValue,
|
|
37
|
+
aliasMap,
|
|
38
|
+
globalMap,
|
|
39
|
+
stack: [],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const dart = generateDartValue({
|
|
43
|
+
type,
|
|
44
|
+
resolved,
|
|
45
|
+
tokenPath,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
output +=
|
|
49
|
+
` static const ${dart.type} ` +
|
|
50
|
+
`${tokenName} = ${dart.value};\n\n`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
output += `}\n`;
|
|
54
|
+
|
|
55
|
+
return output;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============================================================
|
|
59
|
+
// RESOLVE ALIAS
|
|
60
|
+
// ============================================================
|
|
61
|
+
|
|
62
|
+
function resolveAliasReference({
|
|
63
|
+
value,
|
|
64
|
+
aliasMap,
|
|
65
|
+
globalMap,
|
|
66
|
+
stack,
|
|
67
|
+
}) {
|
|
68
|
+
if (!isReference(value)) {
|
|
69
|
+
return {
|
|
70
|
+
value,
|
|
71
|
+
sourceLayer: 'literal',
|
|
72
|
+
sourcePath: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const referencePath = cleanReference(value);
|
|
77
|
+
|
|
78
|
+
// ----------------------------------------
|
|
79
|
+
// Alias → Alias
|
|
80
|
+
// ----------------------------------------
|
|
81
|
+
|
|
82
|
+
if (aliasMap[referencePath]) {
|
|
83
|
+
if (stack.includes(referencePath)) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`❌ Circular alias reference:\n` +
|
|
86
|
+
`${[...stack, referencePath].join(' -> ')}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const nextValue =
|
|
91
|
+
getTokenValue(aliasMap[referencePath]);
|
|
92
|
+
|
|
93
|
+
const resolved = resolveAliasReference({
|
|
94
|
+
value: nextValue,
|
|
95
|
+
aliasMap,
|
|
96
|
+
globalMap,
|
|
97
|
+
stack: [
|
|
98
|
+
...stack,
|
|
99
|
+
referencePath,
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
...resolved,
|
|
105
|
+
sourceLayer: 'alias',
|
|
106
|
+
sourcePath: referencePath,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ----------------------------------------
|
|
111
|
+
// Alias → Global
|
|
112
|
+
// ----------------------------------------
|
|
113
|
+
|
|
114
|
+
if (globalMap[referencePath]) {
|
|
115
|
+
const globalValue =
|
|
116
|
+
getTokenValue(globalMap[referencePath]);
|
|
117
|
+
|
|
118
|
+
if (isReference(globalValue)) {
|
|
119
|
+
return resolveAliasReference({
|
|
120
|
+
value: globalValue,
|
|
121
|
+
aliasMap,
|
|
122
|
+
globalMap,
|
|
123
|
+
stack,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
value: globalValue,
|
|
129
|
+
sourceLayer: 'global',
|
|
130
|
+
sourcePath: referencePath,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw new Error(
|
|
135
|
+
`❌ Broken reference in alias.json:\n` +
|
|
136
|
+
`"${value}"\n` +
|
|
137
|
+
`Expected global or alias token: ${referencePath}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============================================================
|
|
142
|
+
// DART VALUE
|
|
143
|
+
// ============================================================
|
|
144
|
+
|
|
145
|
+
function generateDartValue({
|
|
146
|
+
type,
|
|
147
|
+
resolved,
|
|
148
|
+
tokenPath,
|
|
149
|
+
}) {
|
|
150
|
+
// ----------------------------------------
|
|
151
|
+
// GLOBAL REFERENCE
|
|
152
|
+
// ----------------------------------------
|
|
153
|
+
|
|
154
|
+
if (resolved.sourceLayer === 'global') {
|
|
155
|
+
return {
|
|
156
|
+
type: dartType(type),
|
|
157
|
+
value:
|
|
158
|
+
`GlobalTokens.${toDartIdentifier(
|
|
159
|
+
resolved.sourcePath
|
|
160
|
+
)}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ----------------------------------------
|
|
165
|
+
// LITERAL
|
|
166
|
+
// ----------------------------------------
|
|
167
|
+
|
|
168
|
+
if (type === 'color') {
|
|
169
|
+
const color = colorValue(
|
|
170
|
+
resolved.value
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
if (!color) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`❌ Invalid color token:\n` +
|
|
176
|
+
`${tokenPath}`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
type: 'Color',
|
|
182
|
+
value: color,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (type === 'dimension') {
|
|
187
|
+
const dimension =
|
|
188
|
+
dimensionValue(resolved.value);
|
|
189
|
+
|
|
190
|
+
if (dimension === null) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`❌ Invalid dimension token:\n` +
|
|
193
|
+
`${tokenPath}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
type: 'double',
|
|
199
|
+
value: dimension,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (type === 'string') {
|
|
204
|
+
return {
|
|
205
|
+
type: 'String',
|
|
206
|
+
value: JSON.stringify(
|
|
207
|
+
String(resolved.value)
|
|
208
|
+
),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (type === 'number') {
|
|
213
|
+
return {
|
|
214
|
+
type: 'double',
|
|
215
|
+
value: String(resolved.value),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (type === 'boolean') {
|
|
220
|
+
return {
|
|
221
|
+
type: 'bool',
|
|
222
|
+
value:
|
|
223
|
+
resolved.value ? 'true' : 'false',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
throw new Error(
|
|
228
|
+
`❌ Unsupported alias token type:\n` +
|
|
229
|
+
`${tokenPath}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function dartType(type) {
|
|
234
|
+
switch (type) {
|
|
235
|
+
case 'color':
|
|
236
|
+
return 'Color';
|
|
237
|
+
|
|
238
|
+
case 'dimension':
|
|
239
|
+
return 'double';
|
|
240
|
+
|
|
241
|
+
case 'string':
|
|
242
|
+
return 'String';
|
|
243
|
+
|
|
244
|
+
case 'number':
|
|
245
|
+
return 'double';
|
|
246
|
+
|
|
247
|
+
case 'boolean':
|
|
248
|
+
return 'bool';
|
|
249
|
+
|
|
250
|
+
default:
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Unsupported type: ${type}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = { renderAliasTokens };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const {
|
|
2
|
+
flattenTokens, getTokenValue, getTokenType,
|
|
3
|
+
isReference, cleanReference, toDartIdentifier, colorValue, dimensionValue, dartType,
|
|
4
|
+
} = require('./token-utils');
|
|
5
|
+
|
|
6
|
+
function renderBrandTokens({ brandTokens, aliasTokens, globalTokens, className }) {
|
|
7
|
+
const map = {
|
|
8
|
+
...flattenTokens(brandTokens.global || {}),
|
|
9
|
+
...flattenTokens(brandTokens.alias || {}),
|
|
10
|
+
...flattenTokens(brandTokens.component || {}),
|
|
11
|
+
};
|
|
12
|
+
// Backward-compatible flat brand files (without layer keys).
|
|
13
|
+
if (Object.keys(map).length === 0) Object.assign(map, flattenTokens(brandTokens || {}));
|
|
14
|
+
const aliasMap = flattenTokens(aliasTokens || {});
|
|
15
|
+
const globalMap = flattenTokens(globalTokens || {});
|
|
16
|
+
const body = [];
|
|
17
|
+
let needsUi = false, needsAlias = false, needsGlobal = false;
|
|
18
|
+
|
|
19
|
+
for (const [tokenPath, token] of Object.entries(map)) {
|
|
20
|
+
const type = getTokenType(token) || inferReferenceType(token, tokenPath, aliasMap, globalMap);
|
|
21
|
+
const resolved = resolve(getTokenValue(token), type, aliasMap, globalMap, []);
|
|
22
|
+
needsUi ||= resolved.type === 'Color';
|
|
23
|
+
needsAlias ||= resolved.value.startsWith('AliasTokens.');
|
|
24
|
+
needsGlobal ||= resolved.value.startsWith('GlobalTokens.');
|
|
25
|
+
body.push(` static const ${resolved.type} ${toDartIdentifier(tokenPath)} = ${resolved.value};`, '');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const out = ['// GENERATED FILE - DO NOT EDIT', ''];
|
|
29
|
+
if (needsUi) out.push("import 'dart:ui';");
|
|
30
|
+
if (needsGlobal) out.push("import '../global/global_tokens.dart';");
|
|
31
|
+
if (needsAlias) out.push("import '../alias/alias_tokens.dart';");
|
|
32
|
+
if (needsUi || needsGlobal || needsAlias) out.push('');
|
|
33
|
+
out.push(`class ${className} {`, ` ${className}._();`, '', ...body, '}','');
|
|
34
|
+
return out.join('\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function inferReferenceType(token, tokenPath, aliasMap, globalMap) {
|
|
38
|
+
const value = getTokenValue(token);
|
|
39
|
+
if (!isReference(value)) throw new Error(`❌ Brand token has no type: ${tokenPath}`);
|
|
40
|
+
const ref = cleanReference(value);
|
|
41
|
+
const target = aliasMap[ref] || globalMap[ref];
|
|
42
|
+
if (!target) throw new Error(`❌ Broken brand reference: {${ref}}`);
|
|
43
|
+
return getTokenType(target);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolve(value, type, aliasMap, globalMap, stack) {
|
|
47
|
+
if (isReference(value)) {
|
|
48
|
+
const ref = cleanReference(value);
|
|
49
|
+
if (stack.includes(ref)) throw new Error(`❌ Circular brand reference: ${[...stack, ref].join(' -> ')}`);
|
|
50
|
+
if (aliasMap[ref]) return resolve(getTokenValue(aliasMap[ref]), getTokenType(aliasMap[ref]), aliasMap, globalMap, [...stack, ref]);
|
|
51
|
+
if (globalMap[ref]) return resolve(getTokenValue(globalMap[ref]), getTokenType(globalMap[ref]), aliasMap, globalMap, [...stack, ref]);
|
|
52
|
+
throw new Error(`❌ Broken brand reference: {${ref}}`);
|
|
53
|
+
}
|
|
54
|
+
if (type === 'color') {
|
|
55
|
+
const c = colorValue(value); if (!c) throw new Error(`❌ Invalid brand color: ${value}`);
|
|
56
|
+
return { type: 'Color', value: c };
|
|
57
|
+
}
|
|
58
|
+
if (type === 'dimension') {
|
|
59
|
+
const d = dimensionValue(value); if (d === null) throw new Error(`❌ Invalid brand dimension: ${value}`);
|
|
60
|
+
return { type: 'double', value: d };
|
|
61
|
+
}
|
|
62
|
+
if (type === 'number') return { type: 'double', value: String(value) };
|
|
63
|
+
if (type === 'string') return { type: 'String', value: JSON.stringify(String(value)) };
|
|
64
|
+
if (type === 'boolean') return { type: 'bool', value: value ? 'true' : 'false' };
|
|
65
|
+
throw new Error(`❌ Unsupported brand token type: ${type}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { renderBrandTokens };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const { flattenTokens, getTokenValue, getTokenType, isReference, cleanReference, toDartIdentifier, colorValue, dimensionValue, dartType } = require('./token-utils');
|
|
2
|
+
|
|
3
|
+
function renderCategoryTokens({ tokens, globalTokens, aliasTokens, className }) {
|
|
4
|
+
const map = flattenTokens(tokens || {});
|
|
5
|
+
const globalMap = flattenTokens(globalTokens || {});
|
|
6
|
+
const aliasMap = flattenTokens(aliasTokens || {});
|
|
7
|
+
const body = [];
|
|
8
|
+
let needsUi = false, needsGlobal = false, needsAlias = false;
|
|
9
|
+
|
|
10
|
+
for (const [tokenPath, token] of Object.entries(map)) {
|
|
11
|
+
const type = getTokenType(token);
|
|
12
|
+
const raw = getTokenValue(token);
|
|
13
|
+
const expr = resolve(raw, globalMap, aliasMap, type, tokenPath);
|
|
14
|
+
needsUi ||= expr.type === 'Color';
|
|
15
|
+
needsGlobal ||= expr.value.startsWith('GlobalTokens.');
|
|
16
|
+
needsAlias ||= expr.value.startsWith('AliasTokens.');
|
|
17
|
+
body.push(` static const ${expr.type} ${toDartIdentifier(tokenPath)} = ${expr.value};`, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const out = ['// GENERATED FILE - DO NOT EDIT', ''];
|
|
21
|
+
if (needsUi) out.push("import 'dart:ui';");
|
|
22
|
+
if (needsGlobal) out.push("import '../global/global_tokens.dart';");
|
|
23
|
+
if (needsAlias) out.push("import '../alias/alias_tokens.dart';");
|
|
24
|
+
if (needsUi || needsGlobal || needsAlias) out.push('');
|
|
25
|
+
out.push(`class ${className} {`, ` ${className}._();`, '', ...body, '}','');
|
|
26
|
+
return out.join('\n');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolve(value, globalMap, aliasMap, type, tokenPath) {
|
|
30
|
+
if (isReference(value)) {
|
|
31
|
+
const p = cleanReference(value);
|
|
32
|
+
if (aliasMap[p]) return { type: dartType(type), value: `AliasTokens.${toDartIdentifier(p)}` };
|
|
33
|
+
if (globalMap[p]) return { type: dartType(type), value: `GlobalTokens.${toDartIdentifier(p)}` };
|
|
34
|
+
throw new Error(`❌ Broken reference in ${tokenPath}: {${p}}`);
|
|
35
|
+
}
|
|
36
|
+
if (type === 'color') { const c = colorValue(value); if (!c) throw new Error(`❌ Invalid color: ${value}`); return { type: 'Color', value: c }; }
|
|
37
|
+
if (type === 'dimension') { const d = dimensionValue(value); if (d === null) throw new Error(`❌ Invalid dimension: ${value}`); return { type: 'double', value: d }; }
|
|
38
|
+
if (type === 'number') return { type: 'double', value: String(value) };
|
|
39
|
+
if (type === 'string') return { type: 'String', value: JSON.stringify(String(value)) };
|
|
40
|
+
if (type === 'boolean') return { type: 'bool', value: value ? 'true' : 'false' };
|
|
41
|
+
throw new Error(`❌ Unsupported token type: ${type}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { renderCategoryTokens };
|