UNPKG

@fredboulanger/ui-skins-storybook

Version:

Generate Storybook ui-skins configuration from *.ui_skins.themes.yml files

205 lines (196 loc) 7.08 kB
"use client" // src/dataThemesGenerator.ts import { readFile, writeFile } from "fs/promises"; import { glob } from "glob"; import { parse } from "yaml"; import { join, resolve } from "path"; async function generateThemes(options = {}) { console.log("\u{1F3A8} Generating theme configuration from *.ui_skins.themes.yml files..."); try { const searchPaths = [process.cwd()]; if (options.namespaces) { for (const [namespaceName, namespacePath] of Object.entries(options.namespaces)) { const resolvedPath = resolve(namespacePath); searchPaths.push(resolvedPath); console.log(`\u{1F50D} Searching themes in namespace '${namespaceName}': ${resolvedPath}`); } } const themeFiles = []; for (const searchPath of searchPaths) { try { const files = await glob("**/*.ui_skins.themes.yml", { cwd: searchPath }); themeFiles.push(...files.map((file) => join(searchPath, file))); } catch (error) { console.log(`\u26A0\uFE0F Could not search in path: ${searchPath}`); } } if (themeFiles.length === 0) { console.log("\u26A0\uFE0F No *.ui_skins.themes.yml files found - skipping theme generation"); return; } console.log(`\u{1F4C1} Found ${themeFiles.length} theme file(s):`, themeFiles); const themes = []; for (const themeFile of themeFiles) { try { const content = await readFile(themeFile, "utf8"); const themeData = parse(content); for (const [themeKey, themeConfig] of Object.entries(themeData)) { if (typeof themeConfig === "object" && themeConfig !== null) { const config = themeConfig; if (config.label && config.key && config.target) { themes.push({ key: themeKey, label: config.label, value: themeKey, target: config.target }); console.log(`\u2705 Added theme: ${themeKey} (${config.label}) -> ${config.target}`); } else { console.log(`\u26A0\uFE0F Skipped theme: ${themeKey} - missing required properties (label, key, target)`); } } } console.log(`\u2705 Parsed theme file: ${themeFile}`); } catch (error) { console.error(`\u274C Error parsing ${themeFile}:`, error.message); } } if (themes.length === 0) { console.log("\u26A0\uFE0F No valid themes found in the files - skipping theme generation"); const themesConfigPath2 = join(process.cwd(), ".storybook", "data-themes.ts"); try { await writeFile(themesConfigPath2, "", "utf8"); console.log(`\u{1F5D1}\uFE0F Cleared existing data-themes.ts file`); } catch (error) { } return; } console.log(`\u{1F3AF} Found ${themes.length} theme(s):`, themes.map((t) => t.key)); const themesConfigContent = generateThemesConfig(themes); const themesConfigPath = join(process.cwd(), ".storybook", "data-themes.ts"); await writeFile(themesConfigPath, themesConfigContent, "utf8"); console.log(`\u2705 Generated data-themes.ts with ${themes.length} theme(s)`); console.log(`\u{1F4DD} Updated: ${themesConfigPath}`); console.log(`\u{1F4A1} To use themes, add to your preview.ts:`); console.log(` import { themeDecorators, themeGlobalTypes } from './data-themes'`); console.log(` Then add ...themeDecorators to decorators and ...themeGlobalTypes to globalTypes`); } catch (error) { console.error("\u274C Error generating themes:", error); throw error; } } function generateThemesConfig(themes) { const themeItems = themes.map( (theme) => ` { value: '${theme.value}', title: '${theme.label}' }` ).join(",\n"); const defaultTheme = themes.length > 0 ? themes[0].value : "default"; const defaultTarget = themes.length > 0 ? themes[0].target : "body"; const themeTargets = themes.map( (theme) => ` '${theme.value}': '${theme.target}'` ).join(",\n"); return `// Auto-generated theme configuration from *.ui_skins.themes.yml files // This file is automatically generated - do not edit manually // Theme target mapping const themeTargets = { ${themeTargets} }; // Add a decorator to set data-theme on the correct target element export const themeDecorators = [ (storyFn, context) => { // Wait for the target element to be available setTimeout(() => { const selectedTheme = context.globals.theme || '${defaultTheme}'; const targetSelector = themeTargets[selectedTheme] || '${defaultTarget}'; // Remove data-theme from both html and body to clean previous themes const html = document.querySelector('html'); const body = document.querySelector('body'); if (html) html.removeAttribute('data-theme'); if (body) body.removeAttribute('data-theme'); // Apply the new theme to the correct target const target = document.querySelector(targetSelector); if (target) { target.setAttribute('data-theme', selectedTheme); } }, 0); return storyFn(); }, ]; export const themeGlobalTypes = { theme: { name: 'Theme', description: 'Global theme for components', defaultValue: '${defaultTheme}', toolbar: { icon: 'paintbrush', items: [ ${themeItems} ], }, }, }; `; } // src/logger.ts var logger = { info: (message, ...args) => { console.log(`\u2139\uFE0F ${message}`, ...args); }, warn: (message, ...args) => { console.warn(`\u26A0\uFE0F ${message}`, ...args); }, error: (message, ...args) => { console.error(`\u274C ${message}`, ...args); }, debug: (message, ...args) => { console.debug(`\u{1F41B} ${message}`, ...args); } }; // src/vite-plugin-theme-generator.ts function vitePluginThemeGenerator(options = {}) { const { generateOnStart = true, watch = true, namespaces } = options; let hasGenerated = false; return { name: "vite-plugin-theme-generator", async buildStart() { if (generateOnStart && !hasGenerated) { try { await generateThemes({ namespaces }); hasGenerated = true; } catch (error) { logger.warn("Failed to generate themes on build start:", error); } } }, async load(id) { if (id.endsWith("component.yml") && !hasGenerated) { try { await generateThemes({ namespaces }); hasGenerated = true; } catch (error) { logger.warn("Failed to generate themes:", error); } } }, async handleHotUpdate({ file }) { if (watch && file.endsWith(".ui_skins.themes.yml")) { try { await generateThemes({ namespaces }); logger.info("\u{1F3A8} Themes regenerated due to file change:", file); } catch (error) { logger.warn("Failed to regenerate themes:", error); } } } }; } export { generateThemes, generateThemesConfig, logger, vitePluginThemeGenerator }; //# sourceMappingURL=chunk-AJG4AKJ5.mjs.map