@cityssm/puppeteer-launch
Version:
Launch Puppeteer, falling back to system browsers when the cached ones aren't working or aren't available.
128 lines (107 loc) • 3.9 kB
text/typescript
// eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair
/* eslint-disable security/detect-child-process, sonarjs/os-command */
import { exec } from 'node:child_process'
import Debug from 'debug'
import puppeteer from 'puppeteer'
import { DEBUG_NAMESPACE } from './debug.config.js'
export const INSTALLER_TIMEOUT = 5 * 60 * 1000
const debug = Debug(`${DEBUG_NAMESPACE}:installers`)
/**
* Installs the specified browser for Puppeteer.
* Times out after 5 minutes.
* @param browser - The browser to install ('chrome' or 'firefox').
* @returns A promise that resolves when the installation is complete.
*/
export async function installBrowser(
browser: 'chrome' | 'firefox'
): Promise<void> {
// eslint-disable-next-line promise/avoid-new, @typescript-eslint/return-await
return new Promise((resolve, reject) => {
exec(
`npx puppeteer browsers install ${browser}`,
{ timeout: INSTALLER_TIMEOUT },
(error, stdout, stderr) => {
if (stdout !== '') {
debug('stdout: %s', stdout)
}
if (stderr !== '') {
debug('stderr: %s', stderr)
}
if (error) {
reject(error)
} else {
resolve()
}
}
)
})
}
/**
* Installs the Chrome browser for Puppeteer.
*/
export async function installChromeBrowser(): Promise<void> {
await installBrowser('chrome')
}
/**
* Installs the Firefox browser for Puppeteer.
*/
export async function installFirefoxBrowser(): Promise<void> {
await installBrowser('firefox')
}
export interface TestInstalledBrowserResult {
/** Whether the browser is installed and able to launch */
success: boolean
/** Whether the installer was run */
ranInstaller: boolean
}
/**
* Tests if the specified browser is installed.
* @param browserName - The name of the browser to test ('chrome' or 'firefox').
* @param installIfUnavailable - Whether to install the browser if it's not available.
* @returns An object containing the installation status and whether the installer was run.
*/
export async function testInstalledBrowser(
browserName: 'chrome' | 'firefox',
installIfUnavailable = false
): Promise<TestInstalledBrowserResult> {
let browser: puppeteer.Browser | undefined
try {
browser = await puppeteer.launch({
browser: browserName,
args: ['--no-sandbox']
})
return { success: true, ranInstaller: false }
} catch (error) {
if (installIfUnavailable) {
await installBrowser(browserName)
return {
...(await testInstalledBrowser(browserName, false)),
ranInstaller: true
} satisfies TestInstalledBrowserResult
}
debug('Browser not installed: %O', error)
return { success: false, ranInstaller: false }
} finally {
await browser?.close()
}
}
/**
* Tests if the Puppeteer Chrome (or Chromium) browser is installed.
* @param installIfUnavailable - Whether to install the browser if it's not available.
* @returns A promise that resolves to an object containing the installation status and whether the installer was run.
*/
export async function testInstalledChromeBrowser(
installIfUnavailable = false
): Promise<TestInstalledBrowserResult> {
return await testInstalledBrowser('chrome', installIfUnavailable)
}
/**
* Tests if the Puppeteer Firefox browser is installed.
* @param installIfUnavailable - Whether to install the browser if it's not available.
* @returns A promise that resolves to an object containing the installation status and whether the installer was run.
*/
export async function testInstalledFirefoxBrowser(
installIfUnavailable = false
): Promise<TestInstalledBrowserResult> {
return await testInstalledBrowser('firefox', installIfUnavailable)
}