import { VexThemeColorOptions, VexThemeOptions, VexThemePluginOptions } from '../plugins/themes'; import * as fs from 'fs'; import * as path from 'path'; import { createThemeClassName } from '../utils/naming'; import { DeepPartial } from '../../interfaces/deep-partial.type'; import { mergeDeep } from '../../utils/merge-deep'; import deepClone from '../../utils/deep-clone'; const scssFilePath = path.resolve( __dirname, '../../styles/_generated-themes.scss' ); export function inheritDefaultTheme( theme: DeepPartial, defaultTheme: VexThemeOptions ): VexThemeOptions { return mergeDeep(deepClone(defaultTheme), theme); } function isThemeColorOptions( options: VexThemeColorOptions | Omit ): options is VexThemeColorOptions { return (options as VexThemeColorOptions).defaults !== undefined; } function buildScss(options: VexThemePluginOptions): string { const themes = options.themes; let scss = `// This file is generated automatically by the themes plugin. Do not edit this file. Use the configuration in tailwind.config.ts.\n`; scss += `$generated-themes: (`; /** * Add themes to the theme map */ for (let [themeName, partialThemeOptions] of Object.entries(themes)) { scss += `${themeName}: (`; scss += `selector: "${createThemeClassName(themeName)}", `; /** * Inherit default theme */ let themeOptions: VexThemeOptions; if (themeName !== 'default') { themeOptions = inheritDefaultTheme(partialThemeOptions, themes.default); } else { themeOptions = partialThemeOptions as VexThemeOptions; } /** * Add colors to the theme map */ for (const [colorName, colorOptions] of Object.entries( themeOptions.colors )) { /** * Add default shades for Angular Material to the theme map */ if (isThemeColorOptions(colorOptions)) { for (const [defaultShadeName, defaultShadeValue] of Object.entries( colorOptions.defaults )) { scss += `${colorName}-${defaultShadeName}-shade: ${defaultShadeValue}, `; } } scss += `${colorName}: (`; /** * Add palette to the theme map */ for (const [colorShade, colorValue] of Object.entries( colorOptions.palette )) { scss += `${colorShade}: ${colorValue},`; } scss += `), `; } /** * Add Angular Material colors to the theme map */ scss += 'angular-material: ('; for (const [sectionName, sectionValue] of Object.entries( themeOptions.angularMaterial )) { scss += `${sectionName}: (`; for (const [variantName, variantValue] of Object.entries(sectionValue)) { scss += `${variantName}: (`; for (const [areaName, areaValue] of Object.entries(variantValue)) { scss += `${areaName}: (`; for (const [property, value] of Object.entries(areaValue)) { scss += `${property}: ${value},`; } scss += `), `; } scss += `), `; } scss += `), `; } scss += ')'; /** Close theme map */ scss += `), `; } scss += ');'; return scss; } export default function generateScss(options: VexThemePluginOptions): void { const scss = buildScss(options); let fileContent: string | undefined; try { fileContent = fs.readFileSync(scssFilePath, { encoding: 'utf8' }); } catch (error) { console.error( `[VexTailwindThemePlugin] Error reading file ${scssFilePath}`, error ); } if (!fileContent || fileContent !== scss) { try { fs.writeFileSync(scssFilePath, scss); } catch (error) { console.error( `[VexTailwindThemePlugin] Error writing file ${scssFilePath}`, error ); } } }