openclaw/extensions/diffs/src/pierre-themes.ts

59 lines
1.9 KiB
TypeScript

import fs from "node:fs/promises";
import { createRequire } from "node:module";
import type { ThemeRegistrationResolved } from "@pierre/diffs";
import { RegisteredCustomThemes, ResolvedThemes, ResolvingThemes } from "@pierre/diffs";
type PierreThemeName = "pierre-dark" | "pierre-light";
const diffsRequire = createRequire(import.meta.resolve("@pierre/diffs"));
const PIERRE_THEME_SPECS = [
["pierre-dark", "@pierre/theme/themes/pierre-dark.json"],
["pierre-light", "@pierre/theme/themes/pierre-light.json"],
] as const satisfies ReadonlyArray<readonly [PierreThemeName, string]>;
function createThemeLoader(
themeName: PierreThemeName,
themeSpecifier: string,
): () => Promise<ThemeRegistrationResolved> {
let cachedTheme: ThemeRegistrationResolved | undefined;
return async () => {
if (cachedTheme) {
return cachedTheme;
}
const themePath = diffsRequire.resolve(themeSpecifier);
cachedTheme = {
...(JSON.parse(await fs.readFile(themePath, "utf8")) as Record<string, unknown>),
name: themeName,
} as ThemeRegistrationResolved;
return cachedTheme;
};
}
const PIERRE_THEME_LOADERS = new Map(
PIERRE_THEME_SPECS.map(([themeName, themeSpecifier]) => [
themeName,
createThemeLoader(themeName, themeSpecifier),
]),
);
export function ensurePierreThemesRegistered(): void {
let replacedThemeLoader = false;
for (const [themeName, loader] of PIERRE_THEME_LOADERS) {
if (RegisteredCustomThemes.get(themeName) !== loader) {
RegisteredCustomThemes.set(themeName, loader);
replacedThemeLoader = true;
}
}
if (!replacedThemeLoader) {
return;
}
// If another path swapped these loaders, clear the resolver caches so the
// next render rehydrates the highlighter with the Node-safe theme source.
for (const [themeName] of PIERRE_THEME_LOADERS) {
ResolvedThemes.delete(themeName);
ResolvingThemes.delete(themeName);
}
}