Skip to content

Commit

Permalink
Support Puppeteer 14.x - 19.x (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexey-pelykh authored Jan 6, 2023
1 parent fab5d25 commit 554e824
Show file tree
Hide file tree
Showing 9 changed files with 8,159 additions and 5,284 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,33 @@ jobs:
node-version:
- lts/fermium
- lts/gallium
puppeteer-version:
- '14.0'
- '14.1'
- '14.2'
- '14.3'
- '14.4'
- '15.0'
- '15.1'
- '15.2'
- '15.3'
- '15.4'
- '15.5'
- '16.0'
- '16.1'
- '16.2'
- '17.0'
- '17.1'
- '18.0'
- '18.1'
- '18.2'
- '19.0'
- '19.1'
- '19.2'
- '19.3'
- '19.4'
env:
PUPPETEER_CAPTURE__PUPPETEER_VERSION: -${{ matrix.puppeteer-version }}
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
13,285 changes: 8,046 additions & 5,239 deletions package-lock.json

Large diffs are not rendered by default.

46 changes: 35 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,52 @@
],
"main": "lib/index.js",
"dependencies": {
"async-mutex": "^0.3.2",
"async-mutex": "^0.4.0",
"fluent-ffmpeg": "^2.1.2",
"which": "^2.0.2"
"which": "^3.0.0"
},
"optionalDependencies": {
"@ffmpeg-installer/ffmpeg": "^1.1.0"
},
"peerDependencies": {
"puppeteer": "^14.0.0 || ^15.0.0"
"puppeteer": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@types/fluent-ffmpeg": "^2.1.20",
"@types/jest": "^28.1.3",
"@types/puppeteer": "^5.4.6",
"@types/jest": "^29.2.5",
"@types/puppeteer": "^7.0.4",
"@types/which": "^2.0.1",
"@types/ws": "^8.5.3",
"jest": "^28.1.2",
"puppeteer": "^15.2.0",
"@types/ws": "^8.5.4",
"jest": "^29.3.1",
"puppeteer": "^19.4.0",
"puppeteer-19.4": "npm:puppeteer@^19.4.0",
"puppeteer-19.3": "npm:puppeteer@^19.3.0",
"puppeteer-19.2": "npm:puppeteer@^19.2.0",
"puppeteer-19.1": "npm:puppeteer@^19.1.0",
"puppeteer-19.0": "npm:puppeteer@^19.0.0",
"puppeteer-18.2": "npm:puppeteer@^18.2.0",
"puppeteer-18.1": "npm:puppeteer@^18.1.0",
"puppeteer-18.0": "npm:puppeteer@^18.0.0",
"puppeteer-17.1": "npm:puppeteer@^17.1.0",
"puppeteer-17.0": "npm:puppeteer@^17.0.0",
"puppeteer-16.2": "npm:puppeteer@^16.2.0",
"puppeteer-16.1": "npm:puppeteer@^16.1.0",
"puppeteer-16.0": "npm:puppeteer@^16.0.0",
"puppeteer-15.5": "npm:puppeteer@^15.5.0",
"puppeteer-15.4": "npm:puppeteer@^15.4.0",
"puppeteer-15.3": "npm:puppeteer@^15.3.0",
"puppeteer-15.2": "npm:puppeteer@^15.2.0",
"puppeteer-15.1": "npm:puppeteer@^15.1.0",
"puppeteer-15.0": "npm:puppeteer@^15.0.0",
"puppeteer-14.4": "npm:puppeteer@^14.4.0",
"puppeteer-14.3": "npm:puppeteer@^14.3.0",
"puppeteer-14.2": "npm:puppeteer@^14.2.0",
"puppeteer-14.1": "npm:puppeteer@^14.1.0",
"puppeteer-14.0": "npm:puppeteer@^14.0.0",
"rimraf": "^3.0.2",
"ts-jest": "^28.0.5",
"ts-standard": "^11.0.0",
"typescript": "^4.7.4"
"ts-jest": "^29.0.3",
"ts-standard": "^12.0.2",
"typescript": "^4.9.4"
},
"files": [
"lib/**/*",
Expand Down
6 changes: 3 additions & 3 deletions src/PuppeteerCapture.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import puppeteer from 'puppeteer'
import type { Page as PuppeteerPage } from 'puppeteer'
import { Writable } from 'stream'
import { PuppeteerCaptureEvents } from './PuppeteerCaptureEvents'
import { PuppeteerCaptureStartOptions } from './PuppeteerCaptureStartOptions'

export interface PuppeteerCapture {
page: puppeteer.Page | null
page: PuppeteerPage | null
isCapturing: boolean
captureTimestamp: number
capturedFrames: number
dropCapturedFrames: boolean
recordedFrames: number
attach: (page: puppeteer.Page) => Promise<void>
attach: (page: PuppeteerPage) => Promise<void>
detach: () => Promise<void>
start: (target: string | Writable, options?: PuppeteerCaptureStartOptions) => Promise<void>
stop: () => Promise<void>
Expand Down
12 changes: 6 additions & 6 deletions src/PuppeteerCaptureBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ffmpeg, { FfmpegCommand, setFfmpegPath } from 'fluent-ffmpeg'
import { mkdir } from 'fs/promises'
import { EventEmitter } from 'node:events'
import { dirname } from 'path'
import puppeteer from 'puppeteer'
import type { Page as PuppeteerPage } from 'puppeteer'
import { PassThrough, Writable } from 'stream'
import which from 'which'
import { PuppeteerCapture } from './PuppeteerCapture'
Expand All @@ -27,7 +27,7 @@ export abstract class PuppeteerCaptureBase extends EventEmitter implements Puppe
protected readonly _frameInterval: number
protected readonly _onPageClose: () => void
protected readonly _startStopMutex: Mutex
protected _page: puppeteer.Page | null
protected _page: PuppeteerPage | null
protected _target: string | Writable | null
protected _frameBeingCaptured: Promise<void> | null
protected _captureTimestamp: number
Expand Down Expand Up @@ -77,7 +77,7 @@ export abstract class PuppeteerCaptureBase extends EventEmitter implements Puppe
this._isCapturing = false
}

public get page (): puppeteer.Page | null {
public get page (): PuppeteerPage | null {
return this._page
}

Expand Down Expand Up @@ -105,7 +105,7 @@ export abstract class PuppeteerCaptureBase extends EventEmitter implements Puppe
return this._recordedFrames
}

public async attach (page: puppeteer.Page): Promise<void> {
public async attach (page: PuppeteerPage): Promise<void> {
if (this._page != null) {
throw new Error('Already attached to a page')
}
Expand All @@ -115,7 +115,7 @@ export abstract class PuppeteerCaptureBase extends EventEmitter implements Puppe
this._page = page
}

protected async _attach (page: puppeteer.Page): Promise<void> {
protected async _attach (page: PuppeteerPage): Promise<void> {
}

public async detach (): Promise<void> {
Expand All @@ -128,7 +128,7 @@ export abstract class PuppeteerCaptureBase extends EventEmitter implements Puppe
this._page = null
}

protected async _detach (page: puppeteer.Page): Promise<void> {
protected async _detach (page: PuppeteerPage): Promise<void> {
}

public async start (target: string | Writable, options?: PuppeteerCaptureStartOptions): Promise<void> {
Expand Down
9 changes: 6 additions & 3 deletions src/PuppeteerCaptureViaHeadlessExperimental.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import puppeteer from 'puppeteer'
import type { Browser as PuppeteerBrowser } from 'puppeteer'
import { PassThrough } from 'stream'
import { launch } from './launch'
import { PuppeteerCaptureViaHeadlessExperimental } from './PuppeteerCaptureViaHeadlessExperimental'

const PUPPETEER_LAUNCH_ARGS = process.platform === 'win32' || process.getuid() !== 0
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
const puppeteer = require(`puppeteer${process.env.PUPPETEER_CAPTURE__PUPPETEER_VERSION ?? ''}`)

const PUPPETEER_LAUNCH_ARGS = (process.platform === 'win32' || process.getuid === undefined || process.getuid() !== 0)
? []
: [
'--no-sandbox' // NOTE: https://github.com/puppeteer/puppeteer/issues/3698
]

let browser: puppeteer.Browser
let browser: PuppeteerBrowser
afterEach(async () => {
if (browser != null) {
await browser.close()
Expand Down
31 changes: 16 additions & 15 deletions src/PuppeteerCaptureViaHeadlessExperimental.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import puppeteer from 'puppeteer'
import type {
Browser as PuppeteerBrowser,
CDPSession as PuppeteerCDPSession,
Page as PuppeteerPage
} from 'puppeteer'
import { Protocol } from 'devtools-protocol'
import { MissingHeadlessExperimentalRequiredArgs } from './MissingHeadlessExperimentalRequiredArgs'
import { PuppeteerCaptureBase } from './PuppeteerCaptureBase'
Expand Down Expand Up @@ -39,7 +43,7 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
protected readonly _ejector: string
protected readonly _requestFrameCapture: () => void
protected readonly _onSessionDisconnected: () => void
protected _session: puppeteer.CDPSession | null
protected _session: PuppeteerCDPSession | null
protected _onNewDocumentScript: Protocol.Page.ScriptIdentifier | null

public constructor (options?: PuppeteerCaptureOptions) {
Expand All @@ -59,7 +63,7 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
}
}

protected getPageClient (page: puppeteer.Page): puppeteer.CDPSession {
protected getPageClient (page: PuppeteerPage): PuppeteerCDPSession {
// Before puppeteer 14.4.0, the internal method was client()
if ('client' in page) {
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
Expand All @@ -72,7 +76,7 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
return page._client()
}

protected override async _attach (page: puppeteer.Page): Promise<void> {
protected override async _attach (page: PuppeteerPage): Promise<void> {
PuppeteerCaptureViaHeadlessExperimental.validateBrowserArgs(page.browser())

const session = await page.target().createCDPSession()
Expand All @@ -88,7 +92,7 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
this._onNewDocumentScript = onNewDocumentScript
}

protected override async _detach (page: puppeteer.Page): Promise<void> {
protected override async _detach (page: PuppeteerPage): Promise<void> {
if (this._onNewDocumentScript != null) {
// NOTE: For details, see send('Page.addScriptToEvaluateOnNewDocument') code
await this.getPageClient(page).send('Page.removeScriptToEvaluateOnNewDocument', {
Expand Down Expand Up @@ -121,12 +125,11 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
this.doRequestFrameCapture(this._page, this._session)
}

protected doRequestFrameCapture (page: puppeteer.Page, session: puppeteer.CDPSession): void {
protected doRequestFrameCapture (page: PuppeteerPage, session: PuppeteerCDPSession): void {
const captureTimestamp = this._captureTimestamp
const frameInterval = this._frameInterval
this._frameBeingCaptured = Promise.all(page.frames().map(async (frame) => {
const executionContext = await frame.executionContext()
await executionContext.evaluate(`${this._injected}.process(${captureTimestamp})`)
await frame.evaluate(`${this._injected}.process(${captureTimestamp})`)
})).then(async () => {
return await session.send('HeadlessExperimental.beginFrame', {
frameTimeTicks: captureTimestamp,
Expand Down Expand Up @@ -173,9 +176,8 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
}

for (const frame of page.frames()) {
const executionContext = await frame.executionContext()
await executionContext.evaluate(this._injector)
await executionContext.evaluate(`${this._injected}.activate()`)
await frame.evaluate(this._injector)
await frame.evaluate(`${this._injected}.activate()`)
}

await session.send('HeadlessExperimental.enable')
Expand All @@ -190,9 +192,8 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
}

for (const frame of page.frames()) {
const executionContext = await frame.executionContext()
await executionContext.evaluate(`${this._injected}.deactivate()`)
await executionContext.evaluate(this._ejector)
await frame.evaluate(`${this._injected}.deactivate()`)
await frame.evaluate(this._ejector)
}

await session.send('HeadlessExperimental.disable')
Expand All @@ -210,7 +211,7 @@ export class PuppeteerCaptureViaHeadlessExperimental extends PuppeteerCaptureBas
.catch(() => { })
}

protected static validateBrowserArgs (browser: puppeteer.Browser): void {
protected static validateBrowserArgs (browser: PuppeteerBrowser): void {
const spawnargs = browser.process()?.spawnargs
if (spawnargs == null || !PuppeteerCaptureViaHeadlessExperimental.REQUIRED_ARGS.every(arg => spawnargs.includes(arg))) {
throw new MissingHeadlessExperimentalRequiredArgs()
Expand Down
7 changes: 5 additions & 2 deletions src/capture.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import puppeteer from 'puppeteer'
import type { Page as PuppeteerPage } from 'puppeteer'
import { PuppeteerCapture } from './PuppeteerCapture'
import { PuppeteerCaptureOptions } from './PuppeteerCaptureOptions'
import { PuppeteerCaptureViaHeadlessExperimental } from './PuppeteerCaptureViaHeadlessExperimental'

export async function capture (page: puppeteer.Page, options?: PuppeteerCaptureOptions & { attach?: boolean }): Promise<PuppeteerCapture> {
export async function capture (
page: PuppeteerPage,
options?: PuppeteerCaptureOptions & { attach?: boolean }
): Promise<PuppeteerCapture> {
const puppeteerCapture = new PuppeteerCaptureViaHeadlessExperimental(options)
if (options?.attach !== false) {
await puppeteerCapture.attach(page)
Expand Down
20 changes: 15 additions & 5 deletions src/launch.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import puppeteer from 'puppeteer'
import type {
Browser as PuppeteerBrowser,
BrowserConnectOptions as PuppeteerBrowserConnectOptions,
BrowserLaunchArgumentOptions as PuppeteerBrowserLaunchArgumentOptions,
LaunchOptions as PuppeteerLaunchOptions,
Product as PuppeteerProduct,
PuppeteerNode
} from 'puppeteer'
import { PuppeteerCaptureViaHeadlessExperimental } from './PuppeteerCaptureViaHeadlessExperimental'

/* eslint-disable-next-line @typescript-eslint/no-var-requires */
const puppeteer = require(`puppeteer${process.env.PUPPETEER_CAPTURE__PUPPETEER_VERSION ?? ''}`)

export async function launch (
options?: puppeteer.LaunchOptions & puppeteer.BrowserLaunchArgumentOptions & puppeteer.BrowserConnectOptions & {
product?: puppeteer.Product
options?: PuppeteerLaunchOptions & PuppeteerBrowserLaunchArgumentOptions & PuppeteerBrowserConnectOptions & {
product?: PuppeteerProduct
extraPrefsFirefox?: Record<string, unknown>
}
): Promise<puppeteer.Browser> {
): Promise<PuppeteerBrowser> {
options = {
...(options != null ? options : {}),
args: [
Expand All @@ -15,5 +25,5 @@ export async function launch (
]
}

return await puppeteer.launch(options)
return await (puppeteer as PuppeteerNode).launch(options)
}

0 comments on commit 554e824

Please sign in to comment.