@upstart.gg/vite-plugins 0.0.37

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.
Files changed (58) hide show
  1. package/dist/vite-plugin-upstart-attrs.d.ts +29 -0
  2. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -0
  3. package/dist/vite-plugin-upstart-attrs.js +323 -0
  4. package/dist/vite-plugin-upstart-attrs.js.map +1 -0
  5. package/dist/vite-plugin-upstart-editor/plugin.d.ts +15 -0
  6. package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -0
  7. package/dist/vite-plugin-upstart-editor/plugin.js +55 -0
  8. package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -0
  9. package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts +12 -0
  10. package/dist/vite-plugin-upstart-editor/runtime/click-handler.d.ts.map +1 -0
  11. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +57 -0
  12. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -0
  13. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts +12 -0
  14. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.d.ts.map +1 -0
  15. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +91 -0
  16. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -0
  17. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +22 -0
  18. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -0
  19. package/dist/vite-plugin-upstart-editor/runtime/index.js +62 -0
  20. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -0
  21. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +15 -0
  22. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -0
  23. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +292 -0
  24. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -0
  25. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +126 -0
  26. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -0
  27. package/dist/vite-plugin-upstart-editor/runtime/types.js +1 -0
  28. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts +15 -0
  29. package/dist/vite-plugin-upstart-editor/runtime/utils.d.ts.map +1 -0
  30. package/dist/vite-plugin-upstart-editor/runtime/utils.js +26 -0
  31. package/dist/vite-plugin-upstart-editor/runtime/utils.js.map +1 -0
  32. package/dist/vite-plugin-upstart-theme.d.ts +22 -0
  33. package/dist/vite-plugin-upstart-theme.d.ts.map +1 -0
  34. package/dist/vite-plugin-upstart-theme.js +179 -0
  35. package/dist/vite-plugin-upstart-theme.js.map +1 -0
  36. package/package.json +63 -0
  37. package/src/tests/fixtures/routes/default-layout.tsx +10 -0
  38. package/src/tests/fixtures/routes/dynamic-route.tsx +10 -0
  39. package/src/tests/fixtures/routes/missing-attributes.tsx +8 -0
  40. package/src/tests/fixtures/routes/missing-path.tsx +9 -0
  41. package/src/tests/fixtures/routes/valid-full.tsx +15 -0
  42. package/src/tests/fixtures/routes/valid-minimal.tsx +10 -0
  43. package/src/tests/fixtures/routes/with-comments.tsx +12 -0
  44. package/src/tests/fixtures/routes/with-nested-objects.tsx +15 -0
  45. package/src/tests/upstart-editor-api.test.ts +367 -0
  46. package/src/tests/vite-plugin-upstart-attrs.test.ts +1189 -0
  47. package/src/tests/vite-plugin-upstart-editor.test.ts +81 -0
  48. package/src/upstart-editor-api.ts +204 -0
  49. package/src/vite-plugin-upstart-attrs.ts +708 -0
  50. package/src/vite-plugin-upstart-editor/PLAN.md +1391 -0
  51. package/src/vite-plugin-upstart-editor/plugin.ts +73 -0
  52. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +80 -0
  53. package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +135 -0
  54. package/src/vite-plugin-upstart-editor/runtime/index.ts +90 -0
  55. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +401 -0
  56. package/src/vite-plugin-upstart-editor/runtime/types.ts +120 -0
  57. package/src/vite-plugin-upstart-editor/runtime/utils.ts +34 -0
  58. package/src/vite-plugin-upstart-theme.ts +314 -0
