import { spawnSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { DOCX_PANDOC_VERSION, defaultExportConfig, themeManifestSchema } from "@md-to-pdf/core"; import { createDynamicReferenceDocx, PandocRuntime, validateGeneratedDocx } from "@md-to-pdf/docx-engine"; import { unzipSync } from "fflate"; const directory = path.dirname(fileURLToPath(import.meta.url)); const packageDirectory = path.resolve(directory, ".."); const repositoryDirectory = path.resolve(packageDirectory, "../.."); const fixturePath = path.join( packageDirectory, "fixtures", "pandoc-reference.md" ); const themePath = path.join( repositoryDirectory, "themes", "gov-red-standard", "theme.json" ); const runtime = new PandocRuntime( process.env.DOCX_PANDOC_PATH?.trim() ? { configuredPath: process.env.DOCX_PANDOC_PATH.trim() } : {} ); const runtimeResolution = await runtime.probe(); assert( runtimeResolution.capability.status === "available", runtimeResolution.capability.status === "available" ? "" : runtimeResolution.capability.message ); const pandocExecutable = await runtime.getExecutablePath(); const maximumBuffer = 128 * 1024 * 1024; const decoder = new TextDecoder(); function runPandoc(arguments_, options = {}) { const result = spawnSync(pandocExecutable, arguments_, { encoding: options.binary ? null : "utf8", maxBuffer: maximumBuffer, ...options }); if (result.error) { throw result.error; } if (result.status !== 0) { const stderr = Buffer.isBuffer(result.stderr) ? result.stderr.toString("utf8") : result.stderr; throw new Error( stderr?.trim() || `Pandoc 退出码 ${String(result.status)}` ); } return result; } function assert(condition, message) { if (!condition) { throw new Error(message); } } function countMatches(value, pattern) { return value.match(pattern)?.length ?? 0; } function inspectResult(content, expected) { const validation = validateGeneratedDocx(content); const entries = unzipSync(content); const documentXml = decoder.decode(entries["word/document.xml"]); const stylesXml = decoder.decode(entries["word/styles.xml"]); const settingsXml = decoder.decode(entries["word/settings.xml"]); const relationshipsXml = decoder.decode( entries["word/_rels/document.xml.rels"] ); const bodyIsEditable = documentXml.includes("可编辑的中文正文") && documentXml.includes(" /^word\/footer\d+\.xml$/u.test(name)) .some(([, value]) => decoder.decode(value).includes(" PAGE \\* MERGEFORMAT ") ), headerRelationship: expected.headerCount === 0 || relationshipsXml.includes("relationships/header"), footerRelationship: expected.footerCount === 0 || relationshipsXml.includes("relationships/footer"), evenOddSetting: expected.usesEvenAndOddPages === settingsXml.includes(" createVariant( temporaryDirectory, baseline, theme, markdown, variant ) ); console.log( JSON.stringify( { pandocVersion: detectedVersion, baselineBytes: baseline.byteLength, variants: results }, null, 2 ) ); } finally { fs.rmSync(temporaryDirectory, { recursive: true, force: true }); }