chore: extract BrowserRunner
into its own module (#5850)
* chore: extract `BrowserRunner` into its own module `src/Launcher.ts` is large and hard to work in. It has multiple objects defined in it: * ChromeLauncher * FirefoxLauncher * BrowserRunner * Launcher This change moves BrowserRunner into its own module. More refactorings like this will follow but this is the first step.
This commit is contained in:
parent
b38bb4334f
commit
c6d01c950e
258
src/Launcher.ts
258
src/Launcher.ts
@ -19,26 +19,23 @@ import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as URL from 'url';
|
||||
import * as fs from 'fs';
|
||||
import * as readline from 'readline';
|
||||
import * as debug from 'debug';
|
||||
|
||||
import * as removeFolder from 'rimraf';
|
||||
import * as childProcess from 'child_process';
|
||||
|
||||
import { BrowserFetcher } from './BrowserFetcher';
|
||||
import { Connection } from './Connection';
|
||||
import { Browser } from './Browser';
|
||||
import { helper, assert, debugError } from './helper';
|
||||
import { TimeoutError } from './Errors';
|
||||
import type { ConnectionTransport } from './ConnectionTransport';
|
||||
import { WebSocketTransport } from './WebSocketTransport';
|
||||
import { PipeTransport } from './PipeTransport';
|
||||
import type { Viewport } from './PuppeteerViewport';
|
||||
import { BrowserRunner } from './launcher/BrowserRunner';
|
||||
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const removeFolderAsync = helper.promisify(removeFolder);
|
||||
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||
const debugLauncher = debug('puppeteer:launcher');
|
||||
|
||||
import type {
|
||||
ChromeArgOptions,
|
||||
LaunchOptions,
|
||||
BrowserOptions,
|
||||
} from './launcher/LaunchOptions';
|
||||
|
||||
export interface ProductLauncher {
|
||||
launch(object);
|
||||
@ -48,186 +45,6 @@ export interface ProductLauncher {
|
||||
product: string;
|
||||
}
|
||||
|
||||
export interface ChromeArgOptions {
|
||||
headless?: boolean;
|
||||
args?: string[];
|
||||
userDataDir?: string;
|
||||
devtools?: boolean;
|
||||
}
|
||||
|
||||
export interface LaunchOptions {
|
||||
executablePath?: string;
|
||||
ignoreDefaultArgs?: boolean | string[];
|
||||
handleSIGINT?: boolean;
|
||||
handleSIGTERM?: boolean;
|
||||
handleSIGHUP?: boolean;
|
||||
timeout?: number;
|
||||
dumpio?: boolean;
|
||||
env?: Record<string, string | undefined>;
|
||||
pipe?: boolean;
|
||||
}
|
||||
|
||||
export interface BrowserOptions {
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
defaultViewport?: Viewport;
|
||||
slowMo?: number;
|
||||
}
|
||||
|
||||
class BrowserRunner {
|
||||
_executablePath: string;
|
||||
_processArguments: string[];
|
||||
_tempDirectory?: string;
|
||||
|
||||
proc = null;
|
||||
connection = null;
|
||||
|
||||
_closed = true;
|
||||
_listeners = [];
|
||||
_processClosing: Promise<void>;
|
||||
|
||||
constructor(
|
||||
executablePath: string,
|
||||
processArguments: string[],
|
||||
tempDirectory?: string
|
||||
) {
|
||||
this._executablePath = executablePath;
|
||||
this._processArguments = processArguments;
|
||||
this._tempDirectory = tempDirectory;
|
||||
}
|
||||
|
||||
start(options: LaunchOptions = {}): void {
|
||||
const {
|
||||
handleSIGINT,
|
||||
handleSIGTERM,
|
||||
handleSIGHUP,
|
||||
dumpio,
|
||||
env,
|
||||
pipe,
|
||||
} = options;
|
||||
let stdio: Array<'ignore' | 'pipe'> = ['pipe', 'pipe', 'pipe'];
|
||||
if (pipe) {
|
||||
if (dumpio) stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'];
|
||||
else stdio = ['ignore', 'ignore', 'ignore', 'pipe', 'pipe'];
|
||||
}
|
||||
assert(!this.proc, 'This process has previously been started.');
|
||||
debugLauncher(
|
||||
`Calling ${this._executablePath} ${this._processArguments.join(' ')}`
|
||||
);
|
||||
this.proc = childProcess.spawn(
|
||||
this._executablePath,
|
||||
this._processArguments,
|
||||
{
|
||||
// On non-windows platforms, `detached: true` makes child process a leader of a new
|
||||
// process group, making it possible to kill child process tree with `.kill(-pid)` command.
|
||||
// @see https://nodejs.org/api/child_process.html#child_process_options_detached
|
||||
detached: process.platform !== 'win32',
|
||||
env,
|
||||
stdio,
|
||||
}
|
||||
);
|
||||
if (dumpio) {
|
||||
this.proc.stderr.pipe(process.stderr);
|
||||
this.proc.stdout.pipe(process.stdout);
|
||||
}
|
||||
this._closed = false;
|
||||
this._processClosing = new Promise((fulfill) => {
|
||||
this.proc.once('exit', () => {
|
||||
this._closed = true;
|
||||
// Cleanup as processes exit.
|
||||
if (this._tempDirectory) {
|
||||
removeFolderAsync(this._tempDirectory)
|
||||
.then(() => fulfill())
|
||||
.catch((error) => console.error(error));
|
||||
} else {
|
||||
fulfill();
|
||||
}
|
||||
});
|
||||
});
|
||||
this._listeners = [
|
||||
helper.addEventListener(process, 'exit', this.kill.bind(this)),
|
||||
];
|
||||
if (handleSIGINT)
|
||||
this._listeners.push(
|
||||
helper.addEventListener(process, 'SIGINT', () => {
|
||||
this.kill();
|
||||
process.exit(130);
|
||||
})
|
||||
);
|
||||
if (handleSIGTERM)
|
||||
this._listeners.push(
|
||||
helper.addEventListener(process, 'SIGTERM', this.close.bind(this))
|
||||
);
|
||||
if (handleSIGHUP)
|
||||
this._listeners.push(
|
||||
helper.addEventListener(process, 'SIGHUP', this.close.bind(this))
|
||||
);
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
if (this._closed) return Promise.resolve();
|
||||
helper.removeEventListeners(this._listeners);
|
||||
if (this._tempDirectory) {
|
||||
this.kill();
|
||||
} else if (this.connection) {
|
||||
// Attempt to close the browser gracefully
|
||||
this.connection.send('Browser.close').catch((error) => {
|
||||
debugError(error);
|
||||
this.kill();
|
||||
});
|
||||
}
|
||||
return this._processClosing;
|
||||
}
|
||||
|
||||
kill(): void {
|
||||
helper.removeEventListeners(this._listeners);
|
||||
if (this.proc && this.proc.pid && !this.proc.killed && !this._closed) {
|
||||
try {
|
||||
if (process.platform === 'win32')
|
||||
childProcess.execSync(`taskkill /pid ${this.proc.pid} /T /F`);
|
||||
else process.kill(-this.proc.pid, 'SIGKILL');
|
||||
} catch (error) {
|
||||
// the process might have already stopped
|
||||
}
|
||||
}
|
||||
// Attempt to remove temporary profile directory to avoid littering.
|
||||
try {
|
||||
removeFolder.sync(this._tempDirectory);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!({usePipe?: boolean, timeout: number, slowMo: number, preferredRevision: string})} options
|
||||
*
|
||||
* @return {!Promise<!Connection>}
|
||||
*/
|
||||
async setupConnection(options: {
|
||||
usePipe?: boolean;
|
||||
timeout: number;
|
||||
slowMo: number;
|
||||
preferredRevision: string;
|
||||
}): Promise<Connection> {
|
||||
const { usePipe, timeout, slowMo, preferredRevision } = options;
|
||||
if (!usePipe) {
|
||||
const browserWSEndpoint = await waitForWSEndpoint(
|
||||
this.proc,
|
||||
timeout,
|
||||
preferredRevision
|
||||
);
|
||||
const transport = await WebSocketTransport.create(browserWSEndpoint);
|
||||
this.connection = new Connection(browserWSEndpoint, transport, slowMo);
|
||||
} else {
|
||||
// stdio was assigned during start(), and the 'pipe' option there adds the 4th and 5th items to stdio array
|
||||
const { 3: pipeWrite, 4: pipeRead } = this.proc.stdio;
|
||||
const transport = new PipeTransport(
|
||||
pipeWrite as NodeJS.WritableStream,
|
||||
pipeRead as NodeJS.ReadableStream
|
||||
);
|
||||
this.connection = new Connection('', transport, slowMo);
|
||||
}
|
||||
return this.connection;
|
||||
}
|
||||
}
|
||||
|
||||
class ChromeLauncher implements ProductLauncher {
|
||||
_projectRoot: string;
|
||||
_preferredRevision: string;
|
||||
@ -871,67 +688,6 @@ class FirefoxLauncher implements ProductLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
function waitForWSEndpoint(
|
||||
browserProcess: childProcess.ChildProcess,
|
||||
timeout: number,
|
||||
preferredRevision: string
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const rl = readline.createInterface({ input: browserProcess.stderr });
|
||||
let stderr = '';
|
||||
const listeners = [
|
||||
helper.addEventListener(rl, 'line', onLine),
|
||||
helper.addEventListener(rl, 'close', () => onClose()),
|
||||
helper.addEventListener(browserProcess, 'exit', () => onClose()),
|
||||
helper.addEventListener(browserProcess, 'error', (error) =>
|
||||
onClose(error)
|
||||
),
|
||||
];
|
||||
const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;
|
||||
|
||||
/**
|
||||
* @param {!Error=} error
|
||||
*/
|
||||
function onClose(error?: Error): void {
|
||||
cleanup();
|
||||
reject(
|
||||
new Error(
|
||||
[
|
||||
'Failed to launch the browser process!' +
|
||||
(error ? ' ' + error.message : ''),
|
||||
stderr,
|
||||
'',
|
||||
'TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md',
|
||||
'',
|
||||
].join('\n')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function onTimeout(): void {
|
||||
cleanup();
|
||||
reject(
|
||||
new TimeoutError(
|
||||
`Timed out after ${timeout} ms while trying to connect to the browser! Only Chrome at revision r${preferredRevision} is guaranteed to work.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function onLine(line: string): void {
|
||||
stderr += line + '\n';
|
||||
const match = line.match(/^DevTools listening on (ws:\/\/.*)$/);
|
||||
if (!match) return;
|
||||
cleanup();
|
||||
resolve(match[1]);
|
||||
}
|
||||
|
||||
function cleanup(): void {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
helper.removeEventListeners(listeners);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getWSEndpoint(browserURL: string): Promise<string> {
|
||||
let resolve, reject;
|
||||
const promise = new Promise<string>((res, rej) => {
|
||||
|
@ -18,8 +18,8 @@ import type {
|
||||
LaunchOptions,
|
||||
ChromeArgOptions,
|
||||
BrowserOptions,
|
||||
ProductLauncher,
|
||||
} from './Launcher';
|
||||
} from './launcher/LaunchOptions';
|
||||
import type { ProductLauncher } from './Launcher';
|
||||
import { BrowserFetcher, BrowserFetcherOptions } from './BrowserFetcher';
|
||||
import { puppeteerErrors, PuppeteerErrors } from './Errors';
|
||||
import type { ConnectionTransport } from './ConnectionTransport';
|
||||
|
241
src/launcher/BrowserRunner.ts
Normal file
241
src/launcher/BrowserRunner.ts
Normal file
@ -0,0 +1,241 @@
|
||||
/**
|
||||
* 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 * as debug from 'debug';
|
||||
|
||||
import * as removeFolder from 'rimraf';
|
||||
import * as childProcess from 'child_process';
|
||||
import { helper, assert, debugError } from '../helper';
|
||||
import type { LaunchOptions } from './LaunchOptions';
|
||||
import { Connection } from '../Connection';
|
||||
import { WebSocketTransport } from '../WebSocketTransport';
|
||||
import { PipeTransport } from '../PipeTransport';
|
||||
import * as readline from 'readline';
|
||||
import { TimeoutError } from '../Errors';
|
||||
|
||||
const removeFolderAsync = helper.promisify(removeFolder);
|
||||
const debugLauncher = debug('puppeteer:launcher');
|
||||
|
||||
export class BrowserRunner {
|
||||
private _executablePath: string;
|
||||
private _processArguments: string[];
|
||||
private _tempDirectory?: string;
|
||||
|
||||
proc = null;
|
||||
connection = null;
|
||||
|
||||
private _closed = true;
|
||||
private _listeners = [];
|
||||
private _processClosing: Promise<void>;
|
||||
|
||||
constructor(
|
||||
executablePath: string,
|
||||
processArguments: string[],
|
||||
tempDirectory?: string
|
||||
) {
|
||||
this._executablePath = executablePath;
|
||||
this._processArguments = processArguments;
|
||||
this._tempDirectory = tempDirectory;
|
||||
}
|
||||
|
||||
start(options: LaunchOptions = {}): void {
|
||||
const {
|
||||
handleSIGINT,
|
||||
handleSIGTERM,
|
||||
handleSIGHUP,
|
||||
dumpio,
|
||||
env,
|
||||
pipe,
|
||||
} = options;
|
||||
let stdio: Array<'ignore' | 'pipe'> = ['pipe', 'pipe', 'pipe'];
|
||||
if (pipe) {
|
||||
if (dumpio) stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'];
|
||||
else stdio = ['ignore', 'ignore', 'ignore', 'pipe', 'pipe'];
|
||||
}
|
||||
assert(!this.proc, 'This process has previously been started.');
|
||||
debugLauncher(
|
||||
`Calling ${this._executablePath} ${this._processArguments.join(' ')}`
|
||||
);
|
||||
this.proc = childProcess.spawn(
|
||||
this._executablePath,
|
||||
this._processArguments,
|
||||
{
|
||||
// On non-windows platforms, `detached: true` makes child process a leader of a new
|
||||
// process group, making it possible to kill child process tree with `.kill(-pid)` command.
|
||||
// @see https://nodejs.org/api/child_process.html#child_process_options_detached
|
||||
detached: process.platform !== 'win32',
|
||||
env,
|
||||
stdio,
|
||||
}
|
||||
);
|
||||
if (dumpio) {
|
||||
this.proc.stderr.pipe(process.stderr);
|
||||
this.proc.stdout.pipe(process.stdout);
|
||||
}
|
||||
this._closed = false;
|
||||
this._processClosing = new Promise((fulfill) => {
|
||||
this.proc.once('exit', () => {
|
||||
this._closed = true;
|
||||
// Cleanup as processes exit.
|
||||
if (this._tempDirectory) {
|
||||
removeFolderAsync(this._tempDirectory)
|
||||
.then(() => fulfill())
|
||||
.catch((error) => console.error(error));
|
||||
} else {
|
||||
fulfill();
|
||||
}
|
||||
});
|
||||
});
|
||||
this._listeners = [
|
||||
helper.addEventListener(process, 'exit', this.kill.bind(this)),
|
||||
];
|
||||
if (handleSIGINT)
|
||||
this._listeners.push(
|
||||
helper.addEventListener(process, 'SIGINT', () => {
|
||||
this.kill();
|
||||
process.exit(130);
|
||||
})
|
||||
);
|
||||
if (handleSIGTERM)
|
||||
this._listeners.push(
|
||||
helper.addEventListener(process, 'SIGTERM', this.close.bind(this))
|
||||
);
|
||||
if (handleSIGHUP)
|
||||
this._listeners.push(
|
||||
helper.addEventListener(process, 'SIGHUP', this.close.bind(this))
|
||||
);
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
if (this._closed) return Promise.resolve();
|
||||
helper.removeEventListeners(this._listeners);
|
||||
if (this._tempDirectory) {
|
||||
this.kill();
|
||||
} else if (this.connection) {
|
||||
// Attempt to close the browser gracefully
|
||||
this.connection.send('Browser.close').catch((error) => {
|
||||
debugError(error);
|
||||
this.kill();
|
||||
});
|
||||
}
|
||||
return this._processClosing;
|
||||
}
|
||||
|
||||
kill(): void {
|
||||
helper.removeEventListeners(this._listeners);
|
||||
if (this.proc && this.proc.pid && !this.proc.killed && !this._closed) {
|
||||
try {
|
||||
if (process.platform === 'win32')
|
||||
childProcess.execSync(`taskkill /pid ${this.proc.pid} /T /F`);
|
||||
else process.kill(-this.proc.pid, 'SIGKILL');
|
||||
} catch (error) {
|
||||
// the process might have already stopped
|
||||
}
|
||||
}
|
||||
// Attempt to remove temporary profile directory to avoid littering.
|
||||
try {
|
||||
removeFolder.sync(this._tempDirectory);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async setupConnection(options: {
|
||||
usePipe?: boolean;
|
||||
timeout: number;
|
||||
slowMo: number;
|
||||
preferredRevision: string;
|
||||
}): Promise<Connection> {
|
||||
const { usePipe, timeout, slowMo, preferredRevision } = options;
|
||||
if (!usePipe) {
|
||||
const browserWSEndpoint = await waitForWSEndpoint(
|
||||
this.proc,
|
||||
timeout,
|
||||
preferredRevision
|
||||
);
|
||||
const transport = await WebSocketTransport.create(browserWSEndpoint);
|
||||
this.connection = new Connection(browserWSEndpoint, transport, slowMo);
|
||||
} else {
|
||||
// stdio was assigned during start(), and the 'pipe' option there adds the 4th and 5th items to stdio array
|
||||
const { 3: pipeWrite, 4: pipeRead } = this.proc.stdio;
|
||||
const transport = new PipeTransport(
|
||||
pipeWrite as NodeJS.WritableStream,
|
||||
pipeRead as NodeJS.ReadableStream
|
||||
);
|
||||
this.connection = new Connection('', transport, slowMo);
|
||||
}
|
||||
return this.connection;
|
||||
}
|
||||
}
|
||||
|
||||
function waitForWSEndpoint(
|
||||
browserProcess: childProcess.ChildProcess,
|
||||
timeout: number,
|
||||
preferredRevision: string
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const rl = readline.createInterface({ input: browserProcess.stderr });
|
||||
let stderr = '';
|
||||
const listeners = [
|
||||
helper.addEventListener(rl, 'line', onLine),
|
||||
helper.addEventListener(rl, 'close', () => onClose()),
|
||||
helper.addEventListener(browserProcess, 'exit', () => onClose()),
|
||||
helper.addEventListener(browserProcess, 'error', (error) =>
|
||||
onClose(error)
|
||||
),
|
||||
];
|
||||
const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;
|
||||
|
||||
/**
|
||||
* @param {!Error=} error
|
||||
*/
|
||||
function onClose(error?: Error): void {
|
||||
cleanup();
|
||||
reject(
|
||||
new Error(
|
||||
[
|
||||
'Failed to launch the browser process!' +
|
||||
(error ? ' ' + error.message : ''),
|
||||
stderr,
|
||||
'',
|
||||
'TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md',
|
||||
'',
|
||||
].join('\n')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function onTimeout(): void {
|
||||
cleanup();
|
||||
reject(
|
||||
new TimeoutError(
|
||||
`Timed out after ${timeout} ms while trying to connect to the browser! Only Chrome at revision r${preferredRevision} is guaranteed to work.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function onLine(line: string): void {
|
||||
stderr += line + '\n';
|
||||
const match = line.match(/^DevTools listening on (ws:\/\/.*)$/);
|
||||
if (!match) return;
|
||||
cleanup();
|
||||
resolve(match[1]);
|
||||
}
|
||||
|
||||
function cleanup(): void {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
helper.removeEventListeners(listeners);
|
||||
}
|
||||
});
|
||||
}
|
42
src/launcher/LaunchOptions.ts
Normal file
42
src/launcher/LaunchOptions.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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 { Viewport } from '../PuppeteerViewport';
|
||||
|
||||
export interface ChromeArgOptions {
|
||||
headless?: boolean;
|
||||
args?: string[];
|
||||
userDataDir?: string;
|
||||
devtools?: boolean;
|
||||
}
|
||||
|
||||
export interface LaunchOptions {
|
||||
executablePath?: string;
|
||||
ignoreDefaultArgs?: boolean | string[];
|
||||
handleSIGINT?: boolean;
|
||||
handleSIGTERM?: boolean;
|
||||
handleSIGHUP?: boolean;
|
||||
timeout?: number;
|
||||
dumpio?: boolean;
|
||||
env?: Record<string, string | undefined>;
|
||||
pipe?: boolean;
|
||||
}
|
||||
|
||||
export interface BrowserOptions {
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
defaultViewport?: Viewport;
|
||||
slowMo?: number;
|
||||
}
|
@ -106,7 +106,11 @@ class Source {
|
||||
const fileNames = await readdirAsync(dirPath);
|
||||
const filePaths = fileNames
|
||||
.filter((fileName) => fileName.endsWith(extension))
|
||||
.map((fileName) => path.join(dirPath, fileName));
|
||||
.map((fileName) => path.join(dirPath, fileName))
|
||||
.filter((filePath) => {
|
||||
const stats = fs.lstatSync(filePath);
|
||||
return stats.isDirectory() === false;
|
||||
});
|
||||
return Promise.all(filePaths.map((filePath) => Source.readFile(filePath)));
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,14 @@ async function run() {
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
const checkPublicAPI = require('./check_public_api');
|
||||
const tsSources = await Source.readdir(path.join(PROJECT_DIR, 'src'), 'ts');
|
||||
const tsSources = [
|
||||
/* Source.readdir doesn't deal with nested directories well.
|
||||
* Rather than invest time here when we're going to remove this Doc tooling soon
|
||||
* we'll just list the directories manually.
|
||||
*/
|
||||
...(await Source.readdir(path.join(PROJECT_DIR, 'src'), 'ts')),
|
||||
...(await Source.readdir(path.join(PROJECT_DIR, 'src', 'launcher'), 'ts')),
|
||||
];
|
||||
|
||||
const tsSourcesNoDefinitions = tsSources.filter(
|
||||
(source) => !source.filePath().endsWith('.d.ts')
|
||||
|
Loading…
Reference in New Issue
Block a user