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

chore(agnostic): ship CJS and ESM builds #6095

Merged
merged 5 commits into from Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
29 changes: 29 additions & 0 deletions cjs-entry.js
@@ -0,0 +1,29 @@
/**
* 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/index');
module.exports = puppeteerExport.default;
10 changes: 7 additions & 3 deletions package.json
Expand Up @@ -2,7 +2,7 @@
"name": "puppeteer",
"version": "4.0.0-post",
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
"main": "lib/index.js",
"main": "./cjs-entry.js",
"repository": "github:puppeteer/puppeteer",
"engines": {
"node": ">=10.18.1"
Expand All @@ -28,7 +28,9 @@
"lint": "npm run eslint && npm run tsc && npm run doc",
"doc": "node utils/doclint/cli.js",
"clean-lib": "rm -rf lib",
"tsc": "npm run clean-lib && tsc --version && tsc -p . && cp src/protocol.d.ts lib/",
"tsc": "npm run clean-lib && tsc --version && npm run tsc-cjs && npm run tsc-esm",
"tsc-cjs": "tsc -p . && cp src/protocol.d.ts lib/cjs",
"tsc-esm": "tsc --build tsconfig-esm.json && cp src/protocol.d.ts lib/esm",
"apply-next-version": "node utils/apply_next_version.js",
"update-protocol-d-ts": "node utils/protocol-types-generator update",
"compare-protocol-d-ts": "node utils/protocol-types-generator compare",
Expand All @@ -41,7 +43,8 @@
"lib/",
"index.js",
"install.js",
"typescript-if-required.js"
"typescript-if-required.js",
"cjs-entry.js"
],
"author": "The Chromium Authors",
"license": "Apache-2.0",
Expand All @@ -53,6 +56,7 @@
"mitt": "^2.0.1",
"progress": "^2.0.1",
"proxy-from-env": "^1.0.0",
"read-pkg-up": "^7.0.1",
"rimraf": "^3.0.2",
"tar-fs": "^2.0.0",
"unbzip2-stream": "^1.3.3",
Expand Down
4 changes: 3 additions & 1 deletion scripts/test-install.sh
Expand Up @@ -4,14 +4,16 @@ set -e
# Pack the module into a tarball
npm pack
tarball="$(realpath puppeteer-*.tgz)"
cd "$(mktemp -d)"
TMPDIR="$(mktemp -d)"
cd $TMPDIR
# Check we can install from the tarball.
# This emulates installing from npm and ensures that:
# 1. we publish the right files in the `files` list from package.json
# 2. The install script works and correctly exits without errors
# 3. Requiring Puppeteer from Node works.
npm install --loglevel silent "${tarball}"
node --eval="require('puppeteer')"
ls $TMPDIR/node_modules/puppeteer/.local-chromium/

# Again for Firefox
TMPDIR="$(mktemp -d)"
Expand Down
5 changes: 2 additions & 3 deletions src/common/Puppeteer.ts
Expand Up @@ -23,6 +23,7 @@ import { ProductLauncher } from '../node/Launcher';
import { BrowserFetcher, BrowserFetcherOptions } from '../node/BrowserFetcher';
import { puppeteerErrors, PuppeteerErrors } from './Errors';
import { ConnectionTransport } from './ConnectionTransport';
import readPkgUp from 'read-pkg-up';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work in the browser as well as node?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This is on my list to figure out. The old solution wouldn't have worked either.

I think it only needs to use the package.json in order to know which browser revision to download which is not needed in the browser; the work here is to try to prise that code out into a node specific function.


import { devicesMap } from './DeviceDescriptors';
import { DevicesMap } from './DeviceDescriptors';
Expand Down Expand Up @@ -172,9 +173,7 @@ export class Puppeteer {
this._lazyLauncher.product !== this._productName ||
this._changedProduct
) {
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../../package.json');
const { packageJson } = readPkgUp.sync({ cwd: __dirname });
switch (this._productName) {
case 'firefox':
this._preferredRevision = packageJson.puppeteer.firefox_revision;
Expand Down
25 changes: 12 additions & 13 deletions src/index.ts
Expand Up @@ -14,21 +14,20 @@
* limitations under the License.
*/

import { initializePuppeteer } from './initialize';
import { initializePuppeteer, InitOptions } from './initialize';
import * as path from 'path';
import readPkgUp from 'read-pkg-up';

const packageJsonResult = readPkgUp.sync({
cwd: __dirname,
});
const packageJson = packageJsonResult.packageJson as unknown;

const rootDir = path.dirname(packageJsonResult.path);

const puppeteer = initializePuppeteer({
packageJson: require(path.join(__dirname, '..', 'package.json')),
rootDirectory: path.join(__dirname, '..'),
packageJson: packageJson as InitOptions['packageJson'],
rootDirectory: rootDir,
});

/*
* Has to be CJS here rather than ESM such that the output file ends with
* module.exports = puppeteer.
*
* If this was export default puppeteer the output would be:
* exports.default = puppeteer
* And therefore consuming via require('puppeteer') would break / require the user
* to access require('puppeteer').default;
*/
export = puppeteer;
export default puppeteer;
2 changes: 1 addition & 1 deletion src/initialize.ts
Expand Up @@ -22,7 +22,7 @@ const api = require('./api');
import { helper } from './common/helper';
import { Puppeteer } from './common/Puppeteer';

interface InitOptions {
export interface InitOptions {
packageJson: {
puppeteer: {
chromium_revision: string;
Expand Down
7 changes: 7 additions & 0 deletions tsconfig-esm.json
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./lib/esm",
"module": "ES2015",
},
}
2 changes: 1 addition & 1 deletion tsconfig.json
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"outDir": "./lib",
"outDir": "./lib/cjs",
"target": "ESNext",
"moduleResolution": "node",
"module": "CommonJS",
Expand Down