From 7b5a0ba8c38dd9a8363bb9f6fa0e6fa82a452568 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 20 Apr 2020 13:00:10 -0700 Subject: [PATCH] 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 --- .dev-infra.json | 23 +++ dev-infra/BUILD.bazel | 3 +- dev-infra/cli.ts | 2 + dev-infra/commit-message/BUILD.bazel | 4 +- dev-infra/format/BUILD.bazel | 27 +++ dev-infra/format/cli.ts | 45 +++++ dev-infra/format/config.ts | 11 ++ dev-infra/format/format.ts | 130 +++++++++++++ dev-infra/format/run-commands-parallel.ts | 77 ++++++++ dev-infra/pullapprove/BUILD.bazel | 2 +- dev-infra/tmpl-package.json | 4 + .../ts-circular-dependencies/BUILD.bazel | 2 +- dev-infra/utils/BUILD.bazel | 3 +- dev-infra/utils/repo-files.ts | 40 ++++ package.json | 6 +- yarn.lock | 172 +++++++----------- 16 files changed, 435 insertions(+), 116 deletions(-) create mode 100644 dev-infra/format/BUILD.bazel create mode 100644 dev-infra/format/cli.ts create mode 100644 dev-infra/format/config.ts create mode 100644 dev-infra/format/format.ts create mode 100644 dev-infra/format/run-commands-parallel.ts create mode 100644 dev-infra/utils/repo-files.ts diff --git a/.dev-infra.json b/.dev-infra.json index 2e1c7ddc39..5d8ec701d1 100644 --- a/.dev-infra.json +++ b/.dev-infra.json @@ -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" + ] } } diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 57897870bf..b8ca09d8e5 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -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", diff --git a/dev-infra/cli.ts b/dev-infra/cli.ts index 0c30ac4ae6..86eb3ed563 100644 --- a/dev-infra/cli.ts +++ b/dev-infra/cli.ts @@ -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 ', '', tsCircularDependenciesBuilder) .command('pullapprove ', '', buildPullapproveParser) .command('commit-message ', '', buildCommitMessageParser) + .command('format ', '', buildFormatParser) .wrap(120) .strict() .parse(); diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index 1197df1dcc..fbfe3fe234 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -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", diff --git a/dev-infra/format/BUILD.bazel b/dev-infra/format/BUILD.bazel new file mode 100644 index 0000000000..00f8a0136a --- /dev/null +++ b/dev-infra/format/BUILD.bazel @@ -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", + ], +) diff --git a/dev-infra/format/cli.ts b/dev-infra/format/cli.ts new file mode 100644 index 0000000000..2f04f4d466 --- /dev/null +++ b/dev-infra/format/cli.ts @@ -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 ', 'Run the formatter on provided files', {}, ({check, files}) => { + const executionCmd = check ? checkFiles : formatFiles; + executionCmd(files); + }); +} + +if (require.main === module) { + buildFormatParser(yargs).parse(); +} diff --git a/dev-infra/format/config.ts b/dev-infra/format/config.ts new file mode 100644 index 0000000000..a61bd1f349 --- /dev/null +++ b/dev-infra/format/config.ts @@ -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[]; +} diff --git a/dev-infra/format/format.ts b/dev-infra/format/format.ts new file mode 100644 index 0000000000..0f23ecf9b9 --- /dev/null +++ b/dev-infra/format/format.ts @@ -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; +} diff --git a/dev-infra/format/run-commands-parallel.ts b/dev-infra/format/run-commands-parallel.ts new file mode 100644 index 0000000000..77831d38c3 --- /dev/null +++ b/dev-infra/format/run-commands-parallel.ts @@ -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((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(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)); + }); +} diff --git a/dev-infra/pullapprove/BUILD.bazel b/dev-infra/pullapprove/BUILD.bazel index 36cfb2aab2..b27e1df543 100644 --- a/dev-infra/pullapprove/BUILD.bazel +++ b/dev-infra/pullapprove/BUILD.bazel @@ -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", diff --git a/dev-infra/tmpl-package.json b/dev-infra/tmpl-package.json index 44534e250a..364513cc30 100644 --- a/dev-infra/tmpl-package.json +++ b/dev-infra/tmpl-package.json @@ -10,8 +10,12 @@ }, "peerDependencies": { "chalk": "", + "clang-format": "", + "cli-progress": "", "glob": "", + "inquirer": "", "minimatch": "", + "multimatch": "", "shelljs": "", "typescript": "", "yaml": "", diff --git a/dev-infra/ts-circular-dependencies/BUILD.bazel b/dev-infra/ts-circular-dependencies/BUILD.bazel index d438b66bd5..6b87b1e835 100644 --- a/dev-infra/ts-circular-dependencies/BUILD.bazel +++ b/dev-infra/ts-circular-dependencies/BUILD.bazel @@ -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", diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 35637ade55..648ad4adf1 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -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__"], diff --git a/dev-infra/utils/repo-files.ts b/dev-infra/utils/repo-files.ts new file mode 100644 index 0000000000..b22e2d1859 --- /dev/null +++ b/dev-infra/utils/repo-files.ts @@ -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); +} diff --git a/package.json b/package.json index d1224e2bc3..e4e739baf7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index 625060b6ed..5f0489de2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==