Skip to content

Commit

Permalink
feat: implement the Puppeteer CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
OrKoN committed Nov 22, 2023
1 parent a7fcde9 commit e7b7587
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 23 deletions.
26 changes: 21 additions & 5 deletions docs/browsers-api/browsers.cli._constructor_.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,29 @@ Constructs a new instance of the `CLI` class

```typescript
class CLI {
constructor(cachePath?: string, rl?: readline.Interface);
constructor(
opts?:
| string
| {
cachePath?: string;
scriptName?: string;
prefixCommand?: {
cmd: string;
description: string;
};
allowCachePathOverride?: boolean;
pinnedBrowsers?: Partial<{
[key in Browser]: string;
}>;
},
rl?: readline.Interface
);
}
```

## Parameters

| Parameter | Type | Description |
| --------- | ------------------ | ------------ |
| cachePath | string | _(Optional)_ |
| rl | readline.Interface | _(Optional)_ |
| Parameter | Type | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| opts | string \| { cachePath?: string; scriptName?: string; prefixCommand?: { cmd: string; description: string; }; allowCachePathOverride?: boolean; pinnedBrowsers?: Partial&lt;{ \[key in [Browser](./browsers.browser.md)\]: string; }&gt;; } | _(Optional)_ |
| rl | readline.Interface | _(Optional)_ |
6 changes: 3 additions & 3 deletions docs/browsers-api/browsers.cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export declare class CLI

## Constructors

| Constructor | Modifiers | Description |
| --------------------------------------------------------------- | --------- | ------------------------------------------------------- |
| [(constructor)(cachePath, rl)](./browsers.cli._constructor_.md) | | Constructs a new instance of the <code>CLI</code> class |
| Constructor | Modifiers | Description |
| ---------------------------------------------------------- | --------- | ------------------------------------------------------- |
| [(constructor)(opts, rl)](./browsers.cli._constructor_.md) | | Constructs a new instance of the <code>CLI</code> class |

## Methods

