新增能力:桌面端新增 macOS 打包支持,electron-builder 增加独立 mac 配置块, 产出 dmg,按 arm64、x64 分别单独构建、不产出 universal 包;Pandoc 运行时清单 新增 darwin/arm64、darwin/x64 两个官方发行项(含独立许可证文件来源),桌面 Pandoc 候选路径与 prepare-pandoc-runtime.mjs 均已泛化为按 --platform/--arch 参数选取目标,不再只认 Windows x64。AGENTS.md 不再硬编码 Windows 绝对路径与 强制 PowerShell 语法,改为按当前 Shell 与仓库实际位置自适应。 问题修复:修复解压内置 Pandoc 时未保留 Unix 可执行权限位的问题(unzipSync 不保留压缩包内权限,已补 chmod 0o755);修复根 package.json 中 build:web-runtime/dev/desktop:dev 三个脚本的构建顺序错误 (@md-to-pdf/application 被排在其依赖的 @md-to-pdf/preview-engine 之前,在 没有历史 dist 残留的全新环境中会导致类型解析失败);修正 packages/docx-engine/tests/pandoc-runtime.test.ts 与 apps/desktop/tests/markdown-file.test.ts 中依赖宿主 OS 或路径分隔符的测试 断言,避免这些用例只能在特定平台上通过。 验证结果:docx-engine 包 93 项测试、desktop 包 62 项测试(61 通过 + 1 项 Windows 专属用例按预期跳过)均通过;全项目类型检查、生产构建通过, git diff --check 无空白错误。跳过依赖 Git 忽略的真实客户文档语料(tmp/)的 test:docx-real-world-corpus 与 test:docx-release-gate-suites 后,其余全部 工作区测试均通过;这两步需要接入真实语料的机器上单独执行。本机 macOS (Apple Silicon)实测 npm run package:mac:arm64 全链路成功,内置 Pandoc 3.9.0.2 完成下载、哈希校验与真实 DOCX 冒烟转换;实际截图确认窗口、macOS 原生菜单栏、编辑器工具栏、中文字体与实时预览渲染正常。 兼容与部署:版本统一为 0.6.4。本版本仅完成开发基础设施与验证,未构建正式 发行文件:没有产出真实签名的 dmg、没有重新构建 Windows 安装包,也没有重新 构建 Docker 镜像;正式 macOS 发行产物与 Windows/Docker 同步验证留待 DOCX 发布门禁阶段完成后一并发布。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5N6pcbjV4jVfXrcH3NcLP
116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
import { mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
createMarkdownContentHash,
|
|
createMarkdownDocumentKey,
|
|
findMarkdownFileArgument,
|
|
isMarkdownFilePath,
|
|
readMarkdownDocument,
|
|
readMarkdownFileSnapshot
|
|
} from "../src/markdown-file.js";
|
|
|
|
describe("桌面 Markdown 文件参数", () => {
|
|
it("识别两种 Markdown 扩展名且忽略大小写", () => {
|
|
expect(isMarkdownFilePath("C:\\docs\\周报.md")).toBe(true);
|
|
expect(isMarkdownFilePath("C:\\docs\\说明.MARKDOWN")).toBe(true);
|
|
expect(isMarkdownFilePath("C:\\docs\\说明.txt")).toBe(false);
|
|
});
|
|
|
|
// findMarkdownFileArgument 依赖 Node 的 path 模块识别绝对路径,其行为
|
|
// 随宿主 OS 而定:Windows 风格盘符路径只有在 Windows 宿主上才会被
|
|
// 识别为绝对路径,这与真实部署一致(Windows 安装包只会在 Windows 上
|
|
// 收到 Windows 风格的启动参数),因此该用例仅在 Windows 宿主上有意义。
|
|
it.runIf(process.platform === "win32")("从启动参数中提取绝对 Markdown 路径", () => {
|
|
expect(
|
|
findMarkdownFileArgument(
|
|
[
|
|
"C:\\Program Files\\md-to-pdf\\md-to-pdf.exe",
|
|
"--original-process-start-time=1",
|
|
"D:\\文档\\项目周报.md"
|
|
],
|
|
"C:\\Windows"
|
|
)
|
|
).toBe(path.normalize("D:\\文档\\项目周报.md"));
|
|
});
|
|
|
|
it("根据第二实例的工作目录解析相对路径", () => {
|
|
expect(
|
|
findMarkdownFileArgument(
|
|
["md-to-pdf.exe", "docs\\readme.markdown"],
|
|
"D:\\项目"
|
|
)
|
|
).toBe(path.resolve("D:\\项目", "docs\\readme.markdown"));
|
|
});
|
|
|
|
it("忽略非 Markdown 文件及 Chromium 参数", () => {
|
|
expect(
|
|
findMarkdownFileArgument(
|
|
["md-to-pdf.exe", "--inspect", "notes.txt"],
|
|
"C:\\项目"
|
|
)
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("统一读取 Markdown 并限制解码后的字符数", async () => {
|
|
const directory = await mkdtemp(
|
|
path.join(os.tmpdir(), "md-to-pdf-markdown-")
|
|
);
|
|
const filePath = path.join(directory, "示例.md");
|
|
try {
|
|
await writeFile(filePath, "# 示例", "utf8");
|
|
await expect(
|
|
readMarkdownDocument(filePath, 20)
|
|
).resolves.toEqual({
|
|
markdown: "# 示例",
|
|
fileName: "示例.md"
|
|
});
|
|
await expect(
|
|
readMarkdownDocument(filePath, 2)
|
|
).rejects.toThrow("Markdown 文件过大");
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("拒绝非 Markdown 扩展名", async () => {
|
|
await expect(
|
|
readMarkdownDocument("C:\\docs\\说明.txt", 100)
|
|
).rejects.toThrow("只允许打开 .md 或 .markdown 文件");
|
|
});
|
|
|
|
it("使用真实路径和内容哈希创建文件快照", async () => {
|
|
const directory = await mkdtemp(
|
|
path.join(os.tmpdir(), "md-to-pdf-snapshot-")
|
|
);
|
|
const filePath = path.join(directory, "快照.md");
|
|
try {
|
|
await writeFile(filePath, "# 第一版", "utf8");
|
|
// readMarkdownFileSnapshot 内部会 realpath 解析符号链接(例如 macOS
|
|
// 的 /var -> /private/var),返回的是规范化路径,因此断言也需要对
|
|
// 同一路径做 realpath,而不是直接比较 mkdtemp 拼接出的原始路径。
|
|
const canonicalFilePath = await realpath(filePath);
|
|
const snapshot = await readMarkdownFileSnapshot(filePath, 100);
|
|
|
|
expect(snapshot.document).toEqual({
|
|
markdown: "# 第一版",
|
|
fileName: "快照.md"
|
|
});
|
|
expect(snapshot.filePath).toBe(canonicalFilePath);
|
|
expect(snapshot.documentKey).toBe(
|
|
createMarkdownDocumentKey(canonicalFilePath)
|
|
);
|
|
expect(snapshot.contentHash).toBe(
|
|
createMarkdownContentHash("# 第一版")
|
|
);
|
|
|
|
await writeFile(filePath, "# 第二版", "utf8");
|
|
const changed = await readMarkdownFileSnapshot(filePath, 100);
|
|
expect(changed.contentHash).not.toBe(snapshot.contentHash);
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|