fix(poster): isolate automatic thumbnail generation in Node

This commit is contained in:
tianma-if committed 2026-10-19 18:26:11 +08:00
1 parent 1069edb8f4
commit 9234bac79e
7 files changed
+29 -22

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@ RUN bun install --frozen-lockfile \
FROM dependencies AS build
# Only stale template previews need the build browser; it stays out of runtime.
RUN apk add --no-cache chromium
RUN apk add --no-cache chromium nodejs
ENV EDGE_EVER_POSTER_CHROME_EXECUTABLE=/usr/bin/chromium
COPY apps/api apps/api
COPY apps/web apps/web
+2 -2
View File
@@ -1,9 +1,9 @@
import path from "node:path";
import type { Plugin, ViteDevServer } from "vite";
import { ensurePosterThumbnails, projectRoot, sources } from "../../scripts/prepare-poster-thumbnails.mjs";
import { runPosterThumbnailGeneration, projectRoot, sources } from "../../scripts/prepare-poster-thumbnails.mjs";
/** Generate on build/startup and coalesce template saves while rendering. */
export function automaticPosterThumbnails({ root = projectRoot, generate = ensurePosterThumbnails } = {}): Plugin {
export function automaticPosterThumbnails({ root = projectRoot, generate = runPosterThumbnailGeneration } = {}): Plugin {
let command: "build" | "serve";
let server: ViteDevServer | undefined;
let timer: ReturnType<typeof setTimeout> | undefined;
@@ -1,6 +1,6 @@
{
"version": 2,
"sourceHash": "9cd2f217b58072425f856730ab245e9723160c7fb387b02300824887e91a955e",
"sourceHash": "014dc1441fe7b47b32069e372c8820d70a8b58aa353f6232efaf7f4e1330f508",
"painterHash": "21dcb74339638258001efc7f15a679376cf51ff95ccbcf2cc31026d4f2a94a96",
"width": 480,
"height": 640,
+1 -1
View File
@@ -80,7 +80,7 @@ describe("Docker release contract", () => {
);
expect(dockerfile).toContain("RUN bun run build:web && bun run build:self-hosted");
expect(dockerfile).toContain("scripts/prepare-poster-thumbnails.mjs scripts/prepare-poster-thumbnails.d.mts scripts/");
expect(dockerfile).toContain("RUN apk add --no-cache chromium");
expect(dockerfile).toContain("RUN apk add --no-cache chromium nodejs");
expect(dockerfile).toContain("EDGE_EVER_POSTER_CHROME_EXECUTABLE=/usr/bin/chromium");
expect(dockerfile).toContain("COPY tests/browser/poster-thumbnail-generator.html tests/browser/poster-thumbnail-generator.ts tests/browser/");
expect(runtimeStage).toContain(
+1
View File
@@ -5,3 +5,4 @@ export function ensurePosterThumbnails(options?: {
output?: string;
log?: (message: string) => void;
}): Promise<{ generated: number; skipped: boolean }>;
export function runPosterThumbnailGeneration(options?: { root?: string }): Promise<{ generated: number; skipped: boolean }>;
+20 -3
View File
@@ -243,12 +243,29 @@ export async function ensurePosterThumbnails({ root = projectRoot, output = path
}
}
// Playwright's process/transport lifecycle is supported by Node. Keep browser
// generation out of the application's Vite/Bun process, including Docker builds.
export async function runPosterThumbnailGeneration({ root = projectRoot } = {}) {
if (await thumbnailsCurrent(path.join(root, "apps/web/src/assets/poster-template-previews"), await sourceState(root))) return { generated: 0, skipped: true };
const { stdout, stderr } = await promisify(execFile)("node", [fileURLToPath(import.meta.url), "--json", "--root", root], {
cwd: root, maxBuffer: 8 * 1024 * 1024,
});
if (stderr) process.stderr.write(stderr);
return JSON.parse(stdout);
}
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
const rootIndex = process.argv.indexOf("--root");
const root = rootIndex < 0 ? projectRoot : path.resolve(process.argv[rootIndex + 1]);
if (process.argv.includes("--check")) {
if (!await thumbnailsCurrent(path.join(projectRoot, "apps/web/src/assets/poster-template-previews"), await sourceState())) throw new Error("Poster thumbnails are stale; normal dev/build will regenerate them automatically.");
if (!await thumbnailsCurrent(path.join(root, "apps/web/src/assets/poster-template-previews"), await sourceState(root))) throw new Error("Poster thumbnails are stale; normal dev/build will regenerate them automatically.");
console.log("Poster thumbnails are current");
} else {
const result = await ensurePosterThumbnails();
if (result.skipped) console.log("[poster thumbnails] Already current; no renderer needed");
const json = process.argv.includes("--json");
const result = process.versions.bun
? await runPosterThumbnailGeneration({ root })
: await ensurePosterThumbnails({ root, log: json ? message => console.error(message) : console.log });
if (json) console.log(JSON.stringify(result));
else if (result.skipped) console.log("[poster thumbnails] Already current; no renderer needed");
}
}
+3 -14
View File
@@ -1,4 +1,4 @@
// Run with Bun. A temporary source fixture and Chrome exercise the real Vite
// Run with Node (native TypeScript support). A temporary source fixture and Chrome exercise the real Vite
// watcher and renderer; no working-tree templates or user notes are changed.
import assert from "node:assert/strict";
import { mkdtemp, mkdir, cp, readFile, writeFile, symlink, rm, stat } from "node:fs/promises";
@@ -6,7 +6,7 @@ import path from "node:path";
import { tmpdir } from "node:os";
import { createServer } from "vite";
import { automaticPosterThumbnails } from "../apps/web/poster-thumbnails.ts";
import { sources, projectRoot, ensurePosterThumbnails, openRenderer, sourceState, thumbnailsCurrent } from "./prepare-poster-thumbnails.mjs";
import { sources, projectRoot, ensurePosterThumbnails, sourceState, thumbnailsCurrent } from "./prepare-poster-thumbnails.mjs";
await ensurePosterThumbnails();
const root = await mkdtemp(path.join(tmpdir(), "edgeever-poster-watch-"));
@@ -34,18 +34,7 @@ try {
await symlink(path.join(projectRoot, "apps/web/node_modules"), path.join(root, "apps/web/node_modules"), process.platform === "win32" ? "junction" : "dir");
server = await createServer({
configFile: false, root, logLevel: "error", appType: "mpa",
plugins: [automaticPosterThumbnails({ root, generate: options => ensurePosterThumbnails({
...options,
renderer: async root => {
const runtime = await openRenderer(root);
const prepare = runtime.prepare;
runtime.prepare = async (id, language) => {
if (language === "zh") console.log(`[watch test] Inspecting ${id}`);
return prepare(id, language);
};
return runtime;
},
}) })],
plugins: [automaticPosterThumbnails({ root })],
server: { host: "127.0.0.1", port: 0, fs: { allow: [root, projectRoot] } },
optimizeDeps: { noDiscovery: true },
});