Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(puppeteer): export esm modules in package.json #7964

Merged
merged 6 commits into from Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -19,7 +19,7 @@ yarn.lock
test/coverage.json
temp/
new-docs/
puppeteer.tgz
puppeteer*.tgz
docs-api-json/
docs-dist/
website/docs
16 changes: 16 additions & 0 deletions compat/README.md
@@ -0,0 +1,16 @@
# Compatibility layer

This directory provides an additional compatibility layer between ES modules and CommonJS.

## Why?

Both `./cjs/compat.ts` and `./esm/compat.ts` are written as ES modules, but `./cjs/compat.ts` can additionally use NodeJS CommonJS globals such as `__dirname` and `require` while these are disabled in ES module mode. For more information, see [Differences between ES modules and CommonJS](https://nodejs.org/api/esm.html#differences-between-es-modules-and-commonjs).

## Adding exports

In order to add exports, two things need to be done:

- The exports must be declared in `src/compat.ts`.
- The exports must be realized in `./cjs/compat.ts` and `./esm/compat.ts`.

In the event `compat.ts` becomes too large, you can place declarations in another file. Just make sure `./cjs`, `./esm`, and `src` have the same structure.
3 changes: 3 additions & 0 deletions compat/cjs/compat.ts
@@ -0,0 +1,3 @@
import { dirname } from 'path';

export const puppeteerDirname = dirname(dirname(dirname(__dirname)));
11 changes: 11 additions & 0 deletions compat/cjs/tsconfig.json
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "../../lib/cjs/puppeteer",
"module": "CommonJS"
},
"references": [
{ "path": "../../vendor/tsconfig.cjs.json"}
]
}
6 changes: 6 additions & 0 deletions compat/esm/compat.ts
@@ -0,0 +1,6 @@
import { dirname } from 'path';
import { fileURLToPath } from 'url';

