chore: migrate src/Connection to TypeScript (#5694)

* chore: migrate `src/Connection` to TypeScript

This commit migrates `src/Connection` to TypeScript. It also changes its
exports to be ESM because TypeScript's support for exporting values to
use as types via CommonJS is poor (by design) and so rather than battle
that it made more sense to migrate the file to ESM.

The good news is that TypeScript is still outputting to `lib/` as
CommonJS, so the fact that we author in ESM is actually not a breaking
change at all.

So going forwards we will:

* migrate TS files to use ESM for importing and exporting
* continue to output to `lib/` as CommonJS
* continue to use CommonJS requires when in a `src/*.js` file

I'd also like to split `Connection.ts` into two; I think the
`CDPSession` class belongs in its own file, but I will do that in
another PR to avoid this one becoming bigger than it already is.

I also turned off `@typescript-eslint/no-use-before-define` as I don't
think it was adding value and Puppeteer's codebase seems to have a style
of declaring helper functions at the bottom which is fine by me.

Finally, I updated the DocLint tool so it knows of expected method
mismatches. It was either that or come up with a smart way to support
TypeScript generics in DocLint and given we don't want to use DocLint
that much longer that didn't feel worth it.

* Fix params being required
This commit is contained in:
Jack Franklin 2020-04-21 09:20:25 +01:00 committed by GitHub
parent 376d234ed1
commit a614bc45aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 188 additions and 108 deletions

View File

@ -112,7 +112,8 @@ module.exports = {
"@typescript-eslint/no-unused-vars": 2, "@typescript-eslint/no-unused-vars": 2,
"semi": 0, "semi": 0,
"@typescript-eslint/semi": 2, "@typescript-eslint/semi": 2,
"@typescript-eslint/no-empty-function": 0 "@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-use-before-define": 0
} }
} }
] ]

View File

@ -14,6 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
/** /**
* @typedef {Object} SerializedAXNode * @typedef {Object} SerializedAXNode
* @property {string} role * @property {string} role
@ -53,7 +57,7 @@
class Accessibility { class Accessibility {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._client = client; this._client = client;

View File

@ -19,10 +19,13 @@ const {Target} = require('./Target');
const EventEmitter = require('events'); const EventEmitter = require('events');
const {TaskQueue} = require('./TaskQueue'); const {TaskQueue} = require('./TaskQueue');
const {Events} = require('./Events'); const {Events} = require('./Events');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {Connection} = require('./Connection');
class Browser extends EventEmitter { class Browser extends EventEmitter {
/** /**
* @param {!Puppeteer.Connection} connection * @param {!Connection} connection
* @param {!Array<string>} contextIds * @param {!Array<string>} contextIds
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.Viewport} defaultViewport
@ -36,7 +39,7 @@ class Browser extends EventEmitter {
} }
/** /**
* @param {!Puppeteer.Connection} connection * @param {!Connection} connection
* @param {!Array<string>} contextIds * @param {!Array<string>} contextIds
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.Viewport} defaultViewport
@ -277,7 +280,7 @@ class Browser extends EventEmitter {
class BrowserContext extends EventEmitter { class BrowserContext extends EventEmitter {
/** /**
* @param {!Puppeteer.Connection} connection * @param {!Connection} connection
* @param {!Browser} browser * @param {!Browser} browser
* @param {?string} contextId * @param {?string} contextId
*/ */

View File

