You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
34 lines
1.1 KiB
TypeScript
34 lines
1.1 KiB
TypeScript
import puppeteer, { type Browser } from 'puppeteer'
|
|
|
|
/**
|
|
* HTML → PDF via puppeteer. The browser is a lazy module-level singleton:
|
|
* launch costs ~1s, so it starts on first render and is reused after.
|
|
* Guarding with a promise (not the Browser) makes concurrent first calls
|
|
* share one launch instead of racing two Chromes.
|
|
*/
|
|
let browserPromise: Promise<Browser> | null = null
|
|
|
|
function getBrowser(): Promise<Browser> {
|
|
browserPromise ??= puppeteer.launch()
|
|
return browserPromise
|
|
}
|
|
|
|
export async function renderPdf(html: string): Promise<Buffer> {
|
|
const page = await (await getBrowser()).newPage()
|
|
try {
|
|
await page.setContent(html, { waitUntil: 'load' })
|
|
const bytes = await page.pdf({ format: 'A4', printBackground: true })
|
|
return Buffer.from(bytes)
|
|
} finally {
|
|
await page.close()
|
|
}
|
|
}
|
|
|
|
/** For tests/scripts that must let the process exit; the server never calls it. */
|
|
export async function closePdfBrowser(): Promise<void> {
|
|
if (browserPromise === null) return
|
|
const b = await browserPromise
|
|
browserPromise = null
|
|
await b.close()
|
|
}
|