@@ -0,0 +1,314 @@
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
+ const processed = processTheme(theme);
210
+ const lines: string[] = [
211
+ ` name: "${processed.id}";`,
212
+ ` color-scheme: ${processed.browserColorScheme};`,
213
+ ...((defaultTheme === "dark" && theme.browserColorScheme === "dark") ||
214
+ (defaultTheme === "light" && theme.browserColorScheme === "light")
215
+ ? [` default: true;`]
216
+ : []),
217
+ // ...(type === "dark" && defaultTheme !== "dark" ? [` prefersdark: true;`] : [` prefersdark: false;`]),
218
+ ];
219
+
220
+ // Color variables
221
+ for (const [colorKey, colorValue] of Object.entries(processed.colors)) {
222
+ const kebabKey = ["base100", "base200", "base300"].includes(colorKey)
223
+ ? colorKey.replace("base", "base-")
224
+ : camelToKebab(colorKey);
225
+ lines.push(` --color-${kebabKey}: ${colorValue};`);
226
+ }
227
+
228
+ lines.push("");
229
+
230
+ // Layout variables
231
+ lines.push(` --radius-selector: ${processed.radiusSelector};`);
232
+ lines.push(` --radius-field: ${processed.radiusField};`);
233
+ lines.push(` --radius-box: ${processed.radiusBox};`);
234
+ lines.push(` --size-selector: ${processed.sizeSelector};`);
235
+ lines.push(` --size-field: ${processed.sizeField};`);
236
+ lines.push(` --border: ${processed.border};`);
237
+ lines.push(` --depth: ${processed.depth ? 1 : 0};`);
238
+ lines.push(` --noise: 0;`);
239
+
240
+ // return the generated lines
241
+ return lines.join("\n");
242
+ }
243
+
244
+ /**
245
+ * Unplugin-based theme generator
246
+ */
247
+ export const upstartTheme = createUnplugin<PluginOptions>((opts) => {
248
+ let generated = false;
249
+
250
+ return {
251
+ name: "unplugin-theme-generator",
252
+ apply: "serve",
253
+
254
+ vite: {
255
+ configResolved() {
256
+ // Generate the CSS file once when config is resolved
257
+ if (generated) return;
258
+ generated = true;
259
+
260
+ if (!opts?.themes?.light) {
261
+ console.warn("No light theme provided to unplugin-theme-generator. A light theme is required.");
262
+ return;
263
+ }
264
+
265
+ const outputDir = path.dirname(opts.outputPath);
266
+ if (!fs.existsSync(outputDir)) {
267
+ fs.mkdirSync(outputDir, { recursive: true });
268
+ }
269
+
270
+ const { themes, fontsDisplay = "swap" } = opts;
271
+
272
+ // Generate fonts.css file
273
+ const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);
274
+ const fontsCSSPath = path.join(outputDir, "fonts.css");
275
+ fs.writeFileSync(fontsCSSPath, fontsCSS);
276
+ console.log(`✓ Generated fonts CSS: ${fontsCSSPath}`);
277
+
278
+ const output: string[] = [];
279
+
280
+ // Generate @theme block with fonts from both themes
281
+ output.push(generateThemeBlock(themes.light, themes.dark));
282
+ output.push("");
283
+
284
+ // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly
285
+ // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563
286
+ // Generate @theme block for dark theme if present
287
+ if (themes.dark) {
288
+ output.push("/* Dark theme variables */");
289
+ output.push('@plugin "daisyui/theme" {');
290
+ output.push(generateThemeVariables(themes.dark, "dark", themes.default));
291
+ output.push("}");
292
+ output.push("");
293
+ }
294
+
295
+ // Generate @theme block for light theme
296
+ if (themes.light?.id !== themes.dark?.id) {
297
+ output.push("/* Light theme variables */");
298
+ output.push('@plugin "daisyui/theme" {');
299
+ output.push(generateThemeVariables(themes.light, "light", themes.default));
300
+ output.push("}");
301
+ output.push("");
302
+ }
303
+
304
+ // Write the generated CSS file
305
+ const cssContent = output.join("\n");
306
+ fs.writeFileSync(opts.outputPath, cssContent);
307
+
308
+ console.log(`✓ Generated theme CSS: ${opts.outputPath}`);
309
+ },
310
+ },
311
+ };
312
+ });
313
+
314
+ export default upstartTheme.vite;