@upstart.gg/vite-plugins 0.1.27 → 0.1.29

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.
@@ -11,6 +11,8 @@ interface PluginOptions {
11
11
  outputPath: string;
12
12
  /** Google Fonts display parameter (default: 'swap') */
13
13
  fontsDisplay?: "auto" | "block" | "swap" | "fallback" | "optional";
14
+ /** CSS framework to target (default: 'daisy-ui') */
15
+ cssFramework?: "daisy-ui" | "shadcn-ui";
14
16
  }
15
17
  /**
16
18
  * Unplugin-based theme generator
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-theme.d.ts","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"mappings":";;;;UAWU,aAAA;EACR,MAAA;IACE,KAAA,EAAO,KAAA;IACP,IAAA,GAAO,KAAA;IACP,OAAA;EAAA;EAEF,UAAA;EALA;EAOA,YAAA;AAAA;;;;cAmOW,YAAA,EAAY,QAAA,CAAA,gBAAA,CAAA,aAAA;AAAA,cA8DtB,QAAA"}
1
+ {"version":3,"file":"vite-plugin-upstart-theme.d.ts","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"mappings":";;;;UAWU,aAAA;EACR,MAAA;IACE,KAAA,EAAO,KAAA;IACP,IAAA,GAAO,KAAA;IACP,OAAA;EAAA;EAEF,UAAA;EALA;EAOA,YAAA;EANS;EAQT,YAAA;AAAA;;;;cA4VW,YAAA,EAAY,QAAA,CAAA,gBAAA,CAAA,aAAA;AAAA,cAoEtB,QAAA"}
@@ -100,7 +100,7 @@ function generateThemeBlock(lightTheme, darkTheme = null, defaultTheme = "light"
100
100
  /**
101
101
  * Generates @theme block for a single theme with colors and layout variables
102
102
  */