export const puppeteerDirname = dirname(
dirname(dirname(dirname(fileURLToPath(import.meta.url))))
);
9 changes: 9 additions & 0 deletions compat/esm/tsconfig.json
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "../../lib/esm/puppeteer",
"module": "esnext"
},
"references": [{ "path": "../../vendor/tsconfig.esm.json" }]
}
13 changes: 11 additions & 2 deletions package.json
Expand Up @@ -2,7 +2,12 @@
"name": "puppeteer",
"version": "13.2.0-post",
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
"type": "commonjs",
"main": "./cjs-entry.js",
"exports": {
"import": "./lib/esm/puppeteer/node.js",
"require": "./cjs-entry.js"
},
"types": "lib/types.d.ts",
"repository": "github:puppeteer/puppeteer",
"engines": {
Expand All @@ -29,15 +34,18 @@
"lint": "npm run eslint && npm run build && npm run doc && npm run markdownlint",
"doc": "node utils/doclint/cli.js",
"clean-lib": "rimraf lib",
"build": "npm run tsc && npm run generate-d-ts",
"tsc": "npm run clean-lib && tsc --version && npm run tsc-cjs && npm run tsc-esm",
"build": "npm run tsc && npm run generate-d-ts && npm run generate-pkg-json",
"tsc": "npm run clean-lib && tsc --version && (npm run tsc-cjs & npm run tsc-esm) && (npm run tsc-compat-cjs & npm run tsc-compat-esm)",
OrKoN marked this conversation as resolved.
Show resolved Hide resolved
"tsc-cjs": "tsc -b src/tsconfig.cjs.json",
"tsc-esm": "tsc -b src/tsconfig.esm.json",
"tsc-compat-cjs": "tsc -b compat/cjs/tsconfig.json",
"tsc-compat-esm": "tsc -b compat/esm/tsconfig.json",
"apply-next-version": "node utils/apply_next_version.js",
"test-install": "scripts/test-install.sh",
"clean-docs": "rimraf website/docs && rimraf docs-api-json",
"generate-d-ts": "npm run clean-docs && api-extractor run --local --verbose",
"generate-docs": "npm run generate-d-ts && api-documenter markdown -i docs-api-json -o website/docs && node utils/remove-tag.js",
"generate-pkg-json": "echo '{\"type\": \"module\"}' > lib/esm/package.json",
"ensure-correct-devtools-protocol-revision": "ts-node -s scripts/ensure-correct-devtools-protocol-package",
"ensure-pinned-deps": "ts-node -s scripts/ensure-pinned-deps",
"test-types-file": "ts-node -s scripts/test-ts-definition-files.ts",
Expand All @@ -50,6 +58,7 @@
"lib/**/*.d.ts.map",
"lib/**/*.js",
"lib/**/*.js.map",
"lib/**/package.json",
"install.js",
"typescript-if-required.js",
"cjs-entry.js",
Expand Down
26 changes: 26 additions & 0 deletions scripts/test-install.sh
Expand Up @@ -16,6 +16,26 @@ npm install --loglevel silent "${tarball}"
node --eval="require('puppeteer')"
ls $TMPDIR/node_modules/puppeteer/.local-chromium/

# Testing ES module features
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'"
OrKoN marked this conversation as resolved.
Show resolved Hide resolved
ls $TMPDIR/node_modules/puppeteer/.local-chromium/

node --input-type="module" --eval="
import puppeteer from 'puppeteer';

(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://example.com');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
"

# Again for Firefox
TMPDIR="$(mktemp -d)"
cd $TMPDIR
Expand All @@ -39,3 +59,9 @@ cd $TMPDIR
npm install --loglevel silent "${tarball}"
node --eval="require('puppeteer-core')"

# Testing ES module features
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-core'"
4 changes: 3 additions & 1 deletion src/common/Debug.ts
Expand Up @@ -55,7 +55,9 @@ import { isNode } from '../environment.js';
export const debug = (prefix: string): ((...args: unknown[]) => void) => {
if (isNode) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require('debug')(prefix);
return async (...logArgs: unknown[]) => {
(await import('debug')).default(prefix)(logArgs);
};
}

return (...logArgs: unknown[]): void => {
Expand Down
3 changes: 3 additions & 0 deletions src/compat.ts
@@ -0,0 +1,3 @@
declare const puppeteerDirname: string;

export { puppeteerDirname };
3 changes: 2 additions & 1 deletion src/initialize-node.ts
Expand Up @@ -18,9 +18,10 @@ import { PuppeteerNode } from './node/Puppeteer.js';
import { PUPPETEER_REVISIONS } from './revisions.js';
import pkgDir from 'pkg-dir';
import { Product } from './common/Product.js';
import { puppeteerDirname } from './compat.js';

export const initializePuppeteerNode = (packageName: string): PuppeteerNode => {
const puppeteerRootDirectory = pkgDir.sync(__dirname);
const puppeteerRootDirectory = pkgDir.sync(puppeteerDirname);
jrandolf-zz marked this conversation as resolved.
Show resolved Hide resolved

let preferredRevision = PUPPETEER_REVISIONS.chromium;
const isPuppeteerCore = packageName === 'puppeteer-core';
Expand Down
6 changes: 2 additions & 4 deletions src/node/BrowserFetcher.ts
Expand Up @@ -34,6 +34,8 @@ import createHttpsProxyAgent, {
} from 'https-proxy-agent';
import { getProxyForUrl } from 'proxy-from-env';
import { assert } from '../common/assert.js';
import tar from 'tar-fs';
import bzip from 'unbzip2-stream';

const debugFetcher = debug('puppeteer:fetcher');

Expand Down Expand Up @@ -499,10 +501,6 @@ function install(archivePath: string, folderPath: string): Promise<unknown> {
* @internal
*/
function extractTar(tarPath: string, folderPath: string): Promise<unknown> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const tar = require('tar-fs');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const bzip = require('unbzip2-stream');
return new Promise((fulfill, reject) => {
const tarStream = tar.extract(folderPath);
tarStream.on('error', reject);
Expand Down
14 changes: 11 additions & 3 deletions src/node/NodeWebSocketTransport.ts
Expand Up @@ -15,18 +15,26 @@
*/
import { ConnectionTransport } from '../common/ConnectionTransport.js';
import NodeWebSocket from 'ws';
import { readFileSync } from 'fs';
import { join } from 'path';
import { puppeteerDirname } from '../compat.js';

// We parse rather than import for ES module compatibility.
const version = JSON.parse(
readFileSync(join(puppeteerDirname, 'package.json'), {
encoding: 'utf8',
})
).version;

export class NodeWebSocketTransport implements ConnectionTransport {
static create(url: string): Promise<NodeWebSocketTransport> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require('../../../../package.json');
return new Promise((resolve, reject) => {
const ws = new NodeWebSocket(url, [], {
jrandolf-zz marked this conversation as resolved.
Show resolved Hide resolved
followRedirects: true,
perMessageDeflate: false,
maxPayload: 256 * 1024 * 1024, // 256Mb
headers: {
'User-Agent': `Puppeteer ${pkg.version}`,
'User-Agent': `Puppeteer ${version}`,
},
});

Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Expand Up @@ -12,5 +12,5 @@
*/
"module": "esnext"
},
"include": ["src"]
"include": ["src"],
}
4 changes: 3 additions & 1 deletion utils/prepare_puppeteer_core.js
Expand Up @@ -21,7 +21,9 @@ const path = require('path');
const packagePath = path.join(__dirname, '..', 'package.json');
const json = require(packagePath);

json.name = 'puppeteer-core';
delete json.scripts.install;

json.name = 'puppeteer-core';
json.main = './cjs-entry-core.js';
json.exports.import = './lib/esm/puppeteer/node-puppeteer-core.js';
fs.writeFileSync(packagePath, JSON.stringify(json, null, ' '));