@nd-storybook/storybook 0.4.5 → 0.4.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nd-storybook/storybook",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
|
+
"scripts",
|
|
25
26
|
"tailwind.config.js",
|
|
26
27
|
"tailwind.tokens.js",
|
|
27
28
|
"tokens.utilities.js"
|
|
@@ -0,0 +1,1110 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
// ----------------------
|
|
4
|
+
// 🔧 Config
|
|
5
|
+
// ----------------------
|
|
6
|
+
const files = {
|
|
7
|
+
primitives: './tokens/primitives.value.tokens.json',
|
|
8
|
+
semantics: './tokens/semantic-colors.light.tokens.json',
|
|
9
|
+
semanticsDark: './tokens/semantic-colors.dark.tokens.json',
|
|
10
|
+
spacing: './tokens/semantic-layout.mobile.tokens.json',
|
|
11
|
+
effects: './tokens/effect.styles.tokens.json',
|
|
12
|
+
typography: './tokens/text.styles.tokens.json',
|
|
13
|
+
semanticBorders: './tokens/semantic-border.value.tokens.json',
|
|
14
|
+
responsiveTypography: {
|
|
15
|
+
mobile: './tokens/semantic-typography.mobile.tokens.json',
|
|
16
|
+
tablet: './tokens/semantic-typography.tablet.tokens.json',
|
|
17
|
+
desktop: './tokens/semantic-typography.desktop.tokens.json'
|
|
18
|
+
},
|
|
19
|
+
componentStates: {
|
|
20
|
+
default: './tokens/component-colors.default.tokens.json',
|
|
21
|
+
hover: './tokens/component-colors.hover.tokens.json',
|
|
22
|
+
active: './tokens/component-colors.active.tokens.json',
|
|
23
|
+
disabled: './tokens/component-colors.disabled.tokens.json'
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// ----------------------
|
|
28
|
+
// 🧠 Utilities
|
|
29
|
+
// ----------------------
|
|
30
|
+
function load(file) {
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Error loading file ${file}:`, error.message);
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Function to map font weight strings to numeric values
|
|
40
|
+
function mapFontWeight(value) {
|
|
41
|
+
const fontWeightMap = {
|
|
42
|
+
'Bold': 700,
|
|
43
|
+
'SemiBold': 600,
|
|
44
|
+
'Regular': 400,
|
|
45
|
+
'Light': 300
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return fontWeightMap[value] || value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Legacy reference mapping to handle old token references
|
|
52
|
+
const legacyReferenceMap = {
|
|
53
|
+
// Font size mappings
|
|
54
|
+
'font-size.heading.xl': '28px',
|
|
55
|
+
'font-size.heading.l': '25px',
|
|
56
|
+
'font-size.heading.m': '20px', // Should be 20px for heading.m
|
|
57
|
+
'font-size.heading.s': '18px', // Should be 18px for heading.s
|
|
58
|
+
'font-size.heading.xs': '14px', // Should be 14px for heading.xs
|
|
59
|
+
'font-size.body.xxl': '28px', // Should be 28px
|
|
60
|
+
'font-size.body.xl': '22px', // Should be 22px for body.xl
|
|
61
|
+
'font-size.body.l': '20px', // Should be 20px for body.l
|
|
62
|
+
'font-size.body.m': '18px', // Should be 18px for body.m
|
|
63
|
+
'font-size.body.s': '16px', // Should be 16px for body.s
|
|
64
|
+
'font-size.body.xs': '12px',
|
|
65
|
+
'font-size.body.drop-cap': '96px', // Should be 96px based on your tokens
|
|
66
|
+
'font-size.meta': '14px',
|
|
67
|
+
|
|
68
|
+
// Font family mappings - CORRECTED!
|
|
69
|
+
'typography.font-family.gulliver semibold': "Gulliver Web, Georgia, serif", // ✅ Fixed - no quotes wrapping
|
|
70
|
+
'typography.font-family.gulliver_semibold': "Gulliver Web, Georgia, serif", // ✅ Added alternate key
|
|
71
|
+
'typography.font-family.gulliver': "Gulliver Web, Georgia, serif", // ✅ FIXED - This was the main issue!
|
|
72
|
+
'typography.font-family.fira-sans': "Fira Sans", // ✅ Fixed - removed quotes
|
|
73
|
+
'typography.font-family.montserrat': "Montserrat", // ✅ Fixed - removed quotes
|
|
74
|
+
'typography.font-family.abril-fatface': "Abril Fatface", // ✅ Fixed - removed quotes
|
|
75
|
+
|
|
76
|
+
// Font weight mappings
|
|
77
|
+
'typography.font-weight.light': '300',
|
|
78
|
+
'typography.font-weight.regular': '400',
|
|
79
|
+
'typography.font-weight.semibold': '600', // Added semibold
|
|
80
|
+
'typography.font-weight.bold': '700',
|
|
81
|
+
|
|
82
|
+
// Letter spacing mappings - CORRECTED!
|
|
83
|
+
'typography.letter-spacing.0': '0px', // Changed from 0% to 0px
|
|
84
|
+
'typography.letter-spacing.s': '1.6px', // Should be 1.6px based on your primitives
|
|
85
|
+
'typography.letter-spacing.m': '2.4px', // Should be 2.4px based on your primitives
|
|
86
|
+
'typography.letter-spacing.l': '4px'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function flatten(obj, path = [], result = {}) {
|
|
90
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
91
|
+
const current = [...path, key];
|
|
92
|
+
if (val && typeof val === 'object' && '$value' in val) {
|
|
93
|
+
result[current.join('.')] = val;
|
|
94
|
+
} else if (typeof val === 'object' && val !== null) {
|
|
95
|
+
flatten(val, current, result);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolveReference(ref, flatTokens, visited = new Set()) {
|
|
102
|
+
if (!ref || typeof ref !== 'string') return ref;
|
|
103
|
+
|
|
104
|
+
// If it's not a reference, return as is
|
|
105
|
+
if (!ref.startsWith('{') || !ref.endsWith('}')) return ref;
|
|
106
|
+
|
|
107
|
+
const path = ref.replace(/[{}]/g, '');
|
|
108
|
+
|
|
109
|
+
// Prevent circular references
|
|
110
|
+
if (visited.has(path)) {
|
|
111
|
+
console.warn(`Circular reference detected: ${path}`);
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
visited.add(path);
|
|
116
|
+
|
|
117
|
+
// FIRST: Try to find the token in the actual flattened tokens
|
|
118
|
+
let token = flatTokens[path];
|
|
119
|
+
|
|
120
|
+
// If not found, try some common variations
|
|
121
|
+
if (!token) {
|
|
122
|
+
// Try with different prefixes/formats
|
|
123
|
+
const variations = [
|
|
124
|
+
path,
|
|
125
|
+
path.replace(/^font-size\./, 'fontSize.'),
|
|
126
|
+
path.replace(/^typography\.font-family\./, 'fontFamily.'),
|
|
127
|
+
path.replace(/\s+/g, '_'), // Replace spaces with underscores
|
|
128
|
+
path.replace(/\s+/g, '-'), // Replace spaces with hyphens
|
|
129
|
+
path.replace(/\./g, '-').toLowerCase(),
|
|
130
|
+
// Handle the specific case of gulliver_semibold
|
|
131
|
+
path.replace('gulliver semibold', 'gulliver_semibold'),
|
|
132
|
+
path.replace('gulliver-semibold', 'gulliver_semibold')
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
for (const variant of variations) {
|
|
136
|
+
if (flatTokens[variant]) {
|
|
137
|
+
token = flatTokens[variant];
|
|
138
|
+
console.log(`Found token via variation: ${path} -> ${variant}`);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// If we found the token, use its actual value
|
|
145
|
+
if (token && token.$value) {
|
|
146
|
+
const value = token.$value;
|
|
147
|
+
|
|
148
|
+
// If the value is another reference, resolve it recursively
|
|
149
|
+
if (typeof value === 'string' && value.startsWith('{') && value.endsWith('}')) {
|
|
150
|
+
return resolveReference(value, flatTokens, visited);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// For font weights, convert string to number if needed
|
|
154
|
+
if (path.includes('font-weight')) {
|
|
155
|
+
return mapFontWeight(value);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(`✅ Resolved ${ref} -> ${value}`);
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ONLY use legacy map as a last resort fallback
|
|
163
|
+
if (legacyReferenceMap[path]) {
|
|
164
|
+
console.warn(`⚠️ Using legacy fallback for ${path} (token not found in source)`);
|
|
165
|
+
console.warn(` This should be fixed - token should exist in your JSON files`);
|
|
166
|
+
return legacyReferenceMap[path];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.error(`❌ Reference not found and no fallback: ${path}`);
|
|
170
|
+
console.error(` Available font-family tokens:`,
|
|
171
|
+
Object.keys(flatTokens).filter(k => k.includes('font-family')));
|
|
172
|
+
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function toVar(name) {
|
|
177
|
+
return `var(--${name})`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function kebab(str) {
|
|
181
|
+
return str
|
|
182
|
+
.replace(/\./g, '-') // Replace dots with hyphens
|
|
183
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
184
|
+
.replace(/\+/g, '-plus-') // Replace + with -plus-
|
|
185
|
+
.replace(/[^a-zA-Z0-9-]/g, '-') // Replace other special chars with hyphens
|
|
186
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2') // Add hyphen between camelCase
|
|
187
|
+
.replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
|
|
188
|
+
.toLowerCase(); // Convert to lowercase
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Deep merge utility to properly combine nested objects without overwriting
|
|
192
|
+
function deepMerge(target, source) {
|
|
193
|
+
const result = { ...target };
|
|
194
|
+
for (const key of Object.keys(source)) {
|
|
195
|
+
if (
|
|
196
|
+
source[key] &&
|
|
197
|
+
typeof source[key] === 'object' &&
|
|
198
|
+
!('$value' in source[key]) &&
|
|
199
|
+
!Array.isArray(source[key])
|
|
200
|
+
) {
|
|
201
|
+
// If both are objects (and not token values), merge recursively
|
|
202
|
+
result[key] = deepMerge(result[key] || {}, source[key]);
|
|
203
|
+
} else {
|
|
204
|
+
// Otherwise, use source value
|
|
205
|
+
result[key] = source[key];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ----------------------
|
|
212
|
+
// 🚀 Build All Token Sets
|
|
213
|
+
// ----------------------
|
|
214
|
+
console.log('🔄 Loading and processing token files...');
|
|
215
|
+
|
|
216
|
+
// Load all base tokens using deep merge to preserve nested structures
|
|
217
|
+
// (e.g., border.border-width from primitives AND border.gray from semantics)
|
|
218
|
+
let baseTokens = load(files.primitives);
|
|
219
|
+
baseTokens = deepMerge(baseTokens, load(files.semantics));
|
|
220
|
+
baseTokens = deepMerge(baseTokens, load(files.effects));
|
|
221
|
+
baseTokens = deepMerge(baseTokens, load(files.typography));
|
|
222
|
+
|
|
223
|
+
const flattenedBase = flatten(baseTokens);
|
|
224
|
+
|
|
225
|
+
const primitiveColors = {};
|
|
226
|
+
const semanticColors = {};
|
|
227
|
+
const cssVarMap = {};
|
|
228
|
+
const primitiveFontFamilies = {};
|
|
229
|
+
const primitiveFontSizes = {};
|
|
230
|
+
const primitiveFontWeights = {};
|
|
231
|
+
const primitiveLetterSpacings = {};
|
|
232
|
+
|
|
233
|
+
// Font family fallbacks mapping
|
|
234
|
+
const fontFamilyFallbacks = {
|
|
235
|
+
'gulliver': '"Gulliver Web", Georgia, serif',
|
|
236
|
+
'fira-sans': '"Fira Sans", Arial, sans-serif',
|
|
237
|
+
'montserrat': '"Montserrat", "Helvetica Neue", Arial, sans-serif',
|
|
238
|
+
'abril-fatface': '"Abril Fatface", "Playfair Display", Georgia, serif'
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
for (const [tokenPath, token] of Object.entries(flattenedBase)) {
|
|
242
|
+
// Process font families
|
|
243
|
+
if (tokenPath.includes('font-family') || tokenPath.includes('fontFamily')) {
|
|
244
|
+
const fontName = tokenPath.split('.').pop();
|
|
245
|
+
|
|
246
|
+
// Skip gulliver-semibold since it should just use gulliver
|
|
247
|
+
if (fontName.toLowerCase().includes('gulliver') && fontName.toLowerCase().includes('semibold')) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const kebabName = kebab(fontName);
|
|
252
|
+
const fontKey = `font-family-${kebabName}`;
|
|
253
|
+
|
|
254
|
+
// Use fallback if available, otherwise use the token value
|
|
255
|
+
const fontValue = fontFamilyFallbacks[kebabName] || token.$value;
|
|
256
|
+
|
|
257
|
+
primitiveFontFamilies[kebabName] = toVar(fontKey);
|
|
258
|
+
cssVarMap[fontKey] = fontValue;
|
|
259
|
+
}
|
|
260
|
+
// Process font weights
|
|
261
|
+
else if (tokenPath.includes('font-weight') || tokenPath.includes('fontWeight')) {
|
|
262
|
+
const weightName = tokenPath.split('.').pop();
|
|
263
|
+
const weightKey = `font-weight-${kebab(weightName)}`;
|
|
264
|
+
primitiveFontWeights[kebab(weightName)] = toVar(weightKey);
|
|
265
|
+
cssVarMap[weightKey] = mapFontWeight(token.$value);
|
|
266
|
+
}
|
|
267
|
+
// Process letter spacings
|
|
268
|
+
else if (tokenPath.includes('letter-spacing') || tokenPath.includes('letterSpacing')) {
|
|
269
|
+
const spacingName = tokenPath.split('.').pop();
|
|
270
|
+
const spacingKey = `letter-spacing-${kebab(spacingName)}`;
|
|
271
|
+
primitiveLetterSpacings[kebab(spacingName)] = toVar(spacingKey);
|
|
272
|
+
cssVarMap[spacingKey] = token.$value;
|
|
273
|
+
}
|
|
274
|
+
// Process font sizes
|
|
275
|
+
else if (tokenPath.includes('font-size') || tokenPath.includes('fontSize')) {
|
|
276
|
+
const sizeName = tokenPath.split('.').pop();
|
|
277
|
+
const sizeKey = `font-size-${kebab(sizeName)}`;
|
|
278
|
+
primitiveFontSizes[kebab(sizeName)] = toVar(sizeKey);
|
|
279
|
+
cssVarMap[sizeKey] = token.$value;
|
|
280
|
+
|
|
281
|
+
// Add additional mappings for common references
|
|
282
|
+
if (sizeName.includes('heading') || sizeName.includes('body')) {
|
|
283
|
+
// Create mappings for the old reference format
|
|
284
|
+
const category = sizeName.includes('heading') ? 'heading' : 'body';
|
|
285
|
+
const size = sizeName.split('-').pop();
|
|
286
|
+
cssVarMap[`font-size-${category}-${size}`] = token.$value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Create meta font size tokens for backward compatibility
|
|
291
|
+
if (tokenPath.includes('meta') && (tokenPath.includes('font-size') || tokenPath.includes('fontSize'))) {
|
|
292
|
+
cssVarMap['font-size-meta'] = token.$value;
|
|
293
|
+
cssVarMap['font-size-meta-font-size-meta'] = token.$value;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
for (const [tokenPath, token] of Object.entries(flattenedBase)) {
|
|
298
|
+
if (token?.$type === 'color') {
|
|
299
|
+
const raw = token.$value;
|
|
300
|
+
const resolved = raw.startsWith('{')
|
|
301
|
+
? resolveReference(raw, flattenedBase) ?? raw
|
|
302
|
+
: raw;
|
|
303
|
+
const kebabKey = kebab(tokenPath);
|
|
304
|
+
|
|
305
|
+
// Always prefix with "color-" regardless of whether it's primitive or semantic
|
|
306
|
+
const prefixedKey = tokenPath.startsWith('color.') ? kebabKey : `color-${kebabKey}`;
|
|
307
|
+
|
|
308
|
+
if (tokenPath.startsWith('color.')) {
|
|
309
|
+
const tailwindKey = kebabKey.replace(/^color-/, '');
|
|
310
|
+
primitiveColors[tailwindKey] = toVar(prefixedKey);
|
|
311
|
+
} else {
|
|
312
|
+
semanticColors[kebabKey] = toVar(prefixedKey);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
cssVarMap[prefixedKey] = resolved;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ----------------------
|
|
320
|
+
// 🌑 Process Dark Mode Colors
|
|
321
|
+
// ----------------------
|
|
322
|
+
console.log('🔄 Processing dark mode colors...');
|
|
323
|
+
|
|
324
|
+
const darkModeColors = {};
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const darkSemantics = load(files.semanticsDark);
|
|
328
|
+
const flattenedDark = flatten(darkSemantics);
|
|
329
|
+
|
|
330
|
+
for (const [tokenPath, token] of Object.entries(flattenedDark)) {
|
|
331
|
+
if (token?.$type === 'color') {
|
|
332
|
+
const raw = token.$value;
|
|
333
|
+
const resolved = raw.startsWith('{')
|
|
334
|
+
? resolveReference(raw, {...flattenedBase, ...flattenedDark}) ?? raw
|
|
335
|
+
: raw;
|
|
336
|
+
const kebabKey = kebab(tokenPath);
|
|
337
|
+
|
|
338
|
+
// Use the same key structure as light mode
|
|
339
|
+
const prefixedKey = tokenPath.startsWith('color.') ? kebabKey : `color-${kebabKey}`;
|
|
340
|
+
|
|
341
|
+
darkModeColors[prefixedKey] = resolved;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
console.log(`✅ Processed ${Object.keys(darkModeColors).length} dark mode color tokens`);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error(`Error processing dark mode colors: ${error.message}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ----------------------
|
|
351
|
+
// 🎯 Process Component Tokens
|
|
352
|
+
// ----------------------
|
|
353
|
+
console.log('🔄 Processing component tokens...');
|
|
354
|
+
|
|
355
|
+
const componentColors = {};
|
|
356
|
+
const utilityClasses = {};
|
|
357
|
+
|
|
358
|
+
for (const [state, file] of Object.entries(files.componentStates)) {
|
|
359
|
+
const component = load(file);
|
|
360
|
+
|
|
361
|
+
(function process(obj, prefix = '') {
|
|
362
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
363
|
+
const path = prefix ? `${prefix}-${key}` : key;
|
|
364
|
+
|
|
365
|
+
if (val?.$value) {
|
|
366
|
+
const raw = val.$value;
|
|
367
|
+
const resolved = raw.startsWith('{')
|
|
368
|
+
? resolveReference(raw, flattenedBase) ?? raw
|
|
369
|
+
: raw;
|
|
370
|
+
|
|
371
|
+
const fullVarName = `${path}-${state}`;
|
|
372
|
+
const tailwindKey = state === 'default' ? path : `${path}-${state}`;
|
|
373
|
+
|
|
374
|
+
// Prefix component colors with "color-" instead of "component-"
|
|
375
|
+
const prefixedVarName = `color-${fullVarName}`;
|
|
376
|
+
componentColors[tailwindKey] = toVar(prefixedVarName);
|
|
377
|
+
cssVarMap[prefixedVarName] = resolved;
|
|
378
|
+
|
|
379
|
+
// Generate utility classes based on token name patterns
|
|
380
|
+
if (path.includes('bg')) {
|
|
381
|
+
utilityClasses[`.bg-${tailwindKey}`] = {backgroundColor: `var(--${prefixedVarName})`};
|
|
382
|
+
} else if (path.includes('text')) {
|
|
383
|
+
utilityClasses[`.text-${tailwindKey}`] = {color: `var(--${prefixedVarName})`};
|
|
384
|
+
} else if (path.includes('border')) {
|
|
385
|
+
utilityClasses[`.border-${tailwindKey}`] = {borderColor: `var(--${prefixedVarName})`};
|
|
386
|
+
}
|
|
387
|
+
} else if (typeof val === 'object' && val !== null) {
|
|
388
|
+
process(val, path);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
})(component);
|
|
392
|
+
}
|
|
393
|
+
// -------------------------
|
|
394
|
+
// 🌑 Process Shadow Tokens
|
|
395
|
+
// -------------------------
|
|
396
|
+
console.log('🔄 Processing shadow tokens...');
|
|
397
|
+
|
|
398
|
+
const primitiveBoxShadows = {};
|
|
399
|
+
const shadowUtilities = {};
|
|
400
|
+
|
|
401
|
+
// Helper function to convert shadow object to CSS string
|
|
402
|
+
function shadowObjectToCSS(shadowObj) {
|
|
403
|
+
if (typeof shadowObj === 'string') return shadowObj;
|
|
404
|
+
|
|
405
|
+
const x = shadowObj.x || shadowObj.offsetX || '0px';
|
|
406
|
+
const y = shadowObj.y || shadowObj.offsetY || '0px';
|
|
407
|
+
const blur = shadowObj.blur || shadowObj.blurRadius || '0px';
|
|
408
|
+
const spread = shadowObj.spread || shadowObj.spreadRadius || '0px';
|
|
409
|
+
const color = shadowObj.color || 'rgba(0, 0, 0, 0.1)';
|
|
410
|
+
const inset = shadowObj.inset ? 'inset ' : '';
|
|
411
|
+
|
|
412
|
+
return `${inset}${x} ${y} ${blur} ${spread} ${color}`.trim();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Process shadow tokens from all files
|
|
416
|
+
for (const [tokenPath, token] of Object.entries(flattenedBase)) {
|
|
417
|
+
// Check for shadows at root level (like shadow-s, shadow-m, shadow-l)
|
|
418
|
+
if ((tokenPath.includes('shadow') || tokenPath.includes('Shadow')) && token?.$type === 'shadow' && token?.$value !== undefined) {
|
|
419
|
+
console.log(` Processing shadow token: ${tokenPath}`);
|
|
420
|
+
|
|
421
|
+
let resolved;
|
|
422
|
+
const raw = token.$value;
|
|
423
|
+
|
|
424
|
+
// Handle different value formats
|
|
425
|
+
if (Array.isArray(raw)) {
|
|
426
|
+
// Handle array of shadows (multiple shadows)
|
|
427
|
+
resolved = raw.map(shadow => {
|
|
428
|
+
const shadowStr = shadowObjectToCSS(shadow);
|
|
429
|
+
return shadowStr.replace(/\{([^}]+)\}/g, (match, ref) => {
|
|
430
|
+
const colorValue = resolveReference(match, flattenedBase);
|
|
431
|
+
return colorValue || match;
|
|
432
|
+
});
|
|
433
|
+
}).join(', ');
|
|
434
|
+
} else if (typeof raw === 'object' && !raw.$value) {
|
|
435
|
+
// Handle single object shadow
|
|
436
|
+
resolved = shadowObjectToCSS(raw);
|
|
437
|
+
resolved = resolved.replace(/\{([^}]+)\}/g, (match, ref) => {
|
|
438
|
+
const colorValue = resolveReference(match, flattenedBase);
|
|
439
|
+
return colorValue || match;
|
|
440
|
+
});
|
|
441
|
+
} else if (typeof raw === 'string') {
|
|
442
|
+
// Handle string format shadow
|
|
443
|
+
resolved = raw;
|
|
444
|
+
if (raw.includes('{')) {
|
|
445
|
+
resolved = raw.replace(/\{([^}]+)\}/g, (match, ref) => {
|
|
446
|
+
const colorValue = resolveReference(match, flattenedBase);
|
|
447
|
+
return colorValue || match;
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Extract shadow name from the path
|
|
453
|
+
let shadowName = tokenPath;
|
|
454
|
+
|
|
455
|
+
// Handle different naming patterns
|
|
456
|
+
if (tokenPath.startsWith('shadow.')) {
|
|
457
|
+
shadowName = tokenPath.replace('shadow.', '');
|
|
458
|
+
} else if (tokenPath.startsWith('shadow-')) {
|
|
459
|
+
shadowName = tokenPath.replace('shadow-', '');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const originalKey = kebab(shadowName);
|
|
463
|
+
const kebabKey = `box-shadow-${originalKey}`;
|
|
464
|
+
|
|
465
|
+
// Store as CSS variable reference
|
|
466
|
+
primitiveBoxShadows[originalKey] = `var(--${kebabKey})`;
|
|
467
|
+
// Add to cssVarMap for CSS variable generation
|
|
468
|
+
cssVarMap[kebabKey] = resolved;
|
|
469
|
+
|
|
470
|
+
// Generate shadow utility classes
|
|
471
|
+
shadowUtilities[`.shadow-${originalKey}`] = {boxShadow: `var(--${kebabKey})`};
|
|
472
|
+
|
|
473
|
+
console.log(` ✅ Processed shadow: ${originalKey} = ${resolved}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
//console.log(` Total shadows processed: ${Object.keys(primitiveBoxShadows).length}`);
|
|
477
|
+
//console.log(` Shadows found: ${Object.keys(primitiveBoxShadows).join(', ')}`);
|
|
478
|
+
|
|
479
|
+
// ----------------------
|
|
480
|
+
// 📏 Process Spacing Tokens
|
|
481
|
+
// ----------------------
|
|
482
|
+
console.log('🔄 Processing spacing tokens...');
|
|
483
|
+
|
|
484
|
+
const spacing = {};
|
|
485
|
+
const spacingUtilities = {};
|
|
486
|
+
const primitiveBorderWidth = {};
|
|
487
|
+
const primitiveBorderRadius = {};
|
|
488
|
+
const semanticBorderWidth = {};
|
|
489
|
+
const semanticBorderRadius = {};
|
|
490
|
+
const borderUtilities = {};
|
|
491
|
+
|
|
492
|
+
// Process spacing tokens from semantic-spacing file
|
|
493
|
+
try {
|
|
494
|
+
const rawSpacing = load(files.spacing);
|
|
495
|
+
const flattenedSpacing = flatten({spacing: rawSpacing}, []);
|
|
496
|
+
|
|
497
|
+
for (const [tokenPath, token] of Object.entries(flattenedSpacing)) {
|
|
498
|
+
if (token?.$type === 'dimension') {
|
|
499
|
+
const raw = token.$value;
|
|
500
|
+
const resolved = raw.startsWith('{')
|
|
501
|
+
? resolveReference(raw, {...flattenedBase, ...flattenedSpacing})
|
|
502
|
+
: raw;
|
|
503
|
+
|
|
504
|
+
// Skip if reference couldn't be resolved
|
|
505
|
+
if (!resolved) {
|
|
506
|
+
console.warn(`Could not resolve reference: ${raw} for ${tokenPath}`);
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const kebabKey = kebab(tokenPath);
|
|
511
|
+
const varName = `--${kebabKey}`;
|
|
512
|
+
|
|
513
|
+
spacing[kebabKey] = `var(${varName})`;
|
|
514
|
+
cssVarMap[kebabKey] = resolved;
|
|
515
|
+
|
|
516
|
+
// Generate spacing utility classes
|
|
517
|
+
spacingUtilities[`.p-${kebabKey}`] = {padding: `var(${varName})`};
|
|
518
|
+
spacingUtilities[`.px-${kebabKey}`] = {paddingLeft: `var(${varName})`, paddingRight: `var(${varName})`};
|
|
519
|
+
spacingUtilities[`.py-${kebabKey}`] = {paddingTop: `var(${varName})`, paddingBottom: `var(${varName})`};
|
|
520
|
+
spacingUtilities[`.pt-${kebabKey}`] = {paddingTop: `var(${varName})`};
|
|
521
|
+
spacingUtilities[`.pr-${kebabKey}`] = {paddingRight: `var(${varName})`};
|
|
522
|
+
spacingUtilities[`.pb-${kebabKey}`] = {paddingBottom: `var(${varName})`};
|
|
523
|
+
spacingUtilities[`.pl-${kebabKey}`] = {paddingLeft: `var(${varName})`};
|
|
524
|
+
|
|
525
|
+
spacingUtilities[`.m-${kebabKey}`] = {margin: `var(${varName})`};
|
|
526
|
+
spacingUtilities[`.mx-${kebabKey}`] = {marginLeft: `var(${varName})`, marginRight: `var(${varName})`};
|
|
527
|
+
spacingUtilities[`.my-${kebabKey}`] = {marginTop: `var(${varName})`, marginBottom: `var(${varName})`};
|
|
528
|
+
spacingUtilities[`.mt-${kebabKey}`] = {marginTop: `var(${varName})`};
|
|
529
|
+
spacingUtilities[`.mr-${kebabKey}`] = {marginRight: `var(${varName})`};
|
|
530
|
+
spacingUtilities[`.mb-${kebabKey}`] = {marginBottom: `var(${varName})`};
|
|
531
|
+
spacingUtilities[`.ml-${kebabKey}`] = {marginLeft: `var(${varName})`};
|
|
532
|
+
|
|
533
|
+
spacingUtilities[`.gap-${kebabKey}`] = {gap: `var(${varName})`};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
} catch (error) {
|
|
537
|
+
console.warn(`Error processing semantic spacing: ${error.message}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Also process spacing tokens from primitives file
|
|
541
|
+
for (const [tokenPath, token] of Object.entries(flattenedBase)) {
|
|
542
|
+
if (tokenPath.startsWith('spacing.') && token?.$type === 'dimension') {
|
|
543
|
+
const raw = token.$value;
|
|
544
|
+
const resolved = raw.startsWith('{')
|
|
545
|
+
? resolveReference(raw, flattenedBase)
|
|
546
|
+
: raw;
|
|
547
|
+
|
|
548
|
+
// Skip if reference couldn't be resolved
|
|
549
|
+
if (!resolved) {
|
|
550
|
+
console.warn(`Could not resolve reference: ${raw} for ${tokenPath}`);
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const spacingName = tokenPath.split('.').pop();
|
|
555
|
+
// Use the original name as the key (without spacing- prefix)
|
|
556
|
+
const originalKey = kebab(spacingName);
|
|
557
|
+
// But keep the CSS variable name prefixed
|
|
558
|
+
const kebabKey = `spacing-${originalKey}`;
|
|
559
|
+
const varName = `--${kebabKey}`;
|
|
560
|
+
|
|
561
|
+
// Use the original name as the key in the spacing object
|
|
562
|
+
spacing[originalKey] = `var(${varName})`;
|
|
563
|
+
cssVarMap[kebabKey] = resolved;
|
|
564
|
+
|
|
565
|
+
// Generate spacing utility classes with the original name
|
|
566
|
+
spacingUtilities[`.p-${originalKey}`] = {padding: `var(${varName})`};
|
|
567
|
+
spacingUtilities[`.px-${originalKey}`] = {paddingLeft: `var(${varName})`, paddingRight: `var(${varName})`};
|
|
568
|
+
spacingUtilities[`.py-${originalKey}`] = {paddingTop: `var(${varName})`, paddingBottom: `var(${varName})`};
|
|
569
|
+
spacingUtilities[`.pt-${originalKey}`] = {paddingTop: `var(${varName})`};
|
|
570
|
+
spacingUtilities[`.pr-${originalKey}`] = {paddingRight: `var(${varName})`};
|
|
571
|
+
spacingUtilities[`.pb-${originalKey}`] = {paddingBottom: `var(${varName})`};
|
|
572
|
+
spacingUtilities[`.pl-${originalKey}`] = {paddingLeft: `var(${varName})`};
|
|
573
|
+
|
|
574
|
+
spacingUtilities[`.m-${originalKey}`] = {margin: `var(${varName})`};
|
|
575
|
+
spacingUtilities[`.mx-${originalKey}`] = {marginLeft: `var(${varName})`, marginRight: `var(${varName})`};
|
|
576
|
+
spacingUtilities[`.my-${originalKey}`] = {marginTop: `var(${varName})`, marginBottom: `var(${varName})`};
|
|
577
|
+
spacingUtilities[`.mt-${originalKey}`] = {marginTop: `var(${varName})`};
|
|
578
|
+
spacingUtilities[`.mr-${originalKey}`] = {marginRight: `var(${varName})`};
|
|
579
|
+
spacingUtilities[`.mb-${originalKey}`] = {marginBottom: `var(${varName})`};
|
|
580
|
+
spacingUtilities[`.ml-${originalKey}`] = {marginLeft: `var(${varName})`};
|
|
581
|
+
|
|
582
|
+
spacingUtilities[`.gap-${originalKey}`] = {gap: `var(${varName})`};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ----------------------
|
|
587
|
+
// 🔲 Process Border Tokens
|
|
588
|
+
// ----------------------
|
|
589
|
+
console.log('🔄 Processing border tokens...');
|
|
590
|
+
|
|
591
|
+
// Process border tokens from primitives file
|
|
592
|
+
for (const [tokenPath, token] of Object.entries(flattenedBase)) {
|
|
593
|
+
// Process border width tokens
|
|
594
|
+
if (tokenPath.startsWith('border.border-width.') && token?.$type === 'dimension') {
|
|
595
|
+
const raw = token.$value;
|
|
596
|
+
const resolved = raw.startsWith('{')
|
|
597
|
+
? resolveReference(raw, flattenedBase)
|
|
598
|
+
: raw;
|
|
599
|
+
|
|
600
|
+
// Skip if reference couldn't be resolved
|
|
601
|
+
if (!resolved) {
|
|
602
|
+
console.warn(`Could not resolve reference: ${raw} for ${tokenPath}`);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const widthName = tokenPath.split('.').pop();
|
|
607
|
+
const originalKey = kebab(widthName);
|
|
608
|
+
const kebabKey = `border-width-${originalKey}`;
|
|
609
|
+
|
|
610
|
+
// Store as CSS variable reference instead of raw value
|
|
611
|
+
primitiveBorderWidth[originalKey] = `var(--${kebabKey})`;
|
|
612
|
+
// Make sure to add to cssVarMap for CSS variable generation
|
|
613
|
+
cssVarMap[kebabKey] = resolved;
|
|
614
|
+
|
|
615
|
+
// Generate border width utility classes
|
|
616
|
+
borderUtilities[`.border-${originalKey}`] = {borderWidth: `var(--${kebabKey})`};
|
|
617
|
+
borderUtilities[`.border-t-${originalKey}`] = {borderTopWidth: `var(--${kebabKey})`};
|
|
618
|
+
borderUtilities[`.border-r-${originalKey}`] = {borderRightWidth: `var(--${kebabKey})`};
|
|
619
|
+
borderUtilities[`.border-b-${originalKey}`] = {borderBottomWidth: `var(--${kebabKey})`};
|
|
620
|
+
borderUtilities[`.border-l-${originalKey}`] = {borderLeftWidth: `var(--${kebabKey})`};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Process border radius tokens
|
|
624
|
+
if (tokenPath.startsWith('border.border-radius.') && token?.$type === 'dimension') {
|
|
625
|
+
const raw = token.$value;
|
|
626
|
+
const resolved = raw.startsWith('{')
|
|
627
|
+
? resolveReference(raw, flattenedBase)
|
|
628
|
+
: raw;
|
|
629
|
+
|
|
630
|
+
// Skip if reference couldn't be resolved
|
|
631
|
+
if (!resolved) {
|
|
632
|
+
console.warn(`Could not resolve reference: ${raw} for ${tokenPath}`);
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const radiusName = tokenPath.split('.').pop();
|
|
637
|
+
const originalKey = kebab(radiusName);
|
|
638
|
+
const kebabKey = `border-radius-${originalKey}`;
|
|
639
|
+
|
|
640
|
+
// Store as CSS variable reference instead of raw value
|
|
641
|
+
primitiveBorderRadius[originalKey] = `var(--${kebabKey})`;
|
|
642
|
+
// Make sure to add to cssVarMap for CSS variable generation
|
|
643
|
+
cssVarMap[kebabKey] = resolved;
|
|
644
|
+
|
|
645
|
+
// Generate border radius utility classes
|
|
646
|
+
borderUtilities[`.rounded-${originalKey}`] = {borderRadius: `var(--${kebabKey})`};
|
|
647
|
+
borderUtilities[`.rounded-t-${originalKey}`] = {
|
|
648
|
+
borderTopLeftRadius: `var(--${kebabKey})`,
|
|
649
|
+
borderTopRightRadius: `var(--${kebabKey})`
|
|
650
|
+
};
|
|
651
|
+
borderUtilities[`.rounded-r-${originalKey}`] = {
|
|
652
|
+
borderTopRightRadius: `var(--${kebabKey})`,
|
|
653
|
+
borderBottomRightRadius: `var(--${kebabKey})`
|
|
654
|
+
};
|
|
655
|
+
borderUtilities[`.rounded-b-${originalKey}`] = {
|
|
656
|
+
borderBottomLeftRadius: `var(--${kebabKey})`,
|
|
657
|
+
borderBottomRightRadius: `var(--${kebabKey})`
|
|
658
|
+
};
|
|
659
|
+
borderUtilities[`.rounded-l-${originalKey}`] = {
|
|
660
|
+
borderTopLeftRadius: `var(--${kebabKey})`,
|
|
661
|
+
borderBottomLeftRadius: `var(--${kebabKey})`
|
|
662
|
+
};
|
|
663
|
+
borderUtilities[`.rounded-tl-${originalKey}`] = {borderTopLeftRadius: `var(--${kebabKey})`};
|
|
664
|
+
borderUtilities[`.rounded-tr-${originalKey}`] = {borderTopRightRadius: `var(--${kebabKey})`};
|
|
665
|
+
borderUtilities[`.rounded-bl-${originalKey}`] = {borderBottomLeftRadius: `var(--${kebabKey})`};
|
|
666
|
+
borderUtilities[`.rounded-br-${originalKey}`] = {borderBottomRightRadius: `var(--${kebabKey})`};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// ----------------------
|
|
671
|
+
// 🔲 Process Semantic Border Tokens
|
|
672
|
+
// ----------------------
|
|
673
|
+
console.log('🔄 Processing semantic border tokens...');
|
|
674
|
+
|
|
675
|
+
try {
|
|
676
|
+
const semanticBorders = load(files.semanticBorders);
|
|
677
|
+
|
|
678
|
+
// Process semantic border-width tokens
|
|
679
|
+
if (semanticBorders['border-width']) {
|
|
680
|
+
for (const [key, token] of Object.entries(semanticBorders['border-width'])) {
|
|
681
|
+
if (token?.$type === 'dimension') {
|
|
682
|
+
const raw = token.$value;
|
|
683
|
+
const resolved = raw.startsWith('{')
|
|
684
|
+
? resolveReference(raw, flattenedBase)
|
|
685
|
+
: raw;
|
|
686
|
+
|
|
687
|
+
if (!resolved) {
|
|
688
|
+
console.warn(`Could not resolve reference: ${raw} for border-width-${key}`);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const kebabKey = `border-width-${kebab(key)}`;
|
|
693
|
+
|
|
694
|
+
// Store as CSS variable reference
|
|
695
|
+
semanticBorderWidth[kebab(key)] = `var(--${kebabKey})`;
|
|
696
|
+
cssVarMap[kebabKey] = resolved;
|
|
697
|
+
|
|
698
|
+
// Generate border width utility class
|
|
699
|
+
borderUtilities[`.border-${kebab(key)}`] = {borderWidth: `var(--${kebabKey})`};
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Process semantic border-radius tokens
|
|
705
|
+
if (semanticBorders['border-radius']) {
|
|
706
|
+
for (const [key, token] of Object.entries(semanticBorders['border-radius'])) {
|
|
707
|
+
if (token?.$type === 'dimension') {
|
|
708
|
+
const raw = token.$value;
|
|
709
|
+
const resolved = raw.startsWith('{')
|
|
710
|
+
? resolveReference(raw, flattenedBase)
|
|
711
|
+
: raw;
|
|
712
|
+
|
|
713
|
+
if (!resolved) {
|
|
714
|
+
console.warn(`Could not resolve reference: ${raw} for border-radius-${key}`);
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const kebabKey = `border-radius-${kebab(key)}`;
|
|
719
|
+
|
|
720
|
+
// Store as CSS variable reference
|
|
721
|
+
semanticBorderRadius[kebab(key)] = `var(--${kebabKey})`;
|
|
722
|
+
cssVarMap[kebabKey] = resolved;
|
|
723
|
+
|
|
724
|
+
// Generate border radius utility class
|
|
725
|
+
borderUtilities[`.rounded-${kebab(key)}`] = {borderRadius: `var(--${kebabKey})`};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
} catch (error) {
|
|
730
|
+
console.error(`Error processing semantic borders: ${error.message}`);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// ----------------------
|
|
734
|
+
// 📝 Process Typography Tokens
|
|
735
|
+
// ----------------------
|
|
736
|
+
console.log('🔄 Processing typography tokens...');
|
|
737
|
+
|
|
738
|
+
const rawTypography = load(files.typography);
|
|
739
|
+
const typographyStyles = {};
|
|
740
|
+
const typographyUtilities = {};
|
|
741
|
+
const fontFamilies = {};
|
|
742
|
+
const fontWeights = {};
|
|
743
|
+
const fontSizes = {};
|
|
744
|
+
const lineHeights = {};
|
|
745
|
+
const letterSpacings = {};
|
|
746
|
+
|
|
747
|
+
function processTypographyTokens(obj, prefix = '') {
|
|
748
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
749
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
750
|
+
|
|
751
|
+
if (value?.$type === 'typography' && value?.$value) {
|
|
752
|
+
const typographyValue = value.$value;
|
|
753
|
+
const kebabKey = kebab(path);
|
|
754
|
+
|
|
755
|
+
// Create CSS variables for each typography property
|
|
756
|
+
// These are only added to cssVarMap for filtering later
|
|
757
|
+
for (const [propKey, propValue] of Object.entries(typographyValue)) {
|
|
758
|
+
const propName = `${kebabKey}-${propKey}`;
|
|
759
|
+
let resolvedValue = propValue;
|
|
760
|
+
|
|
761
|
+
// Resolve references if needed
|
|
762
|
+
if (typeof propValue === 'string' && propValue.startsWith('{')) {
|
|
763
|
+
resolvedValue = resolveReference(propValue, flattenedBase);
|
|
764
|
+
if (!resolvedValue) {
|
|
765
|
+
console.warn(`Could not resolve reference: ${propValue} for ${propName}`);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Apply font weight mapping if this is a font weight property
|
|
771
|
+
if (propKey === 'fontWeight') {
|
|
772
|
+
resolvedValue = mapFontWeight(resolvedValue);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
cssVarMap[propName] = resolvedValue;
|
|
776
|
+
|
|
777
|
+
// NOTE: We do NOT collect these into fontFamilies, fontWeights, etc.
|
|
778
|
+
// since we don't want them exported in tailwind.tokens.js
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Helper function to convert a value or reference to a CSS variable reference
|
|
782
|
+
function toCssVarReference(propValue, propKey) {
|
|
783
|
+
// If it's a reference like {typography.font-family.gulliver}
|
|
784
|
+
if (typeof propValue === 'string' && propValue.startsWith('{') && propValue.endsWith('}')) {
|
|
785
|
+
const refPath = propValue.replace(/[{}]/g, '');
|
|
786
|
+
|
|
787
|
+
// Find the corresponding primitive variable
|
|
788
|
+
// For font-family references
|
|
789
|
+
if (refPath.includes('font-family')) {
|
|
790
|
+
let fontName = refPath.split('.').pop();
|
|
791
|
+
|
|
792
|
+
// Special handling: "gulliver semibold" should map to "gulliver"
|
|
793
|
+
// since "Gulliver Semibold" is just the Gulliver font family
|
|
794
|
+
if (fontName.includes('gulliver') && fontName.includes('semibold')) {
|
|
795
|
+
fontName = 'gulliver';
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const kebabName = kebab(fontName);
|
|
799
|
+
return `var(--font-family-${kebabName})`;
|
|
800
|
+
}
|
|
801
|
+
// For font-weight references
|
|
802
|
+
else if (refPath.includes('font-weight')) {
|
|
803
|
+
const weightName = refPath.split('.').pop();
|
|
804
|
+
const kebabName = kebab(weightName);
|
|
805
|
+
return `var(--font-weight-${kebabName})`;
|
|
806
|
+
}
|
|
807
|
+
// For letter-spacing references
|
|
808
|
+
else if (refPath.includes('letter-spacing')) {
|
|
809
|
+
const spacingName = refPath.split('.').pop();
|
|
810
|
+
const kebabName = kebab(spacingName);
|
|
811
|
+
return `var(--letter-spacing-${kebabName})`;
|
|
812
|
+
}
|
|
813
|
+
// For font-size references
|
|
814
|
+
else if (refPath.includes('font-size')) {
|
|
815
|
+
// Handle different font-size reference patterns
|
|
816
|
+
if (refPath.includes('font-size.heading.') || refPath.includes('font-size.body.') || refPath.includes('font-size.meta')) {
|
|
817
|
+
return `var(--${kebab(refPath)})`;
|
|
818
|
+
}
|
|
819
|
+
// Handle numeric font sizes like {typography.font-size.22}
|
|
820
|
+
const sizeName = refPath.split('.').pop();
|
|
821
|
+
return `var(--font-size-${sizeName})`;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// If it's not a reference, return the value as-is
|
|
826
|
+
return propValue;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Create a utility class with CSS variable references to primitives
|
|
830
|
+
const utilityStyle = {
|
|
831
|
+
fontFamily: toCssVarReference(typographyValue.fontFamily, 'fontFamily'),
|
|
832
|
+
fontSize: toCssVarReference(typographyValue.fontSize, 'fontSize'),
|
|
833
|
+
fontWeight: toCssVarReference(typographyValue.fontWeight, 'fontWeight'),
|
|
834
|
+
lineHeight: typographyValue.lineHeight,
|
|
835
|
+
letterSpacing: toCssVarReference(typographyValue.letterSpacing, 'letterSpacing'),
|
|
836
|
+
textDecoration: typographyValue.textDecoration || 'none'
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
// Use font-variant for small-caps instead of text-transform
|
|
840
|
+
if (typographyValue.textTransform === 'small-caps') {
|
|
841
|
+
utilityStyle.fontVariant = 'all-small-caps';
|
|
842
|
+
} else if (typographyValue.textTransform && typographyValue.textTransform !== 'none') {
|
|
843
|
+
utilityStyle.textTransform = typographyValue.textTransform;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
typographyUtilities[`.text-${kebabKey}`] = utilityStyle;
|
|
847
|
+
typographyStyles[kebabKey] = `var(--${kebabKey})`;
|
|
848
|
+
} else if (typeof value === 'object' && !value.$type && value !== null) {
|
|
849
|
+
processTypographyTokens(value, path);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
processTypographyTokens(rawTypography);
|
|
855
|
+
|
|
856
|
+
// ----------------------
|
|
857
|
+
// 📱 Process Responsive Typography Tokens
|
|
858
|
+
// ----------------------
|
|
859
|
+
console.log('🔄 Processing responsive typography tokens...');
|
|
860
|
+
|
|
861
|
+
const responsiveTokens = {
|
|
862
|
+
mobile: {},
|
|
863
|
+
tablet: {},
|
|
864
|
+
desktop: {}
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
function processResponsiveTypography() {
|
|
868
|
+
// Process each breakpoint
|
|
869
|
+
for (const [breakpoint, file] of Object.entries(files.responsiveTypography)) {
|
|
870
|
+
try {
|
|
871
|
+
const rawTokens = load(file);
|
|
872
|
+
|
|
873
|
+
// Add these tokens to the base tokens for reference resolution
|
|
874
|
+
const combinedTokens = {
|
|
875
|
+
...flattenedBase,
|
|
876
|
+
...flatten(rawTokens)
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
// Extract typography tokens
|
|
880
|
+
for (const [key, value] of Object.entries(rawTokens)) {
|
|
881
|
+
if (typeof value === 'object') {
|
|
882
|
+
processResponsiveTypographySection(value, key, breakpoint, combinedTokens);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Process font sizes specifically
|
|
887
|
+
if (rawTokens['font-size']) {
|
|
888
|
+
processFontSizes(rawTokens['font-size'], breakpoint);
|
|
889
|
+
}
|
|
890
|
+
} catch (error) {
|
|
891
|
+
console.error(`Error processing ${breakpoint} typography: ${error.message}`);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Process font sizes from semantic typography tokens
|
|
897
|
+
function processFontSizes(fontSizes, breakpoint) {
|
|
898
|
+
// Process heading sizes
|
|
899
|
+
if (fontSizes.heading) {
|
|
900
|
+
Object.entries(fontSizes.heading).forEach(([size, value]) => {
|
|
901
|
+
if (value.$value) {
|
|
902
|
+
const varName = `font-size-heading-${size}`;
|
|
903
|
+
responsiveTokens[breakpoint][varName] = resolveReference(value.$value, flattenedBase) || value.$value;
|
|
904
|
+
|
|
905
|
+
// For desktop, also add to the main CSS var map (without breakpoint suffix)
|
|
906
|
+
if (breakpoint === 'desktop') {
|
|
907
|
+
cssVarMap[varName] = responsiveTokens[breakpoint][varName];
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Process body sizes
|
|
914
|
+
if (fontSizes.body) {
|
|
915
|
+
Object.entries(fontSizes.body).forEach(([size, value]) => {
|
|
916
|
+
if (value.$value) {
|
|
917
|
+
const varName = `font-size-body-${size}`;
|
|
918
|
+
responsiveTokens[breakpoint][varName] = resolveReference(value.$value, flattenedBase) || value.$value;
|
|
919
|
+
|
|
920
|
+
// For desktop, also add to the main CSS var map (without breakpoint suffix)
|
|
921
|
+
if (breakpoint === 'desktop') {
|
|
922
|
+
cssVarMap[varName] = responsiveTokens[breakpoint][varName];
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Process meta size
|
|
929
|
+
if (fontSizes.meta && fontSizes.meta.$value) {
|
|
930
|
+
const varName = `font-size-meta`;
|
|
931
|
+
responsiveTokens[breakpoint][varName] = resolveReference(fontSizes.meta.$value, flattenedBase) || fontSizes.meta.$value;
|
|
932
|
+
|
|
933
|
+
// For desktop, also add to the main CSS var map (without breakpoint suffix)
|
|
934
|
+
if (breakpoint === 'desktop') {
|
|
935
|
+
cssVarMap[varName] = responsiveTokens[breakpoint][varName];
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function processResponsiveTypographySection(obj, prefix, breakpoint, combinedTokens) {
|
|
941
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
942
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
943
|
+
|
|
944
|
+
if (value?.$type === 'typography' && value?.$value) {
|
|
945
|
+
const typographyValue = value.$value;
|
|
946
|
+
const kebabKey = kebab(path);
|
|
947
|
+
|
|
948
|
+
// Create CSS variables for each typography property
|
|
949
|
+
for (const [propKey, propValue] of Object.entries(typographyValue)) {
|
|
950
|
+
const propName = `${kebabKey}-${propKey}`;
|
|
951
|
+
let resolvedValue = propValue;
|
|
952
|
+
|
|
953
|
+
// Resolve references if needed
|
|
954
|
+
if (typeof propValue === 'string' && propValue.startsWith('{')) {
|
|
955
|
+
resolvedValue = resolveReference(propValue, combinedTokens);
|
|
956
|
+
if (!resolvedValue) {
|
|
957
|
+
console.warn(`Could not resolve reference: ${propValue} for ${propName} in ${breakpoint}`);
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Apply font weight mapping if this is a font weight property
|
|
963
|
+
if (propKey === 'fontWeight') {
|
|
964
|
+
resolvedValue = mapFontWeight(resolvedValue);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Store the responsive value
|
|
968
|
+
responsiveTokens[breakpoint][propName] = resolvedValue;
|
|
969
|
+
}
|
|
970
|
+
} else if (typeof value === 'object' && !value.$type && value !== null) {
|
|
971
|
+
processResponsiveTypographySection(value, path, breakpoint, combinedTokens);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
processResponsiveTypography();
|
|
977
|
+
|
|
978
|
+
// ----------------------
|
|
979
|
+
// 📐 Text Alignment Utilities
|
|
980
|
+
// ----------------------
|
|
981
|
+
console.log('🔄 Adding text alignment utilities...');
|
|
982
|
+
|
|
983
|
+
const textAlignmentUtilities = {
|
|
984
|
+
'.text-left': {textAlign: 'left'},
|
|
985
|
+
'.text-center': {textAlign: 'center'},
|
|
986
|
+
'.text-right': {textAlign: 'right'},
|
|
987
|
+
'.text-justify': {textAlign: 'justify'}
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
// ----------------------
|
|
991
|
+
// ✏️ Write tailwind.tokens.js
|
|
992
|
+
// ----------------------
|
|
993
|
+
console.log('📝 Writing tailwind.tokens.js...');
|
|
994
|
+
|
|
995
|
+
const jsOutput = `
|
|
996
|
+
// Generated from design tokens - DO NOT EDIT DIRECTLY
|
|
997
|
+
// Last generated: ${new Date().toISOString()}
|
|
998
|
+
|
|
999
|
+
export const primitiveColors = ${JSON.stringify(primitiveColors, null, 2)};
|
|
1000
|
+
export const semanticColors = ${JSON.stringify(semanticColors, null, 2)};
|
|
1001
|
+
export const componentColors = ${JSON.stringify(componentColors, null, 2)};
|
|
1002
|
+
export const spacing = ${JSON.stringify(spacing, null, 2)};
|
|
1003
|
+
export const typographyStyles = ${JSON.stringify(typographyStyles, null, 2)};
|
|
1004
|
+
export const primitiveFontFamilies = ${JSON.stringify(primitiveFontFamilies, null, 2)};
|
|
1005
|
+
export const primitiveFontSizes = ${JSON.stringify(primitiveFontSizes, null, 2)};
|
|
1006
|
+
export const primitiveFontWeights = ${JSON.stringify(primitiveFontWeights, null, 2)};
|
|
1007
|
+
export const primitiveLetterSpacings = ${JSON.stringify(primitiveLetterSpacings, null, 2)};
|
|
1008
|
+
export const primitiveBorderWidth = ${JSON.stringify(primitiveBorderWidth, null, 2)};
|
|
1009
|
+
export const primitiveBorderRadius = ${JSON.stringify(primitiveBorderRadius, null, 2)};
|
|
1010
|
+
export const semanticBorderWidth = ${JSON.stringify(semanticBorderWidth, null, 2)};
|
|
1011
|
+
export const semanticBorderRadius = ${JSON.stringify(semanticBorderRadius, null, 2)};
|
|
1012
|
+
export const primitiveBoxShadows = ${JSON.stringify(primitiveBoxShadows, null, 2)};
|
|
1013
|
+
|
|
1014
|
+
`.trim();
|
|
1015
|
+
|
|
1016
|
+
fs.writeFileSync('./tailwind.tokens.js', jsOutput);
|
|
1017
|
+
console.log('✅ tailwind.tokens.js written.');
|
|
1018
|
+
|
|
1019
|
+
// ----------------------
|
|
1020
|
+
// 🎨 Write tokens.css
|
|
1021
|
+
// ----------------------
|
|
1022
|
+
console.log('📝 Writing tokens.css...');
|
|
1023
|
+
|
|
1024
|
+
// Filter out individual typography tokens (we only want font-size-* tokens in the CSS)
|
|
1025
|
+
const filteredCssVarMap = Object.fromEntries(
|
|
1026
|
+
Object.entries(cssVarMap).filter(([key]) => {
|
|
1027
|
+
// Exclude individual typography property tokens (article-*, body-*, heading-*, meta-*, menu-*)
|
|
1028
|
+
// but keep font-size-* tokens
|
|
1029
|
+
if (key.startsWith('font-size-')) return true;
|
|
1030
|
+
|
|
1031
|
+
const isTypographyToken = key.match(/^(article|body|heading|meta|menu)-.*-(fontFamily|fontSize|fontWeight|letterSpacing|lineHeight|textTransform|textDecoration)$/);
|
|
1032
|
+
if (isTypographyToken) return false;
|
|
1033
|
+
|
|
1034
|
+
return true;
|
|
1035
|
+
})
|
|
1036
|
+
);
|
|
1037
|
+
|
|
1038
|
+
const cssOutput = `
|
|
1039
|
+
/**
|
|
1040
|
+
* Design System Tokens
|
|
1041
|
+
* Generated from design tokens - DO NOT EDIT DIRECTLY
|
|
1042
|
+
* Last generated: ${new Date().toISOString()}
|
|
1043
|
+
*/
|
|
1044
|
+
|
|
1045
|
+
:root {
|
|
1046
|
+
${Object.entries(filteredCssVarMap)
|
|
1047
|
+
.map(([key, val]) => ` --${key}: ${val};`)
|
|
1048
|
+
.join('\n')}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/* Dark Mode Colors */
|
|
1052
|
+
@media (prefers-color-scheme: dark) {
|
|
1053
|
+
:root {
|
|
1054
|
+
${Object.entries(darkModeColors)
|
|
1055
|
+
.map(([key, val]) => ` --${key}: ${val};`)
|
|
1056
|
+
.join('\n')}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/* Mobile Typography (Default) */
|
|
1061
|
+
@media (max-width: 767px) {
|
|
1062
|
+
:root {
|
|
1063
|
+
${Object.entries(responsiveTokens.mobile)
|
|
1064
|
+
.map(([key, val]) => ` --${key}: ${val};`)
|
|
1065
|
+
.join('\n')}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/* Tablet Typography (768px - 1023px) */
|
|
1070
|
+
@media (min-width: 768px) and (max-width: 1023px) {
|
|
1071
|
+
:root {
|
|
1072
|
+
${Object.entries(responsiveTokens.tablet)
|
|
1073
|
+
.map(([key, val]) => ` --${key}: ${val};`)
|
|
1074
|
+
.join('\n')}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/* Desktop Typography (1024px+) is defined in the main :root block */
|
|
1079
|
+
`.trim();
|
|
1080
|
+
|
|
1081
|
+
fs.writeFileSync('./src/assets/css/tokens.css', cssOutput);
|
|
1082
|
+
console.log('✅ tokens.css written.');
|
|
1083
|
+
|
|
1084
|
+
// ----------------------
|
|
1085
|
+
// ⚡ Write tokens.utilities.js plugin
|
|
1086
|
+
// ----------------------
|
|
1087
|
+
console.log('📝 Writing tokens.utilities.js...');
|
|
1088
|
+
|
|
1089
|
+
const utilOutput = `
|
|
1090
|
+
/**
|
|
1091
|
+
* Tailwind CSS Utilities Plugin
|
|
1092
|
+
* Generated from design tokens - DO NOT EDIT DIRECTLY
|
|
1093
|
+
* Last generated: ${new Date().toISOString()}
|
|
1094
|
+
*/
|
|
1095
|
+
|
|
1096
|
+
export default function ({ addUtilities }) {
|
|
1097
|
+
addUtilities({
|
|
1098
|
+
...${JSON.stringify(utilityClasses, null, 2)},
|
|
1099
|
+
...${JSON.stringify(spacingUtilities, null, 2)},
|
|
1100
|
+
...${JSON.stringify(borderUtilities, null, 2)},
|
|
1101
|
+
...${JSON.stringify(typographyUtilities, null, 2)},
|
|
1102
|
+
...${JSON.stringify(shadowUtilities, null, 2)},
|
|
1103
|
+
...${JSON.stringify(textAlignmentUtilities, null, 2)}
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
`.trim();
|
|
1107
|
+
|
|
1108
|
+
fs.writeFileSync('./tokens.utilities.js', utilOutput);
|
|
1109
|
+
console.log('✅ Tailwind plugin written: tokens.utilities.js');
|
|
1110
|
+
console.log('🎉 Token processing complete!');
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { rollup } from 'rollup';
|
|
2
|
+
import dts from 'rollup-plugin-dts';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
async function buildTypes() {
|
|
10
|
+
const bundle = await rollup({
|
|
11
|
+
input: path.resolve(__dirname, '../src/index.ts'),
|
|
12
|
+
plugins: [
|
|
13
|
+
dts({
|
|
14
|
+
compilerOptions: {
|
|
15
|
+
baseUrl: path.resolve(__dirname, '..'),
|
|
16
|
+
paths: {
|
|
17
|
+
"@atoms/*": ["src/atoms/*"],
|
|
18
|
+
"@molecules/*": ["src/molecules/*"],
|
|
19
|
+
"@components/*": ["src/components/*"],
|
|
20
|
+
"@experimental/*": ["src/experimental/*"],
|
|
21
|
+
"@hooks/*": ["src/hooks/*"],
|
|
22
|
+
"@helpers/*": ["src/helpers/*"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await bundle.write({
|
|
30
|
+
file: path.resolve(__dirname, '../dist/index.d.ts'),
|
|
31
|
+
format: 'es',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log('✔ Type declarations built');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
buildTypes();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const alleenWaarschuwen = process.argv.includes('--warn');
|
|
6
|
+
|
|
7
|
+
const agent = process.env.npm_config_user_agent || '';
|
|
8
|
+
const manager = agent.split('/')[0];
|
|
9
|
+
const pad = (p) => {
|
|
10
|
+
try {
|
|
11
|
+
return realpathSync(p);
|
|
12
|
+
} catch {
|
|
13
|
+
return p;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const pakketRoot = pad(resolve(dirname(fileURLToPath(import.meta.url)), '..'));
|
|
17
|
+
const startMap = process.env.INIT_CWD ? pad(process.env.INIT_CWD) : pakketRoot;
|
|
18
|
+
|
|
19
|
+
const alsDependency = startMap !== pakketRoot;
|
|
20
|
+
|
|
21
|
+
if (!agent || manager === 'yarn' || alsDependency || process.env.ALLOW_NPM === '1') {
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const rood = '\x1b[31m';
|
|
26
|
+
const geel = '\x1b[33m';
|
|
27
|
+
const vet = '\x1b[1m';
|
|
28
|
+
const uit = '\x1b[0m';
|
|
29
|
+
|
|
30
|
+
if (alleenWaarschuwen) {
|
|
31
|
+
console.error(
|
|
32
|
+
`${geel} Let op: dit project gebruikt yarn. Draai dit liever als ` +
|
|
33
|
+
`${vet}yarn ${process.env.npm_lifecycle_event?.replace(/^pre/, '') || '<script>'}${uit}${geel}.${uit}\n`
|
|
34
|
+
);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.error(`
|
|
39
|
+
${rood}${vet} Dit project gebruikt yarn, geen ${manager}.${uit}
|
|
40
|
+
|
|
41
|
+
Gebruik in plaats daarvan:
|
|
42
|
+
|
|
43
|
+
${vet}yarn install${uit} in plaats van ${manager} install
|
|
44
|
+
${vet}yarn add <pakket>${uit} in plaats van ${manager} install <pakket>
|
|
45
|
+
${vet}yarn add --dev <pakket>${uit} in plaats van ${manager} install --save-dev <pakket>
|
|
46
|
+
|
|
47
|
+
${geel}Waarom:${uit} ${manager} install herschrijft yarn.lock in zijn geheel, ook de
|
|
48
|
+
pakketten waar je niets aan verandert. CI draait op yarn install
|
|
49
|
+
--frozen-lockfile, dus zo'n herschrijving kan de resolutie van de hele
|
|
50
|
+
dependencytree verschuiven.
|
|
51
|
+
|
|
52
|
+
Moet je toch echt ${manager} gebruiken: ALLOW_NPM=1 ${manager} install
|
|
53
|
+
`);
|
|
54
|
+
|
|
55
|
+
process.exit(1);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
8
|
+
|
|
9
|
+
// Files to copy
|
|
10
|
+
const filesToCopy = [
|
|
11
|
+
'tailwind.config.js',
|
|
12
|
+
'tailwind.tokens.js',
|
|
13
|
+
'tokens.utilities.js'
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// Ensure dist directory exists
|
|
17
|
+
if (!fs.existsSync(path.join(rootDir, 'dist'))) {
|
|
18
|
+
fs.mkdirSync(path.join(rootDir, 'dist'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Copy each file
|
|
22
|
+
filesToCopy.forEach(file => {
|
|
23
|
+
const sourcePath = path.join(rootDir, file);
|
|
24
|
+
const destPath = path.join(rootDir, 'dist', file);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
28
|
+
console.log(`✓ Copied ${file} to dist/`);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`Error copying ${file}:`, error.message);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log('✓ Tailwind files copied to dist/');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { optimize } from 'svgo';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
// Standaard alle iconen; geef desgewenst losse paden of mappen mee als argument.
|
|
10
|
+
const targets = process.argv.slice(2).length
|
|
11
|
+
? process.argv.slice(2)
|
|
12
|
+
: [path.resolve(__dirname, '../src/atoms/basicAtoms/Icon/icons')];
|
|
13
|
+
|
|
14
|
+
const config = {
|
|
15
|
+
multipass: true,
|
|
16
|
+
js2svg: { indent: 2, pretty: false },
|
|
17
|
+
// Let op: viewBox moet blijven staan (svgo 4 laat 'm met rust), want Icon.tsx
|
|
18
|
+
// zet width/height op de svg en zonder viewBox schaalt het icoon niet mee.
|
|
19
|
+
plugins: [
|
|
20
|
+
{
|
|
21
|
+
name: 'preset-default',
|
|
22
|
+
params: {
|
|
23
|
+
overrides: {
|
|
24
|
+
// Houd de coördinaten op 2 decimalen; op 16x16 is dat ruim genoeg.
|
|
25
|
+
convertPathData: { floatPrecision: 2 },
|
|
26
|
+
cleanupNumericValues: { floatPrecision: 2 },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function collect(target) {
|
|
34
|
+
const stat = fs.statSync(target);
|
|
35
|
+
if (stat.isFile()) return target.endsWith('.svg') ? [target] : [];
|
|
36
|
+
return fs.readdirSync(target).flatMap((entry) => collect(path.join(target, entry)));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let before = 0;
|
|
40
|
+
let after = 0;
|
|
41
|
+
let changed = 0;
|
|
42
|
+
|
|
43
|
+
for (const file of targets.flatMap(collect)) {
|
|
44
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
45
|
+
const { data } = optimize(source, { ...config, path: file });
|
|
46
|
+
|
|
47
|
+
before += Buffer.byteLength(source);
|
|
48
|
+
after += Buffer.byteLength(data);
|
|
49
|
+
|
|
50
|
+
if (data !== source) {
|
|
51
|
+
fs.writeFileSync(file, data);
|
|
52
|
+
changed++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const saved = before - after;
|
|
57
|
+
const percentage = before ? Math.round((saved / before) * 100) : 0;
|
|
58
|
+
console.log(`svgo: ${changed} bestanden aangepast, ${before} → ${after} bytes (-${percentage}%)`);
|