@webmate-studio/builder 0.1.6 → 0.1.8
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 +2 -2
- package/src/build.js +1 -1
- package/src/tailwind-generator.js +68 -40
- package/src/tailwind-generator-v4.js +0 -341
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webmate-studio/builder",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Webmate Studio Component Builder",
|
|
6
6
|
"keywords": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@tailwindcss/cli": "^4.1.0",
|
|
28
|
-
"@webmate-studio/builder": "^0.1.
|
|
28
|
+
"@webmate-studio/builder": "^0.1.7",
|
|
29
29
|
"@webmate-studio/core": "^0.1.0",
|
|
30
30
|
"@webmate-studio/parser": "^0.1.0",
|
|
31
31
|
"alpinejs": "^3.15.0",
|
package/src/build.js
CHANGED
|
@@ -5,7 +5,7 @@ import { parseComponent } from '@webmate-studio/parser';
|
|
|
5
5
|
import { loadConfig, logger } from '@webmate-studio/core';
|
|
6
6
|
import { cleanComponentHTML, extractStyles } from './html-cleaner.js';
|
|
7
7
|
import { generateManifest } from './manifest.js';
|
|
8
|
-
import { generateComponentCSS } from './tailwind-generator
|
|
8
|
+
import { generateComponentCSS } from './tailwind-generator.js';
|
|
9
9
|
import { bundleComponentIslands } from './bundler.js';
|
|
10
10
|
import { parseDocument } from 'htmlparser2';
|
|
11
11
|
import { DomUtils } from 'htmlparser2';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tailwind CSS Generator for Components
|
|
3
|
-
* Generates scoped Tailwind CSS
|
|
2
|
+
* Tailwind CSS v4 Generator for Components
|
|
3
|
+
* Generates scoped Tailwind CSS using Tailwind v4's new CSS-based configuration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { exec } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import { writeFileSync,
|
|
8
|
+
import { writeFileSync, unlinkSync, mkdtempSync } from 'fs';
|
|
9
9
|
import { join, dirname } from 'path';
|
|
10
10
|
import { tmpdir } from 'os';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
@@ -69,7 +69,50 @@ export function extractTailwindClasses(html) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* Generate
|
|
72
|
+
* Generate CSS with custom theme colors and design tokens for Tailwind v4
|
|
73
|
+
* @param {Object} colors - Color definitions
|
|
74
|
+
* @param {Object} designTokens - Complete design tokens (optional)
|
|
75
|
+
* @returns {string} CSS with @theme directive
|
|
76
|
+
*/
|
|
77
|
+
function generateThemeCSS(colors, designTokens = null) {
|
|
78
|
+
let themeCSS = `@theme {\n`;
|
|
79
|
+
|
|
80
|
+
// Helper to flatten nested colors
|
|
81
|
+
function flattenColors(obj, prefix = '') {
|
|
82
|
+
let css = '';
|
|
83
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
84
|
+
const colorKey = prefix ? `${prefix}-${key}` : key;
|
|
85
|
+
|
|
86
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
87
|
+
// Nested color (e.g., primary.light)
|
|
88
|
+
if (value.DEFAULT) {
|
|
89
|
+
css += ` --color-${colorKey}: ${value.DEFAULT};\n`;
|
|
90
|
+
}
|
|
91
|
+
css += flattenColors(value, colorKey);
|
|
92
|
+
} else {
|
|
93
|
+
// Simple color value
|
|
94
|
+
css += ` --color-${colorKey}: ${value};\n`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return css;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
themeCSS += flattenColors(colors);
|
|
101
|
+
|
|
102
|
+
// Add spacing if provided in design tokens
|
|
103
|
+
if (designTokens?.spacing) {
|
|
104
|
+
if (typeof designTokens.spacing === 'string') {
|
|
105
|
+
themeCSS += ` --spacing: ${designTokens.spacing};\n`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
themeCSS += `}\n`;
|
|
110
|
+
|
|
111
|
+
return themeCSS;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generate Tailwind CSS for specific classes (Tailwind v4)
|
|
73
116
|
* @param {Set<string>|Array<string>} classes - Tailwind classes to include
|
|
74
117
|
* @param {Object} options - Generation options
|
|
75
118
|
* @param {Object} options.designTokens - Custom design tokens (optional)
|
|
@@ -90,14 +133,10 @@ export async function generateTailwindCSS(classes, options = {}) {
|
|
|
90
133
|
|
|
91
134
|
// Create temporary directory
|
|
92
135
|
const tempDir = mkdtempSync(join(tmpdir(), 'tailwind-gen-'));
|
|
93
|
-
const configPath = join(tempDir, 'tailwind.config.cjs');
|
|
94
136
|
const inputPath = join(tempDir, 'input.css');
|
|
95
137
|
const htmlPath = join(tempDir, 'content.html');
|
|
96
138
|
|
|
97
139
|
try {
|
|
98
|
-
// Tailwind v4 uses CSS-based configuration
|
|
99
|
-
// Instead of config file, we generate CSS with custom properties and @theme
|
|
100
|
-
|
|
101
140
|
// Default theme colors (complete design tokens)
|
|
102
141
|
const defaultColors = {
|
|
103
142
|
// Brand colors with variants
|
|
@@ -148,13 +187,13 @@ export async function generateTailwindCSS(classes, options = {}) {
|
|
|
148
187
|
900: '#111827'
|
|
149
188
|
},
|
|
150
189
|
|
|
151
|
-
// Semantic text colors
|
|
190
|
+
// Semantic text colors
|
|
152
191
|
'text-body': '#374151',
|
|
153
192
|
'text-heading': '#111827',
|
|
154
193
|
'text-muted': '#6b7280',
|
|
155
194
|
'text-inverse': '#ffffff',
|
|
156
195
|
|
|
157
|
-
// Semantic background colors
|
|
196
|
+
// Semantic background colors
|
|
158
197
|
'bg-default': '#ffffff',
|
|
159
198
|
'bg-subtle': '#f9fafb',
|
|
160
199
|
'bg-elevated': '#ffffff',
|
|
@@ -168,11 +207,9 @@ export async function generateTailwindCSS(classes, options = {}) {
|
|
|
168
207
|
// Merge with provided design tokens
|
|
169
208
|
let themeColors = defaultColors;
|
|
170
209
|
if (designTokens?.colors) {
|
|
171
|
-
// Convert flat design tokens to nested structure
|
|
172
210
|
const tokens = designTokens.colors;
|
|
173
211
|
themeColors = {
|
|
174
212
|
...defaultColors,
|
|
175
|
-
// Override with provided tokens
|
|
176
213
|
primary: {
|
|
177
214
|
DEFAULT: tokens.primary || defaultColors.primary.DEFAULT,
|
|
178
215
|
light: tokens.primaryLight || defaultColors.primary.light,
|
|
@@ -230,54 +267,45 @@ export async function generateTailwindCSS(classes, options = {}) {
|
|
|
230
267
|
};
|
|
231
268
|
}
|
|
232
269
|
|
|
233
|
-
// Create
|
|
234
|
-
const
|
|
235
|
-
content: [htmlPath],
|
|
236
|
-
safelist: classArray,
|
|
237
|
-
theme: {
|
|
238
|
-
extend: {
|
|
239
|
-
colors: themeColors,
|
|
240
|
-
fontFamily: designTokens?.typography?.fontFamily || {},
|
|
241
|
-
fontSize: designTokens?.typography?.fontSize || {},
|
|
242
|
-
spacing: designTokens?.spacing || {},
|
|
243
|
-
borderRadius: designTokens?.borderRadius || {},
|
|
244
|
-
boxShadow: designTokens?.boxShadow || {}
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
plugins: []
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
writeFileSync(configPath, `module.exports = ${JSON.stringify(config, null, 2)};`);
|
|
251
|
-
|
|
252
|
-
// Create input CSS with Tailwind directives
|
|
270
|
+
// Create input CSS with Tailwind v4 syntax
|
|
271
|
+
const themeCSS = generateThemeCSS(themeColors);
|
|
253
272
|
const inputCSS = `
|
|
254
|
-
@
|
|
255
|
-
|
|
256
|
-
|
|
273
|
+
@import "tailwindcss";
|
|
274
|
+
|
|
275
|
+
${themeCSS}
|
|
257
276
|
`.trim();
|
|
277
|
+
|
|
258
278
|
writeFileSync(inputPath, inputCSS);
|
|
259
279
|
|
|
260
280
|
// Create dummy HTML file with all classes (for content scanning)
|
|
261
281
|
const dummyHTML = `<div class="${classArray.join(' ')}"></div>`;
|
|
262
282
|
writeFileSync(htmlPath, dummyHTML);
|
|
263
283
|
|
|
264
|
-
// Run Tailwind CLI
|
|
284
|
+
// Run Tailwind v4 CLI
|
|
285
|
+
// Tailwind v4 auto-detects HTML files in the same directory
|
|
265
286
|
const tailwindCliPath = getTailwindCommand();
|
|
266
|
-
const command = `node "${tailwindCliPath}" -
|
|
287
|
+
const command = `node "${tailwindCliPath}" -i "${inputPath}" ${minify ? '--minify' : ''}`;
|
|
288
|
+
|
|
289
|
+
// Set NODE_PATH so tailwindcss module can be resolved
|
|
290
|
+
const builderNodeModules = join(__dirname, "..", "node_modules");
|
|
267
291
|
|
|
268
292
|
const { stdout } = await execAsync(command, {
|
|
269
|
-
|
|
293
|
+
cwd: tempDir,
|
|
294
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
295
|
+
env: {
|
|
296
|
+
...process.env,
|
|
297
|
+
NODE_PATH: builderNodeModules
|
|
298
|
+
}
|
|
270
299
|
});
|
|
271
300
|
|
|
272
301
|
return stdout.trim();
|
|
273
302
|
|
|
274
303
|
} catch (error) {
|
|
275
|
-
console.error('[Tailwind Generator] Error:', error);
|
|
304
|
+
console.error('[Tailwind Generator v4] Error:', error);
|
|
276
305
|
throw new Error(`Failed to generate Tailwind CSS: ${error.message}`);
|
|
277
306
|
} finally {
|
|
278
307
|
// Cleanup temp files
|
|
279
308
|
try {
|
|
280
|
-
unlinkSync(configPath);
|
|
281
309
|
unlinkSync(inputPath);
|
|
282
310
|
unlinkSync(htmlPath);
|
|
283
311
|
} catch (e) {
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tailwind CSS v4 Generator for Components
|
|
3
|
-
* Generates scoped Tailwind CSS using Tailwind v4's new CSS-based configuration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { exec } from 'child_process';
|
|
7
|
-
import { promisify } from 'util';
|
|
8
|
-
import { writeFileSync, unlinkSync, mkdtempSync } from 'fs';
|
|
9
|
-
import { join, dirname } from 'path';
|
|
10
|
-
import { tmpdir } from 'os';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const execAsync = promisify(exec);
|
|
14
|
-
|
|
15
|
-
// Get the directory where this module is located
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = dirname(__filename);
|
|
18
|
-
|
|
19
|
-
// Import createRequire for resolving packages
|
|
20
|
-
import { createRequire } from 'module';
|
|
21
|
-
const require = createRequire(import.meta.url);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get tailwindcss CLI path for execution
|
|
25
|
-
* @returns {string} Path to tailwindcss CLI
|
|
26
|
-
*/
|
|
27
|
-
function getTailwindCommand() {
|
|
28
|
-
try {
|
|
29
|
-
// Try to resolve @tailwindcss/cli package which contains the actual binary
|
|
30
|
-
const cliPath = require.resolve('@tailwindcss/cli');
|
|
31
|
-
return cliPath;
|
|
32
|
-
} catch (e) {
|
|
33
|
-
// Fallback: try to find tailwindcss package and use its CLI
|
|
34
|
-
try {
|
|
35
|
-
const tailwindPackagePath = require.resolve('tailwindcss/package.json');
|
|
36
|
-
const tailwindDir = dirname(tailwindPackagePath);
|
|
37
|
-
// Tailwind v4 uses @tailwindcss/cli
|
|
38
|
-
const cliPath = join(tailwindDir, '..', '@tailwindcss', 'cli', 'dist', 'index.js');
|
|
39
|
-
return cliPath;
|
|
40
|
-
} catch (e2) {
|
|
41
|
-
throw new Error('Could not find @tailwindcss/cli. Please install it: npm install @tailwindcss/cli');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Extract Tailwind classes from HTML content
|
|
48
|
-
* @param {string} html - HTML content
|
|
49
|
-
* @returns {Set<string>} Set of unique Tailwind classes
|
|
50
|
-
*/
|
|
51
|
-
export function extractTailwindClasses(html) {
|
|
52
|
-
const classes = new Set();
|
|
53
|
-
|
|
54
|
-
// Match class="..." and class='...'
|
|
55
|
-
const classRegex = /class=["']([^"']+)["']/g;
|
|
56
|
-
let match;
|
|
57
|
-
|
|
58
|
-
while ((match = classRegex.exec(html)) !== null) {
|
|
59
|
-
const classString = match[1];
|
|
60
|
-
// Split by whitespace and add each class
|
|
61
|
-
classString.split(/\s+/).forEach(cls => {
|
|
62
|
-
if (cls.trim()) {
|
|
63
|
-
classes.add(cls.trim());
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return classes;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Generate CSS with custom theme colors and design tokens for Tailwind v4
|
|
73
|
-
* @param {Object} colors - Color definitions
|
|
74
|
-
* @param {Object} designTokens - Complete design tokens (optional)
|
|
75
|
-
* @returns {string} CSS with @theme directive
|
|
76
|
-
*/
|
|
77
|
-
function generateThemeCSS(colors, designTokens = null) {
|
|
78
|
-
let themeCSS = `@theme {\n`;
|
|
79
|
-
|
|
80
|
-
// Helper to flatten nested colors
|
|
81
|
-
function flattenColors(obj, prefix = '') {
|
|
82
|
-
let css = '';
|
|
83
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
84
|
-
const colorKey = prefix ? `${prefix}-${key}` : key;
|
|
85
|
-
|
|
86
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
87
|
-
// Nested color (e.g., primary.light)
|
|
88
|
-
if (value.DEFAULT) {
|
|
89
|
-
css += ` --color-${colorKey}: ${value.DEFAULT};\n`;
|
|
90
|
-
}
|
|
91
|
-
css += flattenColors(value, colorKey);
|
|
92
|
-
} else {
|
|
93
|
-
// Simple color value
|
|
94
|
-
css += ` --color-${colorKey}: ${value};\n`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return css;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
themeCSS += flattenColors(colors);
|
|
101
|
-
|
|
102
|
-
// Add spacing if provided in design tokens
|
|
103
|
-
if (designTokens?.spacing) {
|
|
104
|
-
if (typeof designTokens.spacing === 'string') {
|
|
105
|
-
themeCSS += ` --spacing: ${designTokens.spacing};\n`;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
themeCSS += `}\n`;
|
|
110
|
-
|
|
111
|
-
return themeCSS;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Generate Tailwind CSS for specific classes (Tailwind v4)
|
|
116
|
-
* @param {Set<string>|Array<string>} classes - Tailwind classes to include
|
|
117
|
-
* @param {Object} options - Generation options
|
|
118
|
-
* @param {Object} options.designTokens - Custom design tokens (optional)
|
|
119
|
-
* @param {boolean} options.minify - Minify output CSS
|
|
120
|
-
* @returns {Promise<string>} Generated CSS
|
|
121
|
-
*/
|
|
122
|
-
export async function generateTailwindCSS(classes, options = {}) {
|
|
123
|
-
const {
|
|
124
|
-
designTokens = null,
|
|
125
|
-
minify = true
|
|
126
|
-
} = options;
|
|
127
|
-
|
|
128
|
-
const classArray = Array.isArray(classes) ? classes : Array.from(classes);
|
|
129
|
-
|
|
130
|
-
if (classArray.length === 0) {
|
|
131
|
-
return '/* No Tailwind classes found */';
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Create temporary directory
|
|
135
|
-
const tempDir = mkdtempSync(join(tmpdir(), 'tailwind-gen-'));
|
|
136
|
-
const inputPath = join(tempDir, 'input.css');
|
|
137
|
-
const htmlPath = join(tempDir, 'content.html');
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
// Default theme colors (complete design tokens)
|
|
141
|
-
const defaultColors = {
|
|
142
|
-
// Brand colors with variants
|
|
143
|
-
primary: {
|
|
144
|
-
DEFAULT: '#cf0ce9',
|
|
145
|
-
light: '#fb923c',
|
|
146
|
-
dark: '#c2410c'
|
|
147
|
-
},
|
|
148
|
-
secondary: {
|
|
149
|
-
DEFAULT: '#6366f1',
|
|
150
|
-
light: '#818cf8',
|
|
151
|
-
dark: '#4f46e5'
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
// Feedback colors with variants
|
|
155
|
-
success: {
|
|
156
|
-
DEFAULT: '#10b981',
|
|
157
|
-
light: '#34d399',
|
|
158
|
-
dark: '#059669'
|
|
159
|
-
},
|
|
160
|
-
error: {
|
|
161
|
-
DEFAULT: '#ef4444',
|
|
162
|
-
light: '#f87171',
|
|
163
|
-
dark: '#dc2626'
|
|
164
|
-
},
|
|
165
|
-
warning: {
|
|
166
|
-
DEFAULT: '#f59e0b',
|
|
167
|
-
light: '#fbbf24',
|
|
168
|
-
dark: '#d97706'
|
|
169
|
-
},
|
|
170
|
-
info: {
|
|
171
|
-
DEFAULT: '#3b82f6',
|
|
172
|
-
light: '#60a5fa',
|
|
173
|
-
dark: '#2563eb'
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
// Grays
|
|
177
|
-
gray: {
|
|
178
|
-
50: '#f9fafb',
|
|
179
|
-
100: '#f3f4f6',
|
|
180
|
-
200: '#e5e7eb',
|
|
181
|
-
300: '#d1d5db',
|
|
182
|
-
400: '#9ca3af',
|
|
183
|
-
500: '#6b7280',
|
|
184
|
-
600: '#4b5563',
|
|
185
|
-
700: '#374151',
|
|
186
|
-
800: '#1f2937',
|
|
187
|
-
900: '#111827'
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
// Semantic text colors
|
|
191
|
-
'text-body': '#374151',
|
|
192
|
-
'text-heading': '#111827',
|
|
193
|
-
'text-muted': '#6b7280',
|
|
194
|
-
'text-inverse': '#ffffff',
|
|
195
|
-
|
|
196
|
-
// Semantic background colors
|
|
197
|
-
'bg-default': '#ffffff',
|
|
198
|
-
'bg-subtle': '#f9fafb',
|
|
199
|
-
'bg-elevated': '#ffffff',
|
|
200
|
-
'bg-inverse': '#111827',
|
|
201
|
-
|
|
202
|
-
// Basic colors
|
|
203
|
-
black: '#000000',
|
|
204
|
-
white: '#ffffff'
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
// Merge with provided design tokens
|
|
208
|
-
let themeColors = defaultColors;
|
|
209
|
-
if (designTokens?.colors) {
|
|
210
|
-
const tokens = designTokens.colors;
|
|
211
|
-
themeColors = {
|
|
212
|
-
...defaultColors,
|
|
213
|
-
primary: {
|
|
214
|
-
DEFAULT: tokens.primary || defaultColors.primary.DEFAULT,
|
|
215
|
-
light: tokens.primaryLight || defaultColors.primary.light,
|
|
216
|
-
dark: tokens.primaryDark || defaultColors.primary.dark
|
|
217
|
-
},
|
|
218
|
-
secondary: {
|
|
219
|
-
DEFAULT: tokens.secondary || defaultColors.secondary.DEFAULT,
|
|
220
|
-
light: tokens.secondaryLight || defaultColors.secondary.light,
|
|
221
|
-
dark: tokens.secondaryDark || defaultColors.secondary.dark
|
|
222
|
-
},
|
|
223
|
-
success: {
|
|
224
|
-
DEFAULT: tokens.success || defaultColors.success.DEFAULT,
|
|
225
|
-
light: tokens.successLight || defaultColors.success.light,
|
|
226
|
-
dark: tokens.successDark || defaultColors.success.dark
|
|
227
|
-
},
|
|
228
|
-
error: {
|
|
229
|
-
DEFAULT: tokens.error || defaultColors.error.DEFAULT,
|
|
230
|
-
light: tokens.errorLight || defaultColors.error.light,
|
|
231
|
-
dark: tokens.errorDark || defaultColors.error.dark
|
|
232
|
-
},
|
|
233
|
-
warning: {
|
|
234
|
-
DEFAULT: tokens.warning || defaultColors.warning.DEFAULT,
|
|
235
|
-
light: tokens.warningLight || defaultColors.warning.light,
|
|
236
|
-
dark: tokens.warningDark || defaultColors.warning.dark
|
|
237
|
-
},
|
|
238
|
-
info: {
|
|
239
|
-
DEFAULT: tokens.info || defaultColors.info.DEFAULT,
|
|
240
|
-
light: tokens.infoLight || defaultColors.info.light,
|
|
241
|
-
dark: tokens.infoDark || defaultColors.info.dark
|
|
242
|
-
},
|
|
243
|
-
// Semantic colors
|
|
244
|
-
'text-body': tokens.textBody || defaultColors['text-body'],
|
|
245
|
-
'text-heading': tokens.textHeading || defaultColors['text-heading'],
|
|
246
|
-
'text-muted': tokens.textMuted || defaultColors['text-muted'],
|
|
247
|
-
'text-inverse': tokens.textInverse || defaultColors['text-inverse'],
|
|
248
|
-
'bg-default': tokens.bgDefault || defaultColors['bg-default'],
|
|
249
|
-
'bg-subtle': tokens.bgSubtle || defaultColors['bg-subtle'],
|
|
250
|
-
'bg-elevated': tokens.bgElevated || defaultColors['bg-elevated'],
|
|
251
|
-
'bg-inverse': tokens.bgInverse || defaultColors['bg-inverse'],
|
|
252
|
-
// Grays
|
|
253
|
-
gray: {
|
|
254
|
-
50: tokens.gray50 || defaultColors.gray[50],
|
|
255
|
-
100: tokens.gray100 || defaultColors.gray[100],
|
|
256
|
-
200: tokens.gray200 || defaultColors.gray[200],
|
|
257
|
-
300: tokens.gray300 || defaultColors.gray[300],
|
|
258
|
-
400: tokens.gray400 || defaultColors.gray[400],
|
|
259
|
-
500: tokens.gray500 || defaultColors.gray[500],
|
|
260
|
-
600: tokens.gray600 || defaultColors.gray[600],
|
|
261
|
-
700: tokens.gray700 || defaultColors.gray[700],
|
|
262
|
-
800: tokens.gray800 || defaultColors.gray[800],
|
|
263
|
-
900: tokens.gray900 || defaultColors.gray[900]
|
|
264
|
-
},
|
|
265
|
-
black: tokens.black || defaultColors.black,
|
|
266
|
-
white: tokens.white || defaultColors.white
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Create input CSS with Tailwind v4 syntax
|
|
271
|
-
const themeCSS = generateThemeCSS(themeColors);
|
|
272
|
-
const inputCSS = `
|
|
273
|
-
@import "tailwindcss";
|
|
274
|
-
|
|
275
|
-
${themeCSS}
|
|
276
|
-
`.trim();
|
|
277
|
-
|
|
278
|
-
writeFileSync(inputPath, inputCSS);
|
|
279
|
-
|
|
280
|
-
// Create dummy HTML file with all classes (for content scanning)
|
|
281
|
-
const dummyHTML = `<div class="${classArray.join(' ')}"></div>`;
|
|
282
|
-
writeFileSync(htmlPath, dummyHTML);
|
|
283
|
-
|
|
284
|
-
// Run Tailwind v4 CLI
|
|
285
|
-
// Tailwind v4 auto-detects HTML files in the same directory
|
|
286
|
-
const tailwindCliPath = getTailwindCommand();
|
|
287
|
-
const command = `node "${tailwindCliPath}" -i "${inputPath}" ${minify ? '--minify' : ''}`;
|
|
288
|
-
|
|
289
|
-
// Set NODE_PATH so tailwindcss module can be resolved
|
|
290
|
-
const builderNodeModules = join(__dirname, "..", "node_modules");
|
|
291
|
-
|
|
292
|
-
const { stdout } = await execAsync(command, {
|
|
293
|
-
cwd: tempDir,
|
|
294
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
295
|
-
env: {
|
|
296
|
-
...process.env,
|
|
297
|
-
NODE_PATH: builderNodeModules
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
return stdout.trim();
|
|
302
|
-
|
|
303
|
-
} catch (error) {
|
|
304
|
-
console.error('[Tailwind Generator v4] Error:', error);
|
|
305
|
-
throw new Error(`Failed to generate Tailwind CSS: ${error.message}`);
|
|
306
|
-
} finally {
|
|
307
|
-
// Cleanup temp files
|
|
308
|
-
try {
|
|
309
|
-
unlinkSync(inputPath);
|
|
310
|
-
unlinkSync(htmlPath);
|
|
311
|
-
} catch (e) {
|
|
312
|
-
// Ignore cleanup errors
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Generate Tailwind CSS for a component HTML file
|
|
319
|
-
* @param {string} html - Component HTML content
|
|
320
|
-
* @param {Object} options - Generation options
|
|
321
|
-
* @returns {Promise<{css: string, classes: string[]}>} Generated CSS and extracted classes
|
|
322
|
-
*/
|
|
323
|
-
export async function generateComponentCSS(html, options = {}) {
|
|
324
|
-
// Extract classes from HTML
|
|
325
|
-
const classes = extractTailwindClasses(html);
|
|
326
|
-
|
|
327
|
-
if (classes.size === 0) {
|
|
328
|
-
return {
|
|
329
|
-
css: '/* No Tailwind classes found in component */\n',
|
|
330
|
-
classes: []
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Generate CSS
|
|
335
|
-
const css = await generateTailwindCSS(classes, options);
|
|
336
|
-
|
|
337
|
-
return {
|
|
338
|
-
css,
|
|
339
|
-
classes: Array.from(classes)
|
|
340
|
-
};
|
|
341
|
-
}
|