Skip to content
This repository was archived by the owner on Mar 30, 2025. It is now read-only.

Commit b245906

Browse files
committed
Reuse the browser if files are processed concurrently
1 parent d167ac3 commit b245906

File tree

4 files changed

+53
-6
lines changed

4 files changed

+53
-6
lines changed

__fixtures__/gantt/output.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
<tspan x="10" alignment-baseline="central">Critical tasks</tspan>
109109
</text>
110110
</g>
111-
<path fill="none" stroke="red" stroke-width="2" d="M70593 25v290"/>
111+
<path fill="none" stroke="red" stroke-width="2" d="M70727 25v290"/>
112112
<text x="292" y="25" font-family="'trebuchet ms',verdana,arial,sans-serif" font-size="18" text-anchor="middle">
113113
Adding GANTT diagram functionality to mermaid
114114
</text>

__tests__/index.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { promises as fs, readdirSync } from 'fs';
22
import { join, resolve } from 'path';
33

44
import { toMatchFile } from 'jest-file-snapshot';
5+
import * as puppeteer from 'puppeteer';
56
import * as remark from 'remark';
67

78
import { defaultSVGOOptions, remarkMermaid, RemarkMermaidOptions } from '../src';
@@ -27,3 +28,38 @@ describe('fixtures', () => {
2728
expect(contents).toMatchFile(expected);
2829
});
2930
});
31+
32+
const markdown = `
33+
\`\`\`mermaid
34+
graph TD;
35+
A-->B;
36+
\`\`\`
37+
`;
38+
39+
it('should not launch the browser if no graphs are defined', async () => {
40+
const processor = remark().use(remarkMermaid);
41+
jest.spyOn(puppeteer, 'launch');
42+
await processor.process('```js\n```');
43+
// eslint-disable-next-line jest/no-restricted-matchers
44+
expect(puppeteer.launch).not.toHaveBeenCalled();
45+
});
46+
47+
it('should share the browser for concurrent calls', async () => {
48+
const processor = remark().use(remarkMermaid);
49+
jest.spyOn(puppeteer, 'launch');
50+
await Promise.all(
51+
Array.from<string>({ length: 3 })
52+
.fill(markdown)
53+
.map((content) => processor.process(content)),
54+
);
55+
expect(puppeteer.launch).toHaveBeenCalledTimes(1);
56+
});
57+
58+
it('should close the browser if no more files are being processed', async () => {
59+
const processor = remark().use(remarkMermaid);
60+
jest.spyOn(puppeteer, 'launch');
61+
for (const content of Array.from<string>({ length: 3 }).fill(markdown)) {
62+
await processor.process(content);
63+
}
64+
expect(puppeteer.launch).toHaveBeenCalledTimes(3);
65+
});

jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ module.exports = {
22
preset: 'ts-jest',
33
testEnvironment: 'node',
44
collectCoverageFrom: ['<rootDir>/src/*'],
5+
clearMocks: true,
6+
resetMocks: true,
7+
restoreMocks: true,
58
globals: {
69
'ts-jest': {
710
isolatedModules: true,

src/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { pathToFileURL } from 'url';
44
// eslint-disable-next-line import/no-unresolved
55
import { Code, Parent } from 'mdast';
66
import { Mermaid } from 'mermaid';
7-
import { Browser, launch, LaunchOptions } from 'puppeteer';
7+
import { Browser, launch, LaunchOptions, Page } from 'puppeteer';
88
import * as SVGO from 'svgo';
99
import { Attacher } from 'unified';
1010
import * as visit from 'unist-util-visit';
@@ -89,6 +89,8 @@ export const remarkMermaid: Attacher<[RemarkMermaidOptions?]> = ({
8989
theme = 'default',
9090
} = {}) => {
9191
const optimizer = svgo && new SVGO(svgo);
92+
let browserPromise: Promise<Browser> | undefined;
93+
let count = 0;
9294

9395
return async function transformer(ast) {
9496
const instances: [string, number, Parent][] = [];
@@ -102,10 +104,12 @@ export const remarkMermaid: Attacher<[RemarkMermaidOptions?]> = ({
102104
return ast;
103105
}
104106

105-
let browser: Browser;
107+
count += 1;
108+
browserPromise ??= launch(launchOptions);
109+
const browser = await browserPromise;
110+
let page: Page | undefined;
106111
try {
107-
browser = await launch(launchOptions);
108-
const page = await browser.newPage();
112+
page = await browser.newPage();
109113
await page.goto(String(pathToFileURL(resolve(__dirname, '..', 'index.html'))));
110114
await page.addScriptTag({ path: require.resolve('mermaid/dist/mermaid.min') });
111115
await page.setViewport({ width: 600, height: 3000 });
@@ -135,7 +139,11 @@ export const remarkMermaid: Attacher<[RemarkMermaidOptions?]> = ({
135139
}),
136140
);
137141
} finally {
138-
// @ts-expect-error The browser is referenced before it’s defined on purpose here.
142+
count -= 1;
143+
await page?.close();
144+
}
145+
if (!count) {
146+
browserPromise = undefined;
139147
await browser?.close();
140148
}
141149

0 commit comments

Comments
 (0)