| /** |
| * @license |
| * Copyright 2020 Google Inc. |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| import { |
| Browser as browsers_SupportedBrowser, |
| resolveBuildId, |
| detectBrowserPlatform, |
| getInstalledBrowsers, |
| uninstall, |
| } from '@puppeteer/browsers'; |
| |
| import type {Browser} from '../api/Browser.js'; |
| import type {Configuration} from '../common/Configuration.js'; |
| import type {ConnectOptions} from '../common/ConnectOptions.js'; |
| import {type CommonPuppeteerSettings, Puppeteer} from '../common/Puppeteer.js'; |
| import type {SupportedBrowser} from '../common/SupportedBrowser.js'; |
| import {PUPPETEER_REVISIONS} from '../revisions.js'; |
| |
| import type {BrowserLauncher} from './BrowserLauncher.js'; |
| import {ChromeLauncher} from './ChromeLauncher.js'; |
| import {FirefoxLauncher} from './FirefoxLauncher.js'; |
| import type {ChromeReleaseChannel, LaunchOptions} from './LaunchOptions.js'; |
| |
| /** |
| * Extends the main {@link Puppeteer} class with Node specific behaviour for |
| * fetching and downloading browsers. |
| * |
| * If you're using Puppeteer in a Node environment, this is the class you'll get |
| * when you run `require('puppeteer')` (or the equivalent ES `import`). |
| * |
| * @remarks |
| * The most common method to use is {@link PuppeteerNode.launch | launch}, which |
| * is used to launch and connect to a new browser instance. |
| * |
| * See {@link Puppeteer | the main Puppeteer class} for methods common to all |
| * environments, such as {@link Puppeteer.connect}. |
| * |
| * @example |
| * The following is a typical example of using Puppeteer to drive automation: |
| * |
| * ```ts |
| * import puppeteer from 'puppeteer'; |
| * |
| * const browser = await puppeteer.launch(); |
| * const page = await browser.newPage(); |
| * await page.goto('https://www.google.com'); |
| * // other actions... |
| * await browser.close(); |
| * ``` |
| * |
| * Once you have created a `page` you have access to a large API to interact |
| * with the page, navigate, or find certain elements in that page. |
| * The {@link Page | `page` documentation} lists all the available methods. |
| * |
| * @public |
| */ |
| export class PuppeteerNode extends Puppeteer { |
| #launcher?: BrowserLauncher; |
| #lastLaunchedBrowser?: SupportedBrowser; |
| |
| /** |
| * @internal |
| */ |
| defaultBrowserRevision: string; |
| |
| /** |
| * @internal |
| */ |
| configuration: Configuration = {}; |
| |
| /** |
| * @internal |
| */ |
| constructor( |
| settings: { |
| configuration?: Configuration; |
| } & CommonPuppeteerSettings, |
| ) { |
| const {configuration, ...commonSettings} = settings; |
| super(commonSettings); |
| if (configuration) { |
| this.configuration = configuration; |
| } |
| switch (this.configuration.defaultBrowser) { |
| case 'firefox': |
| this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox; |
| break; |
| default: |
| this.configuration.defaultBrowser = 'chrome'; |
| this.defaultBrowserRevision = PUPPETEER_REVISIONS.chrome; |
| break; |
| } |
| |
| this.connect = this.connect.bind(this); |
| this.launch = this.launch.bind(this); |
| this.executablePath = this.executablePath.bind(this); |
| this.defaultArgs = this.defaultArgs.bind(this); |
| this.trimCache = this.trimCache.bind(this); |
| } |
| |
| /** |
| * This method attaches Puppeteer to an existing browser instance. |
| * |
| * @param options - Set of configurable options to set on the browser. |
| * @returns Promise which resolves to browser instance. |
| */ |
| override connect(options: ConnectOptions): Promise<Browser> { |
| return super.connect(options); |
| } |
| |
| /** |
| * Launches a browser instance with given arguments and options when |
| * specified. |
| * |
| * When using with `puppeteer-core`, |
| * {@link LaunchOptions.executablePath | options.executablePath} or |
| * {@link LaunchOptions.channel | options.channel} must be provided. |
| * |
| * @example |
| * You can use {@link LaunchOptions.ignoreDefaultArgs | options.ignoreDefaultArgs} |
| * to filter out `--mute-audio` from default arguments: |
| * |
| * ```ts |
| * const browser = await puppeteer.launch({ |
| * ignoreDefaultArgs: ['--mute-audio'], |
| * }); |
| * ``` |
| * |
| * @remarks |
| * Puppeteer can also be used to control the Chrome browser, but it works best |
| * with the version of Chrome for Testing downloaded by default. |
| * There is no guarantee it will work with any other version. If Google Chrome |
| * (rather than Chrome for Testing) is preferred, a |
| * {@link https://www.google.com/chrome/browser/canary.html | Chrome Canary} |
| * or |
| * {@link https://www.chromium.org/getting-involved/dev-channel | Dev Channel} |
| * build is suggested. See |
| * {@link https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/ | this article} |
| * for a description of the differences between Chromium and Chrome. |
| * {@link https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md | This article} |
| * describes some differences for Linux users. See |
| * {@link https://developer.chrome.com/blog/chrome-for-testing/ | this doc} for the description |
| * of Chrome for Testing. |
| * |
| * @param options - Options to configure launching behavior. |
| */ |
| launch(options: LaunchOptions = {}): Promise<Browser> { |
| const {browser = this.defaultBrowser} = options; |
| this.#lastLaunchedBrowser = browser; |
| switch (browser) { |
| case 'chrome': |
| this.defaultBrowserRevision = PUPPETEER_REVISIONS.chrome; |
| break; |
| case 'firefox': |
| this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox; |
| break; |
| default: |
| throw new Error(`Unknown product: ${browser}`); |
| } |
| this.#launcher = this.#getLauncher(browser); |
| return this.#launcher.launch(options); |
| } |
| |
| /** |
| * @internal |
| */ |
| #getLauncher(browser: SupportedBrowser): BrowserLauncher { |
| if (this.#launcher && this.#launcher.browser === browser) { |
| return this.#launcher; |
| } |
| switch (browser) { |
| case 'chrome': |
| return new ChromeLauncher(this); |
| case 'firefox': |
| return new FirefoxLauncher(this); |
| default: |
| throw new Error(`Unknown product: ${browser}`); |
| } |
| } |
| |
| /** |
| * The default executable path for a given ChromeReleaseChannel. |
| */ |
| executablePath(channel: ChromeReleaseChannel): string; |
| /** |
| * The default executable path given LaunchOptions. |
| */ |
| executablePath(options: LaunchOptions): string; |
| /** |
| * The default executable path. |
| */ |
| executablePath(): string; |
| executablePath(optsOrChannel?: ChromeReleaseChannel | LaunchOptions): string { |
| if (optsOrChannel === undefined) { |
| return this.#getLauncher(this.lastLaunchedBrowser).executablePath( |
| undefined, |
| /* validatePath= */ false, |
| ); |
| } |
| if (typeof optsOrChannel === 'string') { |
| return this.#getLauncher('chrome').executablePath( |
| optsOrChannel, |
| /* validatePath= */ false, |
| ); |
| } |
| return this.#getLauncher( |
| optsOrChannel.browser ?? this.lastLaunchedBrowser, |
| ).resolveExecutablePath(optsOrChannel.headless, /* validatePath= */ false); |
| } |
| |
| /** |
| * @internal |
| */ |
| get browserVersion(): string { |
| return ( |
| this.configuration?.[this.lastLaunchedBrowser]?.version ?? |
| this.defaultBrowserRevision! |
| ); |
| } |
| |
| /** |
| * The default download path for puppeteer. For puppeteer-core, this |
| * code should never be called as it is never defined. |
| * |
| * @internal |
| */ |
| get defaultDownloadPath(): string | undefined { |
| return this.configuration.cacheDirectory; |
| } |
| |
| /** |
| * The name of the browser that was last launched. |
| */ |
| get lastLaunchedBrowser(): SupportedBrowser { |
| return this.#lastLaunchedBrowser ?? this.defaultBrowser; |
| } |
| |
| /** |
| * The name of the browser that will be launched by default. For |
| * `puppeteer`, this is influenced by your configuration. Otherwise, it's |
| * `chrome`. |
| */ |
| get defaultBrowser(): SupportedBrowser { |
| return this.configuration.defaultBrowser ?? 'chrome'; |
| } |
| |
| /** |
| * @deprecated Do not use as this field as it does not take into account |
| * multiple browsers of different types. Use |
| * {@link PuppeteerNode.defaultBrowser | defaultBrowser} or |
| * {@link PuppeteerNode.lastLaunchedBrowser | lastLaunchedBrowser}. |
| * |
| * @returns The name of the browser that is under automation. |
| */ |
| get product(): string { |
| return this.lastLaunchedBrowser; |
| } |
| |
| /** |
| * @param options - Set of configurable options to set on the browser. |
| * |
| * @returns The default arguments that the browser will be launched with. |
| */ |
| defaultArgs(options: LaunchOptions = {}): string[] { |
| return this.#getLauncher( |
| options.browser ?? this.lastLaunchedBrowser, |
| ).defaultArgs(options); |
| } |
| |
| /** |
| * Removes all non-current Firefox and Chrome binaries in the cache directory |
| * identified by the provided Puppeteer configuration. The current browser |
| * version is determined by resolving PUPPETEER_REVISIONS from Puppeteer |
| * unless `configuration.browserRevision` is provided. |
| * |
| * @remarks |
| * |
| * Note that the method does not check if any other Puppeteer versions |
| * installed on the host that use the same cache directory require the |
| * non-current binaries. |
| * |
| * @public |
| */ |
| async trimCache(): Promise<void> { |
| const platform = detectBrowserPlatform(); |
| if (!platform) { |
| throw new Error('The current platform is not supported.'); |
| } |
| |
| const cacheDir = this.configuration.cacheDirectory!; |
| const installedBrowsers = await getInstalledBrowsers({ |
| cacheDir, |
| }); |
| |
| const puppeteerBrowsers: Array<{ |
| product: SupportedBrowser; |
| browser: browsers_SupportedBrowser; |
| currentBuildId: string; |
| }> = [ |
| { |
| product: 'chrome', |
| browser: browsers_SupportedBrowser.CHROME, |
| currentBuildId: '', |
| }, |
| { |
| product: 'firefox', |
| browser: browsers_SupportedBrowser.FIREFOX, |
| currentBuildId: '', |
| }, |
| ]; |
| |
| // Resolve current buildIds. |
| for (const item of puppeteerBrowsers) { |
| const tag = |
| this.configuration?.[item.product]?.version ?? |
| PUPPETEER_REVISIONS[item.product]; |
| |
| item.currentBuildId = await resolveBuildId(item.browser, platform, tag); |
| } |
| |
| const currentBrowserBuilds = new Set( |
| puppeteerBrowsers.map(browser => { |
| return `${browser.browser}_${browser.currentBuildId}`; |
| }), |
| ); |
| |
| const currentBrowsers = new Set( |
| puppeteerBrowsers.map(browser => { |
| return browser.browser; |
| }), |
| ); |
| |
| for (const installedBrowser of installedBrowsers) { |
| // Don't uninstall browsers that are not managed by Puppeteer yet. |
| if (!currentBrowsers.has(installedBrowser.browser)) { |
| continue; |
| } |
| // Keep the browser build used by the current Puppeteer installation. |
| if ( |
| currentBrowserBuilds.has( |
| `${installedBrowser.browser}_${installedBrowser.buildId}`, |
| ) |
| ) { |
| continue; |
| } |
| |
| await uninstall({ |
| browser: installedBrowser.browser, |
| platform, |
| cacheDir, |
| buildId: installedBrowser.buildId, |
| }); |
| } |
| } |
| } |