UNPKG

node-image-from-html

Version:

This single-dependency library provides a simple API to render HTML content using <a href="https://github.com/puppeteer/puppeteer">Puppeteer</a> (A headless chromium brower) into png and jpeg images, insuring security and efficiency in the process. Writte

111 lines (110 loc) 4.26 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BrowserHandler = void 0; const puppeteer_1 = __importDefault(require("puppeteer")); const ExtendedPage_js_1 = require("./ExtendedPage.js"); /** * @class BrowserHandler * This class is used to handle the browser and pages, allocating pages for use and releasing them back to the handler when idle. * To start rendering, <BrowserHandler>.start() must first be called. */ class BrowserHandler { /** * Constructor for the BrowserHandler class. * @param options The options for the BrowserHandler. */ constructor(options = {}) { this._browser = null; this.pages = []; this.options = options; } /** * Returns the underlying Puppeteer browser instance, throwing an exception if it's not started. */ get browser() { if (this._browser == null) { throw "Puppeteer Engine not started. Please await <BrowserHandler>.launch() before rendering."; } return this._browser; } /** * Starts the BrowserHandler engine & launches a new browser. * @param puppeteerOptions The options for the launched instance of Puppeteer. */ async start(puppeteerOptions = {}) { if (this._browser != null) { throw "Puppeteer Engine already started!"; } this._browser = await puppeteer_1.default.launch(puppeteerOptions); let spawnTasks = []; for (let x = 0; x < (this.options.concurrency ? this.options.concurrency : 1); x++) { spawnTasks.push(this.browser.newPage()); } this.pages = await Promise.all(spawnTasks) .then((pages) => pages.map(p => new ExtendedPage_js_1.ExtendedPage(p))); // Apply the network/js settings. for (const epage of this.pages) { if (this.options.disableJavaScript) { epage.page.setJavaScriptEnabled(false); } if (this.options.disableNetwork) { epage.page.setRequestInterception(true); } } } /** * Returns a page, waiting for one to be free if necessary. * @returns A promise that resolves to an ExtendedPage object. */ async waitForPage() { let elapsed = 0; while (true) { for (const page of this.pages) { if (page.inUse == false) { page.inUse = true; return page; } } await new Promise(resolve => setTimeout(resolve, 100)); elapsed += 100; if (elapsed > (this.options.timeout ? this.options.timeout : 15000)) { throw "Timeout waiting for page to become available. Perhaps attempt a higher concurrency?"; } } } /** * Closes the browser and cleans up any resources. */ async dispose() { await this.browser.close(); } /** * Renders an HTML string to an image format using the first available page, waiting for one to be free if necessary. * @param html HTML to render * @param screenshotOptions The options for the screenshot. * @returns Your rendered HTML as a Buffer. */ async render(html, screenshotOptions) { const epage = await this.waitForPage(); // Uri format supports 2mb of text. const uri = `data:text/html,${encodeURIComponent(html)}`; await epage.page.goto(uri); let target; if (screenshotOptions.selector) { target = await epage.page.$(screenshotOptions.selector); if (!target) { throw "Could not find element with selector: " + screenshotOptions.selector; } } else { target = await epage.page.mainFrame(); } const screenshot = await target.screenshot(screenshotOptions); epage.inUse = false; return screenshot; } } exports.BrowserHandler = BrowserHandler;