mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
fix(browsers): various fixes and improvements (#9966)
This commit is contained in:
parent
fe934ad092
commit
f1211cbec0
@ -20,7 +20,7 @@
|
|||||||
},
|
},
|
||||||
"wireit": {
|
"wireit": {
|
||||||
"build": {
|
"build": {
|
||||||
"command": "tsc -b && chmod a+x lib/cjs/main-cli.js lib/esm/main-cli.js",
|
"command": "tsc -b && chmod a+x lib/cjs/main-cli.js lib/esm/main-cli.js && echo '{\"type\":\"module\"}' > lib/esm/package.json",
|
||||||
"files": [
|
"files": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"tsconfig.json"
|
"tsconfig.json"
|
||||||
|
@ -18,8 +18,9 @@ import {stdin as input, stdout as output} from 'process';
|
|||||||
import * as readline from 'readline';
|
import * as readline from 'readline';
|
||||||
|
|
||||||
import ProgressBar from 'progress';
|
import ProgressBar from 'progress';
|
||||||
import yargs from 'yargs';
|
import type * as Yargs from 'yargs';
|
||||||
import {hideBin} from 'yargs/helpers';
|
import {hideBin} from 'yargs/helpers';
|
||||||
|
import yargs from 'yargs/yargs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
resolveBuildId,
|
resolveBuildId,
|
||||||
@ -69,7 +70,7 @@ export class CLI {
|
|||||||
this.#rl = rl;
|
this.#rl = rl;
|
||||||
}
|
}
|
||||||
|
|
||||||
#defineBrowserParameter(yargs: yargs.Argv<unknown>): void {
|
#defineBrowserParameter(yargs: Yargs.Argv<unknown>): void {
|
||||||
yargs.positional('browser', {
|
yargs.positional('browser', {
|
||||||
description:
|
description:
|
||||||
'Which browser to install <browser>[@<buildId|latest>]. `latest` will try to find the latest available build. `buildId` is a browser-specific identifier such as a version or a revision.',
|
'Which browser to install <browser>[@<buildId|latest>]. `latest` will try to find the latest available build. `buildId` is a browser-specific identifier such as a version or a revision.',
|
||||||
@ -83,7 +84,7 @@ export class CLI {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#definePlatformParameter(yargs: yargs.Argv<unknown>): void {
|
#definePlatformParameter(yargs: Yargs.Argv<unknown>): void {
|
||||||
yargs.option('platform', {
|
yargs.option('platform', {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
desc: 'Platform that the binary needs to be compatible with.',
|
desc: 'Platform that the binary needs to be compatible with.',
|
||||||
@ -92,7 +93,7 @@ export class CLI {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#definePathParameter(yargs: yargs.Argv<unknown>, required = false): void {
|
#definePathParameter(yargs: Yargs.Argv<unknown>, required = false): void {
|
||||||
yargs.option('path', {
|
yargs.option('path', {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
desc: 'Path to the root folder for the browser downloads and installation. The installation folder structure is compatible with the cache structure used by Puppeteer.',
|
desc: 'Path to the root folder for the browser downloads and installation. The installation folder structure is compatible with the cache structure used by Puppeteer.',
|
||||||
@ -105,7 +106,8 @@ export class CLI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async run(argv: string[]): Promise<void> {
|
async run(argv: string[]): Promise<void> {
|
||||||
await yargs(hideBin(argv))
|
const yargsInstance = yargs(hideBin(argv));
|
||||||
|
await yargsInstance
|
||||||
.scriptName('@puppeteer/browsers')
|
.scriptName('@puppeteer/browsers')
|
||||||
.command(
|
.command(
|
||||||
'install <browser>',
|
'install <browser>',
|
||||||
@ -254,7 +256,7 @@ export class CLI {
|
|||||||
)
|
)
|
||||||
.demandCommand(1)
|
.demandCommand(1)
|
||||||
.help()
|
.help()
|
||||||
.wrap(Math.min(120, yargs.terminalWidth()))
|
.wrap(Math.min(120, yargsInstance.terminalWidth()))
|
||||||
.parse();
|
.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +122,12 @@ type LaunchOptions = {
|
|||||||
pipe?: boolean;
|
pipe?: boolean;
|
||||||
dumpio?: boolean;
|
dumpio?: boolean;
|
||||||
args?: string[];
|
args?: string[];
|
||||||
env?: Record<string, string>;
|
env?: Record<string, string | undefined>;
|
||||||
handleSIGINT?: boolean;
|
handleSIGINT?: boolean;
|
||||||
handleSIGTERM?: boolean;
|
handleSIGTERM?: boolean;
|
||||||
handleSIGHUP?: boolean;
|
handleSIGHUP?: boolean;
|
||||||
detached?: boolean;
|
detached?: boolean;
|
||||||
|
onExit?: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function launch(opts: LaunchOptions): Process {
|
export function launch(opts: LaunchOptions): Process {
|
||||||
@ -143,6 +144,11 @@ class Process {
|
|||||||
#args: string[];
|
#args: string[];
|
||||||
#browserProcess: childProcess.ChildProcess;
|
#browserProcess: childProcess.ChildProcess;
|
||||||
#exited = false;
|
#exited = false;
|
||||||
|
// The browser process can be closed externally or from the driver process. We
|
||||||
|
// need to invoke the hooks only once though but we don't know how many times
|
||||||
|
// we will be invoked.
|
||||||
|
#hooksRan = false;
|
||||||
|
#onExitHook = async () => {};
|
||||||
#browserProcessExiting: Promise<void>;
|
#browserProcessExiting: Promise<void>;
|
||||||
|
|
||||||
constructor(opts: LaunchOptions) {
|
constructor(opts: LaunchOptions) {
|
||||||
@ -190,15 +196,36 @@ class Process {
|
|||||||
if (opts.handleSIGHUP) {
|
if (opts.handleSIGHUP) {
|
||||||
process.on('SIGHUP', this.#onDriverProcessSignal);
|
process.on('SIGHUP', this.#onDriverProcessSignal);
|
||||||
}
|
}
|
||||||
this.#browserProcessExiting = new Promise(resolve => {
|
if (opts.onExit) {
|
||||||
this.#browserProcess.once('exit', () => {
|
this.#onExitHook = opts.onExit;
|
||||||
this.#exited = true;
|
}
|
||||||
|
this.#browserProcessExiting = new Promise((resolve, reject) => {
|
||||||
|
this.#browserProcess.once('exit', async () => {
|
||||||
this.#clearListeners();
|
this.#clearListeners();
|
||||||
|
this.#exited = true;
|
||||||
|
try {
|
||||||
|
await this.#runHooks();
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async #runHooks() {
|
||||||
|
if (this.#hooksRan) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#hooksRan = true;
|
||||||
|
await this.#onExitHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
get nodeProcess(): childProcess.ChildProcess {
|
||||||
|
return this.#browserProcess;
|
||||||
|
}
|
||||||
|
|
||||||
#configureStdio(opts: {
|
#configureStdio(opts: {
|
||||||
pipe: boolean;
|
pipe: boolean;
|
||||||
dumpio: boolean;
|
dumpio: boolean;
|
||||||
@ -236,19 +263,24 @@ class Process {
|
|||||||
process.exit(130);
|
process.exit(130);
|
||||||
case 'SIGTERM':
|
case 'SIGTERM':
|
||||||
case 'SIGHUP':
|
case 'SIGHUP':
|
||||||
this.kill();
|
this.close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
|
await this.#runHooks();
|
||||||
if (this.#exited) {
|
if (this.#exited) {
|
||||||
return Promise.resolve();
|
return this.#browserProcessExiting;
|
||||||
}
|
}
|
||||||
this.kill();
|
this.kill();
|
||||||
return this.#browserProcessExiting;
|
return this.#browserProcessExiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasClosed(): Promise<void> {
|
||||||
|
return this.#browserProcessExiting;
|
||||||
|
}
|
||||||
|
|
||||||
kill(): void {
|
kill(): void {
|
||||||
// If the process failed to launch (for example if the browser executable path
|
// If the process failed to launch (for example if the browser executable path
|
||||||
// is invalid), then the process does not get a pid assigned. A call to
|
// is invalid), then the process does not get a pid assigned. A call to
|
||||||
@ -329,6 +361,9 @@ class Process {
|
|||||||
error ? ' ' + error.message : ''
|
error ? ' ' + error.message : ''
|
||||||
}`,
|
}`,
|
||||||
stderr,
|
stderr,
|
||||||
|
'',
|
||||||
|
'TROUBLESHOOTING: https://pptr.dev/troubleshooting',
|
||||||
|
'',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -337,7 +372,7 @@ class Process {
|
|||||||
function onTimeout(): void {
|
function onTimeout(): void {
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new TimeoutError(
|
||||||
`Timed out after ${timeout} ms while waiting for the WS endpoint URL to appear in stdout!`
|
`Timed out after ${timeout} ms while waiting for the WS endpoint URL to appear in stdout!`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -403,3 +438,14 @@ export function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException {
|
|||||||
('errno' in obj || 'code' in obj || 'path' in obj || 'syscall' in obj)
|
('errno' in obj || 'code' in obj || 'path' in obj || 'syscall' in obj)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TimeoutError extends Error {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
constructor(message?: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = this.constructor.name;
|
||||||
|
Error.captureStackTrace(this, this.constructor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ export {
|
|||||||
launch,
|
launch,
|
||||||
computeExecutablePath,
|
computeExecutablePath,
|
||||||
computeSystemExecutablePath,
|
computeSystemExecutablePath,
|
||||||
|
TimeoutError,
|
||||||
CDP_WEBSOCKET_ENDPOINT_REGEX,
|
CDP_WEBSOCKET_ENDPOINT_REGEX,
|
||||||
WEBDRIVER_BIDI_WEBSOCKET_ENDPOINT_REGEX,
|
WEBDRIVER_BIDI_WEBSOCKET_ENDPOINT_REGEX,
|
||||||
} from './launcher.js';
|
} from './launcher.js';
|
||||||
@ -28,6 +29,7 @@ export {
|
|||||||
Browser,
|
Browser,
|
||||||
BrowserPlatform,
|
BrowserPlatform,
|
||||||
ChromeReleaseChannel,
|
ChromeReleaseChannel,
|
||||||
|
createProfile,
|
||||||
} from './browser-data/browser-data.js';
|
} from './browser-data/browser-data.js';
|
||||||
export {CLI, makeProgressCallback} from './CLI.js';
|
export {CLI, makeProgressCallback} from './CLI.js';
|
||||||
export {Cache} from './Cache.js';
|
export {Cache} from './Cache.js';
|
||||||
|
Loading…
Reference in New Issue
Block a user