@upstart.gg/vite-plugins 0.0.139
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/vite-plugin-upstart-attrs.d.ts +29 -0
- package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-attrs.js +286 -0
- package/dist/vite-plugin-upstart-attrs.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/plugin.d.ts +15 -0
- package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/plugin.js +55 -0
- package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts +12 -0
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +57 -0
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts +12 -0
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +91 -0
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +22 -0
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/index.js +62 -0
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +15 -0
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +292 -0
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +126 -0
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/types.js +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts +15 -0
- package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/utils.js +26 -0
- package/dist/vite-plugin-upstart-editor/runtime/utils.js.map +1 -0
- package/dist/vite-plugin-upstart-routes.d.ts +20 -0
- package/dist/vite-plugin-upstart-routes.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-routes.js +143 -0
- package/dist/vite-plugin-upstart-routes.js.map +1 -0
- package/dist/vite-plugin-upstart-theme.d.ts +22 -0
- package/dist/vite-plugin-upstart-theme.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-theme.js +180 -0
- package/dist/vite-plugin-upstart-theme.js.map +1 -0
- package/package.json +63 -0
- package/src/tests/fixtures/routes/default-layout.tsx +10 -0
- package/src/tests/fixtures/routes/dynamic-route.tsx +10 -0
- package/src/tests/fixtures/routes/missing-attributes.tsx +8 -0
- package/src/tests/fixtures/routes/missing-path.tsx +9 -0
- package/src/tests/fixtures/routes/valid-full.tsx +15 -0
- package/src/tests/fixtures/routes/valid-minimal.tsx +10 -0
- package/src/tests/fixtures/routes/with-comments.tsx +12 -0
- package/src/tests/fixtures/routes/with-nested-objects.tsx +15 -0
- package/src/tests/upstart-editor-api.test.ts +367 -0
- package/src/tests/vite-plugin-upstart-attrs.test.ts +957 -0
- package/src/tests/vite-plugin-upstart-editor.test.ts +81 -0
- package/src/tests/vite-plugin-upstart-routes.test.ts +220 -0
- package/src/upstart-editor-api.ts +204 -0
- package/src/vite-plugin-upstart-attrs.ts +616 -0
- package/src/vite-plugin-upstart-editor/PLAN.md +1391 -0
- package/src/vite-plugin-upstart-editor/plugin.ts +73 -0
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +80 -0
- package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +135 -0
- package/src/vite-plugin-upstart-editor/runtime/index.ts +90 -0
- package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +401 -0
- package/src/vite-plugin-upstart-editor/runtime/types.ts +120 -0
- package/src/vite-plugin-upstart-editor/runtime/utils.ts +34 -0
- package/src/vite-plugin-upstart-theme.ts +321 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { createUnplugin } from "unplugin";
|
|
2
|
+
import {
|
|
3
|
+
GenericFont,
|
|
4
|
+
GoogleFont,
|
|
5
|
+
type Theme,
|
|
6
|
+
fontStacksFonts,
|
|
7
|
+
processTheme,
|
|
8
|
+
} from "@upstart.gg/sdk/themes/theme";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
|
|
12
|
+
interface PluginOptions {
|
|
13
|
+
themes: {
|
|
14
|
+
light: Theme;
|
|
15
|
+
dark?: Theme;
|
|
16
|
+
default: "light" | "dark";
|
|
17
|
+
};
|
|
18
|
+
outputPath: string;
|
|
19
|
+
/** Google Fonts display parameter (default: 'swap') */
|
|
20
|
+
fontsDisplay?: "auto" | "block" | "swap" | "fallback" | "optional";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Collects Google Fonts from a theme's typography configuration
|
|
25
|
+
*/
|
|
26
|
+
function collectGoogleFonts(theme: Theme) {
|
|
27
|
+
const { typography } = theme;
|
|
28
|
+
if (!typography) return [];
|
|
29
|
+
|
|
30
|
+
const googleFonts: Array<GoogleFont> = [];
|
|
31
|
+
const seen = new Set<string>();
|
|
32
|
+
|
|
33
|
+
const addFont = (font?: GenericFont) => {
|
|
34
|
+
if (font?.type === "google" && !seen.has(font.family)) {
|
|
35
|
+
seen.add(font.family);
|
|
36
|
+
googleFonts.push(font);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
addFont(typography.sans);
|
|
41
|
+
addFont(typography.serif);
|
|
42
|
+
if (typography.others) {
|
|
43
|
+
Object.values(typography.others).forEach(addFont);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return googleFonts;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generates the fonts.css file content with Google Fonts imports
|
|
51
|
+
*/
|
|
52
|
+
function generateFontsCSS(
|
|
53
|
+
lightTheme: Theme,
|
|
54
|
+
darkTheme: Theme | null = null,
|
|
55
|
+
display: "auto" | "block" | "swap" | "fallback" | "optional" = "swap",
|
|
56
|
+
): string {
|
|
57
|
+
// Collect fonts from both themes
|
|
58
|
+
const allFonts = new Map<string, GoogleFont>();
|
|
59
|
+
|
|
60
|
+
for (const font of collectGoogleFonts(lightTheme)) {
|
|
61
|
+
allFonts.set(font.family, font);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (darkTheme) {
|
|
65
|
+
for (const font of collectGoogleFonts(darkTheme)) {
|
|
66
|
+
allFonts.set(font.family, font);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (allFonts.size === 0) {
|
|
71
|
+
return "/* No Google Fonts to import */\n";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Build the Google Fonts URL
|
|
75
|
+
const families = Array.from(allFonts.values()).map((font) => {
|
|
76
|
+
const name = font.family.replace(/ /g, "+");
|
|
77
|
+
if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) {
|
|
78
|
+
return `family=${name}:wght@${font.weights[0]}..${font.weights.at(-1)}`;
|
|
79
|
+
}
|
|
80
|
+
return `family=${name}`;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const url = `https://fonts.googleapis.com/css2?${families.join("&")}&display=${display}`;
|
|
84
|
+
|
|
85
|
+
return `/* Google Fonts - Auto-generated by vite-plugin-upstart-theme */\n@import url("${url}");\n`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Converts a font definition to a CSS font-family string
|
|
90
|
+
*/
|
|
91
|
+
function fontToCss(font: Theme["typography"]["sans"]): string {
|
|
92
|
+
if (!font) return "";
|
|
93
|
+
|
|
94
|
+
if (font.type === "stack") {
|
|
95
|
+
return fontStacksFonts[font.family];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (font.type === "google") {
|
|
99
|
+
// For Google fonts, we just use the family name
|
|
100
|
+
// The actual font loading will be handled separately
|
|
101
|
+
return `"${font.family}", system-ui, sans-serif`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Converts camelCase to kebab-case
|
|
109
|
+
*/
|
|
110
|
+
function camelToKebab(str: string): string {
|
|
111
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generates @theme block with font definitions
|
|
116
|
+
*/
|
|
117
|
+
function generateThemeBlock(
|
|
118
|
+
lightTheme: Theme,
|
|
119
|
+
darkTheme: Theme | null = null,
|
|
120
|
+
defaultTheme: "light" | "dark" = "light",
|
|
121
|
+
): string {
|
|
122
|
+
const fontDefinitions = new Map<string, string>();
|
|
123
|
+
|
|
124
|
+
// Process light theme fonts
|
|
125
|
+
const lightTypography = lightTheme.typography;
|
|
126
|
+
|
|
127
|
+
// Add sans font
|
|
128
|
+
if (lightTypography.sans) {
|
|
129
|
+
const fontCss = fontToCss(lightTypography.sans);
|
|
130
|
+
if (fontCss) {
|
|
131
|
+
fontDefinitions.set("sans", fontCss);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add serif font
|
|
136
|
+
if (lightTypography.serif) {
|
|
137
|
+
const fontCss = fontToCss(lightTypography.serif);
|
|
138
|
+
if (fontCss) {
|
|
139
|
+
fontDefinitions.set("serif", fontCss);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add other fonts
|
|
144
|
+
if (lightTypography.others) {
|
|
145
|
+
for (const [name, font] of Object.entries(lightTypography.others)) {
|
|
146
|
+
const fontCss = fontToCss(font);
|
|
147
|
+
if (fontCss) {
|
|
148
|
+
fontDefinitions.set(name, fontCss);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Process dark theme fonts if provided
|
|
154
|
+
if (darkTheme) {
|
|
155
|
+
const darkTypography = darkTheme.typography;
|
|
156
|
+
|
|
157
|
+
if (darkTypography.sans) {
|
|
158
|
+
const fontCss = fontToCss(darkTypography.sans);
|
|
159
|
+
if (fontCss) {
|
|
160
|
+
fontDefinitions.set("sans", fontCss);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (darkTypography.serif) {
|
|
165
|
+
const fontCss = fontToCss(darkTypography.serif);
|
|
166
|
+
if (fontCss) {
|
|
167
|
+
fontDefinitions.set("serif", fontCss);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (darkTypography.others) {
|
|
172
|
+
for (const [name, font] of Object.entries(darkTypography.others)) {
|
|
173
|
+
const fontCss = fontToCss(font);
|
|
174
|
+
if (fontCss) {
|
|
175
|
+
fontDefinitions.set(name, fontCss);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const fontLines = Array.from(fontDefinitions.entries())
|
|
182
|
+
.map(([name, value]) => ` --font-${name}: ${value};`)
|
|
183
|
+
.join("\n");
|
|
184
|
+
|
|
185
|
+
return `/* Fonts in the tailwind @theme block */
|
|
186
|
+
@theme {
|
|
187
|
+
${fontLines}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Exclude properties since it produce warnings and is not important */
|
|
191
|
+
@plugin "daisyui" {
|
|
192
|
+
exclude: properties;
|
|
193
|
+
themes: ${lightTheme.id} ${defaultTheme === "light" || !darkTheme ? "--default" : ""}${darkTheme?.id && darkTheme?.id !== lightTheme.id ? `, ${darkTheme.id} ${defaultTheme === "dark" ? "--default" : "--prefersdark"}` : ""};
|
|
194
|
+
logs: true;
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Generates @theme block for a single theme with colors and layout variables
|
|
203
|
+
*/
|
|
204
|
+
function generateThemeVariables(
|
|
205
|
+
theme: Theme,
|
|
206
|
+
type: "light" | "dark",
|
|
207
|
+
defaultTheme: "light" | "dark",
|
|
208
|
+
): string {
|
|
209
|
+
console.log(
|
|
210
|
+
"generateThemeVariables: generating variables for theme",
|
|
211
|
+
theme.id,
|
|
212
|
+
"of type",
|
|
213
|
+
type,
|
|
214
|
+
"defaultTheme:",
|
|
215
|
+
defaultTheme,
|
|
216
|
+
);
|
|
217
|
+
const processed = processTheme(theme);
|
|
218
|
+
const lines: string[] = [
|
|
219
|
+
` name: "${processed.id}";`,
|
|
220
|
+
` color-scheme: ${processed.browserColorScheme};`,
|
|
221
|
+
...((defaultTheme === "dark" && theme.browserColorScheme === "dark") ||
|
|
222
|
+
(defaultTheme === "light" && theme.browserColorScheme === "light")
|
|
223
|
+
? [` default: true;`]
|
|
224
|
+
: []),
|
|
225
|
+
// ...(type === "dark" && defaultTheme !== "dark" ? [` prefersdark: true;`] : [` prefersdark: false;`]),
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
// Color variables
|
|
229
|
+
for (const [colorKey, colorValue] of Object.entries(processed.colors)) {
|
|
230
|
+
const kebabKey = ["base100", "base200", "base300"].includes(colorKey)
|
|
231
|
+
? colorKey.replace("base", "base-")
|
|
232
|
+
: camelToKebab(colorKey);
|
|
233
|
+
lines.push(` --color-${kebabKey}: ${colorValue};`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
lines.push("");
|
|
237
|
+
|
|
238
|
+
// Layout variables
|
|
239
|
+
lines.push(` --radius-selector: ${processed.radiusSelector};`);
|
|
240
|
+
lines.push(` --radius-field: ${processed.radiusField};`);
|
|
241
|
+
lines.push(` --radius-box: ${processed.radiusBox};`);
|
|
242
|
+
lines.push(` --size-selector: ${processed.sizeSelector};`);
|
|
243
|
+
lines.push(` --size-field: ${processed.sizeField};`);
|
|
244
|
+
lines.push(` --border: ${processed.border};`);
|
|
245
|
+
lines.push(` --depth: ${processed.depth ? 1 : 0};`);
|
|
246
|
+
lines.push(` --noise: 0;`);
|
|
247
|
+
|
|
248
|
+
return lines.join("\n");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Unplugin-based theme generator
|
|
253
|
+
*/
|
|
254
|
+
export const upstartTheme = createUnplugin<PluginOptions>((opts) => {
|
|
255
|
+
let generated = false;
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
name: "unplugin-theme-generator",
|
|
259
|
+
apply: "serve",
|
|
260
|
+
|
|
261
|
+
vite: {
|
|
262
|
+
configResolved() {
|
|
263
|
+
// Generate the CSS file once when config is resolved
|
|
264
|
+
if (generated) return;
|
|
265
|
+
generated = true;
|
|
266
|
+
|
|
267
|
+
if (!opts?.themes?.light) {
|
|
268
|
+
console.warn("No light theme provided to unplugin-theme-generator. A light theme is required.");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const outputDir = path.dirname(opts.outputPath);
|
|
273
|
+
if (!fs.existsSync(outputDir)) {
|
|
274
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const { themes, fontsDisplay = "swap" } = opts;
|
|
278
|
+
|
|
279
|
+
// Generate fonts.css file
|
|
280
|
+
const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);
|
|
281
|
+
const fontsCSSPath = path.join(outputDir, "fonts.css");
|
|
282
|
+
fs.writeFileSync(fontsCSSPath, fontsCSS);
|
|
283
|
+
console.log(`✓ Generated fonts CSS: ${fontsCSSPath}`);
|
|
284
|
+
|
|
285
|
+
const output: string[] = [];
|
|
286
|
+
|
|
287
|
+
// Generate @theme block with fonts from both themes
|
|
288
|
+
output.push(generateThemeBlock(themes.light, themes.dark));
|
|
289
|
+
output.push("");
|
|
290
|
+
|
|
291
|
+
// IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly
|
|
292
|
+
// @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563
|
|
293
|
+
// Generate @theme block for dark theme if present
|
|
294
|
+
if (themes.dark) {
|
|
295
|
+
output.push("/* Dark theme variables */");
|
|
296
|
+
output.push('@plugin "daisyui/theme" {');
|
|
297
|
+
output.push(generateThemeVariables(themes.dark, "dark", themes.default));
|
|
298
|
+
output.push("}");
|
|
299
|
+
output.push("");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Generate @theme block for light theme
|
|
303
|
+
if (themes.light?.id !== themes.dark?.id) {
|
|
304
|
+
output.push("/* Light theme variables */");
|
|
305
|
+
output.push('@plugin "daisyui/theme" {');
|
|
306
|
+
output.push(generateThemeVariables(themes.light, "light", themes.default));
|
|
307
|
+
output.push("}");
|
|
308
|
+
output.push("");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Write the generated CSS file
|
|
312
|
+
const cssContent = output.join("\n");
|
|
313
|
+
fs.writeFileSync(opts.outputPath, cssContent);
|
|
314
|
+
|
|
315
|
+
console.log(`✓ Generated theme CSS: ${opts.outputPath}`);
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
export default upstartTheme.vite;
|