@ -13,38 +13,51 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const {assert} = require('./helper'); import helper = require('./helper');
const {Events} = require('./Events'); const {assert} = helper;
const debugProtocol = require('debug')('puppeteer:protocol');
const EventEmitter = require('events'); import EventsModule = require('./Events');
const {Events} = EventsModule;
import debug = require('debug');
const debugProtocol = debug('puppeteer:protocol');
import EventEmitter = require('events');
interface ConnectionCallback {
resolve: Function;
reject: Function;
error: Error;
method: string;
}
export class Connection extends EventEmitter {
_url: string;
_transport: Puppeteer.ConnectionTransport;
_delay: number;
_lastId = 0;
_sessions: Map<string, CDPSession> = new Map();
_closed = false;
_callbacks: Map<number, ConnectionCallback> = new Map();
class Connection extends EventEmitter {
/** /**
* @param {string} url * @param {string} url
* @param {!Puppeteer.ConnectionTransport} transport * @param {!Puppeteer.ConnectionTransport} transport
* @param {number=} delay * @param {number=} delay
*/ */
constructor(url, transport, delay = 0) { constructor(url: string, transport: Puppeteer.ConnectionTransport, delay = 0) {
super(); super();
this._url = url; this._url = url;
this._lastId = 0;
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/
this._callbacks = new Map();
this._delay = delay; this._delay = delay;
this._transport = transport; this._transport = transport;
this._transport.onmessage = this._onMessage.bind(this); this._transport.onmessage = this._onMessage.bind(this);
this._transport.onclose = this._onClose.bind(this); this._transport.onclose = this._onClose.bind(this);
/** @type {!Map<string, !CDPSession>}*/
this._sessions = new Map();
this._closed = false;
} }
/** static fromSession(session: CDPSession): Connection {
* @param {!CDPSession} session
* @return {!Connection}
*/
static fromSession(session) {
return session._connection; return session._connection;
} }
@ -52,34 +65,22 @@ class Connection extends EventEmitter {
* @param {string} sessionId * @param {string} sessionId
* @return {?CDPSession} * @return {?CDPSession}
*/ */
session(sessionId) { session(sessionId: string): CDPSession | null {
return this._sessions.get(sessionId) || null; return this._sessions.get(sessionId) || null;
} }
/** url(): string {
* @return {string}
*/
url() {
return this._url; return this._url;
} }
/** send<ReturnType extends {}>(method: string, params = {}): Promise<ReturnType> {
* @param {string} method
* @param {!Object=} params
* @return {!Promise<?Object>}
*/
send(method, params = {}) {
const id = this._rawSend({method, params}); const id = this._rawSend({method, params});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this._callbacks.set(id, {resolve, reject, error: new Error(), method}); this._callbacks.set(id, {resolve, reject, error: new Error(), method});
}); });
} }
/** _rawSend(message: {}): number {
* @param {*} message
* @return {number}
*/
_rawSend(message) {
const id = ++this._lastId; const id = ++this._lastId;
message = JSON.stringify(Object.assign({}, message, {id})); message = JSON.stringify(Object.assign({}, message, {id}));
debugProtocol('SEND ► ' + message); debugProtocol('SEND ► ' + message);
@ -87,10 +88,7 @@ class Connection extends EventEmitter {
return id; return id;
} }
/** async _onMessage(message: string): Promise<void> {
* @param {string} message
*/
async _onMessage(message) {
if (this._delay) if (this._delay)
await new Promise(f => setTimeout(f, this._delay)); await new Promise(f => setTimeout(f, this._delay));
debugProtocol('◀ RECV ' + message); debugProtocol('◀ RECV ' + message);
@ -125,7 +123,7 @@ class Connection extends EventEmitter {
} }
} }
_onClose() { _onClose(): void {
if (this._closed) if (this._closed)
return; return;
this._closed = true; this._closed = true;
@ -140,7 +138,7 @@ class Connection extends EventEmitter {
this.emit(Events.Connection.Disconnected); this.emit(Events.Connection.Disconnected);
} }
dispose() { dispose(): void {
this._onClose(); this._onClose();
this._transport.close(); this._transport.close();
} }
@ -149,45 +147,53 @@ class Connection extends EventEmitter {
* @param {Protocol.Target.TargetInfo} targetInfo * @param {Protocol.Target.TargetInfo} targetInfo
* @return {!Promise<!CDPSession>} * @return {!Promise<!CDPSession>}
*/ */
async createSession(targetInfo) { async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CDPSession> {
const {sessionId} = await this.send('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true}); const {sessionId} = await this.send<Protocol.Target.attachedToTargetPayload>('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true});
return this._sessions.get(sessionId); return this._sessions.get(sessionId);
} }
} }
class CDPSession extends EventEmitter { interface CDPSessionOnMessageObject {
/** id?: number;
* @param {!Connection} connection method: string;
* @param {string} targetType params: {};
* @param {string} sessionId error: {message: string; data: any};
*/ result?: any;
constructor(connection, targetType, sessionId) {
}
export class CDPSession extends EventEmitter {
_connection: Connection;
_sessionId: string;
_targetType: string;
_callbacks: Map<number, ConnectionCallback> = new Map();
constructor(connection: Connection, targetType: string, sessionId: string) {
super(); super();
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/
this._callbacks = new Map();
this._connection = connection; this._connection = connection;
this._targetType = targetType; this._targetType = targetType;
this._sessionId = sessionId; this._sessionId = sessionId;
} }
/** send<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T]> {
* @param {string} method
* @param {!Object=} params
* @return {!Promise<?Object>}
*/
send(method, params = {}) {
if (!this._connection) if (!this._connection)
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`)); return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`));
const id = this._connection._rawSend({sessionId: this._sessionId, method, params});
const id = this._connection._rawSend({
sessionId: this._sessionId,
method,
/* TODO(jacktfranklin@): once this Firefox bug is solved
* we no longer need the `|| {}` check
* https://bugzilla.mozilla.org/show_bug.cgi?id=1631570
*/
params: params || {}
});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this._callbacks.set(id, {resolve, reject, error: new Error(), method}); this._callbacks.set(id, {resolve, reject, error: new Error(), method});
}); });
} }
/** _onMessage(object: CDPSessionOnMessageObject): void {
* @param {{id?: number, method: string, params: Object, error: {message: string, data: any}, result?: *}} object
*/
_onMessage(object) {
if (object.id && this._callbacks.has(object.id)) { if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id); const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id); this._callbacks.delete(object.id);
@ -201,13 +207,13 @@ class CDPSession extends EventEmitter {
} }
} }
async detach() { async detach(): Promise<void> {
if (!this._connection) if (!this._connection)
throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`); throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
await this._connection.send('Target.detachFromTarget', {sessionId: this._sessionId}); await this._connection.send('Target.detachFromTarget', {sessionId: this._sessionId});
} }
_onClosed() { _onClosed(): void {
for (const callback of this._callbacks.values()) for (const callback of this._callbacks.values())
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`)); callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`));
this._callbacks.clear(); this._callbacks.clear();
@ -222,7 +228,7 @@ class CDPSession extends EventEmitter {
* @param {{error: {message: string, data: any}}} object * @param {{error: {message: string, data: any}}} object
* @return {!Error} * @return {!Error}
*/ */
function createProtocolError(error, method, object) { function createProtocolError(error: Error, method: string, object: { error: { message: string; data: any}}): Error {
let message = `Protocol error (${method}): ${object.error.message}`; let message = `Protocol error (${method}): ${object.error.message}`;
if ('data' in object.error) if ('data' in object.error)
message += ` ${object.error.data}`; message += ` ${object.error.data}`;
@ -234,9 +240,7 @@ function createProtocolError(error, method, object) {
* @param {string} message * @param {string} message
* @return {!Error} * @return {!Error}
*/ */
function rewriteError(error, message) { function rewriteError(error: Error, message: string): Error {
error.message = message; error.message = message;
return error; return error;
} }
module.exports = {Connection, CDPSession};

View File

@ -15,6 +15,9 @@
*/ */
const {helper, debugError, assert} = require('./helper'); const {helper, debugError, assert} = require('./helper');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
const {EVALUATION_SCRIPT_URL} = require('./ExecutionContext'); const {EVALUATION_SCRIPT_URL} = require('./ExecutionContext');
@ -27,7 +30,7 @@ const {EVALUATION_SCRIPT_URL} = require('./ExecutionContext');
class Coverage { class Coverage {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._jsCoverage = new JSCoverage(client); this._jsCoverage = new JSCoverage(client);
@ -67,7 +70,7 @@ module.exports = {Coverage};
class JSCoverage { class JSCoverage {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._client = client; this._client = client;
@ -166,7 +169,7 @@ class JSCoverage {
class CSSCoverage { class CSSCoverage {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._client = client; this._client = client;

View File

@ -15,6 +15,7 @@
*/ */
import helpers = require('./helper'); import helpers = require('./helper');
import {CDPSession} from './Connection';
const {assert} = helpers; const {assert} = helpers;
@ -28,13 +29,13 @@ enum DialogType {
class Dialog { class Dialog {
static Type = DialogType; static Type = DialogType;
private _client: Puppeteer.CDPSession; private _client: CDPSession;
private _type: DialogType; private _type: DialogType;
private _message: string; private _message: string;
private _defaultValue: string; private _defaultValue: string;
private _handled = false; private _handled = false;
constructor(client: Puppeteer.CDPSession, type: DialogType, message: string, defaultValue = '') { constructor(client: CDPSession, type: DialogType, message: string, defaultValue = '') {
this._client = client; this._client = client;
this._type = type; this._type = type;
this._message = message; this._message = message;

View File

@ -14,9 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class EmulationManager { class EmulationManager {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._client = client; this._client = client;

View File

@ -16,13 +16,16 @@
const {helper, assert} = require('./helper'); const {helper, assert} = require('./helper');
const {createJSHandle, JSHandle} = require('./JSHandle'); const {createJSHandle, JSHandle} = require('./JSHandle');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__'; const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__';
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m; const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
class ExecutionContext { class ExecutionContext {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Protocol.Runtime.ExecutionContextDescription} contextPayload * @param {!Protocol.Runtime.ExecutionContextDescription} contextPayload
* @param {?Puppeteer.DOMWorld} world * @param {?Puppeteer.DOMWorld} world
*/ */

View File

@ -24,12 +24,15 @@ const {NetworkManager} = require('./NetworkManager');
// Used as a TypeDef // Used as a TypeDef
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const {TimeoutSettings} = require('./TimeoutSettings'); const {TimeoutSettings} = require('./TimeoutSettings');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
class FrameManager extends EventEmitter { class FrameManager extends EventEmitter {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Puppeteer.Page} page * @param {!Puppeteer.Page} page
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {!TimeoutSettings} timeoutSettings * @param {!TimeoutSettings} timeoutSettings
@ -112,7 +115,7 @@ class FrameManager extends EventEmitter {
return watcher.navigationResponse(); return watcher.navigationResponse();
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {string} url * @param {string} url
* @param {string} referrer * @param {string} referrer
* @param {string} frameId * @param {string} frameId
@ -376,7 +379,7 @@ class FrameManager extends EventEmitter {
class Frame { class Frame {
/** /**
* @param {!FrameManager} frameManager * @param {!FrameManager} frameManager
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {?Frame} parentFrame * @param {?Frame} parentFrame
* @param {string} frameId * @param {string} frameId
*/ */

View File

@ -16,6 +16,9 @@
const {assert} = require('./helper'); const {assert} = require('./helper');
const keyDefinitions = require('./USKeyboardLayout'); const keyDefinitions = require('./USKeyboardLayout');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
/** /**
* @typedef {Object} KeyDescription * @typedef {Object} KeyDescription
@ -28,7 +31,7 @@ const keyDefinitions = require('./USKeyboardLayout');
class Keyboard { class Keyboard {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._client = client; this._client = client;
@ -183,7 +186,7 @@ class Keyboard {
class Mouse { class Mouse {
/** /**
* @param {Puppeteer.CDPSession} client * @param {CDPSession} client
* @param {!Keyboard} keyboard * @param {!Keyboard} keyboard
*/ */
constructor(client, keyboard) { constructor(client, keyboard) {
@ -274,7 +277,7 @@ class Mouse {
class Touchscreen { class Touchscreen {
/** /**
* @param {Puppeteer.CDPSession} client * @param {CDPSession} client
* @param {Keyboard} keyboard * @param {Keyboard} keyboard
*/ */
constructor(client, keyboard) { constructor(client, keyboard) {

View File

@ -15,6 +15,9 @@
*/ */
const {helper, assert, debugError} = require('./helper'); const {helper, assert, debugError} = require('./helper');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
function createJSHandle(context, remoteObject) { function createJSHandle(context, remoteObject) {
const frame = context.frame(); const frame = context.frame();
@ -28,7 +31,7 @@ function createJSHandle(context, remoteObject) {
class JSHandle { class JSHandle {
/** /**
* @param {!Puppeteer.ExecutionContext} context * @param {!Puppeteer.ExecutionContext} context
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Protocol.Runtime.RemoteObject} remoteObject * @param {!Protocol.Runtime.RemoteObject} remoteObject
*/ */
constructor(context, client, remoteObject) { constructor(context, client, remoteObject) {
@ -142,7 +145,7 @@ class JSHandle {
class ElementHandle extends JSHandle { class ElementHandle extends JSHandle {
/** /**
* @param {!Puppeteer.ExecutionContext} context * @param {!Puppeteer.ExecutionContext} context
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Protocol.Runtime.RemoteObject} remoteObject * @param {!Protocol.Runtime.RemoteObject} remoteObject
* @param {!Puppeteer.Page} page * @param {!Puppeteer.Page} page
* @param {!Puppeteer.FrameManager} frameManager * @param {!Puppeteer.FrameManager} frameManager

View File

@ -16,10 +16,13 @@
const EventEmitter = require('events'); const EventEmitter = require('events');
const {helper, assert, debugError} = require('./helper'); const {helper, assert, debugError} = require('./helper');
const {Events} = require('./Events'); const {Events} = require('./Events');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class NetworkManager extends EventEmitter { class NetworkManager extends EventEmitter {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Puppeteer.FrameManager} frameManager * @param {!Puppeteer.FrameManager} frameManager
*/ */
constructor(client, ignoreHTTPSErrors, frameManager) { constructor(client, ignoreHTTPSErrors, frameManager) {
@ -312,7 +315,7 @@ class NetworkManager extends EventEmitter {
class Request { class Request {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {?Puppeteer.Frame} frame * @param {?Puppeteer.Frame} frame
* @param {string} interceptionId * @param {string} interceptionId
* @param {boolean} allowInterception * @param {boolean} allowInterception
@ -524,7 +527,7 @@ const errorReasons = {
class Response { class Response {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Request} request * @param {!Request} request
* @param {!Protocol.Network.Response} responsePayload * @param {!Protocol.Network.Response} responsePayload
*/ */

View File

@ -18,7 +18,9 @@ const fs = require('fs');
const EventEmitter = require('events'); const EventEmitter = require('events');
const mime = require('mime'); const mime = require('mime');
const {Events} = require('./Events'); const {Events} = require('./Events');
const {Connection} = require('./Connection'); // CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {Connection, CDPSession} = require('./Connection');
const {Dialog} = require('./Dialog'); const {Dialog} = require('./Dialog');
const {EmulationManager} = require('./EmulationManager'); const {EmulationManager} = require('./EmulationManager');
const {FrameManager} = require('./FrameManager'); const {FrameManager} = require('./FrameManager');
@ -39,7 +41,7 @@ const writeFileAsync = helper.promisify(fs.writeFile);
class Page extends EventEmitter { class Page extends EventEmitter {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Puppeteer.Target} target * @param {!Puppeteer.Target} target
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.Viewport} defaultViewport
@ -55,7 +57,7 @@ class Page extends EventEmitter {
} }
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
* @param {!Puppeteer.Target} target * @param {!Puppeteer.Target} target
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {!TaskQueue} screenshotTaskQueue * @param {!TaskQueue} screenshotTaskQueue
@ -1361,7 +1363,7 @@ class ConsoleMessage {
class FileChooser { class FileChooser {
/** /**
* @param {Puppeteer.CDPSession} client * @param {CDPSession} client
* @param {Puppeteer.ElementHandle} element * @param {Puppeteer.ElementHandle} element
* @param {!Protocol.Page.fileChooserOpenedPayload} event * @param {!Protocol.Page.fileChooserOpenedPayload} event
*/ */

View File

@ -17,6 +17,9 @@
const {Events} = require('./Events'); const {Events} = require('./Events');
const {Page} = require('./Page'); const {Page} = require('./Page');
const {Worker: PuppeteerWorker} = require('./Worker'); const {Worker: PuppeteerWorker} = require('./Worker');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
// This import is used as a TypeDef, but ESLint's rule doesn't // This import is used as a TypeDef, but ESLint's rule doesn't
// understand that unfortunately. // understand that unfortunately.
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -26,7 +29,7 @@ class Target {
/** /**
* @param {!Protocol.Target.TargetInfo} targetInfo * @param {!Protocol.Target.TargetInfo} targetInfo
* @param {!Puppeteer.BrowserContext} browserContext * @param {!Puppeteer.BrowserContext} browserContext
* @param {!function():!Promise<!Puppeteer.CDPSession>} sessionFactory * @param {!function():!Promise<!CDPSession>} sessionFactory
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.Viewport} defaultViewport
* @param {!TaskQueue} screenshotTaskQueue * @param {!TaskQueue} screenshotTaskQueue
@ -63,7 +66,7 @@ class Target {
} }
/** /**
* @return {!Promise<!Puppeteer.CDPSession>} * @return {!Promise<!CDPSession>}
*/ */
createCDPSession() { createCDPSession() {
return this._sessionFactory(); return this._sessionFactory();

View File

@ -14,10 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
const {helper, assert} = require('./helper'); const {helper, assert} = require('./helper');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class Tracing { class Tracing {
/** /**
* @param {!Puppeteer.CDPSession} client * @param {!CDPSession} client
*/ */
constructor(client) { constructor(client) {
this._client = client; this._client = client;

View File

@ -17,10 +17,13 @@ const EventEmitter = require('events');
const {debugError} = require('./helper'); const {debugError} = require('./helper');
const {ExecutionContext} = require('./ExecutionContext'); const {ExecutionContext} = require('./ExecutionContext');
const {JSHandle} = require('./JSHandle'); const {JSHandle} = require('./JSHandle');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class Worker extends EventEmitter { class Worker extends EventEmitter {
/** /**
* @param {Puppeteer.CDPSession} client * @param {CDPSession} client
* @param {string} url * @param {string} url
* @param {function(string, !Array<!JSHandle>, Protocol.Runtime.StackTrace=):void} consoleAPICalled * @param {function(string, !Array<!JSHandle>, Protocol.Runtime.StackTrace=):void} consoleAPICalled
* @param {function(!Protocol.Runtime.ExceptionDetails):void} exceptionThrown * @param {function(!Protocol.Runtime.ExceptionDetails):void} exceptionThrown

6
src/externs.d.ts vendored
View File

@ -1,4 +1,3 @@
import { Connection as RealConnection, CDPSession as RealCDPSession } from './Connection.js';
import { Browser as RealBrowser, BrowserContext as RealBrowserContext} from './Browser.js'; import { Browser as RealBrowser, BrowserContext as RealBrowserContext} from './Browser.js';
import {Target as RealTarget} from './Target.js'; import {Target as RealTarget} from './Target.js';
import {Page as RealPage} from './Page.js'; import {Page as RealPage} from './Page.js';
@ -11,11 +10,6 @@ import { NetworkManager as RealNetworkManager, Request as RealRequest, Response
import * as child_process from 'child_process'; import * as child_process from 'child_process';
declare global { declare global {
module Puppeteer { module Puppeteer {
export class Connection extends RealConnection {}
export class CDPSession extends RealCDPSession {
on<T extends keyof Protocol.Events>(event: T, listener: (arg: Protocol.Events[T]) => void): this;
send<T extends keyof Protocol.CommandParameters>(message: T, parameters?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T]>;
}
export class Mouse extends RealMouse {} export class Mouse extends RealMouse {}
export class Keyboard extends RealKeyboard {} export class Keyboard extends RealKeyboard {}
export class Touchscreen extends RealTouchscreen {} export class Touchscreen extends RealTouchscreen {}

View File

@ -19,6 +19,7 @@ import debug = require('debug');
const debugError = debug('puppeteer:error'); const debugError = debug('puppeteer:error');
import fs = require('fs'); import fs = require('fs');
import promisify = require('./promisify'); import promisify = require('./promisify');
import {CDPSession} from './Connection';
const {TimeoutError} = Errors; const {TimeoutError} = Errors;
@ -71,7 +72,7 @@ function valueFromRemoteObject(remoteObject: Protocol.Runtime.RemoteObject): unk
return remoteObject.value; return remoteObject.value;
} }
async function releaseObject(client: Puppeteer.CDPSession, remoteObject: Protocol.Runtime.RemoteObject): Promise<void> { async function releaseObject(client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject): Promise<void> {
if (!remoteObject.objectId) if (!remoteObject.objectId)
return; return;
await client.send('Runtime.releaseObject', {objectId: remoteObject.objectId}).catch(error => { await client.send('Runtime.releaseObject', {objectId: remoteObject.objectId}).catch(error => {
@ -185,7 +186,7 @@ async function waitWithTimeout<T extends any>(promise: Promise<T>, taskName: str
} }
} }
async function readProtocolStream(client: Puppeteer.CDPSession, handle: string, path?: string): Promise<Buffer> { async function readProtocolStream(client: CDPSession, handle: string, path?: string): Promise<Buffer> {
let eof = false; let eof = false;
let file; let file;
if (path) if (path)

View File

@ -247,6 +247,41 @@ function compareDocumentations(actual, expected) {
checkType(source + ' ' + actual.name, actual.type, expected.type); checkType(source + ' ' + actual.name, actual.type, expected.type);
} }
/**
* @param {string} source
* @param {!string} actualName
* @param {!string} expectedName
*/
function namingMisMatchInTypeIsExpected(source, actualName, expectedName) {
/* The DocLint tooling doesn't deal well with generics in TypeScript
* source files. We could fix this but the longterm plan is to
* auto-generate documentation from TS. So instead we document here
* the methods that use generics that DocLint trips up on and if it
* finds a mismatch that matches one of the cases below it doesn't
* error. This still means we're protected from accidental changes, as
* if the mismatch doesn't exactly match what's described below
* DocLint will fail.
*/
const expectedNamingMismatches = new Map([
['Method CDPSession.send() method', {
actualName: 'string',
expectedName: 'T'
}],
['Method CDPSession.send() params', {
actualName: 'Object',
expectedName: 'CommandParameters[T]'
}],
]);
const expectedForSource = expectedNamingMismatches.get(source);
if (!expectedForSource) return false;
const namingMismatchIsExpected = expectedForSource.actualName === actualName && expectedForSource.expectedName === expectedName;
return namingMismatchIsExpected;
}
/** /**
* @param {string} source * @param {string} source
* @param {!Documentation.Type} actual * @param {!Documentation.Type} actual
@ -260,8 +295,12 @@ function compareDocumentations(actual, expected) {
const actualName = actual.name.replace(/[\? ]/g, ''); const actualName = actual.name.replace(/[\? ]/g, '');
// TypeScript likes to add some spaces // TypeScript likes to add some spaces
const expectedName = expected.name.replace(/\ /g, ''); const expectedName = expected.name.replace(/\ /g, '');
if (expectedName !== actualName) if (expectedName !== actualName) {
const namingMismatchIsExpected = namingMisMatchInTypeIsExpected(source, actualName, expectedName);
if (!namingMismatchIsExpected)
errors.push(`${source} ${actualName} != ${expectedName}`); errors.push(`${source} ${actualName} != ${expectedName}`);
}
const actualPropertiesMap = new Map(actual.properties.map(property => [property.name, property.type])); const actualPropertiesMap = new Map(actual.properties.map(property => [property.name, property.type]));
const expectedPropertiesMap = new Map(expected.properties.map(property => [property.name, property.type])); const expectedPropertiesMap = new Map(expected.properties.map(property => [property.name, property.type]));
const propertiesDiff = diff(Array.from(actualPropertiesMap.keys()).sort(), Array.from(expectedPropertiesMap.keys()).sort()); const propertiesDiff = diff(Array.from(actualPropertiesMap.keys()).sort(), Array.from(expectedPropertiesMap.keys()).sort());