@vettvangur/design-system 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generate-tailwind-DQMJ7rRw.js +242 -0
- package/dist/index.esm.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { p as paths } from './index.esm.js';
|
|
7
|
+
import { p as parseButtons } from './buttons-o2a9tpOe.js';
|
|
8
|
+
import 'node:process';
|
|
9
|
+
import 'boxen';
|
|
10
|
+
import 'fs';
|
|
11
|
+
import 'path';
|
|
12
|
+
import 'os';
|
|
13
|
+
import 'crypto';
|
|
14
|
+
import 'node:url';
|
|
15
|
+
|
|
16
|
+
const tag = chalk.cyan('[design-system]');
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* This needs to genrate:
|
|
20
|
+
* variables:
|
|
21
|
+
* color.css
|
|
22
|
+
* font.css
|
|
23
|
+
* radius.css
|
|
24
|
+
* shadow.css
|
|
25
|
+
* typography.css
|
|
26
|
+
* core:
|
|
27
|
+
* body.css
|
|
28
|
+
* headline.css
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
async function generateTailwind(type, figmaStyles) {
|
|
32
|
+
console.log(`${tag} starting tailwind generation...`);
|
|
33
|
+
await generateColors(figmaStyles.paint);
|
|
34
|
+
await generateTypography(figmaStyles.text);
|
|
35
|
+
|
|
36
|
+
// core utilities
|
|
37
|
+
await generateBodies(figmaStyles.text);
|
|
38
|
+
await generateHeadlines(figmaStyles.text);
|
|
39
|
+
await generateButtons(figmaStyles);
|
|
40
|
+
console.log(`${tag} finished tailwind generation`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ------- VARIABLES -------
|
|
44
|
+
async function generateColors(data) {
|
|
45
|
+
console.log(`${tag} generating colors...`);
|
|
46
|
+
const outDir = path.join(paths.styles, 'config');
|
|
47
|
+
await fs.mkdir(outDir, {
|
|
48
|
+
recursive: true
|
|
49
|
+
});
|
|
50
|
+
const lines = [];
|
|
51
|
+
for (const [key, entry] of Object.entries(data ?? {})) {
|
|
52
|
+
const paints = entry?.paints ?? [];
|
|
53
|
+
const p = paints[0] ?? {};
|
|
54
|
+
// exporter already normalizes; prefer .hex/.value, else stringify fallback
|
|
55
|
+
const value = (p.hex && String(p.hex)) ?? (p.value && String(p.value)) ?? (p.css && String(p.css)) ?? String(p);
|
|
56
|
+
if (!value) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
lines.push(` --color-${key}: ${value};`);
|
|
60
|
+
}
|
|
61
|
+
const css = `/* Auto-generated. Do not edit by hand. */
|
|
62
|
+
@theme {
|
|
63
|
+
${lines.join('\n')}
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
const filePath = path.join(outDir, 'color.css');
|
|
67
|
+
await fs.writeFile(filePath, css, 'utf8');
|
|
68
|
+
console.log(`${tag} finished generating colors`);
|
|
69
|
+
}
|
|
70
|
+
async function generateButtons(figma) {
|
|
71
|
+
console.log(`${tag} generating buttons...`);
|
|
72
|
+
const outDir = path.join(paths.styles, "core");
|
|
73
|
+
const outFile = path.join(outDir, "button-variants.css");
|
|
74
|
+
|
|
75
|
+
// If file already exists, skip generation
|
|
76
|
+
try {
|
|
77
|
+
await fs.access(outFile, constants.F_OK);
|
|
78
|
+
console.log(`${tag} button-variants.css already exists, skipping`);
|
|
79
|
+
return;
|
|
80
|
+
} catch {
|
|
81
|
+
// access threw -> file does not exist, continue
|
|
82
|
+
}
|
|
83
|
+
const getButtons = await parseButtons(figma);
|
|
84
|
+
const buttons = getButtons.map(b => `@utility ${b} {
|
|
85
|
+
@apply button;
|
|
86
|
+
|
|
87
|
+
}`).join("\n\n");
|
|
88
|
+
const out = `/* Auto-generated. Do not edit by hand. */
|
|
89
|
+
${buttons}
|
|
90
|
+
`;
|
|
91
|
+
await fs.writeFile(outFile, out, "utf8");
|
|
92
|
+
console.log(`${tag} finished generating buttons`);
|
|
93
|
+
}
|
|
94
|
+
async function generateTypography(data) {
|
|
95
|
+
console.log(`${tag} generating typography...`);
|
|
96
|
+
const outDir = path.join(paths.styles, 'config');
|
|
97
|
+
await fs.mkdir(outDir, {
|
|
98
|
+
recursive: true
|
|
99
|
+
});
|
|
100
|
+
const lines = [];
|
|
101
|
+
for (const [key, entry] of Object.entries(data ?? {})) {
|
|
102
|
+
if (!entry || typeof entry !== 'object') {
|
|
103
|
+
console.log(`${tag} [typography] skip "${key}" – entry is not an object`, entry);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const fontSize = entry.fontSize;
|
|
107
|
+
const lh = entry.lineHeight ?? {};
|
|
108
|
+
const lhVal = lh && typeof lh === 'object' ? lh.value : undefined;
|
|
109
|
+
const lhUnit = lh && typeof lh === 'object' ? lh.unit ?? '' : '';
|
|
110
|
+
const hasFontSize = typeof fontSize === 'number' && !Number.isNaN(fontSize);
|
|
111
|
+
const hasLineHeight = typeof lhVal === 'number' && !Number.isNaN(lhVal) && lhUnit !== '';
|
|
112
|
+
if (!hasFontSize && !hasLineHeight) {
|
|
113
|
+
// This will tell you exactly which keys are coming in “empty”
|
|
114
|
+
console.log(`${tag} [typography] "${key}" has no usable fontSize/lineHeight`, {
|
|
115
|
+
fontSize,
|
|
116
|
+
lineHeight: lh
|
|
117
|
+
});
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (hasFontSize) {
|
|
121
|
+
lines.push(` --text-${key}: ${fontSize}px;`);
|
|
122
|
+
}
|
|
123
|
+
if (hasLineHeight) {
|
|
124
|
+
lines.push(` --leading-${key}: ${lhVal}${lhUnit};`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const css = `/* Auto-generated. Do not edit by hand. */
|
|
128
|
+
@theme {
|
|
129
|
+
${lines.join('\n')}
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
const filePath = path.join(outDir, 'typography.css');
|
|
133
|
+
await fs.writeFile(filePath, css, 'utf8');
|
|
134
|
+
console.log(`${tag} finished generating typography`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ------- CORE (fixed grouping incl. semibold) -------
|
|
138
|
+
|
|
139
|
+
async function generateBodies(textMap = {}) {
|
|
140
|
+
console.log(`${tag} generating bodies...`);
|
|
141
|
+
const outDir = path.join(paths.styles, 'core');
|
|
142
|
+
await fs.mkdir(outDir, {
|
|
143
|
+
recursive: true
|
|
144
|
+
});
|
|
145
|
+
const keys = Object.keys(textMap || {}).filter(k => k.startsWith('body-'));
|
|
146
|
+
|
|
147
|
+
// Normalize base: remove a single "mobile" segment after "body-"
|
|
148
|
+
function baseKey(k) {
|
|
149
|
+
const parts = k.split('-');
|
|
150
|
+
const idx = parts.indexOf('mobile');
|
|
151
|
+
if (idx !== -1) parts.splice(idx, 1);
|
|
152
|
+
return parts.join('-');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @type {Map<string,{desktop:string|null,mobile:string|null}>} */
|
|
156
|
+
const groups = new Map();
|
|
157
|
+
for (const k of keys) {
|
|
158
|
+
const base = baseKey(k);
|
|
159
|
+
const g = groups.get(base) ?? {
|
|
160
|
+
desktop: null,
|
|
161
|
+
mobile: null
|
|
162
|
+
};
|
|
163
|
+
if (k.includes('-mobile-')) g.mobile = k;else g.desktop = k;
|
|
164
|
+
groups.set(base, g);
|
|
165
|
+
}
|
|
166
|
+
const blocks = [];
|
|
167
|
+
for (const [base, {
|
|
168
|
+
desktop,
|
|
169
|
+
mobile
|
|
170
|
+
}] of [...groups.entries()].sort()) {
|
|
171
|
+
const hasMobile = !!mobile;
|
|
172
|
+
const hasDesktop = !!desktop;
|
|
173
|
+
const mobileRef = hasMobile ? mobile : desktop;
|
|
174
|
+
const desktopUpgrade = hasMobile && hasDesktop ? `\n @apply desktop-xs:text-${desktop} desktop-xs:leading-${desktop};` : ``;
|
|
175
|
+
|
|
176
|
+
// collapse “body-body-x” → “body-x”
|
|
177
|
+
const isDouble = base.startsWith('body-body-');
|
|
178
|
+
const utilName = isDouble ? base.replace(/^body-body-/, 'body-') : base;
|
|
179
|
+
|
|
180
|
+
// main utility
|
|
181
|
+
blocks.push(`@utility ${utilName} {
|
|
182
|
+
@apply text-${mobileRef} leading-${mobileRef};${desktopUpgrade}
|
|
183
|
+
}`);
|
|
184
|
+
|
|
185
|
+
// only add numeric alias if no collapse happened
|
|
186
|
+
if (!isDouble) {
|
|
187
|
+
const m = /^body-body-(\d+)$/i.exec(base);
|
|
188
|
+
if (m) {
|
|
189
|
+
const n = m[1];
|
|
190
|
+
blocks.push(`@utility body-${n} {
|
|
191
|
+
@apply text-${mobileRef} leading-${mobileRef};${desktopUpgrade}
|
|
192
|
+
}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const out = `/* Auto-generated. Do not edit by hand. */
|
|
197
|
+
${blocks.join('\n\n')}
|
|
198
|
+
`;
|
|
199
|
+
await fs.writeFile(path.join(outDir, 'body.css'), out, 'utf8');
|
|
200
|
+
console.log(`${tag} finished generating bodies`);
|
|
201
|
+
}
|
|
202
|
+
async function generateHeadlines(textMap = {}) {
|
|
203
|
+
console.log(`${tag} generating headlines...`);
|
|
204
|
+
const outDir = path.join(paths.styles, 'core');
|
|
205
|
+
await fs.mkdir(outDir, {
|
|
206
|
+
recursive: true
|
|
207
|
+
});
|
|
208
|
+
const keys = Object.keys(textMap || {}).filter(k => k.startsWith('headline-'));
|
|
209
|
+
|
|
210
|
+
// collect ids present in either mobile or desktop
|
|
211
|
+
const ids = new Set();
|
|
212
|
+
for (const k of keys) {
|
|
213
|
+
let m = /^headline-h(\d+)$/i.exec(k);
|
|
214
|
+
if (m) {
|
|
215
|
+
ids.add(+m[1]);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
m = /^headline-mobile-h(\d+)$/i.exec(k);
|
|
219
|
+
if (m) {
|
|
220
|
+
ids.add(+m[1]);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const blocks = [];
|
|
225
|
+
for (const n of [...ids].sort((a, b) => a - b)) {
|
|
226
|
+
const mobileKey = keys.includes(`headline-mobile-h${n}`) ? `headline-mobile-h${n}` : `headline-h${n}`;
|
|
227
|
+
const hasDesktop = keys.includes(`headline-h${n}`);
|
|
228
|
+
const desktopKey = hasDesktop ? `headline-h${n}` : null;
|
|
229
|
+
const desktopUpgrade = hasDesktop ? `\n @apply desktop-xs:text-${desktopKey} desktop-xs:leading-${desktopKey};` : ``;
|
|
230
|
+
blocks.push(`@utility headline-h${n} {
|
|
231
|
+
@apply text-${mobileKey} leading-${mobileKey};
|
|
232
|
+
@apply font-default;${desktopUpgrade}
|
|
233
|
+
}`);
|
|
234
|
+
}
|
|
235
|
+
const out = `/* Auto-generated. Do not edit by hand. */
|
|
236
|
+
${blocks.join('\n\n')}
|
|
237
|
+
`;
|
|
238
|
+
await fs.writeFile(path.join(outDir, 'headline.css'), out, 'utf8');
|
|
239
|
+
console.log(`${tag} finished generating headlines`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export { generateTailwind as default, generateTailwind };
|
package/dist/index.esm.js
CHANGED
|
@@ -1131,7 +1131,7 @@ async function readFigma() {
|
|
|
1131
1131
|
try {
|
|
1132
1132
|
const {
|
|
1133
1133
|
default: generateTailwind
|
|
1134
|
-
} = await import('./generate-tailwind-
|
|
1134
|
+
} = await import('./generate-tailwind-DQMJ7rRw.js');
|
|
1135
1135
|
const figma = await readFigma();
|
|
1136
1136
|
switch (cmd) {
|
|
1137
1137
|
case 'astro':
|