feat: move color themes into a bundled themes plugin

This commit is contained in:
LukeGus committed 2026-10-18 17:56:34 -05:00
1 parent d049588f7f
commit b414b85746
20 files changed
+305 -406

No files matched your search

+37 -2
View File
@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { readManifest } from "../lib/plugin-dir.mjs";
import { readManifest, isThemeOnly } from "../lib/plugin-dir.mjs";
/**
* The manifest rules live in src/manifest.ts, which the server uses too, so
@@ -25,7 +25,8 @@ export async function validate({ cwd }) {
// The manifest names files. They have to be there.
const referenced = [
raw.backend ?? "dist/backend.js",
raw.frontend ?? "dist/frontend.js",
// A theme only plugin has no frontend code.
...(isThemeOnly(raw) ? [] : [raw.frontend ?? "dist/frontend.js"]),
raw.locales ?? "locales",
];
for (const rel of referenced) {
@@ -35,6 +36,7 @@ export async function validate({ cwd }) {
}
problems.push(...validateThemeFiles(cwd, raw, validateThemeCss));
problems.push(...validateLookCapabilities(cwd, raw));
problems.push(
...(await validateMigrations(cwd, raw.id ?? path.basename(cwd))),
);
@@ -78,6 +80,39 @@ export function validateThemeFiles(cwd, raw, validateThemeCss) {
return problems;
}
/**
* ui:layout and ui:theme must match what the manifest contributes, both
* ways. The manifest check already flags a contribution without its
* capability; this flags a capability with nothing behind it, and a layout
* with no frontend to register it.
*/
export function validateLookCapabilities(cwd, raw) {
const problems = [];
const capabilities = Array.isArray(raw?.capabilities) ? raw.capabilities : [];
const layouts = raw?.contributes?.layouts;
const themes = raw?.contributes?.themes;
const hasLayouts = Array.isArray(layouts) && layouts.length > 0;
const hasThemes = Array.isArray(themes) && themes.length > 0;
if (capabilities.includes("ui:layout") && !hasLayouts) {
problems.push('"ui:layout" is declared but contributes.layouts is empty');
}
if (capabilities.includes("ui:theme") && !hasThemes) {
problems.push('"ui:theme" is declared but contributes.themes is empty');
}
if (hasLayouts) {
const entries = ["index.tsx", "index.ts", "index.mjs", "index.js"];
const frontend = entries.some((entry) =>
fs.existsSync(path.join(cwd, "src", "frontend", entry)),
);
if (!frontend && raw.frontend === undefined) {
problems.push(
"contributes.layouts needs a frontend that calls app.registerLayout",
);
}
}
return problems;
}
/** Env vars core sets for everyone, so a plugin need not list them. */
const CORE_ENV = new Set([
"DATA_DIR",