-
-
Notifications
You must be signed in to change notification settings - Fork 108
/
generate-output.ts
116 lines (95 loc) · 3.03 KB
/
generate-output.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { join, posix, sep } from 'path';
import puppeteer, { Browser } from 'puppeteer';
import { Config, HtmlConfig, PdfConfig } from './config';
import { isHttpUrl } from './is-http-url';
export type Output = PdfOutput | HtmlOutput;
export interface PdfOutput extends BasicOutput {
content: Buffer;
}
export interface HtmlOutput extends BasicOutput {
content: string;
}
interface BasicOutput {
filename: string | undefined;
}
/**
* Store a single browser instance reference so that we can re-use it.
*/
let browserPromise: Promise<Browser> | undefined;
/**
* Close the browser instance.
*/
export const closeBrowser = async () => (await browserPromise)?.close();
/**
* Generate the output (either PDF or HTML).
*/
export async function generateOutput(
html: string,
relativePath: string,
config: PdfConfig,
browserRef?: Browser,
): Promise<PdfOutput>;
export async function generateOutput(
html: string,
relativePath: string,
config: HtmlConfig,
browserRef?: Browser,
): Promise<HtmlOutput>;
export async function generateOutput(
html: string,
relativePath: string,
config: Config,
browserRef?: Browser,
): Promise<Output>;
export async function generateOutput(
html: string,
relativePath: string,
config: Config,
browserRef?: Browser,
): Promise<Output> {
async function getBrowser() {
if (browserRef) {
return browserRef;
}
if (!browserPromise) {
browserPromise = puppeteer.launch({ devtools: config.devtools, ...config.launch_options });
}
return browserPromise;
}
const browser = await getBrowser();
const page = await browser.newPage();
const urlPathname = join(relativePath, 'index.html').split(sep).join(posix.sep);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await page.goto(`http://localhost:${config.port!}/${urlPathname}`); // make sure relative paths work as expected
await page.setContent(html); // overwrite the page content with what was generated from the markdown
for (const stylesheet of config.stylesheet) {
await page.addStyleTag(isHttpUrl(stylesheet) ? { url: stylesheet } : { path: stylesheet });
}
if (config.css) {
await page.addStyleTag({ content: config.css });
}
for (const scriptTagOptions of config.script) {
await page.addScriptTag(scriptTagOptions);
}
/**
* Trick to wait for network to be idle.
*
* @todo replace with page.waitForNetworkIdle once exposed
* @see https://github.com/GoogleChrome/puppeteer/issues/3083
*/
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle0' }),
page.evaluate(() => history.pushState(undefined, '', '#')) /* eslint no-undef: off */,
]);
let outputFileContent: string | Buffer = '';
if (config.devtools) {
await new Promise((resolve) => page.on('close', resolve));
} else if (config.as_html) {
outputFileContent = await page.content();
} else {
await page.emulateMediaType(config.page_media_type);
outputFileContent = await page.pdf(config.pdf_options);
}
await page.close();
return config.devtools ? (undefined as any) : { filename: config.dest, content: outputFileContent };
}