feat: 内置 Desktop Pandoc 最小运行时

This commit is contained in:
SkyJourney
2026-08-02 10:44:40 +08:00
parent 9a2aa3c341
commit 3a8d2c1c2e
16 changed files with 636 additions and 46 deletions
@@ -0,0 +1,76 @@
import { createHash } from "node:crypto";
import { describe, expect, it } from "vitest";
import { strToU8, zipSync } from "fflate";
import {
extractRequiredFiles,
sha256
} from "../scripts/prepare-pandoc-runtime.mjs";
const artifact = {
executableRelativePath: "pandoc.exe",
licenseFiles: ["COPYING.rtf", "COPYRIGHT.txt"]
};
function archive(files: Record<string, string>) {
return zipSync(
Object.fromEntries(
Object.entries(files).map(([name, content]) => [
name,
strToU8(content)
])
)
);
}
describe("Desktop Pandoc 最小运行时构建", () => {
it("仅提取可执行文件和两份许可证文件", () => {
const files = extractRequiredFiles(
archive({
"pandoc-3.9.0.2/pandoc.exe": "executable",
"pandoc-3.9.0.2/COPYING.rtf": "license",
"pandoc-3.9.0.2/COPYRIGHT.txt": "copyright",
"pandoc-3.9.0.2/MANUAL.html": "manual"
}),
artifact
);
expect([...files.keys()].sort()).toEqual([
"COPYING.rtf",
"COPYRIGHT.txt",
"pandoc.exe"
]);
});
it("拒绝缺失许可证文件的归档", () => {
expect(() =>
extractRequiredFiles(
archive({
"pandoc.exe": "executable",
"COPYING.rtf": "license"
}),
artifact
)
).toThrow("COPYRIGHT.txt");
});
it("拒绝用不同目录伪造的重复文件", () => {
expect(() =>
extractRequiredFiles(
archive({
"official/pandoc.exe": "official",
"shadow/pandoc.exe": "shadow",
"official/COPYING.rtf": "license",
"official/COPYRIGHT.txt": "copyright"
}),
artifact
)
).toThrow("重复文件:pandoc.exe");
});
it("使用大写十六进制输出稳定 SHA-256", () => {
const content = strToU8("MorphDoc");
expect(sha256(content)).toBe(
createHash("sha256").update(content).digest("hex").toUpperCase()
);
});
});
+62
View File
@@ -17,6 +17,17 @@ interface ProductBrand {
artifactPrefix: string;
}
interface PandocRuntimeManifest {
version: string;
artifacts: Array<{
platform: string;
architecture: string;
sha256: string;
licenseFiles: string[];
files?: Record<string, { bytes: number; sha256: string }>;
}>;
}
const desktopRoot = fileURLToPath(new URL("../", import.meta.url));
const projectRoot = fileURLToPath(new URL("../../../", import.meta.url));
@@ -54,11 +65,62 @@ describe("桌面发行构建链", () => {
expect(desktopManifest.scripts?.make).toMatch(
/^npm run build:embedded-web && /
);
expect(desktopManifest.scripts?.package).toContain(
"npm run prepare:pandoc"
);
expect(desktopManifest.scripts?.make).toContain(
"npm run prepare:pandoc"
);
expect(rootManifest.scripts?.["desktop:package"]).toBe(
"npm run package -w @md-to-pdf/desktop"
);
});
it("将固定 Pandoc 最小运行时作为 Desktop 独立资源打包", () => {
const desktopManifest = readManifest(`${desktopRoot}/package.json`);
const runtimeManifest = readManifest<PandocRuntimeManifest>(
`${projectRoot}/packages/docx-engine/src/pandoc-runtime.json`
);
const builderConfig = readFileSync(
`${desktopRoot}/electron-builder.config.cjs`,
"utf8"
);
const prepareScript = readFileSync(
`${desktopRoot}/scripts/prepare-pandoc-runtime.mjs`,
"utf8"
);
const windowsArtifact = runtimeManifest.artifacts.find(
(artifact) =>
artifact.platform === "win32" && artifact.architecture === "x64"
);
expect(runtimeManifest.version).toBe("3.9.0.2");
expect(windowsArtifact).toMatchObject({
sha256:
"C97542F2800F446E788D9F74237856D995421AD1BB3CC8324286840C5F272D3A",
licenseFiles: ["COPYING.rtf", "COPYRIGHT.txt"],
files: {
"pandoc.exe": {
bytes: 231056136,
sha256:
"E83F8354C0F507222B5684797B9C5AE766F03889785995D14AAC27816EC456BA"
}
}
});
expect(desktopManifest.scripts?.["prepare:pandoc"]).toBe(
"node scripts/prepare-pandoc-runtime.mjs"
);
expect(builderConfig).toContain(
"`.runtime/pandoc/${pandocRuntime.version}/windows-x86_64`"
);
expect(builderConfig).toContain('"pandoc.exe"');
expect(builderConfig).toContain('"COPYING.rtf"');
expect(builderConfig).toContain('"COPYRIGHT.txt"');
expect(prepareScript).toContain("sha256(downloaded)");
expect(prepareScript).toContain("smokeTestPandoc");
expect(prepareScript).toContain('process.argv.includes("--check")');
});
it("将内置主题示例作为独立资源复制到发行目录", () => {
const builderConfig = readFileSync(
`${desktopRoot}/electron-builder.config.cjs`,