@ethlete/core 5.0.0-next.60 ā 5.0.0-next.61
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/CHANGELOG.md +28 -0
- package/fesm2022/ethlete-core.mjs +269 -203
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/generators/migrate-to-v5/apply-replacements.js +14 -0
- package/generators/migrate-to-v5/color-naming.js +11 -57
- package/generators/migrate-to-v5/create-provider.js +56 -44
- package/generators/migrate-to-v5/migration-files.js +13 -0
- package/generators/migrate-to-v5/migration.js +31 -27
- package/generators/migrate-to-v5/removed-exports.js +7 -27
- package/generators/migrate-to-v5/router-state-service.js +118 -350
- package/generators/migrate-to-v5/schema.json +12 -0
- package/generators/migrate-to-v5/service-classes.js +108 -0
- package/generators/migrate-to-v5/viewport-service.js +195 -443
- package/generators/tailwind-4-color-theme/generator.js +71 -85
- package/package.json +1 -1
- package/types/ethlete-core.d.ts +41 -34
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { formatFiles, logger, visitNotIgnoredFiles } from '@nx/devkit';
|
|
2
|
+
import { posix } from 'path';
|
|
2
3
|
import { Project, SyntaxKind } from 'ts-morph';
|
|
3
4
|
export default async function generate(tree, schema) {
|
|
4
5
|
logger.log('\nš Starting Tailwind 4 theme generator...\n');
|
|
5
6
|
const themesPath = schema.themesPath || 'src/themes.ts';
|
|
6
7
|
const outputPath = schema.outputPath || 'src/styles/generated-tailwind-themes.css';
|
|
7
8
|
const prefix = schema.prefix || 'et';
|
|
8
|
-
// The runtime prefix drives the theme-swap CSS variables (--<runtimePrefix>-color-primary)
|
|
9
|
-
// and selectors (.<runtimePrefix>-color--<name>). It defaults to `prefix` so existing
|
|
10
|
-
// consumers who only ever passed one prefix see no change in output.
|
|
11
9
|
const runtimePrefix = schema.runtimePrefix || prefix;
|
|
12
|
-
// Step 1: Check if themes file exists
|
|
13
10
|
if (!tree.exists(themesPath)) {
|
|
14
11
|
logger.error(`ā Themes file not found at: ${themesPath}`);
|
|
15
12
|
logger.log(`\nPlease specify the correct path using --themesPath option.`);
|
|
@@ -17,16 +14,14 @@ export default async function generate(tree, schema) {
|
|
|
17
14
|
return;
|
|
18
15
|
}
|
|
19
16
|
logger.log(`š Reading themes from: ${themesPath}`);
|
|
20
|
-
// Step 2: Read and parse themes file
|
|
21
17
|
const themesContent = tree.read(themesPath, 'utf-8');
|
|
22
18
|
if (!themesContent) {
|
|
23
19
|
logger.error('ā Failed to read themes file');
|
|
24
20
|
return;
|
|
25
21
|
}
|
|
26
|
-
// Step 3: Try to extract themes using TypeScript
|
|
27
22
|
let themes;
|
|
28
23
|
try {
|
|
29
|
-
themes = extractThemesFromContent(themesContent, themesPath);
|
|
24
|
+
themes = extractThemesFromContent(tree, themesContent, themesPath);
|
|
30
25
|
logger.log(`ā
Found ${themes.length} theme(s)`);
|
|
31
26
|
}
|
|
32
27
|
catch (error) {
|
|
@@ -36,7 +31,7 @@ export default async function generate(tree, schema) {
|
|
|
36
31
|
logger.log(' export const THEMES = [...] satisfies Theme[];\n');
|
|
37
32
|
return;
|
|
38
33
|
}
|
|
39
|
-
//
|
|
34
|
+
// Apply the generation-time default override, if any. The shared theme
|
|
40
35
|
// definitions may mark a default via isDefault, but in a monorepo each app picks its
|
|
41
36
|
// own default at its generation invocation - the option wins over the definitions.
|
|
42
37
|
if (schema.defaultTheme) {
|
|
@@ -50,7 +45,6 @@ export default async function generate(tree, schema) {
|
|
|
50
45
|
return;
|
|
51
46
|
}
|
|
52
47
|
}
|
|
53
|
-
// Step 5: Validate theme configuration
|
|
54
48
|
try {
|
|
55
49
|
validateThemeConfiguration(themes);
|
|
56
50
|
}
|
|
@@ -59,27 +53,24 @@ export default async function generate(tree, schema) {
|
|
|
59
53
|
logger.error(` ${error instanceof Error ? error.message : String(error)}`);
|
|
60
54
|
return;
|
|
61
55
|
}
|
|
62
|
-
// Step 6: Generate Tailwind CSS
|
|
63
56
|
logger.log('\nšØ Generating Tailwind theme CSS...');
|
|
64
57
|
const css = generateTailwindThemeCss(themes, prefix, runtimePrefix, schema);
|
|
65
58
|
const typesOutputPath = schema.typesOutputPath ?? outputPath.replace(/\.css$/, '.d.ts');
|
|
66
59
|
if (typesOutputPath === outputPath) {
|
|
67
60
|
throw new Error('A custom outputPath must end in .css or be paired with a distinct typesOutputPath.');
|
|
68
61
|
}
|
|
69
|
-
// Step 7: Write the generated CSS file
|
|
70
62
|
const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'));
|
|
71
63
|
if (outputDir && !tree.exists(outputDir)) {
|
|
72
64
|
logger.log(`š Creating directory: ${outputDir}`);
|
|
73
65
|
}
|
|
74
66
|
tree.write(outputPath, css);
|
|
75
67
|
logger.log(`ā
Generated Tailwind themes at: ${outputPath}`);
|
|
76
|
-
//
|
|
68
|
+
// Generate the `EthleteColorThemeNameRegistry` augmentation, so `etProvideColor`
|
|
77
69
|
// (and anything else that accepts a `RegisteredColorThemeName`) is checked/autocompleted
|
|
78
70
|
// against this app's actual theme names, instead of a plain `string`.
|
|
79
71
|
const typesDts = generateColorThemeNameTypes(themes, schema);
|
|
80
72
|
tree.write(typesOutputPath, typesDts);
|
|
81
73
|
logger.log(`ā
Generated color theme name types at: ${typesOutputPath}`);
|
|
82
|
-
// Step 9: Try to find and update main styles file
|
|
83
74
|
const mainStylesFiles = findMainStylesFile(tree);
|
|
84
75
|
if (mainStylesFiles.length > 0) {
|
|
85
76
|
logger.log('\nš Found potential main styles files:');
|
|
@@ -92,26 +83,21 @@ export default async function generate(tree, schema) {
|
|
|
92
83
|
}
|
|
93
84
|
logger.log('\nā
Generation completed successfully!\n');
|
|
94
85
|
}
|
|
95
|
-
|
|
96
|
-
//#region Helper Functions
|
|
97
|
-
function extractThemesFromContent(content, filePath) {
|
|
98
|
-
// Create an in-memory TypeScript project
|
|
86
|
+
function extractThemesFromContent(tree, content, filePath) {
|
|
99
87
|
const project = new Project({
|
|
100
88
|
useInMemoryFileSystem: true,
|
|
101
89
|
compilerOptions: {
|
|
102
|
-
target: 99,
|
|
103
|
-
module: 99,
|
|
90
|
+
target: 99,
|
|
91
|
+
module: 99,
|
|
104
92
|
},
|
|
105
93
|
});
|
|
106
|
-
// Add the source file
|
|
107
94
|
const sourceFile = project.createSourceFile(filePath, content);
|
|
95
|
+
const context = { tree, project };
|
|
108
96
|
const themes = [];
|
|
109
|
-
// Find all exported const declarations
|
|
110
97
|
const exportedDeclarations = sourceFile.getVariableDeclarations().filter((decl) => {
|
|
111
98
|
const statement = decl.getVariableStatement();
|
|
112
99
|
return statement?.isExported();
|
|
113
100
|
});
|
|
114
|
-
// Look for the THEMES or themes array
|
|
115
101
|
const themesArray = exportedDeclarations.find((decl) => {
|
|
116
102
|
const name = decl.getName();
|
|
117
103
|
return name === 'THEMES' || name === 'themes';
|
|
@@ -131,7 +117,6 @@ function extractThemesFromContent(content, filePath) {
|
|
|
131
117
|
if (!initializer.isKind(SyntaxKind.ArrayLiteralExpression)) {
|
|
132
118
|
throw new Error('THEMES export must be an array literal');
|
|
133
119
|
}
|
|
134
|
-
// Get the array elements (these are references to the theme const declarations)
|
|
135
120
|
const elements = initializer.getElements();
|
|
136
121
|
for (const element of elements) {
|
|
137
122
|
let themeObj;
|
|
@@ -160,7 +145,7 @@ function extractThemesFromContent(content, filePath) {
|
|
|
160
145
|
continue;
|
|
161
146
|
}
|
|
162
147
|
try {
|
|
163
|
-
const theme = parseThemeObject(themeObj, sourceFile);
|
|
148
|
+
const theme = parseThemeObject(themeObj, sourceFile, context);
|
|
164
149
|
themes.push(theme);
|
|
165
150
|
}
|
|
166
151
|
catch (error) {
|
|
@@ -172,7 +157,7 @@ function extractThemesFromContent(content, filePath) {
|
|
|
172
157
|
}
|
|
173
158
|
return themes;
|
|
174
159
|
}
|
|
175
|
-
function parseThemeObject(obj, sourceFile) {
|
|
160
|
+
function parseThemeObject(obj, sourceFile, context) {
|
|
176
161
|
const properties = obj.getProperties();
|
|
177
162
|
const theme = {};
|
|
178
163
|
for (const prop of properties) {
|
|
@@ -204,7 +189,7 @@ function parseThemeObject(obj, sourceFile) {
|
|
|
204
189
|
case 'secondary':
|
|
205
190
|
case 'tertiary':
|
|
206
191
|
if (initializer.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
207
|
-
theme[propName] = parseThemeSwatch(initializer, sourceFile);
|
|
192
|
+
theme[propName] = parseThemeSwatch(initializer, sourceFile, context);
|
|
208
193
|
}
|
|
209
194
|
break;
|
|
210
195
|
}
|
|
@@ -214,8 +199,6 @@ function parseThemeObject(obj, sourceFile) {
|
|
|
214
199
|
}
|
|
215
200
|
return theme;
|
|
216
201
|
}
|
|
217
|
-
// Makes the named theme the sole default, overriding any isDefault flags in the theme
|
|
218
|
-
// definitions. Matches the theme's name or its CSS-safe form. Returns the resolved name.
|
|
219
202
|
function applyDefaultThemeOverride(themes, defaultTheme) {
|
|
220
203
|
const wanted = createCssThemeName(defaultTheme);
|
|
221
204
|
const target = themes.find((t) => t.name === defaultTheme || createCssThemeName(t.name) === wanted);
|
|
@@ -229,15 +212,12 @@ function applyDefaultThemeOverride(themes, defaultTheme) {
|
|
|
229
212
|
}
|
|
230
213
|
function validateThemeConfiguration(themes) {
|
|
231
214
|
const defaultThemes = themes.filter((t) => t.isDefault);
|
|
232
|
-
// Error: No default theme
|
|
233
215
|
if (defaultThemes.length === 0) {
|
|
234
216
|
throw new Error('No default theme found. At least one theme must have isDefault: true');
|
|
235
217
|
}
|
|
236
|
-
// Error: Multiple default themes
|
|
237
218
|
if (defaultThemes.length > 1) {
|
|
238
219
|
throw new Error(`Multiple default themes found: ${defaultThemes.map((t) => t.name).join(', ')}. Only one theme can have isDefault: true`);
|
|
239
220
|
}
|
|
240
|
-
// Error: Duplicate theme types
|
|
241
221
|
const typeMap = new Map();
|
|
242
222
|
for (const theme of themes) {
|
|
243
223
|
if (!theme.type) {
|
|
@@ -253,7 +233,7 @@ function validateThemeConfiguration(themes) {
|
|
|
253
233
|
}
|
|
254
234
|
}
|
|
255
235
|
}
|
|
256
|
-
function parseThemeSwatch(obj, sourceFile) {
|
|
236
|
+
function parseThemeSwatch(obj, sourceFile, context) {
|
|
257
237
|
const properties = obj.getProperties();
|
|
258
238
|
const swatch = {};
|
|
259
239
|
for (const prop of properties) {
|
|
@@ -265,7 +245,7 @@ function parseThemeSwatch(obj, sourceFile) {
|
|
|
265
245
|
if (!initializer) {
|
|
266
246
|
continue;
|
|
267
247
|
}
|
|
268
|
-
const colorMap = parseColorMap(initializer, sourceFile);
|
|
248
|
+
const colorMap = parseColorMap(initializer, sourceFile, context);
|
|
269
249
|
if (!colorMap) {
|
|
270
250
|
continue;
|
|
271
251
|
}
|
|
@@ -286,13 +266,58 @@ function parseThemeSwatch(obj, sourceFile) {
|
|
|
286
266
|
}
|
|
287
267
|
const COLOR_MAP_KEYS = ['default', 'hover', 'focus', 'active', 'disabled'];
|
|
288
268
|
const isColorMapKey = (name) => COLOR_MAP_KEYS.some((key) => key === name);
|
|
289
|
-
function
|
|
269
|
+
function unwrapTypeExpressions(expression) {
|
|
270
|
+
let current = expression;
|
|
271
|
+
while (current?.isKind(SyntaxKind.AsExpression) || current?.isKind(SyntaxKind.SatisfiesExpression)) {
|
|
272
|
+
current = current.getExpression();
|
|
273
|
+
}
|
|
274
|
+
return current;
|
|
275
|
+
}
|
|
276
|
+
function resolveConstInitializer(name, sourceFile, context) {
|
|
277
|
+
const local = sourceFile.getVariableDeclarations().find((decl) => decl.getName() === name);
|
|
278
|
+
if (local) {
|
|
279
|
+
const initializer = unwrapTypeExpressions(local.getInitializer());
|
|
280
|
+
return initializer ? { initializer, sourceFile } : null;
|
|
281
|
+
}
|
|
282
|
+
const importDecl = sourceFile
|
|
283
|
+
.getImportDeclarations()
|
|
284
|
+
.find((decl) => decl.getNamedImports().some((named) => (named.getAliasNode()?.getText() ?? named.getName()) === name));
|
|
285
|
+
if (!importDecl) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
const specifier = importDecl.getModuleSpecifierValue();
|
|
289
|
+
const filePath = sourceFile.getFilePath();
|
|
290
|
+
const importedName = importDecl
|
|
291
|
+
.getNamedImports()
|
|
292
|
+
.find((named) => (named.getAliasNode()?.getText() ?? named.getName()) === name)
|
|
293
|
+
?.getName() ?? name;
|
|
294
|
+
const warnUnresolved = (reason) => logger.warn(`ā ļø Could not resolve "${name}" imported from "${specifier}" in ${filePath} (${reason}); it is ignored.`);
|
|
295
|
+
if (!specifier.startsWith('.')) {
|
|
296
|
+
warnUnresolved('package imports are not followed');
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
const base = posix.join(posix.dirname(filePath), specifier).replace(/^\//, '');
|
|
300
|
+
const candidates = [`${base}.ts`, posix.join(base, 'index.ts'), base];
|
|
301
|
+
const resolvedPath = candidates.find((candidate) => context.tree.isFile(candidate));
|
|
302
|
+
if (!resolvedPath) {
|
|
303
|
+
warnUnresolved('file not found in the workspace');
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const importedFile = context.project.getSourceFile(resolvedPath) ??
|
|
307
|
+
context.project.createSourceFile(resolvedPath, context.tree.read(resolvedPath, 'utf-8') ?? '');
|
|
308
|
+
const declaration = importedFile.getVariableDeclarations().find((decl) => decl.getName() === importedName);
|
|
309
|
+
const initializer = unwrapTypeExpressions(declaration?.getInitializer());
|
|
310
|
+
if (!initializer?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
311
|
+
warnUnresolved(`not an object literal in ${resolvedPath}`);
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
return { initializer, sourceFile: importedFile };
|
|
315
|
+
}
|
|
316
|
+
function parseColorMap(initializer, sourceFile, context) {
|
|
290
317
|
if (initializer.isKind(SyntaxKind.Identifier)) {
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
return referencedInitializer ? parseColorMap(referencedInitializer, sourceFile) : null;
|
|
318
|
+
const resolved = resolveConstInitializer(initializer.getText(), sourceFile, context);
|
|
319
|
+
return resolved ? parseColorMap(resolved.initializer, resolved.sourceFile, context) : null;
|
|
294
320
|
}
|
|
295
|
-
// Handle spread expressions by resolving references
|
|
296
321
|
if (initializer.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
297
322
|
const colorMap = {};
|
|
298
323
|
const properties = initializer.getProperties();
|
|
@@ -305,30 +330,22 @@ function parseColorMap(initializer, sourceFile) {
|
|
|
305
330
|
}
|
|
306
331
|
}
|
|
307
332
|
else if (prop.isKind(SyntaxKind.SpreadAssignment)) {
|
|
308
|
-
// Handle spread: { ...onColorDark, disabled: '...' }
|
|
309
333
|
const spreadExpr = prop.getExpression();
|
|
310
334
|
if (spreadExpr.isKind(SyntaxKind.Identifier)) {
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const referencedObj = referencedDecl.getInitializer();
|
|
315
|
-
if (referencedObj?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
316
|
-
const spreadColors = parseColorMap(referencedObj, sourceFile);
|
|
317
|
-
if (spreadColors) {
|
|
318
|
-
Object.assign(colorMap, spreadColors);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
335
|
+
const resolved = resolveConstInitializer(spreadExpr.getText(), sourceFile, context);
|
|
336
|
+
if (resolved) {
|
|
337
|
+
Object.assign(colorMap, parseColorMap(resolved.initializer, resolved.sourceFile, context));
|
|
321
338
|
}
|
|
322
339
|
}
|
|
340
|
+
else {
|
|
341
|
+
logger.warn(`ā ļø Unsupported spread "...${spreadExpr.getText()}" in ${sourceFile.getFilePath()}; it is ignored.`);
|
|
342
|
+
}
|
|
323
343
|
}
|
|
324
344
|
}
|
|
325
|
-
// Apply fallbacks for required fields
|
|
326
345
|
const defaultColor = colorMap.default;
|
|
327
346
|
if (defaultColor) {
|
|
328
|
-
// Check if this is a ThemeColorMap (has hover, active, or disabled explicitly set)
|
|
329
347
|
const isThemeColorMap = colorMap.hover !== undefined || colorMap.active !== undefined || colorMap.disabled !== undefined;
|
|
330
348
|
if (isThemeColorMap) {
|
|
331
|
-
// For ThemeColorMap - apply fallbacks for all required fields
|
|
332
349
|
const hoverColor = colorMap.hover || defaultColor;
|
|
333
350
|
const result = {
|
|
334
351
|
default: defaultColor,
|
|
@@ -339,7 +356,6 @@ function parseColorMap(initializer, sourceFile) {
|
|
|
339
356
|
};
|
|
340
357
|
return result;
|
|
341
358
|
}
|
|
342
|
-
// For OnThemeColorMap - no fallbacks needed, all fields are optional except default
|
|
343
359
|
return colorMap;
|
|
344
360
|
}
|
|
345
361
|
return null;
|
|
@@ -347,7 +363,6 @@ function parseColorMap(initializer, sourceFile) {
|
|
|
347
363
|
return null;
|
|
348
364
|
}
|
|
349
365
|
function createCssThemeName(name) {
|
|
350
|
-
// Convert theme name to CSS-safe format (e.g., "Primary Blue" -> "primary-blue")
|
|
351
366
|
return name
|
|
352
367
|
.toLowerCase()
|
|
353
368
|
.replace(/\s+/g, '-')
|
|
@@ -390,31 +405,21 @@ function generateTailwindThemeCss(themes, utilityPrefix, runtimePrefix, schema)
|
|
|
390
405
|
*/
|
|
391
406
|
|
|
392
407
|
`;
|
|
393
|
-
// Validation is now done separately before this function is called
|
|
394
|
-
const defaultThemes = themes.filter((t) => t.isDefault);
|
|
395
|
-
const regularThemes = themes.filter((t) => !t.isDefault);
|
|
396
|
-
// Generate static Tailwind @theme block for each theme
|
|
397
408
|
for (const theme of themes) {
|
|
398
409
|
const name = createCssThemeName(theme.name);
|
|
399
|
-
// Add comment for theme section
|
|
400
410
|
tailwindVars.push(` /* ${theme.name} theme */`);
|
|
401
|
-
// Primary colors for Tailwind utilities
|
|
402
411
|
addTailwindColorVariants(tailwindVars, `${utilityPrefix}-${name}`, theme.primary.color);
|
|
403
412
|
tailwindVars.push('');
|
|
404
|
-
// On colors for Tailwind utilities
|
|
405
413
|
addTailwindColorVariants(tailwindVars, `${utilityPrefix}-on-${name}`, theme.primary.onColor);
|
|
406
414
|
tailwindVars.push('');
|
|
407
|
-
// Ink colors for standalone foreground usage
|
|
408
415
|
addTailwindColorVariants(tailwindVars, `${utilityPrefix}-${name}-ink`, theme.primary.inkColor || theme.primary.color);
|
|
409
416
|
tailwindVars.push('');
|
|
410
|
-
// Secondary colors if present
|
|
411
417
|
if (theme.secondary) {
|
|
412
418
|
addTailwindColorVariants(tailwindVars, `${utilityPrefix}-${name}-secondary`, theme.secondary.color);
|
|
413
419
|
tailwindVars.push('');
|
|
414
420
|
addTailwindColorVariants(tailwindVars, `${utilityPrefix}-on-${name}-secondary`, theme.secondary.onColor);
|
|
415
421
|
tailwindVars.push('');
|
|
416
422
|
}
|
|
417
|
-
// Tertiary colors if present
|
|
418
423
|
if (theme.tertiary) {
|
|
419
424
|
addTailwindColorVariants(tailwindVars, `${utilityPrefix}-${name}-tertiary`, theme.tertiary.color);
|
|
420
425
|
tailwindVars.push('');
|
|
@@ -423,10 +428,8 @@ function generateTailwindThemeCss(themes, utilityPrefix, runtimePrefix, schema)
|
|
|
423
428
|
}
|
|
424
429
|
}
|
|
425
430
|
tailwindVars.push('');
|
|
426
|
-
|
|
427
|
-
const
|
|
428
|
-
const hasSecondary = mainThemes.some((t) => t.secondary);
|
|
429
|
-
const hasTertiary = mainThemes.some((t) => t.tertiary);
|
|
431
|
+
const hasSecondary = themes.some((t) => t.secondary);
|
|
432
|
+
const hasTertiary = themes.some((t) => t.tertiary);
|
|
430
433
|
// Collect the dynamic theme colors once. They are emitted in `@theme` (so Tailwind generates
|
|
431
434
|
// the utilities) and ALSO re-declared on every color selector in the alias block below. A
|
|
432
435
|
// Tailwind `@theme` variable only lands on `:root`, so `rgb(var(--<runtime>-color-*))` resolves
|
|
@@ -444,19 +447,14 @@ function generateTailwindThemeCss(themes, utilityPrefix, runtimePrefix, schema)
|
|
|
444
447
|
}
|
|
445
448
|
tailwindVars.push(' /* Dynamic theme colors (references runtime CSS variables) */');
|
|
446
449
|
tailwindVars.push(...dynamicColorVars);
|
|
447
|
-
// Generate runtime CSS for all themes
|
|
448
450
|
themes.forEach((theme) => {
|
|
449
451
|
const name = createCssThemeName(theme.name);
|
|
450
|
-
// Determine if this is the default theme
|
|
451
452
|
const isDefault = theme.isDefault;
|
|
452
|
-
// Generate color variant (.<runtimePrefix>-color--{name})
|
|
453
453
|
if (isDefault) {
|
|
454
|
-
// Default color gets :root and .<runtimePrefix>-color--default selectors
|
|
455
454
|
const selectors = [':root', `.${runtimePrefix}-color--default`, `.${runtimePrefix}-color--${name}`];
|
|
456
455
|
themeVars.push(`${selectors.join(', ')} {`);
|
|
457
456
|
}
|
|
458
457
|
else {
|
|
459
|
-
// Regular colors just get their own class
|
|
460
458
|
themeVars.push(`.${runtimePrefix}-color--${name} {`);
|
|
461
459
|
}
|
|
462
460
|
addThemeColorVariants(themeVars, runtimePrefix, '', theme);
|
|
@@ -468,7 +466,6 @@ function generateTailwindThemeCss(themes, utilityPrefix, runtimePrefix, schema)
|
|
|
468
466
|
while (dynamicColorAliasLines.length && dynamicColorAliasLines[dynamicColorAliasLines.length - 1] === '') {
|
|
469
467
|
dynamicColorAliasLines.pop();
|
|
470
468
|
}
|
|
471
|
-
// Convenience var aliases - available on any element with a color class (or root for default)
|
|
472
469
|
const aliasBlock = `/* Convenience aliases (rgb + solid + opacity variants) */
|
|
473
470
|
@layer base {
|
|
474
471
|
:root, :where([class*="${runtimePrefix}-color--"]) {
|
|
@@ -508,14 +505,12 @@ function addDynamicThemeColors(vars, utilityPrefix, runtimePrefix, tailwindName,
|
|
|
508
505
|
if (addSpacingBefore && vars.length > 0 && vars[vars.length - 1] !== '') {
|
|
509
506
|
vars.push('');
|
|
510
507
|
}
|
|
511
|
-
// Color variants
|
|
512
508
|
vars.push(` --color-${utilityPrefix}-${tailwindName}: rgb(var(--${runtimePrefix}-color-${cssVarName}));`);
|
|
513
509
|
vars.push(` --color-${utilityPrefix}-${tailwindName}-hover: rgb(var(--${runtimePrefix}-color-${cssVarName}-hover));`);
|
|
514
510
|
vars.push(` --color-${utilityPrefix}-${tailwindName}-focus: rgb(var(--${runtimePrefix}-color-${cssVarName}-focus));`);
|
|
515
511
|
vars.push(` --color-${utilityPrefix}-${tailwindName}-active: rgb(var(--${runtimePrefix}-color-${cssVarName}-active));`);
|
|
516
512
|
vars.push(` --color-${utilityPrefix}-${tailwindName}-disabled: rgb(var(--${runtimePrefix}-color-${cssVarName}-disabled));`);
|
|
517
513
|
vars.push('');
|
|
518
|
-
// On-color variants
|
|
519
514
|
vars.push(` --color-${utilityPrefix}-on-${tailwindName}: rgb(var(--${runtimePrefix}-color-on-${cssVarName}));`);
|
|
520
515
|
vars.push(` --color-${utilityPrefix}-on-${tailwindName}-hover: rgb(var(--${runtimePrefix}-color-on-${cssVarName}-hover));`);
|
|
521
516
|
vars.push(` --color-${utilityPrefix}-on-${tailwindName}-focus: rgb(var(--${runtimePrefix}-color-on-${cssVarName}-focus));`);
|
|
@@ -532,25 +527,18 @@ function addDynamicInkColors(vars, utilityPrefix, runtimePrefix, tailwindName, c
|
|
|
532
527
|
vars.push('');
|
|
533
528
|
}
|
|
534
529
|
function addTailwindColorVariants(vars, colorName, colorSet) {
|
|
535
|
-
// Tailwind 4 requires --color-* prefix and rgb() wrapper
|
|
536
|
-
// Always generate all variants with fallbacks
|
|
537
530
|
vars.push(` --color-${colorName}: rgb(${colorSet.default});`);
|
|
538
|
-
// For hover: use hover if exists, otherwise default
|
|
539
531
|
const hoverValue = 'hover' in colorSet && colorSet.hover ? colorSet.hover : colorSet.default;
|
|
540
532
|
vars.push(` --color-${colorName}-hover: rgb(${hoverValue});`);
|
|
541
|
-
// For focus: use focus if exists, otherwise hover, otherwise default
|
|
542
533
|
const focusValue = 'focus' in colorSet && colorSet.focus ? colorSet.focus : hoverValue;
|
|
543
534
|
vars.push(` --color-${colorName}-focus: rgb(${focusValue});`);
|
|
544
|
-
// For active: use active if exists, otherwise hover, otherwise default
|
|
545
535
|
const activeValue = 'active' in colorSet && colorSet.active ? colorSet.active : hoverValue;
|
|
546
536
|
vars.push(` --color-${colorName}-active: rgb(${activeValue});`);
|
|
547
|
-
// For disabled: use disabled if exists, otherwise default
|
|
548
537
|
const disabledValue = 'disabled' in colorSet && colorSet.disabled ? colorSet.disabled : colorSet.default;
|
|
549
538
|
vars.push(` --color-${colorName}-disabled: rgb(${disabledValue});`);
|
|
550
539
|
}
|
|
551
540
|
function addThemeColorVariants(vars, prefix, altPrefix, theme) {
|
|
552
541
|
const addSwatch = (level, swatch) => {
|
|
553
|
-
// Color variants with fallbacks
|
|
554
542
|
const defaultColor = swatch.color.default;
|
|
555
543
|
const hoverColor = swatch.color.hover || defaultColor;
|
|
556
544
|
const focusColor = swatch.color.focus || hoverColor;
|
|
@@ -562,7 +550,6 @@ function addThemeColorVariants(vars, prefix, altPrefix, theme) {
|
|
|
562
550
|
vars.push(` --${prefix}-color-${altPrefix}${level}-active: ${activeColor};`);
|
|
563
551
|
vars.push(` --${prefix}-color-${altPrefix}${level}-disabled: ${disabledColor};`);
|
|
564
552
|
vars.push('');
|
|
565
|
-
// On color variants with fallbacks
|
|
566
553
|
const onDefaultColor = swatch.onColor.default;
|
|
567
554
|
const onHoverColor = swatch.onColor.hover || onDefaultColor;
|
|
568
555
|
const onFocusColor = swatch.onColor.focus || onHoverColor;
|
|
@@ -605,4 +592,3 @@ function findMainStylesFile(tree) {
|
|
|
605
592
|
});
|
|
606
593
|
return potentialFiles;
|
|
607
594
|
}
|
|
608
|
-
//#endregion
|