feat(dev-infra): create format tool in @angular/dev-infra-private (#36726)

Previously we used gulp to run our formatter, currently clang-format,
across our repository.  This new tool within ng-dev allows us to
migrate away from our gulp based solution as our gulp solution had
issue with memory pressure and would cause OOM errors with too large
of change sets.

PR Close #36726
This commit is contained in:
Joey Perrott 2020-04-20 13:00:10 -07:00 committed by Andrew Kushnir
parent ad8c4cdd75
commit 7b5a0ba8c3
16 changed files with 435 additions and 116 deletions

View File

@ -43,5 +43,28 @@
"ve",
"zone.js"
]
},
"format": {
"matchers": [
"dev-infra/**/*.{js,ts}",
"packages/**/*.{js,ts}",
"!packages/zone.js",
"!packages/common/locales/**/*.{js,ts}",
"!packages/common/src/i18n/available_locales.ts",
"!packages/common/src/i18n/currencies.ts",
"!packages/common/src/i18n/locale_en.ts",
"modules/benchmarks/**/*.{js,ts}",
"modules/e2e_util/**/*.{js,ts}",
"modules/playground/**/*.{js,ts}",
"tools/**/*.{js,ts}",
"!tools/gulp-tasks/cldr/extract.js",
"!tools/public_api_guard/**/*.d.ts",
"!tools/ts-api-guardian/test/fixtures/**",
"./*.{js,ts}",
"!**/node_modules/**",
"!**/dist/**",
"!**/built/**",
"!shims_for_IE.js"
]
}
}

View File

