Skip to content

Commit

Permalink
Support an instance mode for node-html-to-image
Browse files Browse the repository at this point in the history
**This is a breaking API change (for now)**

node-html-to-image's default export is now a function which returns an
instance object with a render() method which is the old
nodeHtmlToImage() functionality. The reason for doing this is to allow
us to instantiate a single 'image maker' object that we call render() on
multiple times and reuse the same puppeteer cluster.

In addition,
* Update the type definitions.
* Use Cluster.execute() instead of Cluster.queue() plus Cluster.idle()
  as this will allow better use in concurrent situations. We will only
  wait for our jobs to finish rather than for the entire cluster to go
  idle.

Fixes frinyvonnick#80
  • Loading branch information
joshk0 committed Jan 28, 2021
1 parent a9db417 commit 040a6db
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 34 deletions.
64 changes: 38 additions & 26 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
const puppeteer = require('puppeteer')
const { Cluster } = require('puppeteer-cluster')

const { makeScreenshot } = require('./screenshot.js')

module.exports = async function(options) {
module.exports = function(options) {
let instance = {};

const {
html,
content,
output,
puppeteerArgs = {},
} = options
} = options;

if (!html) {
throw Error('You must provide an html property.')
}

const cluster = await Cluster.launch({
const _cp = Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 2,
puppeteerOptions: { ...puppeteerArgs, headless: true },
});

let buffers = []
instance.shutdown = async () => {
const _cluster = await _cp;
await _cluster.close();
}

instance.render = async (renderOpts) => {
const {
html,
content,
output,
} = renderOpts;

await cluster.task(async ({ page, data: { content, output } }) => {
const buffer = await makeScreenshot(page, { ...options, content, output })
if (!html) {
throw Error('You must provide an html property.')
}

buffers.push(buffer);
});
let buffers = [];

const shouldBatch = Array.isArray(content)
const contents = shouldBatch ? content : [{ ...content, output }]
const _cluster = await _cp;
await _cluster.task(async ({ page, data: { content, output } }) => {
const buffer = await makeScreenshot(page, { ...renderOpts, content, output })
buffers.push(buffer);
});

contents.forEach(content => {
const { output, ...pageContent } = content
cluster.queue({ output, content: pageContent })
})
const shouldBatch = Array.isArray(content)
const contents = shouldBatch ? content : [{ ...content, output }]

await cluster.idle();
await cluster.close();
let promises = [];

return shouldBatch ? buffers : buffers[0]
}
contents.forEach(content => {
const { output, ...pageContent } = content;
promises.push(_cluster.execute({ output, content: pageContent }));
})

await Promise.all(promises);
return shouldBatch ? buffers : buffers[0];
};

return instance;
}
11 changes: 8 additions & 3 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/// <reference types="node" />

import { NodeHtmlToImageOptions } from './options';
import { NodeHtmlToImageConstructorOptions, NodeHtmlToImageOptions } from './options';

/**
* `node-html-to-image` takes a screenshot of the body tag's content.
* @param options Options to pass to the generatorr
*/
declare function nodeHtmlToImage(options: NodeHtmlToImageOptions): Promise<string | Buffer | (string | Buffer)[]>;
declare function nodeHtmlToImage(options: NodeHtmlToImageConstructorOptions): NodeHtmlToImageInstance;

export interface NodeHtmlToImageInstance {
render(options: NodeHtmlToImageOptions): Promise<string | Buffer | (string | Buffer)[]>;
}

export default nodeHtmlToImage;
export * from './options';
export * from './options';
13 changes: 8 additions & 5 deletions types/options.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import type { LaunchOptions, LoadEvent } from 'puppeteer';

export interface NodeHtmlToImageConstructorOptions {
/**
* The puppeteerArgs property let you pass down custom configuration to puppeteer.
* {@link https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#puppeteerlaunchoptions Learn more}
*/
puppeteerArgs?: LaunchOptions;
}

/**
* Available options to pass to the library
*/
Expand Down Expand Up @@ -32,11 +40,6 @@ export interface NodeHtmlToImageOptions {
* {@link https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#pagesetcontenthtml-options Learn more}
*/
waitUntil?: LoadEvent;
/**
* The puppeteerArgs property let you pass down custom configuration to puppeteer.
* {@link https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#puppeteerlaunchoptions Learn more}
*/
puppeteerArgs?: LaunchOptions;
/**
* Function executes before screenshot is taken and provides access to puppeteer page element
*/
Expand Down

0 comments on commit 040a6db

Please sign in to comment.