103
- function generateThemeVariables(theme, type, defaultTheme) {
103
+ function generateThemeVariables(theme, defaultTheme) {
104
104
  const processed = processTheme(theme);
105
105
  const lines = [
106
106
  ` name: "${processed.id}";`,
@@ -127,6 +127,112 @@ function generateThemeVariables(theme, type, defaultTheme) {
127
127
  return lines.join("\n");
128
128
  }
129
129
  /**
130
+ * Builds the CSS variable block for a single shadcn theme (`:root` or `.dark`)
131
+ */
132
+ function buildShadcnVars(theme) {
133
+ const processed = processTheme(theme);
134
+ const { colors } = processed;
135
+ const monoFont = theme.typography?.others?.mono;
136
+ const monoFamilyValue = monoFont ? fontToCss(monoFont) : "ui-monospace, monospace";
137
+ const sansFamilyValue = theme.typography?.sans ? fontToCss(theme.typography.sans) : "ui-sans-serif, system-ui, sans-serif";
138
+ return [
139
+ ["--radius", processed.radiusField],
140
+ ["--background", colors.base100],
141
+ ["--foreground", colors.baseContent],
142
+ ["--card", colors.base200],
143
+ ["--card-foreground", colors.baseContent],
144
+ ["--popover", colors.base200],
145
+ ["--popover-foreground", colors.baseContent],
146
+ ["--primary", colors.primary],
147
+ ["--primary-foreground", colors.primaryContent],
148
+ ["--secondary", colors.secondary],
149
+ ["--secondary-foreground", colors.secondaryContent],
150
+ ["--muted", colors.neutral],
151
+ ["--muted-foreground", colors.neutralContent],
152
+ ["--accent", colors.base300],
153
+ ["--accent-foreground", colors.baseContent],
154
+ ["--destructive", colors.error],
155
+ ["--border", colors.base300],
156
+ ["--input", colors.base300],
157
+ ["--ring", colors.primary],
158
+ ["--chart-1", colors.primary],
159
+ ["--chart-2", colors.secondary],
160
+ ["--chart-3", colors.accent],
161
+ ["--chart-4", colors.success],
162
+ ["--chart-5", colors.warning],
163
+ ["--sidebar", colors.base200],
164
+ ["--sidebar-foreground", colors.baseContent],
165
+ ["--sidebar-primary", colors.primary],
166
+ ["--sidebar-primary-foreground", colors.primaryContent],
167
+ ["--sidebar-accent", colors.base300],
168
+ ["--sidebar-accent-foreground", colors.baseContent],
169
+ ["--sidebar-border", colors.base300],
170
+ ["--sidebar-ring", colors.primary],
171
+ ["--font-sans", sansFamilyValue],
172
+ ["--font-mono", monoFamilyValue]
173
+ ].map(([k, v]) => ` ${k}: ${v};`).join("\n");
174
+ }
175
+ /**
176
+ * Generates the full shadcn-compatible CSS output (no DaisyUI, no @plugin blocks).
177
+ * Uses :root for light theme and .dark for dark theme.
178
+ */
179
+ function generateShadcnThemeCSS(lightTheme, darkTheme = null) {
180
+ const output = [];
181
+ output.push(`@theme inline {
182
+ --radius-sm: calc(var(--radius) - 4px);
183
+ --radius-md: calc(var(--radius) - 2px);
184
+ --radius-lg: var(--radius);
185
+ --radius-xl: calc(var(--radius) + 4px);
186
+ --color-background: var(--background);
187
+ --color-foreground: var(--foreground);
188
+ --color-card: var(--card);
189
+ --color-card-foreground: var(--card-foreground);
190
+ --color-popover: var(--popover);
191
+ --color-popover-foreground: var(--popover-foreground);
192
+ --color-primary: var(--primary);
193
+ --color-primary-foreground: var(--primary-foreground);
194
+ --color-secondary: var(--secondary);
195
+ --color-secondary-foreground: var(--secondary-foreground);
196
+ --color-muted: var(--muted);
197
+ --color-muted-foreground: var(--muted-foreground);
198
+ --color-accent: var(--accent);
199
+ --color-accent-foreground: var(--accent-foreground);
200
+ --color-destructive: var(--destructive);
201
+ --color-border: var(--border);
202
+ --color-input: var(--input);
203
+ --color-ring: var(--ring);
204
+ --color-chart-1: var(--chart-1);
205
+ --color-chart-2: var(--chart-2);
206
+ --color-chart-3: var(--chart-3);
207
+ --color-chart-4: var(--chart-4);
208
+ --color-chart-5: var(--chart-5);
209
+ --color-sidebar: var(--sidebar);
210
+ --color-sidebar-foreground: var(--sidebar-foreground);
211
+ --color-sidebar-primary: var(--sidebar-primary);
212
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
213
+ --color-sidebar-accent: var(--sidebar-accent);
214
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
215
+ --color-sidebar-border: var(--sidebar-border);
216
+ --color-sidebar-ring: var(--sidebar-ring);
217
+ --font-sans: var(--font-sans);
218
+ --font-mono: var(--font-mono);
219
+ }`);
220
+ output.push("");
221
+ const lightSelector = !darkTheme && lightTheme.browserColorScheme === "dark" ? ":root, .dark" : ":root";
222
+ output.push(`/* ${lightTheme.name} theme */`);
223
+ output.push(`${lightSelector} {`);
224
+ output.push(buildShadcnVars(lightTheme));
225
+ output.push("}");
226
+ if (darkTheme && darkTheme.id !== lightTheme.id) {
227
+ output.push("");
228
+ output.push(`/* ${darkTheme.name} theme (dark) */`);
229
+ output.push(".dark {");
230
+ output.push(buildShadcnVars(darkTheme));
231
+ output.push("}");
232
+ }
233
+ return output.join("\n");
234
+ }
235
+ /**
130
236
  * Unplugin-based theme generator
131
237
  */
132
238
  const upstartTheme = createUnplugin((opts) => {
@@ -143,28 +249,32 @@ const upstartTheme = createUnplugin((opts) => {
143
249
  }
144
250
  const outputDir = path.dirname(opts.outputPath);
145
251
  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
146
- const { themes, fontsDisplay = "swap" } = opts;
252
+ const { themes, fontsDisplay = "swap", cssFramework = "daisy-ui" } = opts;
147
253
  const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);
148
254
  const fontsCSSPath = path.join(outputDir, "fonts.css");
149
255
  fs.writeFileSync(fontsCSSPath, fontsCSS);
150
- const output = [];
151
- output.push(generateThemeBlock(themes.light, themes.dark));
152
- output.push("");
153
- if (themes.dark) {
154
- output.push("/* Dark theme variables */");
155
- output.push("@plugin \"daisyui/theme\" {");
156
- output.push(generateThemeVariables(themes.dark, "dark", themes.default));
157
- output.push("}");
158
- output.push("");
159
- }
160
- if (themes.light?.id !== themes.dark?.id) {
161
- output.push("/* Light theme variables */");
162
- output.push("@plugin \"daisyui/theme\" {");
163
- output.push(generateThemeVariables(themes.light, "light", themes.default));
164
- output.push("}");
256
+ let cssContent;
257
+ if (cssFramework === "shadcn-ui") cssContent = generateShadcnThemeCSS(themes.light, themes.dark ?? null);
258
+ else {
259
+ const output = [];
260
+ output.push(generateThemeBlock(themes.light, themes.dark));
165
261
  output.push("");
262
+ if (themes.dark) {
263
+ output.push("/* Dark theme variables */");
264
+ output.push("@plugin \"daisyui/theme\" {");
265
+ output.push(generateThemeVariables(themes.dark, themes.default));
266
+ output.push("}");
267
+ output.push("");
268
+ }
269
+ if (themes.light?.id !== themes.dark?.id) {
270
+ output.push("/* Light theme variables */");
271
+ output.push("@plugin \"daisyui/theme\" {");
272
+ output.push(generateThemeVariables(themes.light, themes.default));
273
+ output.push("}");
274
+ output.push("");
275
+ }
276
+ cssContent = output.join("\n");
166
277
  }
167
- const cssContent = output.join("\n");
168
278
  fs.writeFileSync(opts.outputPath, cssContent);
169
279
  } }
170
280
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-upstart-theme.js","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport {\n GenericFont,\n GoogleFont,\n type Theme,\n fontStacksFonts,\n processTheme,\n} from \"@upstart.gg/sdk/themes/theme\";\nimport fs from \"fs\";\nimport path from \"path\";\n\ninterface PluginOptions {\n themes: {\n light: Theme;\n dark?: Theme;\n default: \"light\" | \"dark\";\n };\n outputPath: string;\n /** Google Fonts display parameter (default: 'swap') */\n fontsDisplay?: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\";\n}\n\n/**\n * Collects Google Fonts from a theme's typography configuration\n */\nfunction collectGoogleFonts(theme: Theme) {\n const { typography } = theme;\n if (!typography) return [];\n\n const googleFonts: Array<GoogleFont> = [];\n const seen = new Set<string>();\n\n const addFont = (font?: GenericFont) => {\n if (font?.type === \"google\" && !seen.has(font.family)) {\n seen.add(font.family);\n googleFonts.push(font);\n }\n };\n\n addFont(typography.sans);\n addFont(typography.serif);\n if (typography.others) {\n Object.values(typography.others).forEach(addFont);\n }\n\n return googleFonts;\n}\n\n/**\n * Generates the fonts.css file content with Google Fonts imports\n */\nfunction generateFontsCSS(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n display: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\" = \"swap\",\n): string {\n // Collect fonts from both themes\n const allFonts = new Map<string, GoogleFont>();\n\n for (const font of collectGoogleFonts(lightTheme)) {\n allFonts.set(font.family, font);\n }\n\n if (darkTheme) {\n for (const font of collectGoogleFonts(darkTheme)) {\n allFonts.set(font.family, font);\n }\n }\n\n if (allFonts.size === 0) {\n return \"/* No Google Fonts to import */\\n\";\n }\n\n // Build the Google Fonts URL\n const families = Array.from(allFonts.values()).map((font) => {\n const name = font.family.replace(/ /g, \"+\");\n if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) {\n return `family=${name}:wght@${font.weights[0]}..${font.weights.at(-1)}`;\n }\n return `family=${name}`;\n });\n\n const url = `https://fonts.googleapis.com/css2?${families.join(\"&\")}&display=${display}`;\n\n return `/* Google Fonts - Auto-generated by vite-plugin-upstart-theme */\\n@import url(\"${url}\");\\n`;\n}\n\n/**\n * Converts a font definition to a CSS font-family string\n */\nfunction fontToCss(font: Theme[\"typography\"][\"sans\"]): string {\n if (!font) return \"\";\n\n if (font.type === \"stack\") {\n return fontStacksFonts[font.family];\n }\n\n if (font.type === \"google\") {\n // For Google fonts, we just use the family name\n // The actual font loading will be handled separately\n return `\"${font.family}\", system-ui, sans-serif`;\n }\n\n return \"\";\n}\n\n/**\n * Converts camelCase to kebab-case\n */\nfunction camelToKebab(str: string): string {\n return str.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Generates @theme block with font definitions\n */\nfunction generateThemeBlock(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n defaultTheme: \"light\" | \"dark\" = \"light\",\n): string {\n const fontDefinitions = new Map<string, string>();\n\n // Process light theme fonts\n const lightTypography = lightTheme.typography;\n\n // Add sans font\n if (lightTypography.sans) {\n const fontCss = fontToCss(lightTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n // Add serif font\n if (lightTypography.serif) {\n const fontCss = fontToCss(lightTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n // Add other fonts\n if (lightTypography.others) {\n for (const [name, font] of Object.entries(lightTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n\n // Process dark theme fonts if provided\n if (darkTheme) {\n const darkTypography = darkTheme.typography;\n\n if (darkTypography.sans) {\n const fontCss = fontToCss(darkTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n if (darkTypography.serif) {\n const fontCss = fontToCss(darkTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n if (darkTypography.others) {\n for (const [name, font] of Object.entries(darkTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n }\n\n const fontLines = Array.from(fontDefinitions.entries())\n .map(([name, value]) => ` --font-${name}: ${value};`)\n .join(\"\\n\");\n\n return `/* Fonts in the tailwind @theme block */\n@theme {\n ${fontLines}\n}\n\n/* Exclude properties since it produce warnings and is not important */\n@plugin \"daisyui\" {\n exclude: properties;\n themes: ${lightTheme.id} ${defaultTheme === \"light\" || !darkTheme ? \"--default\" : \"\"}${darkTheme?.id && darkTheme?.id !== lightTheme.id ? `, ${darkTheme.id} ${defaultTheme === \"dark\" ? \"--default\" : \"--prefersdark\"}` : \"\"};\n logs: true;\n}\n`;\n}\n\n//\n\n/**\n * Generates @theme block for a single theme with colors and layout variables\n */\nfunction generateThemeVariables(\n theme: Theme,\n type: \"light\" | \"dark\",\n defaultTheme: \"light\" | \"dark\",\n): string {\n const processed = processTheme(theme);\n const lines: string[] = [\n ` name: \"${processed.id}\";`,\n ` color-scheme: ${processed.browserColorScheme};`,\n ...((defaultTheme === \"dark\" && theme.browserColorScheme === \"dark\") ||\n (defaultTheme === \"light\" && theme.browserColorScheme === \"light\")\n ? [` default: true;`]\n : []),\n // ...(type === \"dark\" && defaultTheme !== \"dark\" ? [` prefersdark: true;`] : [` prefersdark: false;`]),\n ];\n\n // Color variables\n for (const [colorKey, colorValue] of Object.entries(processed.colors)) {\n const kebabKey = [\"base100\", \"base200\", \"base300\"].includes(colorKey)\n ? colorKey.replace(\"base\", \"base-\")\n : camelToKebab(colorKey);\n lines.push(` --color-${kebabKey}: ${colorValue};`);\n }\n\n lines.push(\"\");\n\n // Layout variables\n lines.push(` --radius-selector: ${processed.radiusSelector};`);\n lines.push(` --radius-field: ${processed.radiusField};`);\n lines.push(` --radius-box: ${processed.radiusBox};`);\n lines.push(` --size-selector: ${processed.sizeSelector};`);\n lines.push(` --size-field: ${processed.sizeField};`);\n lines.push(` --border: ${processed.border};`);\n lines.push(` --depth: ${processed.depth ? 1 : 0};`);\n lines.push(` --noise: 0;`);\n\n // return the generated lines\n return lines.join(\"\\n\");\n}\n\n/**\n * Unplugin-based theme generator\n */\nexport const upstartTheme = createUnplugin<PluginOptions>((opts) => {\n let generated = false;\n\n return {\n name: \"unplugin-theme-generator\",\n apply: \"serve\",\n\n vite: {\n configResolved() {\n // Generate the CSS file once when config is resolved\n if (generated) return;\n generated = true;\n\n if (!opts?.themes?.light) {\n console.warn(\"No light theme provided to unplugin-theme-generator. A light theme is required.\");\n return;\n }\n\n const outputDir = path.dirname(opts.outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n const { themes, fontsDisplay = \"swap\" } = opts;\n\n // Generate fonts.css file\n const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);\n const fontsCSSPath = path.join(outputDir, \"fonts.css\");\n fs.writeFileSync(fontsCSSPath, fontsCSS);\n\n const output: string[] = [];\n\n // Generate @theme block with fonts from both themes\n output.push(generateThemeBlock(themes.light, themes.dark));\n output.push(\"\");\n\n // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly\n // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563\n // Generate @theme block for dark theme if present\n if (themes.dark) {\n output.push(\"/* Dark theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.dark, \"dark\", themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Generate @theme block for light theme\n if (themes.light?.id !== themes.dark?.id) {\n output.push(\"/* Light theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.light, \"light\", themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Write the generated CSS file\n const cssContent = output.join(\"\\n\");\n fs.writeFileSync(opts.outputPath, cssContent);\n },\n },\n };\n});\n\nexport default upstartTheme.vite;\n"],"mappings":"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"}
1
+ {"version":3,"file":"vite-plugin-upstart-theme.js","names":[],"sources":["../src/vite-plugin-upstart-theme.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport {\n GenericFont,\n GoogleFont,\n type Theme,\n fontStacksFonts,\n processTheme,\n} from \"@upstart.gg/sdk/themes/theme\";\nimport fs from \"fs\";\nimport path from \"path\";\n\ninterface PluginOptions {\n themes: {\n light: Theme;\n dark?: Theme;\n default: \"light\" | \"dark\";\n };\n outputPath: string;\n /** Google Fonts display parameter (default: 'swap') */\n fontsDisplay?: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\";\n /** CSS framework to target (default: 'daisy-ui') */\n cssFramework?: \"daisy-ui\" | \"shadcn-ui\";\n}\n\n/**\n * Collects Google Fonts from a theme's typography configuration\n */\nfunction collectGoogleFonts(theme: Theme) {\n const { typography } = theme;\n if (!typography) return [];\n\n const googleFonts: Array<GoogleFont> = [];\n const seen = new Set<string>();\n\n const addFont = (font?: GenericFont) => {\n if (font?.type === \"google\" && !seen.has(font.family)) {\n seen.add(font.family);\n googleFonts.push(font);\n }\n };\n\n addFont(typography.sans);\n addFont(typography.serif);\n if (typography.others) {\n Object.values(typography.others).forEach(addFont);\n }\n\n return googleFonts;\n}\n\n/**\n * Generates the fonts.css file content with Google Fonts imports\n */\nfunction generateFontsCSS(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n display: \"auto\" | \"block\" | \"swap\" | \"fallback\" | \"optional\" = \"swap\",\n): string {\n // Collect fonts from both themes\n const allFonts = new Map<string, GoogleFont>();\n\n for (const font of collectGoogleFonts(lightTheme)) {\n allFonts.set(font.family, font);\n }\n\n if (darkTheme) {\n for (const font of collectGoogleFonts(darkTheme)) {\n allFonts.set(font.family, font);\n }\n }\n\n if (allFonts.size === 0) {\n return \"/* No Google Fonts to import */\\n\";\n }\n\n // Build the Google Fonts URL\n const families = Array.from(allFonts.values()).map((font) => {\n const name = font.family.replace(/ /g, \"+\");\n if (font.weights && font.weights.length > 1 && font.weights[0] !== font.weights.at(-1)) {\n return `family=${name}:wght@${font.weights[0]}..${font.weights.at(-1)}`;\n }\n return `family=${name}`;\n });\n\n const url = `https://fonts.googleapis.com/css2?${families.join(\"&\")}&display=${display}`;\n\n return `/* Google Fonts - Auto-generated by vite-plugin-upstart-theme */\\n@import url(\"${url}\");\\n`;\n}\n\n/**\n * Converts a font definition to a CSS font-family string\n */\nfunction fontToCss(font: Theme[\"typography\"][\"sans\"]): string {\n if (!font) return \"\";\n\n if (font.type === \"stack\") {\n return fontStacksFonts[font.family];\n }\n\n if (font.type === \"google\") {\n // For Google fonts, we just use the family name\n // The actual font loading will be handled separately\n return `\"${font.family}\", system-ui, sans-serif`;\n }\n\n return \"\";\n}\n\n/**\n * Converts camelCase to kebab-case\n */\nfunction camelToKebab(str: string): string {\n return str.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Generates @theme block with font definitions\n */\nfunction generateThemeBlock(\n lightTheme: Theme,\n darkTheme: Theme | null = null,\n defaultTheme: \"light\" | \"dark\" = \"light\",\n): string {\n const fontDefinitions = new Map<string, string>();\n\n // Process light theme fonts\n const lightTypography = lightTheme.typography;\n\n // Add sans font\n if (lightTypography.sans) {\n const fontCss = fontToCss(lightTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n // Add serif font\n if (lightTypography.serif) {\n const fontCss = fontToCss(lightTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n // Add other fonts\n if (lightTypography.others) {\n for (const [name, font] of Object.entries(lightTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n\n // Process dark theme fonts if provided\n if (darkTheme) {\n const darkTypography = darkTheme.typography;\n\n if (darkTypography.sans) {\n const fontCss = fontToCss(darkTypography.sans);\n if (fontCss) {\n fontDefinitions.set(\"sans\", fontCss);\n }\n }\n\n if (darkTypography.serif) {\n const fontCss = fontToCss(darkTypography.serif);\n if (fontCss) {\n fontDefinitions.set(\"serif\", fontCss);\n }\n }\n\n if (darkTypography.others) {\n for (const [name, font] of Object.entries(darkTypography.others)) {\n const fontCss = fontToCss(font);\n if (fontCss) {\n fontDefinitions.set(name, fontCss);\n }\n }\n }\n }\n\n const fontLines = Array.from(fontDefinitions.entries())\n .map(([name, value]) => ` --font-${name}: ${value};`)\n .join(\"\\n\");\n\n return `/* Fonts in the tailwind @theme block */\n@theme {\n ${fontLines}\n}\n\n/* Exclude properties since it produce warnings and is not important */\n@plugin \"daisyui\" {\n exclude: properties;\n themes: ${lightTheme.id} ${defaultTheme === \"light\" || !darkTheme ? \"--default\" : \"\"}${darkTheme?.id && darkTheme?.id !== lightTheme.id ? `, ${darkTheme.id} ${defaultTheme === \"dark\" ? \"--default\" : \"--prefersdark\"}` : \"\"};\n logs: true;\n}\n`;\n}\n\n/**\n * Generates @theme block for a single theme with colors and layout variables\n */\nfunction generateThemeVariables(theme: Theme, defaultTheme: \"light\" | \"dark\"): string {\n const processed = processTheme(theme);\n const lines: string[] = [\n ` name: \"${processed.id}\";`,\n ` color-scheme: ${processed.browserColorScheme};`,\n ...((defaultTheme === \"dark\" && theme.browserColorScheme === \"dark\") ||\n (defaultTheme === \"light\" && theme.browserColorScheme === \"light\")\n ? [` default: true;`]\n : []),\n // ...(type === \"dark\" && defaultTheme !== \"dark\" ? [` prefersdark: true;`] : [` prefersdark: false;`]),\n ];\n\n // Color variables\n for (const [colorKey, colorValue] of Object.entries(processed.colors)) {\n const kebabKey = [\"base100\", \"base200\", \"base300\"].includes(colorKey)\n ? colorKey.replace(\"base\", \"base-\")\n : camelToKebab(colorKey);\n lines.push(` --color-${kebabKey}: ${colorValue};`);\n }\n\n lines.push(\"\");\n\n // Layout variables\n lines.push(` --radius-selector: ${processed.radiusSelector};`);\n lines.push(` --radius-field: ${processed.radiusField};`);\n lines.push(` --radius-box: ${processed.radiusBox};`);\n lines.push(` --size-selector: ${processed.sizeSelector};`);\n lines.push(` --size-field: ${processed.sizeField};`);\n lines.push(` --border: ${processed.border};`);\n lines.push(` --depth: ${processed.depth ? 1 : 0};`);\n lines.push(` --noise: 0;`);\n\n // return the generated lines\n return lines.join(\"\\n\");\n}\n\n/**\n * Builds the CSS variable block for a single shadcn theme (`:root` or `.dark`)\n */\nfunction buildShadcnVars(theme: Theme): string {\n const processed = processTheme(theme);\n const { colors } = processed;\n\n const monoFont = theme.typography?.others?.mono;\n const monoFamilyValue = monoFont ? fontToCss(monoFont) : \"ui-monospace, monospace\";\n const sansFamilyValue = theme.typography?.sans\n ? fontToCss(theme.typography.sans)\n : \"ui-sans-serif, system-ui, sans-serif\";\n\n const vars: [string, string][] = [\n [\"--radius\", processed.radiusField],\n [\"--background\", colors.base100],\n [\"--foreground\", colors.baseContent],\n [\"--card\", colors.base200],\n [\"--card-foreground\", colors.baseContent],\n [\"--popover\", colors.base200],\n [\"--popover-foreground\", colors.baseContent],\n [\"--primary\", colors.primary],\n [\"--primary-foreground\", colors.primaryContent],\n [\"--secondary\", colors.secondary],\n [\"--secondary-foreground\", colors.secondaryContent],\n [\"--muted\", colors.neutral],\n [\"--muted-foreground\", colors.neutralContent],\n // shadcn --accent = hover surface, NOT the vibrant accent color\n [\"--accent\", colors.base300],\n [\"--accent-foreground\", colors.baseContent],\n [\"--destructive\", colors.error],\n [\"--border\", colors.base300],\n [\"--input\", colors.base300],\n [\"--ring\", colors.primary],\n [\"--chart-1\", colors.primary],\n [\"--chart-2\", colors.secondary],\n // vibrant accent goes in chart-3\n [\"--chart-3\", colors.accent],\n [\"--chart-4\", colors.success],\n [\"--chart-5\", colors.warning],\n [\"--sidebar\", colors.base200],\n [\"--sidebar-foreground\", colors.baseContent],\n [\"--sidebar-primary\", colors.primary],\n [\"--sidebar-primary-foreground\", colors.primaryContent],\n [\"--sidebar-accent\", colors.base300],\n [\"--sidebar-accent-foreground\", colors.baseContent],\n [\"--sidebar-border\", colors.base300],\n [\"--sidebar-ring\", colors.primary],\n [\"--font-sans\", sansFamilyValue],\n [\"--font-mono\", monoFamilyValue],\n ];\n\n return vars.map(([k, v]) => ` ${k}: ${v};`).join(\"\\n\");\n}\n\n/**\n * Generates the full shadcn-compatible CSS output (no DaisyUI, no @plugin blocks).\n * Uses :root for light theme and .dark for dark theme.\n */\nfunction generateShadcnThemeCSS(lightTheme: Theme, darkTheme: Theme | null = null): string {\n const output: string[] = [];\n\n // Tailwind v4 @theme inline — wires CSS vars to Tailwind utility classes\n output.push(`@theme inline {\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --color-chart-1: var(--chart-1);\n --color-chart-2: var(--chart-2);\n --color-chart-3: var(--chart-3);\n --color-chart-4: var(--chart-4);\n --color-chart-5: var(--chart-5);\n --color-sidebar: var(--sidebar);\n --color-sidebar-foreground: var(--sidebar-foreground);\n --color-sidebar-primary: var(--sidebar-primary);\n --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n --color-sidebar-accent: var(--sidebar-accent);\n --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n --color-sidebar-border: var(--sidebar-border);\n --color-sidebar-ring: var(--sidebar-ring);\n --font-sans: var(--font-sans);\n --font-mono: var(--font-mono);\n}`);\n\n output.push(\"\");\n\n // Light theme — always :root\n // If only a dark theme is provided (no separate light), use :root, .dark\n const isDarkOnly = !darkTheme && lightTheme.browserColorScheme === \"dark\";\n const lightSelector = isDarkOnly ? \":root, .dark\" : \":root\";\n\n output.push(`/* ${lightTheme.name} theme */`);\n output.push(`${lightSelector} {`);\n output.push(buildShadcnVars(lightTheme));\n output.push(\"}\");\n\n // Dark theme — .dark selector\n if (darkTheme && darkTheme.id !== lightTheme.id) {\n output.push(\"\");\n output.push(`/* ${darkTheme.name} theme (dark) */`);\n output.push(\".dark {\");\n output.push(buildShadcnVars(darkTheme));\n output.push(\"}\");\n }\n\n return output.join(\"\\n\");\n}\n\n/**\n * Unplugin-based theme generator\n */\nexport const upstartTheme = createUnplugin<PluginOptions>((opts) => {\n let generated = false;\n\n return {\n name: \"unplugin-theme-generator\",\n apply: \"serve\",\n\n vite: {\n configResolved() {\n // Generate the CSS file once when config is resolved\n if (generated) return;\n generated = true;\n\n if (!opts?.themes?.light) {\n console.warn(\"No light theme provided to unplugin-theme-generator. A light theme is required.\");\n return;\n }\n\n const outputDir = path.dirname(opts.outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n const { themes, fontsDisplay = \"swap\", cssFramework = \"daisy-ui\" } = opts;\n\n // Generate fonts.css file (shared by both frameworks)\n const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);\n const fontsCSSPath = path.join(outputDir, \"fonts.css\");\n fs.writeFileSync(fontsCSSPath, fontsCSS);\n\n let cssContent: string;\n\n if (cssFramework === \"shadcn-ui\") {\n cssContent = generateShadcnThemeCSS(themes.light, themes.dark ?? null);\n } else {\n const output: string[] = [];\n\n // Generate @theme block with fonts from both themes\n output.push(generateThemeBlock(themes.light, themes.dark));\n output.push(\"\");\n\n // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly\n // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563\n // Generate @theme block for dark theme if present\n if (themes.dark) {\n output.push(\"/* Dark theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.dark, themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n // Generate @theme block for light theme\n if (themes.light?.id !== themes.dark?.id) {\n output.push(\"/* Light theme variables */\");\n output.push('@plugin \"daisyui/theme\" {');\n output.push(generateThemeVariables(themes.light, themes.default));\n output.push(\"}\");\n output.push(\"\");\n }\n\n cssContent = output.join(\"\\n\");\n }\n\n fs.writeFileSync(opts.outputPath, cssContent);\n },\n },\n };\n});\n\nexport default upstartTheme.vite;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstart.gg/vite-plugins",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,7 +23,7 @@
23
23
  "oxc-parser": "0.101.0",
24
24
  "unplugin": "^2.3.11",
25
25
  "zimmerframe": "^1.1.4",
26
- "@upstart.gg/sdk": "^0.1.27"
26
+ "@upstart.gg/sdk": "^0.1.29"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9",
@@ -113,7 +113,7 @@
113
113
  },
114
114
  "peerDependencies": {
115
115
  "zod": "4.3.6",
116
- "@upstart.gg/sdk": "^0.1.27"
116
+ "@upstart.gg/sdk": "^0.1.29"
117
117
  },
118
118
  "author": "Upstart",
119
119
  "publishConfig": {
@@ -18,6 +18,8 @@ interface PluginOptions {
18
18
  outputPath: string;
19
19
  /** Google Fonts display parameter (default: 'swap') */
20
20
  fontsDisplay?: "auto" | "block" | "swap" | "fallback" | "optional";
21
+ /** CSS framework to target (default: 'daisy-ui') */
22
+ cssFramework?: "daisy-ui" | "shadcn-ui";
21
23
  }
22
24
 
23
25
  /**
@@ -196,16 +198,10 @@ function generateThemeBlock(
196
198
  `;
197
199
  }
198
200
 
199
- //
200
-
201
201
  /**
202
202
  * Generates @theme block for a single theme with colors and layout variables
203
203
  */
204
- function generateThemeVariables(
205
- theme: Theme,
206
- type: "light" | "dark",
207
- defaultTheme: "light" | "dark",
208
- ): string {
204
+ function generateThemeVariables(theme: Theme, defaultTheme: "light" | "dark"): string {
209
205
  const processed = processTheme(theme);
210
206
  const lines: string[] = [
211
207
  ` name: "${processed.id}";`,
@@ -241,6 +237,133 @@ function generateThemeVariables(
241
237
  return lines.join("\n");
242
238
  }
243
239
 
240
+ /**
241
+ * Builds the CSS variable block for a single shadcn theme (`:root` or `.dark`)
242
+ */
243
+ function buildShadcnVars(theme: Theme): string {
244
+ const processed = processTheme(theme);
245
+ const { colors } = processed;
246
+
247
+ const monoFont = theme.typography?.others?.mono;
248
+ const monoFamilyValue = monoFont ? fontToCss(monoFont) : "ui-monospace, monospace";
249
+ const sansFamilyValue = theme.typography?.sans
250
+ ? fontToCss(theme.typography.sans)
251
+ : "ui-sans-serif, system-ui, sans-serif";
252
+
253
+ const vars: [string, string][] = [
254
+ ["--radius", processed.radiusField],
255
+ ["--background", colors.base100],
256
+ ["--foreground", colors.baseContent],
257
+ ["--card", colors.base200],
258
+ ["--card-foreground", colors.baseContent],
259
+ ["--popover", colors.base200],
260
+ ["--popover-foreground", colors.baseContent],
261
+ ["--primary", colors.primary],
262
+ ["--primary-foreground", colors.primaryContent],
263
+ ["--secondary", colors.secondary],
264
+ ["--secondary-foreground", colors.secondaryContent],
265
+ ["--muted", colors.neutral],
266
+ ["--muted-foreground", colors.neutralContent],
267
+ // shadcn --accent = hover surface, NOT the vibrant accent color
268
+ ["--accent", colors.base300],
269
+ ["--accent-foreground", colors.baseContent],
270
+ ["--destructive", colors.error],
271
+ ["--border", colors.base300],
272
+ ["--input", colors.base300],
273
+ ["--ring", colors.primary],
274
+ ["--chart-1", colors.primary],
275
+ ["--chart-2", colors.secondary],
276
+ // vibrant accent goes in chart-3
277
+ ["--chart-3", colors.accent],
278
+ ["--chart-4", colors.success],
279
+ ["--chart-5", colors.warning],
280
+ ["--sidebar", colors.base200],
281
+ ["--sidebar-foreground", colors.baseContent],
282
+ ["--sidebar-primary", colors.primary],
283
+ ["--sidebar-primary-foreground", colors.primaryContent],
284
+ ["--sidebar-accent", colors.base300],
285
+ ["--sidebar-accent-foreground", colors.baseContent],
286
+ ["--sidebar-border", colors.base300],
287
+ ["--sidebar-ring", colors.primary],
288
+ ["--font-sans", sansFamilyValue],
289
+ ["--font-mono", monoFamilyValue],
290
+ ];
291
+
292
+ return vars.map(([k, v]) => ` ${k}: ${v};`).join("\n");
293
+ }
294
+
295
+ /**
296
+ * Generates the full shadcn-compatible CSS output (no DaisyUI, no @plugin blocks).
297
+ * Uses :root for light theme and .dark for dark theme.
298
+ */
299
+ function generateShadcnThemeCSS(lightTheme: Theme, darkTheme: Theme | null = null): string {
300
+ const output: string[] = [];
301
+
302
+ // Tailwind v4 @theme inline — wires CSS vars to Tailwind utility classes
303
+ output.push(`@theme inline {
304
+ --radius-sm: calc(var(--radius) - 4px);
305
+ --radius-md: calc(var(--radius) - 2px);
306
+ --radius-lg: var(--radius);
307
+ --radius-xl: calc(var(--radius) + 4px);
308
+ --color-background: var(--background);
309
+ --color-foreground: var(--foreground);
310
+ --color-card: var(--card);
311
+ --color-card-foreground: var(--card-foreground);
312
+ --color-popover: var(--popover);
313
+ --color-popover-foreground: var(--popover-foreground);
314
+ --color-primary: var(--primary);
315
+ --color-primary-foreground: var(--primary-foreground);
316
+ --color-secondary: var(--secondary);
317
+ --color-secondary-foreground: var(--secondary-foreground);
318
+ --color-muted: var(--muted);
319
+ --color-muted-foreground: var(--muted-foreground);
320
+ --color-accent: var(--accent);
321
+ --color-accent-foreground: var(--accent-foreground);
322
+ --color-destructive: var(--destructive);
323
+ --color-border: var(--border);
324
+ --color-input: var(--input);
325
+ --color-ring: var(--ring);
326
+ --color-chart-1: var(--chart-1);
327
+ --color-chart-2: var(--chart-2);
328
+ --color-chart-3: var(--chart-3);
329
+ --color-chart-4: var(--chart-4);
330
+ --color-chart-5: var(--chart-5);
331
+ --color-sidebar: var(--sidebar);
332
+ --color-sidebar-foreground: var(--sidebar-foreground);
333
+ --color-sidebar-primary: var(--sidebar-primary);
334
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
335
+ --color-sidebar-accent: var(--sidebar-accent);
336
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
337
+ --color-sidebar-border: var(--sidebar-border);
338
+ --color-sidebar-ring: var(--sidebar-ring);
339
+ --font-sans: var(--font-sans);
340
+ --font-mono: var(--font-mono);
341
+ }`);
342
+
343
+ output.push("");
344
+
345
+ // Light theme — always :root
346
+ // If only a dark theme is provided (no separate light), use :root, .dark
347
+ const isDarkOnly = !darkTheme && lightTheme.browserColorScheme === "dark";
348
+ const lightSelector = isDarkOnly ? ":root, .dark" : ":root";
349
+
350
+ output.push(`/* ${lightTheme.name} theme */`);
351
+ output.push(`${lightSelector} {`);
352
+ output.push(buildShadcnVars(lightTheme));
353
+ output.push("}");
354
+
355
+ // Dark theme — .dark selector
356
+ if (darkTheme && darkTheme.id !== lightTheme.id) {
357
+ output.push("");
358
+ output.push(`/* ${darkTheme.name} theme (dark) */`);
359
+ output.push(".dark {");
360
+ output.push(buildShadcnVars(darkTheme));
361
+ output.push("}");
362
+ }
363
+
364
+ return output.join("\n");
365
+ }
366
+
244
367
  /**
245
368
  * Unplugin-based theme generator
246
369
  */
@@ -267,41 +390,47 @@ export const upstartTheme = createUnplugin<PluginOptions>((opts) => {
267
390
  fs.mkdirSync(outputDir, { recursive: true });
268
391
  }
269
392
 
270
- const { themes, fontsDisplay = "swap" } = opts;
393
+ const { themes, fontsDisplay = "swap", cssFramework = "daisy-ui" } = opts;
271
394
 
272
- // Generate fonts.css file
395
+ // Generate fonts.css file (shared by both frameworks)
273
396
  const fontsCSS = generateFontsCSS(themes.light, themes.dark, fontsDisplay);
274
397
  const fontsCSSPath = path.join(outputDir, "fonts.css");
275
398
  fs.writeFileSync(fontsCSSPath, fontsCSS);
276
399
 
277
- const output: string[] = [];
400
+ let cssContent: string;
278
401
 
279
- // Generate @theme block with fonts from both themes
280
- output.push(generateThemeBlock(themes.light, themes.dark));
281
- output.push("");
402
+ if (cssFramework === "shadcn-ui") {
403
+ cssContent = generateShadcnThemeCSS(themes.light, themes.dark ?? null);
404
+ } else {
405
+ const output: string[] = [];
282
406
 
283
- // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly
284
- // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563
285
- // Generate @theme block for dark theme if present
286
- if (themes.dark) {
287
- output.push("/* Dark theme variables */");
288
- output.push('@plugin "daisyui/theme" {');
289
- output.push(generateThemeVariables(themes.dark, "dark", themes.default));
290
- output.push("}");
407
+ // Generate @theme block with fonts from both themes
408
+ output.push(generateThemeBlock(themes.light, themes.dark));
291
409
  output.push("");
292
- }
293
410
 
294
- // Generate @theme block for light theme
295
- if (themes.light?.id !== themes.dark?.id) {
296
- output.push("/* Light theme variables */");
297
- output.push('@plugin "daisyui/theme" {');
298
- output.push(generateThemeVariables(themes.light, "light", themes.default));
299
- output.push("}");
300
- output.push("");
411
+ // IMPORTANT; Place dark theme first OTHERWISE daisyUI will not apply it correctly
412
+ // @see https://github.com/saadeghi/daisyui/issues/3921#issuecomment-3059524563
413
+ // Generate @theme block for dark theme if present
414
+ if (themes.dark) {
415
+ output.push("/* Dark theme variables */");
416
+ output.push('@plugin "daisyui/theme" {');
417
+ output.push(generateThemeVariables(themes.dark, themes.default));
418
+ output.push("}");
419
+ output.push("");
420
+ }
421
+
422
+ // Generate @theme block for light theme
423
+ if (themes.light?.id !== themes.dark?.id) {
424
+ output.push("/* Light theme variables */");
425
+ output.push('@plugin "daisyui/theme" {');
426
+ output.push(generateThemeVariables(themes.light, themes.default));
427
+ output.push("}");
428
+ output.push("");
429
+ }
430
+
431
+ cssContent = output.join("\n");
301
432
  }
302
433
 
303
- // Write the generated CSS file
304
- const cssContent = output.join("\n");
305
434
  fs.writeFileSync(opts.outputPath, cssContent);
306
435
  },
307
436
  },