From 816269379fa8b2a7093f20ee774f770d8a48ec01 Mon Sep 17 00:00:00 2001 From: winston Date: Wed, 10 Jan 2024 07:44:11 +0100 Subject: [PATCH] feat(compiler): add verbose errors for wrong user config (#288) --- packages/catppuccin-vsc/src/theme/index.ts | 8 +- .../src/theme/ui/customNames.ts | 113 +++++++++++------- packages/catppuccin-vsc/src/types/index.d.ts | 1 + packages/catppuccin-vsc/src/utils.ts | 12 +- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/packages/catppuccin-vsc/src/theme/index.ts b/packages/catppuccin-vsc/src/theme/index.ts index 55b55158..79330149 100644 --- a/packages/catppuccin-vsc/src/theme/index.ts +++ b/packages/catppuccin-vsc/src/theme/index.ts @@ -45,16 +45,12 @@ export const compileTheme = ( isLatte: flavor === "latte", }; - const flavourName = `Catppuccin ${flavorData.name}`; - - const theme = { - name: flavourName, + return { + name: `Catppuccin ${flavorData.name}`, type: context.isLatte ? "light" : "dark", colors: getUiColors(context), semanticHighlighting: true, semanticTokenColors: getSemanticTokens(context), tokenColors: getTokenColors(context), }; - - return theme; }; diff --git a/packages/catppuccin-vsc/src/theme/ui/customNames.ts b/packages/catppuccin-vsc/src/theme/ui/customNames.ts index f35ce9f5..d0c3e00d 100644 --- a/packages/catppuccin-vsc/src/theme/ui/customNames.ts +++ b/packages/catppuccin-vsc/src/theme/ui/customNames.ts @@ -1,58 +1,79 @@ -import { CatppuccinPalette, ThemeContext } from "@/types"; +import type { ThemeContext, ColorName } from "@/types"; import { opacity } from "@/theme/utils"; +import { flavors } from "@catppuccin/palette"; type CustomNamedColors = Record; +const ctpColors = new Set(Object.keys(flavors.mocha.colors)); + +class CustomUIColorError extends Error { + constructor(key: string, value: string, message = "") { + super( + `Invalid value: "${value}" for key "${key}" in "catppuccin.customUIColors".\n${message}`, + ); + this.name = "CustomUIColorError"; + } +} + +/** + * @throws if the value is not a valid 'colorName' or 'colorName opacityValue' + * @returns a tuple of [colorName, opacityValue] + */ +const parseCustomUiColor = (k: string, v: string): [string, number] => { + const entry = v.split(" "); + if (entry.length > 2) { + throw new CustomUIColorError( + k, + v, + 'Too many arguments, expected "colorName" or "colorName opacityValue".', + ); + } + let opacityValue = 1; + if (entry.length == 2) { + opacityValue = Number(entry[1]); + if (Number.isNaN(opacityValue)) { + throw new CustomUIColorError(k, v, "Opacity value is not a number."); + } + if (opacityValue < 0 || opacityValue > 1) { + throw new CustomUIColorError( + k, + v, + "Opacity value is not between 0 and 1.", + ); + } + } + return [entry[0], opacityValue]; +}; +const hexColorRegex = /^#([\dA-Fa-f]{3,4}){1,2}$/; + const customNamedColors = (context: ThemeContext): CustomNamedColors => { const { flavor, palette, options } = context; const accent = palette[options.accent]; - return { - ...Object.entries({ - // collect the options, overwrite the "all" config with the current palette config - ...options.customUIColors.all, - ...options.customUIColors[flavor], - }) - .map(([k, v]) => { - // deal with accents - if (v.startsWith("accent")) { - const entry = v.split(" "); - return entry.length === 1 - ? { - [k]: accent, - } - : { - [k]: opacity(accent, Number(entry[1])), - }; - } - - // allow custom hex colors - if (v.startsWith("#")) { - return { - [k]: v, - }; - } - - //check if the entry is a "color opacity" mapping - const entry = v.split(" "); - v = - entry.length === 1 - ? // resolve to the palette color - palette[v as keyof CatppuccinPalette] - : // call the opacity function - opacity( - palette[entry[0] as keyof CatppuccinPalette], - Number(entry[1]), - ); - - return { - [k]: v, - }; - }) - // TODO: rework this to get rid of the reduce - // eslint-disable-next-line unicorn/no-array-reduce - .reduce((previous, current) => ({ ...previous, ...current }), {}), + const customUIColors = { + ...options.customUIColors.all, + ...options.customUIColors[flavor], }; + + for (const [k, v] of Object.entries(customUIColors)) { + // don't change custom hex colors + if (hexColorRegex.test(v)) continue; + + const [parsedColor, opacityValue] = parseCustomUiColor(k, v); + + let color: string; + if (parsedColor === "accent") { + color = accent; + } else if (ctpColors.has(parsedColor)) { + color = palette[parsedColor as ColorName]; + } else { + throw new CustomUIColorError(k, v, "Invalid color name."); + } + + customUIColors[k] = opacity(color, opacityValue); + } + + return customUIColors; }; export default customNamedColors; diff --git a/packages/catppuccin-vsc/src/types/index.d.ts b/packages/catppuccin-vsc/src/types/index.d.ts index 8d853ce1..638554f7 100644 --- a/packages/catppuccin-vsc/src/types/index.d.ts +++ b/packages/catppuccin-vsc/src/types/index.d.ts @@ -14,6 +14,7 @@ export type * from "@catppuccin/vsc-typegen/types/gitlens"; export type CatppuccinAccent = AccentName; export type CatppuccinFlavor = FlavorName; +export { type ColorName } from "@catppuccin/palette"; export type CatppuccinWorkbenchMode = "default" | "flat" | "minimal"; export type CatppuccinBracketMode = | "rainbow" diff --git a/packages/catppuccin-vsc/src/utils.ts b/packages/catppuccin-vsc/src/utils.ts index e1954774..7f026dc4 100644 --- a/packages/catppuccin-vsc/src/utils.ts +++ b/packages/catppuccin-vsc/src/utils.ts @@ -132,9 +132,15 @@ export const updateThemes = async ( return writeThemeFile(paths[flavor], theme); }); - Promise.all(promises).then(() => { - promptToReload(trigger); - }); + Promise.all(promises) + .then(() => { + promptToReload(trigger); + }) + .catch((error) => { + window.showErrorMessage( + "Failed to save re-compiled theme: \n" + error.message, + ); + }); }; export const syncToIconPack = () => {