@ -9,9 +9,10 @@ ts_library(
module_name = "@angular/dev-infra-private",
deps = [
"//dev-infra/commit-message",
"//dev-infra/format",
"//dev-infra/pullapprove",
"//dev-infra/ts-circular-dependencies",
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/node",
"@npm//@types/yargs",
"@npm//yargs",

View File

@ -10,6 +10,7 @@ import * as yargs from 'yargs';
import {tsCircularDependenciesBuilder} from './ts-circular-dependencies/index';
import {buildPullapproveParser} from './pullapprove/cli';
import {buildCommitMessageParser} from './commit-message/cli';
import {buildFormatParser} from './format/cli';
yargs.scriptName('ng-dev')
.demandCommand()
@ -17,6 +18,7 @@ yargs.scriptName('ng-dev')
.command('ts-circular-deps <command>', '', tsCircularDependenciesBuilder)
.command('pullapprove <command>', '', buildPullapproveParser)
.command('commit-message <command>', '', buildCommitMessageParser)
.command('format <command>', '', buildFormatParser)
.wrap(120)
.strict()
.parse();

View File

@ -13,7 +13,7 @@ ts_library(
module_name = "@angular/dev-infra-private/commit-message",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/node",
"@npm//@types/shelljs",
"@npm//@types/yargs",
@ -29,7 +29,7 @@ ts_library(
srcs = ["validate.spec.ts"],
deps = [
":commit-message",
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/events",
"@npm//@types/jasmine",
"@npm//@types/node",

View File

@ -0,0 +1,27 @@
load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library(
name = "format",
srcs = [
"cli.ts",
"config.ts",
"format.ts",
"run-commands-parallel.ts",
],
module_name = "@angular/dev-infra-private/format",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils",
"@npm//@types/cli-progress",
"@npm//@types/inquirer",
"@npm//@types/node",
"@npm//@types/shelljs",
"@npm//@types/yargs",
"@npm//cli-progress",
"@npm//inquirer",
"@npm//multimatch",
"@npm//shelljs",
"@npm//tslib",
"@npm//yargs",
],
)

45
dev-infra/format/cli.ts Normal file
View File

@ -0,0 +1,45 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as yargs from 'yargs';
import {allChangedFilesSince, allFiles} from '../utils/repo-files';
import {checkFiles, formatFiles} from './format';
/** Build the parser for the format commands. */
export function buildFormatParser(localYargs: yargs.Argv) {
return localYargs.help()
.strict()
.demandCommand()
.option('check', {
type: 'boolean',
default: process.env['CI'] ? true : false,
description: 'Run the formatter to check formatting rather than updating code format'
})
.command(
'all', 'Run the formatter on all files in the repository', {},
({check}) => {
const executionCmd = check ? checkFiles : formatFiles;
executionCmd(allFiles());
})
.command(
'changed [shaOrRef]', 'Run the formatter on files changed since the provided sha/ref', {},
({shaOrRef, check}) => {
const sha = shaOrRef || 'master';
const executionCmd = check ? checkFiles : formatFiles;
executionCmd(allChangedFilesSince(sha));
})
.command('files <files..>', 'Run the formatter on provided files', {}, ({check, files}) => {
const executionCmd = check ? checkFiles : formatFiles;
executionCmd(files);
});
}
if (require.main === module) {
buildFormatParser(yargs).parse();
}

View File

@ -0,0 +1,11 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export interface FormatConfig {
matchers: string[];
}

130
dev-infra/format/format.ts Normal file
View File

@ -0,0 +1,130 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {prompt} from 'inquirer';
import * as multimatch from 'multimatch';
import {join} from 'path';
import {getAngularDevConfig, getRepoBaseDir} from '../utils/config';
import {FormatConfig} from './config';
import {runInParallel} from './run-commands-parallel';
/** By default, run the formatter on all javascript and typescript files. */
const DEFAULT_MATCHERS = ['**/*.{t,j}s'];
/**
* Format provided files in place.
*/
export async function formatFiles(unfilteredFiles: string[]) {
// Whether any files failed to format.
let formatFailed = false;
// All files which formatting should be applied to.
const files = filterFilesByMatchers(unfilteredFiles);
console.info(`Formatting ${files.length} file(s)`);
// Run the formatter to format the files in place, split across (number of available
// cpu threads - 1) processess. The task is done in multiple processess to speed up
// the overall time of the task, as running across entire repositories takes a large
// amount of time.
// As a data point for illustration, using 8 process rather than 1 cut the execution
// time from 276 seconds to 39 seconds for the same 2700 files
await runInParallel(files, `${getFormatterBinary()} -i -style=file`, (file, code, _, stderr) => {
if (code !== 0) {
formatFailed = true;
console.error(`Error running clang-format on: ${file}`);
console.error(stderr);
console.error();
}
});
// The process should exit as a failure if any of the files failed to format.
if (formatFailed) {
console.error(`Formatting failed, see errors above for more information.`);
process.exit(1);
}
console.info(`√ Formatting complete.`);
process.exit(0);
}
/**
* Check provided files for formatting correctness.
*/
export async function checkFiles(unfilteredFiles: string[]) {
// All files which formatting should be applied to.
const files = filterFilesByMatchers(unfilteredFiles);
// Files which are currently not formatted correctly.
const failures: string[] = [];
console.info(`Checking format of ${files.length} file(s)`);
// Run the formatter to check the format of files, split across (number of available
// cpu threads - 1) processess. The task is done in multiple processess to speed up
// the overall time of the task, as running across entire repositories takes a large
// amount of time.
// As a data point for illustration, using 8 process rather than 1 cut the execution
// time from 276 seconds to 39 seconds for the same 2700 files.
await runInParallel(files, `${getFormatterBinary()} --Werror -n -style=file`, (file, code) => {
// Add any files failing format checks to the list.
if (code !== 0) {
failures.push(file);
}
});
if (failures.length) {
// Provide output expressing which files are failing formatting.
console.group('\nThe following files are out of format:');
for (const file of failures) {
console.info(` - ${file}`);
}
console.groupEnd();
console.info();
// If the command is run in a non-CI environment, prompt to format the files immediately.
let runFormatter = false;
if (!process.env['CI']) {
runFormatter = (await prompt({
type: 'confirm',
name: 'runFormatter',
message: 'Format the files now?',
})).runFormatter;
}
if (runFormatter) {
// Format the failing files as requested.
await formatFiles(failures);
process.exit(0);
} else {
// Inform user how to format files in the future.
console.info();
console.info(`To format the failing file run the following command:`);
console.info(` yarn ng-dev format files ${failures.join(' ')}`);
process.exit(1);
}
} else {
console.info('√ All files correctly formatted.');
process.exit(0);
}
}
/** Get the full path of the formatter binary to execute. */
function getFormatterBinary() {
return join(getRepoBaseDir(), 'node_modules/.bin/clang-format');
}
/** Filter a list of files to only contain files which are expected to be formatted. */
function filterFilesByMatchers(allFiles: string[]) {
const matchers =
getAngularDevConfig<'format', FormatConfig>().format.matchers || DEFAULT_MATCHERS;
const files = multimatch(allFiles, matchers, {dot: true});
console.info(`Formatting enforced on ${files.length} of ${allFiles.length} file(s)`);
return files;
}

View File

@ -0,0 +1,77 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Bar} from 'cli-progress';
import {cpus} from 'os';
import {exec} from 'shelljs';
const AVAILABLE_THREADS = Math.max(cpus().length - 1, 1);
type CallbackFunction = (file: string, code?: number, stdout?: string, stderr?: string) => void;
/**
* Run the provided commands in parallel for each provided file.
*
* A promise is returned, completed when the command has completed running for each file.
*/
export function runInParallel(providedFiles: string[], cmd: string, callback: CallbackFunction) {
return new Promise<void>((resolve) => {
if (providedFiles.length === 0) {
return resolve();
}
// The progress bar instance to use for progress tracking.
const progressBar =
new Bar({format: `[{bar}] ETA: {eta}s | {value}/{total} files`, clearOnComplete: true});
// A local copy of the files to run the command on.
const files = providedFiles.slice();
// An array to represent the current usage state of each of the threads for parallelization.
const threads = new Array<boolean>(AVAILABLE_THREADS).fill(false);
// Recursively run the command on the next available file from the list using the provided
// thread.
function runCommandInThread(thread: number) {
// Get the next file.
const file = files.pop();
// If no file was pulled from the array, return as there are no more files to run against.
if (!file) {
return;
}
exec(
`${cmd} ${file}`,
{async: true, silent: true},
(code, stdout, stderr) => {
// Run the provided callback function.
callback(file, code, stdout, stderr);
// Note in the progress bar another file being completed.
progressBar.increment(1);
// If more files exist in the list, run again to work on the next file,
// using the same slot.
if (files.length) {
return runCommandInThread(thread);
}
// If not more files are available, mark the thread as unused.
threads[thread] = false;
// If all of the threads are false, as they are unused, mark the progress bar
// completed and resolve the promise.
if (threads.every(active => !active)) {
progressBar.stop();
resolve();
}
},
);
// Mark the thread as in use as the command execution has been started.
threads[thread] = true;
}
// Start the progress bar
progressBar.start(files.length, 0);
// Start running the command on files from the least in each available thread.
threads.forEach((_, idx) => runCommandInThread(idx));
});
}

View File

@ -13,7 +13,7 @@ ts_library(
module_name = "@angular/dev-infra-private/pullapprove",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/minimatch",
"@npm//@types/node",
"@npm//@types/shelljs",

View File

@ -10,8 +10,12 @@
},
"peerDependencies": {
"chalk": "<from-root>",
"clang-format": "<from-root>",
"cli-progress": "<from-root>",
"glob": "<from-root>",
"inquirer": "<from-root>",
"minimatch": "<from-root>",
"multimatch": "<from-root>",
"shelljs": "<from-root>",
"typescript": "<from-root>",
"yaml": "<from-root>",

View File

@ -6,7 +6,7 @@ ts_library(
module_name = "@angular/dev-infra-private/ts-circular-dependencies",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/glob",
"@npm//@types/node",
"@npm//@types/yargs",

View File

@ -1,9 +1,10 @@
load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library(
name = "config",
name = "utils",
srcs = [
"config.ts",
"repo-files.ts",
],
module_name = "@angular/dev-infra-private/utils",
visibility = ["//dev-infra:__subpackages__"],

View File

@ -0,0 +1,40 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {exec} from 'shelljs';
import {getRepoBaseDir} from './config';
/**
* A list of all files currently in the repo which have been modified since the provided sha.
*
* git diff
* Deleted files (--diff-filter=d) are not included as they are not longer present in the repo
* and can not be checked anymore.
*
* git ls-files
* Untracked files (--others), which are not matched by .gitignore (--exclude-standard)
* as they are expected to become tracked files.
*/
export function allChangedFilesSince(sha = 'HEAD') {
const diffFiles = gitOutputAsArray(`git diff --name-only --diff-filter=d ${sha}`);
const untrackedFiles = gitOutputAsArray(`git ls-files --others --exclude-standard`);
// Use a set to deduplicate the list as its possible for a file to show up in both lists.
return Array.from(new Set([...diffFiles, ...untrackedFiles]));
}
export function allFiles() {
return gitOutputAsArray(`git ls-files`);
}
function gitOutputAsArray(cmd: string) {
return exec(cmd, {cwd: getRepoBaseDir(), silent: true})
.split('\n')
.map(x => x.trim())
.filter(x => !!x);
}

View File

@ -70,7 +70,7 @@
"@types/diff": "^3.5.1",
"@types/fs-extra": "4.0.2",
"@types/hammerjs": "2.0.35",
"@types/inquirer": "^0.0.44",
"@types/inquirer": "^6.5.0",
"@types/jasmine": "3.5.10",
"@types/jasminewd2": "^2.0.8",
"@types/minimist": "^1.2.0",
@ -150,6 +150,7 @@
"@bazel/buildifier": "^0.29.0",
"@bazel/ibazel": "^0.12.3",
"@octokit/graphql": "^4.3.1",
"@types/cli-progress": "^3.4.2",
"@types/json5": "^0.0.30",
"@types/minimist": "^1.2.0",
"@yarnpkg/lockfile": "^1.1.0",
@ -159,6 +160,7 @@
"cldr": "4.10.0",
"cldr-data": "36.0.0",
"cldrjs": "0.5.0",
"cli-progress": "^3.7.0",
"conventional-changelog": "^2.0.3",
"entities": "1.1.1",
"firebase-tools": "^7.11.0",
@ -171,11 +173,13 @@
"gulp-git": "^2.7.0",
"gulp-tslint": "8.1.2",
"husky": "^4.2.3",
"inquirer": "^7.1.0",
"jpm": "1.3.1",
"json5": "^2.1.2",
"karma-browserstack-launcher": "^1.3.0",
"karma-sauce-launcher": "^2.0.2",
"madge": "^3.6.0",
"multimatch": "^4.0.0",
"mutation-observer": "^1.0.3",
"rewire": "2.5.2",
"sauce-connect": "https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz",

172
yarn.lock
View File

@ -1227,6 +1227,13 @@
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.11.tgz#d3614d6c5f500142358e6ed24e1bf16657536c50"
integrity sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==
"@types/cli-progress@^3.4.2":
version "3.4.2"
resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.4.2.tgz#03dfa3d507e9dda85ba4a14006c37e8b7094c605"
integrity sha512-9Rlk664JggbgDLDMCM/8HziTh6ZU2IBLVS2/Kkh3T/TNVlpWlwgLrFl7kDyQBOlX1pofPM05ZKG/GyuULJ0FfA==
dependencies:
"@types/node" "*"
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@ -1292,13 +1299,13 @@
resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.35.tgz#7b7c950c7d54593e23bffc8d2b4feba9866a7277"
integrity sha512-4mUIMSZ2U4UOWq1b+iV7XUTE4w+Kr3x+Zb/Qz5ROO6BTZLw2c8/ftjq0aRgluguLs4KRuBnrOy/s389HVn1/zA==
"@types/inquirer@^0.0.44":
version "0.0.44"
resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-0.0.44.tgz#60ce954581cfdf44ad3899ec4cdc5fbe3fef1694"
integrity sha512-ugbhy1yBtCz5iTWYF+AGRS/UcMcWicdyHhxl9VaeFYc3ueg0CCssthQLB3rIcIOeGtfG6WPEvHdLu/IjKYfefg==
"@types/inquirer@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-6.5.0.tgz#b83b0bf30b88b8be7246d40e51d32fe9d10e09be"
integrity sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==
dependencies:
"@types/rx" "*"
"@types/through" "*"
rxjs "^6.4.0"
"@types/jasmine@*", "@types/jasmine@3.5.10":
version "3.5.10"
@ -1327,7 +1334,7 @@
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73"
integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=
"@types/minimatch@*":
"@types/minimatch@*", "@types/minimatch@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
@ -1384,107 +1391,6 @@
dependencies:
"@types/node" "*"
"@types/rx-core-binding@*":
version "4.0.4"
resolved "https://registry.yarnpkg.com/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz#d969d32f15a62b89e2862c17b3ee78fe329818d3"
integrity sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ==
dependencies:
"@types/rx-core" "*"
"@types/rx-core@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/rx-core/-/rx-core-4.0.3.tgz#0b3354b1238cedbe2b74f6326f139dbc7a591d60"
integrity sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA=
"@types/rx-lite-aggregates@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz#6efb2b7f3d5f07183a1cb2bd4b1371d7073384c2"
integrity sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg==
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-async@*":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz#27fbf0caeff029f41e2d2aae638b05e91ceb600c"
integrity sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw==
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-backpressure@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz#05abb19bdf87cc740196c355e5d0b37bb50b5d56"
integrity sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA==
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-coincidence@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz#80bd69acc4054a15cdc1638e2dc8843498cd85c0"
integrity sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ==
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-experimental@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz#c532f5cbdf3f2c15da16ded8930d1b2984023cbd"
integrity sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0=
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-joinpatterns@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz#f70fe370518a8432f29158cc92ffb56b4e4afc3e"
integrity sha1-9w/jcFGKhDLykVjMkv+1a05K/D4=
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-testing@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz#21b19d11f4dfd6ffef5a9d1648e9c8879bfe21e9"
integrity sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek=
dependencies:
"@types/rx-lite-virtualtime" "*"
"@types/rx-lite-time@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz#0eda65474570237598f3448b845d2696f2dbb1c4"
integrity sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw==
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite-virtualtime@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz#4b30cacd0fe2e53af29f04f7438584c7d3959537"
integrity sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg==
dependencies:
"@types/rx-lite" "*"
"@types/rx-lite@*":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@types/rx-lite/-/rx-lite-4.0.6.tgz#3c02921c4244074234f26b772241bcc20c18c253"
integrity sha512-oYiDrFIcor9zDm0VDUca1UbROiMYBxMLMaM6qzz4ADAfOmA9r1dYEcAFH+2fsPI5BCCjPvV9pWC3X3flbrvs7w==
dependencies:
"@types/rx-core" "*"
"@types/rx-core-binding" "*"
"@types/rx@*":
version "4.1.1"
resolved "https://registry.yarnpkg.com/@types/rx/-/rx-4.1.1.tgz#598fc94a56baed975f194574e0f572fd8e627a48"
integrity sha1-WY/JSla67ZdfGUV04PVy/Y5iekg=
dependencies:
"@types/rx-core" "*"
"@types/rx-core-binding" "*"
"@types/rx-lite" "*"
"@types/rx-lite-aggregates" "*"
"@types/rx-lite-async" "*"
"@types/rx-lite-backpressure" "*"
"@types/rx-lite-coincidence" "*"
"@types/rx-lite-experimental" "*"
"@types/rx-lite-joinpatterns" "*"
"@types/rx-lite-testing" "*"
"@types/rx-lite-time" "*"
"@types/rx-lite-virtualtime" "*"
"@types/selenium-webdriver@3.0.7":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.7.tgz#5d3613d1ab3ca08b74d19683a3a7c573129ab18f"
@ -2178,6 +2084,11 @@ array-differ@^1.0.0:
resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031"
integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=
array-differ@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b"
integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==
array-each@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f"
@ -2240,6 +2151,11 @@ array-union@^1.0.1:
dependencies:
array-uniq "^1.0.1"
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
array-uniq@^1.0.1, array-uniq@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
@ -2260,7 +2176,7 @@ arrify@^1.0.0, arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
arrify@^2.0.0:
arrify@^2.0.0, arrify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
@ -3352,6 +3268,14 @@ cli-cursor@^3.1.0:
dependencies:
restore-cursor "^3.1.0"
cli-progress@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.7.0.tgz#8983a67ab692aad8598511b9d9e3b35e2dead43e"
integrity sha512-xo2HeQ3vNyAO2oYF5xfrk5YM6jzaDNEbeJRLAQir6QlH54g4f6AXW+fLyJ/f12gcTaCbJznsOdQcr/yusp/Kjg==
dependencies:
colors "^1.1.2"
string-width "^4.2.0"
cli-spinners@^2.0.0, cli-spinners@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77"
@ -7376,6 +7300,25 @@ inquirer@7.0.0:
strip-ansi "^5.1.0"
through "^2.3.6"
inquirer@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==
dependencies:
ansi-escapes "^4.2.1"
chalk "^3.0.0"
cli-cursor "^3.1.0"
cli-width "^2.0.0"
external-editor "^3.0.3"
figures "^3.0.0"
lodash "^4.17.15"
mute-stream "0.0.8"
run-async "^2.4.0"
rxjs "^6.5.3"
string-width "^4.1.0"
strip-ansi "^6.0.0"
through "^2.3.6"
inquirer@~6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7"
@ -9685,6 +9628,17 @@ multimatch@^2.0.0:
arrify "^1.0.0"
minimatch "^3.0.0"
multimatch@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3"
integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==
dependencies:
"@types/minimatch" "^3.0.3"
array-differ "^3.0.0"
array-union "^2.1.0"
arrify "^2.0.1"
minimatch "^3.0.4"
multipipe@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b"
@ -12256,7 +12210,7 @@ rsvp@^3.6.2:
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==
run-async@^2.2.0:
run-async@^2.2.0, run-async@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8"
integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==