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==