chore: migrate src/Puppeteer to TypeScript (#5789)

* chore: migrate src/Puppeteer to TypeScript.

* fix api js
This commit is contained in:
Jack Franklin 2020-05-05 09:36:44 +01:00 committed by GitHub
parent 209e25c0e1
commit 890c215142
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 154 additions and 234 deletions

View File

@ -25,7 +25,7 @@ for (const className in api) {
// Expose alias for deprecated method.
Page.prototype.emulateMedia = Page.prototype.emulateMediaType;
const Puppeteer = require('./lib/Puppeteer');
const {Puppeteer} = require('./lib/Puppeteer');
const packageJson = require('./package.json');
let preferredRevision = packageJson.puppeteer.chromium_revision;
const isPuppeteerCore = packageJson.name === 'puppeteer-core';

View File

@ -20,18 +20,19 @@ import * as EventEmitter from 'events';
import {TaskQueue} from './TaskQueue';
import {Events} from './Events';
import {Connection} from './Connection';
import {ChildProcess} from 'child_process';
type BrowserCloseCallback = () => Promise<void> | void;
export class Browser extends EventEmitter {
static async create(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: Puppeteer.ChildProcess, closeCallback?: BrowserCloseCallback): Promise<Browser> {
static async create(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: ChildProcess, closeCallback?: BrowserCloseCallback): Promise<Browser> {
const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
await connection.send('Target.setDiscoverTargets', {discover: true});
return browser;
}
_ignoreHTTPSErrors: boolean;
_defaultViewport?: Puppeteer.Viewport;
_process?: Puppeteer.ChildProcess;
_process?: ChildProcess;
_screenshotTaskQueue = new TaskQueue();
_connection: Connection;
_closeCallback: BrowserCloseCallback;
@ -40,7 +41,7 @@ export class Browser extends EventEmitter {
// TODO: once Target is in TypeScript we can type this properly.
_targets: Map<string, Target>;
constructor(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: Puppeteer.ChildProcess, closeCallback?: BrowserCloseCallback) {
constructor(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: ChildProcess, closeCallback?: BrowserCloseCallback) {
super();
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._defaultViewport = defaultViewport;
@ -61,7 +62,7 @@ export class Browser extends EventEmitter {
this._connection.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
}
process(): Puppeteer.ChildProcess | null {
process(): ChildProcess | null {
return this._process;
}

View File

@ -99,10 +99,6 @@ function existsAsync(filePath: string): Promise<boolean> {
});
}
/**
* @typedef {Object} BrowserFetcher.Options
*/
export interface BrowserFetcherOptions {
platform?: Platform;
product?: string;

View File

@ -882,7 +882,7 @@ const devices: Device[] = [
}
];
type DevicesMap = {
export type DevicesMap = {
[name: string]: Device;
};
@ -891,5 +891,4 @@ const devicesMap: DevicesMap = {};
for (const device of devices)
devicesMap[device.name] = device;
export = devicesMap;
export {devicesMap};

View File

@ -23,3 +23,9 @@ class CustomError extends Error {
}
export class TimeoutError extends CustomError {}
export type PuppeteerErrors = Record<string, typeof CustomError>;
export const puppeteerErrors: PuppeteerErrors = {
TimeoutError,
};

View File

@ -375,9 +375,6 @@ class ChromeLauncher implements ProductLauncher {
}
/**
* @implements {!Puppeteer.ProductLauncher}
*/
class FirefoxLauncher implements ProductLauncher {
_projectRoot: string;
_preferredRevision: string;
@ -738,7 +735,7 @@ class FirefoxLauncher implements ProductLauncher {
}
function waitForWSEndpoint(browserProcess: Puppeteer.ChildProcess, timeout: number, preferredRevision: string): Promise<string> {
function waitForWSEndpoint(browserProcess: childProcess.ChildProcess, timeout: number, preferredRevision: string): Promise<string> {
return new Promise((resolve, reject) => {
const rl = readline.createInterface({input: browserProcess.stderr});
let stderr = '';

View File

@ -1,174 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {default: Launcher} = require('./Launcher');
const {BrowserFetcher} = require('./BrowserFetcher');
const Errors = require('./Errors');
const DeviceDescriptors = require('./DeviceDescriptors');
// Import used as typedef
// eslint-disable-next-line no-unused-vars
const {Browser} = require('./Browser');
const QueryHandler = require('./QueryHandler');
module.exports = class {
/**
* @param {string} projectRoot
* @param {string} preferredRevision
* @param {boolean} isPuppeteerCore
* @param {string} productName
*/
constructor(projectRoot, preferredRevision, isPuppeteerCore, productName) {
this._projectRoot = projectRoot;
this._preferredRevision = preferredRevision;
this._isPuppeteerCore = isPuppeteerCore;
// track changes to Launcher configuration via options or environment variables
this.__productName = productName;
}
/**
* @param {!(Puppeteer.LaunchOptions & Puppeteer.ChromeArgOptions & Puppeteer.BrowserOptions & {product?: string, extraPrefsFirefox?: !object})=} options
* @return {!Promise<!Browser>}
*/
launch(options = {}) {
if (options.product)
this._productName = options.product;
return this._launcher.launch(options);
}
/**
* @param {!(Puppeteer.BrowserOptions & {browserWSEndpoint?: string, browserURL?: string, transport?: !Puppeteer.ConnectionTransport}) & {product?: string}=} options
* @return {!Promise<!Browser>}
*/
connect(options) {
if (options.product)
this._productName = options.product;
return this._launcher.connect(options);
}
/**
* @param {string} name
*/
set _productName(name) {
if (this.__productName !== name)
this._changedProduct = true;
this.__productName = name;
}
/**
* @return {string}
*/
get _productName() {
return this.__productName;
}
/**
* @return {string}
*/
executablePath() {
return this._launcher.executablePath();
}
/**
* @return {!Puppeteer.ProductLauncher}
*/
get _launcher() {
if (!this._lazyLauncher || this._lazyLauncher.product !== this._productName || this._changedProduct) {
// @ts-ignore
const packageJson = require('../package.json');
switch (this._productName) {
case 'firefox':
this._preferredRevision = packageJson.puppeteer.firefox_revision;
break;
case 'chrome':
default:
this._preferredRevision = packageJson.puppeteer.chromium_revision;
}
this._changedProduct = false;
this._lazyLauncher = Launcher(this._projectRoot, this._preferredRevision, this._isPuppeteerCore, this._productName);
}
return this._lazyLauncher;
}
/**
* @return {string}
*/
get product() {
return this._launcher.product;
}
/**
* @return {Object}
*/
get devices() {
return DeviceDescriptors;
}
/**
* @return {Object}
*/
get errors() {
return Errors;
}
/**
* @param {!Puppeteer.ChromeArgOptions=} options
* @return {!Array<string>}
*/
defaultArgs(options) {
return this._launcher.defaultArgs(options);
}
/** TODO(jacktfranklin@): Once this file is TS we can type this
* using the BrowserFectcherOptions interface.
*/
/**
* @typedef {Object} BrowserFetcherOptions
* @property {('linux'|'mac'|'win32'|'win64')=} platform
* @property {('chrome'|'firefox')=} product
* @property {string=} path
* @property {string=} host
*/
/**
* @param {!BrowserFetcherOptions} options
* @return {!BrowserFetcher}
*/
createBrowserFetcher(options) {
return new BrowserFetcher(this._projectRoot, options);
}
/**
* @param {string} name
* @param {!Function} queryHandler
*/
__experimental_registerCustomQueryHandler(name, queryHandler) {
QueryHandler.registerCustomQueryHandler(name, queryHandler);
}
/**
* @param {string} name
*/
__experimental_unregisterCustomQueryHandler(name) {
QueryHandler.unregisterCustomQueryHandler(name);
}
__experimental_customQueryHandlers() {
return QueryHandler.customQueryHandlers();
}
__experimental_clearQueryHandlers() {
QueryHandler.clearQueryHandlers();
}
};

131
src/Puppeteer.ts Normal file
View File

@ -0,0 +1,131 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Launcher from './Launcher';
import type {LaunchOptions, ChromeArgOptions, BrowserOptions, ProductLauncher} from './Launcher';
import {BrowserFetcher, BrowserFetcherOptions} from './BrowserFetcher';
import {puppeteerErrors, PuppeteerErrors} from './Errors';
import {devicesMap} from './DeviceDescriptors';
import type {DevicesMap} from './/DeviceDescriptors';
import {Browser} from './Browser';
import * as QueryHandler from './QueryHandler';
export class Puppeteer {
_projectRoot: string;
_preferredRevision: string;
_isPuppeteerCore: boolean;
_changedProduct = false;
__productName: string;
_lazyLauncher: ProductLauncher;
constructor(projectRoot: string, preferredRevision: string, isPuppeteerCore: boolean, productName: string) {
this._projectRoot = projectRoot;
this._preferredRevision = preferredRevision;
this._isPuppeteerCore = isPuppeteerCore;
// track changes to Launcher configuration via options or environment variables
this.__productName = productName;
}
launch(options: LaunchOptions & ChromeArgOptions & BrowserOptions & {product?: string; extraPrefsFirefox?: {}} = {}): Promise<Browser> {
if (options.product)
this._productName = options.product;
return this._launcher.launch(options);
}
connect(options: BrowserOptions & {
browserWSEndpoint?: string;
browserURL?: string;
transport?: Puppeteer.ConnectionTransport;
product?: string;
}): Promise<Browser> {
if (options.product)
this._productName = options.product;
return this._launcher.connect(options);
}
set _productName(name: string) {
if (this.__productName !== name)
this._changedProduct = true;
this.__productName = name;
}
get _productName(): string {
return this.__productName;
}
executablePath(): string {
return this._launcher.executablePath();
}
get _launcher(): ProductLauncher {
if (!this._lazyLauncher || this._lazyLauncher.product !== this._productName || this._changedProduct) {
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
switch (this._productName) {
case 'firefox':
this._preferredRevision = packageJson.puppeteer.firefox_revision;
break;
case 'chrome':
default:
this._preferredRevision = packageJson.puppeteer.chromium_revision;
}
this._changedProduct = false;
this._lazyLauncher = Launcher(this._projectRoot, this._preferredRevision, this._isPuppeteerCore, this._productName);
}
return this._lazyLauncher;
}
get product(): string {
return this._launcher.product;
}
get devices(): DevicesMap {
return devicesMap;
}
get errors(): PuppeteerErrors {
return puppeteerErrors;
}
defaultArgs(options: ChromeArgOptions): string[] {
return this._launcher.defaultArgs(options);
}
createBrowserFetcher(options: BrowserFetcherOptions): BrowserFetcher {
return new BrowserFetcher(this._projectRoot, options);
}
// eslint-disable-next-line @typescript-eslint/camelcase
__experimental_registerCustomQueryHandler(name: string, queryHandler: QueryHandler.QueryHandler): void {
QueryHandler.registerCustomQueryHandler(name, queryHandler);
}
// eslint-disable-next-line @typescript-eslint/camelcase
__experimental_unregisterCustomQueryHandler(name: string): void {
QueryHandler.unregisterCustomQueryHandler(name);
}
// eslint-disable-next-line @typescript-eslint/camelcase
__experimental_customQueryHandlers(): Map<string, QueryHandler.QueryHandler> {
return QueryHandler.customQueryHandlers();
}
// eslint-disable-next-line @typescript-eslint/camelcase
__experimental_clearQueryHandlers(): void {
QueryHandler.clearQueryHandlers();
}
}

View File

@ -31,7 +31,7 @@ module.exports = {
Keyboard: require('./Input').Keyboard,
Mouse: require('./Input').Mouse,
Page: require('./Page').Page,
Puppeteer: require('./Puppeteer'),
Puppeteer: require('./Puppeteer').Puppeteer,
Request: require('./NetworkManager').Request,
Response: require('./NetworkManager').Response,
SecurityDetails: require('./NetworkManager').SecurityDetails,

42
src/externs.d.ts vendored
View File

@ -1,10 +1,9 @@
import {Page as RealPage} from './Page.js';
import * as child_process from 'child_process';
declare global {
module Puppeteer {
export class Page extends RealPage { }
/* TODO(jacktfranklin@): once DOMWorld, Page, and FrameManager are in TS
* we can remove this and instead use the type defined in LifeCycleWatcher
*/
@ -17,45 +16,6 @@ declare global {
onclose?: () => void,
}
/* TODO(jacktfranklin@): these are duplicated from Launcher.ts.
* Once src/Puppeteer is migrated to TypeScript it can use those defs
* and we can delete these.
*/
export interface ProductLauncher {
launch(object)
connect(object)
executablePath: () => string,
defaultArgs(object)
product:string,
}
export interface ChromeArgOptions {
headless?: boolean;
args?: string[];
userDataDir?: string;
devtools?: boolean;
}
export interface LaunchOptions {
executablePath?: string;
ignoreDefaultArgs?: boolean | string[];
handleSIGINT?: boolean;
handleSIGTERM?: boolean;
handleSIGHUP?: boolean;
timeout?: number;
dumpio?: boolean;
env?: Record<string, string | undefined>;
pipe?: boolean;
}
export interface BrowserOptions {
ignoreHTTPSErrors?: boolean;
defaultViewport?: Puppeteer.Viewport;
slowMo?: number;
}
export interface ChildProcess extends child_process.ChildProcess { }
export type Viewport = {
width: number;
height: number;

View File

@ -496,8 +496,8 @@ describe('Launcher specs', function() {
});
it('should require top-level DeviceDescriptors', async() => {
const {puppeteer, puppeteerPath} = getTestState();
const Devices = require(path.join(puppeteerPath, '/DeviceDescriptors'));
expect(Devices['iPhone 6']).toBe(puppeteer.devices['iPhone 6']);
const {devicesMap} = require(path.join(puppeteerPath, '/DeviceDescriptors'));
expect(devicesMap['iPhone 6']).toBe(puppeteer.devices['iPhone 6']);
});
});

View File

@ -355,6 +355,10 @@ function compareDocumentations(actual, expected) {
actualName: 'Array<string>',
expectedName: 'Array<PermissionType>'
}],
['Method Puppeteer.createBrowserFetcher() options', {
actualName: 'Object',
expectedName: 'BrowserFetcherOptions'
}],
]);
const expectedForSource = expectedNamingMismatches.get(source);