src/theme/define.ts
65 lines and 5 functions in DrDustinEdwards/enarratio, at commit 4c5695a on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| assertCssText | 13 to 19 | 2 | 100% | OkCRAP 2 | Ok100% |
| assertScheme | 21 to 47 | 5 | 89% | WatchCRAP 5 | Watch69.4% |
| <anonymous:29> | 29 to 29 | 1 | 100% | OkCRAP 1 | Watch66.7% |
| <anonymous:30> | 30 to 30 | 1 | 100% | OkCRAP 1 | Watch66.7% |
| defineTheme | 56 to 70 | 5 | 69% | WatchCRAP 5.7 | Critical29.6% |
Imports
It imports src/theme/color.ts, src/theme/types.ts. Imported by src/index.ts, src/theme/stylesheet.ts.
Source
import { isHexColor } from "./color.js";
import type { ColorScheme, Theme } from "./types.js";
const GRIDLINES = new Set(["none", "x", "y", "both"]);
/**
* Characters that could end a CSS comment, declaration, rule or the surrounding <style> element.
* A theme's name and fonts are written into CSS, and themes may come from data (F10, A10).
*/
const UNSAFE_CSS_TEXT = /[<>{};\\]|\/\*|\*\//;
/** Throws unless `text` can be written into CSS as it is. */
export function assertCssText(where: string, text: string): void {
if (UNSAFE_CSS_TEXT.test(text)) {
throw new Error(
`${where}: "${text}" contains a character not allowed in CSS text (<, >, {, }, ;, \\, /* or */)`,
);
}
}
function assertScheme(themeName: string, which: "light" | "dark", scheme: ColorScheme): void {
const where = `theme "${themeName}" ${which}`;
const colors: [string, string][] = [
["background", scheme.background],
["text", scheme.text],
["mutedText", scheme.mutedText],
["grid", scheme.grid],
["focus", scheme.focus],
...scheme.series.map((c, i): [string, string] => [`series[${i}]`, c]),
...scheme.sequential.map((c, i): [string, string] => [`sequential[${i}]`, c]),
["status.good", scheme.status.good],
["status.warning", scheme.status.warning],
["status.bad", scheme.status.bad],
["status.unknown", scheme.status.unknown],
];
if (scheme.series.length !== 8) {
throw new Error(`${where}: series must have exactly 8 colors, has ${scheme.series.length}`);
}
if (scheme.sequential.length !== 5) {
throw new Error(
`${where}: sequential must have exactly 5 colors, has ${scheme.sequential.length}`,
);
}
for (const [name, value] of colors) {
if (!isHexColor(value)) throw new Error(`${where}.${name}: "${value}" is not a hex color`);
}
}
/**
* Validates a theme's shape and returns it unchanged. TypeScript already checks the shape at
* compile time; this is for themes built from data at run time (JSON, a CMS), where a typo would
* otherwise become an invisible chart instead of an error.
*
* It does not judge the colors: {@link checkTheme} does that, and reports rather than throws.
*/
export function defineTheme(theme: Theme): Theme {
if (theme.name.trim() === "") throw new Error("a theme needs a non-empty name");
assertCssText("theme name", theme.name);
if (theme.fonts.body.trim() === "") throw new Error(`theme "${theme.name}": fonts.body is empty`);
assertCssText(`theme "${theme.name}" fonts.body`, theme.fonts.body);
if (theme.fonts.numeric !== undefined) {
assertCssText(`theme "${theme.name}" fonts.numeric`, theme.fonts.numeric);
}
if (!GRIDLINES.has(theme.gridlines)) {
throw new Error(`theme "${theme.name}": gridlines must be none, x, y or both`);
}
assertScheme(theme.name, "light", theme.light);
assertScheme(theme.name, "dark", theme.dark);
return theme;
}