@fredboulanger/ui-skins-storybook
Version:
Generate Storybook ui-skins configuration from *.ui_skins.themes.yml files
205 lines (196 loc) • 7.08 kB
JavaScript
"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