Expand Down
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 79 additions & 14 deletions packages/browsers/src/CLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,37 @@ interface ClearArgs {
export class CLI {
#cachePath;
#rl?: readline.Interface;
#scriptName = '';
#allowCachePathOverride = true;
#pinnedBrowsers?: Partial<{[key in Browser]: string}>;
#prefixCommand?: {cmd: string; description: string};

constructor(cachePath = process.cwd(), rl?: readline.Interface) {
this.#cachePath = cachePath;
constructor(
opts?:
| string
| {
cachePath?: string;
scriptName?: string;
prefixCommand?: {cmd: string; description: string};
allowCachePathOverride?: boolean;
pinnedBrowsers?: Partial<{[key in Browser]: string}>;
},
rl?: readline.Interface
) {
if (!opts) {
opts = {};
}
if (typeof opts === 'string') {
opts = {
cachePath: opts,
};
}
this.#cachePath = opts.cachePath ?? process.cwd();
this.#rl = rl;
this.#scriptName = opts.scriptName ?? '@puppeteer/browsers';
this.#allowCachePathOverride = opts.allowCachePathOverride ?? true;
this.#pinnedBrowsers = opts.pinnedBrowsers;
this.#prefixCommand = opts.prefixCommand;
}

#defineBrowserParameter(yargs: Yargs.Argv<unknown>): void {
Expand All @@ -98,6 +125,9 @@ export class CLI {
}

#definePathParameter(yargs: Yargs.Argv<unknown>, required = false): void {
if (!this.#allowCachePathOverride) {
return;
}
yargs.option('path', {
type: 'string',
desc: 'Path to the root folder for the browser downloads and installation. The installation folder structure is compatible with the cache structure used by Puppeteer.',
Expand All @@ -111,8 +141,28 @@ export class CLI {

async run(argv: string[]): Promise<void> {
const yargsInstance = yargs(hideBin(argv));
await yargsInstance
.scriptName('@puppeteer/browsers')
let target = yargsInstance.scriptName(this.#scriptName);
if (this.#prefixCommand) {
target = target.command(
this.#prefixCommand.cmd,
this.#prefixCommand.description,
yargs => {
return this.#build(yargs);
}
);
} else {
target = this.#build(target);
}
await target
.demandCommand(1)
.help()
.wrap(Math.min(120, yargsInstance.terminalWidth()))
.parse();
}

#build(yargs: Yargs.Argv<unknown>): Yargs.Argv<unknown> {
const latestOrPinned = this.#pinnedBrowsers ? 'pinned' : 'latest';
return yargs
.command(
'install <browser>',
'Download and install the specified browser. If successful, the command outputs the actual browser buildId that was installed and the absolute path to the browser executable (format: <browser>@<buildID> <path>).',
Expand All @@ -126,7 +176,7 @@ export class CLI {
});
yargs.example(
'$0 install chrome',
'Install the latest available build of the Chrome browser.'
`Install the ${latestOrPinned} available build of the Chrome browser.`
);
yargs.example(
'$0 install chrome@latest',
Expand Down Expand Up @@ -176,17 +226,28 @@ export class CLI {
'$0 install firefox --platform mac',
'Install the latest Mac (Intel) build of the Firefox browser.'
);
yargs.example(
'$0 install firefox --path /tmp/my-browser-cache',
'Install to the specified cache directory.'
);
if (this.#allowCachePathOverride) {
yargs.example(
'$0 install firefox --path /tmp/my-browser-cache',
'Install to the specified cache directory.'
);
}
},
async argv => {
const args = argv as unknown as InstallArgs;
args.platform ??= detectBrowserPlatform();
if (!args.platform) {
throw new Error(`Could not resolve the current platform`);
}
if (args.browser.buildId === 'pinned') {
const pinnedVersion = this.#pinnedBrowsers?.[args.browser.name];
if (!pinnedVersion) {
throw new Error(
`No pinned version found for ${args.browser.name}`
);
}
args.browser.buildId = pinnedVersion;
}
args.browser.buildId = await resolveBuildId(
args.browser.name,
args.platform,
Expand Down Expand Up @@ -272,7 +333,9 @@ export class CLI {
)
.command(
'clear',
'Removes all installed browsers from the specified cache directory',
this.#allowCachePathOverride
? 'Removes all installed browsers from the specified cache directory'
: `Removes all installed browsers from ${this.#cachePath}`,
yargs => {
this.#definePathParameter(yargs, true);
},
Expand All @@ -296,9 +359,7 @@ export class CLI {
}
)
.demandCommand(1)
.help()
.wrap(Math.min(120, yargsInstance.terminalWidth()))
.parse();
.help();
}

#parseBrowser(version: string): Browser {
Expand All @@ -307,7 +368,11 @@ export class CLI {

#parseBuildId(version: string): string {
const parts = version.split('@');
return parts.length === 2 ? parts[1]! : 'latest';
return parts.length === 2
? parts[1]!
: this.#pinnedBrowsers
? 'pinned'
: 'latest';
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/puppeteer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"automation"
],
"type": "commonjs",
"bin": "./lib/esm/puppeteer/node/cli.js",
"main": "./lib/cjs/puppeteer/puppeteer.js",
"types": "./lib/types.d.ts",
"exports": {
Expand Down Expand Up @@ -77,7 +78,7 @@
]
},
"build:tsc": {
"command": "tsc -b",
"command": "tsc -b && tsx ../../tools/chmod.ts 755 lib/cjs/puppeteer/node/cli.js lib/esm/puppeteer/node/cli.js",
"clean": "if-file-deleted",
"dependencies": [
"../puppeteer-core:build",
Expand Down
41 changes: 41 additions & 0 deletions packages/puppeteer/src/node/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env node

/**
* Copyright 2023 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 {CLI, Browser} from '@puppeteer/browsers';
import {PUPPETEER_REVISIONS} from 'puppeteer-core/internal/revisions.js';

import puppeteer from '../puppeteer.js';

// TODO: deprecate downloadPath in favour of cacheDirectory.
const cacheDir =
puppeteer.configuration.downloadPath ??
puppeteer.configuration.cacheDirectory!;

void new CLI({
cachePath: cacheDir,
scriptName: 'puppeteer',
prefixCommand: {
cmd: 'browsers',
description: 'Manage browsers of this Puppeteer installation',
},
allowCachePathOverride: false,
pinnedBrowsers: {
[Browser.CHROME]: PUPPETEER_REVISIONS.chrome,
[Browser.FIREFOX]: PUPPETEER_REVISIONS.firefox,
},
}).run(process.argv);

0 comments on commit e7b7587

Please sign in to comment.