feat: export puppeteer methods (#8493)

This commit is contained in:
jrandolf 2022-06-09 19:00:50 +02:00 committed by GitHub
parent b30f3f44cd
commit 465a7c405f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 105 additions and 178 deletions

View File

@ -1,6 +1,5 @@
test/assets/modernizr.js
third_party/*
utils/browser/puppeteer-web.js
utils/doclint/check_public_api/test/
node6/*
node6-test/*

1
.gitignore vendored
View File

@ -14,7 +14,6 @@ test-ts-types/**/dist/
package-lock.json
yarn.lock
/node6
/utils/browser/puppeteer-web.js
/lib
test/coverage.json
temp/

View File

@ -121,8 +121,6 @@ lib
- vendor <== the output of compiling `vendor/tsconfig.esm.json`
```
The main entry point for the Node module Puppeteer is `cjs-entry.js`. This imports `lib/cjs/puppeteer/index.js` and exposes it to Node users.
### tsconfig for the tests
We also maintain `test/tsconfig.test.json`. This is **only used to compile the unit test `*.spec.ts` files**. When the tests are run, we first compile Puppeteer as normal before running the unit tests **against the compiled output**. Doing this lets the test run against the compiled code we ship to users so it gives us more confidence in our compiled output being correct.

View File

@ -1,29 +0,0 @@
/**
* Copyright 2020 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.
*/
/**
* We use `export default puppeteer` in `src/index.ts` to expose the library But
* TypeScript in CJS mode compiles that to `exports.default = `. This means that
* our CJS Node users would have to use `require('puppeteer').default` which
* isn't very nice.
*
* So instead we expose this file as our entry point. This requires the compiled
* Puppeteer output and re-exports the `default` export via `module.exports.`
* This means that we can publish to CJS and ESM whilst maintaining the expected
* import behaviour for CJS and ESM users.
*/
const puppeteerExport = require('./lib/cjs/puppeteer/node-puppeteer-core.js');
module.exports = puppeteerExport.default;

View File

@ -1,29 +0,0 @@
/**
* Copyright 2020 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.
*/
/**
* We use `export default puppeteer` in `src/index.ts` to expose the library But
* TypeScript in CJS mode compiles that to `exports.default = `. This means that
* our CJS Node users would have to use `require('puppeteer').default` which
* isn't very nice.
*
* So instead we expose this file as our entry point. This requires the compiled
* Puppeteer output and re-exports the `default` export via `module.exports.`
* This means that we can publish to CJS and ESM whilst maintaining the expected
* import behaviour for CJS and ESM users.
*/
const puppeteerExport = require('./lib/cjs/puppeteer/node.js');
module.exports = puppeteerExport.default;

View File

@ -9,12 +9,12 @@
"automation"
],
"type": "commonjs",
"main": "./cjs-entry.js",
"main": "./lib/cjs/puppeteer/puppeteer.js",
"exports": {
".": {
"types": "./lib/types.d.ts",
"import": "./lib/esm/puppeteer/node.js",
"require": "./cjs-entry.js"
"import": "./lib/esm/puppeteer/puppeteer.js",
"require": "./lib/cjs/puppeteer/puppeteer.js"
},
"./*": {
"import": "./*",
@ -67,16 +67,9 @@
"build-docs-production": "cd website && npm install && npm run build"
},
"files": [
"lib/types.d.ts",
"lib/**/*.d.ts",
"lib/**/*.d.ts.map",
"lib/**/*.js",
"lib/**/*.js.map",
"lib/**/package.json",
"lib",
"install.js",
"typescript-if-required.js",
"cjs-entry.js",
"cjs-entry-core.js"
"typescript-if-required.js"
],
"author": "The Chromium Authors",
"license": "Apache-2.0",

View File

@ -40,6 +40,24 @@ import puppeteer from 'puppeteer';
})();
"
echo "Testing... Chrome ES Modules Destructuring"
TMPDIR="$(mktemp -d)"
cd $TMPDIR
echo '{"type":"module"}' >>$TMPDIR/package.json
npm install --loglevel silent "${tarball}"
node --input-type="module" --eval="import puppeteer from 'puppeteer'"
node --input-type="module" --eval="import 'puppeteer/lib/esm/puppeteer/revisions.js';"
node --input-type="module" --eval="
import { launch } from 'puppeteer';
(async () => {
const browser = await launch();
const page = await browser.newPage();
await page.goto('http://example.com');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
"
echo "Testing... Chrome Webpack ES Modules"
TMPDIR="$(mktemp -d)"
cd $TMPDIR

View File

@ -1,19 +0,0 @@
module.exports = {
extends: '../.eslintrc.js',
/**
* ESLint rules
*
* All available rules: http://eslint.org/docs/rules/
*
* Rules take the following form:
* "rule-name", [severity, \{ opts \}]
* Severity: 2 == error, 1 == warning, 0 == off.
*/
rules: {
'no-console': [
2,
{ allow: ['warn', 'error', 'assert', 'timeStamp', 'time', 'timeEnd'] },
],
'no-debugger': 0,
},
};

View File

@ -117,10 +117,10 @@ export class LifecycleWatcher {
helper.addEventListener(
frameManager._client,
CDPSessionEmittedEvents.Disconnected,
() =>
this._terminate(
new Error('Navigation failed because browser has disconnected!')
)
this._terminate.bind(
this,
new Error('Navigation failed because browser has disconnected!')
)
),
helper.addEventListener(
this._frameManager,

View File

@ -66,6 +66,14 @@ export class Puppeteer {
*/
constructor(settings: CommonPuppeteerSettings) {
this._isPuppeteerCore = settings.isPuppeteerCore;
this.connect = this.connect.bind(this);
this.registerCustomQueryHandler =
this.registerCustomQueryHandler.bind(this);
this.unregisterCustomQueryHandler =
this.unregisterCustomQueryHandler.bind(this);
this.customQueryHandlerNames = this.customQueryHandlerNames.bind(this);
this.clearCustomQueryHandlers = this.clearCustomQueryHandlers.bind(this);
}
/**

View File

@ -1,24 +0,0 @@
/**
* Copyright 2020 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 { Puppeteer } from './common/Puppeteer.js';
export const initializePuppeteerWeb = (packageName: string): Puppeteer => {
const isPuppeteerCore = packageName === 'puppeteer-core';
return new Puppeteer({
isPuppeteerCore,
});
};

View File

@ -14,22 +14,22 @@
* limitations under the License.
*/
import { PuppeteerNode } from './node/Puppeteer.js';
import { PUPPETEER_REVISIONS } from './revisions.js';
import { sync } from 'pkg-dir';
import { Product } from './common/Product.js';
import { rootDirname } from './constants.js';
import { PuppeteerNode } from './node/Puppeteer.js';
import { PUPPETEER_REVISIONS } from './revisions.js';
export const initializePuppeteerNode = (packageName: string): PuppeteerNode => {
export const initializePuppeteer = (packageName: string): PuppeteerNode => {
const isPuppeteerCore = packageName === 'puppeteer-core';
const puppeteerRootDirectory = sync(rootDirname);
let preferredRevision = PUPPETEER_REVISIONS.chromium;
const isPuppeteerCore = packageName === 'puppeteer-core';
// puppeteer-core ignores environment variables
const productName = isPuppeteerCore
? undefined
: process.env['PUPPETEER_PRODUCT'] ||
process.env['npm_config_puppeteer_product'] ||
process.env['npm_package_config_puppeteer_product'];
const productName = !isPuppeteerCore
? ((process.env['PUPPETEER_PRODUCT'] ||
process.env['npm_config_puppeteer_product'] ||
process.env['npm_package_config_puppeteer_product']) as Product)
: undefined;
if (!isPuppeteerCore && productName === 'firefox')
preferredRevision = PUPPETEER_REVISIONS.firefox;
@ -38,6 +38,6 @@ export const initializePuppeteerNode = (packageName: string): PuppeteerNode => {
projectRoot: puppeteerRootDirectory,
preferredRevision,
isPuppeteerCore,
productName: productName as Product,
productName,
});
};

View File

@ -101,6 +101,12 @@ export class PuppeteerNode extends Puppeteer {
this._projectRoot = projectRoot;
this.__productName = productName;
this._preferredRevision = preferredRevision;
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.createBrowserFetcher = this.createBrowserFetcher.bind(this);
}
/**

View File

@ -17,7 +17,7 @@
import https, { RequestOptions } from 'https';
import ProgressBar from 'progress';
import URL from 'url';
import puppeteer from '../node.js';
import puppeteer from '../puppeteer.js';
import { PUPPETEER_REVISIONS } from '../revisions.js';
import { PuppeteerNode } from './Puppeteer.js';
import createHttpsProxyAgent, {

View File

@ -14,12 +14,20 @@
* limitations under the License.
*/
import { isNode } from './environment.js';
import { initializePuppeteerNode } from './initialize-node.js';
import { initializePuppeteer } from './initializePuppeteer.js';
if (!isNode) {
throw new Error('Cannot run puppeteer-core outside of Node.js');
}
const puppeteer = initializePuppeteer('puppeteer-core');
export const {
clearCustomQueryHandlers,
connect,
createBrowserFetcher,
customQueryHandlerNames,
defaultArgs,
executablePath,
launch,
registerCustomQueryHandler,
unregisterCustomQueryHandler,
} = puppeteer;
const puppeteer = initializePuppeteerNode('puppeteer-core');
export default puppeteer;

View File

@ -14,12 +14,20 @@
* limitations under the License.
*/
import { isNode } from './environment.js';
import { initializePuppeteerNode } from './initialize-node.js';
import { initializePuppeteer } from './initializePuppeteer.js';
if (!isNode) {
throw new Error('Trying to run Puppeteer-Node in a web environment.');
}
const puppeteer = initializePuppeteer('puppeteer');
export const {
clearCustomQueryHandlers,
connect,
createBrowserFetcher,
customQueryHandlerNames,
defaultArgs,
executablePath,
launch,
registerCustomQueryHandler,
unregisterCustomQueryHandler,
} = puppeteer;
const puppeteer = initializePuppeteerNode('puppeteer');
export default puppeteer;

View File

@ -1,25 +0,0 @@
/**
* Copyright 2020 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 { isNode } from './environment.js';
import { initializePuppeteerWeb } from './initialize-web.js';
if (isNode) {
throw new Error('Trying to run Puppeteer-Web in a Node environment');
}
const puppeteer = initializePuppeteerWeb('puppeteer');
export default puppeteer;

View File

@ -15,7 +15,7 @@
*/
import { Connection } from '../lib/esm/puppeteer/common/Connection.js';
import { BrowserWebSocketTransport } from '../lib/esm/puppeteer/common/BrowserWebSocketTransport.js';
import puppeteer from '../lib/esm/puppeteer/web.js';
import puppeteer from '../lib/esm/puppeteer/puppeteer.js';
import expect from '../node_modules/expect/build-es5/index.js';
import { getWebSocketEndpoint } from './helper.js';

View File

@ -2,6 +2,18 @@ const { describe, it } = require('mocha');
const { getCoverageResults } = require('./coverage-utils.js');
const expect = require('expect');
const EXCLUDED_METHODS = new Set([
'Puppeteer.registerCustomQueryHandler',
'Puppeteer.unregisterCustomQueryHandler',
'Puppeteer.customQueryHandlerNames',
'Puppeteer.clearCustomQueryHandlers',
'PuppeteerNode.connect',
'PuppeteerNode.launch',
'PuppeteerNode.executablePath',
'PuppeteerNode.defaultArgs',
'PuppeteerNode.createBrowserFetcher',
]);
describe('API coverage test', () => {
it('calls every method', () => {
if (!process.env.COVERAGE) return;
@ -9,7 +21,8 @@ describe('API coverage test', () => {
const coverageMap = getCoverageResults();
const missingMethods = [];
for (const method of coverageMap.keys()) {
if (!coverageMap.get(method)) missingMethods.push(method);
if (!coverageMap.get(method) && !EXCLUDED_METHODS.has(method))
missingMethods.push(method);
}
if (missingMethods.length) {
console.error(

View File

@ -148,9 +148,12 @@ describe('Launcher specs', function () {
await server.waitForRequest('/one-style.css');
remote.disconnect();
const error = await navigationPromise;
expect(error.message).toBe(
'Navigation failed because browser has disconnected!'
);
expect(
[
'Navigation failed because browser has disconnected!',
'Protocol error (Page.navigate): Target closed.',
].includes(error.message)
).toBeTruthy();
await browser.close();
});
it('should reject waitForSelector when browser closes', async () => {

View File

@ -19,7 +19,7 @@ import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import sinon from 'sinon';
import puppeteer from '../lib/cjs/puppeteer/node.js';
import puppeteer from '../lib/cjs/puppeteer/puppeteer.js';
import {
Browser,
BrowserContext,

View File

@ -24,8 +24,8 @@ const json = require(packagePath);
delete json.scripts.install;
json.name = 'puppeteer-core';
json.main = './cjs-entry-core.js';
json.exports['.'].import = './lib/esm/puppeteer/node-puppeteer-core.js';
json.exports['.'].require = './cjs-entry-core.js';
json.main = './lib/cjs/puppeteer/puppeteer-core.js';
json.exports['.'].import = './lib/esm/puppeteer/puppeteer-core.js';
json.exports['.'].require = './lib/cjs/puppeteer/puppeteer-core.js';
fs.writeFileSync(packagePath, JSON.stringify(json, null, ' '));