fix(css,preview): scope utilities under the view root, preview with real boards and foreign css

This commit is contained in:
hh committed 2026-08-29 19:39:19 +02:00
1 parent 0a7742eb5e
commit 8c971e431a
10 files changed
+458 -14

No files matched your search

+63 -1
View File
@@ -3,12 +3,13 @@ import {
existsSync,
mkdtempSync,
readdirSync,
readFileSync,
statSync,
writeFileSync,
} from "node:fs";
import { createServer } from "node:http";
import { homedir, tmpdir } from "node:os";
import { extname, join, normalize } from "node:path";
import { basename, extname, join, normalize, resolve } from "node:path";
import { extractFile } from "@electron/asar";
import esbuild from "esbuild";
import sveltePlugin from "esbuild-svelte";
@@ -65,6 +66,26 @@ if (!theme) {
);
}
/**
* Other plugins' stylesheets, loaded after ours the way Obsidian would load a
* plugin enabled later: EXTRA_CSS=path[,path…]. This is how a cascade fight
* with another Tailwind build is reproduced outside Obsidian.
*/
const extraCss = (process.env.EXTRA_CSS ?? "")
.split(",")
.map((item) => item.trim())
.filter(Boolean)
.map((item) => resolve(item));
for (const file of extraCss) {
if (!existsSync(file)) console.warn(`preview: EXTRA_CSS not found: ${file}`);
}
/** Real boards instead of the fixtures: BOARDS=<folder of .md boards>. */
const boards = process.env.BOARDS ? resolve(process.env.BOARDS) : null;
if (boards && !existsSync(boards)) {
console.warn(`preview: BOARDS folder not found: ${boards}`);
}
const ctx = await esbuild.context({
entryPoints: [join(root, "preview/entry.ts")],
bundle: true,
@@ -85,9 +106,17 @@ const TYPES = {
".css": "text/css; charset=utf-8",
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".md": "text/plain; charset=utf-8",
};
function boardNames() {
if (!boards || !existsSync(boards)) return null;
return readdirSync(boards)
.filter((name) => name.endsWith(".md"))
.sort();
}
createServer((request, response) => {
const url = decodeURIComponent((request.url ?? "/").split("?")[0]);
const external = { "/app.css": appCss, "/theme.css": theme };
@@ -103,6 +132,37 @@ createServer((request, response) => {
return;
}
if (url === "/extra.css") {
const body = extraCss
.filter((file) => existsSync(file))
.map((file) => `/* ${file} */\n${readFileSync(file, "utf8")}`)
.join("\n");
response
.writeHead(200, { "content-type": TYPES[".css"], "cache-control": "no-store" })
.end(body);
return;
}
if (url === "/boards.json") {
const names = boardNames();
const body = names ? { folder: basename(boards), names } : null;
response
.writeHead(200, { "content-type": TYPES[".json"], "cache-control": "no-store" })
.end(JSON.stringify(body));
return;
}
if (url.startsWith("/boards/") && boards) {
const file = join(boards, normalize(url.slice("/boards/".length)));
if (!file.startsWith(boards) || !existsSync(file) || statSync(file).isDirectory()) {
response.writeHead(404).end("not found");
return;
}
response.writeHead(200, { "content-type": TYPES[".md"], "cache-control": "no-store" });
createReadStream(file).pipe(response);
return;
}
const file = join(root, normalize(url === "/" ? "/preview/index.html" : url));
if (!file.startsWith(root) || !existsSync(file) || statSync(file).isDirectory()) {
response.writeHead(404).end("not found");
@@ -115,4 +175,6 @@ createServer((request, response) => {
createReadStream(file).pipe(response);
}).listen(port, () => {
console.log(`preview: http://localhost:${port}/`);
if (boards) console.log(`preview: boards from ${boards}`);
if (extraCss.length > 0) console.log(`preview: extra css ${extraCss.join(", ")}`);
});