refactor: 收敛渲染链路共享抽象

This commit is contained in:
SkyJourney
2026-07-27 00:46:16 +08:00
parent b459def232
commit ad18149c10
17 changed files with 480 additions and 255 deletions
+12 -11
View File
@@ -4,6 +4,7 @@ import { performance } from "node:perf_hooks";
import Fastify from "fastify";
import {
EXPORT_CONFIG_VERSION,
createPagedDocumentPayload,
defaultExportConfig,
exportConfigSchema,
supportedPaperFormats
@@ -354,17 +355,17 @@ export function buildApp(options: BuildAppOptions = {}) {
const rendered = renderedResult.document;
try {
const generated = await pdfGenerator.generate({
articleHtml: rendered.articleHtml,
fileName:
typeof request.body.fileName === "string"
? request.body.fileName
: "文档.md",
metadata: rendered.metadata,
features: rendered.features,
themeCss,
exportConfig: parsedConfig.data
});
const generated = await pdfGenerator.generate(
createPagedDocumentPayload({
document: rendered,
fileName:
typeof request.body.fileName === "string"
? request.body.fileName
: "文档.md",
themeCss,
exportConfig: parsedConfig.data
})
);
const pdfFileName = createPdfFileName(request.body.fileName);
const requestMs = performance.now() - requestStartedAt;
const serverTiming = createPdfServerTiming(
+7 -33
View File
@@ -1,21 +1,11 @@
import { chromium, type Browser } from "playwright";
import { performance } from "node:perf_hooks";
import type { ExportConfig } from "@md-to-pdf/core";
import type {
PagedDocumentPayload,
PagedDocumentRenderResult
} from "@md-to-pdf/core";
export interface PdfRenderPayload {
articleHtml: string;
fileName: string;
metadata: {
title: string;
author: string;
subject: string;
keywords: string[];
language: string;
};
features: string[];
themeCss: string;
exportConfig: ExportConfig;
}
export type PdfRenderPayload = PagedDocumentPayload;
export interface PdfGenerationResult {
pdf: Buffer;
@@ -62,22 +52,6 @@ export interface PdfEngineRuntimeLimits {
timeoutMs: number;
}
interface PagedRuntimeResult {
pageCount: number;
contentHeight: number;
mermaidErrors: string[];
timings: {
setupMs: number;
mermaidMs: number;
mermaidFitMs: number;
mermaidConversionMs: number;
resourceWaitMs: number;
paginationMs: number;
finalizeMs: number;
totalMs: number;
};
}
interface PdfDocumentResult
extends Omit<PdfGenerationResult, "timings"> {
timings: Pick<
@@ -457,13 +431,13 @@ export class PlaywrightPdfGenerator implements PdfGenerator {
const documentRenderStartedAt = performance.now();
const renderResult = await page.evaluate(
async (renderPayload): Promise<PagedRuntimeResult> => {
async (renderPayload): Promise<PagedDocumentRenderResult> => {
const renderer = (
window as typeof window & {
__mdToPdfRender?: (
payload: PdfRenderPayload,
target: "pdf"
) => Promise<PagedRuntimeResult>;
) => Promise<PagedDocumentRenderResult>;
}
).__mdToPdfRender;
if (!renderer) {
+37 -1
View File
@@ -27,6 +27,7 @@ export interface ThemeRegistryOptions {
bundledRoot: string;
localRoot: string;
onWarning?: (message: string) => void;
cacheTtlMs?: number;
}
const assetContentTypes: Record<string, string> = {
@@ -275,7 +276,18 @@ async function loadThemeCssFile(
}
export function createThemeRegistry(options: ThemeRegistryOptions) {
async function list() {
const cacheTtlMs = options.cacheTtlMs ?? 1_000;
if (!Number.isFinite(cacheTtlMs) || cacheTtlMs < 0) {
throw new Error("主题缓存有效期必须是非负有限数值");
}
let cachedThemes:
| {
expiresAt: number;
promise: Promise<ThemeRecord[]>;
}
| undefined;
async function scanThemes() {
const [bundledThemes, localThemes] = await Promise.all([
readThemeRoot(options.bundledRoot, "bundled"),
readThemeRoot(options.localRoot, "local", options.onWarning)
@@ -293,6 +305,29 @@ export function createThemeRegistry(options: ThemeRegistryOptions) {
return themes;
}
function list() {
const now = Date.now();
if (cachedThemes && now < cachedThemes.expiresAt) {
return cachedThemes.promise;
}
const promise = scanThemes();
cachedThemes = {
expiresAt: now + cacheTtlMs,
promise
};
void promise.catch(() => {
if (cachedThemes?.promise === promise) {
cachedThemes = undefined;
}
});
return promise;
}
function invalidate() {
cachedThemes = undefined;
}
async function get(themeId: string) {
return (await list()).find((theme) => theme.manifest.id === themeId);
}
@@ -340,6 +375,7 @@ export function createThemeRegistry(options: ThemeRegistryOptions) {
get,
getAsset,
getCss,
invalidate,
list
};
}