feat(exporters): PDF export (lazy puppeteer-core + system Chrome)#31
feat(exporters): PDF export (lazy puppeteer-core + system Chrome)#31
Conversation
…core) - exportPdf() in packages/exporters renders HTML to PDF via puppeteer-core connected to the user's installed system Chrome (chrome-discovery.ts) - exportArtifact() dispatcher in index.ts already routes 'pdf' lazily - codesign:export IPC in exporter-ipc.ts covers all four formats including PDF - PreviewToolbar already surfaces PDF in the export dropdown menu - i18n keys for PDF export present in en.json + zh-CN.json - puppeteer-core moved to peerDependencies (optional) — zero install size impact - puppeteer-core kept in devDependencies for test execution - Add exporter-ipc.test.ts covering bad-payload, unknown-format, and valid-pdf cases Signed-off-by: hqhq1025 <1506751656@qq.com>
| "puppeteer-core": "^24.10.0", | ||
| "zip-lib": "^1.0.4" | ||
| }, | ||
| "peerDependencies": { |
There was a problem hiding this comment.
[Blocker] puppeteer-core was removed from the runtime dependency graph here, but packages/exporters/src/pdf.ts:31 still lazy-imports it at export time and apps/desktop/package.json:15 does not declare it. The packaged Electron app only includes out/** plus package.json (apps/desktop/electron-builder.yml:6), so PDF export will fail once this is installed from a release build.
Suggested fix:
{
"dependencies": {
"@open-codesign/shared": "workspace:*",
"pptxgenjs": "^3.12.0",
"puppeteer-core": "^24.10.0",
"zip-lib": "^1.0.4"
}
}| // Test the parseRequest logic in isolation by importing only what we need from the module. | ||
| // We exercise the contract through the exported types rather than the private function. | ||
|
|
||
| describe('ExportRequest parsing', () => { |
There was a problem hiding this comment.
[Minor] This test replays the validation branches by hand instead of exercising the production parseRequest/registerExporterIpc path, so it can stay green even if the real IPC handler regresses.
Suggested fix:
// apps/desktop/src/main/exporter-ipc.ts
export function parseRequest(raw: unknown): ExportRequest { /* existing body */ }
// apps/desktop/src/main/exporter-ipc.test.ts
import { parseRequest } from './exporter-ipc';
expect(() => parseRequest(null)).toThrowError(
expect.objectContaining({ code: 'IPC_BAD_INPUT' }),
);…equest for tests Move puppeteer-core from peerDependencies back to dependencies so it is bundled in the packaged Electron app and dynamic import does not ENOENT at runtime (~3-5 MB SDK only, no Chromium). Export parseRequest from exporter-ipc.ts so the test exercises the real function instead of duplicating the validation logic inline. Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- No high-confidence issues found on the current added/modified lines in
apps/desktop/src/main/exporter-ipc.tsandapps/desktop/src/main/exporter-ipc.test.ts.
Summary
- Review mode: follow-up after new commits
- No new regressions found in the latest PR diff.
- Project-context docs check:
docs/VISION.mdanddocs/PRINCIPLES.mdare not present in this checkout (Not found in repo/docs), so constraint interpretation usedCLAUDE.mdplus diff-local evidence. - Residual risk/testing gap: IPC handler branch behavior (
registerExporterIpc) is still not directly exercised by this follow-up change set.
Testing
- Not run (automation)
open-codesign Bot
Summary
exportPdf()inpackages/exporters/src/pdf.tsrenders HTML → PDF viapuppeteer-coreconnected to the user's system Chrome (discovered bychrome-discovery.ts)exportArtifact()dispatcher inindex.tsalready routes'pdf'lazily — the module only loads on first export callcodesign:exportIPC inexporter-ipc.tscovers all four formats including PDF with a singleshowSaveDialogflowPreviewToolbaralready surfaces PDF in the export dropdown;store.exportActive()calls it through the preload bridgepuppeteer-coremoved fromdependencies→peerDependencies(optional) +devDependencies— zero install-size impact; end users who want PDF export need system Chrome, not bundled Chromiumexporter-ipc.test.tscovering bad-payload (IPC_BAD_INPUT), unknown-format (EXPORTER_UNKNOWN), and valid-pdf-request casesen.json+zh-CN.json(export.items.pdf.*)Demo coverage
Enables reproduction of Claude Design's Client Case Study one-pager → PDF demo.
Hard constraint compliance
puppeteer-coreis optional peer dep, not bundledEXPORTER_NO_CHROMEwith install URL otherwiseimport('puppeteer-core')only executes insideexportPdf(), not at app startTest plan
exportPdfwrites file and returns expected{ path, bytes }chromePathoverride skips discovery entirelyEXPORTER_PDF_FAILEDIPC_BAD_INPUTEXPORTER_UNKNOWN{ status: 'cancelled' }(covered by IPC handler logic test)EXPORTER_NO_CHROME