import { URL } from 'url'; import puppeteer from 'puppeteer-core'; import HttpServer from './HttpServer.mjs'; const defaultViewport = { // Using a 16:9 ratio here by default to match Twitter's image card dimensions width: 1200, height: 675, deviceScaleFactor: 2, }; export default class ScreenshotHelper { /** @type {HttpServer} */ #httpServer = null; /** @type {puppeteer.Browser} */ #browser = null; /** @type {puppeteer.Page} */ #page = null; defaultParams = null; get isOpen() { return !!this.#browser; } /** @type {puppeteer.Page} */ get page() { return this.#page; } async open() { await this.close(); // Start the HTTP server this.#httpServer = new HttpServer; await this.#httpServer.open(); // Connect to Browserless this.#browser = await puppeteer.connect({ browserWSEndpoint: process.env.BROWSERLESS_ENDPOINT, }); // Create a new page and set the viewport this.#page = await this.#browser.newPage(); await this.applyViewport(); } async applyViewport(viewport = {}) { if (this.#page) { await this.#page.setViewport({ ...defaultViewport, ...viewport, }); } } async capture(path, options = {}) { if (!this.isOpen) { await this.open(); } await this.applyViewport(options.viewport); // Navigate to the URL let host = process.env.SCREENSHOT_HOST || 'localhost'; let url = new URL(`http://${host}:${this.#httpServer.port}/screenshots/`); url.hash = path; let params = { ...this.defaultParams, ...options.params, }; if (params) { // We can't use url.searchParams because they need to come after the hash url.hash += '?'; url.hash += Object.keys(params) .map(key => `${key}=${params[key]}`) .join('&'); } await this.#page.goto(url, { waitUntil: 'networkidle0', // Wait until the network is idle }); // Wait an additional 1000ms await this.#page.waitForNetworkIdle({ idleTime: 1000 }); // Take the screenshot return await this.#page.screenshot(); } async close() { if (this.#httpServer) { await this.#httpServer.close(); } this.#httpServer = null; if (this.#page) { await this.#page.close(); } this.#page = null; if (this.#browser) { await this.#browser.close(); } this.#browser = null; } }