From 7a6533efd3aa2df4c9ac5a0a3479461bfa8f813e Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Mon, 21 Aug 2023 14:59:12 -0600 Subject: [PATCH 01/55] chore: v3 prerelease branch --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e2dc35fee..d78849297 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "2.11.8", + "version": "3.0.0-beta.0", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { @@ -120,4 +120,4 @@ "test:perf": "ts-node test/perf/parser.perf.ts" }, "types": "lib/index.d.ts" -} \ No newline at end of file +} From 80915eb51241ad085ffd5b221e4e68960eefadef Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Mon, 21 Aug 2023 21:05:11 +0000 Subject: [PATCH 02/55] chore(release): 3.0.0-beta.1 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d78849297..340159049 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.0", + "version": "3.0.0-beta.1", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From b9c2da4055174cb2923c7b16c3d462aec3270368 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 09:53:35 -0700 Subject: [PATCH 03/55] feat: ESM support (#759) * fix: set moduleResolution to Node16 * fix: use file urls * chore: add types * feat: support linking CJS plugins into ESM plugins * chore: integration tests for module interoperability * test: add CJS/ESM interoperability tests * fix: e2e tests * test: isolate cache, config, and data dir for e2e tests * test: test bug * test: make less noisy * test: esm/cjs hooks * chore: parallelize e2e tests * chore: typo * test: parallelize e2e tests * test: no more hanging tests * chore: un-parallelize esm-cjs tests * chore: add DEBUG to esm-cjs interop tests * chore: add DEBUG to esm-cjs interop tests * chore: try enabling debug again * test: fix node 20 tests * chore: update DEBUG env var * chore: debug tests * chore: debug tests * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: use path.join * test: stop using replace-in-file * chore: more test debugging * fix: use path.join when registering ts-node * test: run in parallel * test: run tests serially * fix: dont remove undefined values from tsconfig * fix: further isolate ts-configs * feat: throw error when loading ESM paths from linked plugins * fix: default esm to true * test: compilation errors * feat: better developer experience * fix: add getPluginsList * chore(release): 2.11.9 [skip ci] * fix: add getPluginsList to Config interface * chore(release): 2.11.10 [skip ci] --------- Co-authored-by: svc-cli-bot --- .github/workflows/test.yml | 23 ++ CHANGELOG.md | 18 ++ package.json | 12 +- src/config/config.ts | 11 + src/config/plugin.ts | 5 +- src/config/ts-node.ts | 136 ++++++--- src/errors/errors/cli.ts | 2 + src/errors/index.ts | 7 + src/interfaces/config.ts | 1 + src/interfaces/pjson.ts | 1 + src/interfaces/plugin.ts | 4 + src/interfaces/ts-config.ts | 9 + src/main.ts | 29 +- src/module-loader.ts | 18 +- test/config/config.flexible.test.ts | 2 + test/config/config.test.ts | 2 + test/integration/esm-cjs.ts | 421 ++++++++++++++++++++++++++++ test/integration/plugins.e2e.ts | 158 +++++------ test/integration/sf.e2e.ts | 18 +- test/integration/util.ts | 142 +++++++--- yarn.lock | 201 ++++++------- 21 files changed, 904 insertions(+), 316 deletions(-) create mode 100644 test/integration/esm-cjs.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5263d1c1..68f90185d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,6 +52,29 @@ jobs: - run: yarn install --network-timeout 600000 - run: yarn build - run: yarn test:e2e + esm-cjs-interop: + needs: linux-unit-tests + strategy: + matrix: + os: ["ubuntu-latest", "windows-latest"] + node_version: [lts/-1, lts/*, latest] + exclude: + - os: windows-latest + node_version: lts/* + - os: windows-latest + node_version: lts/-1 + fail-fast: false + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node_version }} + cache: yarn + - run: yarn install --network-timeout 600000 + - run: yarn build + - run: yarn test:esm-cjs nuts: needs: linux-unit-tests uses: salesforcecli/github-workflows/.github/workflows/externalNut.yml@main diff --git a/CHANGELOG.md b/CHANGELOG.md index ac5e5ece4..13f803a38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## [2.11.10](https://github.com/oclif/core/compare/2.11.9...2.11.10) (2023-08-23) + + +### Bug Fixes + +* add getPluginsList to Config interface ([cba7a75](https://github.com/oclif/core/commit/cba7a755cf5d0ff4886edd0db33d911441979984)) + + + +## [2.11.9](https://github.com/oclif/core/compare/2.11.8...2.11.9) (2023-08-23) + + +### Bug Fixes + +* add getPluginsList ([b01083f](https://github.com/oclif/core/commit/b01083fb7132f3b3b35b98a6d43996b1713ca5ef)) + + + ## [2.11.8](https://github.com/oclif/core/compare/2.11.7...2.11.8) (2023-08-08) diff --git a/package.json b/package.json index 340159049..e1fb9395b 100644 --- a/package.json +++ b/package.json @@ -66,13 +66,14 @@ "chai": "^4.3.7", "chai-as-promised": "^7.1.1", "commitlint": "^12.1.4", + "cross-env": "^7.0.3", "eslint": "^7.32.0", "eslint-config-oclif": "^4.0.0", "eslint-config-oclif-typescript": "^1.0.3", "fancy-test": "^2.0.16", "globby": "^11.1.0", "husky": "6", - "mocha": "^8.4.0", + "mocha": "^10.2.0", "nock": "^13.3.0", "proxyquire": "^2.1.3", "shelljs": "^0.8.5", @@ -110,14 +111,15 @@ "scripts": { "build": "shx rm -rf lib && tsc", "commitlint": "commitlint", + "compile": "tsc", "lint": "eslint . --ext .ts --config .eslintrc", "posttest": "yarn lint", - "compile": "tsc", "prepack": "yarn run build", - "test": "mocha --forbid-only \"test/**/*.test.ts\"", - "test:e2e": "mocha --forbid-only \"test/**/*.e2e.ts\" --timeout 1200000", "pretest": "yarn build --noEmit && tsc -p test --noEmit --skipLibCheck", - "test:perf": "ts-node test/perf/parser.perf.ts" + "test:e2e": "mocha --forbid-only \"test/**/*.e2e.ts\" --parallel --timeout 1200000", + "test:esm-cjs": "cross-env DEBUG=e2e:* ts-node test/integration/esm-cjs.ts", + "test:perf": "ts-node test/perf/parser.perf.ts", + "test": "mocha --forbid-only \"test/**/*.test.ts\"" }, "types": "lib/index.d.ts" } diff --git a/src/config/config.ts b/src/config/config.ts index 078069583..0e3382754 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -111,6 +111,8 @@ export class Config implements IConfig { private _commandIDs!: string[] + private static _rootPlugin: Plugin.Plugin + constructor(public options: Options) {} static async load(opts: LoadOptions = module.filename || __dirname): Promise { @@ -127,11 +129,16 @@ export class Config implements IConfig { return config } + static get rootPlugin(): Plugin.Plugin | undefined { + return Config._rootPlugin + } + // eslint-disable-next-line complexity public async load(): Promise { settings.performanceEnabled = (settings.performanceEnabled === undefined ? this.options.enablePerf : settings.performanceEnabled) ?? false const plugin = new Plugin.Plugin({root: this.options.root}) await plugin.load() + Config._rootPlugin = plugin this.plugins.push(plugin) this.root = plugin.root this.pjson = plugin.pjson @@ -544,6 +551,10 @@ export class Config implements IConfig { return url.toString() } + public getPluginsList(): IPlugin[] { + return this.plugins + } + protected dir(category: 'cache' | 'data' | 'config'): string { const base = process.env[`XDG_${category.toUpperCase()}_HOME`] || (this.windows && process.env.LOCALAPPDATA) || diff --git a/src/config/plugin.ts b/src/config/plugin.ts index 0320c5092..1a6d253cf 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -105,6 +105,8 @@ export class Plugin implements IPlugin { type!: string + moduleType!: 'module' | 'commonjs' + root!: string alias!: string @@ -150,6 +152,7 @@ export class Plugin implements IPlugin { this.root = root this._debug('reading %s plugin %s', this.type, root) this.pjson = await loadJSON(path.join(root, 'package.json')) + this.moduleType = this.pjson.type === 'module' ? 'module' : 'commonjs' this.name = this.pjson.name this.alias = this.options.name ?? this.pjson.name const pjsonPath = path.join(root, 'package.json') @@ -185,7 +188,7 @@ export class Plugin implements IPlugin { public get commandsDir(): string | undefined { if (this._commandsDir) return this._commandsDir - this._commandsDir = tsPath(this.root, this.pjson.oclif.commands, this.type) + this._commandsDir = tsPath(this.root, this.pjson.oclif.commands, this) return this._commandsDir } diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index eb35f6840..478f5e2dc 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -2,24 +2,27 @@ import * as fs from 'fs' import * as path from 'path' import * as TSNode from 'ts-node' -import {TSConfig} from '../interfaces/ts-config' +import {TSConfig, Plugin} from '../interfaces' import {settings} from '../settings' import {isProd} from '../util' import {Debug} from './util' +import {Config} from './config' +import {memoizedWarn} from '../errors' // eslint-disable-next-line new-cap const debug = Debug('ts-node') -const TYPE_ROOTS = [`${__dirname}/../node_modules/@types`] -const ROOT_DIRS: string[] = [] +const TS_CONFIGS: Record = {} +const REGISTERED = new Set() function loadTSConfig(root: string): TSConfig | undefined { + if (TS_CONFIGS[root]) return TS_CONFIGS[root] const tsconfigPath = path.join(root, 'tsconfig.json') let typescript: typeof import('typescript') | undefined try { typescript = require('typescript') } catch { try { - typescript = require(root + '/node_modules/typescript') + typescript = require(path.join(root, 'node_modules', 'typescript')) } catch {} } @@ -34,47 +37,70 @@ function loadTSConfig(root: string): TSConfig | undefined { 'did not contain a "compilerOptions" section.') } + TS_CONFIGS[root] = tsconfig return tsconfig } } -function registerTSNode(root: string) { +function registerTSNode(root: string): TSConfig | undefined { const tsconfig = loadTSConfig(root) if (!tsconfig) return + if (REGISTERED.has(root)) return tsconfig debug('registering ts-node at', root) const tsNodePath = require.resolve('ts-node', {paths: [root, __dirname]}) + debug('ts-node path:', tsNodePath) const tsNode: typeof TSNode = require(tsNodePath) - TYPE_ROOTS.push(`${root}/node_modules/@types`) + const typeRoots = [ + path.join(root, 'node_modules', '@types'), + ] + + const rootDirs: string[] = [] if (tsconfig.compilerOptions.rootDirs) { - ROOT_DIRS.push(...tsconfig.compilerOptions.rootDirs.map(r => path.join(root, r))) + for (const r of tsconfig.compilerOptions.rootDirs) { + rootDirs.push(path.join(root, r)) + } + } else if (tsconfig.compilerOptions.rootDir) { + rootDirs.push(path.join(root, tsconfig.compilerOptions.rootDir)) } else { - ROOT_DIRS.push(`${root}/src`) + rootDirs.push(path.join(root, 'src')) } - const cwd = process.cwd() - try { - process.chdir(root) - tsNode.register({ - skipProject: true, - transpileOnly: true, - compilerOptions: { - esModuleInterop: tsconfig.compilerOptions.esModuleInterop, - target: tsconfig.compilerOptions.target || 'es2017', - experimentalDecorators: tsconfig.compilerOptions.experimentalDecorators || false, - emitDecoratorMetadata: tsconfig.compilerOptions.emitDecoratorMetadata || false, - module: 'commonjs', - sourceMap: true, - rootDirs: ROOT_DIRS, - typeRoots: TYPE_ROOTS, - jsx: 'react', - }, - }) - return tsconfig - } finally { - process.chdir(cwd) + const conf: TSNode.RegisterOptions = { + compilerOptions: { + esModuleInterop: tsconfig.compilerOptions.esModuleInterop, + target: tsconfig.compilerOptions.target ?? 'es2019', + experimentalDecorators: tsconfig.compilerOptions.experimentalDecorators ?? false, + emitDecoratorMetadata: tsconfig.compilerOptions.emitDecoratorMetadata ?? false, + module: tsconfig.compilerOptions.module ?? 'commonjs', + sourceMap: tsconfig.compilerOptions.sourceMap ?? true, + rootDirs, + typeRoots, + }, + skipProject: true, + transpileOnly: true, + esm: tsconfig['ts-node']?.esm ?? true, + scope: true, + scopeDir: root, + cwd: root, + experimentalSpecifierResolution: tsconfig['ts-node']?.experimentalSpecifierResolution ?? 'explicit', + } + + if (tsconfig.compilerOptions.moduleResolution) { + // @ts-expect-error TSNode.RegisterOptions.compilerOptions is typed as a plain object + conf.compilerOptions.moduleResolution = tsconfig.compilerOptions.moduleResolution } + + if (tsconfig.compilerOptions.jsx) { + // @ts-expect-error TSNode.RegisterOptions.compilerOptions is typed as a plain object + conf.compilerOptions.jsx = tsconfig.compilerOptions.jsx + } + + tsNode.register(conf) + REGISTERED.add(root) + + return tsconfig } /** @@ -82,23 +108,53 @@ function registerTSNode(root: string) { * this is for developing typescript plugins/CLIs * if there is a tsconfig and the original sources exist, it attempts to require ts-node */ -export function tsPath(root: string, orig: string, type?: string): string -export function tsPath(root: string, orig: string | undefined, type?: string): string | undefined -export function tsPath(root: string, orig: string | undefined, type?: string): string | undefined { +export function tsPath(root: string, orig: string, plugin: Plugin): string +export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined +// eslint-disable-next-line complexity +export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined { if (!orig) return orig orig = orig.startsWith(root) ? orig : path.join(root, orig) - const skipTSNode = - // the CLI specifically turned it off - (settings.tsnodeEnabled === false) || - // the CLI didn't specify ts-node and it is production - (settings.tsnodeEnabled === undefined && isProd()) + // NOTE: The order of these checks matter! - // We always want to load the tsconfig for linked plugins. - if (skipTSNode && type !== 'link') return orig + if (settings.tsnodeEnabled === false) { + debug(`Skipping ts-node registration for ${root} because tsNodeEnabled is explicitly set to false`) + return orig + } + + // Skip ts-node registration if plugin is an ESM plugin executing from a CJS plugin + if (plugin?.moduleType === 'module' && Config.rootPlugin?.moduleType === 'commonjs') { + debug(`Skipping ts-node registration for ${root} because it's an ESM module but the root plugin is CommonJS`) + if (plugin.type === 'link') + memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-compiled from a CommonJS root plugin. Existing compiled source will be used instead.`) + + return orig + } + + // If plugin is an ESM plugin being executed from an ESM root plugin, check to see if ts-node/esm loader has been set + // either in the NODE_OPTIONS env var or from the exec args. If the ts-node/esm loader has NOT been loaded then we want + // to skip ts-node registration so that it falls back on the compiled source. + if (plugin?.moduleType === 'module') { + const tsNodeEsmLoaderInExecArgv = process.execArgv.includes('--loader') && process.execArgv.includes('ts-node/esm') + const tsNodeEsmLoaderInNodeOptions = process.env.NODE_OPTIONS?.includes('--loader=ts-node/esm') ?? false + if (!tsNodeEsmLoaderInExecArgv && !tsNodeEsmLoaderInNodeOptions) { + debug(`Skipping ts-node registration for ${root} because it's an ESM module but the ts-node/esm loader hasn't been run`) + debug('try setting NODE_OPTIONS="--loader ts-node/esm" in your environment.') + if (plugin.type === 'link') { + memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-compiled without setting NODE_OPTIONS="--loader=ts-node/esm" in the environment. Existing compiled source will be used instead.`) + } + + return orig + } + } + + if (settings.tsnodeEnabled === undefined && isProd() && plugin?.type !== 'link') { + debug(`Skipping ts-node registration for ${root} because NODE_ENV is NOT "test" or "development"`) + return orig + } try { - const tsconfig = type === 'link' ? registerTSNode(root) : loadTSConfig(root) + const tsconfig = registerTSNode(root) if (!tsconfig) return orig const {rootDir, rootDirs, outDir} = tsconfig.compilerOptions const rootDirPath = rootDir || (rootDirs || [])[0] diff --git a/src/errors/errors/cli.ts b/src/errors/errors/cli.ts index 67df36c92..3fb241c7f 100644 --- a/src/errors/errors/cli.ts +++ b/src/errors/errors/cli.ts @@ -24,11 +24,13 @@ export class CLIError extends Error implements OclifError { oclif: OclifError['oclif'] = {} code?: string + suggestions?: string[] constructor(error: string | Error, options: { exit?: number | false } & PrettyPrintableError = {}) { super(error instanceof Error ? error.message : error) addOclifExitCode(this, options) this.code = options.code + this.suggestions = options.suggestions } get stack(): string { diff --git a/src/errors/index.ts b/src/errors/index.ts index e3810907d..4fbbbfe0e 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -54,3 +54,10 @@ export function warn(input: string | Error): void { console.error(message) if (config.errorLogger) config.errorLogger.log(err?.stack ?? '') } + +const WARNINGS = new Set() +export function memoizedWarn(input: string | Error): void { + if (!WARNINGS.has(input)) warn(input) + + WARNINGS.add(input) +} diff --git a/src/interfaces/config.ts b/src/interfaces/config.ts index 28ec10f1e..cc75d0afa 100644 --- a/src/interfaces/config.ts +++ b/src/interfaces/config.ts @@ -132,6 +132,7 @@ export interface Config { s3Url(key: string): string; s3Key(type: 'versioned' | 'unversioned', ext: '.tar.gz' | '.tar.xz', options?: Config.s3Key.Options): string; s3Key(type: keyof PJSON.S3.Templates, options?: Config.s3Key.Options): string; + getPluginsList(): Plugin[]; } export namespace Config { diff --git a/src/interfaces/pjson.ts b/src/interfaces/pjson.ts index 13c5818f8..2834167ce 100644 --- a/src/interfaces/pjson.ts +++ b/src/interfaces/pjson.ts @@ -9,6 +9,7 @@ export interface PJSON { bin?: string; dirname?: string; hooks?: Record; + plugins?: string[]; }; } diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index 303f20846..617dc74ad 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -52,6 +52,10 @@ export interface Plugin { * examples: core, link, user, dev */ type: string; + /** + * Plugin is written in ESM or CommonJS + */ + moduleType: 'module' | 'commonjs'; /** * base path of plugin */ diff --git a/src/interfaces/ts-config.ts b/src/interfaces/ts-config.ts index b65654178..d871120e2 100644 --- a/src/interfaces/ts-config.ts +++ b/src/interfaces/ts-config.ts @@ -7,5 +7,14 @@ export interface TSConfig { esModuleInterop?: boolean; experimentalDecorators?: boolean; emitDecoratorMetadata?: boolean; + module?: string; + moduleResolution?: string; + sourceMap?: boolean; + jsx?: boolean; }; + 'ts-node'?: { + esm?: boolean; + experimentalSpecifierResolution?: 'node' | 'explicit'; + scope?: boolean; + } } diff --git a/src/main.ts b/src/main.ts index f5e0792d5..46e4b95f5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,10 +8,11 @@ import {Config} from './config' import {getHelpFlagAdditions, loadHelpClass, normalizeArgv} from './help' import {settings} from './settings' import {Errors, flush} from '.' -import {join, dirname} from 'path' import {stdout} from './cli-ux/stream' import {Performance} from './performance' +const debug = require('debug')('oclif:main') + const log = (message = '', ...args: any[]) => { message = typeof message === 'string' ? message : inspect(message) stdout.write(format(message, ...args) + '\n') @@ -47,6 +48,10 @@ export async function run(argv?: string[], options?: Interfaces.LoadOptions): Pr Performance.debug() } + debug(`process.execPath: ${process.execPath}`) + debug(`process.execArgv: ${process.execArgv}`) + debug('process.argv: %O', process.argv) + argv = argv ?? process.argv.slice(2) // Handle the case when a file URL string or URL is passed in such as 'import.meta.url'; covert to file path. if (options && ((typeof options === 'string' && options.startsWith('file://')) || options instanceof URL)) { @@ -100,10 +105,6 @@ export async function run(argv?: string[], options?: Interfaces.LoadOptions): Pr } } -function getTsConfigPath(dir: string, type: 'esm' | 'cjs'): string { - return type === 'cjs' ? join(dir, '..', 'tsconfig.json') : join(dirname(fileURLToPath(dir)), '..', 'tsconfig.json') -} - /** * Load and run oclif CLI * @@ -112,47 +113,42 @@ function getTsConfigPath(dir: string, type: 'esm' | 'cjs'): string { * * @example For ESM dev.js * ``` - * #!/usr/bin/env ts-node - * // eslint-disable-next-line node/shebang + * #!/usr/bin/env node * (async () => { * const oclif = await import('@oclif/core') - * await oclif.execute({type: 'esm', development: true, dir: import.meta.url}) + * await oclif.execute({development: true, dir: import.meta.url}) * })() * ``` * * @example For ESM run.js * ``` * #!/usr/bin/env node - * // eslint-disable-next-line node/shebang * (async () => { * const oclif = await import('@oclif/core') - * await oclif.execute({type: 'esm', dir: import.meta.url}) + * await oclif.execute({dir: import.meta.url}) * })() * ``` * * @example For CJS dev.js * ``` * #!/usr/bin/env node - * // eslint-disable-next-line node/shebang * (async () => { * const oclif = await import('@oclif/core') - * await oclif.execute({type: 'cjs', development: true, dir: __dirname}) + * await oclif.execute({development: true, dir: __dirname}) * })() * ``` * * @example For CJS run.js * ``` * #!/usr/bin/env node - * // eslint-disable-next-line node/shebang * (async () => { * const oclif = await import('@oclif/core') - * await oclif.execute({type: 'cjs', dir: import.meta.url}) + * await oclif.execute({dir: __dirname}) * })() * ``` */ export async function execute( options: { - type: 'cjs' | 'esm'; dir: string; args?: string[]; loadOptions?: Interfaces.LoadOptions; @@ -162,9 +158,6 @@ export async function execute( if (options.development) { // In dev mode -> use ts-node and dev plugins process.env.NODE_ENV = 'development' - require('ts-node').register({ - project: getTsConfigPath(options.dir, options.type), - }) settings.debug = true } diff --git a/src/module-loader.ts b/src/module-loader.ts index fcf7843fa..dcacd81f4 100644 --- a/src/module-loader.ts +++ b/src/module-loader.ts @@ -5,7 +5,7 @@ import * as fs from 'fs-extra' import {ModuleLoadError} from './errors' import {Config as IConfig} from './interfaces' import {Plugin as IPlugin} from './interfaces' -import * as Config from './config' +import {tsPath} from './config' const getPackageType = require('get-package-type') @@ -15,6 +15,10 @@ const getPackageType = require('get-package-type') // eslint-disable-next-line camelcase const s_EXTENSIONS: string[] = ['.ts', '.js', '.mjs', '.cjs'] +const isPlugin = (config: IConfig|IPlugin): config is IPlugin => { + return (config).type !== undefined +} + /** * Provides a static class with several utility methods to work with Oclif config / plugin to load ESM or CJS Node * modules and source files. @@ -81,7 +85,9 @@ export default class ModuleLoader { return {isESM, module, filePath} } catch (error: any) { if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { - throw new ModuleLoadError(`${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}: ${error.message}`) + throw new ModuleLoadError( + `${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}: ${error.message}`, + ) } throw error @@ -129,17 +135,13 @@ export default class ModuleLoader { */ static resolvePath(config: IConfig|IPlugin, modulePath: string): {isESM: boolean; filePath: string} { let isESM: boolean - let filePath: string - - const isPlugin = (config: IConfig|IPlugin): config is IPlugin => { - return (config).type !== undefined - } + let filePath: string | undefined try { filePath = require.resolve(modulePath) isESM = ModuleLoader.isPathModule(filePath) } catch { - filePath = isPlugin(config) ? Config.tsPath(config.root, modulePath, config.type) : Config.tsPath(config.root, modulePath) + filePath = (isPlugin(config) ? tsPath(config.root, modulePath, config) : tsPath(config.root, modulePath)) ?? modulePath let fileExists = false let isDirectory = false diff --git a/test/config/config.flexible.test.ts b/test/config/config.flexible.test.ts index 217fc0dc5..20c1957b5 100644 --- a/test/config/config.flexible.test.ts +++ b/test/config/config.flexible.test.ts @@ -99,6 +99,7 @@ describe('Config with flexible taxonomy', () => { topics: [], valid: true, tag: 'tag', + moduleType: 'commonjs', } const pluginB: IPlugin = { @@ -117,6 +118,7 @@ describe('Config with flexible taxonomy', () => { topics: [], valid: true, tag: 'tag', + moduleType: 'commonjs', } const plugins: IPlugin[] = [pluginA, pluginB] diff --git a/test/config/config.test.ts b/test/config/config.test.ts index 7de416f80..9b4b062d7 100644 --- a/test/config/config.test.ts +++ b/test/config/config.test.ts @@ -282,6 +282,7 @@ describe('Config', () => { topics: [], valid: true, tag: 'tag', + moduleType: 'commonjs', } const pluginB: IPlugin = { @@ -300,6 +301,7 @@ describe('Config', () => { topics: [], valid: true, tag: 'tag', + moduleType: 'commonjs', } const plugins: IPlugin[] = [pluginA, pluginB] let test = fancy diff --git a/test/integration/esm-cjs.ts b/test/integration/esm-cjs.ts new file mode 100644 index 000000000..827cd3a7a --- /dev/null +++ b/test/integration/esm-cjs.ts @@ -0,0 +1,421 @@ +/** + * These integration tests do not use mocha because we encountered an issue with + * spawning child processes for testing root ESM plugins with linked ESM plugins. + * This scenario works as expected when running outside of mocha. + * + * Instead of spending more time diagnosing the root cause, we are just going to + * run these integration tests using ts-node and a lightweight homemade test runner. + */ +import * as fs from 'fs/promises' +import * as path from 'path' +import {Executor, setup} from './util' +import {expect} from 'chai' +import {bold, green, red} from 'chalk' + +const FAILED: string[] = [] +const PASSED: string[] = [] + +async function test(name: string, fn: () => Promise) { + try { + await fn() + PASSED.push(name) + console.log(green('โœ“'), name) + } catch (error) { + FAILED.push(name) + console.log(red('๐„‚'), name) + console.log(error) + } +} + +function exit(): never { + console.log() + console.log(bold('#### Summary ####')) + + for (const name of PASSED) { + console.log(green('โœ“'), name) + } + + for (const name of FAILED) { + console.log(red('๐„‚'), name) + } + + console.log(`${green('Passed:')} ${PASSED.length}`) + console.log(`${red('Failed:')} ${FAILED.length}`) + + // eslint-disable-next-line no-process-exit, unicorn/no-process-exit + process.exit(FAILED.length) +} + +type Plugin = { + name: string; + command: string; + package: string; + repo: string; +} + +type Script = 'run' | 'dev' + +type InstallPluginOptions = { + executor: Executor; + plugin: Plugin; + script: Script; +} + +type LinkPluginOptions = { + executor: Executor; + plugin: Plugin; + script: Script; +} + +type RunCommandOptions = { + executor: Executor; + plugin: Plugin; + script: Script; + expectStrings?: string[]; + env?: Record; +} + +type ModifyCommandOptions = { + executor: Executor; + plugin: Plugin; + from: string; + to: string; +} + +type CleanUpOptions = { + executor: Executor; + script: Script; + plugin: Plugin; +} + +(async () => { + const PLUGINS = { + esm1: { + name: 'plugin-test-esm-1', + command: 'esm1', + package: '@oclif/plugin-test-esm-1', + repo: 'https://github.com/oclif/plugin-test-esm-1', + commandText: 'hello I am an ESM plugin', + hookText: 'Greetings! from plugin-test-esm-1 init hook', + }, + esm2: { + name: 'plugin-test-esm-2', + command: 'esm2', + package: '@oclif/plugin-test-esm-2', + repo: 'https://github.com/oclif/plugin-test-esm-2', + commandText: 'hello I am an ESM plugin', + hookText: 'Greetings! from plugin-test-esm-2 init hook', + }, + cjs1: { + name: 'plugin-test-cjs-1', + command: 'cjs1', + package: '@oclif/plugin-test-cjs-1', + repo: 'https://github.com/oclif/plugin-test-cjs-1', + commandText: 'hello I am a CJS plugin', + hookText: 'Greetings! from plugin-test-cjs-1 init hook', + }, + cjs2: { + name: 'plugin-test-cjs-2', + command: 'cjs2', + package: '@oclif/plugin-test-cjs-2', + repo: 'https://github.com/oclif/plugin-test-cjs-2', + commandText: 'hello I am a CJS plugin', + hookText: 'Greetings! from plugin-test-cjs-2 init hook', + }, + } + + async function installPlugin(options: InstallPluginOptions): Promise { + const result = await options.executor.executeCommand(`plugins:install ${options.plugin.package}`, options.script) + expect(result.code).to.equal(0) + + const pluginsResult = await options.executor.executeCommand('plugins', options.script) + expect(pluginsResult.stdout).to.include(options.plugin.name) + } + + async function linkPlugin(options: LinkPluginOptions): Promise { + const pluginExecutor = await setup(__filename, { + repo: options.plugin.repo, + subDir: options.executor.parentDir, + }) + + const result = await options.executor.executeCommand(`plugins:link ${pluginExecutor.pluginDir}`, options.script) + expect(result.code).to.equal(0) + + const pluginsResult = await options.executor.executeCommand('plugins', options.script) + expect(pluginsResult.stdout).to.include(options.plugin.name) + + return pluginExecutor + } + + async function modifyCommand(options: ModifyCommandOptions): Promise { + const filePath = path.join(options.executor.pluginDir, 'src', 'commands', `${options.plugin.command}.ts`) + const content = await fs.readFile(filePath, 'utf8') + const modifiedContent = content.replace(options.from, options.to) + await fs.writeFile(filePath, modifiedContent) + } + + async function runCommand(options: RunCommandOptions): Promise { + const env = {...process.env, ...options.env} + const result = await options.executor.executeCommand(options.plugin.command, options.script, {env}) + expect(result.code).to.equal(0) + + if (options.expectStrings) { + for (const expectString of options.expectStrings) { + expect(result.stdout).to.include(expectString) + } + } + } + + async function cleanUp(options: CleanUpOptions): Promise { + await options.executor.executeCommand(`plugins:uninstall @oclif/${options.plugin.name}`) + expect((await options.executor.executeCommand('plugins')).stdout).to.not.include(options.plugin.name) + } + + const args = process.argv.slice(process.argv.indexOf(__filename) + 1) + const runInParallel = args.includes('--parallel') + const skip = args.find(arg => arg.startsWith('--skip=')) + + const skips = skip ? skip.split('=')[1].split(',') : [] + const runEsmTests = !skips.includes('esm') + const runCjsTests = !skips.includes('cjs') + + console.log('Node version:', process.version) + console.log(runInParallel ? '๐Ÿ‡ Running tests in parallel' : '๐Ÿข Running tests sequentially') + if (skips.length > 0) console.log(`๐Ÿšจ Skipping ${skips.join(', ')} tests ๐Ÿšจ`) + + let cjsExecutor: Executor + let esmExecutor: Executor + + const cjsBefore = async () => { + cjsExecutor = await setup(__filename, {repo: PLUGINS.cjs1.repo, subDir: 'cjs'}) + } + + const esmBefore = async () => { + esmExecutor = await setup(__filename, {repo: PLUGINS.esm1.repo, subDir: 'esm'}) + } + + const cjsTests = async () => { + await test('Install CJS plugin to CJS root plugin', async () => { + const plugin = PLUGINS.cjs2 + + await installPlugin({executor: cjsExecutor, plugin, script: 'run'}) + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'dev', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + }) + + await test('Install ESM plugin to CJS root plugin', async () => { + const plugin = PLUGINS.esm1 + + await installPlugin({executor: cjsExecutor, plugin, script: 'run'}) + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'dev', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + }) + + await test('Link CJS plugin to CJS root plugin', async () => { + const plugin = PLUGINS.cjs2 + + const linkedPlugin = await linkPlugin({executor: cjsExecutor, plugin, script: 'run'}) + + // test bin/run + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + // test un-compiled changes with bin/run + await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'run', + expectStrings: ['howdy', plugin.hookText], + }) + + // test un-compiled changes with bin/dev + await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'dev', + expectStrings: ['cheers', plugin.hookText], + }) + + await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + }) + + await test('Link ESM plugin to CJS root plugin', async () => { + const plugin = PLUGINS.esm2 + + await linkPlugin({executor: cjsExecutor, plugin, script: 'run'}) + + // test bin/run + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + + // test bin/dev + await runCommand({ + executor: cjsExecutor, + plugin, + script: 'dev', + expectStrings: [plugin.commandText, plugin.hookText], + }) + + await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + }) + } + + const esmTests = async () => { + await test('Install CJS plugin to ESM root plugin', async () => { + const plugin = PLUGINS.cjs1 + + await installPlugin({executor: esmExecutor, plugin, script: 'run'}) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'dev', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + }) + + await test('Install ESM plugin to ESM root plugin', async () => { + const plugin = PLUGINS.esm2 + + await installPlugin({executor: esmExecutor, plugin, script: 'run'}) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'dev', + expectStrings: [plugin.commandText, plugin.hookText], + }) + await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + }) + + await test('Link CJS plugin to ESM root plugin', async () => { + const plugin = PLUGINS.cjs1 + + const linkedPlugin = await linkPlugin({executor: esmExecutor, plugin, script: 'run'}) + // test bin/run + await runCommand({ + executor: esmExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + // test un-compiled changes with bin/run + await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'run', + expectStrings: ['howdy', plugin.hookText], + }) + + // test un-compiled changes with bin/dev + await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'dev', + expectStrings: ['cheers', plugin.hookText], + }) + + await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + }) + + await test('Link ESM plugin to ESM root plugin', async () => { + const plugin = PLUGINS.esm2 + + const linkedPlugin = await linkPlugin({executor: esmExecutor, plugin, script: 'run'}) + // test bin/run + // NOTE: this also tests that the compiled source is used when ts-node/esm loader is not specified + await runCommand({ + executor: esmExecutor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + // test un-compiled changes with bin/run + await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'run', + expectStrings: ['howdy', plugin.hookText], + env: {NODE_OPTIONS: '--loader=ts-node/esm'}, + }) + + // test un-compiled changes with bin/dev + await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) + await runCommand({ + executor: esmExecutor, + plugin, + script: 'dev', + expectStrings: ['cheers', plugin.hookText], + env: {NODE_OPTIONS: '--loader=ts-node/esm'}, + }) + + await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + }) + } + + if (runInParallel) { + await Promise.all([ + runCjsTests ? cjsBefore() : Promise.resolve(), + runEsmTests ? esmBefore() : Promise.resolve(), + ]) + + await Promise.all([ + runCjsTests ? cjsTests() : Promise.resolve(), + runEsmTests ? esmTests() : Promise.resolve(), + ]) + } else { + if (runCjsTests) await cjsBefore() + if (runEsmTests) await esmBefore() + + if (runCjsTests) await cjsTests() + if (runEsmTests) await esmTests() + } + + exit() +})() + diff --git a/test/integration/plugins.e2e.ts b/test/integration/plugins.e2e.ts index 8bdb42373..b8206320e 100644 --- a/test/integration/plugins.e2e.ts +++ b/test/integration/plugins.e2e.ts @@ -30,20 +30,20 @@ describe('oclif plugins', () => { }) it('should show description', () => { - expect(help.output).to.include('oclif example Hello World CLI') + expect(help.stdout).to.include('oclif example Hello World CLI') }) it('should show version', () => { - expect(help.output).to.include('VERSION\n oclif-hello-world/0.0.0') + expect(help.stdout).to.include('VERSION\n oclif-hello-world/0.0.0') }) it('should show usage', () => { - expect(help.output).to.include('USAGE\n $ oclif-hello-world [COMMAND]') + expect(help.stdout).to.include('USAGE\n $ oclif-hello-world [COMMAND]') }) it('should show topics', () => { - expect(help.output).to.include('TOPICS\n plugins') + expect(help.stdout).to.include('TOPICS\n plugins') }) it('should show commands', () => { const regex = /COMMANDS\n\s\sautocomplete|\s\scommands|\s\shelp|\s\splugins|\s\sversion|\s\supdate|\s\swhich/ - expect(regex.test(help.output!)).to.be.true + expect(regex.test(help.stdout!)).to.be.true }) }) @@ -54,20 +54,20 @@ describe('oclif plugins', () => { }) it('should show summary', () => { - expect(help.output).to.include('List installed plugins.') + expect(help.stdout).to.include('List installed plugins.') }) it('should show usage', () => { - expect(help.output).to.include('USAGE\n $ oclif-hello-world plugins [--json] [--core]') + expect(help.stdout).to.include('USAGE\n $ oclif-hello-world plugins [--json] [--core]') }) it('should show description', () => { - expect(help.output).to.include('DESCRIPTION\n List installed plugins.') + expect(help.stdout).to.include('DESCRIPTION\n List installed plugins.') }) it('should show examples', () => { - expect(help.output).to.include('EXAMPLES\n $ oclif-hello-world plugins') + expect(help.stdout).to.include('EXAMPLES\n $ oclif-hello-world plugins') }) it('should show commands', () => { const regex = /COMMANDS\n\s\splugins:inspect|\s\splugins:install|\s\splugins:link|\s\splugins:uninstall|\s\splugins:update/ - expect(regex.test(help.output!)).to.be.true + expect(regex.test(help.stdout!)).to.be.true }) }) @@ -78,31 +78,31 @@ describe('oclif plugins', () => { }) it('should show summary', () => { - expect(help.output).to.include('Installs a plugin into the CLI.') + expect(help.stdout).to.include('Installs a plugin into the CLI.') }) it('should show usage', () => { - expect(help.output).to.include('USAGE\n $ oclif-hello-world plugins:install PLUGIN...') + expect(help.stdout).to.include('USAGE\n $ oclif-hello-world plugins:install PLUGIN...') }) it('should show arguments', () => { - expect(help.output).to.include('ARGUMENTS\n PLUGIN Plugin to install.') + expect(help.stdout).to.include('ARGUMENTS\n PLUGIN Plugin to install.') }) it('should show flags', () => { - expect(help.output).to.include('FLAGS\n') - expect(help.output).to.include('-f, --force Run yarn install with force flag.') - expect(help.output).to.include('-h, --help Show CLI help.') - expect(help.output).to.include('-v, --verbose') + expect(help.stdout).to.include('FLAGS\n') + expect(help.stdout).to.include('-f, --force Run yarn install with force flag.') + expect(help.stdout).to.include('-h, --help Show CLI help.') + expect(help.stdout).to.include('-v, --verbose') }) it('should show description', () => { - expect(help.output).to.include('DESCRIPTION\n Installs a plugin into the CLI.') + expect(help.stdout).to.include('DESCRIPTION\n Installs a plugin into the CLI.') }) it('should show aliases', () => { - expect(help.output).to.include('ALIASES\n $ oclif-hello-world plugins:add') + expect(help.stdout).to.include('ALIASES\n $ oclif-hello-world plugins:add') }) it('should show examples', () => { - expect(help.output).to.include('EXAMPLES\n') - expect(help.output).to.include('$ oclif-hello-world plugins:install myplugin') - expect(help.output).to.include('$ oclif-hello-world plugins:install https://github.com/someuser/someplugin') - expect(help.output).to.include('$ oclif-hello-world plugins:install someuser/someplugin') + expect(help.stdout).to.include('EXAMPLES\n') + expect(help.stdout).to.include('$ oclif-hello-world plugins:install myplugin') + expect(help.stdout).to.include('$ oclif-hello-world plugins:install https://github.com/someuser/someplugin') + expect(help.stdout).to.include('$ oclif-hello-world plugins:install someuser/someplugin') }) }) }) @@ -112,68 +112,68 @@ describe('oclif plugins', () => { it('should show commands', async () => { commands = await executor.executeCommand('commands') - expect(commands.output).to.include('commands') - expect(commands.output).to.include('help') - expect(commands.output).to.include('plugins') - expect(commands.output).to.include('plugins:inspect') - expect(commands.output).to.include('plugins:install') - expect(commands.output).to.include('plugins:link') - expect(commands.output).to.include('plugins:uninstall') - expect(commands.output).to.include('plugins:update') - expect(commands.output).to.include('version') - expect(commands.output).to.include('which') + expect(commands.stdout).to.include('commands') + expect(commands.stdout).to.include('help') + expect(commands.stdout).to.include('plugins') + expect(commands.stdout).to.include('plugins:inspect') + expect(commands.stdout).to.include('plugins:install') + expect(commands.stdout).to.include('plugins:link') + expect(commands.stdout).to.include('plugins:uninstall') + expect(commands.stdout).to.include('plugins:update') + expect(commands.stdout).to.include('version') + expect(commands.stdout).to.include('which') }) - it('should fliter commands', async () => { + it('should filter commands', async () => { commands = await executor.executeCommand('commands --filter "command=plugins"') - expect(commands.output).to.include('plugins') - expect(commands.output).to.include('plugins:inspect') - expect(commands.output).to.include('plugins:install') - expect(commands.output).to.include('plugins:link') - expect(commands.output).to.include('plugins:uninstall') - expect(commands.output).to.include('plugins:update') + expect(commands.stdout).to.include('plugins') + expect(commands.stdout).to.include('plugins:inspect') + expect(commands.stdout).to.include('plugins:install') + expect(commands.stdout).to.include('plugins:link') + expect(commands.stdout).to.include('plugins:uninstall') + expect(commands.stdout).to.include('plugins:update') - expect(commands.output).to.not.include('commands') - expect(commands.output).to.not.include('help') - expect(commands.output).to.not.include('version') - expect(commands.output).to.not.include('which') + expect(commands.stdout).to.not.include('commands') + expect(commands.stdout).to.not.include('help') + expect(commands.stdout).to.not.include('version') + expect(commands.stdout).to.not.include('which') }) - it('should extedned columns', async () => { + it('should extend columns', async () => { commands = await executor.executeCommand('commands --extended') - expect(commands.output).to.include('Command') - expect(commands.output).to.include('Summary') - expect(commands.output).to.include('Description') - expect(commands.output).to.include('Usage') - expect(commands.output).to.include('Plugin') - expect(commands.output).to.include('Type') - expect(commands.output).to.include('Hidden') + expect(commands.stdout).to.include('Command') + expect(commands.stdout).to.include('Summary') + expect(commands.stdout).to.include('Description') + expect(commands.stdout).to.include('Usage') + expect(commands.stdout).to.include('Plugin') + expect(commands.stdout).to.include('Type') + expect(commands.stdout).to.include('Hidden') }) it('should filter columns', async () => { commands = await executor.executeCommand('commands --columns Command') - expect(commands.output).to.include('Command') - expect(commands.output).to.not.include('Summary') + expect(commands.stdout).to.include('Command') + expect(commands.stdout).to.not.include('Summary') }) it('should show commands in csv', async () => { commands = await executor.executeCommand('commands --csv') - expect(commands.output).to.include('Command,Summary\n') - expect(commands.output).to.include('commands') - expect(commands.output).to.include('help') - expect(commands.output).to.include('plugins') - expect(commands.output).to.include('plugins:inspect') - expect(commands.output).to.include('plugins:install') - expect(commands.output).to.include('plugins:link') - expect(commands.output).to.include('plugins:uninstall') - expect(commands.output).to.include('plugins:update') - expect(commands.output).to.include('version') - expect(commands.output).to.include('which') + expect(commands.stdout).to.include('Command,Summary\n') + expect(commands.stdout).to.include('commands') + expect(commands.stdout).to.include('help') + expect(commands.stdout).to.include('plugins') + expect(commands.stdout).to.include('plugins:inspect') + expect(commands.stdout).to.include('plugins:install') + expect(commands.stdout).to.include('plugins:link') + expect(commands.stdout).to.include('plugins:uninstall') + expect(commands.stdout).to.include('plugins:update') + expect(commands.stdout).to.include('version') + expect(commands.stdout).to.include('which') }) it('should show commands in json', async () => { commands = await executor.executeCommand('commands --json') - const json = JSON.parse(commands.output!) as Array<{ id: string }> + const json = JSON.parse(commands.stdout!) as Array<{ id: string }> const commandIds = json.map(j => j.id) expect(commandIds).to.include('commands') expect(commandIds).to.include('help') @@ -197,11 +197,11 @@ describe('oclif plugins', () => { it('should install the plugin', async () => { const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available 2>&1') expect(result.code).to.equal(0) - expect(result.output).to.include('@oclif/plugin-warn-if-update-available... installed v') + expect(result.stdout).to.include('@oclif/plugin-warn-if-update-available... installed v') const pluginsResult = await executor.executeCommand('plugins') expect(pluginsResult.code).to.equal(0) - expect(pluginsResult.output).to.include('@oclif/plugin-warn-if-update-available') + expect(pluginsResult.stdout).to.include('@oclif/plugin-warn-if-update-available') }) }) @@ -214,9 +214,9 @@ describe('oclif plugins', () => { const result = await executor.executeCommand('plugins:install https://github.com/oclif/plugin-warn-if-update-available') expect(result.code).to.equal(0) - const pluginsResult = await executor.executeCommand('plugins') + const pluginsResult = await executor.executeCommand('plugins --core') expect(pluginsResult.code).to.equal(0) - expect(pluginsResult.output).to.include('@oclif/plugin-warn-if-update-available') + expect(pluginsResult.stdout).to.include('@oclif/plugin-warn-if-update-available') }) }) @@ -224,11 +224,11 @@ describe('oclif plugins', () => { it('should install the plugin', async () => { const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available --force 2>&1') expect(result.code).to.equal(0) - expect(result.output).to.include('@oclif/plugin-warn-if-update-available... installed v') + expect(result.stdout).to.include('@oclif/plugin-warn-if-update-available... installed v') const pluginsResult = await executor.executeCommand('plugins') expect(pluginsResult.code).to.equal(0) - expect(pluginsResult.output).to.include('@oclif/plugin-warn-if-update-available') + expect(pluginsResult.stdout).to.include('@oclif/plugin-warn-if-update-available') }) }) @@ -240,11 +240,11 @@ describe('oclif plugins', () => { it('should uninstall the plugin', async () => { const result = await executor.executeCommand('plugins:uninstall @oclif/plugin-warn-if-update-available 2>&1') expect(result.code).to.equal(0) - expect(result.output).to.include('Uninstalling @oclif/plugin-warn-if-update-available... done\n') + expect(result.stdout).to.include('Uninstalling @oclif/plugin-warn-if-update-available... done\n') const pluginsResult = await executor.executeCommand('plugins') expect(pluginsResult.code).to.equal(0) - expect(pluginsResult.output).to.not.include('@oclif/plugin-warn-if-update-available') + expect(pluginsResult.stdout).to.not.include('@oclif/plugin-warn-if-update-available') }) }) }) @@ -255,16 +255,16 @@ describe('oclif plugins', () => { version = await executor.executeCommand('version') }) - it('should show version', () => expect(version.output).to.include('oclif-hello-world/0.0.0')) - it('should show platform', () => expect(version.output).to.include(process.platform)) - it('should show arch', () => expect(version.output).to.include(os.arch())) - it('should show node version', () => expect(version.output).to.include(process.version)) + it('should show version', () => expect(version.stdout).to.include('oclif-hello-world/0.0.0')) + it('should show platform', () => expect(version.stdout).to.include(process.platform)) + it('should show arch', () => expect(version.stdout).to.include(os.arch())) + it('should show node version', () => expect(version.stdout).to.include(process.version)) }) describe('plugin-which', () => { it('should show the plugin that a command belongs to', async () => { const result = await executor.executeCommand('which plugins:install') - expect(result.output).to.include('@oclif/plugin-plugins') + expect(result.stdout).to.include('@oclif/plugin-plugins') }) }) }) diff --git a/test/integration/sf.e2e.ts b/test/integration/sf.e2e.ts index 48a4615d8..3b2bc4949 100644 --- a/test/integration/sf.e2e.ts +++ b/test/integration/sf.e2e.ts @@ -51,7 +51,7 @@ describe('Salesforce CLI (sf)', () => { * */ const regex = /^.*?USAGE.*?FLAGS.*?GLOBAL FLAGS.*?DESCRIPTION.*?EXAMPLES.*?FLAG DESCRIPTIONS.*?CONFIGURATION VARIABLES.*?ENVIRONMENT VARIABLES.*$/gs - expect(regex.test(help.output!)).to.be.true + expect(regex.test(help.stdout!)).to.be.true }) it('should show custom short help', async () => { @@ -72,20 +72,20 @@ describe('Salesforce CLI (sf)', () => { * */ const regex = /^.*?USAGE.*?FLAGS.*?GLOBAL FLAGS.*?(?!DESCRIPTION).*?(?!EXAMPLES).*?(?!FLAG DESCRIPTIONS).*?(?!CONFIGURATION VARIABLES).*?(?!ENVIRONMENT VARIABLES).*$/gs - expect(regex.test(help.output!)).to.be.true + expect(regex.test(help.stdout!)).to.be.true }) it('should show version using -v', async () => { const version = await executor.executeCommand('-v') - expect(version.output).to.include('@salesforce/cli') - expect(version.output).to.include(process.platform) - expect(version.output).to.include(os.arch()) - expect(version.output).to.include(process.version) + expect(version.stdout).to.include('@salesforce/cli') + expect(version.stdout).to.include(process.platform) + expect(version.stdout).to.include(os.arch()) + expect(version.stdout).to.include(process.version) }) it('should have formatted json success output', async () => { const config = await executor.executeCommand('config list --json') - const result = parseJson(config.output!) + const result = parseJson(config.stdout!) expect(result).to.have.property('status') expect(result).to.have.property('result') expect(result).to.have.property('warnings') @@ -93,7 +93,7 @@ describe('Salesforce CLI (sf)', () => { it('should have formatted json error output', async () => { const config = await executor.executeCommand('config set DOES_NOT_EXIST --json') - const result = parseJson(config.output!) + const result = parseJson(config.stdout!) expect(result).to.have.property('status') expect(result).to.have.property('stack') expect(result).to.have.property('name') @@ -103,7 +103,7 @@ describe('Salesforce CLI (sf)', () => { it('should handle varargs', async () => { const config = await executor.executeCommand('config set disable-telemetry=true org-api-version=54.0 --global --json') - const parsed = parseJson(config.output!) + const parsed = parseJson(config.stdout!) expect(parsed.status).to.equal(0) const results = parsed.result as {successes: Array<{success: boolean}>, failures: Array<{failed: boolean}>} for (const result of results.successes) { diff --git a/test/integration/util.ts b/test/integration/util.ts index 024b43f94..0e724e1d1 100644 --- a/test/integration/util.ts +++ b/test/integration/util.ts @@ -6,65 +6,117 @@ import * as chalk from 'chalk' import * as fs from 'fs' import * as os from 'os' import * as path from 'path' +import {Interfaces} from '../../src' + +const debug = require('debug')('e2e') export type ExecError = cp.ExecException & { stderr: string; stdout: string }; -export interface Result { +export type Result = { code: number; - output?: string; + stdout?: string; + stderr?: string; error?: ExecError } -export interface Options { +export type SetupOptions = { repo: string; plugins?: string[]; + subDir?: string; +} + +export type ExecutorOptions = { + pluginDir: string; + testFileName: string; } -function updatePkgJson(testDir: string, obj: Record): void { +export type ExecOptions = cp.ExecSyncOptionsWithBufferEncoding & {silent?: boolean} + +function updatePkgJson(testDir: string, obj: Record): Interfaces.PJSON { const pkgJsonFile = path.join(testDir, 'package.json') const pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf-8')) obj.dependencies = Object.assign(pkgJson.dependencies || {}, obj.dependencies || {}) obj.resolutions = Object.assign(pkgJson.resolutions || {}, obj.resolutions || {}) const updated = Object.assign(pkgJson, obj) fs.writeFileSync(pkgJsonFile, JSON.stringify(updated, null, 2)) + + return updated } export class Executor { - public constructor(private testDir: string) {} + public usesJsScript = false + public pluginDir: string + public testFileName: string + public parentDir: string + public pluginName: string + + public constructor(options: ExecutorOptions) { + this.pluginDir = options.pluginDir + this.testFileName = options.testFileName + this.parentDir = path.basename(path.dirname(this.pluginDir)) + this.pluginName = path.basename(this.pluginDir) + + this.debug = debug.extend(`${this.testFileName}:${this.parentDir}:${this.pluginName}`) + } + + public clone(repo: string): Promise { + const result = this.exec(`git clone ${repo} ${this.pluginDir} --depth 1`) + this.usesJsScript = fs.existsSync(path.join(this.pluginDir, 'bin', 'run.js')) + return result + } - public executeInTestDir(cmd: string, silent = true): Promise { - return this.exec(cmd, this.testDir, silent) + public executeInTestDir(cmd: string, options?: ExecOptions): Promise { + return this.exec(cmd, {...options, cwd: this.pluginDir} as ExecOptions) } - public executeCommand(cmd: string): Promise { - const executable = process.platform === 'win32' ? path.join('bin', 'run.cmd') : path.join('bin', 'run') - return this.executeInTestDir(`${executable} ${cmd}`) + public executeCommand(cmd: string, script: 'run' | 'dev' = 'run', options: ExecOptions = {}): Promise { + const executable = process.platform === 'win32' ? + path.join('bin', `${script}.cmd`) : + path.join('bin', `${script}${this.usesJsScript ? '.js' : ''}`) + return this.executeInTestDir(`${executable} ${cmd}`, options) } - public exec(cmd: string, cwd = process.cwd(), silent = true): Promise { + public exec(cmd: string, options?: ExecOptions): Promise { + const cwd = options?.cwd ?? process.cwd() + const silent = options?.silent ?? true return new Promise(resolve => { + this.debug(cmd, chalk.dim(`(cwd: ${cwd})`)) if (silent) { try { - const r = cp.execSync(cmd, {stdio: 'pipe', cwd}) - resolve({code: 0, output: r.toString()}) + const r = cp.execSync(cmd, { + stdio: 'pipe', + ...options, + cwd, + }) + const stdout = r.toString() + this.debug(stdout) + resolve({code: 0, stdout}) } catch (error) { const err = error as ExecError - resolve({code: 1, error: err, output: err.stdout.toString()}) + this.debug('stdout', err.stdout.toString()) + this.debug('stderr', err.stderr.toString()) + resolve({ + code: 1, + error: err, + stdout: err.stdout.toString(), + stderr: err.stderr.toString(), + }) } } else { - console.log(chalk.cyan(cmd)) cp.execSync(cmd, {stdio: 'inherit', cwd}) resolve({code: 0}) } }) } + + public debug: (...args: any[]) => void } // eslint-disable-next-line valid-jsdoc /** * Setup for integration tests. * - * Clones the hello-world repo from github + * Clones the requested repo from github * Adds the local version of @oclif/core to the package.json * Adds relevant oclif plugins * Builds the package @@ -73,56 +125,74 @@ export class Executor { * - OCLIF_CORE_E2E_TEST_DIR: the directory that you want the setup to happen in * - OCLIF_CORE_E2E_SKIP_SETUP: skip all the setup steps (useful if iterating on tests) */ -export async function setup(testFile: string, options: Options): Promise { +export async function setup(testFile: string, options: SetupOptions): Promise { const testFileName = path.basename(testFile) - const location = path.join(process.env.OCLIF_CORE_E2E_TEST_DIR || os.tmpdir(), testFileName) - const [name] = options.repo.match(/(?<=\/).+?(?=\.)/) ?? ['hello-world'] - const testDir = path.join(location, name) - const executor = new Executor(testDir) + const dir = process.env.OCLIF_CORE_E2E_TEST_DIR || os.tmpdir() + const testDir = options.subDir ? path.join(dir, testFileName, options.subDir) : path.join(dir, testFileName) + + const name = options.repo.slice(options.repo.lastIndexOf('/') + 1) + const pluginDir = path.join(testDir, name) + const executor = new Executor({pluginDir, testFileName}) - console.log(chalk.cyan(`${testFileName}:`), testDir) + executor.debug('plugin directory:', pluginDir) if (process.env.OCLIF_CORE_E2E_SKIP_SETUP === 'true') { console.log(chalk.yellow.bold('OCLIF_CORE_E2E_SKIP_SETUP is true. Skipping test setup...')) return executor } - await mkdirp(location) - rm('-rf', testDir) + await mkdirp(testDir) + rm('-rf', pluginDir) - const clone = `git clone ${options.repo} ${testDir}` - console.log(chalk.cyan(`${testFileName}:`), clone) - await executor.exec(clone) + await executor.clone(options.repo) - console.log(chalk.cyan(`${testFileName}:`), 'Updating package.json') + executor.debug('Updating package.json') const dependencies = {'@oclif/core': `file:${path.resolve('.')}`} + let pjson: Interfaces.PJSON if (options.plugins) { // eslint-disable-next-line unicorn/prefer-object-from-entries const pluginDeps = options.plugins.reduce((x, y) => ({...x, [y]: 'latest'}), {}) - updatePkgJson(testDir, { + pjson = updatePkgJson(pluginDir, { resolutions: {'@oclif/core': path.resolve('.')}, dependencies: Object.assign(dependencies, pluginDeps), oclif: {plugins: options.plugins}, }) } else { - updatePkgJson(testDir, { + pjson = updatePkgJson(pluginDir, { resolutions: {'@oclif/core': path.resolve('.')}, dependencies, }) } - const install = 'yarn install --force' - console.log(chalk.cyan(`${testFileName}:`), install) - const yarnInstallRes = await executor.executeInTestDir(install, false) + executor.debug('updated dependencies:', JSON.stringify(pjson.dependencies, null, 2)) + executor.debug('updated resolutions:', JSON.stringify(pjson.resolutions, null, 2)) + executor.debug('updated plugins:', JSON.stringify(pjson.oclif.plugins, null, 2)) + + const bin = (pjson.oclif.bin ?? pjson.name.replace(/-/g, '_')).toUpperCase() + const dataDir = path.join(testDir, 'data', pjson.oclif.bin ?? pjson.name) + const cacheDir = path.join(testDir, 'cache', pjson.oclif.bin ?? pjson.name) + const configDir = path.join(testDir, 'config', pjson.oclif.bin ?? pjson.name) + + await mkdirp(dataDir) + await mkdirp(configDir) + await mkdirp(cacheDir) + + process.env[`${bin}_DATA_DIR`] = dataDir + process.env[`${bin}_CONFIG_DIR`] = configDir + process.env[`${bin}_CACHE_DIR`] = cacheDir + + executor.debug(`${bin}_DATA_DIR:`, process.env[`${bin}_DATA_DIR`]) + executor.debug(`${bin}_CONFIG_DIR:`, process.env[`${bin}_CONFIG_DIR`]) + executor.debug(`${bin}_CACHE_DIR:`, process.env[`${bin}_CACHE_DIR`]) + + const yarnInstallRes = await executor.executeInTestDir('yarn install --force') if (yarnInstallRes.code !== 0) { console.error(yarnInstallRes?.error) throw new Error('Failed to run `yarn install`') } - const build = 'yarn build' - console.log(chalk.cyan(`${testFileName}:`), build) - const yarnBuildRes = await executor.executeInTestDir(build, false) + const yarnBuildRes = await executor.executeInTestDir('yarn build') if (yarnBuildRes.code !== 0) { console.error(yarnBuildRes?.error) throw new Error('Failed to run `yarn build`') diff --git a/yarn.lock b/yarn.lock index 42d0def4d..0117b62ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -823,11 +823,6 @@ "@typescript-eslint/types" "4.31.2" eslint-visitor-keys "^2.0.0" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -893,11 +888,6 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: dependencies: type-fest "^0.21.3" -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -927,10 +917,10 @@ ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= -anymatch@~3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -1013,6 +1003,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -1122,20 +1119,20 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.5.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.3.1" + fsevents "~2.3.2" ci-info@^3.2.0: version "3.2.0" @@ -1287,6 +1284,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -1298,7 +1302,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2: +cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1312,14 +1316,7 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: +debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1809,7 +1806,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@~2.3.1: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -1855,17 +1852,17 @@ git-raw-commits@^2.0.0: split2 "^3.0.0" through2 "^4.0.0" -glob-parent@^5.1.2, glob-parent@~5.1.0: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1922,11 +1919,6 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -2085,11 +2077,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2181,10 +2168,10 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" @@ -2323,14 +2310,7 @@ lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - -log-symbols@^4.0.0: +log-symbols@4.1.0, log-symbols@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -2425,7 +2405,14 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -2446,33 +2433,29 @@ minimist@^1.2.3: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== -mocha@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff" - integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ== +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" + chokidar "3.5.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.6" - growl "1.10.5" + glob "7.2.0" he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" + nanoid "3.3.3" + serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -2507,10 +2490,10 @@ nanocolors@^0.1.0, nanocolors@^0.1.5: resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6" integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ== -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== natural-compare@^1.4.0: version "1.4.0" @@ -2828,10 +2811,10 @@ readable-stream@3, readable-stream@^3.0.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" @@ -2962,10 +2945,10 @@ semver@^7.2.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.0, semve dependencies: lru-cache "^6.0.0" -serialize-javascript@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" @@ -3087,14 +3070,6 @@ stdout-stderr@^0.1.9: debug "^4.1.1" strip-ansi "^6.0.0" -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -3118,13 +3093,6 @@ strip-ansi@*: dependencies: ansi-regex "^6.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3371,13 +3339,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -which@2.0.2, which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -3385,12 +3346,12 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: - string-width "^1.0.2 || 2" + isexe "^2.0.0" widest-line@^3.1.0: version "3.1.0" @@ -3409,10 +3370,10 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^7.0.0: version "7.0.0" From c4d3c579a6a58bde3b76acf848455df0d6f6daa0 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 16:57:29 +0000 Subject: [PATCH 04/55] chore(release): 3.0.0-beta.2 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e1fb9395b..87d21b50b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.1", + "version": "3.0.0-beta.2", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 6affd921033eded154366c121664309655e8f351 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 10:25:14 -0700 Subject: [PATCH 05/55] More v3 features (#772) * fix: set moduleResolution to Node16 * fix: use file urls * chore: add types * feat: support linking CJS plugins into ESM plugins * chore: integration tests for module interoperability * test: add CJS/ESM interoperability tests * fix: e2e tests * test: isolate cache, config, and data dir for e2e tests * test: test bug * test: make less noisy * test: esm/cjs hooks * chore: parallelize e2e tests * chore: typo * test: parallelize e2e tests * test: no more hanging tests * chore: un-parallelize esm-cjs tests * chore: add DEBUG to esm-cjs interop tests * chore: add DEBUG to esm-cjs interop tests * chore: try enabling debug again * test: fix node 20 tests * chore: update DEBUG env var * chore: debug tests * chore: debug tests * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: more debugging * test: use path.join * test: stop using replace-in-file * chore: more test debugging * fix: use path.join when registering ts-node * test: run in parallel * test: run tests serially * fix: dont remove undefined values from tsconfig * fix: further isolate ts-configs * feat: throw error when loading ESM paths from linked plugins * fix: default esm to true * test: compilation errors * feat: better developer experience * fix: add getPluginsList * chore(release): 2.11.9 [skip ci] * fix: add getPluginsList to Config interface * chore(release): 2.11.10 [skip ci] * feat: use exports * fix: ux class * fix: add exports for help and perf * chore: tests * feat: use Map for config.plugins * feat: add noCacheDefault * feat: add exports for Args and Flags * chore: fix tests * feat: put permutations into manifest * fix: add return to execute * fix: add version to PJSON interface * feat: drop node 14 and 16 support * fix: use CLIError for integer flag errors * fix: skip findRoot for linked plugins * chore: remove console.log * fix: dont load core plugin if already loaded as linked or user plugin * fix: ux export * chore: making things readonly * feat: add config export * test: make tests passing again * feat: rename cli-ux global to ux global * chore: go back to node 16 support * chore: migration guide * chore: remove source-map register * test: remove resolution * chore: update migration guide * fix: revert link findRoot optimization * fix: remove unneeded exports * feat: add PluginLoader * chore: update migration guide * chore: merge conflict * chore: more merge conflicts * test: recursive mkdir --------- Co-authored-by: svc-cli-bot --- .github/workflows/test.yml | 17 - .mocharc.json | 3 +- README.md | 16 +- flush.d.ts | 3 - flush.js | 1 - MIGRATION.md => guides/V2_MIGRATION.md | 0 guides/V3_MIGRATION.md | 168 +++ handle.js | 1 - package.json | 29 +- src/cli-ux/action/pride-spinner.ts | 30 - src/command.ts | 6 +- src/config/config.ts | 217 ++- src/config/index.ts | 3 +- src/config/plugin-loader.ts | 145 ++ src/config/plugin.ts | 45 +- src/config/ts-node.ts | 2 +- src/errors/config.ts | 2 +- src/errors/handle.ts | 4 +- src/errors/index.ts | 2 +- src/errors/logger.ts | 6 +- src/execute.ts | 69 + src/flags.ts | 7 +- src/help/index.ts | 6 +- src/index.ts | 47 +- src/interfaces/config.ts | 51 +- src/interfaces/parser.ts | 80 +- src/interfaces/pjson.ts | 1 + src/interfaces/plugin.ts | 5 +- src/main.ts | 69 +- src/module-loader.ts | 12 +- src/parser/errors.ts | 2 +- src/parser/parse.ts | 39 +- src/performance.ts | 4 +- src/screen.ts | 4 +- src/settings.ts | 3 +- src/{cli-ux => ux}/README.md | 0 src/{cli-ux => ux}/action/base.ts | 4 +- src/{cli-ux => ux}/action/simple.ts | 0 src/{cli-ux => ux}/action/spinner.ts | 2 +- src/{cli-ux => ux}/action/spinners.ts | 0 src/{cli-ux => ux}/config.ts | 16 +- src/{cli-ux => ux}/exit.ts | 4 +- src/ux/flush.ts | 29 + src/{cli-ux => ux}/global.d.ts | 2 +- src/{cli-ux => ux}/index.ts | 108 +- src/{cli-ux => ux}/list.ts | 0 src/{cli-ux => ux}/prompt.ts | 4 +- src/{cli-ux => ux}/stream.ts | 0 src/{cli-ux => ux}/styled/index.ts | 0 src/{cli-ux => ux}/styled/json.ts | 2 +- src/{cli-ux => ux}/styled/object.ts | 0 src/{cli-ux => ux}/styled/progress.ts | 0 src/{cli-ux => ux}/styled/table.ts | 0 src/{cli-ux => ux}/styled/tree.ts | 0 src/{cli-ux => ux}/wait.ts | 0 test/cli-ux/export.test.ts | 22 +- test/cli-ux/fancy.ts | 10 +- test/cli-ux/index.test.ts | 2 +- test/cli-ux/prompt.test.ts | 2 +- test/cli-ux/styled/header.test.ts | 3 +- test/cli-ux/styled/object.test.ts | 2 +- test/cli-ux/styled/progress.test.ts | 2 +- test/cli-ux/styled/table.e2e.ts | 2 +- test/cli-ux/styled/table.test.ts | 2 +- test/cli-ux/styled/tree.test.ts | 2 +- test/command/command.test.ts | 4 + test/command/main-esm.test.ts | 3 +- test/command/main.test.ts | 2 +- test/config/config.flexible.test.ts | 7 +- test/config/config.test.ts | 9 +- test/config/esm.test.ts | 2 +- test/config/mixed-cjs-esm.test.ts | 2 +- test/config/mixed-esm-cjs.test.ts | 2 +- test/config/typescript.test.ts | 2 +- test/errors/handle.test.ts | 6 +- test/errors/warn.test.ts | 8 +- test/help/format-commands.test.ts | 2 +- test/help/help-test-utils.ts | 11 +- test/help/show-customized-help.test.ts | 7 +- test/help/show-help.test.ts | 7 + test/help/util.test.ts | 1 + test/integration/util.ts | 10 +- test/parser/parse.test.ts | 1 - yarn.lock | 1689 +++++++++++++++++++++++- 84 files changed, 2402 insertions(+), 692 deletions(-) delete mode 100644 flush.d.ts delete mode 100644 flush.js rename MIGRATION.md => guides/V2_MIGRATION.md (100%) create mode 100644 guides/V3_MIGRATION.md delete mode 100644 handle.js delete mode 100644 src/cli-ux/action/pride-spinner.ts create mode 100644 src/config/plugin-loader.ts create mode 100644 src/execute.ts rename src/{cli-ux => ux}/README.md (100%) rename src/{cli-ux => ux}/action/base.ts (97%) rename src/{cli-ux => ux}/action/simple.ts (100%) rename src/{cli-ux => ux}/action/spinner.ts (98%) rename src/{cli-ux => ux}/action/spinners.ts (100%) rename src/{cli-ux => ux}/config.ts (69%) rename src/{cli-ux => ux}/exit.ts (83%) create mode 100644 src/ux/flush.ts rename src/{cli-ux => ux}/global.d.ts (73%) rename src/{cli-ux => ux}/index.ts (61%) rename src/{cli-ux => ux}/list.ts (100%) rename src/{cli-ux => ux}/prompt.ts (97%) rename src/{cli-ux => ux}/stream.ts (100%) rename src/{cli-ux => ux}/styled/index.ts (100%) rename src/{cli-ux => ux}/styled/json.ts (91%) rename src/{cli-ux => ux}/styled/object.ts (100%) rename src/{cli-ux => ux}/styled/progress.ts (100%) rename src/{cli-ux => ux}/styled/table.ts (100%) rename src/{cli-ux => ux}/styled/tree.ts (100%) rename src/{cli-ux => ux}/wait.ts (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68f90185d..f3a3d15fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,23 +13,6 @@ jobs: windows-unit-tests: needs: linux-unit-tests uses: salesforcecli/github-workflows/.github/workflows/unitTestsWindows.yml@main - node14: - needs: linux-unit-tests - strategy: - matrix: - os: ["ubuntu-latest", "windows-latest"] - node_version: [14.x] - fail-fast: false - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node_version }} - cache: yarn - - run: yarn install - - run: yarn build - - run: yarn test e2e: needs: linux-unit-tests strategy: diff --git a/.mocharc.json b/.mocharc.json index 377430530..dc60a4ab5 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -3,8 +3,7 @@ "reporter": "spec", "require": [ "test/helpers/init.js", - "ts-node/register", - "source-map-support/register" + "ts-node/register" ], "timeout": 60000, "watch-extensions": [ diff --git a/README.md b/README.md index d6ef3b5d7..c8b893e95 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,14 @@ base library for oclif CLIs Migrating ===== -See the [migration guide](./MIGRATION.md) for an overview of breaking changes that occurred between v1 and v2. +See the [v2 migration guide](./guides/V2_MIGRATION.md) for an overview of breaking changes that occurred between v1 and v2. + +See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaking changes that occurred between v2 and v3. CLI UX ===== -The [ux README](./src/cli-ux/README.md) contains detailed usage examples of using the `ux` export. +The [ux README](./src/ux/README.md) contains detailed usage examples of using the `ux` export. Usage ===== @@ -28,7 +30,7 @@ You can, however, use `@oclif/core` in a standalone script like this: #!/usr/bin/env ts-node import * as fs from 'fs' -import {Command, Flags} from '@oclif/core' +import {Command, Flags, flush, handle} from '@oclif/core' class LS extends Command { static description = 'List the files in a directory.' @@ -50,10 +52,10 @@ class LS extends Command { } } -LS.run().then(() => { - require('@oclif/core/flush') -}, () => { - require('@oclif/core/handle') +LS.run().then(async () => { + await flush() +}, async (err) => { + await handle(err) }) ``` diff --git a/flush.d.ts b/flush.d.ts deleted file mode 100644 index 5ca9b097b..000000000 --- a/flush.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const flush: () => Promise - -export = flush diff --git a/flush.js b/flush.js deleted file mode 100644 index 404a25a62..000000000 --- a/flush.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib').flush diff --git a/MIGRATION.md b/guides/V2_MIGRATION.md similarity index 100% rename from MIGRATION.md rename to guides/V2_MIGRATION.md diff --git a/guides/V3_MIGRATION.md b/guides/V3_MIGRATION.md new file mode 100644 index 000000000..eac4ffaf0 --- /dev/null +++ b/guides/V3_MIGRATION.md @@ -0,0 +1,168 @@ +Migrating to @oclif/core@V3 +============== + +- [Migrating to @oclif/core@V3](#migrating-to-oclifcorev3) + - [BREAKING CHANGES โ—](#breaking-changes-) + - [Dropping node 14 and node 16 support](#dropping-node-14-and-node-16-support) + - [Bin Scripts for ESM/CJS Interoperability](#bin-scripts-for-esmcjs-interoperability) + - [`Config.plugins`](#configplugins) + - [Readonly properties on `Config`](#readonly-properties-on-config) + - [Private methods on `Plugin`](#private-methods-on-plugin) + - [`ux` exports](#ux-exports) + - [`global['cli-ux']` -\> `global.ux`](#globalcli-ux---globalux) + - [`handle`](#handle) + - [`noCacheDefault` flag property replaces `isWritingManifest`](#nocachedefault-flag-property-replaces-iswritingmanifest) + - [Features ๐ŸŽ‰](#features-) + - [Exports](#exports) + - [Cache Flexible taxonomy Command Permutations](#cache-flexible-taxonomy-command-permutations) + + +## BREAKING CHANGES โ— + +### Dropping node 14 and node 16 support + The end-of-life date for Node.js 14 was [April 30, 2023](https://nodejs.org/en/about/releases/). + +The end-of-life date for Node.js 16 was [September 11, 2023](https://nodejs.org/en/about/releases/). This date is earlier than previously published. Node.jsโ€™s [blog](https://nodejs.org/en/blog/announcements/nodejs16-eol/) explains why they chose this earlier end-of-life date. + +### Bin Scripts for ESM/CJS Interoperability + +In order to support ESM and CommonJS plugin interoperability you will need to update your bin scripts to match these: + +[CJS Template](https://github.com/oclif/hello-world/tree/main/bin) + +[ESM Template](https://github.com/oclif/hello-world-esm/tree/main/bin) + +**You will also need to update any references to the bin scripts to include the .js extension.** + +If you'd like to migrate your plugin to ESM, please read our guide [here](https://oclif.io/docs/esm) + +### `Config.plugins` +`Config.plugins` is now a `Map` where the keys are the plugin names and the values are the loaded `Plugin` instances. Previously it was an array of loaded `Plugin` instances. + +By using a `Map` we can now do more efficient lookups during command execution. `Config.getPluginsList` was added in case you still would like a flat array of `Plugin` instances. + +### Readonly properties on `Config` +Various properties on `Config` are now `readonly` + - `name` + - `version` + - `channel` + - `pjson` + - `root` + - `arch` + - `bin` + - `cacheDir` + - `configDir` + - `dataDir` + - `dirname` + - `errLog` + - `home` + - `platform` + - `shell` + - `userAgent` + - `windows` + - `debug` + - `npmRegistry` + - `userPJSON` + - `plugins` + - `binPath` + - `binAliases` + - `nsisCustomization` + - `valid` + - `flexibleTaxonomy` + - `commands` + +### Private methods on `Plugin` +The `_manifest` and `warn` methods on `Plugin` are now `private` + +### `ux` exports + +The following exports are no longer available on `ux`: + +- `ActionBase` +- `config` +- `Config` +- `ExitError` +- `IPromptOptions` +- `Table` + +If you still need these you can import them from `@oclif/core/ux`: + +```typescript +import { + ActionBase, + config, + Config, + ExitError, + IPromptOptions, + Table, +} from '@oclif/core/ux' +``` + +### `global['cli-ux']` -> `global.ux` + +The global `cli-ux` object has been renamed to `ux` to be consistent with the module's name + +### `handle` + +The exported `handle` function for handling errors in bin scripts is now asynchronous + +### `noCacheDefault` flag property replaces `isWritingManifest` + +Version 2 allowed you to optionally return non-sensitive input if the `default` or `defaultHelp` flag/arg properties were called during manifest creation. This is helpful if you don't want sensitive data to be put into the `oclif.manifest.json` and then released to npm. To do this you had to handle the `isWritingManifest` parameter passed in to the `default` and `defaultHelp` callbacks. + +```typescript +export const mySensitiveFlag = Flags.string({ + default: async (context, isWritingManifest) => { + if (isWritingManifest) { + return undefined; + } + + return 'sensitive info' + }, +}); +``` + +Version 3 removes the `isWritingManifest` parameter in favor of a flag and arg property, `noCacheDefault`. Setting it to true will automatically keep it from being cached in the manfiest. + +```typescript +export const mySensitiveFlag = Flags.string({ + noCacheDefault: true, + default: async (context) => { + return 'sensitive info' + }, +}); +``` + + +## Features ๐ŸŽ‰ + +### Exports +The `exports` property in the package.json are now used for exporting individual pieces of functionality. + +The main export should continue to work as it did in v2: + +```typescript +import {Interfaces, ux} from '@oclif/core' +``` + +But you can now import individual modules if desired: + +```typescript +import Interfaces from '@oclif/core/interfaces' +import ux from '@oclif/core/ux' +``` + +These are the exports that are available: +`@oclif/core/execute` +`@oclif/core/flush` +`@oclif/core/handle` +`@oclif/core/interfaces` +`@oclif/core/run` +`@oclif/core/settings` +`@oclif/core/ux` + +**You will need to set moduleResolution to Node16 in your tsconfig.json to use this feature.** + +### Cache Flexible taxonomy Command Permutations + +The command permutations for flexible taxonomy are now cached in the oclif.manifest.json allowing for quicker startup times. diff --git a/handle.js b/handle.js deleted file mode 100644 index 96302f7b2..000000000 --- a/handle.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = error => require('./lib/errors/handle').handle(error) diff --git a/package.json b/package.json index 87d21b50b..01309b2e2 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "cli-progress": "^3.12.0", "debug": "^4.3.4", "ejs": "^3.1.8", - "fs-extra": "^9.1.0", "get-package-type": "^0.1.0", "globby": "^11.1.0", "hyperlinker": "^1.0.0", @@ -24,7 +23,6 @@ "natural-orderby": "^2.0.3", "object-treeify": "^1.1.33", "password-prompt": "^1.1.2", - "semver": "^7.5.3", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", @@ -39,15 +37,15 @@ "devDependencies": { "@commitlint/config-conventional": "^12.1.4", "@oclif/plugin-help": "^5.2.8", - "@oclif/plugin-plugins": "^2.4.7", - "@oclif/test": "^2.3.15", + "@oclif/plugin-plugins": "^3.3.0", + "@oclif/test": "^2.4.7", "@types/ansi-styles": "^3.2.1", "@types/benchmark": "^2.1.2", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/clean-stack": "^2.1.1", "@types/ejs": "^3.1.2", - "@types/fs-extra": "^9.0.13", + "@types/glob": "^8.1.0", "@types/indent-string": "^4.0.1", "@types/js-yaml": "^3.12.7", "@types/mocha": "^8.2.3", @@ -55,8 +53,7 @@ "@types/node": "^16", "@types/node-notifier": "^8.0.2", "@types/proxyquire": "^1.3.28", - "@types/semver": "^7.5.0", - "@types/shelljs": "^0.8.11", + "@types/shelljs": "^0.8.12", "@types/slice-ansi": "^4.0.0", "@types/strip-ansi": "^5.2.1", "@types/supports-color": "^8.1.1", @@ -83,20 +80,26 @@ "typescript": "^4.9.5" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, "files": [ - "/lib", - "/flush.js", - "/flush.d.ts", - "/handle.js" + "/lib" ], "homepage": "https://github.com/oclif/core", "keywords": [ "oclif" ], "license": "MIT", - "main": "lib/index.js", + "exports": { + ".": "./lib/index.js", + "./execute": "./lib/execute.js", + "./flush": "./lib/ux/flush.js", + "./handle": "./lib/errors/handle.js", + "./interfaces": "./lib/interfaces/index.js", + "./run": "./lib/main.js", + "./settings": "./lib/settings.js", + "./ux": "./lib/ux/index.js" + }, "repository": "oclif/core", "oclif": { "bin": "oclif", diff --git a/src/cli-ux/action/pride-spinner.ts b/src/cli-ux/action/pride-spinner.ts deleted file mode 100644 index a4bf142ec..000000000 --- a/src/cli-ux/action/pride-spinner.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as chalk from 'chalk' -import * as supportsColor from 'supports-color' - -import SpinnerAction from './spinner' - -function color(s: string, frameIndex: number): string { - const prideColors = [ - chalk.keyword('pink'), - chalk.red, - chalk.keyword('orange'), - chalk.yellow, - chalk.green, - chalk.cyan, - chalk.blue, - chalk.magenta, - ] - - if (!supportsColor) return s - const has256 = supportsColor.stdout ? supportsColor.stdout.has256 : (process.env.TERM || '').includes('256') - const prideColor = prideColors[frameIndex] || prideColors[0] - return has256 ? prideColor(s) : chalk.magenta(s) -} - -export default class PrideSpinnerAction extends SpinnerAction { - protected _frame(): string { - const frame = this.frames[this.frameIndex] - this.frameIndex = ++this.frameIndex % this.frames.length - return color(frame, this.frameIndex) - } -} diff --git a/src/command.ts b/src/command.ts index 3fc4995b8..fba35e53e 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,7 +1,7 @@ import {fileURLToPath} from 'url' import * as chalk from 'chalk' import {format, inspect} from 'util' -import {ux} from './cli-ux' +import ux from './ux' import {Config} from './config' import * as Errors from './errors' import {PrettyPrintableError} from './errors' @@ -27,7 +27,7 @@ import {CommandError} from './interfaces/errors' import {boolean} from './flags' import {requireJson} from './util' import {PJSON} from './interfaces' -import {stdout, stderr} from './cli-ux/stream' +import {stdout, stderr} from './ux/stream' const pjson = requireJson(__dirname, '..', 'package.json') @@ -433,6 +433,8 @@ export namespace Command { flags: {[name: string]: Flag.Cached}; args: {[name: string]: Arg.Cached}; hasDynamicHelp?: boolean; + permutations?: string[] + aliasPermutations?: string[]; } export type Flag = CompletableFlag diff --git a/src/config/config.ts b/src/config/config.ts index 0e3382754..f77604889 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -8,23 +8,24 @@ import {format} from 'util' import {Options, Plugin as IPlugin} from '../interfaces/plugin' import {Config as IConfig, ArchTypes, PlatformTypes, LoadOptions, VersionDetails} from '../interfaces/config' import {Hook, Hooks, PJSON, Topic} from '../interfaces' -import * as Plugin from './plugin' -import {Debug, compact, loadJSON, collectUsableIds, getCommandIdPermutations} from './util' +import {Debug, compact, collectUsableIds, getCommandIdPermutations} from './util' import {ensureArgObject, isProd, requireJson} from '../util' import ModuleLoader from '../module-loader' import {getHelpFlagAdditions} from '../help' import {Command} from '../command' import {CompletableOptionFlag, Arg} from '../interfaces/parser' -import {stdout} from '../cli-ux/stream' -import {Performance} from '../performance' -import {settings} from '../settings' +import {stdout} from '../ux/stream' +import Performance from '../performance' +import settings from '../settings' import {userInfo as osUserInfo} from 'node:os' import {sep} from 'node:path' +import PluginLoader from './plugin-loader' // eslint-disable-next-line new-cap const debug = Debug() const _pjson = requireJson(__dirname, '..', '..', 'package.json') +const BASE = `${_pjson.name}@${_pjson.version}` function channelFromVersion(version: string) { const m = version.match(/[^-]+(?:-([^.]+))?/) @@ -69,7 +70,7 @@ class Permutations extends Map> { } export class Config implements IConfig { - private _base = `${_pjson.name}@${_pjson.version}` + private _base = BASE public arch!: ArchTypes public bin!: string @@ -87,7 +88,7 @@ export class Config implements IConfig { public npmRegistry?: string public pjson!: PJSON.CLI public platform!: PlatformTypes - public plugins: IPlugin[] = [] + public plugins: Map = new Map() public root!: string public shell!: string public topicSeparator: ':' | ' ' = ':' @@ -111,7 +112,9 @@ export class Config implements IConfig { private _commandIDs!: string[] - private static _rootPlugin: Plugin.Plugin + private pluginLoader!: PluginLoader + + private static _rootPlugin: IPlugin constructor(public options: Options) {} @@ -122,30 +125,52 @@ export class Config implements IConfig { } if (typeof opts === 'string') opts = {root: opts} - if (isConfig(opts)) return opts + if (isConfig(opts)) { + /** + * Reload the Config based on the version required by the command. + * This is needed because the command is given the Config instantiated + * by the root plugin, which may be a different version than the one + * required by the command. + * + * Doing this ensures that the command can freely use any method on Config that + * exists in the version of Config required by the command but may not exist on the + * root's instance of Config. + */ + if (BASE !== opts._base) { + debug(`reloading config from ${opts._base} to ${BASE}`) + const config = new Config({...opts.options, plugins: opts.plugins}) + await config.load() + return config + } + + return opts + } const config = new Config(opts) await config.load() return config } - static get rootPlugin(): Plugin.Plugin | undefined { + static get rootPlugin(): IPlugin | undefined { return Config._rootPlugin } // eslint-disable-next-line complexity public async load(): Promise { settings.performanceEnabled = (settings.performanceEnabled === undefined ? this.options.enablePerf : settings.performanceEnabled) ?? false - const plugin = new Plugin.Plugin({root: this.options.root}) - await plugin.load() - Config._rootPlugin = plugin - this.plugins.push(plugin) - this.root = plugin.root - this.pjson = plugin.pjson + this.pluginLoader = new PluginLoader({root: this.options.root, plugins: this.options.plugins}) + Config._rootPlugin = await this.pluginLoader.loadRoot() + + this.root = Config._rootPlugin.root + this.pjson = Config._rootPlugin.pjson + + this.plugins.set(Config._rootPlugin.name, Config._rootPlugin) + this.root = Config._rootPlugin.root + this.pjson = Config._rootPlugin.pjson this.name = this.pjson.name this.version = this.options.version || this.pjson.version || '0.0.0' this.channel = this.options.channel || channelFromVersion(this.version) - this.valid = plugin.valid + this.valid = Config._rootPlugin.valid this.arch = (os.arch() === 'ia32' ? 'x86' : os.arch() as any) this.platform = WSL ? 'wsl' : os.platform() as any @@ -201,62 +226,35 @@ export class Config implements IConfig { debug('config done') marker?.addDetails({ - plugins: this.plugins.length, + plugins: this.plugins.size, commandPermutations: this.commands.length, - commands: this.plugins.reduce((acc, p) => acc + p.commands.length, 0), + commands: [...this.plugins.values()].reduce((acc, p) => acc + p.commands.length, 0), topics: this.topics.length, }) marker?.stop() } - async loadPluginsAndCommands(): Promise { + async loadPluginsAndCommands(opts?: {force: boolean}): Promise { const marker = Performance.mark('config.loadPluginsAndCommands') - await this.loadUserPlugins() - await this.loadDevPlugins() - await this.loadCorePlugins() + const {plugins, errors} = await this.pluginLoader.loadChildren({ + devPlugins: this.options.devPlugins, + userPlugins: this.options.userPlugins, + dataDir: this.dataDir, + rootPlugin: Config._rootPlugin, + force: opts?.force ?? false, + }) - for (const plugin of this.plugins) { + this.plugins = plugins + for (const plugin of this.plugins.values()) { this.loadCommands(plugin) this.loadTopics(plugin) } - marker?.stop() - } - - public async loadCorePlugins(): Promise { - if (this.pjson.oclif.plugins) { - await this.loadPlugins(this.root, 'core', this.pjson.oclif.plugins) - } - } - - public async loadDevPlugins(): Promise { - if (this.options.devPlugins !== false) { - // do not load oclif.devPlugins in production - if (this.isProd) return - try { - const devPlugins = this.pjson.oclif.devPlugins - if (devPlugins) await this.loadPlugins(this.root, 'dev', devPlugins) - } catch (error: any) { - process.emitWarning(error) - } + for (const error of errors) { + this.warn(error) } - } - public async loadUserPlugins(): Promise { - if (this.options.userPlugins !== false) { - try { - const userPJSONPath = path.join(this.dataDir, 'package.json') - debug('reading user plugins pjson %s', userPJSONPath) - const pjson = await loadJSON(userPJSONPath) - this.userPJSON = pjson - if (!pjson.oclif) pjson.oclif = {schema: 1} - if (!pjson.oclif.plugins) pjson.oclif.plugins = [] - await this.loadPlugins(userPJSONPath, 'user', pjson.oclif.plugins.filter((p: any) => p.type === 'user')) - await this.loadPlugins(userPJSONPath, 'link', pjson.oclif.plugins.filter((p: any) => p.type === 'link')) - } catch (error: any) { - if (error.code !== 'ENOENT') process.emitWarning(error) - } - } + marker?.stop() } public async runHook( @@ -291,7 +289,7 @@ export class Config implements IConfig { successes: [], failures: [], } as Hook.Result - const promises = this.plugins.map(async p => { + const promises = [...this.plugins.values()].map(async p => { const debug = require('debug')([this.bin, p.name, 'hooks', event].join(':')) const context: Hook.Context = { config: this, @@ -380,7 +378,7 @@ export class Config implements IConfig { }) if (jitResult.failures[0]) throw jitResult.failures[0].error if (jitResult.successes[0]) { - await this.loadPluginsAndCommands() + await this.loadPluginsAndCommands({force: true}) c = this.findCommand(id) ?? c } else { // this means that no jit_plugin_not_installed hook exists, so we should run the default behavior @@ -529,7 +527,7 @@ export class Config implements IConfig { cliVersion, architecture, nodeVersion, - pluginVersions: Object.fromEntries(this.plugins.map(p => [p.name, {version: p.version, type: p.type, root: p.root}])), + pluginVersions: Object.fromEntries([...this.plugins.values()].map(p => [p.name, {version: p.version, type: p.type, root: p.root}])), osVersion: `${os.type()} ${os.release()}`, shell: this.shell, rootPath: this.root, @@ -552,7 +550,7 @@ export class Config implements IConfig { } public getPluginsList(): IPlugin[] { - return this.plugins + return [...this.plugins.values()] } protected dir(category: 'cache' | 'data' | 'config'): string { @@ -603,51 +601,6 @@ export class Config implements IConfig { return 0 } - protected async loadPlugins(root: string, type: string, plugins: (string | { root?: string; name?: string; tag?: string })[], parent?: Plugin.Plugin): Promise { - if (!plugins || plugins.length === 0) return - const mark = Performance.mark(`config.loadPlugins#${type}`) - debug('loading plugins', plugins) - await Promise.all((plugins || []).map(async plugin => { - try { - const opts: Options = {type, root} - if (typeof plugin === 'string') { - opts.name = plugin - } else { - opts.name = plugin.name || opts.name - opts.tag = plugin.tag || opts.tag - opts.root = plugin.root || opts.root - } - - const pluginMarker = Performance.mark(`plugin.load#${opts.name!}`) - const instance = new Plugin.Plugin(opts) - await instance.load() - pluginMarker?.addDetails({ - hasManifest: instance.hasManifest, - commandCount: instance.commands.length, - topicCount: instance.topics.length, - type: instance.type, - usesMain: Boolean(instance.pjson.main), - name: instance.name, - }) - pluginMarker?.stop() - if (this.plugins.find(p => p.name === instance.name)) return - this.plugins.push(instance) - if (parent) { - instance.parent = parent - if (!parent.children) parent.children = [] - parent.children.push(instance) - } - - await this.loadPlugins(instance.root, type, instance.pjson.oclif.plugins || [], instance) - } catch (error: any) { - this.warn(error, 'loadPlugins') - } - })) - - mark?.addDetails({pluginCount: plugins.length}) - mark?.stop() - } - protected warn(err: string | Error | { name: string; detail: string }, scope?: string): void { if (this.warned) return @@ -691,7 +644,8 @@ export class Config implements IConfig { } private isJitPluginCommand(c: Command.Loadable): boolean { - return Object.keys(this.pjson.oclif.jitPlugins ?? {}).includes(c.pluginName ?? '') && !this.plugins.find(p => p.name === c?.pluginName) + // Return true if the command's plugin is listed under oclif.jitPlugins AND if the plugin hasn't been loaded to this.plugins + return Object.keys(this.pjson.oclif.jitPlugins ?? {}).includes(c.pluginName ?? '') && Boolean(c?.pluginName && !this.plugins.has(c.pluginName)) } private getCmdLookupId(id: string): string { @@ -709,6 +663,7 @@ export class Config implements IConfig { private loadCommands(plugin: IPlugin) { const marker = Performance.mark(`config.loadCommands#${plugin.name}`, {plugin: plugin.name}) for (const command of plugin.commands) { + // set canonical command id if (this._commands.has(command.id)) { const prioritizedCommand = this.determinePriority([this._commands.get(command.id)!, command]) this._commands.set(prioritizedCommand.id, prioritizedCommand) @@ -716,11 +671,12 @@ export class Config implements IConfig { this._commands.set(command.id, command) } - const permutations = this.flexibleTaxonomy ? getCommandIdPermutations(command.id) : [command.id] - for (const permutation of permutations) { + // set every permutation + for (const permutation of command.permutations ?? [command.id]) { this.commandPermutations.add(permutation, command.id) } + // set command aliases for (const alias of command.aliases ?? []) { if (this._commands.has(alias)) { const prioritizedCommand = this.determinePriority([this._commands.get(alias)!, command]) @@ -729,8 +685,8 @@ export class Config implements IConfig { this._commands.set(alias, {...command, id: alias}) } - const aliasPermutations = this.flexibleTaxonomy ? getCommandIdPermutations(alias) : [alias] - for (const permutation of aliasPermutations) { + // set every permutation of the aliases + for (const permutation of command.aliasPermutations ?? [alias]) { this.commandPermutations.add(permutation, command.id) } } @@ -842,24 +798,19 @@ export class Config implements IConfig { */ private insertLegacyPlugins(plugins: IPlugin[]) { for (const plugin of plugins) { - const idx = this.plugins.findIndex(p => p.name === plugin.name) - if (idx !== -1) { - // invalid plugin instance found in `this.plugins` - // replace with the oclif-compatible one - this.plugins.splice(idx, 1, plugin) - } - + this.plugins.set(plugin.name, plugin) this.loadCommands(plugin) } } } // when no manifest exists, the default is calculated. This may throw, so we need to catch it -const defaultFlagToCached = async (flag: CompletableOptionFlag, isWritingManifest = false) => { - // Prefer the helpDefaultValue function (returns a friendly string for complex types) +const defaultFlagToCached = async (flag: CompletableOptionFlag, respectNoCacheDefault: boolean) => { + if (respectNoCacheDefault && flag.noCacheDefault) return + // Prefer the defaultHelp function (returns a friendly string for complex types) if (typeof flag.defaultHelp === 'function') { try { - return await flag.defaultHelp({options: flag, flags: {}}, isWritingManifest) + return await flag.defaultHelp({options: flag, flags: {}}) } catch { return } @@ -868,18 +819,19 @@ const defaultFlagToCached = async (flag: CompletableOptionFlag, isWritingMa // if not specified, try the default function if (typeof flag.default === 'function') { try { - return await flag.default({options: flag, flags: {}}, isWritingManifest) + return await flag.default({options: flag, flags: {}}) } catch {} } else { return flag.default } } -const defaultArgToCached = async (arg: Arg, isWritingManifest = false): Promise => { - // Prefer the helpDefaultValue function (returns a friendly string for complex types) +const defaultArgToCached = async (arg: Arg, respectNoCacheDefault: boolean): Promise => { + if (respectNoCacheDefault && arg.noCacheDefault) return + // Prefer the defaultHelp function (returns a friendly string for complex types) if (typeof arg.defaultHelp === 'function') { try { - return await arg.defaultHelp({options: arg, flags: {}}, isWritingManifest) + return await arg.defaultHelp({options: arg, flags: {}}) } catch { return } @@ -888,14 +840,14 @@ const defaultArgToCached = async (arg: Arg, isWritingManifest = false): Pro // if not specified, try the default function if (typeof arg.default === 'function') { try { - return await arg.default({options: arg, flags: {}}, isWritingManifest) + return await arg.default({options: arg, flags: {}}) } catch {} } else { return arg.default } } -export async function toCached(c: Command.Class, plugin?: IPlugin | undefined, isWritingManifest?: boolean): Promise { +export async function toCached(c: Command.Class, plugin?: IPlugin, respectNoCacheDefault = false): Promise { const flags = {} as {[k: string]: Command.Flag.Cached} for (const [name, flag] of Object.entries(c.flags || {})) { @@ -918,6 +870,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin | undefined, i deprecateAliases: c.deprecateAliases, aliases: flag.aliases, delimiter: flag.delimiter, + noCacheDefault: flag.noCacheDefault, } } else { flags[name] = { @@ -936,11 +889,12 @@ export async function toCached(c: Command.Class, plugin?: IPlugin | undefined, i dependsOn: flag.dependsOn, relationships: flag.relationships, exclusive: flag.exclusive, - default: await defaultFlagToCached(flag, isWritingManifest), + default: await defaultFlagToCached(flag, respectNoCacheDefault), deprecated: flag.deprecated, deprecateAliases: c.deprecateAliases, aliases: flag.aliases, delimiter: flag.delimiter, + noCacheDefault: flag.noCacheDefault, } // a command-level placeholder in the manifest so that oclif knows it should regenerate the command during help-time if (typeof flag.defaultHelp === 'function') { @@ -956,8 +910,9 @@ export async function toCached(c: Command.Class, plugin?: IPlugin | undefined, i description: arg.description, required: arg.required, options: arg.options, - default: await defaultArgToCached(arg, isWritingManifest), + default: await defaultArgToCached(arg, respectNoCacheDefault), hidden: arg.hidden, + noCacheDefault: arg.noCacheDefault, } } diff --git a/src/config/index.ts b/src/config/index.ts index f83d0d268..a79d4c3bf 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,4 +1,5 @@ -export {Config, toCached} from './config' +export {Config} from './config' +export {toCached} from './config' export {Plugin} from './plugin' export {tsPath} from './ts-node' diff --git a/src/config/plugin-loader.ts b/src/config/plugin-loader.ts new file mode 100644 index 000000000..4da7520c2 --- /dev/null +++ b/src/config/plugin-loader.ts @@ -0,0 +1,145 @@ +import * as path from 'path' + +import {Options, Plugin as IPlugin} from '../interfaces/plugin' +import * as Plugin from './plugin' +import {loadJSON, Debug} from './util' +import {isProd} from '../util' +import Performance from '../performance' + +// eslint-disable-next-line new-cap +const debug = Debug() + +type PluginLoaderOptions = { + root: string; + plugins?: IPlugin[] | PluginsMap; +} + +type LoadOpts = { + devPlugins?: boolean; + userPlugins?: boolean; + dataDir: string; + rootPlugin: IPlugin; + force?: boolean; +} + +type PluginsMap = Map + +export default class PluginLoader { + public plugins: PluginsMap = new Map() + public errors: (string | Error)[] = [] + private pluginsProvided = false + + constructor(public options: PluginLoaderOptions) { + if (options.plugins) { + this.pluginsProvided = true + this.plugins = Array.isArray(options.plugins) ? new Map(options.plugins.map(p => [p.name, p])) : options.plugins + } + } + + public async loadRoot(): Promise { + let rootPlugin: IPlugin + if (this.pluginsProvided) { + const plugins = [...this.plugins.values()] + rootPlugin = plugins.find(p => p.root === this.options.root) ?? plugins[0] + } else { + rootPlugin = new Plugin.Plugin({root: this.options.root}) + await rootPlugin.load() + } + + this.plugins.set(rootPlugin.name, rootPlugin) + return rootPlugin + } + + public async loadChildren(opts: LoadOpts): Promise<{plugins: PluginsMap; errors: (string | Error)[]}> { + if (!this.pluginsProvided || opts.force) { + await this.loadUserPlugins(opts) + await this.loadDevPlugins(opts) + await this.loadCorePlugins(opts) + } + + return {plugins: this.plugins, errors: this.errors} + } + + private async loadCorePlugins(opts: LoadOpts): Promise { + if (opts.rootPlugin.pjson.oclif.plugins) { + await this.loadPlugins(opts.rootPlugin.root, 'core', opts.rootPlugin.pjson.oclif.plugins) + } + } + + private async loadDevPlugins(opts: LoadOpts): Promise { + if (opts.devPlugins !== false) { + // do not load oclif.devPlugins in production + if (isProd()) return + try { + const devPlugins = opts.rootPlugin.pjson.oclif.devPlugins + if (devPlugins) await this.loadPlugins(opts.rootPlugin.root, 'dev', devPlugins) + } catch (error: any) { + process.emitWarning(error) + } + } + } + + private async loadUserPlugins(opts: LoadOpts): Promise { + if (opts.userPlugins !== false) { + try { + const userPJSONPath = path.join(opts.dataDir, 'package.json') + debug('reading user plugins pjson %s', userPJSONPath) + const pjson = await loadJSON(userPJSONPath) + if (!pjson.oclif) pjson.oclif = {schema: 1} + if (!pjson.oclif.plugins) pjson.oclif.plugins = [] + await this.loadPlugins(userPJSONPath, 'user', pjson.oclif.plugins.filter((p: any) => p.type === 'user')) + await this.loadPlugins(userPJSONPath, 'link', pjson.oclif.plugins.filter((p: any) => p.type === 'link')) + } catch (error: any) { + if (error.code !== 'ENOENT') process.emitWarning(error) + } + } + } + + private async loadPlugins(root: string, type: string, plugins: (string | { root?: string; name?: string; tag?: string })[], parent?: Plugin.Plugin): Promise { + if (!plugins || plugins.length === 0) return + const mark = Performance.mark(`config.loadPlugins#${type}`) + debug('loading plugins', plugins) + await Promise.all((plugins || []).map(async plugin => { + try { + const name = typeof plugin === 'string' ? plugin : plugin.name! + const opts: Options = { + name, + type, + root, + } + if (typeof plugin !== 'string') { + opts.tag = plugin.tag || opts.tag + opts.root = plugin.root || opts.root + } + + if (this.plugins.has(name)) return + const pluginMarker = Performance.mark(`plugin.load#${name}`) + const instance = new Plugin.Plugin(opts) + await instance.load() + pluginMarker?.addDetails({ + hasManifest: instance.hasManifest, + commandCount: instance.commands.length, + topicCount: instance.topics.length, + type: instance.type, + usesMain: Boolean(instance.pjson.main), + name: instance.name, + }) + pluginMarker?.stop() + + this.plugins.set(instance.name, instance) + if (parent) { + instance.parent = parent + if (!parent.children) parent.children = [] + parent.children.push(instance) + } + + await this.loadPlugins(instance.root, type, instance.pjson.oclif.plugins || [], instance) + } catch (error: any) { + this.errors.push(error) + } + })) + + mark?.addDetails({pluginCount: plugins.length}) + mark?.stop() + } +} diff --git a/src/config/plugin.ts b/src/config/plugin.ts index 1a6d253cf..0d3f988b2 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -5,7 +5,7 @@ import {inspect} from 'util' import {Plugin as IPlugin, PluginOptions} from '../interfaces/plugin' import {toCached} from './config' -import {Debug} from './util' +import {Debug, getCommandIdPermutations} from './util' import {Manifest} from '../interfaces/manifest' import {PJSON} from '../interfaces/pjson' import {Topic} from '../interfaces/topic' @@ -14,7 +14,7 @@ import {compact, exists, resolvePackage, flatMap, loadJSON, mapValues} from './u import {isProd, requireJson} from '../util' import ModuleLoader from '../module-loader' import {Command} from '../command' -import {Performance} from '../performance' +import Performance from '../performance' const _pjson = requireJson(__dirname, '..', '..', 'package.json') @@ -94,7 +94,6 @@ async function findRoot(name: string | undefined, root: string) { } export class Plugin implements IPlugin { - // static loadedPlugins: {[name: string]: Plugin} = {} _base = `${_pjson.name}@${_pjson.version}` name!: string @@ -131,6 +130,8 @@ export class Plugin implements IPlugin { private _commandsDir!: string | undefined + private flexibleTaxonomy!: boolean + // eslint-disable-next-line new-cap protected _debug = Debug() @@ -138,25 +139,20 @@ export class Plugin implements IPlugin { constructor(public options: PluginOptions) {} - /** - * Loads a plugin - * @param isWritingManifest - if true, exclude selected data from manifest - * default is false to maintain backwards compatibility - * @returns Promise - */ - public async load(isWritingManifest?: boolean): Promise { + public async load(): Promise { this.type = this.options.type || 'core' this.tag = this.options.tag const root = await findRoot(this.options.name, this.options.root) - if (!root) throw new Error(`could not find package.json with ${inspect(this.options)}`) + if (!root) throw new CLIError(`could not find package.json with ${inspect(this.options)}`) this.root = root this._debug('reading %s plugin %s', this.type, root) this.pjson = await loadJSON(path.join(root, 'package.json')) + this.flexibleTaxonomy = this.options?.flexibleTaxonomy || this.pjson.oclif?.flexibleTaxonomy || false this.moduleType = this.pjson.type === 'module' ? 'module' : 'commonjs' this.name = this.pjson.name this.alias = this.options.name ?? this.pjson.name const pjsonPath = path.join(root, 'package.json') - if (!this.name) throw new Error(`no name in ${pjsonPath}`) + if (!this.name) throw new CLIError(`no name in ${pjsonPath}`) if (!isProd() && !this.pjson.files) this.warn(`files attribute must be specified in ${pjsonPath}`) // eslint-disable-next-line new-cap this._debug = Debug(this.name) @@ -169,7 +165,7 @@ export class Plugin implements IPlugin { this.hooks = mapValues(this.pjson.oclif.hooks || {}, i => Array.isArray(i) ? i : [i]) - this.manifest = await this._manifest(Boolean(this.options.ignoreManifest), Boolean(this.options.errorOnManifestCreate), isWritingManifest) + this.manifest = await this._manifest() this.commands = Object .entries(this.manifest.commands) .map(([id, c]) => ({ @@ -215,11 +211,11 @@ export class Plugin implements IPlugin { return ids } - async findCommand(id: string, opts: {must: true}): Promise + public async findCommand(id: string, opts: {must: true}): Promise - async findCommand(id: string, opts?: {must: boolean}): Promise + public async findCommand(id: string, opts?: {must: boolean}): Promise - async findCommand(id: string, opts: {must?: boolean} = {}): Promise { + public async findCommand(id: string, opts: {must?: boolean} = {}): Promise { const marker = Performance.mark(`plugin.findCommand#${this.name}.${id}`, {id, plugin: this.name}) const fetch = async () => { if (!this.commandsDir) return @@ -253,7 +249,11 @@ export class Plugin implements IPlugin { return cmd } - protected async _manifest(ignoreManifest: boolean, errorOnManifestCreate = false, isWritingManifest = false): Promise { + private async _manifest(): Promise { + const ignoreManifest = Boolean(this.options.ignoreManifest) + const errorOnManifestCreate = Boolean(this.options.errorOnManifestCreate) + const respectNoCacheDefault = Boolean(this.options.respectNoCacheDefault) + const readManifest = async (dotfile = false): Promise => { try { const p = path.join(this.root, `${dotfile ? '.' : ''}oclif.manifest.json`) @@ -288,7 +288,14 @@ export class Plugin implements IPlugin { version: this.version, commands: (await Promise.all(this.commandIDs.map(async id => { try { - return [id, await toCached(await this.findCommand(id, {must: true}), this, isWritingManifest)] + const cached = await toCached(await this.findCommand(id, {must: true}), this, respectNoCacheDefault) + if (this.flexibleTaxonomy) { + const permutations = getCommandIdPermutations(id) + const aliasPermutations = cached.aliases.flatMap(a => getCommandIdPermutations(a)) + return [id, {...cached, permutations, aliasPermutations} as Command.Cached] + } + + return [id, cached] } catch (error: any) { const scope = 'toCached' if (Boolean(errorOnManifestCreate) === false) this.warn(error, scope) @@ -306,7 +313,7 @@ export class Plugin implements IPlugin { return manifest } - protected warn(err: string | Error | CLIError, scope?: string): void { + private warn(err: string | Error | CLIError, scope?: string): void { if (this.warned) return if (typeof err === 'string') err = new Error(err) process.emitWarning(this.addErrorScope(err, scope)) diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index 478f5e2dc..d4fb96a9e 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -3,7 +3,7 @@ import * as path from 'path' import * as TSNode from 'ts-node' import {TSConfig, Plugin} from '../interfaces' -import {settings} from '../settings' +import settings from '../settings' import {isProd} from '../util' import {Debug} from './util' import {Config} from './config' diff --git a/src/errors/config.ts b/src/errors/config.ts index 7a757f969..b0876cada 100644 --- a/src/errors/config.ts +++ b/src/errors/config.ts @@ -1,4 +1,4 @@ -import {settings} from '../settings' +import settings from '../settings' import {Logger} from './logger' function displayWarnings() { diff --git a/src/errors/handle.ts b/src/errors/handle.ts index 2af24d151..81cb6ecf4 100644 --- a/src/errors/handle.ts +++ b/src/errors/handle.ts @@ -7,7 +7,7 @@ import clean = require('clean-stack') import {CLIError} from './errors/cli' import {OclifError, PrettyPrintableError} from '../interfaces' -export const handle = (err: Error & Partial & Partial & {skipOclifErrorHandling?: boolean}): void => { +export default async function handle(err: Error & Partial & Partial & {skipOclifErrorHandling?: boolean}): Promise { try { if (!err) err = new CLIError('no error?') if (err.message === 'SIGINT') process.exit(1) @@ -27,7 +27,7 @@ export const handle = (err: Error & Partial & Partial process.exit(exitCode)) .catch(console.error) } else process.exit(exitCode) diff --git a/src/errors/index.ts b/src/errors/index.ts index 4fbbbfe0e..a55c06bd2 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -1,4 +1,4 @@ -export {handle} from './handle' +export {default as handle} from './handle' export {ExitError} from './errors/exit' export {ModuleLoadError} from './errors/module-load' export {CLIError} from './errors/cli' diff --git a/src/errors/logger.ts b/src/errors/logger.ts index cff03cfb7..ece0311b2 100644 --- a/src/errors/logger.ts +++ b/src/errors/logger.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs-extra' -import * as path from 'path' +import * as fs from 'fs/promises' +import {dirname} from 'path' import stripAnsi = require('strip-ansi') const timestamp = () => new Date().toISOString() @@ -34,7 +34,7 @@ export class Logger { if (this.buffer.length === 0) return const mylines = this.buffer this.buffer = [] - await fs.mkdirp(path.dirname(this.file)) + await fs.mkdir(dirname(this.file), {recursive: true}) await fs.appendFile(this.file, mylines.join('\n') + '\n') }) await this.flushing diff --git a/src/execute.ts b/src/execute.ts new file mode 100644 index 000000000..a8761b9ad --- /dev/null +++ b/src/execute.ts @@ -0,0 +1,69 @@ +import settings from './settings' +import flush from './ux/flush' +import handle from './errors/handle' +import run from './main' +import * as Interfaces from './interfaces' + +/** + * Load and run oclif CLI + * + * @param options - options to load the CLI + * @returns Promise + * + * @example For ESM dev.js + * ``` + * #!/usr/bin/env node + * void (async () => { + * const oclif = await import('@oclif/core') + * await oclif.execute({development: true, dir: import.meta.url}) + * })() + * ``` + * + * @example For ESM run.js + * ``` + * #!/usr/bin/env node + * void (async () => { + * const oclif = await import('@oclif/core') + * await oclif.execute({dir: import.meta.url}) + * })() + * ``` + * + * @example For CJS dev.js + * ``` + * #!/usr/bin/env node + * void (async () => { + * const oclif = await import('@oclif/core') + * await oclif.execute({development: true, dir: __dirname}) + * })() + * ``` + * + * @example For CJS run.js + * ``` + * #!/usr/bin/env node + * void (async () => { + * const oclif = await import('@oclif/core') + * await oclif.execute({dir: __dirname}) + * })() + * ``` + */ +export default async function execute( + options: { + dir: string; + args?: string[]; + loadOptions?: Interfaces.LoadOptions; + development?: boolean; + }, +): Promise { + if (options.development) { + // In dev mode -> use ts-node and dev plugins + process.env.NODE_ENV = 'development' + settings.debug = true + } + + return run(options.args ?? process.argv.slice(2), options.loadOptions ?? options.dir) + .then(async result => { + flush() + return result + }) + .catch(async error => handle(error)) +} diff --git a/src/flags.ts b/src/flags.ts index a266d7377..dc99a670f 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -3,6 +3,7 @@ import {Help} from './help' import {BooleanFlag} from './interfaces' import {FlagDefinition, OptionFlagDefaults, FlagParser} from './interfaces/parser' import {dirExists, fileExists} from './util' +import {CLIError} from './errors' /** * Create a custom flag. @@ -55,12 +56,12 @@ export function boolean( export const integer = custom({ parse: async (input, _, opts) => { if (!/^-?\d+$/.test(input)) - throw new Error(`Expected an integer but received: ${input}`) + throw new CLIError(`Expected an integer but received: ${input}`) const num = Number.parseInt(input, 10) if (opts.min !== undefined && num < opts.min) - throw new Error(`Expected an integer greater than or equal to ${opts.min} but received: ${input}`) + throw new CLIError(`Expected an integer greater than or equal to ${opts.min} but received: ${input}`) if (opts.max !== undefined && num > opts.max) - throw new Error(`Expected an integer less than or equal to ${opts.max} but received: ${input}`) + throw new CLIError(`Expected an integer less than or equal to ${opts.max} but received: ${input}`) return num }, }) diff --git a/src/help/index.ts b/src/help/index.ts index e7fe8236a..37cf2bba9 100644 --- a/src/help/index.ts +++ b/src/help/index.ts @@ -9,7 +9,7 @@ import {formatCommandDeprecationWarning, getHelpFlagAdditions, standardizeIDFrom import {HelpFormatter} from './formatter' import {toCached} from '../config/config' import {Command} from '../command' -import {stdout} from '../cli-ux/stream' +import {stdout} from '../ux/stream' export {CommandHelp} from './command' export {standardizeIDFromArgv, loadHelpClass, getHelpFlagAdditions, normalizeArgv} from './util' @@ -106,7 +106,7 @@ export class Help extends HelpBase { const command = this.config.findCommand(subject) if (command) { if (command.hasDynamicHelp && command.pluginType !== 'jit') { - const dynamicCommand = await toCached(await command.load(), undefined, false) + const dynamicCommand = await toCached(await command.load()) await this.showCommandHelp(dynamicCommand) } else { await this.showCommandHelp(command) @@ -142,7 +142,7 @@ export class Help extends HelpBase { const subTopics = this.sortedTopics.filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1) const subCommands = this.sortedCommands.filter(c => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1) - const plugin = this.config.plugins.find(p => p.name === command.pluginName) + const plugin = this.config.plugins.get(command.pluginName!) const state = this.config.pjson?.oclif?.state || plugin?.pjson?.oclif?.state || command.state diff --git a/src/index.ts b/src/index.ts index 29a438e62..31b78759d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -import * as semver from 'semver' - import {Command} from './command' -import {run, execute} from './main' +import run from './main' +import execute from './execute' +import handle from './errors/handle' import {Config, Plugin, tsPath, toCached} from './config' import * as Interfaces from './interfaces' import * as Errors from './errors' @@ -11,14 +11,12 @@ import {CommandHelp, HelpBase, Help, loadHelpClass} from './help' import {toStandardizedId, toConfiguredId} from './help/util' import * as Parser from './parser' import {Hook} from './interfaces/hooks' -import {settings, Settings} from './settings' +import settings, {Settings} from './settings' import {HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable} from './help/formatter' -import * as ux from './cli-ux' -import {requireJson} from './util' -import {stderr, stdout} from './cli-ux/stream' -import {Performance} from './performance' - -const flush = ux.flush +import ux from './ux' +import {stderr, stdout} from './ux/stream' +import Performance from './performance' +import flush from './ux/flush' export { Args, @@ -26,30 +24,31 @@ export { CommandHelp, Config, Errors, + execute, Flags, - loadHelpClass, + flush, + handle, Help, HelpBase, HelpSection, - HelpSectionRenderer, HelpSectionKeyValueTable, + HelpSectionRenderer, Hook, Interfaces, + loadHelpClass, Parser, - Plugin, Performance, + Plugin, run, - toCached, - tsPath, - toStandardizedId, - toConfiguredId, settings, Settings, - flush, - ux, - execute, stderr, stdout, + toCached, + toConfiguredId, + toStandardizedId, + tsPath, + ux, } function checkCWD() { @@ -62,12 +61,4 @@ function checkCWD() { } } -function checkNodeVersion() { - const pjson = requireJson(__dirname, '..', 'package.json') - if (!semver.satisfies(process.versions.node, pjson.engines.node)) { - stderr.write(`WARNING\nWARNING Node version must be ${pjson.engines.node} to use this CLI\nWARNING Current node version: ${process.versions.node}\nWARNING\n`) - } -} - checkCWD() -checkNodeVersion() diff --git a/src/interfaces/config.ts b/src/interfaces/config.ts index cc75d0afa..c694e35ac 100644 --- a/src/interfaces/config.ts +++ b/src/interfaces/config.ts @@ -25,91 +25,90 @@ export type VersionDetails = { } export interface Config { - name: string; - version: string; - channel: string; - pjson: PJSON.CLI; - root: string; + readonly name: string; + readonly version: string; + readonly channel: string; + readonly pjson: PJSON.CLI; + readonly root: string; /** * process.arch */ - arch: ArchTypes; + readonly arch: ArchTypes; /** * bin name of CLI command */ - bin: string; + readonly bin: string; /** * cache directory to use for CLI * * example ~/Library/Caches/mycli or ~/.cache/mycli */ - cacheDir: string; + readonly cacheDir: string; /** * config directory to use for CLI * * example: ~/.config/mycli */ - configDir: string; + readonly configDir: string; /** * data directory to use for CLI * * example: ~/.local/share/mycli */ - dataDir: string; + readonly dataDir: string; /** * base dirname to use in cacheDir/configDir/dataDir */ - dirname: string; + readonly dirname: string; /** * points to a file that should be appended to for error logs * * example: ~/Library/Caches/mycli/error.log */ - errlog: string; + readonly errlog: string; /** * path to home directory * * example: /home/myuser */ - home: string; + readonly home: string; /** * process.platform */ - platform: PlatformTypes; + readonly platform: PlatformTypes; /** * active shell */ - shell: string; + readonly shell: string; /** * user agent to use for http calls * * example: mycli/1.2.3 (darwin-x64) node-9.0.0 */ - userAgent: string; + readonly userAgent: string; /** * if windows */ - windows: boolean; + readonly windows: boolean; /** * debugging level * * set by ${BIN}_DEBUG or DEBUG=$BIN */ - debug: number; + readonly debug: number; /** * npm registry to use for installing plugins */ - npmRegistry?: string; - userPJSON?: PJSON.User; - plugins: Plugin[]; - binPath?: string; + readonly npmRegistry?: string; + readonly plugins: Map; + readonly binPath?: string; /** * name of any bin aliases that will execute the cli */ - binAliases?: string[]; - nsisCustomization?: string; - valid: boolean; - flexibleTaxonomy?: boolean; + readonly binAliases?: string[]; + readonly nsisCustomization?: string; + readonly valid: boolean; + readonly flexibleTaxonomy?: boolean; topicSeparator: ':' | ' '; readonly commands: Command.Loadable[]; readonly topics: Topic[]; diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts index 631b4ed49..462a58113 100644 --- a/src/interfaces/parser.ts +++ b/src/interfaces/parser.ts @@ -59,24 +59,8 @@ export type DefaultContext = { /** * Type to define a default value for a flag. * @param context The context of the flag. - * @param isWritingManifest Informs the function that a manifest file is being written. - * The manifest file is used to store the flag definitions, with a default value if present, for a command and is published to npm. - * When a manifest file is being written, the default value may contain data that should not be included in the manifest. - * The plugin developer can use isWritingManifest to determine if the default value should be omitted from the manifest. - * in the function's implementation. - * @example - * static flags = { - * foo: flags.string({ - * defaultHelp: async (context, isWritingManifest) => { - * if (isWritingManifest) { - * return undefined - * } - * return 'value that is used outside a manifest' - * }, - * }), - * } */ -export type FlagDefault = T | ((context: DefaultContext

>, isWritingManifest?: boolean) => Promise) +export type FlagDefault = T | ((context: DefaultContext

>) => Promise) /** * Type to define a defaultHelp value for a flag. @@ -84,66 +68,20 @@ export type FlagDefault = T | ((context: DefaultContext

{ - * if (isWritingManifest) { - * return undefined - * } - * return 'value that is used outside a manifest' - * }, - * }), - * } */ -export type FlagDefaultHelp = T | ((context: DefaultContext

>, isWritingManifest?: boolean) => Promise) +export type FlagDefaultHelp = T | ((context: DefaultContext

>) => Promise) /** * Type to define a default value for an arg. * @param context The context of the arg. - * @param isWritingManifest Informs the function that a manifest file is being written. - * The manifest file is used to store the arg definitions, with a default value if present, for a command and is published to npm. - * When a manifest file is being written, the default value may contain data that should not be included in the manifest. - * The plugin developer can use isWritingManifest to determine if the default value should be omitted from the manifest. - * in the function's implementation. - * @example - * public static readonly args = { - * one: Args.string({ - * default: async (context, isWritingManifest) => { - * if (isWritingManifest) { - * return undefined - * } - * return 'value that is used outside a manifest' - * }), - * }; */ -export type ArgDefault = T | ((context: DefaultContext>, isWritingManifest?: boolean) => Promise) +export type ArgDefault = T | ((context: DefaultContext>) => Promise) /** * Type to define a defaultHelp value for an arg. * @param context The context of the arg. - * @param isWritingManifest Informs the function that a manifest file is being written. - * The manifest file is used to store the arg definitions, with a default value if present via defaultHelp, for a command and is published to npm. - * When a manifest file is being written, the default value may contain data that should not be included in the manifest. - * The plugin developer can use isWritingManifest to determine if the default value should be omitted from the manifest. - * in the function's implementation. - * @example - * public static readonly args = { - * one: Args.string({ - * defaultHelp: async (context, isWritingManifest) => { - * if (isWritingManifest) { - * return undefined - * } - * return 'value that is used outside a manifest' - * }), - * }; */ -export type ArgDefaultHelp = T | ((context: DefaultContext>, isWritingManifest?: boolean) => Promise) +export type ArgDefaultHelp = T | ((context: DefaultContext>) => Promise) export type FlagRelationship = string | {name: string; when: (flags: Record) => Promise}; export type Relationship = { @@ -226,6 +164,11 @@ export type FlagProps = { * separate on spaces. */ delimiter?: ',', + /** + * If true, the value returned by defaultHelp will not be cached in the oclif.manifest.json. + * This is helpful if the default value contains sensitive data that shouldn't be published to npm. + */ + noCacheDefault?: boolean; } export type ArgProps = { @@ -247,6 +190,11 @@ export type ArgProps = { options?: string[]; ignoreStdin?: boolean; + /** + * If true, the value returned by defaultHelp will not be cached in the oclif.manifest.json. + * This is helpful if the default value contains sensitive data that shouldn't be published to npm. + */ + noCacheDefault?: boolean; } export type BooleanFlagProps = FlagProps & { diff --git a/src/interfaces/pjson.ts b/src/interfaces/pjson.ts index 2834167ce..22f0af21f 100644 --- a/src/interfaces/pjson.ts +++ b/src/interfaces/pjson.ts @@ -2,6 +2,7 @@ import {HelpOptions} from './help' export interface PJSON { [k: string]: any; + version: string; dependencies?: {[name: string]: string}; devDependencies?: {[name: string]: string}; oclif: { diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index 617dc74ad..f4467e90b 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -9,8 +9,10 @@ export interface PluginOptions { tag?: string; ignoreManifest?: boolean; errorOnManifestCreate?: boolean; + respectNoCacheDefault?: boolean; parent?: Plugin; children?: Plugin[]; + flexibleTaxonomy?: boolean; } export interface Options extends PluginOptions { @@ -20,6 +22,7 @@ export interface Options extends PluginOptions { channel?: string; version?: string; enablePerf?: boolean; + plugins?: Map } export interface Plugin { @@ -77,5 +80,5 @@ export interface Plugin { findCommand(id: string, opts: { must: true }): Promise; findCommand(id: string, opts?: { must: boolean }): Promise | undefined; - load(isWritingManifest: boolean): Promise; + load(): Promise; } diff --git a/src/main.ts b/src/main.ts index 46e4b95f5..c0107d24c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,10 +6,8 @@ import * as Interfaces from './interfaces' import {URL} from 'url' import {Config} from './config' import {getHelpFlagAdditions, loadHelpClass, normalizeArgv} from './help' -import {settings} from './settings' -import {Errors, flush} from '.' -import {stdout} from './cli-ux/stream' -import {Performance} from './performance' +import {stdout} from './ux/stream' +import Performance from './performance' const debug = require('debug')('oclif:main') @@ -36,7 +34,7 @@ export const versionAddition = (argv: string[], config?: Interfaces.Config): boo return false } -export async function run(argv?: string[], options?: Interfaces.LoadOptions): Promise { +export default async function run(argv?: string[], options?: Interfaces.LoadOptions): Promise { const marker = Performance.mark('main.run') const initMarker = Performance.mark('main.run#init') @@ -104,64 +102,3 @@ export async function run(argv?: string[], options?: Interfaces.LoadOptions): Pr await collectPerf() } } - -/** - * Load and run oclif CLI - * - * @param options - options to load the CLI - * @returns Promise - * - * @example For ESM dev.js - * ``` - * #!/usr/bin/env node - * (async () => { - * const oclif = await import('@oclif/core') - * await oclif.execute({development: true, dir: import.meta.url}) - * })() - * ``` - * - * @example For ESM run.js - * ``` - * #!/usr/bin/env node - * (async () => { - * const oclif = await import('@oclif/core') - * await oclif.execute({dir: import.meta.url}) - * })() - * ``` - * - * @example For CJS dev.js - * ``` - * #!/usr/bin/env node - * (async () => { - * const oclif = await import('@oclif/core') - * await oclif.execute({development: true, dir: __dirname}) - * })() - * ``` - * - * @example For CJS run.js - * ``` - * #!/usr/bin/env node - * (async () => { - * const oclif = await import('@oclif/core') - * await oclif.execute({dir: __dirname}) - * })() - * ``` - */ -export async function execute( - options: { - dir: string; - args?: string[]; - loadOptions?: Interfaces.LoadOptions; - development?: boolean; - }, -): Promise { - if (options.development) { - // In dev mode -> use ts-node and dev plugins - process.env.NODE_ENV = 'development' - settings.debug = true - } - - await run(options.args ?? process.argv.slice(2), options.loadOptions ?? options.dir) - .then(async () => flush()) - .catch(Errors.handle) -} diff --git a/src/module-loader.ts b/src/module-loader.ts index dcacd81f4..715a2e7b2 100644 --- a/src/module-loader.ts +++ b/src/module-loader.ts @@ -1,10 +1,8 @@ import * as path from 'path' import * as url from 'url' -import * as fs from 'fs-extra' - +import {existsSync, lstatSync} from 'fs' import {ModuleLoadError} from './errors' -import {Config as IConfig} from './interfaces' -import {Plugin as IPlugin} from './interfaces' +import {Config as IConfig, Plugin as IPlugin} from './interfaces' import {tsPath} from './config' const getPackageType = require('get-package-type') @@ -145,10 +143,10 @@ export default class ModuleLoader { let fileExists = false let isDirectory = false - if (fs.existsSync(filePath)) { + if (existsSync(filePath)) { fileExists = true try { - if (fs.lstatSync(filePath)?.isDirectory?.()) { + if (lstatSync(filePath)?.isDirectory?.()) { fileExists = false isDirectory = true } @@ -186,7 +184,7 @@ export default class ModuleLoader { for (const extension of s_EXTENSIONS) { const testPath = `${filePath}${extension}` - if (fs.existsSync(testPath)) { + if (existsSync(testPath)) { return testPath } } diff --git a/src/parser/errors.ts b/src/parser/errors.ts index d6a832159..63fd884b8 100644 --- a/src/parser/errors.ts +++ b/src/parser/errors.ts @@ -1,7 +1,7 @@ import {CLIError} from '../errors' import {flagUsages} from './help' -import {renderList} from '../cli-ux/list' +import {renderList} from '../ux/list' import * as chalk from 'chalk' import {OptionFlag, Flag} from '../interfaces' import {uniq} from '../config/util' diff --git a/src/parser/parse.ts b/src/parser/parse.ts index 640c4c863..cc8d4a772 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -28,34 +28,7 @@ try { debug = () => {} } -/** - * Support reading from stdin in Node 14 and older. - * - * This generally works for Node 14 and older EXCEPT when it's being - * run from another process, in which case it will hang indefinitely. Because - * of that issue we updated this to use AbortController but since AbortController - * is only available in Node 16 and newer, we have to keep this legacy version. - * - * See these issues for more details on the hanging indefinitely bug: - * https://github.com/oclif/core/issues/330 - * https://github.com/oclif/core/pull/363 - * - * @returns Promise - */ -const readStdinLegacy = async (): Promise => { - const {stdin} = process - let result - if (stdin.isTTY) return null - result = '' - stdin.setEncoding('utf8') - for await (const chunk of stdin) { - result += chunk - } - - return result -} - -const readStdinWithTimeout = async (): Promise => { +const readStdin = async (): Promise => { const {stdin, stdout} = process // process.stdin.isTTY is true whenever it's running in a terminal. @@ -97,16 +70,6 @@ const readStdinWithTimeout = async (): Promise => { }) } -const readStdin = async (): Promise => { - const {stdin, version} = process - debug('stdin.isTTY', stdin.isTTY) - - const nodeMajorVersion = Number(version.split('.')[0].replace(/^v/, '')) - debug('node version', nodeMajorVersion) - - return nodeMajorVersion > 14 ? readStdinWithTimeout() : readStdinLegacy() -} - function isNegativeNumber(input: string): boolean { return /^-\d/g.test(input) } diff --git a/src/performance.ts b/src/performance.ts index 7cc2664f1..d1e58f8d7 100644 --- a/src/performance.ts +++ b/src/performance.ts @@ -1,5 +1,5 @@ import {PerformanceObserver, performance} from 'perf_hooks' -import {settings} from './settings' +import settings from './settings' type Details = Record type PerfResult = { @@ -58,7 +58,7 @@ class Marker { } } -export class Performance { +export default class Performance { private static markers: Record = {} private static _results: PerfResult[] = [] private static _highlights: PerfHighlights diff --git a/src/screen.ts b/src/screen.ts index 68b1e6fbb..cc86f4500 100644 --- a/src/screen.ts +++ b/src/screen.ts @@ -1,5 +1,5 @@ -import {stdout, stderr} from './cli-ux/stream' -import {settings} from './settings' +import {stdout, stderr} from './ux/stream' +import settings from './settings' function termwidth(stream: any): number { if (!stream.isTTY) { diff --git a/src/settings.ts b/src/settings.ts index 9f797f800..dd94d6d84 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -40,4 +40,5 @@ export type Settings = { // Set global.oclif to the new object if it wasn't set before if (!(global as any).oclif) (global as any).oclif = {} -export const settings: Settings = (global as any).oclif as Settings +const settings: Settings = (global as any).oclif as Settings +export default settings diff --git a/src/cli-ux/README.md b/src/ux/README.md similarity index 100% rename from src/cli-ux/README.md rename to src/ux/README.md diff --git a/src/cli-ux/action/base.ts b/src/ux/action/base.ts similarity index 97% rename from src/cli-ux/action/base.ts rename to src/ux/action/base.ts index 9a53d193e..6e71dcb59 100644 --- a/src/cli-ux/action/base.ts +++ b/src/ux/action/base.ts @@ -49,8 +49,8 @@ export class ActionBase { } private get globals(): { action: { task?: ITask }; output: string | undefined } { - (global as any)['cli-ux'] = (global as any)['cli-ux'] || {} - const globals = (global as any)['cli-ux'] + (global as any).ux = (global as any).ux || {} + const globals = (global as any).ux globals.action = globals.action || {} return globals } diff --git a/src/cli-ux/action/simple.ts b/src/ux/action/simple.ts similarity index 100% rename from src/cli-ux/action/simple.ts rename to src/ux/action/simple.ts diff --git a/src/cli-ux/action/spinner.ts b/src/ux/action/spinner.ts similarity index 98% rename from src/cli-ux/action/spinner.ts rename to src/ux/action/spinner.ts index 3b205dede..05ca3e50d 100644 --- a/src/cli-ux/action/spinner.ts +++ b/src/ux/action/spinner.ts @@ -2,7 +2,6 @@ import * as chalk from 'chalk' import * as supportsColor from 'supports-color' const stripAnsi = require('strip-ansi') const ansiStyles = require('ansi-styles') -const ansiEscapes = require('ansi-escapes') import {errtermwidth} from '../../screen' import spinners from './spinners' import {ActionBase, ActionType} from './base' @@ -74,6 +73,7 @@ export default class SpinnerAction extends ActionBase { private _reset() { if (!this.output) return + const ansiEscapes = require('ansi-escapes') const lines = this._lines(this.output) this._write(this.std, ansiEscapes.cursorLeft + ansiEscapes.cursorUp(lines) + ansiEscapes.eraseDown) this.output = undefined diff --git a/src/cli-ux/action/spinners.ts b/src/ux/action/spinners.ts similarity index 100% rename from src/cli-ux/action/spinners.ts rename to src/ux/action/spinners.ts diff --git a/src/cli-ux/config.ts b/src/ux/config.ts similarity index 69% rename from src/cli-ux/config.ts rename to src/ux/config.ts index 9b567b28b..5734a5746 100644 --- a/src/cli-ux/config.ts +++ b/src/ux/config.ts @@ -1,13 +1,9 @@ -import * as semver from 'semver' import {PJSON} from '../interfaces/pjson' import {requireJson} from '../util' import spinner from './action/spinner' import simple from './action/spinner' -import pride from './action/pride-spinner' import {ActionBase} from './action/base' -const version = semver.parse(requireJson(__dirname, '..', '..', 'package.json').version)! - export type Levels = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' export interface ConfigMessage { @@ -17,7 +13,7 @@ export interface ConfigMessage { } const g: any = global -const globals = g['cli-ux'] || (g['cli-ux'] = {}) +const globals = g.ux || (g.ux = {}) const actionType = ( Boolean(process.stderr.isTTY) && @@ -27,15 +23,12 @@ const actionType = ( ) || 'simple' const Action = actionType === 'spinner' ? spinner : simple -const PrideAction = actionType === 'spinner' ? pride : simple export class Config { outputLevel: Levels = 'info' action: ActionBase = new Action() - prideAction: ActionBase = new PrideAction() - errorsHandled = false showStackTrace = true @@ -58,9 +51,10 @@ export class Config { } function fetch() { - if (globals[version.major]) return globals[version.major] - globals[version.major] = new Config() - return globals[version.major] + const major = requireJson(__dirname, '..', '..', 'package.json').version.split('.')[0] + if (globals[major]) return globals[major] + globals[major] = new Config() + return globals[major] } export const config: Config = fetch() diff --git a/src/cli-ux/exit.ts b/src/ux/exit.ts similarity index 83% rename from src/cli-ux/exit.ts rename to src/ux/exit.ts index 4f036df30..a7ce32f27 100644 --- a/src/cli-ux/exit.ts +++ b/src/ux/exit.ts @@ -1,5 +1,5 @@ export class ExitError extends Error { - public 'cli-ux': { + public ux: { exit: number; } @@ -11,7 +11,7 @@ export class ExitError extends Error { const code = 'EEXIT' super(error ? error.message : `${code}: ${status}`) this.error = error - this['cli-ux'] = {exit: status} + this.ux = {exit: status} this.code = code } } diff --git a/src/ux/flush.ts b/src/ux/flush.ts new file mode 100644 index 000000000..ceae1d816 --- /dev/null +++ b/src/ux/flush.ts @@ -0,0 +1,29 @@ +import {Errors, stdout} from '..' + +function timeout(p: Promise, ms: number) { + function wait(ms: number, unref = false) { + return new Promise(resolve => { + const t: any = setTimeout(() => resolve(null), ms) + if (unref) t.unref() + }) + } + + return Promise.race([p, wait(ms, true).then(() => Errors.error('timed out'))]) +} + +async function _flush() { + const p = new Promise(resolve => { + stdout.once('drain', () => resolve(null)) + }) + const flushed = stdout.write('') + + if (flushed) { + return Promise.resolve() + } + + return p +} + +export default async function flush(ms = 10_000): Promise { + await timeout(_flush(), ms) +} diff --git a/src/cli-ux/global.d.ts b/src/ux/global.d.ts similarity index 73% rename from src/cli-ux/global.d.ts rename to src/ux/global.d.ts index 701002e3a..08e1e9e13 100644 --- a/src/cli-ux/global.d.ts +++ b/src/ux/global.d.ts @@ -1,5 +1,5 @@ declare namespace NodeJS { interface Global { - 'cli-ux': any; + ux: any; } } diff --git a/src/cli-ux/index.ts b/src/ux/index.ts similarity index 61% rename from src/cli-ux/index.ts rename to src/ux/index.ts index 32fcebec1..fd7b2a67c 100644 --- a/src/cli-ux/index.ts +++ b/src/ux/index.ts @@ -10,34 +10,11 @@ import {Table} from './styled' import * as uxPrompt from './prompt' import uxWait from './wait' import {stdout} from './stream' +import flush from './flush' const hyperlinker = require('hyperlinker') -function timeout(p: Promise, ms: number) { - function wait(ms: number, unref = false) { - return new Promise(resolve => { - const t: any = setTimeout(() => resolve(null), ms) - if (unref) t.unref() - }) - } - - return Promise.race([p, wait(ms, true).then(() => Errors.error('timed out'))]) -} - -async function _flush() { - const p = new Promise(resolve => { - stdout.once('drain', () => resolve(null)) - }) - const flushed = stdout.write('') - - if (flushed) { - return Promise.resolve() - } - - return p -} - -export class ux { +export default class ux { public static config: Config = config public static get prompt(): typeof uxPrompt.prompt { @@ -59,17 +36,13 @@ export class ux { return config.action } - public static get prideAction(): ActionBase { - return config.prideAction - } - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types public static styledObject(obj: any, keys?: string[]): void { - this.info(styled.styledObject(obj, keys)) + this.log(styled.styledObject(obj, keys)) } public static styledHeader(header: string): void { - this.info(chalk.dim('=== ') + chalk.bold(header) + '\n') + this.log(chalk.dim('=== ') + chalk.bold(header) + '\n') } public static get styledJSON(): typeof styled.styledJSON { @@ -97,13 +70,13 @@ export class ux { } public static trace(format: string, ...args: string[]): void { - if (this.config.outputLevel === 'trace') { + if (ux.config.outputLevel === 'trace') { stdout.write(util.format(format, ...args) + '\n') } } public static debug(format: string, ...args: string[]): void { - if (['trace', 'debug'].includes(this.config.outputLevel)) { + if (['trace', 'debug'].includes(ux.config.outputLevel)) { stdout.write(util.format(format, ...args) + '\n') } } @@ -136,67 +109,32 @@ export class ux { } public static async flush(ms = 10_000): Promise { - await timeout(_flush(), ms) + await flush(ms) + } + + public static error(err: Error | string, options: {code?: string; exit?: number} = {}): never { + throw Errors.error(err, options) } -} -const action = ux.action -const annotation = ux.annotation -const anykey = ux.anykey -const confirm = ux.confirm -const debug = ux.debug -const done = ux.done -const error = Errors.error -const exit = Errors.exit -const flush = ux.flush -const info = ux.info -const log = ux.log -const prideAction = ux.prideAction -const progress = ux.progress -const prompt = ux.prompt -const styledHeader = ux.styledHeader -const styledJSON = ux.styledJSON -const styledObject = ux.styledObject -const table = ux.table -const trace = ux.trace -const tree = ux.tree -const url = ux.url -const wait = ux.wait -const warn = Errors.warn + public static exit(code = 0): never { + throw Errors.exit(code) + } + + public static warn(err: Error | string): void { + Errors.warn(err) + } +} export { - action, ActionBase, - annotation, - anykey, config, Config, - confirm, - debug, - done, - error, - exit, ExitError, - flush, - info, IPromptOptions, - log, - prideAction, - progress, - prompt, - styledHeader, - styledJSON, - styledObject, - table, Table, - trace, - tree, - url, - wait, - warn, } -const cliuxProcessExitHandler = async () => { +const uxProcessExitHandler = async () => { try { await ux.done() } catch (error) { @@ -207,7 +145,7 @@ const cliuxProcessExitHandler = async () => { // to avoid MaxListenersExceededWarning // only attach named listener once -const cliuxListener = process.listeners('exit').find(fn => fn.name === cliuxProcessExitHandler.name) -if (!cliuxListener) { - process.once('exit', cliuxProcessExitHandler) +const uxListener = process.listeners('exit').find(fn => fn.name === uxProcessExitHandler.name) +if (!uxListener) { + process.once('exit', uxProcessExitHandler) } diff --git a/src/cli-ux/list.ts b/src/ux/list.ts similarity index 100% rename from src/cli-ux/list.ts rename to src/ux/list.ts diff --git a/src/cli-ux/prompt.ts b/src/ux/prompt.ts similarity index 97% rename from src/cli-ux/prompt.ts rename to src/ux/prompt.ts index 3fc303151..7f4d7a0cc 100644 --- a/src/cli-ux/prompt.ts +++ b/src/ux/prompt.ts @@ -3,8 +3,6 @@ import config from './config' import * as chalk from 'chalk' import {stderr} from './stream' -const ansiEscapes = require('ansi-escapes') -const passwordPrompt = require('password-prompt') export interface IPromptOptions { prompt?: string; @@ -78,6 +76,7 @@ async function single(options: IPromptConfig): Promise { } function replacePrompt(prompt: string) { + const ansiEscapes = require('ansi-escapes') stderr.write(ansiEscapes.cursorHide + ansiEscapes.cursorUp(1) + ansiEscapes.cursorLeft + prompt + ansiEscapes.cursorDown(1) + ansiEscapes.cursorLeft + ansiEscapes.cursorShow) } @@ -93,6 +92,7 @@ async function _prompt(name: string, inputOptions: Partial = {}) default: '', ...inputOptions, } + const passwordPrompt = require('password-prompt') switch (options.type) { case 'normal': return normal(options) diff --git a/src/cli-ux/stream.ts b/src/ux/stream.ts similarity index 100% rename from src/cli-ux/stream.ts rename to src/ux/stream.ts diff --git a/src/cli-ux/styled/index.ts b/src/ux/styled/index.ts similarity index 100% rename from src/cli-ux/styled/index.ts rename to src/ux/styled/index.ts diff --git a/src/cli-ux/styled/json.ts b/src/ux/styled/json.ts similarity index 91% rename from src/cli-ux/styled/json.ts rename to src/ux/styled/json.ts index 6da73b23c..363b4927a 100644 --- a/src/cli-ux/styled/json.ts +++ b/src/ux/styled/json.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk' -import {ux} from '../../index' +import ux from '../index' export default function styledJSON(obj: unknown): void { const json = JSON.stringify(obj, null, 2) diff --git a/src/cli-ux/styled/object.ts b/src/ux/styled/object.ts similarity index 100% rename from src/cli-ux/styled/object.ts rename to src/ux/styled/object.ts diff --git a/src/cli-ux/styled/progress.ts b/src/ux/styled/progress.ts similarity index 100% rename from src/cli-ux/styled/progress.ts rename to src/ux/styled/progress.ts diff --git a/src/cli-ux/styled/table.ts b/src/ux/styled/table.ts similarity index 100% rename from src/cli-ux/styled/table.ts rename to src/ux/styled/table.ts diff --git a/src/cli-ux/styled/tree.ts b/src/ux/styled/tree.ts similarity index 100% rename from src/cli-ux/styled/tree.ts rename to src/ux/styled/tree.ts diff --git a/src/cli-ux/wait.ts b/src/ux/wait.ts similarity index 100% rename from src/cli-ux/wait.ts rename to src/ux/wait.ts diff --git a/test/cli-ux/export.test.ts b/test/cli-ux/export.test.ts index 4dfb48a20..5caa755aa 100644 --- a/test/cli-ux/export.test.ts +++ b/test/cli-ux/export.test.ts @@ -1,23 +1,23 @@ -import {ux} from '../../src' +import ux, {Table, IPromptOptions, Config, ActionBase, ExitError} from '../../src/ux' import {expect} from 'chai' type MyColumns = Record -const options: ux.Table.table.Options = {} -const columns: ux.Table.table.Columns = {} -const iPromptOptions: ux.IPromptOptions = {} +const options: Table.table.Options = {} +const columns: Table.table.Columns = {} +const iPromptOptions: IPromptOptions = {} -describe('cli-ux exports', () => { +describe('ux exports', () => { it('should have exported members on par with old cli-ux module', () => { expect(options).to.be.ok expect(columns).to.be.ok expect(iPromptOptions).to.be.ok - expect(ux.Table.table.Flags).to.be.ok - expect(typeof ux.Table.table.flags).to.be.equal('function') - expect(typeof ux.Table.table).to.be.equal('function') + expect(Table.table.Flags).to.be.ok + expect(typeof Table.table.flags).to.be.equal('function') + expect(typeof Table.table).to.be.equal('function') expect(ux.config).to.be.ok - expect(typeof ux.Config).to.be.equal('function') - expect(typeof ux.ActionBase).to.be.equal('function') - expect(typeof ux.ExitError).to.be.equal('function') + expect(typeof Config).to.be.equal('function') + expect(typeof ActionBase).to.be.equal('function') + expect(typeof ExitError).to.be.equal('function') }) }) diff --git a/test/cli-ux/fancy.ts b/test/cli-ux/fancy.ts index f006e1bc1..9f7a60bbf 100644 --- a/test/cli-ux/fancy.ts +++ b/test/cli-ux/fancy.ts @@ -1,8 +1,8 @@ import {expect, fancy as base, FancyTypes} from 'fancy-test' -import * as fs from 'fs-extra' -import * as path from 'path' +import {rm} from 'node:fs/promises' +import {join} from 'node:path' -import {ux} from '../../src' +import ux from '../../src/ux' export { expect, @@ -14,8 +14,8 @@ let count = 0 export const fancy = base .do(async (ctx: {count: number; base: string}) => { ctx.count = count++ - ctx.base = path.join(__dirname, '../tmp', `test-${ctx.count}`) - await fs.remove(ctx.base) + ctx.base = join(__dirname, '../tmp', `test-${ctx.count}`) + await rm(ctx.base, {recursive: true, force: true}) const chalk = require('chalk') chalk.level = 0 }) diff --git a/test/cli-ux/index.test.ts b/test/cli-ux/index.test.ts index 6d9e14e9e..234c9d025 100644 --- a/test/cli-ux/index.test.ts +++ b/test/cli-ux/index.test.ts @@ -1,4 +1,4 @@ -import {ux} from '../../src' +import ux from '../../src/ux' import {expect, fancy} from './fancy' const hyperlinker = require('hyperlinker') diff --git a/test/cli-ux/prompt.test.ts b/test/cli-ux/prompt.test.ts index 08426d680..18676fb74 100644 --- a/test/cli-ux/prompt.test.ts +++ b/test/cli-ux/prompt.test.ts @@ -2,7 +2,7 @@ import * as chai from 'chai' const expect = chai.expect -import {ux} from '../../src' +import ux from '../../src/ux' import {fancy} from './fancy' diff --git a/test/cli-ux/styled/header.test.ts b/test/cli-ux/styled/header.test.ts index b5b084da9..619d071a8 100644 --- a/test/cli-ux/styled/header.test.ts +++ b/test/cli-ux/styled/header.test.ts @@ -1,7 +1,8 @@ import {expect} from 'chai' -import {ux, stdout} from '../../../src' +import {stdout} from '../../../src' import {stub, SinonStub} from 'sinon' +import ux from '../../../src/ux' describe('styled/header', () => { let writeStub: SinonStub diff --git a/test/cli-ux/styled/object.test.ts b/test/cli-ux/styled/object.test.ts index d2dd2aa09..f0e4d938e 100644 --- a/test/cli-ux/styled/object.test.ts +++ b/test/cli-ux/styled/object.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import {ux} from '../../../src' +import ux from '../../../src/ux' describe('styled/object', () => { fancy diff --git a/test/cli-ux/styled/progress.test.ts b/test/cli-ux/styled/progress.test.ts index 300f726c0..098ec0658 100644 --- a/test/cli-ux/styled/progress.test.ts +++ b/test/cli-ux/styled/progress.test.ts @@ -1,5 +1,5 @@ import {expect, fancy} from 'fancy-test' -import {ux} from '../../../src' +import ux from '../../../src/ux' describe('progress', () => { // single bar diff --git a/test/cli-ux/styled/table.e2e.ts b/test/cli-ux/styled/table.e2e.ts index dba2df692..a5b162d11 100644 --- a/test/cli-ux/styled/table.e2e.ts +++ b/test/cli-ux/styled/table.e2e.ts @@ -1,5 +1,5 @@ import {expect, fancy} from 'fancy-test' -import {ux} from '../../../src' +import ux from '../../../src/ux' describe('styled/table', () => { describe('null/undefined handling', () => { diff --git a/test/cli-ux/styled/table.test.ts b/test/cli-ux/styled/table.test.ts index 977954dd4..90056da6f 100644 --- a/test/cli-ux/styled/table.test.ts +++ b/test/cli-ux/styled/table.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import {ux} from '../../../src' +import ux from '../../../src/ux' import * as screen from '../../../src/screen' /* eslint-disable camelcase */ diff --git a/test/cli-ux/styled/tree.test.ts b/test/cli-ux/styled/tree.test.ts index 56ffb0e07..5e4e470b4 100644 --- a/test/cli-ux/styled/tree.test.ts +++ b/test/cli-ux/styled/tree.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import {ux} from '../../../src' +import ux from '../../../src/ux' describe('styled/tree', () => { fancy diff --git a/test/command/command.test.ts b/test/command/command.test.ts index ecade0257..4b2cae5d3 100644 --- a/test/command/command.test.ts +++ b/test/command/command.test.ts @@ -152,6 +152,7 @@ describe('command', () => { allowNo: false, type: 'boolean', delimiter: undefined, + noCacheDefault: undefined, }, flagb: { aliases: undefined, @@ -174,6 +175,7 @@ describe('command', () => { default: 'a', options: ['a', 'b'], delimiter: undefined, + noCacheDefault: undefined, }, flagc: { aliases: undefined, @@ -199,6 +201,7 @@ describe('command', () => { required: false, summary: undefined, type: 'option', + noCacheDefault: undefined, }, }, @@ -210,6 +213,7 @@ describe('command', () => { required: true, options: ['af', 'b'], default: 'a', + noCacheDefault: undefined, }, }, }) diff --git a/test/command/main-esm.test.ts b/test/command/main-esm.test.ts index 2df93664a..9596116ce 100644 --- a/test/command/main-esm.test.ts +++ b/test/command/main-esm.test.ts @@ -2,7 +2,7 @@ import {expect, fancy} from 'fancy-test' import * as path from 'path' import * as url from 'url' -import {run} from '../../src/main' +import run from '../../src/main' // This tests file URL / import.meta.url simulation. const convertToFileURL = (filepath: string) => url.pathToFileURL(filepath).toString() @@ -16,6 +16,7 @@ root = convertToFileURL(root) describe('main-esm', () => { fancy .stdout() + .skip() // skip until oclif/test is on v3 .do(() => run(['plugins'], root)) .do((output: any) => expect(output.stdout).to.equal('No plugins installed.\n')) .it('runs plugins') diff --git a/test/command/main.test.ts b/test/command/main.test.ts index 5789742fd..41116965d 100644 --- a/test/command/main.test.ts +++ b/test/command/main.test.ts @@ -4,7 +4,7 @@ import * as path from 'path' import {createSandbox, SinonSandbox, SinonStub} from 'sinon' import stripAnsi = require('strip-ansi') import {requireJson} from '../../src/util' -import {run} from '../../src/main' +import run from '../../src/main' import {Interfaces, stdout} from '../../src/index' const pjson = requireJson(__dirname, '..', '..', 'package.json') diff --git a/test/config/config.flexible.test.ts b/test/config/config.flexible.test.ts index 20c1957b5..f8ea56f82 100644 --- a/test/config/config.flexible.test.ts +++ b/test/config/config.flexible.test.ts @@ -7,6 +7,7 @@ import {Plugin as IPlugin} from '../../src/interfaces' import {expect, fancy} from './test' import {Flags, Interfaces} from '../../src' import {Command} from '../../src/command' +import {getCommandIdPermutations} from '../../src/config/util' interface Options { pjson?: any; @@ -66,6 +67,7 @@ describe('Config with flexible taxonomy', () => { }, pluginType: types[0] ?? 'core', pluginAlias: '@My/plugina', + permutations: getCommandIdPermutations(commandIds[0]), } const commandPluginB: Command.Loadable = { strict: false, @@ -81,6 +83,7 @@ describe('Config with flexible taxonomy', () => { }, pluginType: types[1] ?? 'core', pluginAlias: '@My/pluginb', + permutations: getCommandIdPermutations(commandIds[1]), } const hooks = {} const pluginA: IPlugin = { @@ -120,7 +123,7 @@ describe('Config with flexible taxonomy', () => { tag: 'tag', moduleType: 'commonjs', } - const plugins: IPlugin[] = [pluginA, pluginB] + const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) test = test.add('config', async () => { const config = await Config.load() @@ -128,7 +131,7 @@ describe('Config with flexible taxonomy', () => { config.plugins = plugins config.pjson.oclif.plugins = ['@My/pluginb', '@My/plugina'] config.pjson.dependencies = {'@My/pluginb': '0.0.0', '@My/plugina': '0.0.0'} - for (const plugin of config.plugins) { + for (const plugin of config.plugins.values()) { // @ts-expect-error private method config.loadCommands(plugin) // @ts-expect-error private method diff --git a/test/config/config.test.ts b/test/config/config.test.ts index 9b4b062d7..bafbb852e 100644 --- a/test/config/config.test.ts +++ b/test/config/config.test.ts @@ -5,7 +5,8 @@ import {Plugin as IPlugin} from '../../src/interfaces' import * as util from '../../src/config/util' import {expect, fancy} from './test' -import {Command, Config, Interfaces} from '../../src' +import {Config, Interfaces} from '../../src' +import {Command} from '../../src/command' interface Options { pjson?: any; @@ -63,7 +64,9 @@ describe('Config', () => { // Config.load reads the package.json to determine the version and channel // In order to allow prerelease branches to pass, we need to strip the prerelease // tag from the version and switch the channel to stable. + // @ts-expect-error because readonly property config.version = config.version.replace(/-beta\.\d/g, '') + // @ts-expect-error because readonly property config.channel = 'stable' // eslint-disable-next-line prefer-const @@ -303,7 +306,7 @@ describe('Config', () => { tag: 'tag', moduleType: 'commonjs', } - const plugins: IPlugin[] = [pluginA, pluginB] + const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) let test = fancy .resetConfig() .env(env, {clear: true}) @@ -316,7 +319,7 @@ describe('Config', () => { config.plugins = plugins config.pjson.oclif.plugins = ['@My/pluginb', '@My/plugina'] config.pjson.dependencies = {'@My/pluginb': '0.0.0', '@My/plugina': '0.0.0'} - for (const plugin of config.plugins) { + for (const plugin of config.plugins.values()) { // @ts-expect-error private method config.loadCommands(plugin) // @ts-expect-error private method diff --git a/test/config/esm.test.ts b/test/config/esm.test.ts index 03bc0ca56..57bfc4709 100644 --- a/test/config/esm.test.ts +++ b/test/config/esm.test.ts @@ -17,7 +17,7 @@ const withConfig = fancy describe('esm', () => { withConfig .it('has commandsDir', ({config}) => { - expect(config.plugins[0]).to.deep.include({ + expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) diff --git a/test/config/mixed-cjs-esm.test.ts b/test/config/mixed-cjs-esm.test.ts index 4025dfde5..45053773f 100644 --- a/test/config/mixed-cjs-esm.test.ts +++ b/test/config/mixed-cjs-esm.test.ts @@ -13,7 +13,7 @@ const withConfig = fancy describe('mixed-cjs-esm', () => { withConfig .it('has commandsDir', ({config}) => { - expect(config.plugins[0]).to.deep.include({ + expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) diff --git a/test/config/mixed-esm-cjs.test.ts b/test/config/mixed-esm-cjs.test.ts index dec3b4f00..c9f44daee 100644 --- a/test/config/mixed-esm-cjs.test.ts +++ b/test/config/mixed-esm-cjs.test.ts @@ -13,7 +13,7 @@ const withConfig = fancy describe('mixed-cjs-esm', () => { withConfig .it('has commandsDir', ({config}) => { - expect(config.plugins[0]).to.deep.include({ + expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) diff --git a/test/config/typescript.test.ts b/test/config/typescript.test.ts index 1770e9ce7..b8da2b69a 100644 --- a/test/config/typescript.test.ts +++ b/test/config/typescript.test.ts @@ -13,7 +13,7 @@ const withConfig = fancy describe('typescript', () => { withConfig .it('has commandsDir', ({config}) => { - expect(config.plugins[0]).to.deep.include({ + expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) diff --git a/test/errors/handle.test.ts b/test/errors/handle.test.ts index 6cb5c2d47..db08132a6 100644 --- a/test/errors/handle.test.ts +++ b/test/errors/handle.test.ts @@ -1,10 +1,10 @@ import {expect, fancy} from 'fancy-test' -import * as fs from 'fs-extra' +import {readFileSync} from 'node:fs' import * as path from 'path' import * as process from 'process' import {CLIError, config, ExitError} from '../../src/errors' -import {handle} from '../../src/errors/handle' +import handle from '../../src/errors/handle' import {exit as exitErrorThrower} from '../../src/errors' const errlog = path.join(__dirname, '../tmp/mytest/error.log') @@ -106,7 +106,7 @@ describe('handle', () => { handle(new CLIError('uh oh!')) expect(ctx.stderr).to.equal(` ${x} Error: uh oh!\n`) await config.errorLogger!.flush() - expect(fs.readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!') + expect(readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!') expect(process.exitCode).to.equal(2) }) diff --git a/test/errors/warn.test.ts b/test/errors/warn.test.ts index 65567a953..3f807a620 100644 --- a/test/errors/warn.test.ts +++ b/test/errors/warn.test.ts @@ -1,10 +1,10 @@ import {expect, fancy} from 'fancy-test' -import * as fs from 'fs-extra' -import * as path from 'path' +import {readFile} from 'node:fs/promises' +import {join} from 'node:path' import {config, warn} from '../../src/errors' -const errlog = path.join(__dirname, '../tmp/mytest/warn.log') +const errlog = join(__dirname, '../tmp/mytest/warn.log') describe('warn', () => { fancy @@ -20,6 +20,6 @@ describe('warn', () => { expect(ctx.stderr).to.contain('Warning: foo!') expect(process.exitCode).to.be.undefined await config.errorLogger!.flush() - expect(fs.readFileSync(errlog, 'utf8')).to.contain('Warning: foo!') + expect(await readFile(errlog, 'utf8')).to.contain('Warning: foo!') }) }) diff --git a/test/help/format-commands.test.ts b/test/help/format-commands.test.ts index eb59c9ce0..ec88b81a3 100644 --- a/test/help/format-commands.test.ts +++ b/test/help/format-commands.test.ts @@ -1,4 +1,4 @@ -import {Command} from '../../src' +import {Command} from '../../src/command' import {expect, test as base} from '@oclif/test' import stripAnsi = require('strip-ansi') diff --git a/test/help/help-test-utils.ts b/test/help/help-test-utils.ts index a58ca8a89..e9542dc16 100644 --- a/test/help/help-test-utils.ts +++ b/test/help/help-test-utils.ts @@ -83,11 +83,16 @@ export const topicHelp = (topic: Interfaces.Topic) => ({ }, }) -export function monkeyPatchCommands(config: any, plugins: Array<{commands: Command.Class[], topics: Interfaces.Topic[]}>) { - config.plugins = plugins +export function monkeyPatchCommands(config: any, plugins: Array<{name: string, commands: Command.Class[], topics: Interfaces.Topic[]}>) { + const pluginsMap = new Map() + for (const plugin of plugins) { + pluginsMap.set(plugin.name, plugin) + } + + config.plugins = pluginsMap config._commands = new Map() config._topics = new Map() - for (const plugin of config.plugins) { + for (const plugin of config.plugins.values()) { config.loadCommands(plugin) config.loadTopics(plugin) } diff --git a/test/help/show-customized-help.test.ts b/test/help/show-customized-help.test.ts index 965963af5..e8d5586a6 100644 --- a/test/help/show-customized-help.test.ts +++ b/test/help/show-customized-help.test.ts @@ -4,7 +4,8 @@ import * as path from 'path' import {CommandHelp, Help} from '../../src/help' import {AppsIndexWithDesc, AppsDestroy, AppsCreate, AppsTopic, AppsAdminTopic, AppsAdminAdd} from './fixtures/fixtures' -import {Interfaces, Config, Command} from '../../src' +import {Interfaces, Config} from '../../src' +import {Command} from '../../src/command' import {monkeyPatchCommands} from './help-test-utils' const g: any = global @@ -89,6 +90,7 @@ describe('showHelp for root', () => { const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsIndexWithDesc, AppsCreate, AppsDestroy], topics: [], }]) @@ -120,6 +122,7 @@ COMMANDS const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsIndexWithDesc], topics: [], }]) @@ -150,6 +153,7 @@ describe('showHelp for a command', () => { const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsCreate], topics: [AppsTopic], }]) @@ -179,6 +183,7 @@ CUSTOM const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsIndexWithDesc, AppsCreate, AppsAdminAdd], topics: [AppsTopic, AppsAdminTopic], }]) diff --git a/test/help/show-help.test.ts b/test/help/show-help.test.ts index 8f6ffab7c..976089a32 100644 --- a/test/help/show-help.test.ts +++ b/test/help/show-help.test.ts @@ -100,6 +100,7 @@ COMMANDS const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsIndex], topics: [], }]) @@ -129,6 +130,7 @@ describe('showHelp for a topic', () => { const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsCreate, AppsDestroy], topics: [AppsTopic], }]) @@ -154,6 +156,7 @@ COMMANDS const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsCreate, AppsDestroy, AppsAdminAdd], topics: [AppsTopic, AppsAdminTopic], }]) @@ -182,6 +185,7 @@ COMMANDS const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsCreate, AppsDestroy, AppsAdminIndex, AppsAdminAdd], topics: [AppsTopic, AppsAdminTopic], }]) @@ -210,6 +214,7 @@ COMMANDS .do(async ctx => { const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsCreate, AppsDestroy, AppsAdminAdd, DbCreate], topics: [AppsTopic, AppsAdminTopic, DbTopic], }]) @@ -239,6 +244,7 @@ describe('showHelp for a command', () => { .do(async ctx => { const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsCreate], topics: [AppsTopic], }]) @@ -264,6 +270,7 @@ DESCRIPTION .do(async ctx => { const config = ctx.config monkeyPatchCommands(config, [{ + name: 'plugin-1', commands: [AppsIndex, AppsCreate, AppsAdminAdd], topics: [AppsTopic, AppsAdminTopic], }]) diff --git a/test/help/util.test.ts b/test/help/util.test.ts index d5c289b65..29cfc8fa0 100644 --- a/test/help/util.test.ts +++ b/test/help/util.test.ts @@ -27,6 +27,7 @@ describe('util', () => { test .it('loads help class defined in pjson.oclif.helpClass', async () => { config.pjson.oclif.helpClass = '../test/help/_test-help-class' + // @ts-expect-error readonly property config.root = resolve(__dirname, '..') expect(configuredHelpClass).to.not.be.undefined diff --git a/test/integration/util.ts b/test/integration/util.ts index 0e724e1d1..44e875e99 100644 --- a/test/integration/util.ts +++ b/test/integration/util.ts @@ -1,6 +1,6 @@ import {rm} from 'shelljs' -import {mkdirp} from 'fs-extra' +import {mkdir} from 'node:fs/promises' import * as cp from 'child_process' import * as chalk from 'chalk' import * as fs from 'fs' @@ -141,7 +141,7 @@ export async function setup(testFile: string, options: SetupOptions): Promise=2.2.7 <3" +abbrev@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -851,6 +1139,28 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +agent-base@6, agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@^4.2.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -898,6 +1208,11 @@ ansi-regex@^6.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.0.tgz#ecc7f5933cbe5ac7b33e209a5ff409ab1669c6b2" integrity sha512-tAaOSrWCHF+1Ear1Z4wnJCXA9GGox4K6Ic85a5qalES2aeEwQGr7UC93mwef49536PkCYjzkp0zIxfFvexJ6zQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -905,13 +1220,18 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.1, ansi-styles@^4.3.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -925,6 +1245,32 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +"aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +archy@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== + +are-we-there-yet@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" + integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +are-we-there-yet@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-4.0.1.tgz#05a6fc0e5f70771b673e82b0f915616e0ace8fd3" + integrity sha512-2zuA+jpOYBRgoBCfa+fB87Rk0oGJjDX6pxGzqH6f33NzUhG25Xur6R0u0Z9VVAq8Z5JvQpQI6j6rtonuivC8QA== + dependencies: + delegates "^1.0.0" + readable-stream "^4.1.0" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -982,6 +1328,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + benchmark@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" @@ -990,7 +1341,17 @@ benchmark@^2.1.4: lodash "^4.17.4" platform "^1.3.3" -binary-extensions@^2.0.0: +bin-links@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.2.tgz#13321472ea157e9530caded2b7281496d698665b" + integrity sha512-jxJ0PbXR8eQyPlExCvCs3JFnikvs1Yp4gUJt6nmgathdOwvur+q22KWC3h20gvWl4T/14DXKj2IlkJwwZkZPOw== + dependencies: + cmd-shim "^6.0.0" + npm-normalize-package-bin "^3.0.0" + read-cmd-shim "^4.0.0" + write-file-atomic "^5.0.0" + +binary-extensions@^2.0.0, binary-extensions@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== @@ -1033,11 +1394,44 @@ browserslist@^4.16.6: nanocolors "^0.1.5" node-releases "^1.1.76" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtin-modules@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== +builtins@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" + +cacache@^17.0.0, cacache@^17.0.4, cacache@^17.1.3: + version "17.1.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" + integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^7.7.1" + minipass "^7.0.3" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1114,6 +1508,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -1134,11 +1533,28 @@ chokidar@3.5.3: optionalDependencies: fsevents "~2.3.2" +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + ci-info@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== +ci-info@^3.6.1, ci-info@^3.7.1, ci-info@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +cidr-regex@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-3.1.1.tgz#ba1972c57c66f61875f18fd7dd487469770b571d" + integrity sha512-RBqYd32aDwbCMFJRL6wHOlDNYJsPNTt8vC82ErHF5vKt8QQzxm1FrkW8s/R5pVrXMf17sba09Uoy91PKiddAsw== + dependencies: + ip-regex "^4.1.0" + clean-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" @@ -1153,6 +1569,11 @@ clean-stack@*: dependencies: escape-string-regexp "5.0.0" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + clean-stack@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-3.0.1.tgz#155bf0b2221bf5f4fba89528d24c5953f17fe3a8" @@ -1160,6 +1581,14 @@ clean-stack@^3.0.1: dependencies: escape-string-regexp "4.0.0" +cli-columns@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-columns/-/cli-columns-4.0.0.tgz#9fe4d65975238d55218c41bd2ed296a7fa555646" + integrity sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ== + dependencies: + string-width "^4.2.3" + strip-ansi "^6.0.1" + cli-progress@^3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942" @@ -1167,6 +1596,15 @@ cli-progress@^3.12.0: dependencies: string-width "^4.2.3" +cli-table3@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1176,6 +1614,16 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +cmd-shim@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" + integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1200,6 +1648,19 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +columnify@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" + integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== + dependencies: + strip-ansi "^6.0.1" + wcwidth "^1.0.0" + commitlint@^12.1.4: version "12.1.4" resolved "https://registry.yarnpkg.com/commitlint/-/commitlint-12.1.4.tgz#afc185957afdd07d2ebb6c78b5956d407fc09ad0" @@ -1208,6 +1669,11 @@ commitlint@^12.1.4: "@commitlint/cli" "^12.1.4" "@commitlint/types" "^12.1.4" +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -1226,6 +1692,11 @@ confusing-browser-globals@1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== +console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + content-type@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -1302,7 +1773,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.1, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1311,12 +1782,17 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1353,6 +1829,18 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + diff@5.0.0, diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -1363,6 +1851,11 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -1384,6 +1877,11 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ejs@^3.1.8: version "3.1.8" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" @@ -1401,6 +1899,18 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encoding@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -1408,6 +1918,16 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1681,7 +2201,22 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -fancy-test@^2.0.16, fancy-test@^2.0.18: +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + +fancy-test@^2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.18.tgz#36f938a7207c90b2bf2f85b9c7d0cb6de8444635" integrity sha512-wA9xzWMJ4L51Jcr9k06koPwi58bbUkTZrqqNYd6z7DHky1jW+D5jc/q86zPmkVdnjOrCg91VOeEzyOjTLIlD8A== @@ -1695,6 +2230,20 @@ fancy-test@^2.0.16, fancy-test@^2.0.18: nock "^13.3.0" stdout-stderr "^0.1.9" +fancy-test@^2.0.34: + version "2.0.35" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.35.tgz#18c0ccd767a7332bea186369a7e7a79a3b4582bb" + integrity sha512-XE0L7yAFOKY2jDnkBDDQ3JBD7xbBFwAFl1Z/5WNKBeVNlaEP08wuRTPE3xj2k+fnUp2CMUfD+6aiIS+4pcrjwg== + dependencies: + "@types/chai" "*" + "@types/lodash" "*" + "@types/node" "*" + "@types/sinon" "*" + lodash "^4.17.13" + mock-stdin "^1.0.0" + nock "^13.3.3" + stdout-stderr "^0.1.9" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1721,6 +2270,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + fastq@^1.6.0: version "1.11.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" @@ -1791,6 +2345,14 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + fs-extra@^9.0, fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -1801,6 +2363,20 @@ fs-extra@^9.0, fs-extra@^9.0.0, fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-minipass@^3.0.0, fs-minipass@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1821,6 +2397,34 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + +gauge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.1.tgz#1efc801b8ff076b86ef3e9a7a280a975df572112" + integrity sha512-CmykPMJGuNan/3S4kZOpvvPYSNqSHANiWnh9XcMU2pSjtBfF0XzZ2p1bFAxTbnFxyBuPxQYHhzwaoOmUdqzvxQ== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^4.0.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -1871,6 +2475,17 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.2.2, glob@^10.2.7: + version "10.3.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" + integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^7.0.0, glob@^7.1.3: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -1883,6 +2498,18 @@ glob@^7.0.0, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -1919,6 +2546,11 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graceful-fs@^4.2.11, graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -1934,6 +2566,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -1958,6 +2595,18 @@ hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" +hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" + integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== + dependencies: + lru-cache "^7.5.1" + +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-call@^5.2.2: version "5.3.0" resolved "https://registry.yarnpkg.com/http-call/-/http-call-5.3.0.tgz#4ded815b13f423de176eb0942d69c43b25b148db" @@ -1970,6 +2619,30 @@ http-call@^5.2.2: parse-json "^4.0.0" tunnel-agent "^0.6.0" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + husky@6: version "6.0.0" resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" @@ -1980,9 +2653,28 @@ hyperlinker@^1.0.0: resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-walk@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.3.tgz#0fcdb6decaccda35e308a7b0948645dd9523b7bb" + integrity sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA== + dependencies: + minimatch "^9.0.0" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.1, ignore@^5.2.0: @@ -2031,11 +2723,39 @@ ini@^1.3.4: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@^4.1.0, ini@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + +init-package-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-5.0.0.tgz#030cf0ea9c84cfc1b0dc2e898b45d171393e4b40" + integrity sha512-kBhlSheBfYmq3e0L1ii+VKe3zBTLL5lDCDWR+f9dLmEGSB3MqLlMlsolubSsyI88Bg6EA+BIMlomAnQ1SwgQBw== + dependencies: + npm-package-arg "^10.0.0" + promzard "^1.0.0" + read "^2.0.0" + read-package-json "^6.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^5.0.0" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +ip-regex@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + irregular-plurals@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.3.0.tgz#67d0715d4361a60d9fd9ee80af3881c631a31ee2" @@ -2060,6 +2780,13 @@ is-builtin-module@^3.1.0: dependencies: builtin-modules "^3.0.0" +is-cidr@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-4.0.2.tgz#94c7585e4c6c77ceabf920f8cde51b8c0fda8814" + integrity sha512-z4a1ENUajDbEl/Q6/pVBpTR1nBjjEE1X7qb7bmWYanNnPoKAvUCPFKeXV6Fe4mgTkWKBqiHIcwsI3SndiO5FeA== + dependencies: + cidr-regex "^3.1.1" + is-core-module@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" @@ -2067,6 +2794,13 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.8.1: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -2089,6 +2823,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -2153,6 +2892,15 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +jackspeak@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.0.tgz#aa228a94de830f31d4e4f0184427ce91c4ff1493" + integrity sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -2198,6 +2946,11 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" + integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -2213,6 +2966,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +json-stringify-nice@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" + integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== + json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -2232,10 +2990,20 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@^1.2.0: +jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +just-diff-apply@^5.2.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" + integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== + +just-diff@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-6.0.2.tgz#03b65908543ac0521caf6d8eb85035f7d27ea285" + integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA== just-extend@^4.0.2: version "4.2.1" @@ -2255,6 +3023,119 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +libnpmaccess@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" + integrity sha512-vHBVMw1JFMTgEk15zRsJuSAg7QtGGHpUSEfnbcRL1/gTBag9iEfJbyjpDmdJmwMhvpoLoNBtdAUCdGnaP32hhw== + dependencies: + npm-package-arg "^10.1.0" + npm-registry-fetch "^14.0.3" + +libnpmdiff@^5.0.19: + version "5.0.19" + resolved "https://registry.yarnpkg.com/libnpmdiff/-/libnpmdiff-5.0.19.tgz#c56a8b1fcd7690f12e527c0ab21dbdbd259c27fe" + integrity sha512-caqIA7SzPeyqOn55GodejyEJRIXaFnzuqxrO9uyXtH4soom4wjDAkU97L1WrBSuVtDk3IZQD72daVeT2GqHSjA== + dependencies: + "@npmcli/arborist" "^6.3.0" + "@npmcli/disparity-colors" "^3.0.0" + "@npmcli/installed-package-contents" "^2.0.2" + binary-extensions "^2.2.0" + diff "^5.1.0" + minimatch "^9.0.0" + npm-package-arg "^10.1.0" + pacote "^15.0.8" + tar "^6.1.13" + +libnpmexec@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/libnpmexec/-/libnpmexec-6.0.3.tgz#f7ea9c028443d890297e1bbe2d5605f68b118470" + integrity sha512-E87xEzxChUe0qZgoqht5D5t13B876rPoTD877v9ZUSMztBFpuChQn5UNO3z5NaeBpEwWq/BAnQfMYRWR6sVAZA== + dependencies: + "@npmcli/arborist" "^6.3.0" + "@npmcli/run-script" "^6.0.0" + ci-info "^3.7.1" + npm-package-arg "^10.1.0" + npmlog "^7.0.1" + pacote "^15.0.8" + proc-log "^3.0.0" + read "^2.0.0" + read-package-json-fast "^3.0.2" + semver "^7.3.7" + walk-up-path "^3.0.1" + +libnpmfund@^4.0.19: + version "4.0.19" + resolved "https://registry.yarnpkg.com/libnpmfund/-/libnpmfund-4.0.19.tgz#ec615b9a5fb996185fca132420b6be265dc485f1" + integrity sha512-g2XV/oqBLo0Mau/nmqvIoNHRmAQqzSvSjSR9npO0+buEqGmyRHDeQJKDI3RxpLcQgd0IuNNAoTjXXpoKcX90EQ== + dependencies: + "@npmcli/arborist" "^6.3.0" + +libnpmhook@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-9.0.3.tgz#5dbd6a146feb7e11993d36a26f750ae2347bb1d9" + integrity sha512-wMZe58sI7KLhg0+nUWZW5KdMfjNNcOIIbkoP19BDHYoUF9El7eeUWkGNxUGzpHkPKiGoQ1z/v6CYin4deebeuw== + dependencies: + aproba "^2.0.0" + npm-registry-fetch "^14.0.3" + +libnpmorg@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-5.0.4.tgz#94eec2b84fbef736457eb27894c972ae6f5cac82" + integrity sha512-YqYXLMAN0Y1eJH4w3hUFN9648xfSdvJANMsdeZTOWJOW4Pqp8qapJFzQdqCfUkg+tEuQmnaFQQKXvkMZC51+Mw== + dependencies: + aproba "^2.0.0" + npm-registry-fetch "^14.0.3" + +libnpmpack@^5.0.19: + version "5.0.19" + resolved "https://registry.yarnpkg.com/libnpmpack/-/libnpmpack-5.0.19.tgz#e9790ebbcb078469d59dbb7e5ee7defe3039cc22" + integrity sha512-xxkROnxTZF3imCJ9ve+6ELtRYvOBMwvrKlMGJx6JhmvD5lqIPGOJpY8oY+w8XLmLX1N06scYuLonkFpF2ayrjQ== + dependencies: + "@npmcli/arborist" "^6.3.0" + "@npmcli/run-script" "^6.0.0" + npm-package-arg "^10.1.0" + pacote "^15.0.8" + +libnpmpublish@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-7.5.0.tgz#a118c8fdc680947c960648ed8b4c94d15e42e0ab" + integrity sha512-zctH6QcTJ093lpxmkufr2zr3AJ9V90hcRilDFNin6n91ODj+S28RdyMFFJpa9NwyztmyV2hlWLyZv0GaOQBDyA== + dependencies: + ci-info "^3.6.1" + normalize-package-data "^5.0.0" + npm-package-arg "^10.1.0" + npm-registry-fetch "^14.0.3" + proc-log "^3.0.0" + semver "^7.3.7" + sigstore "^1.4.0" + ssri "^10.0.1" + +libnpmsearch@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-6.0.2.tgz#b6a531a312855dd3bf84dd273b1033dd09b4cbec" + integrity sha512-p+5BF19AvnVg8mcIQhy6yWhI6jHQRVMYaIaKeITEfYAffWsqbottA/WZdMtHL76hViC6SFM1WdclM1w5eAIa1g== + dependencies: + npm-registry-fetch "^14.0.3" + +libnpmteam@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-5.0.3.tgz#196657e9d87c0cc914c44fee588ad2b838074a3c" + integrity sha512-7XOGhi45s+ml6TyrhJUTyrErcoDMKGKfEtiTEco4ofU7BGGAUOalVztKMVLLJgJOOXdIAIlzCHqkTXEuSiyCiA== + dependencies: + aproba "^2.0.0" + npm-registry-fetch "^14.0.3" + +libnpmversion@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/libnpmversion/-/libnpmversion-4.0.2.tgz#cad9cd1b287fcf9576a64edfe71491b49a65d06f" + integrity sha512-n1X70mFHv8Piy4yos+MFWUARSkTbyV5cdsHScaIkuwYvRAF/s2VtYScDzWB4Oe8uNEuGNdjiRR1E/Dh1tMvv6g== + dependencies: + "@npmcli/git" "^4.0.1" + "@npmcli/run-script" "^6.0.0" + json-parse-even-better-errors "^3.0.0" + proc-log "^3.0.0" + semver "^7.3.7" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -2332,11 +3213,42 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.0.3, make-fetch-happen@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^5.0.0" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" + map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -2419,6 +3331,20 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.0, minimatch@^9.0.1, minimatch@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -2433,6 +3359,83 @@ minimist@^1.2.3: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-fetch@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" + integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" + integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== + +minizlib@^2.1.1, minizlib@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mocha@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" @@ -2475,7 +3478,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.0.0, ms@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -2485,6 +3488,11 @@ multimap@^1.1.0: resolved "https://registry.yarnpkg.com/multimap/-/multimap-1.1.0.tgz#5263febc085a1791c33b59bb3afc6a76a2a10ca8" integrity sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw== +mute-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + nanocolors@^0.1.0, nanocolors@^0.1.5: version "0.1.12" resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6" @@ -2505,6 +3513,11 @@ natural-orderby@^2.0.3: resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== +negotiator@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -2531,11 +3544,52 @@ nock@*, nock@^13.3.0: lodash "^4.17.21" propagate "^2.0.0" +nock@^13.3.3: + version "13.3.3" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.3.tgz#179759c07d3f88ad3e794ace885629c1adfd3fe7" + integrity sha512-z+KUlILy9SK/RjpeXDiDUEAq4T94ADPHE3qaRkf66mpEhzc/ytOMm3Bwdrbq6k1tMWkbdujiKim3G2tfQARuJw== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash "^4.17.21" + propagate "^2.0.0" + +node-gyp@^9.0.0, node-gyp@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" + integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^11.0.3" + nopt "^6.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + node-releases@^1.1.76: version "1.1.76" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e" integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA== +nopt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" + integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== + dependencies: + abbrev "^1.0.0" + +nopt@^7.0.0, nopt@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== + dependencies: + abbrev "^2.0.0" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -2556,11 +3610,93 @@ normalize-package-data@^3.0.0: semver "^7.3.4" validate-npm-package-license "^3.0.1" +normalize-package-data@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" + integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== + dependencies: + hosted-git-info "^6.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-audit-report@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-5.0.0.tgz#83ac14aeff249484bde81eff53c3771d5048cf95" + integrity sha512-EkXrzat7zERmUhHaoren1YhTxFwsOu5jypE84k6632SXTHcQE1z8V51GC6GVZt8LxkC+tbBcKMUBZAgk8SUSbw== + +npm-bundled@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" + integrity sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ== + dependencies: + npm-normalize-package-bin "^3.0.0" + +npm-install-checks@^6.0.0, npm-install-checks@^6.1.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.2.0.tgz#fae55b9967b03ac309695ec96629492d5cedf371" + integrity sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g== + dependencies: + semver "^7.1.1" + +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== + +npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" + integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== + dependencies: + hosted-git-info "^6.0.0" + proc-log "^3.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + +npm-packlist@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" + integrity sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q== + dependencies: + ignore-walk "^6.0.0" + +npm-pick-manifest@^8.0.0, npm-pick-manifest@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz#2159778d9c7360420c925c1a2287b5a884c713aa" + integrity sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg== + dependencies: + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^10.0.0" + semver "^7.3.5" + +npm-profile@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-7.0.1.tgz#a37dae08b22e662ece2c6e08946f9fcd9fdef663" + integrity sha512-VReArOY/fCx5dWL66cbJ2OMogTQAVVQA//8jjmjkarboki3V7UJ0XbGFW+khRwiAJFQjuH0Bqr/yF7Y5RZdkMQ== + dependencies: + npm-registry-fetch "^14.0.0" + proc-log "^3.0.0" + +npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3, npm-registry-fetch@^14.0.5: + version "14.0.5" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz#fe7169957ba4986a4853a650278ee02e568d115d" + integrity sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA== + dependencies: + make-fetch-happen "^11.0.0" + minipass "^5.0.0" + minipass-fetch "^3.0.0" + minipass-json-stream "^1.0.1" + minizlib "^2.1.2" + npm-package-arg "^10.0.0" + proc-log "^3.0.0" + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -2568,6 +3704,105 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-user-validate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-2.0.0.tgz#7b69bbbff6f7992a1d9a8968d52fd6b6db5431b6" + integrity sha512-sSWeqAYJ2dUPStJB+AEj0DyLRltr/f6YNcvCA7phkB8/RMLMnVsQ41GMwHo/ERZLYNDsyB2wPm7pZo1mqPOl7Q== + +npm@^9.7.2: + version "9.8.1" + resolved "https://registry.yarnpkg.com/npm/-/npm-9.8.1.tgz#b8f070cc770128b38017160491504184863329f0" + integrity sha512-AfDvThQzsIXhYgk9zhbk5R+lh811lKkLAeQMMhSypf1BM7zUafeIIBzMzespeuVEJ0+LvY36oRQYf7IKLzU3rw== + dependencies: + "@isaacs/string-locale-compare" "^1.1.0" + "@npmcli/arborist" "^6.3.0" + "@npmcli/config" "^6.2.1" + "@npmcli/fs" "^3.1.0" + "@npmcli/map-workspaces" "^3.0.4" + "@npmcli/package-json" "^4.0.1" + "@npmcli/promise-spawn" "^6.0.2" + "@npmcli/run-script" "^6.0.2" + abbrev "^2.0.0" + archy "~1.0.0" + cacache "^17.1.3" + chalk "^5.3.0" + ci-info "^3.8.0" + cli-columns "^4.0.0" + cli-table3 "^0.6.3" + columnify "^1.6.0" + fastest-levenshtein "^1.0.16" + fs-minipass "^3.0.2" + glob "^10.2.7" + graceful-fs "^4.2.11" + hosted-git-info "^6.1.1" + ini "^4.1.1" + init-package-json "^5.0.0" + is-cidr "^4.0.2" + json-parse-even-better-errors "^3.0.0" + libnpmaccess "^7.0.2" + libnpmdiff "^5.0.19" + libnpmexec "^6.0.3" + libnpmfund "^4.0.19" + libnpmhook "^9.0.3" + libnpmorg "^5.0.4" + libnpmpack "^5.0.19" + libnpmpublish "^7.5.0" + libnpmsearch "^6.0.2" + libnpmteam "^5.0.3" + libnpmversion "^4.0.2" + make-fetch-happen "^11.1.1" + minimatch "^9.0.3" + minipass "^5.0.0" + minipass-pipeline "^1.2.4" + ms "^2.1.2" + node-gyp "^9.4.0" + nopt "^7.2.0" + npm-audit-report "^5.0.0" + npm-install-checks "^6.1.1" + npm-package-arg "^10.1.0" + npm-pick-manifest "^8.0.1" + npm-profile "^7.0.1" + npm-registry-fetch "^14.0.5" + npm-user-validate "^2.0.0" + npmlog "^7.0.1" + p-map "^4.0.0" + pacote "^15.2.0" + parse-conflict-json "^3.0.1" + proc-log "^3.0.0" + qrcode-terminal "^0.12.0" + read "^2.1.0" + semver "^7.5.4" + sigstore "^1.7.0" + ssri "^10.0.4" + supports-color "^9.4.0" + tar "^6.1.15" + text-table "~0.2.0" + tiny-relative-date "^1.3.0" + treeverse "^3.0.0" + validate-npm-package-name "^5.0.0" + which "^3.0.1" + write-file-atomic "^5.0.1" + +npmlog@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + +npmlog@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-7.0.1.tgz#7372151a01ccb095c47d8bf1d0771a4ff1f53ac8" + integrity sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg== + dependencies: + are-we-there-yet "^4.0.0" + console-control-strings "^1.1.0" + gauge "^5.0.0" + set-blocking "^2.0.0" + object-treeify@^1.1.33: version "1.1.33" resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" @@ -2620,11 +3855,42 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pacote@^15.0.0, pacote@^15.0.8, pacote@^15.2.0: + version "15.2.0" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" + integrity sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA== + dependencies: + "@npmcli/git" "^4.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/promise-spawn" "^6.0.1" + "@npmcli/run-script" "^6.0.0" + cacache "^17.0.0" + fs-minipass "^3.0.0" + minipass "^5.0.0" + npm-package-arg "^10.0.0" + npm-packlist "^7.0.0" + npm-pick-manifest "^8.0.0" + npm-registry-fetch "^14.0.0" + proc-log "^3.0.0" + promise-retry "^2.0.1" + read-package-json "^6.0.0" + read-package-json-fast "^3.0.0" + sigstore "^1.3.0" + ssri "^10.0.0" + tar "^6.1.11" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2632,6 +3898,15 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-conflict-json@^3.0.0, parse-conflict-json@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz#67dc55312781e62aa2ddb91452c7606d1969960c" + integrity sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw== + dependencies: + json-parse-even-better-errors "^3.0.0" + just-diff "^6.0.0" + just-diff-apply "^5.2.0" + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -2683,6 +3958,14 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -2727,16 +4010,64 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +postcss-selector-parser@^6.0.10: + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-all-reject-late@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" + integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== + +promise-call-limit@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" + integrity sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +promzard@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-1.0.0.tgz#3246f8e6c9895a77c0549cefb65828ac0f6c006b" + integrity sha512-KQVDEubSUHGSt5xLakaToDFrSoZhStB8dXLzk2xvwR67gJktrHFvpR63oZgHyK19WKbHFLXJqCPXdVR3aBP8Ig== + dependencies: + read "^2.0.0" + propagate@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" @@ -2761,6 +4092,11 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qrcode-terminal@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" + integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2783,6 +4119,29 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +read-cmd-shim@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" + integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q== + +read-package-json-fast@^3.0.0, read-package-json-fast@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== + dependencies: + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" + +read-package-json@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.4.tgz#90318824ec456c287437ea79595f4c2854708836" + integrity sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw== + dependencies: + glob "^10.2.2" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^5.0.0" + npm-normalize-package-bin "^3.0.0" + read-pkg-up@^7.0.0, read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -2802,6 +4161,13 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +read@^2.0.0, read@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/read/-/read-2.1.0.tgz#69409372c54fe3381092bc363a00650b6ac37218" + integrity sha512-bvxi1QLJHcaywCAEsAk4DG3nVoqiY2Csps3qzWalhj5hFqRn1d/OixkFXtLO1PrgHUcAP0FNaSY/5GYNfENFFQ== + dependencies: + mute-stream "~1.0.0" + readable-stream@3, readable-stream@^3.0.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -2811,6 +4177,26 @@ readable-stream@3, readable-stream@^3.0.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^4.1.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.2.tgz#e6aced27ad3b9d726d8308515b9a1b98dc1b9d13" + integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -2885,6 +4271,11 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.20 is-core-module "^2.2.0" path-parse "^1.0.6" +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -2921,6 +4312,11 @@ safe-regex@^2.1.1: dependencies: regexp-tree "~0.1.1" +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -2938,7 +4334,14 @@ semver@^6.1.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.0, semver@^7.5.3: +semver@^7.0.0, semver@^7.1.1, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +semver@^7.2.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== @@ -2952,6 +4355,11 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -2993,6 +4401,27 @@ shx@^0.3.4: minimist "^1.2.3" shelljs "^0.8.5" +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sigstore@^1.3.0, sigstore@^1.4.0, sigstore@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.9.0.tgz#1e7ad8933aa99b75c6898ddd0eeebc3eb0d59875" + integrity sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A== + dependencies: + "@sigstore/bundle" "^1.1.0" + "@sigstore/protobuf-specs" "^0.2.0" + "@sigstore/sign" "^1.0.0" + "@sigstore/tuf" "^1.0.3" + make-fetch-happen "^11.0.1" + sinon@^11.1.2: version "11.1.2" resolved "https://registry.yarnpkg.com/sinon/-/sinon-11.1.2.tgz#9e78850c747241d5c59d1614d8f9cbe8840e8674" @@ -3019,6 +4448,28 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.6.2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -3062,6 +4513,13 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +ssri@^10.0.0, ssri@^10.0.1, ssri@^10.0.4: + version "10.0.5" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" + integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== + dependencies: + minipass "^7.0.3" + stdout-stderr@^0.1.9: version "0.1.13" resolved "https://registry.yarnpkg.com/stdout-stderr/-/stdout-stderr-0.1.13.tgz#54e3450f3d4c54086a49c0c7f8786a44d1844b6f" @@ -3070,7 +4528,7 @@ stdout-stderr@^0.1.9: debug "^4.1.1" strip-ansi "^6.0.0" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3079,13 +4537,29 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string_decoder@^1.1.1: +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@*: version "7.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.0.tgz#1dc49b980c3a4100366617adac59327eefdefcb0" @@ -3093,12 +4567,12 @@ strip-ansi@*: dependencies: ansi-regex "^6.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^6.0.1" strip-bom@^3.0.0: version "3.0.0" @@ -3138,6 +4612,11 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" +supports-color@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -3158,15 +4637,27 @@ table@^6.0.9: string-width "^4.2.0" strip-ansi "^6.0.0" +tar@^6.1.11, tar@^6.1.13, tar@^6.1.15, tar@^6.1.2: + version "6.1.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" + integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-table@^0.2.0: +text-table@^0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== through2@^4.0.0: version "4.0.2" @@ -3180,6 +4671,11 @@ through2@^4.0.0: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tiny-relative-date@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07" + integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -3192,6 +4688,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +treeverse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-3.0.0.tgz#dd82de9eb602115c6ebd77a574aae67003cb48c8" + integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ== + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" @@ -3238,11 +4739,16 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.4.1, tslib@^2.5.0: +tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -3250,6 +4756,15 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tuf-js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" + integrity sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg== + dependencies: + "@tufjs/models" "1.0.4" + debug "^4.3.4" + make-fetch-happen "^11.1.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -3304,6 +4819,20 @@ typescript@^4.9.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -3316,7 +4845,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -3331,7 +4860,7 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -3339,6 +4868,25 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" + integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== + dependencies: + builtins "^5.0.0" + +walk-up-path@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-3.0.1.tgz#c8d78d5375b4966c717eb17ada73dbd41490e886" + integrity sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA== + +wcwidth@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -3346,13 +4894,27 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -which@^2.0.1: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" +which@^3.0.0, which@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" + integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -3375,7 +4937,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -3384,11 +4946,28 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^5.0.0, write-file-atomic@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" From e5a9d53b02dfbd03683c2041259e04b4b7bae152 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 17:30:12 +0000 Subject: [PATCH 06/55] chore(release): 3.0.0-beta.3 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01309b2e2..2b9a19ef1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From b99ff3ad0ec6889e815c5386a595e64739f83044 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 17:30:47 +0000 Subject: [PATCH 07/55] chore(release): 3.0.0-beta.4 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b9a19ef1..460bdd73f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.3", + "version": "3.0.0-beta.4", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 6254f9c0b34fdb0ec412ffd566eea9e4b685fa32 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 12:18:19 -0600 Subject: [PATCH 08/55] fix: add main to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 460bdd73f..19c8d6ed7 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "oclif" ], "license": "MIT", + "main": "./lib/index.js", "exports": { ".": "./lib/index.js", "./execute": "./lib/execute.js", From e1762866fe1146b923868e409c688053c51d653d Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 18:18:45 +0000 Subject: [PATCH 09/55] chore(release): 3.0.0-beta.5 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 19c8d6ed7..98fef08be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.4", + "version": "3.0.0-beta.5", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From dcb264b565ac76196bf2e83779de402ce2760a5b Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 14:04:41 -0600 Subject: [PATCH 10/55] fix: export CustomOptions --- src/interfaces/index.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 74c76d62c..253cc20c2 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,14 +1,14 @@ -export {AlphabetLowercase, AlphabetUppercase} from './alphabet' -export {Config, ArchTypes, PlatformTypes, LoadOptions, VersionDetails, PluginVersionDetail} from './config' -export {OclifError, PrettyPrintableError, CommandError} from './errors' -export {HelpOptions} from './help' -export {Hook, Hooks} from './hooks' -export {Manifest} from './manifest' -export {S3Manifest} from './s3-manifest' -export {BooleanFlag, Flag, OptionFlag, Deprecation} from './parser' -export {PJSON} from './pjson' -export {Plugin, PluginOptions, Options} from './plugin' -export {Topic} from './topic' -export {TSConfig} from './ts-config' -export {InferredFlags} from './flags' -export {InferredArgs} from './args' +export type {AlphabetLowercase, AlphabetUppercase} from './alphabet' +export type {Config, ArchTypes, PlatformTypes, LoadOptions, VersionDetails, PluginVersionDetail} from './config' +export type {OclifError, PrettyPrintableError, CommandError} from './errors' +export type {HelpOptions} from './help' +export type {Hook, Hooks} from './hooks' +export type {Manifest} from './manifest' +export type {S3Manifest} from './s3-manifest' +export type {Arg, BooleanFlag, Flag, OptionFlag, Deprecation, CustomOptions} from './parser' +export type {PJSON} from './pjson' +export type {Plugin, PluginOptions, Options} from './plugin' +export type {Topic} from './topic' +export type {TSConfig} from './ts-config' +export type {InferredFlags} from './flags' +export type {InferredArgs} from './args' From a8b9f1e1fb4ef50ce4862484a688949cef72750c Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 20:05:27 +0000 Subject: [PATCH 11/55] chore(release): 3.0.0-beta.6 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98fef08be..94d45fcee 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From e798bcd890058b29d10dcbe9b3dbf8fa5ea84e20 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 14:27:14 -0600 Subject: [PATCH 12/55] fix: interface exports --- src/interfaces/index.ts | 10 +++++++++- src/ux/index.ts | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 253cc20c2..76e7ca5b4 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -5,7 +5,15 @@ export type {HelpOptions} from './help' export type {Hook, Hooks} from './hooks' export type {Manifest} from './manifest' export type {S3Manifest} from './s3-manifest' -export type {Arg, BooleanFlag, Flag, OptionFlag, Deprecation, CustomOptions} from './parser' +export type { + Arg, + BooleanFlag, + CustomOptions, + Deprecation, + Flag, + FlagDefinition, + OptionFlag, +} from './parser' export type {PJSON} from './pjson' export type {Plugin, PluginOptions, Options} from './plugin' export type {Topic} from './topic' diff --git a/src/ux/index.ts b/src/ux/index.ts index fd7b2a67c..c1883ac01 100644 --- a/src/ux/index.ts +++ b/src/ux/index.ts @@ -7,6 +7,7 @@ import {ExitError} from './exit' import {IPromptOptions} from './prompt' import * as styled from './styled' import {Table} from './styled' +export * from './styled/table' import * as uxPrompt from './prompt' import uxWait from './wait' import {stdout} from './stream' From da49a7a8858044c2cd5a5cdb29492b7b4f23c381 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 20:27:39 +0000 Subject: [PATCH 13/55] chore(release): 3.0.0-beta.7 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94d45fcee..2d746fe1c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.6", + "version": "3.0.0-beta.7", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 979cf86c105a473274de97ed34e8adca829ad3f3 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 15:12:45 -0600 Subject: [PATCH 14/55] feat: remove ux and interfaces export --- guides/V3_MIGRATION.md | 36 +++++-------------------------- package.json | 4 +--- src/index.ts | 2 +- src/ux/index.ts | 49 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 37 deletions(-) diff --git a/guides/V3_MIGRATION.md b/guides/V3_MIGRATION.md index eac4ffaf0..c130a80d4 100644 --- a/guides/V3_MIGRATION.md +++ b/guides/V3_MIGRATION.md @@ -8,7 +8,6 @@ Migrating to @oclif/core@V3 - [`Config.plugins`](#configplugins) - [Readonly properties on `Config`](#readonly-properties-on-config) - [Private methods on `Plugin`](#private-methods-on-plugin) - - [`ux` exports](#ux-exports) - [`global['cli-ux']` -\> `global.ux`](#globalcli-ux---globalux) - [`handle`](#handle) - [`noCacheDefault` flag property replaces `isWritingManifest`](#nocachedefault-flag-property-replaces-iswritingmanifest) @@ -74,29 +73,6 @@ Various properties on `Config` are now `readonly` ### Private methods on `Plugin` The `_manifest` and `warn` methods on `Plugin` are now `private` -### `ux` exports - -The following exports are no longer available on `ux`: - -- `ActionBase` -- `config` -- `Config` -- `ExitError` -- `IPromptOptions` -- `Table` - -If you still need these you can import them from `@oclif/core/ux`: - -```typescript -import { - ActionBase, - config, - Config, - ExitError, - IPromptOptions, - Table, -} from '@oclif/core/ux' -``` ### `global['cli-ux']` -> `global.ux` @@ -122,7 +98,7 @@ export const mySensitiveFlag = Flags.string({ }); ``` -Version 3 removes the `isWritingManifest` parameter in favor of a flag and arg property, `noCacheDefault`. Setting it to true will automatically keep it from being cached in the manfiest. +Version 3 removes the `isWritingManifest` parameter in favor of a flag and arg property, `noCacheDefault`. Setting it to true will automatically keep it from being cached in the manifest. ```typescript export const mySensitiveFlag = Flags.string({ @@ -137,29 +113,27 @@ export const mySensitiveFlag = Flags.string({ ## Features ๐ŸŽ‰ ### Exports -The `exports` property in the package.json are now used for exporting individual pieces of functionality. +The `exports` property in the package.json are now used for exporting a few individual pieces of functionality. The main export should continue to work as it did in v2: ```typescript -import {Interfaces, ux} from '@oclif/core' +import {handle, flush} from '@oclif/core' ``` But you can now import individual modules if desired: ```typescript -import Interfaces from '@oclif/core/interfaces' -import ux from '@oclif/core/ux' +import handle from '@oclif/core/handle' +import flush from '@oclif/core/flush' ``` These are the exports that are available: `@oclif/core/execute` `@oclif/core/flush` `@oclif/core/handle` -`@oclif/core/interfaces` `@oclif/core/run` `@oclif/core/settings` -`@oclif/core/ux` **You will need to set moduleResolution to Node16 in your tsconfig.json to use this feature.** diff --git a/package.json b/package.json index 2d746fe1c..08a9ed628 100644 --- a/package.json +++ b/package.json @@ -96,10 +96,8 @@ "./execute": "./lib/execute.js", "./flush": "./lib/ux/flush.js", "./handle": "./lib/errors/handle.js", - "./interfaces": "./lib/interfaces/index.js", "./run": "./lib/main.js", - "./settings": "./lib/settings.js", - "./ux": "./lib/ux/index.js" + "./settings": "./lib/settings.js" }, "repository": "oclif/core", "oclif": { diff --git a/src/index.ts b/src/index.ts index 31b78759d..40a4ae33c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,7 @@ import * as Parser from './parser' import {Hook} from './interfaces/hooks' import settings, {Settings} from './settings' import {HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable} from './help/formatter' -import ux from './ux' +import * as ux from './ux' import {stderr, stdout} from './ux/stream' import Performance from './performance' import flush from './ux/flush' diff --git a/src/ux/index.ts b/src/ux/index.ts index c1883ac01..0f9f4d950 100644 --- a/src/ux/index.ts +++ b/src/ux/index.ts @@ -11,7 +11,7 @@ export * from './styled/table' import * as uxPrompt from './prompt' import uxWait from './wait' import {stdout} from './stream' -import flush from './flush' +import * as _flush from './flush' const hyperlinker = require('hyperlinker') @@ -110,7 +110,7 @@ export default class ux { } public static async flush(ms = 10_000): Promise { - await flush(ms) + await _flush.default(ms) } public static error(err: Error | string, options: {code?: string; exit?: number} = {}): never { @@ -126,7 +126,52 @@ export default class ux { } } +const action = ux.action +const annotation = ux.annotation +const anykey = ux.anykey +const confirm = ux.confirm +const debug = ux.debug +const done = ux.done +const error = Errors.error +const exit = Errors.exit +const flush = ux.flush +const info = ux.info +const log = ux.log +const progress = ux.progress +const prompt = ux.prompt +const styledHeader = ux.styledHeader +const styledJSON = ux.styledJSON +const styledObject = ux.styledObject +const table = ux.table +const trace = ux.trace +const tree = ux.tree +const url = ux.url +const wait = ux.wait +const warn = Errors.warn + export { + action, + annotation, + anykey, + confirm, + debug, + done, + error, + exit, + flush, + info, + log, + progress, + prompt, + styledHeader, + styledJSON, + styledObject, + table, + trace, + tree, + url, + wait, + warn, ActionBase, config, Config, From 1f015df13c969907179fe74046a1998dabe76824 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 21:13:10 +0000 Subject: [PATCH 15/55] chore(release): 3.0.0-beta.8 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08a9ed628..936efa8ea 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.7", + "version": "3.0.0-beta.8", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 84c971844822b6e175053c8395098e4e2884feec Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 31 Aug 2023 15:15:38 -0600 Subject: [PATCH 16/55] fix: revert ux changes --- src/ux/index.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/ux/index.ts b/src/ux/index.ts index 0f9f4d950..b1df2bbc1 100644 --- a/src/ux/index.ts +++ b/src/ux/index.ts @@ -39,11 +39,11 @@ export default class ux { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types public static styledObject(obj: any, keys?: string[]): void { - this.log(styled.styledObject(obj, keys)) + this.info(styled.styledObject(obj, keys)) } public static styledHeader(header: string): void { - this.log(chalk.dim('=== ') + chalk.bold(header) + '\n') + this.info(chalk.dim('=== ') + chalk.bold(header) + '\n') } public static get styledJSON(): typeof styled.styledJSON { @@ -71,13 +71,13 @@ export default class ux { } public static trace(format: string, ...args: string[]): void { - if (ux.config.outputLevel === 'trace') { + if (this.config.outputLevel === 'trace') { stdout.write(util.format(format, ...args) + '\n') } } public static debug(format: string, ...args: string[]): void { - if (['trace', 'debug'].includes(ux.config.outputLevel)) { + if (['trace', 'debug'].includes(this.config.outputLevel)) { stdout.write(util.format(format, ...args) + '\n') } } @@ -112,18 +112,6 @@ export default class ux { public static async flush(ms = 10_000): Promise { await _flush.default(ms) } - - public static error(err: Error | string, options: {code?: string; exit?: number} = {}): never { - throw Errors.error(err, options) - } - - public static exit(code = 0): never { - throw Errors.exit(code) - } - - public static warn(err: Error | string): void { - Errors.warn(err) - } } const action = ux.action From 648a025a803ff8d4484b78f7c14031e2e259d8a0 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 31 Aug 2023 21:18:21 +0000 Subject: [PATCH 17/55] chore(release): 3.0.0-beta.9 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 936efa8ea..e782e2e61 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.8", + "version": "3.0.0-beta.9", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 27fd3f1280bcfde281d866d5cd6c3fe7b9ee53dd Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 1 Sep 2023 05:11:12 -0700 Subject: [PATCH 18/55] feat: revert exports changes (#776) * feat: revert exports changes * fix: run e2e tests even if linting error * fix: revert cli-ux dir renaming --- README.md | 2 +- flush.d.ts | 3 +++ flush.js | 1 + guides/V3_MIGRATION.md | 26 -------------------------- handle.js | 1 + package.json | 13 ++++--------- src/{ux => cli-ux}/README.md | 0 src/{ux => cli-ux}/action/base.ts | 0 src/{ux => cli-ux}/action/simple.ts | 0 src/{ux => cli-ux}/action/spinner.ts | 0 src/{ux => cli-ux}/action/spinners.ts | 0 src/{ux => cli-ux}/config.ts | 0 src/{ux => cli-ux}/exit.ts | 0 src/{ux => cli-ux}/flush.ts | 2 +- src/{ux => cli-ux}/global.d.ts | 0 src/{ux => cli-ux}/index.ts | 19 +++++++++---------- src/{ux => cli-ux}/list.ts | 0 src/{ux => cli-ux}/prompt.ts | 1 + src/{ux => cli-ux}/stream.ts | 0 src/{ux => cli-ux}/styled/index.ts | 0 src/{ux => cli-ux}/styled/json.ts | 2 +- src/{ux => cli-ux}/styled/object.ts | 0 src/{ux => cli-ux}/styled/progress.ts | 0 src/{ux => cli-ux}/styled/table.ts | 0 src/{ux => cli-ux}/styled/tree.ts | 0 src/{ux => cli-ux}/wait.ts | 0 src/command.ts | 4 ++-- src/config/config.ts | 4 ++-- src/config/ts-node.ts | 2 +- src/errors/config.ts | 2 +- src/errors/handle.ts | 2 +- src/errors/index.ts | 2 +- src/execute.ts | 6 +++--- src/help/index.ts | 2 +- src/index.ts | 10 +++++----- src/main.ts | 2 +- src/parser/errors.ts | 2 +- src/performance.ts | 2 +- src/screen.ts | 4 ++-- src/settings.ts | 3 +-- test/cli-ux/export.test.ts | 2 +- test/cli-ux/fancy.ts | 2 +- test/cli-ux/index.test.ts | 2 +- test/cli-ux/prompt.test.ts | 2 +- test/cli-ux/styled/header.test.ts | 2 +- test/cli-ux/styled/object.test.ts | 2 +- test/cli-ux/styled/progress.test.ts | 2 +- test/cli-ux/styled/table.e2e.ts | 2 +- test/cli-ux/styled/table.test.ts | 2 +- test/cli-ux/styled/tree.test.ts | 2 +- test/errors/handle.test.ts | 2 +- test/integration/sf.e2e.ts | 10 +++++++++- test/integration/util.ts | 13 ++++++++----- 53 files changed, 73 insertions(+), 89 deletions(-) create mode 100644 flush.d.ts create mode 100644 flush.js create mode 100644 handle.js rename src/{ux => cli-ux}/README.md (100%) rename src/{ux => cli-ux}/action/base.ts (100%) rename src/{ux => cli-ux}/action/simple.ts (100%) rename src/{ux => cli-ux}/action/spinner.ts (100%) rename src/{ux => cli-ux}/action/spinners.ts (100%) rename src/{ux => cli-ux}/config.ts (100%) rename src/{ux => cli-ux}/exit.ts (100%) rename src/{ux => cli-ux}/flush.ts (89%) rename src/{ux => cli-ux}/global.d.ts (100%) rename src/{ux => cli-ux}/index.ts (97%) rename src/{ux => cli-ux}/list.ts (100%) rename src/{ux => cli-ux}/prompt.ts (99%) rename src/{ux => cli-ux}/stream.ts (100%) rename src/{ux => cli-ux}/styled/index.ts (100%) rename src/{ux => cli-ux}/styled/json.ts (92%) rename src/{ux => cli-ux}/styled/object.ts (100%) rename src/{ux => cli-ux}/styled/progress.ts (100%) rename src/{ux => cli-ux}/styled/table.ts (100%) rename src/{ux => cli-ux}/styled/tree.ts (100%) rename src/{ux => cli-ux}/wait.ts (100%) diff --git a/README.md b/README.md index c8b893e95..2a7e60cd1 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaki CLI UX ===== -The [ux README](./src/ux/README.md) contains detailed usage examples of using the `ux` export. +The [ux README](./src/cli-ux/README.md) contains detailed usage examples of using the `ux` export. Usage ===== diff --git a/flush.d.ts b/flush.d.ts new file mode 100644 index 000000000..5ca9b097b --- /dev/null +++ b/flush.d.ts @@ -0,0 +1,3 @@ +declare const flush: () => Promise + +export = flush diff --git a/flush.js b/flush.js new file mode 100644 index 000000000..080811026 --- /dev/null +++ b/flush.js @@ -0,0 +1 @@ +module.exports = require('./lib/cli-ux/flush').flush diff --git a/guides/V3_MIGRATION.md b/guides/V3_MIGRATION.md index c130a80d4..82811fad4 100644 --- a/guides/V3_MIGRATION.md +++ b/guides/V3_MIGRATION.md @@ -12,7 +12,6 @@ Migrating to @oclif/core@V3 - [`handle`](#handle) - [`noCacheDefault` flag property replaces `isWritingManifest`](#nocachedefault-flag-property-replaces-iswritingmanifest) - [Features ๐ŸŽ‰](#features-) - - [Exports](#exports) - [Cache Flexible taxonomy Command Permutations](#cache-flexible-taxonomy-command-permutations) @@ -112,31 +111,6 @@ export const mySensitiveFlag = Flags.string({ ## Features ๐ŸŽ‰ -### Exports -The `exports` property in the package.json are now used for exporting a few individual pieces of functionality. - -The main export should continue to work as it did in v2: - -```typescript -import {handle, flush} from '@oclif/core' -``` - -But you can now import individual modules if desired: - -```typescript -import handle from '@oclif/core/handle' -import flush from '@oclif/core/flush' -``` - -These are the exports that are available: -`@oclif/core/execute` -`@oclif/core/flush` -`@oclif/core/handle` -`@oclif/core/run` -`@oclif/core/settings` - -**You will need to set moduleResolution to Node16 in your tsconfig.json to use this feature.** - ### Cache Flexible taxonomy Command Permutations The command permutations for flexible taxonomy are now cached in the oclif.manifest.json allowing for quicker startup times. diff --git a/handle.js b/handle.js new file mode 100644 index 000000000..aff2c83fc --- /dev/null +++ b/handle.js @@ -0,0 +1 @@ +module.exports = async error => require('./lib/errors/handle').handle(error) diff --git a/package.json b/package.json index e782e2e61..9ce7fda23 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,10 @@ "node": ">=16.0.0" }, "files": [ - "/lib" + "/lib", + "/flush.js", + "/flush.d.ts", + "/handle.js" ], "homepage": "https://github.com/oclif/core", "keywords": [ @@ -91,14 +94,6 @@ ], "license": "MIT", "main": "./lib/index.js", - "exports": { - ".": "./lib/index.js", - "./execute": "./lib/execute.js", - "./flush": "./lib/ux/flush.js", - "./handle": "./lib/errors/handle.js", - "./run": "./lib/main.js", - "./settings": "./lib/settings.js" - }, "repository": "oclif/core", "oclif": { "bin": "oclif", diff --git a/src/ux/README.md b/src/cli-ux/README.md similarity index 100% rename from src/ux/README.md rename to src/cli-ux/README.md diff --git a/src/ux/action/base.ts b/src/cli-ux/action/base.ts similarity index 100% rename from src/ux/action/base.ts rename to src/cli-ux/action/base.ts diff --git a/src/ux/action/simple.ts b/src/cli-ux/action/simple.ts similarity index 100% rename from src/ux/action/simple.ts rename to src/cli-ux/action/simple.ts diff --git a/src/ux/action/spinner.ts b/src/cli-ux/action/spinner.ts similarity index 100% rename from src/ux/action/spinner.ts rename to src/cli-ux/action/spinner.ts diff --git a/src/ux/action/spinners.ts b/src/cli-ux/action/spinners.ts similarity index 100% rename from src/ux/action/spinners.ts rename to src/cli-ux/action/spinners.ts diff --git a/src/ux/config.ts b/src/cli-ux/config.ts similarity index 100% rename from src/ux/config.ts rename to src/cli-ux/config.ts diff --git a/src/ux/exit.ts b/src/cli-ux/exit.ts similarity index 100% rename from src/ux/exit.ts rename to src/cli-ux/exit.ts diff --git a/src/ux/flush.ts b/src/cli-ux/flush.ts similarity index 89% rename from src/ux/flush.ts rename to src/cli-ux/flush.ts index ceae1d816..148975efb 100644 --- a/src/ux/flush.ts +++ b/src/cli-ux/flush.ts @@ -24,6 +24,6 @@ async function _flush() { return p } -export default async function flush(ms = 10_000): Promise { +export async function flush(ms = 10_000): Promise { await timeout(_flush(), ms) } diff --git a/src/ux/global.d.ts b/src/cli-ux/global.d.ts similarity index 100% rename from src/ux/global.d.ts rename to src/cli-ux/global.d.ts diff --git a/src/ux/index.ts b/src/cli-ux/index.ts similarity index 97% rename from src/ux/index.ts rename to src/cli-ux/index.ts index b1df2bbc1..a882f9258 100644 --- a/src/ux/index.ts +++ b/src/cli-ux/index.ts @@ -7,15 +7,14 @@ import {ExitError} from './exit' import {IPromptOptions} from './prompt' import * as styled from './styled' import {Table} from './styled' -export * from './styled/table' import * as uxPrompt from './prompt' import uxWait from './wait' import {stdout} from './stream' -import * as _flush from './flush' +import {flush as _flush} from './flush' const hyperlinker = require('hyperlinker') -export default class ux { +export class ux { public static config: Config = config public static get prompt(): typeof uxPrompt.prompt { @@ -110,7 +109,7 @@ export default class ux { } public static async flush(ms = 10_000): Promise { - await _flush.default(ms) + await _flush(ms) } } @@ -139,15 +138,20 @@ const warn = Errors.warn export { action, + ActionBase, annotation, anykey, + config, + Config, confirm, debug, done, error, exit, + ExitError, flush, info, + IPromptOptions, log, progress, prompt, @@ -155,17 +159,12 @@ export { styledJSON, styledObject, table, + Table, trace, tree, url, wait, warn, - ActionBase, - config, - Config, - ExitError, - IPromptOptions, - Table, } const uxProcessExitHandler = async () => { diff --git a/src/ux/list.ts b/src/cli-ux/list.ts similarity index 100% rename from src/ux/list.ts rename to src/cli-ux/list.ts diff --git a/src/ux/prompt.ts b/src/cli-ux/prompt.ts similarity index 99% rename from src/ux/prompt.ts rename to src/cli-ux/prompt.ts index 7f4d7a0cc..a3a8c0d11 100644 --- a/src/ux/prompt.ts +++ b/src/cli-ux/prompt.ts @@ -93,6 +93,7 @@ async function _prompt(name: string, inputOptions: Partial = {}) ...inputOptions, } const passwordPrompt = require('password-prompt') + switch (options.type) { case 'normal': return normal(options) diff --git a/src/ux/stream.ts b/src/cli-ux/stream.ts similarity index 100% rename from src/ux/stream.ts rename to src/cli-ux/stream.ts diff --git a/src/ux/styled/index.ts b/src/cli-ux/styled/index.ts similarity index 100% rename from src/ux/styled/index.ts rename to src/cli-ux/styled/index.ts diff --git a/src/ux/styled/json.ts b/src/cli-ux/styled/json.ts similarity index 92% rename from src/ux/styled/json.ts rename to src/cli-ux/styled/json.ts index 363b4927a..55a06282a 100644 --- a/src/ux/styled/json.ts +++ b/src/cli-ux/styled/json.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk' -import ux from '../index' +import {ux} from '../index' export default function styledJSON(obj: unknown): void { const json = JSON.stringify(obj, null, 2) diff --git a/src/ux/styled/object.ts b/src/cli-ux/styled/object.ts similarity index 100% rename from src/ux/styled/object.ts rename to src/cli-ux/styled/object.ts diff --git a/src/ux/styled/progress.ts b/src/cli-ux/styled/progress.ts similarity index 100% rename from src/ux/styled/progress.ts rename to src/cli-ux/styled/progress.ts diff --git a/src/ux/styled/table.ts b/src/cli-ux/styled/table.ts similarity index 100% rename from src/ux/styled/table.ts rename to src/cli-ux/styled/table.ts diff --git a/src/ux/styled/tree.ts b/src/cli-ux/styled/tree.ts similarity index 100% rename from src/ux/styled/tree.ts rename to src/cli-ux/styled/tree.ts diff --git a/src/ux/wait.ts b/src/cli-ux/wait.ts similarity index 100% rename from src/ux/wait.ts rename to src/cli-ux/wait.ts diff --git a/src/command.ts b/src/command.ts index fba35e53e..1bccd6ef6 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,7 +1,7 @@ import {fileURLToPath} from 'url' import * as chalk from 'chalk' import {format, inspect} from 'util' -import ux from './ux' +import {ux} from './cli-ux' import {Config} from './config' import * as Errors from './errors' import {PrettyPrintableError} from './errors' @@ -27,7 +27,7 @@ import {CommandError} from './interfaces/errors' import {boolean} from './flags' import {requireJson} from './util' import {PJSON} from './interfaces' -import {stdout, stderr} from './ux/stream' +import {stdout, stderr} from './cli-ux/stream' const pjson = requireJson(__dirname, '..', 'package.json') diff --git a/src/config/config.ts b/src/config/config.ts index f77604889..3e038e0cb 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -14,9 +14,9 @@ import ModuleLoader from '../module-loader' import {getHelpFlagAdditions} from '../help' import {Command} from '../command' import {CompletableOptionFlag, Arg} from '../interfaces/parser' -import {stdout} from '../ux/stream' +import {stdout} from '../cli-ux/stream' import Performance from '../performance' -import settings from '../settings' +import {settings} from '../settings' import {userInfo as osUserInfo} from 'node:os' import {sep} from 'node:path' import PluginLoader from './plugin-loader' diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index d4fb96a9e..478f5e2dc 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -3,7 +3,7 @@ import * as path from 'path' import * as TSNode from 'ts-node' import {TSConfig, Plugin} from '../interfaces' -import settings from '../settings' +import {settings} from '../settings' import {isProd} from '../util' import {Debug} from './util' import {Config} from './config' diff --git a/src/errors/config.ts b/src/errors/config.ts index b0876cada..7a757f969 100644 --- a/src/errors/config.ts +++ b/src/errors/config.ts @@ -1,4 +1,4 @@ -import settings from '../settings' +import {settings} from '../settings' import {Logger} from './logger' function displayWarnings() { diff --git a/src/errors/handle.ts b/src/errors/handle.ts index 81cb6ecf4..0c0af3d3d 100644 --- a/src/errors/handle.ts +++ b/src/errors/handle.ts @@ -7,7 +7,7 @@ import clean = require('clean-stack') import {CLIError} from './errors/cli' import {OclifError, PrettyPrintableError} from '../interfaces' -export default async function handle(err: Error & Partial & Partial & {skipOclifErrorHandling?: boolean}): Promise { +export async function handle(err: Error & Partial & Partial & {skipOclifErrorHandling?: boolean}): Promise { try { if (!err) err = new CLIError('no error?') if (err.message === 'SIGINT') process.exit(1) diff --git a/src/errors/index.ts b/src/errors/index.ts index a55c06bd2..4fbbbfe0e 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -1,4 +1,4 @@ -export {default as handle} from './handle' +export {handle} from './handle' export {ExitError} from './errors/exit' export {ModuleLoadError} from './errors/module-load' export {CLIError} from './errors/cli' diff --git a/src/execute.ts b/src/execute.ts index a8761b9ad..f83b01475 100644 --- a/src/execute.ts +++ b/src/execute.ts @@ -1,6 +1,6 @@ -import settings from './settings' -import flush from './ux/flush' -import handle from './errors/handle' +import {settings} from './settings' +import {flush} from './cli-ux/flush' +import {handle} from './errors/handle' import run from './main' import * as Interfaces from './interfaces' diff --git a/src/help/index.ts b/src/help/index.ts index 37cf2bba9..20cc06234 100644 --- a/src/help/index.ts +++ b/src/help/index.ts @@ -9,7 +9,7 @@ import {formatCommandDeprecationWarning, getHelpFlagAdditions, standardizeIDFrom import {HelpFormatter} from './formatter' import {toCached} from '../config/config' import {Command} from '../command' -import {stdout} from '../ux/stream' +import {stdout} from '../cli-ux/stream' export {CommandHelp} from './command' export {standardizeIDFromArgv, loadHelpClass, getHelpFlagAdditions, normalizeArgv} from './util' diff --git a/src/index.ts b/src/index.ts index 40a4ae33c..97621e091 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import {Command} from './command' import run from './main' import execute from './execute' -import handle from './errors/handle' +import {handle} from './errors/handle' import {Config, Plugin, tsPath, toCached} from './config' import * as Interfaces from './interfaces' import * as Errors from './errors' @@ -11,12 +11,12 @@ import {CommandHelp, HelpBase, Help, loadHelpClass} from './help' import {toStandardizedId, toConfiguredId} from './help/util' import * as Parser from './parser' import {Hook} from './interfaces/hooks' -import settings, {Settings} from './settings' +import {settings, Settings} from './settings' import {HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable} from './help/formatter' -import * as ux from './ux' -import {stderr, stdout} from './ux/stream' +import * as ux from './cli-ux' +import {stderr, stdout} from './cli-ux/stream' import Performance from './performance' -import flush from './ux/flush' +import {flush} from './cli-ux/flush' export { Args, diff --git a/src/main.ts b/src/main.ts index c0107d24c..488679ab4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,7 +6,7 @@ import * as Interfaces from './interfaces' import {URL} from 'url' import {Config} from './config' import {getHelpFlagAdditions, loadHelpClass, normalizeArgv} from './help' -import {stdout} from './ux/stream' +import {stdout} from './cli-ux/stream' import Performance from './performance' const debug = require('debug')('oclif:main') diff --git a/src/parser/errors.ts b/src/parser/errors.ts index 63fd884b8..d6a832159 100644 --- a/src/parser/errors.ts +++ b/src/parser/errors.ts @@ -1,7 +1,7 @@ import {CLIError} from '../errors' import {flagUsages} from './help' -import {renderList} from '../ux/list' +import {renderList} from '../cli-ux/list' import * as chalk from 'chalk' import {OptionFlag, Flag} from '../interfaces' import {uniq} from '../config/util' diff --git a/src/performance.ts b/src/performance.ts index d1e58f8d7..9705f88e0 100644 --- a/src/performance.ts +++ b/src/performance.ts @@ -1,5 +1,5 @@ import {PerformanceObserver, performance} from 'perf_hooks' -import settings from './settings' +import {settings} from './settings' type Details = Record type PerfResult = { diff --git a/src/screen.ts b/src/screen.ts index cc86f4500..68b1e6fbb 100644 --- a/src/screen.ts +++ b/src/screen.ts @@ -1,5 +1,5 @@ -import {stdout, stderr} from './ux/stream' -import settings from './settings' +import {stdout, stderr} from './cli-ux/stream' +import {settings} from './settings' function termwidth(stream: any): number { if (!stream.isTTY) { diff --git a/src/settings.ts b/src/settings.ts index dd94d6d84..9f797f800 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -40,5 +40,4 @@ export type Settings = { // Set global.oclif to the new object if it wasn't set before if (!(global as any).oclif) (global as any).oclif = {} -const settings: Settings = (global as any).oclif as Settings -export default settings +export const settings: Settings = (global as any).oclif as Settings diff --git a/test/cli-ux/export.test.ts b/test/cli-ux/export.test.ts index 5caa755aa..4a50da411 100644 --- a/test/cli-ux/export.test.ts +++ b/test/cli-ux/export.test.ts @@ -1,4 +1,4 @@ -import ux, {Table, IPromptOptions, Config, ActionBase, ExitError} from '../../src/ux' +import {ux, Table, IPromptOptions, Config, ActionBase, ExitError} from '../../src/cli-ux' import {expect} from 'chai' type MyColumns = Record diff --git a/test/cli-ux/fancy.ts b/test/cli-ux/fancy.ts index 9f7a60bbf..74d8c174f 100644 --- a/test/cli-ux/fancy.ts +++ b/test/cli-ux/fancy.ts @@ -2,7 +2,7 @@ import {expect, fancy as base, FancyTypes} from 'fancy-test' import {rm} from 'node:fs/promises' import {join} from 'node:path' -import ux from '../../src/ux' +import {ux} from '../../src/cli-ux' export { expect, diff --git a/test/cli-ux/index.test.ts b/test/cli-ux/index.test.ts index 234c9d025..866449a9a 100644 --- a/test/cli-ux/index.test.ts +++ b/test/cli-ux/index.test.ts @@ -1,4 +1,4 @@ -import ux from '../../src/ux' +import {ux} from '../../src/cli-ux' import {expect, fancy} from './fancy' const hyperlinker = require('hyperlinker') diff --git a/test/cli-ux/prompt.test.ts b/test/cli-ux/prompt.test.ts index 18676fb74..fd8c6815c 100644 --- a/test/cli-ux/prompt.test.ts +++ b/test/cli-ux/prompt.test.ts @@ -2,7 +2,7 @@ import * as chai from 'chai' const expect = chai.expect -import ux from '../../src/ux' +import {ux} from '../../src/cli-ux' import {fancy} from './fancy' diff --git a/test/cli-ux/styled/header.test.ts b/test/cli-ux/styled/header.test.ts index 619d071a8..65fb7146b 100644 --- a/test/cli-ux/styled/header.test.ts +++ b/test/cli-ux/styled/header.test.ts @@ -2,7 +2,7 @@ import {expect} from 'chai' import {stdout} from '../../../src' import {stub, SinonStub} from 'sinon' -import ux from '../../../src/ux' +import {ux} from '../../../src/cli-ux' describe('styled/header', () => { let writeStub: SinonStub diff --git a/test/cli-ux/styled/object.test.ts b/test/cli-ux/styled/object.test.ts index f0e4d938e..8c35056bd 100644 --- a/test/cli-ux/styled/object.test.ts +++ b/test/cli-ux/styled/object.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import ux from '../../../src/ux' +import {ux} from '../../../src/cli-ux' describe('styled/object', () => { fancy diff --git a/test/cli-ux/styled/progress.test.ts b/test/cli-ux/styled/progress.test.ts index 098ec0658..63ecd754a 100644 --- a/test/cli-ux/styled/progress.test.ts +++ b/test/cli-ux/styled/progress.test.ts @@ -1,5 +1,5 @@ import {expect, fancy} from 'fancy-test' -import ux from '../../../src/ux' +import {ux} from '../../../src/cli-ux' describe('progress', () => { // single bar diff --git a/test/cli-ux/styled/table.e2e.ts b/test/cli-ux/styled/table.e2e.ts index a5b162d11..c590209f1 100644 --- a/test/cli-ux/styled/table.e2e.ts +++ b/test/cli-ux/styled/table.e2e.ts @@ -1,5 +1,5 @@ import {expect, fancy} from 'fancy-test' -import ux from '../../../src/ux' +import {ux} from '../../../src/cli-ux' describe('styled/table', () => { describe('null/undefined handling', () => { diff --git a/test/cli-ux/styled/table.test.ts b/test/cli-ux/styled/table.test.ts index 90056da6f..6fa148a51 100644 --- a/test/cli-ux/styled/table.test.ts +++ b/test/cli-ux/styled/table.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import ux from '../../../src/ux' +import {ux} from '../../../src/cli-ux' import * as screen from '../../../src/screen' /* eslint-disable camelcase */ diff --git a/test/cli-ux/styled/tree.test.ts b/test/cli-ux/styled/tree.test.ts index 5e4e470b4..0706d0649 100644 --- a/test/cli-ux/styled/tree.test.ts +++ b/test/cli-ux/styled/tree.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import ux from '../../../src/ux' +import {ux} from '../../../src/cli-ux' describe('styled/tree', () => { fancy diff --git a/test/errors/handle.test.ts b/test/errors/handle.test.ts index db08132a6..29536b888 100644 --- a/test/errors/handle.test.ts +++ b/test/errors/handle.test.ts @@ -4,7 +4,7 @@ import * as path from 'path' import * as process from 'process' import {CLIError, config, ExitError} from '../../src/errors' -import handle from '../../src/errors/handle' +import {handle} from '../../src/errors/handle' import {exit as exitErrorThrower} from '../../src/errors' const errlog = path.join(__dirname, '../tmp/mytest/error.log') diff --git a/test/integration/sf.e2e.ts b/test/integration/sf.e2e.ts index 3b2bc4949..69ea75431 100644 --- a/test/integration/sf.e2e.ts +++ b/test/integration/sf.e2e.ts @@ -15,7 +15,15 @@ describe('Salesforce CLI (sf)', () => { let executor: Executor before(async () => { process.env.SFDX_TELEMETRY_DISABLE_ACKNOWLEDGEMENT = 'true' - executor = await setup(__filename, {repo: 'https://github.com/salesforcecli/cli'}) + executor = await setup(__filename, { + repo: 'https://github.com/salesforcecli/cli', + // Allowing failed install here because it let's attempt to run the tests + // even if there's a linting error caused by the code changes. This is only + // acceptable since we're crossing major versions at the moment. It should + // be removed as soon as sf uses v3. + allowFailedInstall: true, + compileCmd: 'yarn compile', + }) }) it('should show custom help', async () => { diff --git a/test/integration/util.ts b/test/integration/util.ts index 44e875e99..98d4e588f 100644 --- a/test/integration/util.ts +++ b/test/integration/util.ts @@ -23,6 +23,8 @@ export type SetupOptions = { repo: string; plugins?: string[]; subDir?: string; + allowFailedInstall?: boolean; + compileCmd?: string; } export type ExecutorOptions = { @@ -187,15 +189,16 @@ export async function setup(testFile: string, options: SetupOptions): Promise Date: Fri, 1 Sep 2023 12:11:30 +0000 Subject: [PATCH 19/55] chore(release): 3.0.0-beta.10 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ce7fda23..c17c81270 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.9", + "version": "3.0.0-beta.10", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From cec0ef28e26cf12fb427fa2d0ea0527dcf71932f Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 1 Sep 2023 06:17:46 -0600 Subject: [PATCH 20/55] fix: revert styledJSON change --- src/cli-ux/styled/json.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli-ux/styled/json.ts b/src/cli-ux/styled/json.ts index 55a06282a..6da73b23c 100644 --- a/src/cli-ux/styled/json.ts +++ b/src/cli-ux/styled/json.ts @@ -1,6 +1,6 @@ import * as chalk from 'chalk' -import {ux} from '../index' +import {ux} from '../../index' export default function styledJSON(obj: unknown): void { const json = JSON.stringify(obj, null, 2) From 30acd47a7dbc5ec4b1418ca5a9a84c4e667340b5 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Fri, 1 Sep 2023 12:18:08 +0000 Subject: [PATCH 21/55] chore(release): 3.0.0-beta.11 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c17c81270..a62fa0003 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.11", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 96ca18085defa7a055b1e3df56131c9305988a50 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 1 Sep 2023 12:49:41 -0600 Subject: [PATCH 22/55] fix: update imports in Config --- src/config/config.ts | 34 ++++++++++++++++------------------ src/interfaces/plugin.ts | 4 ---- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/config/config.ts b/src/config/config.ts index 28e78ef30..6ed2583a7 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,10 +1,10 @@ -import {CLIError, error, exit, warn} from '../errors' -import * as ejs from 'ejs' -import * as os from 'os' -import * as path from 'path' -import {fileURLToPath, URL} from 'url' -import {format} from 'util' +import {fileURLToPath, URL} from 'node:url' +import {format} from 'node:util' +import {userInfo as osUserInfo, arch, platform, homedir, tmpdir, type, release} from 'node:os' +import {sep, join} from 'node:path' +import * as ejs from 'ejs' +import {CLIError, error, exit, warn} from '../errors' import {Options, Plugin as IPlugin} from '../interfaces/plugin' import {Config as IConfig, ArchTypes, PlatformTypes, LoadOptions, VersionDetails} from '../interfaces/config' import {Hook, Hooks, PJSON, Topic} from '../interfaces' @@ -17,8 +17,6 @@ import {CompletableOptionFlag, Arg} from '../interfaces/parser' import {stdout} from '../cli-ux/stream' import Performance from '../performance' import {settings} from '../settings' -import {userInfo as osUserInfo} from 'node:os' -import {sep} from 'node:path' import PluginLoader from './plugin-loader' // eslint-disable-next-line new-cap @@ -171,8 +169,8 @@ export class Config implements IConfig { this.channel = this.options.channel || channelFromVersion(this.version) this.valid = Config._rootPlugin.valid - this.arch = (os.arch() === 'ia32' ? 'x86' : os.arch() as any) - this.platform = WSL ? 'wsl' : os.platform() as any + this.arch = (arch() === 'ia32' ? 'x86' : arch() as any) + this.platform = WSL ? 'wsl' : platform() as any this.windows = this.platform === 'win32' this.bin = this.pjson.oclif.bin || this.name this.binAliases = this.pjson.oclif.binAliases @@ -186,11 +184,11 @@ export class Config implements IConfig { this.shell = this._shell() this.debug = this._debug() - this.home = process.env.HOME || (this.windows && this.windowsHome()) || os.homedir() || os.tmpdir() + this.home = process.env.HOME || (this.windows && this.windowsHome()) || homedir() || tmpdir() this.cacheDir = this.scopedEnvVar('CACHE_DIR') || this.macosCacheDir() || this.dir('cache') this.configDir = this.scopedEnvVar('CONFIG_DIR') || this.dir('config') this.dataDir = this.scopedEnvVar('DATA_DIR') || this.dir('data') - this.errlog = path.join(this.cacheDir, 'error.log') + this.errlog = join(this.cacheDir, 'error.log') this.binPath = this.scopedEnvVar('BINPATH') this.npmRegistry = this.scopedEnvVar('NPM_REGISTRY') || this.pjson.oclif.npmRegistry @@ -527,7 +525,7 @@ export class Config implements IConfig { architecture, nodeVersion, pluginVersions: Object.fromEntries([...this.plugins.values()].map(p => [p.name, {version: p.version, type: p.type, root: p.root}])), - osVersion: `${os.type()} ${os.release()}`, + osVersion: `${type()} ${release()}`, shell: this.shell, rootPath: this.root, } @@ -544,7 +542,7 @@ export class Config implements IConfig { const host = this.pjson.oclif.update.s3.host if (!host) throw new Error('no s3 host is set') const url = new URL(host) - url.pathname = path.join(url.pathname, key) + url.pathname = join(url.pathname, key) return url.toString() } @@ -555,8 +553,8 @@ export class Config implements IConfig { protected dir(category: 'cache' | 'data' | 'config'): string { const base = process.env[`XDG_${category.toUpperCase()}_HOME`] || (this.windows && process.env.LOCALAPPDATA) || - path.join(this.home, category === 'data' ? '.local/share' : '.' + category) - return path.join(base, this.dirname) + join(this.home, category === 'data' ? '.local/share' : '.' + category) + return join(base, this.dirname) } protected windowsHome(): string | undefined { @@ -564,7 +562,7 @@ export class Config implements IConfig { } protected windowsHomedriveHome(): string | undefined { - return (process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE!, process.env.HOMEPATH!)) + return (process.env.HOMEDRIVE && process.env.HOMEPATH && join(process.env.HOMEDRIVE!, process.env.HOMEPATH!)) } protected windowsUserprofileHome(): string | undefined { @@ -572,7 +570,7 @@ export class Config implements IConfig { } protected macosCacheDir(): string | undefined { - return (this.platform === 'darwin' && path.join(this.home, 'Library', 'Caches', this.dirname)) || undefined + return (this.platform === 'darwin' && join(this.home, 'Library', 'Caches', this.dirname)) || undefined } protected _shell(): string { diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index 037712219..f4467e90b 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -80,9 +80,5 @@ export interface Plugin { findCommand(id: string, opts: { must: true }): Promise; findCommand(id: string, opts?: { must: boolean }): Promise | undefined; -<<<<<<< HEAD load(): Promise; -======= - load(isWritingManifest?: boolean): Promise; ->>>>>>> main } From 660ec2cf60ac307755155f7ad895150289dce757 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Fri, 1 Sep 2023 18:50:08 +0000 Subject: [PATCH 23/55] chore(release): 3.0.0-beta.12 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a62fa0003..58560267e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.11", + "version": "3.0.0-beta.12", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 299df21d1ca0ead15bd55312dec6d859c0c4c862 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Wed, 6 Sep 2023 07:58:35 -0700 Subject: [PATCH 24/55] feat: skip ts-node register for ESM plugins (#778) * feat: skip ts-node register for ESM plugins * chore: update debug message --- src/config/ts-node.ts | 33 ++++++++++-------------------- test/integration/esm-cjs.ts | 40 ++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index 478f5e2dc..416cbf708 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -6,7 +6,6 @@ import {TSConfig, Plugin} from '../interfaces' import {settings} from '../settings' import {isProd} from '../util' import {Debug} from './util' -import {Config} from './config' import {memoizedWarn} from '../errors' // eslint-disable-next-line new-cap const debug = Debug('ts-node') @@ -110,7 +109,6 @@ function registerTSNode(root: string): TSConfig | undefined { */ export function tsPath(root: string, orig: string, plugin: Plugin): string export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined -// eslint-disable-next-line complexity export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined { if (!orig) return orig orig = orig.startsWith(root) ? orig : path.join(root, orig) @@ -122,32 +120,21 @@ export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): return orig } - // Skip ts-node registration if plugin is an ESM plugin executing from a CJS plugin - if (plugin?.moduleType === 'module' && Config.rootPlugin?.moduleType === 'commonjs') { - debug(`Skipping ts-node registration for ${root} because it's an ESM module but the root plugin is CommonJS`) + // Skip ts-node registration for ESM plugins. + // The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time. + // See the following: + // - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228 + // - https://github.com/nodejs/node/issues/49432 + // - https://github.com/nodejs/node/pull/49407 + // - https://github.com/nodejs/node/issues/34049 + if (plugin?.moduleType === 'module') { + debug(`Skipping ts-node registration for ${root} because it's an ESM module`) if (plugin.type === 'link') - memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-compiled from a CommonJS root plugin. Existing compiled source will be used instead.`) + memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`) return orig } - // If plugin is an ESM plugin being executed from an ESM root plugin, check to see if ts-node/esm loader has been set - // either in the NODE_OPTIONS env var or from the exec args. If the ts-node/esm loader has NOT been loaded then we want - // to skip ts-node registration so that it falls back on the compiled source. - if (plugin?.moduleType === 'module') { - const tsNodeEsmLoaderInExecArgv = process.execArgv.includes('--loader') && process.execArgv.includes('ts-node/esm') - const tsNodeEsmLoaderInNodeOptions = process.env.NODE_OPTIONS?.includes('--loader=ts-node/esm') ?? false - if (!tsNodeEsmLoaderInExecArgv && !tsNodeEsmLoaderInNodeOptions) { - debug(`Skipping ts-node registration for ${root} because it's an ESM module but the ts-node/esm loader hasn't been run`) - debug('try setting NODE_OPTIONS="--loader ts-node/esm" in your environment.') - if (plugin.type === 'link') { - memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-compiled without setting NODE_OPTIONS="--loader=ts-node/esm" in the environment. Existing compiled source will be used instead.`) - } - - return orig - } - } - if (settings.tsnodeEnabled === undefined && isProd() && plugin?.type !== 'link') { debug(`Skipping ts-node registration for ${root} because NODE_ENV is NOT "test" or "development"`) return orig diff --git a/test/integration/esm-cjs.ts b/test/integration/esm-cjs.ts index 827cd3a7a..afbd5c7eb 100644 --- a/test/integration/esm-cjs.ts +++ b/test/integration/esm-cjs.ts @@ -365,34 +365,34 @@ type CleanUpOptions = { await test('Link ESM plugin to ESM root plugin', async () => { const plugin = PLUGINS.esm2 - const linkedPlugin = await linkPlugin({executor: esmExecutor, plugin, script: 'run'}) + await linkPlugin({executor: esmExecutor, plugin, script: 'run'}) // test bin/run - // NOTE: this also tests that the compiled source is used when ts-node/esm loader is not specified await runCommand({ executor: esmExecutor, plugin, script: 'run', expectStrings: [plugin.commandText, plugin.hookText], }) - // test un-compiled changes with bin/run - await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'run', - expectStrings: ['howdy', plugin.hookText], - env: {NODE_OPTIONS: '--loader=ts-node/esm'}, - }) - // test un-compiled changes with bin/dev - await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'dev', - expectStrings: ['cheers', plugin.hookText], - env: {NODE_OPTIONS: '--loader=ts-node/esm'}, - }) + // Skipping these because we decided to not support auto-transpiling ESM plugins at this time. + // // test un-compiled changes with bin/run + // await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) + // await runCommand({ + // executor: esmExecutor, + // plugin, + // script: 'run', + // expectStrings: ['howdy', plugin.hookText], + // env: {NODE_OPTIONS: '--loader=ts-node/esm'}, + // }) + // // test un-compiled changes with bin/dev + // await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) + // await runCommand({ + // executor: esmExecutor, + // plugin, + // script: 'dev', + // expectStrings: ['cheers', plugin.hookText], + // env: {NODE_OPTIONS: '--loader=ts-node/esm'}, + // }) await cleanUp({executor: esmExecutor, plugin, script: 'run'}) }) From c1423d5fcda984b0e5a9f707a135e2941a27b9ca Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Wed, 6 Sep 2023 14:58:58 +0000 Subject: [PATCH 25/55] chore(release): 3.0.0-beta.13 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58560267e..198447fc7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.12", + "version": "3.0.0-beta.13", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From bd2461b26b98f2ec3bc6aac99db02e6df2f3b277 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 22 Sep 2023 11:07:19 -0700 Subject: [PATCH 26/55] feat: final v3 features (#779) * feat: add charAliases * feat: add Flags.option * fix: undo default options * fix: allow bin/dev.js to auto-transpile ESM plugin * chore: update execute examples * fix: update tsnode skip logic * chore: test debugging * fix: ts-node skip logic * fix: ts-node skip logic * feat: cache relativePath and isESM in manifest * fix: calculate id permutations at runtime to support backwards compatability * perf: avoid findLegacyRoot for linked plugins * chore: remove env var * fix: improve perf metrics * perf: improve perf debug output * perf: more debug improvements * test: compilation errors * fix: make relativePath OS safe * test: use sf esm branch * perf: give full hook path * chore: test debugging * chore: test debugging * chore: test debugging * test: set shell for e2e tests * fix: flag types respect defaults * feat: node protocol * test: move windows sf integration tests into separate job * test: update assertion on plugins install test * test: use correct file path * test: use -Force * test: use bash shell * test: clean up * test: remove shell option * test: oclif/config, core v1, and core v2 interop tests * chore: drop node 16 * test: remove lts/-1 * chore: code review * chore: add pre core migration guide * refactor: break up ModuleLoader * chore: remove unicorn/consistent-function-scoping * chore: remove unicorn/no-missing-imports * chore: remove @typescript-eslint/no-empty-function * chore: remove ban-ts-comment and ban-ts-ignore * chore: add sort-import rule * fix: throw error if non-multiple flag provided more than once * test: mutliples of non-multiple flag test * test: ut for charAliases (#791) * test: duplicate aliases tests * test: extend timeout * test: split windows esm-cjs tests * test: parallelize linux interop tests too * test: typo * test: use right executor * test: use right executor for clean up * chore: update eslint libs (#792) * chore: update eslint libs * chore: clean up * chore: tests and linting * test: windows paths * fix: exports * test: update run import * chore: replaceAll * fix: error exit codes * fix: use es2021 * feat: use ES2022 * fix: use ES2021 again * test: incorporate flags and args in esm-cjs tests * test: use correct expected values --------- Co-authored-by: Shane McLaughlin --- .eslintrc | 19 - .eslintrc.json | 14 + .github/workflows/create-github-release.yml | 1 + .github/workflows/test.yml | 64 +- README.md | 4 +- guides/PRE_CORE_MIGRATION.md | 182 ++ guides/V3_MIGRATION.md | 35 + package.json | 23 +- src/args.ts | 29 +- src/cli-ux/action/base.ts | 31 +- src/cli-ux/action/simple.ts | 18 +- src/cli-ux/action/spinner.ts | 12 +- src/cli-ux/config.ts | 12 +- src/cli-ux/flush.ts | 4 +- src/cli-ux/index.ts | 79 +- src/cli-ux/list.ts | 2 +- src/cli-ux/prompt.ts | 41 +- src/cli-ux/styled/index.ts | 17 +- src/cli-ux/styled/json.ts | 2 +- src/cli-ux/styled/object.ts | 13 +- src/cli-ux/styled/table.ts | 68 +- src/cli-ux/wait.ts | 8 +- src/command.ts | 65 +- src/config/config.ts | 109 +- src/config/plugin-loader.ts | 32 +- src/config/plugin.ts | 97 +- src/config/ts-node.ts | 73 +- src/config/util.ts | 49 +- src/errors/config.ts | 2 +- src/errors/errors/cli.ts | 15 +- src/errors/errors/exit.ts | 2 - src/errors/errors/module-load.ts | 2 - src/errors/errors/pretty-print.ts | 11 +- src/errors/handle.ts | 35 +- src/errors/index.ts | 16 +- src/errors/logger.ts | 8 +- src/execute.ts | 14 +- src/flags.ts | 186 +- src/help/command.ts | 63 +- src/help/docopts.ts | 10 +- src/help/formatter.ts | 22 +- src/help/index.ts | 44 +- src/help/root.ts | 5 +- src/help/util.ts | 18 +- src/index.ts | 73 +- src/interfaces/config.ts | 8 +- src/interfaces/errors.ts | 2 +- src/interfaces/parser.ts | 158 +- src/interfaces/plugin.ts | 1 + src/main.ts | 16 +- src/module-loader.ts | 328 +-- src/parser/errors.ts | 9 +- src/parser/help.ts | 2 +- src/parser/index.ts | 3 +- src/parser/parse.ts | 129 +- src/parser/validate.ts | 27 +- src/performance.ts | 37 +- src/screen.ts | 2 +- src/util.ts | 117 +- test/.eslintrc.json | 10 + test/cli-ux/export.test.ts | 2 +- test/cli-ux/fancy.ts | 10 +- test/cli-ux/prompt.test.ts | 2 +- test/cli-ux/styled/header.test.ts | 2 +- test/command/command.test.ts | 278 +-- .../test-help-in-src/src/test-help-plugin.ts | 5 +- test/command/main-esm.test.ts | 19 +- test/command/main.test.ts | 25 +- test/config/config.flexible.test.ts | 17 +- test/config/config.test.ts | 71 +- test/config/esm.test.ts | 8 +- .../fixtures/typescript/src/hooks/postrun.ts | 2 +- test/config/help.config.test.ts | 8 +- test/config/mixed-cjs-esm.test.ts | 6 +- test/config/mixed-esm-cjs.test.ts | 6 +- test/config/test.ts | 7 +- test/config/ts-node.test.ts | 107 +- test/config/typescript.test.ts | 6 +- test/errors/handle.test.ts | 134 +- test/errors/warn.test.ts | 1 - test/help/fixtures/fixtures.ts | 42 +- test/help/format-command-with-options.test.ts | 172 +- test/help/format-command.test.ts | 196 +- test/help/format-commands.test.ts | 4 +- test/help/format-root.test.ts | 78 +- test/help/format-topic.test.ts | 6 +- test/help/format-topics.test.ts | 2 +- test/help/help-test-utils.ts | 4 +- test/help/show-customized-help.test.ts | 22 +- test/help/show-help.test.ts | 28 +- test/help/util.test.ts | 2 +- test/integration/esm-cjs.ts | 475 +++-- test/integration/plugins.e2e.ts | 10 +- test/integration/sf.e2e.ts | 11 +- test/integration/util.ts | 94 +- test/interfaces/args.test-types.ts | 7 +- test/interfaces/flags.test-types.ts | 554 +++-- test/module-loader/module-loader.test.ts | 42 +- test/parser/parse.test.ts | 130 +- test/parser/validate.test.ts | 56 +- test/perf/parser.perf.ts | 10 +- test/util.test.ts | 2 +- tsconfig.json | 4 +- yarn.lock | 1890 ++++++++++------- 104 files changed, 4244 insertions(+), 2791 deletions(-) delete mode 100644 .eslintrc create mode 100644 .eslintrc.json create mode 100644 guides/PRE_CORE_MIGRATION.md create mode 100644 test/.eslintrc.json diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index bbbcc9f44..000000000 --- a/.eslintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": [ - "oclif", - "oclif-typescript" - ], - "rules": { - "unicorn/prefer-module": "off", - "unicorn/prefer-node-protocol": "off", - "unicorn/import-style": "off", - "unicorn/consistent-function-scoping": "off", - "unicorn/no-array-reduce": "off", - "unicorn/prefer-array-some": "off", - "node/no-missing-import": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/ban-ts-ignore": "off", - "@typescript-eslint/ban-ts-comment": "off", - "no-useless-constructor": "off" - } -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..2887d4e87 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "extends": [ + "oclif", + "oclif-typescript" + ], + "rules": { + "sort-imports": "error", + "unicorn/prefer-module": "off", + "unicorn/import-style": "error", + "unicorn/no-array-reduce": "off", + "unicorn/prefer-array-some": "off", + "no-useless-constructor": "off" + } +} diff --git a/.github/workflows/create-github-release.yml b/.github/workflows/create-github-release.yml index afe0cc289..4dfce7f21 100644 --- a/.github/workflows/create-github-release.yml +++ b/.github/workflows/create-github-release.yml @@ -24,6 +24,7 @@ jobs: # However, if this is a manual release (workflow_dispatch), then we want to disable skip-on-empty # This helps recover from forgetting to add semantic commits ('fix:', 'feat:', etc.) skip-on-empty: ${{ github.event_name == 'push' }} + generate-readme: false # docs: # # Most repos won't use this # # Depends on the 'release' job to avoid git collisions, not for any functionality reason diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f3a3d15fa..aa47b8e20 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,13 +17,11 @@ jobs: needs: linux-unit-tests strategy: matrix: - os: ["ubuntu-latest", "windows-latest"] - node_version: [lts/-1, lts/*, latest] + os: [ubuntu-latest, windows-latest] + node_version: [lts/*, latest] exclude: - os: windows-latest node_version: lts/* - - os: windows-latest - node_version: lts/-1 fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -32,39 +30,77 @@ jobs: with: node-version: ${{ matrix.node_version }} cache: yarn - - run: yarn install --network-timeout 600000 + - uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main - run: yarn build - - run: yarn test:e2e + - if: runner.os == 'Windows' + run: yarn mocha --forbid-only "test/**/*.e2e.ts" --exclude "test/integration/sf.e2e.ts" --parallel --timeout 1200000 + - if: runner.os == 'Linux' + run: yarn test:e2e + windows-sf-e2e: + # For whatever reason the windows-latest runner doesn't like it when you shell yarn commands in the sf repo + # which is an integral part of the setup for the tests. Instead, we replicate the setup here. + needs: linux-unit-tests + strategy: + fail-fast: false + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: latest + cache: yarn + - uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main + - run: yarn build + - run: yarn link + - run: New-Item -Path D:\a -Name "e2e" -ItemType "directory" + - run: New-Item -Path D:\a\e2e -Name "sf.e2e.ts" -ItemType "directory" + - run: | + git clone https://github.com/salesforcecli/cli.git --branch mdonnalley/esm + cd cli + + $Json = Get-Content package.json | ConvertFrom-Json + $Json.dependencies | Add-Member -Force -MemberType NoteProperty -Name "@oclif/core" -Value "file:D:\a\core\core" + $Json.resolutions | Add-Member -MemberType NoteProperty -Name "@oclif/core" -Value "D:\a\core\core" + $Json | ConvertTo-Json -Depth 9 | Out-File package.json + + yarn install --network-timeout 600000 + yarn link @oclif/core + yarn build + working-directory: D:\a\e2e\sf.e2e.ts + - run: yarn mocha --forbid-only "test/integration/sf.e2e.ts" --parallel --timeout 1200000 + env: + OCLIF_CORE_E2E_SKIP_SETUP: true + OCLIF_CORE_E2E_TEST_DIR: D:\a\e2e + DEBUG: e2e:* esm-cjs-interop: needs: linux-unit-tests strategy: matrix: - os: ["ubuntu-latest", "windows-latest"] - node_version: [lts/-1, lts/*, latest] + os: [ubuntu-latest, windows-latest] + node_version: [lts/*, latest] + test: [esm, cjs, precore, coreV1, coreV2] exclude: - os: windows-latest node_version: lts/* - - os: windows-latest - node_version: lts/-1 fail-fast: false runs-on: ${{ matrix.os }} - timeout-minutes: 60 + timeout-minutes: 75 steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node_version }} cache: yarn - - run: yarn install --network-timeout 600000 + - uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main - run: yarn build - - run: yarn test:esm-cjs + - run: yarn test:esm-cjs --test=${{ matrix.test }} nuts: needs: linux-unit-tests uses: salesforcecli/github-workflows/.github/workflows/externalNut.yml@main strategy: fail-fast: false matrix: - os: ["ubuntu-latest", "windows-latest"] + os: [ubuntu-latest, windows-latest] externalProjectGitUrl: - https://github.com/salesforcecli/plugin-auth - https://github.com/salesforcecli/plugin-data diff --git a/README.md b/README.md index 2a7e60cd1..1c577b447 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,11 @@ base library for oclif CLIs Migrating ===== +See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaking changes that occurred between v2 and v3. + See the [v2 migration guide](./guides/V2_MIGRATION.md) for an overview of breaking changes that occurred between v1 and v2. -See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaking changes that occurred between v2 and v3. +Migrating from `@oclif/config` and `@oclif/command`? See the [v1 migration guide](./guides/PRE_CORE_MIGRATION.md). CLI UX ===== diff --git a/guides/PRE_CORE_MIGRATION.md b/guides/PRE_CORE_MIGRATION.md new file mode 100644 index 000000000..16a6fdc20 --- /dev/null +++ b/guides/PRE_CORE_MIGRATION.md @@ -0,0 +1,182 @@ +Migrating to @oclif/core from deprecated libraries +============== + +Migrating to `@oclif/core` from the deprecated oclif libraries (`@oclif/config`, `@oclif/command`, `@oclif/error`, `@oclif/parser`) is relatively straight forward. + +- [Migrating to @oclif/core from deprecated libraries](#migrating-to-oclifcore-from-deprecated-libraries) + - [Update Imports](#update-imports) + - [Update your bin scripts](#update-your-bin-scripts) + - [Add `main` to your package.json](#add-main-to-your-packagejson) + - [Restore `-h`, `-v`, and `version`](#restore--h--v-and-version) + - [Configure the `topicSeparator`](#configure-the-topicseparator) + - [Update `this.parse` to `await this.parse`](#update-thisparse-to-await-thisparse) + - [Update `default` property on flag definitions](#update-default-property-on-flag-definitions) + - [Replace cli-ux library with `ux`](#replace-cli-ux-library-with-ux) + - [Single command CLIs](#single-command-clis) + +## Update Imports + +Replace imports from the old libraries with `@oclif/core`. For example, + +```typescript +import Help from '@oclif/plugin-help'; +import {Topic} from '@oclif/config'; +import {Command, Flags} from '@oclif/command' +``` + +With this import: + +```typescript +import {Command, Flags, Topic, Help} from '@oclif/core'; +``` + +## Update your bin scripts + +`@oclif/core` now supports separate bin scripts for production and development. + +You can copy these new bin scripts directly from our [example repository](https://github.com/oclif/hello-world/tree/main/bin). + +## Add `main` to your package.json + +We recommend that all oclif plugins specify the `main` field in their package.json so that we can begin working on supporting Yarn v2. + +```json +{ + "main": "lib/index.js" +} +``` + +All plugins will be required to have this field in the next major version of `@oclif/core`. + +## Restore `-h`, `-v`, and `version` + +`@oclif/config` automatically added `-h` as a short flag for `--help`, `-v` as a short flag for `--version`, and `version` as an alias for `--version`. + +`@oclif/core` removes these so you can now use those flags for whatever you want! However, we've added a way to restore that functionality if you want to keep it. + +Simply add the `additionalHelpFlags` and `additionalVersionFlags` properties to the oclif section of your package.json: + +```json +{ + "oclif": { + "additionalHelpFlags": ["-h"], + "additionalVersionFlags": ["-v"] + } +} +``` + +To get the `version` command, install `@oclif/plugin-version` into your CLI: + +```json +{ + "dependencies": { + "@oclif/plugin-version": "^3" + }, + "oclif": { + "plugins": [ + "@oclif/plugin-version" + ] + } +} +``` + +## Configure the `topicSeparator` + +By default, the `topicSeparator` is set to a colon (`:`) to maintain backwards compatibility with existing CLIs. If you prefer, you can now set it to a space. + +For colons: +```json +{ + "oclif": { + "topicSeparator": ":" + } +} +``` + +For spaces: +```json +{ + "oclif": { + "topicSeparator": " " + } +} +``` + +**NOTE: Using colons always works, even if you set the `topicSeparator` to spaces.** This means that you can enable spaces in your CLI without introducing a breaking change to your users. + +## Update `this.parse` to `await this.parse` + +The `parse` method on `Command` is now asynchronous (more [here](https://oclif.io/blog/#async-command-parsing)). So you'll now need to `await` any calls to `this.parse`: + +`const { args, flags } = this.parse(MyCommand)` => `const { args, flags } = await this.parse(MyCommand)` + +## Update `default` property on flag definitions + +The `default` property on flag definitions is now asynchronous. So you'll now need to await those. + +Example: + +```typescript +import {Command, Flags} from '@oclif/core' +import {readFile} from 'fs/promises' + +function getTeam(): Promise { + return readFile('team.txt', 'utf-8') +} + +export const team = Flags.build({ + char: 't', + description: 'team to use', + default: () => getTeam(), +}) + +export class MyCLI extends Command { + static flags = { + team: team(), + } + + async run() { + const {flags} = this.parse(MyCLI) + if (flags.team) console.log(`--team is ${flags.team}`) + } +} +``` + +## Replace cli-ux library with `ux` + +The [`cli-ux` library](https://github.com/oclif/cli-ux) has also been moved into `@oclif/core` in order to break a complex circular dependency between the two projects. + +All the exports that were available from `cli-ux` are now available under the `ux` namespace, with the exception of the `cli` export which was identical to the `ux` export. + +Old: + +```typescript +import { cli } from 'cli-ux` + +cli.log('hello world') +cli.action.start('doing things') +cli.action.stop() +``` + +New: + +```typescript +import { ux } from '@oclif/core` + +ux.log('hello world') +ux.action.start('doing things') +ux.action.stop() +``` + +## Single command CLIs + +Single command CLIs now are configured in a different way. To ensure your migrated CLI work as before, you have to add the following to your `oclif` configuration in the `package.json`: + +```json +"oclif": { + "default": ".", + "commands": "./lib" +} +``` + +Where `./lib` points to the folder in which your `tsconfig.json` is configured to output to (if you are using TypeScript), and your single command CLI entrypoint `index.(ts|js)` is located. diff --git a/guides/V3_MIGRATION.md b/guides/V3_MIGRATION.md index 82811fad4..9b83abc6f 100644 --- a/guides/V3_MIGRATION.md +++ b/guides/V3_MIGRATION.md @@ -13,6 +13,8 @@ Migrating to @oclif/core@V3 - [`noCacheDefault` flag property replaces `isWritingManifest`](#nocachedefault-flag-property-replaces-iswritingmanifest) - [Features ๐ŸŽ‰](#features-) - [Cache Flexible taxonomy Command Permutations](#cache-flexible-taxonomy-command-permutations) + - [charAliases Flag Property](#charaliases-flag-property) + - [Flags.option](#flagsoption) ## BREAKING CHANGES โ— @@ -114,3 +116,36 @@ export const mySensitiveFlag = Flags.string({ ### Cache Flexible taxonomy Command Permutations The command permutations for flexible taxonomy are now cached in the oclif.manifest.json allowing for quicker startup times. + +### charAliases Flag Property + +You can now define single character flag aliases using the `charAliases` property. + +### Flags.option + +There's a new flag type that infers the flag's type from the provided options. + +In v2 you would have needed to do something like this, + +```typescript +type Options = 'foo' | 'bar' +export default class MyCommand extends Command { + static flags = { + name: Flags.custom({ + options: ['foo', 'bar'], + })(), + } +} +``` + +Now in v3 you can do this, + +```typescript +export default class MyCommand extends Command { + static flags = { + name: Flags.option({ + options: ['foo', 'bar'] as const, + })(), + } +} +``` diff --git a/package.json b/package.json index 198447fc7..c3867d28d 100644 --- a/package.json +++ b/package.json @@ -45,15 +45,12 @@ "@types/chai-as-promised": "^7.1.5", "@types/clean-stack": "^2.1.1", "@types/ejs": "^3.1.2", - "@types/glob": "^8.1.0", "@types/indent-string": "^4.0.1", "@types/js-yaml": "^3.12.7", "@types/mocha": "^8.2.3", "@types/nock": "^11.1.0", - "@types/node": "^16", + "@types/node": "^18", "@types/node-notifier": "^8.0.2", - "@types/proxyquire": "^1.3.28", - "@types/shelljs": "^0.8.12", "@types/slice-ansi": "^4.0.0", "@types/strip-ansi": "^5.2.1", "@types/supports-color": "^8.1.1", @@ -64,23 +61,21 @@ "chai-as-promised": "^7.1.1", "commitlint": "^12.1.4", "cross-env": "^7.0.3", - "eslint": "^7.32.0", - "eslint-config-oclif": "^4.0.0", - "eslint-config-oclif-typescript": "^1.0.3", - "fancy-test": "^2.0.16", + "eslint": "^8.49.0", + "eslint-config-oclif": "^5.0.0", + "eslint-config-oclif-typescript": "^2.0.1", + "fancy-test": "^3.0.0-beta.2", "globby": "^11.1.0", "husky": "6", "mocha": "^10.2.0", "nock": "^13.3.0", - "proxyquire": "^2.1.3", - "shelljs": "^0.8.5", "shx": "^0.3.4", "sinon": "^11.1.2", - "tsd": "^0.25.0", - "typescript": "^4.9.5" + "tsd": "^0.29.0", + "typescript": "^5" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "files": [ "/lib", @@ -109,7 +104,7 @@ "build": "shx rm -rf lib && tsc", "commitlint": "commitlint", "compile": "tsc", - "lint": "eslint . --ext .ts --config .eslintrc", + "lint": "eslint . --ext .ts", "posttest": "yarn lint", "prepack": "yarn run build", "pretest": "yarn build --noEmit && tsc -p test --noEmit --skipLibCheck", diff --git a/src/args.ts b/src/args.ts index 9522ec751..861bf8954 100644 --- a/src/args.ts +++ b/src/args.ts @@ -1,7 +1,8 @@ -import {URL} from 'url' + import {Arg, ArgDefinition} from './interfaces/parser' -import {Command} from './command' import {dirExists, fileExists, isNotFalsy} from './util' +import {Command} from './command' +import {URL} from 'node:url' /** * Create a custom arg. @@ -22,15 +23,13 @@ import {dirExists, fileExists, isNotFalsy} from './util' */ export function custom>(defaults: Partial>): ArgDefinition export function custom>(defaults: Partial>): ArgDefinition { - return (options: any = {}) => { - return { - parse: async (i: string, _context: Command, _opts: P) => i, - ...defaults, - ...options, - input: [] as string[], - type: 'option', - } - } + return (options: any = {}) => ({ + parse: async (i: string, _context: Command, _opts: P) => i, + ...defaults, + ...options, + input: [] as string[], + type: 'option', + }) } export const boolean = custom({ @@ -38,7 +37,7 @@ export const boolean = custom({ }) export const integer = custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { if (!/^-?\d+$/.test(input)) throw new Error(`Expected an integer but received: ${input}`) const num = Number.parseInt(input, 10) @@ -51,7 +50,7 @@ export const integer = custom({ }) export const directory = custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { if (opts.exists) return dirExists(input) return input @@ -59,7 +58,7 @@ export const directory = custom({ }) export const file = custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { if (opts.exists) return fileExists(input) return input @@ -71,7 +70,7 @@ export const file = custom({ * if the string is not a valid URL. */ export const url = custom({ - parse: async input => { + async parse(input) { try { return new URL(input) } catch { diff --git a/src/cli-ux/action/base.ts b/src/cli-ux/action/base.ts index 6e71dcb59..bd2a5e54e 100644 --- a/src/cli-ux/action/base.ts +++ b/src/cli-ux/action/base.ts @@ -1,6 +1,6 @@ -import {inspect} from 'util' -import {castArray} from '../../util' import {stderr, stdout} from '../stream' +import {castArray} from '../../util' +import {inspect} from 'node:util' export interface ITask { action: string; @@ -37,7 +37,7 @@ export class ActionBase { } public stop(msg = 'done'): void { - const task = this.task + const {task} = this if (!task) { return } @@ -80,7 +80,7 @@ export class ActionBase { } set status(status: string | undefined) { - const task = this.task + const {task} = this if (!task) { return } @@ -93,8 +93,8 @@ export class ActionBase { task.status = status } - public async pauseAsync(fn: () => Promise, icon?: string): Promise { - const task = this.task + public async pauseAsync(fn: () => Promise, icon?: string): Promise { + const {task} = this const active = task && task.active if (task && active) { this._pause(icon) @@ -111,7 +111,7 @@ export class ActionBase { } public pause(fn: () => any, icon?: string): Promise { - const task = this.task + const {task} = this const active = task && task.active if (task && active) { this._pause(icon) @@ -143,7 +143,9 @@ export class ActionBase { throw new Error('not implemented') } - protected _updateStatus(_: string | undefined, __?: string): void {} + protected _updateStatus(_: string | undefined, __?: string): void { + // Not implemented + } // mock out stdout/stderr so it doesn't screw up the rendering protected _stdout(toggle: boolean): void { @@ -191,7 +193,7 @@ export class ActionBase { // add newline if there isn't one already // otherwise we'll just overwrite it when we render - if (output && std && output[output.length - 1] !== '\n') { + if (output && std && output.at(-1) !== '\n') { this._write(std, '\n') } } catch (error) { @@ -202,14 +204,19 @@ export class ActionBase { // write to the real stdout/stderr protected _write(std: 'stdout' | 'stderr', s: string | string[]): void { switch (std) { - case 'stdout': + case 'stdout': { this.stdmockOrigs.stdout.apply(stdout, castArray(s) as [string]) break - case 'stderr': + } + + case 'stderr': { this.stdmockOrigs.stderr.apply(stderr, castArray(s) as [string]) break - default: + } + + default: { throw new Error(`invalid std: ${std}`) } + } } } diff --git a/src/cli-ux/action/simple.ts b/src/cli-ux/action/simple.ts index c576011df..30db51f2f 100644 --- a/src/cli-ux/action/simple.ts +++ b/src/cli-ux/action/simple.ts @@ -4,7 +4,7 @@ export default class SimpleAction extends ActionBase { public type: ActionType = 'simple' protected _start(): void { - const task = this.task + const {task} = this if (!task) return this._render(task.action, task.status) } @@ -14,27 +14,29 @@ export default class SimpleAction extends ActionBase { else this._flush() } - protected _resume(): void {} + protected _resume(): void { + // Not implemented + } protected _updateStatus(status: string, prevStatus?: string, newline = false): void { - const task = this.task + const {task, std} = this if (!task) return - if (task.active && !prevStatus) this._write(this.std, ` ${status}`) - else this._write(this.std, `${task.action}... ${status}`) + if (task.active && !prevStatus) this._write(std, ` ${status}`) + else this._write(std, `${task.action}... ${status}`) if (newline || !prevStatus) this._flush() } protected _stop(status: string): void { - const task = this.task + const {task} = this if (!task) return this._updateStatus(status, task.status, true) } private _render(action: string, status?: string) { - const task = this.task + const {task, std} = this if (!task) return if (task.active) this._flush() - this._write(this.std, status ? `${action}... ${status}` : `${action}...`) + this._write(std, status ? `${action}... ${status}` : `${action}...`) } private _flush() { diff --git a/src/cli-ux/action/spinner.ts b/src/cli-ux/action/spinner.ts index 05ca3e50d..4965c54f2 100644 --- a/src/cli-ux/action/spinner.ts +++ b/src/cli-ux/action/spinner.ts @@ -1,10 +1,10 @@ -import * as chalk from 'chalk' import * as supportsColor from 'supports-color' -const stripAnsi = require('strip-ansi') -const ansiStyles = require('ansi-styles') +import {ActionBase, ActionType} from './base' +import ansiStyles from 'ansi-styles' +import chalk from 'chalk' import {errtermwidth} from '../../screen' import spinners from './spinners' -import {ActionBase, ActionType} from './base' +import stripAnsi from 'strip-ansi' function color(s: string): string { if (!supportsColor) return s @@ -61,14 +61,14 @@ export default class SpinnerAction extends ActionBase { } private _render(icon?: string) { - const task = this.task + const {task, std, output} = this if (!task) return this._reset() this._flushStdout() const frame = icon === 'spinner' ? ` ${this._frame()}` : icon || '' const status = task.status ? ` ${task.status}` : '' this.output = `${task.action}...${frame}${status}\n` - this._write(this.std, this.output) + this._write(std, output!) } private _reset() { diff --git a/src/cli-ux/config.ts b/src/cli-ux/config.ts index 5734a5746..5718f9775 100644 --- a/src/cli-ux/config.ts +++ b/src/cli-ux/config.ts @@ -1,8 +1,8 @@ +import {ActionBase} from './action/base' import {PJSON} from '../interfaces/pjson' import {requireJson} from '../util' +import simple from './action/simple' import spinner from './action/spinner' -import simple from './action/spinner' -import {ActionBase} from './action/base' export type Levels = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' @@ -16,10 +16,10 @@ const g: any = global const globals = g.ux || (g.ux = {}) const actionType = ( - Boolean(process.stderr.isTTY) && - !process.env.CI && - !['dumb', 'emacs-color'].includes(process.env.TERM!) && - 'spinner' + Boolean(process.stderr.isTTY) + && !process.env.CI + && !['dumb', 'emacs-color'].includes(process.env.TERM!) + && 'spinner' ) || 'simple' const Action = actionType === 'spinner' ? spinner : simple diff --git a/src/cli-ux/flush.ts b/src/cli-ux/flush.ts index 148975efb..c6a0c152d 100644 --- a/src/cli-ux/flush.ts +++ b/src/cli-ux/flush.ts @@ -17,9 +17,7 @@ async function _flush() { }) const flushed = stdout.write('') - if (flushed) { - return Promise.resolve() - } + if (flushed) return return p } diff --git a/src/cli-ux/index.ts b/src/cli-ux/index.ts index a882f9258..1b7811f7a 100644 --- a/src/cli-ux/index.ts +++ b/src/cli-ux/index.ts @@ -1,16 +1,14 @@ + import * as Errors from '../errors' -import * as util from 'util' -import * as chalk from 'chalk' -import {ActionBase} from './action/base' -import {config, Config} from './config' -import {ExitError} from './exit' -import {IPromptOptions} from './prompt' import * as styled from './styled' -import {Table} from './styled' import * as uxPrompt from './prompt' -import uxWait from './wait' -import {stdout} from './stream' +import {Config, config} from './config' +import {ActionBase} from './action/base' import {flush as _flush} from './flush' +import chalk from 'chalk' +import {stdout} from './stream' +import {format as utilFormat} from 'node:util' +import uxWait from './wait' const hyperlinker = require('hyperlinker') @@ -36,7 +34,6 @@ export class ux { return config.action } - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types public static styledObject(obj: any, keys?: string[]): void { this.info(styled.styledObject(obj, keys)) } @@ -71,18 +68,18 @@ export class ux { public static trace(format: string, ...args: string[]): void { if (this.config.outputLevel === 'trace') { - stdout.write(util.format(format, ...args) + '\n') + stdout.write(utilFormat(format, ...args) + '\n') } } public static debug(format: string, ...args: string[]): void { if (['trace', 'debug'].includes(this.config.outputLevel)) { - stdout.write(util.format(format, ...args) + '\n') + stdout.write(utilFormat(format, ...args) + '\n') } } public static info(format: string, ...args: string[]): void { - stdout.write(util.format(format, ...args) + '\n') + stdout.write(utilFormat(format, ...args) + '\n') } public static log(format?: string, ...args: string[]): void { @@ -113,45 +110,40 @@ export class ux { } } -const action = ux.action -const annotation = ux.annotation -const anykey = ux.anykey -const confirm = ux.confirm -const debug = ux.debug -const done = ux.done -const error = Errors.error -const exit = Errors.exit -const flush = ux.flush -const info = ux.info -const log = ux.log -const progress = ux.progress -const prompt = ux.prompt -const styledHeader = ux.styledHeader -const styledJSON = ux.styledJSON -const styledObject = ux.styledObject -const table = ux.table -const trace = ux.trace -const tree = ux.tree -const url = ux.url -const wait = ux.wait -const warn = Errors.warn +const { + action, + annotation, + anykey, + confirm, + debug, + done, + flush, + info, + log, + progress, + prompt, + styledHeader, + styledJSON, + styledObject, + table, + trace, + tree, + url, + wait, +} = ux +const {error, exit, warn} = Errors export { action, - ActionBase, annotation, anykey, - config, - Config, confirm, debug, done, error, exit, - ExitError, flush, info, - IPromptOptions, log, progress, prompt, @@ -159,7 +151,6 @@ export { styledJSON, styledObject, table, - Table, trace, tree, url, @@ -182,3 +173,9 @@ const uxListener = process.listeners('exit').find(fn => fn.name === uxProcessExi if (!uxListener) { process.once('exit', uxProcessExitHandler) } + +export {ExitError} from './exit' +export {IPromptOptions} from './prompt' +export {Table} from './styled' +export {ActionBase} from './action/base' +export {config, Config} from './config' diff --git a/src/cli-ux/list.ts b/src/cli-ux/list.ts index 4a0306b36..46773cb7d 100644 --- a/src/cli-ux/list.ts +++ b/src/cli-ux/list.ts @@ -1,5 +1,5 @@ -import {stdtermwidth} from '../screen' import {maxBy} from '../util' +import {stdtermwidth} from '../screen' const wordwrap = require('wordwrap') function linewrap(length: number, s: string): string { diff --git a/src/cli-ux/prompt.ts b/src/cli-ux/prompt.ts index a3a8c0d11..9af137f84 100644 --- a/src/cli-ux/prompt.ts +++ b/src/cli-ux/prompt.ts @@ -1,7 +1,6 @@ import * as Errors from '../errors' -import config from './config' - -import * as chalk from 'chalk' +import chalk from 'chalk' +import {config} from './config' import {stderr} from './stream' export interface IPromptOptions { @@ -77,8 +76,8 @@ async function single(options: IPromptConfig): Promise { function replacePrompt(prompt: string) { const ansiEscapes = require('ansi-escapes') - stderr.write(ansiEscapes.cursorHide + ansiEscapes.cursorUp(1) + ansiEscapes.cursorLeft + prompt + - ansiEscapes.cursorDown(1) + ansiEscapes.cursorLeft + ansiEscapes.cursorShow) + stderr.write(ansiEscapes.cursorHide + ansiEscapes.cursorUp(1) + ansiEscapes.cursorLeft + prompt + + ansiEscapes.cursorDown(1) + ansiEscapes.cursorLeft + ansiEscapes.cursorShow) } async function _prompt(name: string, inputOptions: Partial = {}): Promise { @@ -95,11 +94,15 @@ async function _prompt(name: string, inputOptions: Partial = {}) const passwordPrompt = require('password-prompt') switch (options.type) { - case 'normal': + case 'normal': { return normal(options) - case 'single': + } + + case 'single': { return single(options) - case 'mask': + } + + case 'mask': { return passwordPrompt(options.prompt, { method: options.type, required: options.required, @@ -108,15 +111,20 @@ async function _prompt(name: string, inputOptions: Partial = {}) replacePrompt(getPrompt(name, 'hide', inputOptions.default)) return value }) - case 'hide': + } + + case 'hide': { return passwordPrompt(options.prompt, { method: options.type, required: options.required, default: options.default, }) - default: + } + + default: { throw new Error(`unexpected type ${options.type}`) } + } } /** @@ -126,9 +134,7 @@ async function _prompt(name: string, inputOptions: Partial = {}) * @returns Promise */ export async function prompt(name: string, options: IPromptOptions = {}): Promise { - return config.action.pauseAsync(() => { - return _prompt(name, options) - }, chalk.cyan('?')) + return config.action.pauseAsync(() => _prompt(name, options), chalk.cyan('?')) } /** @@ -139,7 +145,8 @@ export async function prompt(name: string, options: IPromptOptions = {}): Promis export function confirm(message: string): Promise { return config.action.pauseAsync(async () => { const confirm = async (): Promise => { - const response = (await _prompt(message)).toLowerCase() + const raw = await _prompt(message) + const response = raw.toLowerCase() if (['n', 'no'].includes(response)) return false if (['y', 'yes'].includes(response)) return true return confirm() @@ -157,9 +164,9 @@ export function confirm(message: string): Promise { export async function anykey(message?: string): Promise { const tty = Boolean(process.stdin.setRawMode) if (!message) { - message = tty ? - `Press any key to continue or ${chalk.yellow('q')} to exit` : - `Press enter to continue or ${chalk.yellow('q')} to exit` + message = tty + ? `Press any key to continue or ${chalk.yellow('q')} to exit` + : `Press enter to continue or ${chalk.yellow('q')} to exit` } const char = await prompt(message, {type: 'single', required: false}) diff --git a/src/cli-ux/styled/index.ts b/src/cli-ux/styled/index.ts index 291c51575..93ce7faa2 100644 --- a/src/cli-ux/styled/index.ts +++ b/src/cli-ux/styled/index.ts @@ -1,13 +1,6 @@ -import styledJSON from './json' -import styledObject from './object' -import * as Table from './table' -import tree from './tree' -import progress from './progress' -export { - styledJSON, - styledObject, - Table, - tree, - progress, -} +export * as Table from './table' +export {default as progress} from './progress' +export {default as styledJSON} from './json' +export {default as styledObject} from './object' +export {default as tree} from './tree' diff --git a/src/cli-ux/styled/json.ts b/src/cli-ux/styled/json.ts index 6da73b23c..c7db532d0 100644 --- a/src/cli-ux/styled/json.ts +++ b/src/cli-ux/styled/json.ts @@ -1,4 +1,4 @@ -import * as chalk from 'chalk' +import chalk from 'chalk' import {ux} from '../../index' diff --git a/src/cli-ux/styled/object.ts b/src/cli-ux/styled/object.ts index fd872ef79..96345c016 100644 --- a/src/cli-ux/styled/object.ts +++ b/src/cli-ux/styled/object.ts @@ -1,7 +1,6 @@ -import * as chalk from 'chalk' -import * as util from 'util' +import chalk from 'chalk' +import {inspect} from 'node:util' -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export default function styledObject(obj: any, keys?: string[]): string { const output: string[] = [] const keyLengths = Object.keys(obj).map(key => key.toString().length) @@ -10,16 +9,14 @@ export default function styledObject(obj: any, keys?: string[]): string { if (typeof obj === 'string' || typeof obj === 'number') return obj if (typeof obj === 'object') { return Object.keys(obj) - .map(k => k + ': ' + util.inspect(obj[k])) + .map(k => k + ': ' + inspect(obj[k])) .join(', ') } - return util.inspect(obj) + return inspect(obj) } - const logKeyValue = (key: string, value: any): string => { - return `${chalk.blue(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value) - } + const logKeyValue = (key: string, value: any): string => `${chalk.blue(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value) for (const key of keys || Object.keys(obj).sort()) { const value = obj[key] diff --git a/src/cli-ux/styled/table.ts b/src/cli-ux/styled/table.ts index 04f8b0cf2..5d173d268 100644 --- a/src/cli-ux/styled/table.ts +++ b/src/cli-ux/styled/table.ts @@ -1,15 +1,14 @@ -import * as Interfaces from '../../interfaces' import * as F from '../../flags' -import {stdtermwidth} from '../../screen' -import * as chalk from 'chalk' +import * as Interfaces from '../../interfaces' import {capitalize, sumBy} from '../../util' +import chalk from 'chalk' +import {inspect} from 'node:util' +import {orderBy} from 'natural-orderby' import {safeDump} from 'js-yaml' -import {inspect} from 'util' +import sliceAnsi from 'slice-ansi' import {stdout} from '../stream' - -const sw = require('string-width') -const {orderBy} = require('natural-orderby') -const sliceAnsi = require('slice-ansi') +import {stdtermwidth} from '../../screen' +import sw from 'string-width' class Table> { options: table.Options & { printLine(s: any): any } @@ -23,7 +22,7 @@ class Table> { const extended = col.extended ?? false // turn null and undefined into empty strings by default const get = col.get ?? ((row: any) => row[key] ?? '') - const header = typeof col.header === 'string' ? col.header : capitalize(key.replace(/_/g, ' ')) + const header = typeof col.header === 'string' ? col.header : capitalize(key.replaceAll('_', ' ')) const minWidth = Math.max(col.minWidth ?? 0, sw(header) + 1) return { @@ -66,7 +65,6 @@ class Table> { // filter rows if (this.options.filter) { - /* eslint-disable-next-line prefer-const */ let [header, regex] = this.options.filter!.split('=') const isNot = header[0] === '-' if (isNot) header = header.slice(1) @@ -84,9 +82,7 @@ class Table> { if (this.options.sort) { const sorters = this.options.sort!.split(',') const sortHeaders = sorters.map(k => k[0] === '-' ? k.slice(1) : k) - const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map(c => { - return ((v: any) => v[c.key]) - }) + const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map(c => ((v: any) => v[c.key])) const sortKeysOrder = sorters.map(k => k[0] === '-' ? 'desc' : 'asc') rows = orderBy(rows, sortKeys, sortKeysOrder) } @@ -103,18 +99,25 @@ class Table> { this.data = rows switch (this.options.output) { - case 'csv': + case 'csv': { this.outputCSV() break - case 'json': + } + + case 'json': { this.outputJSON() break - case 'yaml': + } + + case 'yaml': { this.outputYAML() break - default: + } + + default: { this.outputTable() } + } } private findColumnFromHeader(header: string): (table.Column & { key: string; width?: number; maxWidth?: number }) | undefined { @@ -141,15 +144,10 @@ class Table> { private resolveColumnsToObjectArray() { const {data, columns} = this - return data.map((d: any) => { - // eslint-disable-next-line unicorn/prefer-object-from-entries - return columns.reduce((obj, col) => { - return { - ...obj, - [col.key]: d[col.key] ?? '', - } - }, {}) - }) + return data.map((d: any) => + + Object.fromEntries(columns.map(col => [col.key, d[col.key] ?? ''])), + ) } private outputJSON() { @@ -338,7 +336,7 @@ export namespace table { export function flags(): IFlags export function flags(opts: { except: Z | Z[] }): ExcludeFlags export function flags(opts: { only: K | K[] }): IncludeFlags - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + export function flags(opts?: any): any { if (opts) { const f = {} @@ -380,12 +378,10 @@ export namespace table { } } -const getWidestColumnWith = (data: any[], columnKey: string): number => { - return data.reduce((previous, current) => { - const d = current[columnKey] - // convert multi-line cell to single longest line - // for width calculations - const manyLines = (d as string).split('\n') - return Math.max(previous, manyLines.length > 1 ? Math.max(...manyLines.map((r: string) => sw(r))) : sw(d)) - }, 0) -} +const getWidestColumnWith = (data: any[], columnKey: string): number => data.reduce((previous, current) => { + const d = current[columnKey] + // convert multi-line cell to single longest line + // for width calculations + const manyLines = (d as string).split('\n') + return Math.max(previous, manyLines.length > 1 ? Math.max(...manyLines.map((r: string) => sw(r))) : sw(d)) +}, 0) diff --git a/src/cli-ux/wait.ts b/src/cli-ux/wait.ts index 3d5560c4b..7514385ff 100644 --- a/src/cli-ux/wait.ts +++ b/src/cli-ux/wait.ts @@ -1,5 +1,3 @@ -export default (ms = 1000): Promise => { - return new Promise(resolve => { - setTimeout(resolve, ms) - }) -} +export default (ms = 1000): Promise => new Promise(resolve => { + setTimeout(resolve, ms) +}) diff --git a/src/command.ts b/src/command.ts index 1bccd6ef6..7a54e2136 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,33 +1,34 @@ -import {fileURLToPath} from 'url' -import * as chalk from 'chalk' -import {format, inspect} from 'util' -import {ux} from './cli-ux' -import {Config} from './config' + import * as Errors from './errors' -import {PrettyPrintableError} from './errors' import * as Parser from './parser' import { + ArgInput, + ArgOutput, + ArgProps, BooleanFlagProps, - CompletableFlag, Deprecation, - Arg as IArg, - ArgInput, FlagInput, FlagOutput, + Arg as IArg, + Flag as IFlag, Input, - ArgProps, OptionFlagProps, ParserOutput, - ArgOutput, } from './interfaces/parser' -import {formatCommandDeprecationWarning, formatFlagDeprecationWarning, toConfiguredId, normalizeArgv} from './help/util' -import {Plugin} from './interfaces/plugin' -import {LoadOptions} from './interfaces/config' +import {format, inspect} from 'node:util' +import {formatCommandDeprecationWarning, formatFlagDeprecationWarning, normalizeArgv, toConfiguredId} from './help/util' +import {requireJson, uniq} from './util' +import {stderr, stdout} from './cli-ux/stream' import {CommandError} from './interfaces/errors' -import {boolean} from './flags' -import {requireJson} from './util' +import {Config} from './config' +import {LoadOptions} from './interfaces/config' import {PJSON} from './interfaces' -import {stdout, stderr} from './cli-ux/stream' +import {Plugin} from './interfaces/plugin' +import {PrettyPrintableError} from './errors' +import {boolean} from './flags' +import chalk from 'chalk' +import {fileURLToPath} from 'node:url' +import {ux} from './cli-ux' const pjson = requireJson(__dirname, '..', 'package.json') @@ -63,7 +64,7 @@ export abstract class Command { * The tweet-sized description for your class, used in a parent-commands * sub-command listing and as the header for the command help. */ - public static summary?: string; + public static summary?: string /** * A full description of how to use the command. @@ -76,9 +77,9 @@ export abstract class Command { public static hidden: boolean /** Mark the command as a given state (e.g. beta or deprecated) in help */ - public static state?: 'beta' | 'deprecated' | string; + public static state?: 'beta' | 'deprecated' | string - public static deprecationOptions?: Deprecation; + public static deprecationOptions?: Deprecation /** * Emit deprecation warning when a command alias is used @@ -103,9 +104,9 @@ export abstract class Command { public static plugin: Plugin | undefined - public static readonly pluginName?: string; - public static readonly pluginType?: string; - public static readonly pluginAlias?: string; + public static readonly pluginName?: string + public static readonly pluginType?: string + public static readonly pluginAlias?: string /** * An array of examples to show at the end of the command's help. @@ -190,6 +191,7 @@ export abstract class Command { } static set baseFlags(flags: FlagInput) { + // eslint-disable-next-line prefer-object-spread this._baseFlags = Object.assign({}, this.baseFlags, flags) this.flags = {} // force the flags setter to run } @@ -202,6 +204,7 @@ export abstract class Command { } public static set flags(flags: FlagInput) { + // eslint-disable-next-line prefer-object-spread this._flags = Object.assign({}, this._flags ?? {}, this.baseFlags, flags) } @@ -214,7 +217,9 @@ export abstract class Command { try { this.debug = require('debug')(this.id ? `${this.config.bin}:${this.id}` : this.config.bin) } catch { - this.debug = () => {} + this.debug = () => { + // noop + } } } @@ -315,8 +320,10 @@ export abstract class Command { } const deprecateAliases = flagDef?.deprecateAliases - const aliases = (flagDef?.aliases ?? []).map(a => a.length === 1 ? `-${a}` : `--${a}`) - if (deprecateAliases && aliases.length > 0) { + if (deprecateAliases) { + const aliases = uniq([...flagDef?.aliases ?? [], ...flagDef?.charAliases ?? []]).map(a => a.length === 1 ? `-${a}` : `--${a}`) + if (aliases.length === 0) return + const foundAliases = aliases.filter(alias => this.argv.some(a => a.startsWith(alias))) for (const alias of foundAliases) { let preferredUsage = `--${flagDef?.name}` @@ -373,7 +380,7 @@ export abstract class Command { protected async finally(_: Error | undefined): Promise { try { - const config = Errors.config + const {config} = Errors if (config.errorLogger) await config.errorLogger.flush() } catch (error: any) { console.error(error) @@ -435,9 +442,11 @@ export namespace Command { hasDynamicHelp?: boolean; permutations?: string[] aliasPermutations?: string[]; + isESM?: boolean; + relativePath?: string[]; } - export type Flag = CompletableFlag + export type Flag = IFlag export namespace Flag { export type Cached = Omit & (BooleanFlagProps | OptionFlagProps) diff --git a/src/config/config.ts b/src/config/config.ts index 6ed2583a7..83428679a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,23 +1,23 @@ -import {fileURLToPath, URL} from 'node:url' -import {format} from 'node:util' -import {userInfo as osUserInfo, arch, platform, homedir, tmpdir, type, release} from 'node:os' -import {sep, join} from 'node:path' - import * as ejs from 'ejs' +import {ArchTypes, Config as IConfig, LoadOptions, PlatformTypes, VersionDetails} from '../interfaces/config' +import {Arg, OptionFlag} from '../interfaces/parser' import {CLIError, error, exit, warn} from '../errors' -import {Options, Plugin as IPlugin} from '../interfaces/plugin' -import {Config as IConfig, ArchTypes, PlatformTypes, LoadOptions, VersionDetails} from '../interfaces/config' +import {Debug, collectUsableIds, getCommandIdPermutations} from './util' import {Hook, Hooks, PJSON, Topic} from '../interfaces' -import {Debug, compact, collectUsableIds, getCommandIdPermutations} from './util' -import {ensureArgObject, isProd, requireJson} from '../util' -import ModuleLoader from '../module-loader' -import {getHelpFlagAdditions} from '../help' +import {Plugin as IPlugin, Options} from '../interfaces/plugin' +import {URL, fileURLToPath} from 'node:url' +import {arch, userInfo as osUserInfo, release, tmpdir, type} from 'node:os' +import {compact, ensureArgObject, getHomeDir, getPlatform, isProd, requireJson} from '../util' +import {join, sep} from 'node:path' + import {Command} from '../command' -import {CompletableOptionFlag, Arg} from '../interfaces/parser' -import {stdout} from '../cli-ux/stream' -import Performance from '../performance' -import {settings} from '../settings' +import {Performance} from '../performance' import PluginLoader from './plugin-loader' +import {format} from 'node:util' +import {getHelpFlagAdditions} from '../help' +import {loadWithData} from '../module-loader' +import {settings} from '../settings' +import {stdout} from '../cli-ux/stream' // eslint-disable-next-line new-cap const debug = Debug() @@ -95,8 +95,8 @@ export class Config implements IConfig { public valid!: boolean public version!: string public windows!: boolean - public binAliases?: string[]; - public nsisCustomization?:string; + public binAliases?: string[] + public nsisCustomization?:string protected warned = false @@ -155,6 +155,7 @@ export class Config implements IConfig { // eslint-disable-next-line complexity public async load(): Promise { settings.performanceEnabled = (settings.performanceEnabled === undefined ? this.options.enablePerf : settings.performanceEnabled) ?? false + const marker = Performance.mark('config.load') this.pluginLoader = new PluginLoader({root: this.options.root, plugins: this.options.plugins}) Config._rootPlugin = await this.pluginLoader.loadRoot() @@ -170,7 +171,7 @@ export class Config implements IConfig { this.valid = Config._rootPlugin.valid this.arch = (arch() === 'ia32' ? 'x86' : arch() as any) - this.platform = WSL ? 'wsl' : platform() as any + this.platform = WSL ? 'wsl' : getPlatform() this.windows = this.platform === 'win32' this.bin = this.pjson.oclif.bin || this.name this.binAliases = this.pjson.oclif.binAliases @@ -184,7 +185,7 @@ export class Config implements IConfig { this.shell = this._shell() this.debug = this._debug() - this.home = process.env.HOME || (this.windows && this.windowsHome()) || homedir() || tmpdir() + this.home = process.env.HOME || (this.windows && this.windowsHome()) || getHomeDir() || tmpdir() this.cacheDir = this.scopedEnvVar('CACHE_DIR') || this.macosCacheDir() || this.dir('cache') this.configDir = this.scopedEnvVar('CONFIG_DIR') || this.dir('config') this.dataDir = this.scopedEnvVar('DATA_DIR') || this.dir('data') @@ -217,8 +218,6 @@ export class Config implements IConfig { }, } - const marker = Performance.mark('config.load') - await this.loadPluginsAndCommands() debug('config done') @@ -232,7 +231,7 @@ export class Config implements IConfig { } async loadPluginsAndCommands(opts?: {force: boolean}): Promise { - const marker = Performance.mark('config.loadPluginsAndCommands') + const pluginsMarker = Performance.mark('config.loadAllPlugins') const {plugins, errors} = await this.pluginLoader.loadChildren({ devPlugins: this.options.devPlugins, userPlugins: this.options.userPlugins, @@ -242,16 +241,19 @@ export class Config implements IConfig { }) this.plugins = plugins + pluginsMarker?.stop() + + const commandsMarker = Performance.mark('config.loadAllCommands') for (const plugin of this.plugins.values()) { this.loadCommands(plugin) this.loadTopics(plugin) } + commandsMarker?.stop() + for (const error of errors) { this.warn(error) } - - marker?.stop() } public async runHook( @@ -311,13 +313,12 @@ export class Config implements IConfig { const marker = Performance.mark(`config.runHook#${p.name}(${hook})`) try { /* eslint-disable no-await-in-loop */ - const {isESM, module, filePath} = await ModuleLoader.loadWithData(p, hook) - + const {isESM, module, filePath} = await loadWithData(p, join(p.root, hook)) debug('start', isESM ? '(import)' : '(require)', filePath) - const result = timeout ? - await withTimeout(timeout, search(module).call(context, {...opts as any, config: this})) : - await search(module).call(context, {...opts as any, config: this}) + const result = timeout + ? await withTimeout(timeout, search(module).call(context, {...opts as any, config: this})) + : await search(module).call(context, {...opts as any, config: this}) final.successes.push({plugin: p, result}) if (p.name === '@oclif/plugin-legacy' && event === 'init') { @@ -354,9 +355,9 @@ export class Config implements IConfig { let c = cachedCommand ?? this.findCommand(id) if (!c) { const matches = this.flexibleTaxonomy ? this.findMatches(id, argv) : [] - const hookResult = this.flexibleTaxonomy && matches.length > 0 ? - await this.runHook('command_incomplete', {id, argv, matches}) : - await this.runHook('command_not_found', {id, argv}) + const hookResult = this.flexibleTaxonomy && matches.length > 0 + ? await this.runHook('command_incomplete', {id, argv, matches}) + : await this.runHook('command_not_found', {id, argv}) if (hookResult.successes[0]) return hookResult.successes[0].result as T if (hookResult.failures[0]) throw hookResult.failures[0].error @@ -413,7 +414,7 @@ export class Config implements IConfig { */ public scopedEnvVarKey(k: string): string { return [this.bin, k] - .map(p => p.replace(/@/g, '').replace(/[/-]/g, '_')) + .map(p => p.replaceAll('@', '').replaceAll(/[/-]/g, '_')) .join('_') .toUpperCase() } @@ -424,8 +425,8 @@ export class Config implements IConfig { * @returns {string[]} e.g. ['SF_DEBUG', 'SFDX_DEBUG'] */ public scopedEnvVarKeys(k: string): string[] { - return [this.bin, ...this.binAliases ?? []].filter(alias => Boolean(alias)).map(alias => - [alias.replace(/@/g, '').replace(/[/-]/g, '_'), k].join('_').toUpperCase()) + return [this.bin, ...this.binAliases ?? []].filter(Boolean).map(alias => + [alias.replaceAll('@', '').replaceAll(/[/-]/g, '_'), k].join('_').toUpperCase()) } public findCommand(id: string, opts: { must: true }): Command.Loadable @@ -464,13 +465,11 @@ export class Config implements IConfig { * @returns string[] */ public findMatches(partialCmdId: string, argv: string[]): Command.Loadable[] { - const flags = argv.filter(arg => !getHelpFlagAdditions(this).includes(arg) && arg.startsWith('-')).map(a => a.replace(/-/g, '')) + const flags = argv.filter(arg => !getHelpFlagAdditions(this).includes(arg) && arg.startsWith('-')).map(a => a.replaceAll('-', '')) const possibleMatches = [...this.commandPermutations.get(partialCmdId)].map(k => this._commands.get(k)!) const matches = possibleMatches.filter(command => { - const cmdFlags = Object.entries(command.flags).flatMap(([flag, def]) => { - return def.char ? [def.char, flag] : [flag] - }) as string[] + const cmdFlags = Object.entries(command.flags).flatMap(([flag, def]) => def.char ? [def.char, flag] : [flag]) as string[] // A command is a match if the provided flags belong to the full command return flags.every(f => cmdFlags.includes(f)) @@ -539,7 +538,7 @@ export class Config implements IConfig { } public s3Url(key: string): string { - const host = this.pjson.oclif.update.s3.host + const {host} = this.pjson.oclif.update.s3 if (!host) throw new Error('no s3 host is set') const url = new URL(host) url.pathname = join(url.pathname, key) @@ -551,9 +550,9 @@ export class Config implements IConfig { } protected dir(category: 'cache' | 'data' | 'config'): string { - const base = process.env[`XDG_${category.toUpperCase()}_HOME`] || - (this.windows && process.env.LOCALAPPDATA) || - join(this.home, category === 'data' ? '.local/share' : '.' + category) + const base = process.env[`XDG_${category.toUpperCase()}_HOME`] + || (this.windows && process.env.LOCALAPPDATA) + || join(this.home, category === 'data' ? '.local/share' : '.' + category) return join(base, this.dirname) } @@ -575,7 +574,7 @@ export class Config implements IConfig { protected _shell(): string { let shellPath - const COMSPEC = process.env.COMSPEC + const {COMSPEC} = process.env const SHELL = process.env.SHELL ?? osUserInfo().shell?.split(sep)?.pop() if (SHELL) { shellPath = SHELL.split('/') @@ -585,7 +584,7 @@ export class Config implements IConfig { shellPath = ['unknown'] } - return shellPath[shellPath.length - 1] + return shellPath.at(-1) ?? 'unknown' } protected _debug(): number { @@ -668,8 +667,13 @@ export class Config implements IConfig { this._commands.set(command.id, command) } + // v3 moved command id permutations to the manifest, but some plugins may not have + // the new manifest yet. For those, we need to calculate the permutations here. + const permutations = this.flexibleTaxonomy && command.permutations === undefined + ? getCommandIdPermutations(command.id) + : command.permutations ?? [command.id] // set every permutation - for (const permutation of command.permutations ?? [command.id]) { + for (const permutation of permutations) { this.commandPermutations.add(permutation, command.id) } @@ -683,7 +687,14 @@ export class Config implements IConfig { } // set every permutation of the aliases - for (const permutation of command.aliasPermutations ?? [alias]) { + + // v3 moved command alias permutations to the manifest, but some plugins may not have + // the new manifest yet. For those, we need to calculate the permutations here. + const aliasPermutations = this.flexibleTaxonomy && command.aliasPermutations === undefined + ? getCommandIdPermutations(alias) + : command.permutations ?? [alias] + // set every permutation + for (const permutation of aliasPermutations) { this.commandPermutations.add(permutation, command.id) } } @@ -802,7 +813,7 @@ export class Config implements IConfig { } // when no manifest exists, the default is calculated. This may throw, so we need to catch it -const defaultFlagToCached = async (flag: CompletableOptionFlag, respectNoCacheDefault: boolean) => { +const defaultFlagToCached = async (flag: OptionFlag, respectNoCacheDefault: boolean) => { if (respectNoCacheDefault && flag.noCacheDefault) return // Prefer the defaultHelp function (returns a friendly string for complex types) if (typeof flag.defaultHelp === 'function') { @@ -866,6 +877,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin, respectNoCach deprecated: flag.deprecated, deprecateAliases: c.deprecateAliases, aliases: flag.aliases, + charAliases: flag.charAliases, delimiter: flag.delimiter, noCacheDefault: flag.noCacheDefault, } @@ -890,6 +902,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin, respectNoCach deprecated: flag.deprecated, deprecateAliases: c.deprecateAliases, aliases: flag.aliases, + charAliases: flag.charAliases, delimiter: flag.delimiter, noCacheDefault: flag.noCacheDefault, } diff --git a/src/config/plugin-loader.ts b/src/config/plugin-loader.ts index 4da7520c2..7965a08bb 100644 --- a/src/config/plugin-loader.ts +++ b/src/config/plugin-loader.ts @@ -1,10 +1,10 @@ -import * as path from 'path' - -import {Options, Plugin as IPlugin} from '../interfaces/plugin' import * as Plugin from './plugin' -import {loadJSON, Debug} from './util' -import {isProd} from '../util' -import Performance from '../performance' +import {Plugin as IPlugin, Options} from '../interfaces/plugin' +import {isProd, readJson} from '../util' +import {Debug} from './util' +import {PJSON} from '../interfaces' +import {Performance} from '../performance' +import {join} from 'node:path' // eslint-disable-next-line new-cap const debug = Debug() @@ -42,8 +42,18 @@ export default class PluginLoader { const plugins = [...this.plugins.values()] rootPlugin = plugins.find(p => p.root === this.options.root) ?? plugins[0] } else { + const marker = Performance.mark('plugin.load#root') rootPlugin = new Plugin.Plugin({root: this.options.root}) await rootPlugin.load() + marker?.addDetails({ + hasManifest: rootPlugin.hasManifest ?? false, + commandCount: rootPlugin.commands.length, + topicCount: rootPlugin.topics.length, + type: rootPlugin.type, + usesMain: Boolean(rootPlugin.pjson.main), + name: rootPlugin.name, + }) + marker?.stop() } this.plugins.set(rootPlugin.name, rootPlugin) @@ -71,7 +81,7 @@ export default class PluginLoader { // do not load oclif.devPlugins in production if (isProd()) return try { - const devPlugins = opts.rootPlugin.pjson.oclif.devPlugins + const {devPlugins} = opts.rootPlugin.pjson.oclif if (devPlugins) await this.loadPlugins(opts.rootPlugin.root, 'dev', devPlugins) } catch (error: any) { process.emitWarning(error) @@ -82,9 +92,9 @@ export default class PluginLoader { private async loadUserPlugins(opts: LoadOpts): Promise { if (opts.userPlugins !== false) { try { - const userPJSONPath = path.join(opts.dataDir, 'package.json') + const userPJSONPath = join(opts.dataDir, 'package.json') debug('reading user plugins pjson %s', userPJSONPath) - const pjson = await loadJSON(userPJSONPath) + const pjson = await readJson(userPJSONPath) if (!pjson.oclif) pjson.oclif = {schema: 1} if (!pjson.oclif.plugins) pjson.oclif.plugins = [] await this.loadPlugins(userPJSONPath, 'user', pjson.oclif.plugins.filter((p: any) => p.type === 'user')) @@ -112,6 +122,10 @@ export default class PluginLoader { opts.root = plugin.root || opts.root } + if (parent) { + opts.parent = parent + } + if (this.plugins.has(name)) return const pluginMarker = Performance.mark(`plugin.load#${name}`) const instance = new Plugin.Plugin(opts) diff --git a/src/config/plugin.ts b/src/config/plugin.ts index 0d3f988b2..8f623db75 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -1,20 +1,24 @@ import {CLIError, error} from '../errors' -import * as globby from 'globby' -import * as path from 'path' -import {inspect} from 'util' - +import { + Debug, + flatMap, + getCommandIdPermutations, + mapValues, + resolvePackage, +} from './util' import {Plugin as IPlugin, PluginOptions} from '../interfaces/plugin' -import {toCached} from './config' -import {Debug, getCommandIdPermutations} from './util' +import {compact, exists, isProd, readJson, requireJson} from '../util' +import {dirname, join, parse, relative, sep} from 'node:path' +import {loadWithData, loadWithDataFromManifest} from '../module-loader' +import {Command} from '../command' import {Manifest} from '../interfaces/manifest' import {PJSON} from '../interfaces/pjson' +import {Performance} from '../performance' import {Topic} from '../interfaces/topic' +import {inspect} from 'node:util' +import {sync} from 'globby' +import {toCached} from './config' import {tsPath} from './ts-node' -import {compact, exists, resolvePackage, flatMap, loadJSON, mapValues} from './util' -import {isProd, requireJson} from '../util' -import ModuleLoader from '../module-loader' -import {Command} from '../command' -import Performance from '../performance' const _pjson = requireJson(__dirname, '..', '..', 'package.json') @@ -33,9 +37,9 @@ function topicsToArray(input: any, base?: string): Topic[] { // essentially just "cd .." function * up(from: string) { - while (path.dirname(from) !== from) { + while (dirname(from) !== from) { yield from - from = path.dirname(from) + from = dirname(from) } yield from @@ -43,9 +47,9 @@ function * up(from: string) { async function findSourcesRoot(root: string) { for (const next of up(root)) { - const cur = path.join(next, 'package.json') + const cur = join(next, 'package.json') // eslint-disable-next-line no-await-in-loop - if (await exists(cur)) return path.dirname(cur) + if (await exists(cur)) return dirname(cur) } } @@ -64,18 +68,18 @@ async function findRootLegacy(name: string | undefined, root: string): Promise(join(next, 'package.json')) if (pkg.name === name) return next } catch {} } else { - cur = path.join(next, 'package.json') + cur = join(next, 'package.json') // eslint-disable-next-line no-await-in-loop - if (await exists(cur)) return path.dirname(cur) + if (await exists(cur)) return dirname(cur) } } } @@ -87,12 +91,21 @@ async function findRoot(name: string | undefined, root: string) { pkgPath = resolvePackage(name, {paths: [root]}) } catch {} - return pkgPath ? findSourcesRoot(path.dirname(pkgPath)) : findRootLegacy(name, root) + return pkgPath ? findSourcesRoot(dirname(pkgPath)) : findRootLegacy(name, root) } return findSourcesRoot(root) } +const cachedCommandCanBeUsed = (manifest: Manifest | undefined, id: string): boolean => + Boolean(manifest?.commands[id] && ('isESM' in manifest.commands[id] && 'relativePath' in manifest.commands[id])) + +const search = (cmd: any) => { + if (typeof cmd.run === 'function') return cmd + if (cmd.default && cmd.default.run) return cmd.default + return Object.values(cmd).find((cmd: any) => typeof cmd.run === 'function') +} + export class Plugin implements IPlugin { _base = `${_pjson.name}@${_pjson.version}` @@ -142,16 +155,20 @@ export class Plugin implements IPlugin { public async load(): Promise { this.type = this.options.type || 'core' this.tag = this.options.tag - const root = await findRoot(this.options.name, this.options.root) + if (this.options.parent) this.parent = this.options.parent as Plugin + // Linked plugins already have a root so there's no need to search for it. + // However there could be child plugins nested inside the linked plugin, in which + // case we still need to search for the child plugin's root. + const root = this.type === 'link' && !this.parent ? this.options.root : await findRoot(this.options.name, this.options.root) if (!root) throw new CLIError(`could not find package.json with ${inspect(this.options)}`) this.root = root this._debug('reading %s plugin %s', this.type, root) - this.pjson = await loadJSON(path.join(root, 'package.json')) + this.pjson = await readJson(join(root, 'package.json')) this.flexibleTaxonomy = this.options?.flexibleTaxonomy || this.pjson.oclif?.flexibleTaxonomy || false this.moduleType = this.pjson.type === 'module' ? 'module' : 'commonjs' this.name = this.pjson.name this.alias = this.options.name ?? this.pjson.name - const pjsonPath = path.join(root, 'package.json') + const pjsonPath = join(root, 'package.json') if (!this.name) throw new CLIError(`no name in ${pjsonPath}`) if (!isProd() && !this.pjson.files) this.warn(`files attribute must be specified in ${pjsonPath}`) // eslint-disable-next-line new-cap @@ -197,12 +214,12 @@ export class Plugin implements IPlugin { '**/*.+(js|cjs|mjs|ts|tsx)', '!**/*.+(d.ts|test.ts|test.js|spec.ts|spec.js)?(x)', ] - const ids = globby.sync(patterns, {cwd: this.commandsDir}) + const ids = sync(patterns, {cwd: this.commandsDir}) .map(file => { - const p = path.parse(file) + const p = parse(file) const topics = p.dir.split('/') const command = p.name !== 'index' && p.name - const id = [...topics, command].filter(f => f).join(':') + const id = [...topics, command].filter(Boolean).join(':') return id === '' ? '.' : id }) this._debug('found commands', ids) @@ -217,29 +234,28 @@ export class Plugin implements IPlugin { public async findCommand(id: string, opts: {must?: boolean} = {}): Promise { const marker = Performance.mark(`plugin.findCommand#${this.name}.${id}`, {id, plugin: this.name}) + const fetch = async () => { if (!this.commandsDir) return - const search = (cmd: any) => { - if (typeof cmd.run === 'function') return cmd - if (cmd.default && cmd.default.run) return cmd.default - return Object.values(cmd).find((cmd: any) => typeof cmd.run === 'function') - } - - let m + let module + let isESM: boolean | undefined + let filePath: string | undefined try { - const p = path.join(this.commandsDir ?? this.pjson.oclif.commands, ...id.split(':')) - const {isESM, module, filePath} = await ModuleLoader.loadWithData(this, p) + ({isESM, module, filePath} = cachedCommandCanBeUsed(this.manifest, id) + ? await loadWithDataFromManifest(this.manifest.commands[id], this.root) + : await loadWithData(this, join(this.commandsDir ?? this.pjson.oclif.commands, ...id.split(':')))) this._debug(isESM ? '(import)' : '(require)', filePath) - m = module } catch (error: any) { if (!opts.must && error.code === 'MODULE_NOT_FOUND') return throw error } - const cmd = search(m) + const cmd = search(module) if (!cmd) return cmd.id = id cmd.plugin = this + cmd.isESM = isESM + cmd.relativePath = relative(this.root, filePath || '').split(sep) return cmd } @@ -256,8 +272,8 @@ export class Plugin implements IPlugin { const readManifest = async (dotfile = false): Promise => { try { - const p = path.join(this.root, `${dotfile ? '.' : ''}oclif.manifest.json`) - const manifest: Manifest = await loadJSON(p) + const p = join(this.root, `${dotfile ? '.' : ''}oclif.manifest.json`) + const manifest = await readJson(p) if (!process.env.OCLIF_NEXT_VERSION && manifest.version.split('-')[0] !== this.version.split('-')[0]) { process.emitWarning(`Mismatched version in ${this.name} plugin manifest. Expected: ${this.version} Received: ${manifest.version}\nThis usually means you have an oclif.manifest.json file that should be deleted in development. This file should be automatically generated when publishing.`) } else { @@ -302,6 +318,7 @@ export class Plugin implements IPlugin { else throw this.addErrorScope(error, scope) } }))) + // eslint-disable-next-line unicorn/no-await-expression-member, unicorn/prefer-native-coercion-functions .filter((f): f is [string, Command.Cached] => Boolean(f)) .reduce((commands, [id, c]) => { commands[id] = c diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index 416cbf708..67093f80e 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -1,39 +1,40 @@ -import * as fs from 'fs' -import * as path from 'path' import * as TSNode from 'ts-node' - -import {TSConfig, Plugin} from '../interfaces' -import {settings} from '../settings' -import {isProd} from '../util' +import {Plugin, TSConfig} from '../interfaces' +import {isProd, readJsonSync} from '../util' +import {join, relative as pathRelative} from 'node:path' +import {Config} from './config' import {Debug} from './util' +import {existsSync} from 'node:fs' import {memoizedWarn} from '../errors' +import {settings} from '../settings' + // eslint-disable-next-line new-cap const debug = Debug('ts-node') -const TS_CONFIGS: Record = {} +export const TS_CONFIGS: Record = {} const REGISTERED = new Set() function loadTSConfig(root: string): TSConfig | undefined { if (TS_CONFIGS[root]) return TS_CONFIGS[root] - const tsconfigPath = path.join(root, 'tsconfig.json') + const tsconfigPath = join(root, 'tsconfig.json') let typescript: typeof import('typescript') | undefined try { typescript = require('typescript') } catch { try { - typescript = require(path.join(root, 'node_modules', 'typescript')) + typescript = require(join(root, 'node_modules', 'typescript')) } catch {} } - if (fs.existsSync(tsconfigPath) && typescript) { + if (existsSync(tsconfigPath) && typescript) { const tsconfig = typescript.parseConfigFileTextToJson( tsconfigPath, - fs.readFileSync(tsconfigPath, 'utf8'), + readJsonSync(tsconfigPath, false), ).config if (!tsconfig || !tsconfig.compilerOptions) { throw new Error( - `Could not read and parse tsconfig.json at ${tsconfigPath}, or it ` + - 'did not contain a "compilerOptions" section.') + `Could not read and parse tsconfig.json at ${tsconfigPath}, or it ` + + 'did not contain a "compilerOptions" section.') } TS_CONFIGS[root] = tsconfig @@ -51,19 +52,19 @@ function registerTSNode(root: string): TSConfig | undefined { const tsNode: typeof TSNode = require(tsNodePath) const typeRoots = [ - path.join(root, 'node_modules', '@types'), + join(root, 'node_modules', '@types'), ] const rootDirs: string[] = [] if (tsconfig.compilerOptions.rootDirs) { for (const r of tsconfig.compilerOptions.rootDirs) { - rootDirs.push(path.join(root, r)) + rootDirs.push(join(root, r)) } } else if (tsconfig.compilerOptions.rootDir) { - rootDirs.push(path.join(root, tsconfig.compilerOptions.rootDir)) + rootDirs.push(join(root, tsconfig.compilerOptions.rootDir)) } else { - rootDirs.push(path.join(root, 'src')) + rootDirs.push(join(root, 'src')) } const conf: TSNode.RegisterOptions = { @@ -111,7 +112,7 @@ export function tsPath(root: string, orig: string, plugin: Plugin): string export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined { if (!orig) return orig - orig = orig.startsWith(root) ? orig : path.join(root, orig) + orig = orig.startsWith(root) ? orig : join(root, orig) // NOTE: The order of these checks matter! @@ -120,22 +121,28 @@ export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): return orig } - // Skip ts-node registration for ESM plugins. - // The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time. - // See the following: - // - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228 - // - https://github.com/nodejs/node/issues/49432 - // - https://github.com/nodejs/node/pull/49407 - // - https://github.com/nodejs/node/issues/34049 - if (plugin?.moduleType === 'module') { - debug(`Skipping ts-node registration for ${root} because it's an ESM module`) + const isProduction = isProd() + /** + * Skip ts-node registration for ESM plugins. + * The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time. + * See the following: + * - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228 + * - https://github.com/nodejs/node/issues/49432 + * - https://github.com/nodejs/node/pull/49407 + * - https://github.com/nodejs/node/issues/34049 + * + * We still register ts-node for ESM plugins when NODE_ENV is "test" or "development" and root plugin is also ESM. + * In other words, this allows plugins to be auto-transpiled when developing locally using `bin/dev.js`. + */ + if ((isProduction || Config.rootPlugin?.moduleType === 'commonjs') && plugin?.moduleType === 'module') { + debug(`Skipping ts-node registration for ${root} because it's an ESM module (NODE_ENV: ${process.env.NODE_ENV}, root plugin module type: ${Config.rootPlugin?.moduleType})))`) if (plugin.type === 'link') memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`) return orig } - if (settings.tsnodeEnabled === undefined && isProd() && plugin?.type !== 'link') { + if (settings.tsnodeEnabled === undefined && isProduction && plugin?.type !== 'link') { debug(`Skipping ts-node registration for ${root} because NODE_ENV is NOT "test" or "development"`) return orig } @@ -147,17 +154,17 @@ export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): const rootDirPath = rootDir || (rootDirs || [])[0] if (!rootDirPath || !outDir) return orig // rewrite path from ./lib/foo to ./src/foo - const lib = path.join(root, outDir) // ./lib - const src = path.join(root, rootDirPath) // ./src - const relative = path.relative(lib, orig) // ./commands + const lib = join(root, outDir) // ./lib + const src = join(root, rootDirPath) // ./src + const relative = pathRelative(lib, orig) // ./commands // For hooks, it might point to a js file, not a module. Something like "./hooks/myhook.js" which doesn't need the js. - const out = path.join(src, relative).replace(/\.js$/, '') // ./src/commands + const out = join(src, relative).replace(/\.js$/, '') // ./src/commands // this can be a directory of commands or point to a hook file // if it's a directory, we check if the path exists. If so, return the path to the directory. // For hooks, it might point to a module, not a file. Something like "./hooks/myhook" // That file doesn't exist, and the real file is "./hooks/myhook.ts" // In that case we attempt to resolve to the filename. If it fails it will revert back to the lib path - if (fs.existsSync(out) || fs.existsSync(out + '.ts')) return out + if (existsSync(out) || existsSync(out + '.ts')) return out return orig } catch (error: any) { debug(error) diff --git a/src/config/util.ts b/src/config/util.ts index b0b2e8152..182b336a5 100644 --- a/src/config/util.ts +++ b/src/config/util.ts @@ -1,5 +1,3 @@ -import * as fs from 'fs' - const debug = require('debug') export function flatMap(arr: T[], fn: (i: T) => U[]): U[] { @@ -14,37 +12,10 @@ export function mapValues, TResult>(obj: {[P in ke }, {} as any) } -export function exists(path: string): Promise { - // eslint-disable-next-line no-promise-executor-return - return new Promise(resolve => resolve(fs.existsSync(path))) -} - export function resolvePackage(id: string, paths: { paths: string[] }): string { return require.resolve(id, paths) } -export function loadJSON(path: string): Promise { - debug('config')('loadJSON %s', path) - return new Promise((resolve, reject) => { - fs.readFile(path, 'utf8', (err: any, d: any) => { - try { - if (err) reject(err) - else resolve(JSON.parse(d)) - } catch (error: any) { - reject(error) - } - }) - }) -} - -export function compact(a: (T | undefined)[]): T[] { - return a.filter((a): a is T => Boolean(a)) -} - -export function uniq(arr: T[]): T[] { - return [...new Set(arr)].sort() -} - function displayWarnings() { if (process.listenerCount('warning') > 1) return process.on('warning', (warning: any) => { @@ -54,7 +25,10 @@ function displayWarnings() { } export function Debug(...scope: string[]): (..._: any) => void { - if (!debug) return (..._: any[]) => {} + if (!debug) return (..._: any[]) => { + // noop + } + const d = debug(['config', ...scope].join(':')) if (d.enabled) displayWarnings() return (...args: any[]) => d(...args) @@ -107,16 +81,5 @@ export function getCommandIdPermutations(commandId: string): string[] { * @param commandIds string[] * @returns string[] */ -export function collectUsableIds(commandIds: string[]): Set { - const usuableIds: string[] = [] - for (const id of commandIds) { - const parts = id.split(':') - while (parts.length > 0) { - const name = parts.join(':') - if (name) usuableIds.push(name) - parts.pop() - } - } - - return new Set(usuableIds) -} +export const collectUsableIds = (commandIds: string[]): Set => + new Set(commandIds.flatMap(id => id.split(':').map((_, i, a) => a.slice(0, i + 1).join(':')))) diff --git a/src/errors/config.ts b/src/errors/config.ts index 7a757f969..9efff616e 100644 --- a/src/errors/config.ts +++ b/src/errors/config.ts @@ -1,5 +1,5 @@ -import {settings} from '../settings' import {Logger} from './logger' +import {settings} from '../settings' function displayWarnings() { if (process.listenerCount('warning') > 1) return diff --git a/src/errors/errors/cli.ts b/src/errors/errors/cli.ts index 3fb241c7f..b4aa7e869 100644 --- a/src/errors/errors/cli.ts +++ b/src/errors/errors/cli.ts @@ -1,11 +1,10 @@ -import * as chalk from 'chalk' -import * as indent from 'indent-string' -import * as cs from 'clean-stack' -import * as wrap from 'wrap-ansi' - -import * as screen from '../../screen' +import {OclifError, PrettyPrintableError} from '../../interfaces/errors' +import chalk from 'chalk' import {config} from '../config' -import {PrettyPrintableError, OclifError} from '../../interfaces/errors' +import cs from 'clean-stack' +import {errtermwidth} from '../../screen' +import indent from 'indent-string' +import wrap from 'wrap-ansi' /** * properties specific to internal oclif error handling @@ -47,7 +46,7 @@ export class CLIError extends Error implements OclifError { } let output = `${this.name}: ${this.message}` - output = wrap(output, screen.errtermwidth - 6, {trim: false, hard: true} as any) + output = wrap(output, errtermwidth - 6, {trim: false, hard: true} as any) output = indent(output, 3) output = indent(output, 1, {indent: this.bang, includeEmptyLines: true} as any) output = indent(output, 1) diff --git a/src/errors/errors/exit.ts b/src/errors/errors/exit.ts index 11dd684d9..7796c0a12 100644 --- a/src/errors/errors/exit.ts +++ b/src/errors/errors/exit.ts @@ -2,8 +2,6 @@ import {CLIError} from './cli' import {OclifError} from '../../interfaces' export class ExitError extends CLIError implements OclifError { - oclif!: { exit: number } - code = 'EEXIT' constructor(exitCode = 1) { diff --git a/src/errors/errors/module-load.ts b/src/errors/errors/module-load.ts index 07afa7d79..211c1d9e5 100644 --- a/src/errors/errors/module-load.ts +++ b/src/errors/errors/module-load.ts @@ -2,8 +2,6 @@ import {CLIError} from './cli' import {OclifError} from '../../interfaces' export class ModuleLoadError extends CLIError implements OclifError { - oclif!: { exit: number } - code = 'MODULE_NOT_FOUND' constructor(message: string) { diff --git a/src/errors/errors/pretty-print.ts b/src/errors/errors/pretty-print.ts index 1f29a1275..3067501db 100644 --- a/src/errors/errors/pretty-print.ts +++ b/src/errors/errors/pretty-print.ts @@ -1,9 +1,8 @@ -import * as wrap from 'wrap-ansi' -import indent = require('indent-string') - -import * as screen from '../../screen' -import {config} from '../config' import {PrettyPrintableError} from '../../interfaces/errors' +import {config} from '../config' +import {errtermwidth} from '../../screen' +import indent from 'indent-string' +import wrap from 'wrap-ansi' // These exist for backwards compatibility with CLIError type CLIErrorDisplayOptions = { name?: string; bang?: string } @@ -48,7 +47,7 @@ export default function prettyPrint(error: Error & PrettyPrintableError & CLIErr .filter(Boolean) .join('\n') - let output = wrap(formatted, screen.errtermwidth - 6, {trim: false, hard: true} as any) + let output = wrap(formatted, errtermwidth - 6, {trim: false, hard: true} as any) output = indent(output, 3) output = indent(output, 1, {indent: bang || '', includeEmptyLines: true} as any) output = indent(output, 1) diff --git a/src/errors/handle.ts b/src/errors/handle.ts index 0c0af3d3d..138e73d8e 100644 --- a/src/errors/handle.ts +++ b/src/errors/handle.ts @@ -1,26 +1,39 @@ /* eslint-disable no-process-exit */ /* eslint-disable unicorn/no-process-exit */ +import {OclifError, PrettyPrintableError} from '../interfaces' +import {CLIError} from './errors/cli' +import {ExitError} from '.' +import clean from 'clean-stack' import {config} from './config' import prettyPrint from './errors/pretty-print' -import {ExitError} from '.' -import clean = require('clean-stack') -import {CLIError} from './errors/cli' -import {OclifError, PrettyPrintableError} from '../interfaces' -export async function handle(err: Error & Partial & Partial & {skipOclifErrorHandling?: boolean}): Promise { +/** + * This is an odd abstraction for process.exit, but it allows us to stub it in tests. + * + * https://github.com/sinonjs/sinon/issues/562 + */ +export const Exit = { + exit(code = 0) { + process.exit(code) + }, +} + +type ErrorToHandle = Error & Partial & Partial & {skipOclifErrorHandling?: boolean} + +export async function handle(err: ErrorToHandle): Promise { try { if (!err) err = new CLIError('no error?') - if (err.message === 'SIGINT') process.exit(1) + if (err.message === 'SIGINT') Exit.exit(1) const shouldPrint = !(err instanceof ExitError) && !err.skipOclifErrorHandling const pretty = prettyPrint(err) const stack = clean(err.stack || '', {pretty: true}) if (shouldPrint) { - console.error(pretty ? pretty : stack) + console.error(pretty ?? stack) } - const exitCode = err.oclif?.exit !== undefined && err.oclif?.exit !== false ? err.oclif?.exit : 1 + const exitCode = err.oclif?.exit ?? 1 if (config.errorLogger && err.code !== 'EEXIT') { if (stack) { @@ -28,12 +41,12 @@ export async function handle(err: Error & Partial & Partia } await config.errorLogger.flush() - .then(() => process.exit(exitCode)) + .then(() => Exit.exit(exitCode)) .catch(console.error) - } else process.exit(exitCode) + } else Exit.exit(exitCode) } catch (error: any) { console.error(err.stack) console.error(error.stack) - process.exit(1) + Exit.exit(1) } } diff --git a/src/errors/index.ts b/src/errors/index.ts index 4fbbbfe0e..17cc8217f 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -1,3 +1,9 @@ +import {CLIError, addOclifExitCode} from './errors/cli' +import {OclifError, PrettyPrintableError} from '../interfaces' +import prettyPrint, {applyPrettyPrintOptions} from './errors/pretty-print' +import {ExitError} from './errors/exit' +import {config} from './config' + export {handle} from './handle' export {ExitError} from './errors/exit' export {ModuleLoadError} from './errors/module-load' @@ -5,14 +11,6 @@ export {CLIError} from './errors/cli' export {Logger} from './logger' export {config} from './config' -import {config} from './config' -import {CLIError, addOclifExitCode} from './errors/cli' -import {ExitError} from './errors/exit' -import prettyPrint, {applyPrettyPrintOptions} from './errors/pretty-print' -import {OclifError, PrettyPrintableError} from '../interfaces' - -export {PrettyPrintableError} - export function exit(code = 0): never { throw new ExitError(code) } @@ -61,3 +59,5 @@ export function memoizedWarn(input: string | Error): void { WARNINGS.add(input) } + +export {PrettyPrintableError} from '../interfaces' diff --git a/src/errors/logger.ts b/src/errors/logger.ts index ece0311b2..f2a79c329 100644 --- a/src/errors/logger.ts +++ b/src/errors/logger.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs/promises' -import {dirname} from 'path' +import {appendFile, mkdir} from 'node:fs/promises' +import {dirname} from 'node:path' import stripAnsi = require('strip-ansi') const timestamp = () => new Date().toISOString() @@ -34,8 +34,8 @@ export class Logger { if (this.buffer.length === 0) return const mylines = this.buffer this.buffer = [] - await fs.mkdir(dirname(this.file), {recursive: true}) - await fs.appendFile(this.file, mylines.join('\n') + '\n') + await mkdir(dirname(this.file), {recursive: true}) + await appendFile(this.file, mylines.join('\n') + '\n') }) await this.flushing } diff --git a/src/execute.ts b/src/execute.ts index f83b01475..d5e95aa25 100644 --- a/src/execute.ts +++ b/src/execute.ts @@ -1,8 +1,8 @@ -import {settings} from './settings' +import {LoadOptions} from './interfaces' import {flush} from './cli-ux/flush' import {handle} from './errors/handle' -import run from './main' -import * as Interfaces from './interfaces' +import {run} from './main' +import {settings} from './settings' /** * Load and run oclif CLI @@ -12,7 +12,7 @@ import * as Interfaces from './interfaces' * * @example For ESM dev.js * ``` - * #!/usr/bin/env node + * #!/usr/bin/env ts-node * void (async () => { * const oclif = await import('@oclif/core') * await oclif.execute({development: true, dir: import.meta.url}) @@ -30,7 +30,7 @@ import * as Interfaces from './interfaces' * * @example For CJS dev.js * ``` - * #!/usr/bin/env node + * #!/usr/bin/env ts-node * void (async () => { * const oclif = await import('@oclif/core') * await oclif.execute({development: true, dir: __dirname}) @@ -46,11 +46,11 @@ import * as Interfaces from './interfaces' * })() * ``` */ -export default async function execute( +export async function execute( options: { dir: string; args?: string[]; - loadOptions?: Interfaces.LoadOptions; + loadOptions?: LoadOptions; development?: boolean; }, ): Promise { diff --git a/src/flags.ts b/src/flags.ts index 786d3db26..61df7db68 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -1,10 +1,45 @@ -import {URL} from 'url' -import {loadHelpClass} from './help' -import {BooleanFlag} from './interfaces' -import {FlagDefinition, OptionFlagDefaults, FlagParser} from './interfaces/parser' +/* eslint-disable valid-jsdoc */ +import {BooleanFlag, CustomOptions, FlagDefinition, OptionFlag} from './interfaces' import {dirExists, fileExists} from './util' import {CLIError} from './errors' +import {URL} from 'node:url' +import {loadHelpClass} from './help' + +type NotArray = T extends Array ? never: T; + +export function custom( + defaults: Partial> & { + multiple: true + } & ( + {required: true} | {default: OptionFlag['default']} + ), +): FlagDefinition + +export function custom( + defaults: Partial> & { + multiple?: false | undefined; + } & ( + {required: true} | {default: OptionFlag, P>['default']} + ), +): FlagDefinition + +export function custom( + defaults: Partial> & { + default?: OptionFlag, P>['default'] | undefined; + multiple?: false | undefined; + required?: false | undefined; + }, +): FlagDefinition + +export function custom( + defaults: Partial> & { + multiple: true; + default?: OptionFlag['default'] | undefined; + required?: false | undefined; + }, +): FlagDefinition +export function custom(): FlagDefinition /** * Create a custom flag. * @@ -22,24 +57,17 @@ import {CLIError} from './errors' * }, * }) */ -export function custom>( - defaults: {parse: FlagParser, multiple: true} & Partial>, -): FlagDefinition -export function custom>( - defaults: {parse: FlagParser} & Partial>, -): FlagDefinition -export function custom>(defaults: Partial>): FlagDefinition -export function custom>(defaults: Partial>): FlagDefinition { - return (options: any = {}) => { - return { - parse: async (input, _ctx, _opts) => input, - ...defaults, - ...options, - input: [] as string[], - multiple: Boolean(options.multiple === undefined ? defaults.multiple : options.multiple), - type: 'option', - } - } +export function custom( + defaults?: Partial>, +): FlagDefinition { + return (options: any = {}) => ({ + parse: async (input, _ctx, _opts) => input, + ...defaults, + ...options, + input: [] as string[], + multiple: Boolean(options.multiple === undefined ? defaults?.multiple ?? false : options.multiple), + type: 'option', + }) } export function boolean( @@ -54,7 +82,7 @@ export function boolean( } export const integer = custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { if (!/^-?\d+$/.test(input)) throw new CLIError(`Expected an integer but received: ${input}`) const num = Number.parseInt(input, 10) @@ -67,7 +95,7 @@ export const integer = custom({ }) export const directory = custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { if (opts.exists) return dirExists(input) return input @@ -75,7 +103,7 @@ export const directory = custom({ }) export const file = custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { if (opts.exists) return fileExists(input) return input @@ -87,37 +115,97 @@ export const file = custom({ * if the string is not a valid URL. */ export const url = custom({ - parse: async input => { + async parse(input) { try { return new URL(input) } catch { - throw new Error(`Expected a valid url but received: ${input}`) + throw new CLIError(`Expected a valid url but received: ${input}`) } }, }) -const stringFlag = custom({}) -export {stringFlag as string} - -export const version = (opts: Partial> = {}): BooleanFlag => { - return boolean({ - description: 'Show CLI version.', - ...opts, - parse: async (_: any, ctx) => { - ctx.log(ctx.config.userAgent) - ctx.exit(0) - }, - }) -} +export const string = custom() + +export const version = (opts: Partial> = {}): BooleanFlag => boolean({ + description: 'Show CLI version.', + ...opts, + async parse(_, ctx) { + ctx.log(ctx.config.userAgent) + ctx.exit(0) + }, +}) -export const help = (opts: Partial> = {}): BooleanFlag => { - return boolean({ - description: 'Show CLI help.', - ...opts, - parse: async (_, cmd) => { - const Help = await loadHelpClass(cmd.config) - await new Help(cmd.config, cmd.config.pjson.helpOptions).showHelp(cmd.id ? [cmd.id, ...cmd.argv] : cmd.argv) - cmd.exit(0) - }, +export const help = (opts: Partial> = {}): BooleanFlag => boolean({ + description: 'Show CLI help.', + ...opts, + async parse(_, cmd) { + const Help = await loadHelpClass(cmd.config) + await new Help(cmd.config, cmd.config.pjson.helpOptions).showHelp(cmd.id ? [cmd.id, ...cmd.argv] : cmd.argv) + cmd.exit(0) + }, +}) + +type ElementType> = T[number]; + +export function option( + defaults: Partial[], P>> & { + options: T; + multiple: true + } & ( + {required: true} | { + default: OptionFlag[], P>['default'] | undefined; + } + ), +): FlagDefinition + +export function option( + defaults: Partial, P>> & { + options: T; + multiple?: false | undefined; + } & ( + {required: true} | {default: OptionFlag, P>['default']} + ), +): FlagDefinition + +export function option( + defaults: Partial, P>> & { + options: T; + default?: OptionFlag, P>['default'] | undefined; + multiple?: false | undefined; + required?: false | undefined; + }, +): FlagDefinition + +export function option( + defaults: Partial[], P>> & { + options: T; + multiple: true; + default?: OptionFlag[], P>['default'] | undefined; + required?: false | undefined; + }, +): FlagDefinition + +/** + * Create a custom flag that infers the flag type from the provided options. + * + * @example + * export default class MyCommand extends Command { + * static flags = { + * name: Flags.option({ + * options: ['foo', 'bar'] as const, + * })(), + * } + * } + */ +export function option( + defaults: Partial, P>> & {options: T}, +): FlagDefinition { + return (options: any = {}) => ({ + parse: async (input, _ctx, _opts) => input, + ...defaults, + ...options, + input: [] as string[], + multiple: Boolean(options.multiple === undefined ? defaults.multiple : options.multiple), + type: 'option', }) } diff --git a/src/help/command.ts b/src/help/command.ts index 1d64b6b6b..7faae8825 100644 --- a/src/help/command.ts +++ b/src/help/command.ts @@ -1,20 +1,16 @@ -import * as chalk from 'chalk' -import stripAnsi = require('strip-ansi') - -import {castArray, compact, ensureArgObject, sortBy} from '../util' import * as Interfaces from '../interfaces' import {HelpFormatter, HelpSection, HelpSectionRenderer} from './formatter' -import {DocOpts} from './docopts' +import {castArray, compact, ensureArgObject, sortBy} from '../util' import {Command} from '../command' +import {DocOpts} from './docopts' +import chalk from 'chalk' +import stripAnsi from 'strip-ansi' // Don't use os.EOL because we need to ensure that a string // written on any platform, that may use \r\n or \n, will be // split on any platform, not just the os specific EOL at runtime. const POSSIBLE_LINE_FEED = /\r\n|\n/ -const { - underline, -} = chalk let { dim, } = chalk @@ -123,15 +119,15 @@ export class CommandHelp extends HelpFormatter { } protected usage(): string { - const usage = this.command.usage + const {usage} = this.command const body = (usage ? castArray(usage) : [this.defaultUsage()]) .map(u => { const allowedSpacing = this.opts.maxWidth - this.indentSpacing const line = `$ ${this.config.bin} ${u}`.trim() if (line.length > allowedSpacing) { const splitIndex = line.slice(0, Math.max(0, allowedSpacing)).lastIndexOf(' ') - return line.slice(0, Math.max(0, splitIndex)) + '\n' + - this.indent(this.wrap(line.slice(Math.max(0, splitIndex)), this.indentSpacing * 2)) + return line.slice(0, Math.max(0, splitIndex)) + '\n' + + this.indent(this.wrap(line.slice(Math.max(0, splitIndex)), this.indentSpacing * 2)) } return this.wrap(line) @@ -180,48 +176,38 @@ export class CommandHelp extends HelpFormatter { protected examples(examples: Command.Example[] | undefined | string): string | undefined { if (!examples || examples.length === 0) return - const formatIfCommand = (example: string): string => { - example = this.render(example) - if (example.startsWith(this.config.bin)) return dim(`$ ${example}`) - if (example.startsWith(`$ ${this.config.bin}`)) return dim(example) - return example - } - - const isCommand = (example: string) => stripAnsi(formatIfCommand(example)).startsWith(`$ ${this.config.bin}`) - const body = castArray(examples).map(a => { let description let commands if (typeof a === 'string') { const lines = a .split(POSSIBLE_LINE_FEED) - .filter(line => Boolean(line)) + .filter(Boolean) // If the example is \n then format correctly - // eslint-disable-next-line unicorn/no-array-callback-reference - if (lines.length >= 2 && !isCommand(lines[0]) && lines.slice(1).every(isCommand)) { + if (lines.length >= 2 && !this.isCommand(lines[0]) && lines.slice(1).every(i => this.isCommand(i))) { description = lines[0] commands = lines.slice(1) } else { - return lines.map(line => formatIfCommand(line)).join('\n') + return lines.map(line => this.formatIfCommand(line)).join('\n') } } else { description = a.description commands = [a.command] } - const multilineSeparator = - this.config.platform === 'win32' ? - (this.config.shell.includes('powershell') ? '`' : '^') : - '\\' + const multilineSeparator + = this.config.platform === 'win32' + ? (this.config.shell.includes('powershell') ? '`' : '^') + : '\\' // The command will be indented in the section, which is also indented const finalIndentedSpacing = this.indentSpacing * 2 - const multilineCommands = commands.map(c => { + const multilineCommands = commands.map(c => // First indent keeping room for escaped newlines - return this.indent(this.wrap(formatIfCommand(c), finalIndentedSpacing + 4)) + this.indent(this.wrap(this.formatIfCommand(c), finalIndentedSpacing + 4)) // Then add the escaped newline - .split(POSSIBLE_LINE_FEED).join(` ${multilineSeparator}\n `) - }).join('\n') + .split(POSSIBLE_LINE_FEED).join(` ${multilineSeparator}\n `), + ).join('\n') return `${this.wrap(description, finalIndentedSpacing)}\n\n${multilineCommands}` }).join('\n\n') @@ -270,7 +256,7 @@ export class CommandHelp extends HelpFormatter { } if (flag.multiple) value += '...' - if (!value.includes('|')) value = underline(value) + if (!value.includes('|')) value = chalk.underline(value) label += `=${value}` } @@ -312,5 +298,16 @@ export class CommandHelp extends HelpFormatter { return body } + + private formatIfCommand(example: string): string { + example = this.render(example) + if (example.startsWith(this.config.bin)) return dim(`$ ${example}`) + if (example.startsWith(`$ ${this.config.bin}`)) return dim(example) + return example + } + + private isCommand(example: string): boolean { + return stripAnsi(this.formatIfCommand(example)).startsWith(`$ ${this.config.bin}`) + } } export default CommandHelp diff --git a/src/help/docopts.ts b/src/help/docopts.ts index 37ef17e52..3ee8f8299 100644 --- a/src/help/docopts.ts +++ b/src/help/docopts.ts @@ -79,9 +79,7 @@ export class DocOpts { public toString(): string { const opts = this.cmd.id === '.' || this.cmd.id === '' ? [] : ['<%= command.id %>'] if (this.cmd.args) { - const a = Object.values(ensureArgObject(this.cmd.args)).map(arg => { - return arg.required ? arg.name.toUpperCase() : `[${arg.name.toUpperCase()}]` - }) || [] + const a = Object.values(ensureArgObject(this.cmd.args)).map(arg => arg.required ? arg.name.toUpperCase() : `[${arg.name.toUpperCase()}]`) || [] opts.push(...a) } @@ -156,11 +154,7 @@ export class DocOpts { delete this.flagMap[toCombine] } - if (isRequired) { - elementMap[flagName] = `(${elementMap[flagName] || ''})` - } else { - elementMap[flagName] = `[${elementMap[flagName] || ''}]` - } + elementMap[flagName] = isRequired ? `(${elementMap[flagName] || ''})` : `[${elementMap[flagName] || ''}]` // We handled this flag, don't handle it again delete this.flagMap[flagName] diff --git a/src/help/formatter.ts b/src/help/formatter.ts index 4b61a569f..ad039f851 100644 --- a/src/help/formatter.ts +++ b/src/help/formatter.ts @@ -1,19 +1,13 @@ -import * as Chalk from 'chalk' -import indent = require('indent-string') -import stripAnsi = require('strip-ansi') -import {Command} from '../command' - import * as Interfaces from '../interfaces' +import {Command} from '../command' +import chalk from 'chalk' +import indent from 'indent-string' import {stdtermwidth} from '../screen' +import stripAnsi from 'strip-ansi' import {template} from './util' - -const width = require('string-width') -const widestLine = require('widest-line') - -const wrap = require('wrap-ansi') -const { - bold, -} = Chalk +import widestLine from 'widest-line' +import width from 'string-width' +import wrap from 'wrap-ansi' export type HelpSectionKeyValueTable = {name: string; description: string}[] export type HelpSection = {header: string; body: string | HelpSectionKeyValueTable | [string, string | undefined][] | undefined} | undefined; @@ -202,7 +196,7 @@ export class HelpFormatter { } const output = [ - bold(header), + chalk.bold(header), this.indent(Array.isArray(newBody) ? this.renderList(newBody, {stripAnsi: this.opts.stripAnsi, indentation: 2}) : newBody), ].join('\n') return this.opts.stripAnsi ? stripAnsi(output) : output diff --git a/src/help/index.ts b/src/help/index.ts index 20cc06234..871d6c2f5 100644 --- a/src/help/index.ts +++ b/src/help/index.ts @@ -1,15 +1,17 @@ -import stripAnsi = require('strip-ansi') -import * as util from 'util' import * as Interfaces from '../interfaces' -import {error} from '../errors' -import CommandHelp from './command' -import RootHelp from './root' import {compact, sortBy, uniqBy} from '../util' import {formatCommandDeprecationWarning, getHelpFlagAdditions, standardizeIDFromArgv, toConfiguredId} from './util' -import {HelpFormatter} from './formatter' -import {toCached} from '../config/config' import {Command} from '../command' +import {CommandHelp} from './command' +import {HelpFormatter} from './formatter' +import RootHelp from './root' +import {error} from '../errors' +import {format} from 'node:util' import {stdout} from '../cli-ux/stream' +import {toCached} from '../config/config' + +import stripAnsi = require('strip-ansi') + export {CommandHelp} from './command' export {standardizeIDFromArgv, loadHelpClass, getHelpFlagAdditions, normalizeArgv} from './util' @@ -62,7 +64,7 @@ export class Help extends HelpBase { } protected get sortedCommands(): Command.Loadable[] { - let commands = this.config.commands + let {commands} = this.config commands = commands.filter(c => this.opts.all || !c.hidden) commands = sortBy(commands, c => c.id) @@ -148,9 +150,9 @@ export class Help extends HelpBase { if (state) { this.log( - state === 'deprecated' ? - `${formatCommandDeprecationWarning(toConfiguredId(name, this.config), command.deprecationOptions)}` : - `This command is in ${state}.\n`, + state === 'deprecated' + ? `${formatCommandDeprecationWarning(toConfiguredId(name, this.config), command.deprecationOptions)}` + : `This command is in ${state}.\n`, ) } @@ -185,9 +187,9 @@ export class Help extends HelpBase { const state = this.config.pjson?.oclif?.state if (state) { this.log( - state === 'deprecated' ? - `${this.config.bin} is deprecated` : - `${this.config.bin} is in ${state}.\n`, + state === 'deprecated' + ? `${this.config.bin} is deprecated` + : `${this.config.bin} is in ${state}.\n`, ) } @@ -212,7 +214,7 @@ export class Help extends HelpBase { } protected async showTopicHelp(topic: Interfaces.Topic): Promise { - const name = topic.name + const {name} = topic const depth = name.split(':').length const subTopics = this.sortedTopics.filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1) @@ -241,8 +243,8 @@ export class Help extends HelpBase { protected formatCommand(command: Command.Class | Command.Loadable | Command.Cached): string { if (this.config.topicSeparator !== ':') { - command.id = command.id.replace(/:/g, this.config.topicSeparator) - command.aliases = command.aliases && command.aliases.map(a => a.replace(/:/g, this.config.topicSeparator)) + command.id = command.id.replaceAll(':', this.config.topicSeparator) + command.aliases = command.aliases && command.aliases.map(a => a.replaceAll(':', this.config.topicSeparator)) } const help = this.getCommandHelpClass(command) @@ -257,7 +259,7 @@ export class Help extends HelpBase { if (commands.length === 0) return '' const body = this.renderList(commands.map(c => { - if (this.config.topicSeparator !== ':') c.id = c.id.replace(/:/g, this.config.topicSeparator) + if (this.config.topicSeparator !== ':') c.id = c.id.replaceAll(':', this.config.topicSeparator) return [ c.id, this.summary(c), @@ -291,7 +293,7 @@ export class Help extends HelpBase { const summary = description.split('\n')[0] description = description.split('\n').slice(1).join('\n') let topicID = `${topic.name}:COMMAND` - if (this.config.topicSeparator !== ':') topicID = topicID.replace(/:/g, this.config.topicSeparator) + if (this.config.topicSeparator !== ':') topicID = topicID.replaceAll(':', this.config.topicSeparator) let output = compact([ summary, this.section(this.opts.usageHeader || 'USAGE', `$ ${this.config.bin} ${topicID}`), @@ -304,7 +306,7 @@ export class Help extends HelpBase { protected formatTopics(topics: Interfaces.Topic[]): string { if (topics.length === 0) return '' const body = this.renderList(topics.map(c => { - if (this.config.topicSeparator !== ':') c.name = c.name.replace(/:/g, this.config.topicSeparator) + if (this.config.topicSeparator !== ':') c.name = c.name.replaceAll(':', this.config.topicSeparator) return [ c.name, c.description && this.render(c.description.split('\n')[0]), @@ -322,6 +324,6 @@ export class Help extends HelpBase { } protected log(...args: string[]): void { - stdout.write(util.format.apply(this, args) + '\n') + stdout.write(format.apply(this, args) + '\n') } } diff --git a/src/help/root.ts b/src/help/root.ts index 795a0cfb1..6aaa587f2 100644 --- a/src/help/root.ts +++ b/src/help/root.ts @@ -1,8 +1,7 @@ -import stripAnsi = require('strip-ansi') - -import {compact} from '../util' import * as Interfaces from '../interfaces' import {HelpFormatter} from './formatter' +import {compact} from '../util' +import stripAnsi = require('strip-ansi') export default class RootHelp extends HelpFormatter { constructor(public config: Interfaces.Config, public opts: Interfaces.HelpOptions) { diff --git a/src/help/util.ts b/src/help/util.ts index d6e0c460d..278c26050 100644 --- a/src/help/util.ts +++ b/src/help/util.ts @@ -1,8 +1,8 @@ import * as ejs from 'ejs' -import {Config as IConfig, HelpOptions, Deprecation} from '../interfaces' +import {Deprecation, HelpOptions, Config as IConfig} from '../interfaces' import {Help, HelpBase} from '.' -import ModuleLoader from '../module-loader' import {collectUsableIds} from '../config/util' +import {load} from '../module-loader' interface HelpBaseDerived { new(config: IConfig, opts?: Partial): HelpBase; @@ -13,12 +13,12 @@ function extractClass(exported: any): HelpBaseDerived { } export async function loadHelpClass(config: IConfig): Promise { - const pjson = config.pjson + const {pjson} = config const configuredClass = pjson && pjson.oclif && pjson.oclif.helpClass if (configuredClass) { try { - const exported = await ModuleLoader.load(config, configuredClass) as HelpBaseDerived + const exported = await load(config, configuredClass) as HelpBaseDerived return extractClass(exported) as HelpBaseDerived } catch (error: any) { throw new Error(`Unable to load configured help class "${configuredClass}", failed with message:\n${error.message}`) @@ -28,7 +28,6 @@ export async function loadHelpClass(config: IConfig): Promise { return Help } -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function template(context: any): (t: string) => string { function render(t: string): string { return ejs.render(t, context) @@ -37,6 +36,9 @@ export function template(context: any): (t: string) => string { return render } +const isFlag = (s: string) => s.startsWith('-') +const isArgWithValue = (s: string) => s.includes('=') + function collateSpacedCmdIDFromArgs(argv: string[], config: IConfig): string[] { if (argv.length === 1) return argv @@ -45,8 +47,6 @@ function collateSpacedCmdIDFromArgs(argv: string[], config: IConfig): string[] { const final: string[] = [] const idPresent = (id: string) => ids.has(id) - const isFlag = (s: string) => s.startsWith('-') - const isArgWithValue = (s: string) => s.includes('=') const finalizeId = (s?: string) => s ? [...final, s].join(':') : final.join(':') const hasArgs = () => { @@ -78,12 +78,12 @@ function collateSpacedCmdIDFromArgs(argv: string[], config: IConfig): string[] { } export function toStandardizedId(commandID: string, config: IConfig): string { - return commandID.replace(new RegExp(config.topicSeparator, 'g'), ':') + return commandID.replaceAll(new RegExp(config.topicSeparator, 'g'), ':') } export function toConfiguredId(commandID: string, config: IConfig): string { const defaultTopicSeparator = ':' - return commandID.replace(new RegExp(defaultTopicSeparator, 'g'), config.topicSeparator || defaultTopicSeparator) + return commandID.replaceAll(new RegExp(defaultTopicSeparator, 'g'), config.topicSeparator || defaultTopicSeparator) } export function standardizeIDFromArgv(argv: string[], config: IConfig): string[] { diff --git a/src/index.ts b/src/index.ts index 97621e091..4eea78bfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,55 +1,4 @@ -import {Command} from './command' -import run from './main' -import execute from './execute' -import {handle} from './errors/handle' -import {Config, Plugin, tsPath, toCached} from './config' -import * as Interfaces from './interfaces' -import * as Errors from './errors' -import * as Flags from './flags' -import * as Args from './args' -import {CommandHelp, HelpBase, Help, loadHelpClass} from './help' -import {toStandardizedId, toConfiguredId} from './help/util' -import * as Parser from './parser' -import {Hook} from './interfaces/hooks' -import {settings, Settings} from './settings' -import {HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable} from './help/formatter' -import * as ux from './cli-ux' -import {stderr, stdout} from './cli-ux/stream' -import Performance from './performance' -import {flush} from './cli-ux/flush' - -export { - Args, - Command, - CommandHelp, - Config, - Errors, - execute, - Flags, - flush, - handle, - Help, - HelpBase, - HelpSection, - HelpSectionKeyValueTable, - HelpSectionRenderer, - Hook, - Interfaces, - loadHelpClass, - Parser, - Performance, - Plugin, - run, - settings, - Settings, - stderr, - stdout, - toCached, - toConfiguredId, - toStandardizedId, - tsPath, - ux, -} +import {stderr} from './cli-ux/stream' function checkCWD() { try { @@ -62,3 +11,23 @@ function checkCWD() { } checkCWD() + +export * as Args from './args' +export * as Errors from './errors' +export * as Flags from './flags' +export * as Interfaces from './interfaces' +export * as Parser from './parser' +export * as ux from './cli-ux' +export {CommandHelp, HelpBase, Help, loadHelpClass} from './help' +export {Config, toCached, Plugin, tsPath} from './config' +export {HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable} from './help/formatter' +export {Settings, settings} from './settings' +export {stdout, stderr} from './cli-ux/stream' +export {toConfiguredId, toStandardizedId} from './help/util' +export {Command} from './command' +export {Hook} from './interfaces/hooks' +export {Performance} from './performance' +export {execute} from './execute' +export {flush} from './cli-ux/flush' +export {handle} from './errors/handle' +export {run} from './main' diff --git a/src/interfaces/config.ts b/src/interfaces/config.ts index c694e35ac..df7eebb5e 100644 --- a/src/interfaces/config.ts +++ b/src/interfaces/config.ts @@ -1,11 +1,11 @@ +import {Hook, Hooks} from './hooks' +import {Options, Plugin} from './plugin' +import {Command} from '../command' import {PJSON} from './pjson' -import {Hooks, Hook} from './hooks' -import {Plugin, Options} from './plugin' import {Topic} from './topic' -import {Command} from '../command' export type LoadOptions = Options | string | Config | undefined -export type PlatformTypes = 'darwin' | 'linux' | 'win32' | 'aix' | 'freebsd' | 'openbsd' | 'sunos' | 'wsl' +export type PlatformTypes = NodeJS.Platform | 'wsl' export type ArchTypes = 'arm' | 'arm64' | 'mips' | 'mipsel' | 'ppc' | 'ppc64' | 's390' | 's390x' | 'x32' | 'x64' | 'x86' export type PluginVersionDetail = { diff --git a/src/interfaces/errors.ts b/src/interfaces/errors.ts index f31c0d0a0..e0c083e3a 100644 --- a/src/interfaces/errors.ts +++ b/src/interfaces/errors.ts @@ -2,7 +2,7 @@ export type CommandError = Error & {exitCode?: number}; export interface OclifError { oclif: { - exit?: number | false; + exit?: number; }; } diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts index 462a58113..7fc4ded0f 100644 --- a/src/interfaces/parser.ts +++ b/src/interfaces/parser.ts @@ -1,6 +1,5 @@ -import {Command} from '../command' import {AlphabetLowercase, AlphabetUppercase} from './alphabet' -import {Config} from './config' +import {Command} from '../command' export type FlagOutput = { [name: string]: any } export type ArgOutput = { [name: string]: any } @@ -154,6 +153,10 @@ export type FlagProps = { * Alternate names that can be used for this flag. */ aliases?: string[]; + /** + * Alternate short chars that can be used for this flag. + */ + charAliases?: (AlphabetLowercase | AlphabetUppercase)[]; /** * Emit deprecation warning when a flag alias is provided */ @@ -205,13 +208,14 @@ export type BooleanFlagProps = FlagProps & { export type OptionFlagProps = FlagProps & { type: 'option'; helpValue?: string; - options?: string[]; + options?: readonly string[]; multiple?: boolean; } export type FlagParserContext = Command & {token: FlagToken} -export type FlagParser = (input: I, context: FlagParserContext, opts: P & OptionFlag) => Promise +export type FlagParser = (input: I, context: FlagParserContext, opts: P & OptionFlag) => + T extends Array ? Promise : Promise export type ArgParserContext = Command & {token: ArgToken} @@ -238,32 +242,118 @@ export type BooleanFlag = FlagProps & BooleanFlagProps & { parse: (input: boolean, context: FlagParserContext, opts: FlagProps & BooleanFlagProps) => Promise } -export type OptionFlagDefaults = FlagProps & OptionFlagProps & { - parse: FlagParser - defaultHelp?: FlagDefaultHelp; - input: string[]; - default?: M extends true ? FlagDefault : FlagDefault; -} - export type OptionFlag = FlagProps & OptionFlagProps & { parse: FlagParser defaultHelp?: FlagDefaultHelp; input: string[]; -} & ({ default?: FlagDefault; - multiple: false; -} | { - default?: FlagDefault; - multiple: true; -}) +} -export type FlagDefinition = { +type ReturnTypeSwitches = {multiple: boolean; requiredOrDefaulted: boolean} + +/** + * The logic here is as follows: + * - If requiredOrDefaulted is true && multiple is true, then the return type is T[] + * - It's possible that T extends an Array, if so we want to return T so that the return isn't T[][] + * - If requiredOrDefaulted is true && multiple is false, then the return type is T + * - If requiredOrDefaulted is false && multiple is true, then the return type is T[] | undefined + * - It's possible that T extends an Array, if so we want to return T so that the return isn't T[][] + * - If requiredOrDefaulted is false && multiple is false, then the return type is T | undefined + */ +type FlagReturnType = + R['requiredOrDefaulted'] extends true ? + R['multiple'] extends true ? + [T] extends [Array] ? T : + T[] : + T : + R['multiple'] extends true ? + [T] extends [Array] ? T | undefined : + T[] | undefined : + T | undefined + +/** + * FlagDefinition types a function that takes `options` and returns an OptionFlag. + * + * This is returned by `Flags.custom()` and `Flags.option()`, which each take a `defaults` object + * that mirrors the OptionFlag interface. + * + * The `T` in the `OptionFlag` return type is determined by a combination of the provided defaults for + * `multiple`, `required`, and `default` and the provided options for those same properties. If these properties + * are provided in the options, they override the defaults. + * + * no options or defaults -> T | undefined + * `required` -> T + * `default` -> T + * `multiple` -> T[] | undefined + * `required` + `multiple` -> T[] + * `default` + `multiple` -> T[] + */ +export type FlagDefinition< + T, + P = CustomOptions, + R extends ReturnTypeSwitches = {multiple: false, requiredOrDefaulted: false} +> = { + ( + // `multiple` is set to false and `required` is set to true in options, potentially overriding the default + options: P & { multiple: false; required: true } & Partial, P>> + ): OptionFlag>; + ( + // `multiple` is set to true and `required` is set to false in options, potentially overriding the default + options: P & { multiple: true; required: false } & Partial, P>> + ): OptionFlag>; + ( + // `multiple` is set to true and `required` is set to false in options, potentially overriding the default + options: P & { multiple: false; required: false } & Partial, P>> + ): OptionFlag>; + ( + options: R['multiple'] extends true ? + // `multiple` is defaulted to true and either `required=true` or `default` are provided in options + P & ( + { required: true } | + { default: OptionFlag, P>['default'] } + ) & Partial, P>> : + // `multiple` is NOT defaulted to true and either `required=true` or `default` are provided in options + P & { multiple?: false | undefined } & ( + { required: true } | + { default: OptionFlag, P>['default'] } + ) & Partial, P>> + ): OptionFlag>; + ( + options: R['multiple'] extends true ? + // `multiple` is defaulted to true and either `required=true` or `default` are provided in options + P & ( + { required: true } | + { default: OptionFlag, P>['default'] } + ) & Partial, P>> : + // `multiple` is NOT defaulted to true but `multiple=true` and either `required=true` or `default` are provided in options + P & { multiple: true } & ( + { required: true } | + { default: OptionFlag, P>['default'] } + ) & Partial, P>> + ): OptionFlag>; + ( + // `multiple` is not provided in options but either `required=true` or `default` are provided + options: P & { multiple?: false | undefined; } & ( + { required: true } | + { default: OptionFlag, P>['default'] } + ) & Partial, P>> + ): OptionFlag>; + ( + // `required` is set to false in options, potentially overriding the default + options: P & { required: false } & Partial, P>> + ): OptionFlag>; + ( + // `multiple` is set to false in options, potentially overriding the default + options: P & { multiple: false } & Partial, P>> + ): OptionFlag>; ( - options: P & { multiple: true } & ({ required: true } | { default: FlagDefault }) & Partial> - ): OptionFlag; - (options: P & { multiple: true } & Partial>): OptionFlag; - (options: P & ({ required: true } | { default: FlagDefault }) & Partial>): OptionFlag; - (options?: P & Partial>): OptionFlag; + // Catch all for when `multiple` is not set in the options + options?: P & { multiple?: false | undefined } & Partial, P>> + ): OptionFlag>; + ( + // `multiple` is set to true in options, potentially overriding the default + options: P & { multiple: true } & Partial, P>> + ): OptionFlag>; } export type Flag = BooleanFlag | OptionFlag @@ -290,26 +380,6 @@ export type ParserContext = Command & { token?: FlagToken | ArgToken; } -export type CompletionContext = { - args?: { [name: string]: string }; - flags?: { [name: string]: string }; - argv?: string[]; - config: Config; -} - -export type Completion = { - skipCache?: boolean; - cacheDuration?: number; - cacheKey?(ctx: CompletionContext): Promise; - options(ctx: CompletionContext): Promise; -} - -export type CompletableOptionFlag = OptionFlag & { - completion?: Completion; -} - -export type CompletableFlag = BooleanFlag | CompletableOptionFlag - -export type FlagInput = { [P in keyof T]: CompletableFlag } +export type FlagInput = { [P in keyof T]: Flag } export type ArgInput = { [P in keyof T]: Arg } diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index f4467e90b..50207bcba 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -77,6 +77,7 @@ export interface Plugin { hooks: { [k: string]: string[] }; readonly commandIDs: string[]; readonly topics: Topic[]; + readonly hasManifest: boolean; findCommand(id: string, opts: { must: true }): Promise; findCommand(id: string, opts?: { must: boolean }): Promise | undefined; diff --git a/src/main.ts b/src/main.ts index 488679ab4..435346c45 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,11 @@ -import {fileURLToPath} from 'url' - -import {format, inspect} from 'util' - import * as Interfaces from './interfaces' -import {URL} from 'url' -import {Config} from './config' +import {URL, fileURLToPath} from 'node:url' +import {format, inspect} from 'node:util' import {getHelpFlagAdditions, loadHelpClass, normalizeArgv} from './help' + +import {Config} from './config' +import {Performance} from './performance' import {stdout} from './cli-ux/stream' -import Performance from './performance' const debug = require('debug')('oclif:main') @@ -34,14 +32,14 @@ export const versionAddition = (argv: string[], config?: Interfaces.Config): boo return false } -export default async function run(argv?: string[], options?: Interfaces.LoadOptions): Promise { +export async function run(argv?: string[], options?: Interfaces.LoadOptions): Promise { const marker = Performance.mark('main.run') const initMarker = Performance.mark('main.run#init') const collectPerf = async () => { marker?.stop() - initMarker?.stop() + if (!initMarker?.stopped) initMarker?.stop() await Performance.collect() Performance.debug() } diff --git a/src/module-loader.ts b/src/module-loader.ts index 715a2e7b2..c3105d027 100644 --- a/src/module-loader.ts +++ b/src/module-loader.ts @@ -1,8 +1,9 @@ -import * as path from 'path' -import * as url from 'url' -import {existsSync, lstatSync} from 'fs' -import {ModuleLoadError} from './errors' import {Config as IConfig, Plugin as IPlugin} from './interfaces' +import {existsSync, lstatSync} from 'node:fs' +import {extname, join, sep} from 'node:path' +import {Command} from './command' +import {ModuleLoadError} from './errors' +import {pathToFileURL} from 'node:url' import {tsPath} from './config' const getPackageType = require('get-package-type') @@ -13,86 +14,115 @@ const getPackageType = require('get-package-type') // eslint-disable-next-line camelcase const s_EXTENSIONS: string[] = ['.ts', '.js', '.mjs', '.cjs'] -const isPlugin = (config: IConfig|IPlugin): config is IPlugin => { - return (config).type !== undefined -} +const isPlugin = (config: IConfig|IPlugin): config is IPlugin => (config).type !== undefined /** - * Provides a static class with several utility methods to work with Oclif config / plugin to load ESM or CJS Node - * modules and source files. + * Loads and returns a module. + * + * Uses `getPackageType` to determine if `type` is set to 'module. If so loads '.js' files as ESM otherwise uses + * a bare require to load as CJS. Also loads '.mjs' files as ESM. + * + * Uses dynamic import to load ESM source or require for CommonJS. * - * @author Michael Leahy (https://github.com/typhonrt) + * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that + * provides a consistent stack trace and info. + * + * @param {IConfig|IPlugin} config - Oclif config or plugin config. + * @param {string} modulePath - NPM module name or file path to load. + * + * @returns {Promise<*>} The entire ESM module from dynamic import or CJS module by require. */ -// eslint-disable-next-line unicorn/no-static-only-class -export default class ModuleLoader { - /** - * Loads and returns a module. - * - * Uses `getPackageType` to determine if `type` is set to 'module. If so loads '.js' files as ESM otherwise uses - * a bare require to load as CJS. Also loads '.mjs' files as ESM. - * - * Uses dynamic import to load ESM source or require for CommonJS. - * - * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that - * provides a consistent stack trace and info. - * - * @param {IConfig|IPlugin} config - Oclif config or plugin config. - * @param {string} modulePath - NPM module name or file path to load. - * - * @returns {Promise<*>} The entire ESM module from dynamic import or CJS module by require. - */ - static async load(config: IConfig|IPlugin, modulePath: string): Promise { - let filePath: string | undefined - let isESM: boolean | undefined - try { - ({isESM, filePath} = ModuleLoader.resolvePath(config, modulePath)) - // It is important to await on import to catch the error code. - return isESM ? await import(url.pathToFileURL(filePath).href) : require(filePath) - } catch (error: any) { - if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { - throw new ModuleLoadError(`${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}`) - } +export async function load(config: IConfig|IPlugin, modulePath: string): Promise { + let filePath: string | undefined + let isESM: boolean | undefined + try { + ({isESM, filePath} = resolvePath(config, modulePath)) + return isESM ? await import(pathToFileURL(filePath).href) : require(filePath) + } catch (error: any) { + if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { + throw new ModuleLoadError(`${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}`) + } - throw error + throw error + } +} + +/** + * Loads a module and returns an object with the module and data about the module. + * + * Uses `getPackageType` to determine if `type` is set to `module`. If so loads '.js' files as ESM otherwise uses + * a bare require to load as CJS. Also loads '.mjs' files as ESM. + * + * Uses dynamic import to load ESM source or require for CommonJS. + * + * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that + * provides a consistent stack trace and info. + * + * @param {IConfig|IPlugin} config - Oclif config or plugin config. + * @param {string} modulePath - NPM module name or file path to load. + * + * @returns {Promise<{isESM: boolean, module: *, filePath: string}>} An object with the loaded module & data including + * file path and whether the module is ESM. + */ +export async function loadWithData(config: IConfig|IPlugin, modulePath: string): Promise<{isESM: boolean; module: any; filePath: string}> { + let filePath: string | undefined + let isESM: boolean | undefined + try { + ({isESM, filePath} = resolvePath(config, modulePath)) + const module = isESM ? await import(pathToFileURL(filePath).href) : require(filePath) + return {isESM, module, filePath} + } catch (error: any) { + if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { + throw new ModuleLoadError( + `${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}: ${error.message}`, + ) } + + throw error } +} - /** - * Loads a module and returns an object with the module and data about the module. - * - * Uses `getPackageType` to determine if `type` is set to `module`. If so loads '.js' files as ESM otherwise uses - * a bare require to load as CJS. Also loads '.mjs' files as ESM. - * - * Uses dynamic import to load ESM source or require for CommonJS. - * - * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that - * provides a consistent stack trace and info. - * - * @param {IConfig|IPlugin} config - Oclif config or plugin config. - * @param {string} modulePath - NPM module name or file path to load. - * - * @returns {Promise<{isESM: boolean, module: *, filePath: string}>} An object with the loaded module & data including - * file path and whether the module is ESM. - */ - static async loadWithData(config: IConfig|IPlugin, modulePath: string): Promise<{isESM: boolean; module: any; filePath: string}> { - let filePath: string | undefined - let isESM: boolean | undefined - try { - ({isESM, filePath} = ModuleLoader.resolvePath(config, modulePath)) - const module = isESM ? await import(url.pathToFileURL(filePath).href) : require(filePath) - return {isESM, module, filePath} - } catch (error: any) { - if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { - throw new ModuleLoadError( - `${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}: ${error.message}`, - ) - } +/** + * Loads a module and returns an object with the module and data about the module. + * + * Uses cached `isESM` and `relativePath` in plugin manifest to determine if dynamic import (isESM = true) + * or require (isESM = false | undefined) should be used. + * + * A unique error, ModuleLoadError, combines both CJS and ESM loader module not found errors into a single error that + * provides a consistent stack trace and info. + * + * @param {Command.Cached} cached - Cached command data from plugin manifest. + * @param {string} modulePath - NPM module name or file path to load. + * + * @returns {Promise<{isESM: boolean, module: *, filePath: string}>} An object with the loaded module & data including + * file path and whether the module is ESM. + */ +export async function loadWithDataFromManifest(cached: Command.Cached, modulePath: string): Promise<{isESM: boolean; module: any; filePath: string}> { + const {isESM, relativePath, id} = cached + if (!relativePath) { + throw new ModuleLoadError(`Cached command ${id} does not have a relative path`) + } + + if (isESM === undefined) { + throw new ModuleLoadError(`Cached command ${id} does not have the isESM property set`) + } - throw error + const filePath = join(modulePath, relativePath.join(sep)) + try { + const module = isESM ? await import(pathToFileURL(filePath).href) : require(filePath) + return {isESM, module, filePath} + } catch (error: any) { + if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { + throw new ModuleLoadError( + `${isESM ? 'import()' : 'require'} failed to load ${filePath || modulePath}: ${error.message}`, + ) } + + throw error } +} - /** +/** * For `.js` files uses `getPackageType` to determine if `type` is set to `module` in associated `package.json`. If * the `modulePath` provided ends in `.mjs` it is assumed to be ESM. * @@ -101,94 +131,96 @@ export default class ModuleLoader { * @returns {boolean} The modulePath is an ES Module. * @see https://www.npmjs.com/package/get-package-type */ - static isPathModule(filePath: string): boolean { - const extension = path.extname(filePath).toLowerCase() - - switch (extension) { - case '.js': - case '.jsx': - case '.ts': - case '.tsx': - return getPackageType.sync(filePath) === 'module' - - case '.mjs': - case '.mts': - return true - - default: - return false - } +export function isPathModule(filePath: string): boolean { + const extension = extname(filePath).toLowerCase() + + switch (extension) { + case '.js': + case '.jsx': + case '.ts': + case '.tsx': { + return getPackageType.sync(filePath) === 'module' } - /** - * Resolves a modulePath first by `require.resolve` to allow Node to resolve an actual module. If this fails then - * the `modulePath` is resolved from the root of the provided config. `Config.tsPath` is used for initial resolution. - * If this file path does not exist then several extensions are tried from `s_EXTENSIONS` in order: '.js', '.mjs', - * '.cjs'. After a file path has been selected `isPathModule` is used to determine if the file is an ES Module. - * - * @param {IConfig|IPlugin} config - Oclif config or plugin config. - * @param {string} modulePath - File path to load. - * - * @returns {{isESM: boolean, filePath: string}} An object including file path and whether the module is ESM. - */ - static resolvePath(config: IConfig|IPlugin, modulePath: string): {isESM: boolean; filePath: string} { - let isESM: boolean - let filePath: string | undefined - - try { - filePath = require.resolve(modulePath) - isESM = ModuleLoader.isPathModule(filePath) - } catch { - filePath = (isPlugin(config) ? tsPath(config.root, modulePath, config) : tsPath(config.root, modulePath)) ?? modulePath - - let fileExists = false - let isDirectory = false - if (existsSync(filePath)) { - fileExists = true - try { - if (lstatSync(filePath)?.isDirectory?.()) { - fileExists = false - isDirectory = true - } - } catch {} - } + case '.mjs': + case '.mts': { + return true + } - if (!fileExists) { - // Try all supported extensions. - let foundPath = ModuleLoader.findFile(filePath) - if (!foundPath && isDirectory) { - // Since filePath is a directory, try looking for index file. - foundPath = ModuleLoader.findFile(path.join(filePath, 'index')) - } + default: { + return false + } + } +} - if (foundPath) { - filePath = foundPath +/** + * Resolves a modulePath first by `require.resolve` to allow Node to resolve an actual module. If this fails then + * the `modulePath` is resolved from the root of the provided config. `Config.tsPath` is used for initial resolution. + * If this file path does not exist then several extensions are tried from `s_EXTENSIONS` in order: '.js', '.mjs', + * '.cjs'. After a file path has been selected `isPathModule` is used to determine if the file is an ES Module. + * + * @param {IConfig|IPlugin} config - Oclif config or plugin config. + * @param {string} modulePath - File path to load. + * + * @returns {{isESM: boolean, filePath: string}} An object including file path and whether the module is ESM. + */ +function resolvePath(config: IConfig|IPlugin, modulePath: string): {isESM: boolean; filePath: string} { + let isESM: boolean + let filePath: string | undefined + + try { + filePath = require.resolve(modulePath) + isESM = isPathModule(filePath) + } catch { + filePath = (isPlugin(config) ? tsPath(config.root, modulePath, config) : tsPath(config.root, modulePath)) ?? modulePath + + let fileExists = false + let isDirectory = false + if (existsSync(filePath)) { + fileExists = true + try { + if (lstatSync(filePath)?.isDirectory?.()) { + fileExists = false + isDirectory = true } + } catch {} + } + + if (!fileExists) { + // Try all supported extensions. + let foundPath = findFile(filePath) + if (!foundPath && isDirectory) { + // Since filePath is a directory, try looking for index file. + foundPath = findFile(join(filePath, 'index')) } - isESM = ModuleLoader.isPathModule(filePath) + if (foundPath) { + filePath = foundPath + } } - return {isESM, filePath} + isESM = isPathModule(filePath) } - /** - * Try adding the different extensions from `s_EXTENSIONS` to find the file. - * - * @param {string} filePath - File path to load. - * - * @returns {string | null} Modified file path including extension or null if file is not found. - */ - static findFile(filePath: string) : string | null { - // eslint-disable-next-line camelcase - for (const extension of s_EXTENSIONS) { - const testPath = `${filePath}${extension}` + return {isESM, filePath} +} - if (existsSync(testPath)) { - return testPath - } - } +/** + * Try adding the different extensions from `s_EXTENSIONS` to find the file. + * + * @param {string} filePath - File path to load. + * + * @returns {string | null} Modified file path including extension or null if file is not found. + */ +function findFile(filePath: string) : string | null { + // eslint-disable-next-line camelcase + for (const extension of s_EXTENSIONS) { + const testPath = `${filePath}${extension}` - return null + if (existsSync(testPath)) { + return testPath + } } + + return null } diff --git a/src/parser/errors.ts b/src/parser/errors.ts index d6a832159..499120dbf 100644 --- a/src/parser/errors.ts +++ b/src/parser/errors.ts @@ -1,11 +1,10 @@ +import {Arg, ArgInput, CLIParseErrorOptions} from '../interfaces/parser' +import {Flag, OptionFlag} from '../interfaces' import {CLIError} from '../errors' - +import chalk from 'chalk' import {flagUsages} from './help' import {renderList} from '../cli-ux/list' -import * as chalk from 'chalk' -import {OptionFlag, Flag} from '../interfaces' -import {uniq} from '../config/util' -import {Arg, ArgInput, CLIParseErrorOptions} from '../interfaces/parser' +import {uniq} from '../util' export {CLIError} from '../errors' diff --git a/src/parser/help.ts b/src/parser/help.ts index 9480dc475..7b6bfd396 100644 --- a/src/parser/help.ts +++ b/src/parser/help.ts @@ -1,5 +1,5 @@ -import * as chalk from 'chalk' import {Flag, FlagUsageOptions} from '../interfaces/parser' +import chalk from 'chalk' import {sortBy} from '../util' export function flagUsage(flag: Flag, options: FlagUsageOptions = {}): [string, string | undefined] { diff --git a/src/parser/index.ts b/src/parser/index.ts index 422af2846..95a62791e 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,6 +1,7 @@ +import {ArgInput, FlagInput, Input, OutputArgs, OutputFlags, ParserOutput} from '../interfaces/parser' import {Parser} from './parse' import {validate} from './validate' -import {ArgInput, FlagInput, Input, OutputArgs, OutputFlags, ParserOutput} from '../interfaces/parser' + export {flagUsages} from './help' export async function parse< diff --git a/src/parser/parse.ts b/src/parser/parse.ts index cc8d4a772..6376ec80c 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -4,7 +4,7 @@ import { ArgParserContext, ArgToken, BooleanFlag, - CompletableFlag, + Flag, FlagParserContext, FlagToken, Metadata, @@ -17,15 +17,18 @@ import { ParserOutput, ParsingToken, } from '../interfaces/parser' -import * as readline from 'readline' import {isTruthy, last, pickBy} from '../util' +import {createInterface} from 'node:readline' let debug: any try { - // eslint-disable-next-line no-negated-condition - debug = process.env.CLI_FLAGS_DEBUG !== '1' ? () => {} : require('debug')('../parser') + debug = process.env.CLI_FLAGS_DEBUG === '1' ? require('debug')('../parser') : () => { + // noop + } } catch { - debug = () => {} + debug = () => { + // noop + } } const readStdin = async (): Promise => { @@ -42,10 +45,10 @@ const readStdin = async (): Promise => { return new Promise(resolve => { let result = '' const ac = new AbortController() - const signal = ac.signal + const {signal} = ac const timeout = setTimeout(() => ac.abort(), 100) - const rl = readline.createInterface({ + const rl = createInterface({ input: stdin, output: stdout, terminal: false, @@ -74,6 +77,12 @@ function isNegativeNumber(input: string): boolean { return /^-\d/g.test(input) } +const validateOptions = (flag: OptionFlag, input: string): string => { + if (flag.options && !flag.options.includes(input)) + throw new FlagInvalidOptionError(flag, input) + return input +} + export class Parser, BFlags extends OutputFlags, TArgs extends OutputArgs> { private readonly argv: string[] @@ -91,47 +100,14 @@ export class Parser f.type === 'boolean') as any - this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap(flag => { - return (flag.aliases ?? []).map(a => [a, flag]) - })) + this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap(flag => ([...flag.aliases ?? [], ...flag.charAliases ?? []]).map(a => [a, flag]))) } public async parse(): Promise> { this._debugInput() - const findLongFlag = (arg: string):string | undefined => { - const name = arg.slice(2) - if (this.input.flags[name]) { - return name - } - - if (this.flagAliases[name]) { - return this.flagAliases[name].name - } - - if (arg.startsWith('--no-')) { - const flag = this.booleanFlags[arg.slice(5)] - if (flag && flag.allowNo) return flag.name - } - } - - const findShortFlag = ([_, char]: string):string | undefined => { - if (this.flagAliases[char]) { - return this.flagAliases[char].name - } - - return Object.keys(this.input.flags).find(k => (this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined)) - } - - const findFlag = (arg: string): { name?: string, isLong: boolean } => { - const isLong = arg.startsWith('--') - const short = isLong ? false : arg.startsWith('-') - const name = isLong ? findLongFlag(arg) : (short ? findShortFlag(arg) : undefined) - return {name, isLong} - } - const parseFlag = (arg: string): boolean => { - const {name, isLong} = findFlag(arg) + const {name, isLong} = this.findFlag(arg) if (!name) { const i = arg.indexOf('=') if (i !== -1) { @@ -150,15 +126,20 @@ export class Parser o.type === 'flag' && o.flag === name)) { + throw new CLIError(`Flag --${name} can only be specified once`) + } + this.currentFlag = flag const input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2) // if the value ends up being one of the command's flags, the user didn't provide an input - if ((typeof input !== 'string') || findFlag(input).name) { + if ((typeof input !== 'string') || this.findFlag(input).name) { throw new CLIError(`Flag --${name} expects a value`) } - this.raw.push({type: 'flag', flag: flag.name, input: input}) + this.raw.push({type: 'flag', flag: flag.name, input}) } else { this.raw.push({type: 'flag', flag: flag.name, input: arg}) // push the rest of the short characters back on the stack @@ -233,12 +214,6 @@ export class Parser { type ValueFunction = (fws: FlagWithStrategy, flags?: Record) => Promise - const validateOptions = (flag: OptionFlag, input: string): string => { - if (flag.options && !flag.options.includes(input)) - throw new FlagInvalidOptionError(flag, input) - return input - } - const parseFlagOrThrowError = async (input: any, flag: BooleanFlag | OptionFlag, context: ParserContext | undefined, token?: FlagToken) => { if (!flag.parse) return input @@ -292,6 +267,7 @@ export class Parser v.trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1')) .map(async v => parseFlagOrThrowError(v, i.inputFlag.flag, this.context, {...last(i.tokens) as FlagToken, input: v})), + // eslint-disable-next-line unicorn/no-await-expression-member )).map(v => validateOptions(i.inputFlag.flag as OptionFlag, v)), } } @@ -343,18 +319,19 @@ export class Parser Promise.resolve(isTruthy(process.env[i.inputFlag.flag.env as string] ?? 'false')), + valueFunction: async (i: FlagWithStrategy) => isTruthy(process.env[i.inputFlag.flag.env as string] ?? 'false'), } } } // no input, but flag has default value + // eslint-disable-next-line no-constant-binary-expression, valid-typeof if (typeof fws.inputFlag.flag.default !== undefined) { return { ...fws, metadata: {setFromDefault: true}, - valueFunction: typeof fws.inputFlag.flag.default === 'function' ? - (i: FlagWithStrategy, allFlags = {}) => fws.inputFlag.flag.default({options: i.inputFlag.flag, flags: allFlags}) : - async () => fws.inputFlag.flag.default, + valueFunction: typeof fws.inputFlag.flag.default === 'function' + ? (i: FlagWithStrategy, allFlags = {}) => fws.inputFlag.flag.default({options: i.inputFlag.flag, flags: allFlags}) + : async () => fws.inputFlag.flag.default, } } @@ -365,11 +342,11 @@ export class Parser { if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.defaultHelp) { return { - ...fws, helpFunction: typeof fws.inputFlag.flag.defaultHelp === 'function' ? + ...fws, helpFunction: typeof fws.inputFlag.flag.defaultHelp === 'function' // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there - (i: FlagWithStrategy, flags: Record, ...context) => i.inputFlag.flag.defaultHelp({options: i.inputFlag, flags}, ...context) : + ? (i: FlagWithStrategy, flags: Record, ...context) => i.inputFlag.flag.defaultHelp({options: i.inputFlag, flags}, ...context) // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there - (i: FlagWithStrategy) => i.inputFlag.flag.defaultHelp, + : (i: FlagWithStrategy) => i.inputFlag.flag.defaultHelp, } } @@ -400,12 +377,12 @@ export class Parser Object.fromEntries( fwsArray.filter(fws => fws.value !== undefined) .map(fws => [fws.inputFlag.name, fws.value]), - ) + ) as TFlags & BFlags & { json: boolean | undefined } type FlagWithStrategy = { inputFlag: { name: string, - flag: CompletableFlag + flag: Flag } tokens?: FlagToken[], valueFunction?: ValueFunction; @@ -434,7 +411,6 @@ export class Parser typeof fws.helpFunction === 'function')) ? await addDefaultHelp(flagsWithAllValues) : flagsWithAllValues return { - // @ts-ignore original version returned an any. Not sure how to get to the return type for `flags` prop flags: fwsArrayToObject(finalFlags), metadata: {flags: Object.fromEntries(finalFlags.filter(fws => fws.metadata).map(fws => [fws.inputFlag.name, fws.metadata as MetadataFlag]))}, } @@ -488,7 +464,7 @@ export class Parser (this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined)) + } + + private findFlag(arg: string): { name?: string, isLong: boolean } { + const isLong = arg.startsWith('--') + const short = isLong ? false : arg.startsWith('-') + const name = isLong ? this.findLongFlag(arg) : (short ? this.findShortFlag(arg) : undefined) + return {name, isLong} + } } diff --git a/src/parser/validate.ts b/src/parser/validate.ts index fe5c342f4..0f7d0ea02 100644 --- a/src/parser/validate.ts +++ b/src/parser/validate.ts @@ -1,13 +1,13 @@ +import {Arg, Flag, FlagRelationship, ParserInput, ParserOutput} from '../interfaces/parser' import { + FailedFlagValidationError, InvalidArgsSpecError, + NonExistentFlagsError, RequiredArgsError, - Validation, UnexpectedArgsError, - FailedFlagValidationError, - NonExistentFlagsError, + Validation, } from './errors' -import {Arg, CompletableFlag, Flag, FlagRelationship, ParserInput, ParserOutput} from '../interfaces/parser' -import {uniq} from '../config/util' +import {uniq} from '../util' export async function validate(parse: { input: ParserInput; @@ -172,18 +172,25 @@ export async function validate(parse: { return {...base, status: 'success'} } - function validateRelationships(name: string, flag: CompletableFlag): Promise[] { + function validateRelationships(name: string, flag: Flag): Promise[] { return ((flag.relationships ?? []).map(relationship => { switch (relationship.type) { - case 'all': + case 'all': { return validateDependsOn(name, relationship.flags) - case 'some': + } + + case 'some': { return validateSome(name, relationship.flags) - case 'none': + } + + case 'none': { return validateExclusive(name, relationship.flags) - default: + } + + default: { throw new Error(`Unknown relationship type: ${relationship.type}`) } + } })) } diff --git a/src/performance.ts b/src/performance.ts index 9705f88e0..284eb2af8 100644 --- a/src/performance.ts +++ b/src/performance.ts @@ -1,4 +1,4 @@ -import {PerformanceObserver, performance} from 'perf_hooks' +import {PerformanceObserver, performance} from 'node:perf_hooks' import {settings} from './settings' type Details = Record @@ -16,7 +16,8 @@ type PerfHighlights = { runTime: number; initTime: number; commandLoadTime: number; - pluginLoadTimes: Record; + commandRunTime: number; + pluginLoadTimes: Record; corePluginsLoadTime: number; userPluginsLoadTime: number; linkedPluginsLoadTime: number; @@ -24,10 +25,10 @@ type PerfHighlights = { } class Marker { - public module: string; - public method: string; - public scope: string; - public stopped = false; + public module: string + public method: string + public scope: string + public stopped = false private startMarker: string private stopMarker: string @@ -58,7 +59,7 @@ class Marker { } } -export default class Performance { +export class Performance { private static markers: Record = {} private static _results: PerfResult[] = [] private static _highlights: PerfHighlights @@ -140,7 +141,7 @@ export default class Performance { const pluginLoadTimes = Object.fromEntries(Performance.results .filter(({name}) => name.startsWith('plugin.load#')) .sort((a, b) => b.duration - a.duration) - .map(({scope, duration}) => [scope, duration])) + .map(({scope, duration, details}) => [scope, {duration, details}])) const hookRunTimes = Performance.results .filter(({name}) => name.startsWith('config.runHook#')) @@ -163,10 +164,13 @@ export default class Performance { .sort((a, b) => b.duration - a.duration) .map(({scope, duration}) => [scope, duration])) + const commandRunTime = Performance.results.find(({name}) => name.startsWith('config.runCommand#'))?.duration ?? 0 + Performance._highlights = { configLoadTime: Performance.getResult('config.load')?.duration ?? 0, runTime: Performance.getResult('main.run')?.duration ?? 0, initTime: Performance.getResult('main.run#init')?.duration ?? 0, + commandRunTime, commandLoadTime, pluginLoadTimes, hookRunTimes, @@ -199,17 +203,21 @@ export default class Performance { if (!Performance.enabled) return const debug = require('debug')('perf') - + debug('Total Time: %sms', Performance.highlights.runTime.toFixed(4)) debug('Init Time: %sms', Performance.highlights.initTime.toFixed(4)) debug('Config Load Time: %sms', Performance.highlights.configLoadTime.toFixed(4)) - debug('Command Load Time: %sms', Performance.highlights.commandLoadTime.toFixed(4)) - debug('Execution Time: %sms', Performance.highlights.runTime.toFixed(4)) + debug(' โ€ข Plugins Load Time: %sms', Performance.getResult('config.loadAllPlugins')?.duration.toFixed(4) ?? 0) + debug(' โ€ข Commands Load Time: %sms', Performance.getResult('config.loadAllCommands')?.duration.toFixed(4) ?? 0) debug('Core Plugin Load Time: %sms', Performance.highlights.corePluginsLoadTime.toFixed(4)) debug('User Plugin Load Time: %sms', Performance.highlights.userPluginsLoadTime.toFixed(4)) debug('Linked Plugin Load Time: %sms', Performance.highlights.linkedPluginsLoadTime.toFixed(4)) debug('Plugin Load Times:') - for (const [plugin, duration] of Object.entries(Performance.highlights.pluginLoadTimes)) { - debug(` ${plugin}: ${duration.toFixed(4)}ms`) + for (const [plugin, result] of Object.entries(Performance.highlights.pluginLoadTimes)) { + if (result.details.hasManifest) { + debug(` ${plugin}: ${result.duration.toFixed(4)}ms`) + } else { + debug(` ${plugin}: ${result.duration.toFixed(4)}ms (no manifest!)`) + } } debug('Hook Run Times:') @@ -219,5 +227,8 @@ export default class Performance { debug(` ${plugin}: ${duration.toFixed(4)}ms`) } } + + debug('Command Load Time: %sms', Performance.highlights.commandLoadTime.toFixed(4)) + debug('Command Run Time: %sms', Performance.highlights.commandRunTime.toFixed(4)) } } diff --git a/src/screen.ts b/src/screen.ts index 68b1e6fbb..529513d9c 100644 --- a/src/screen.ts +++ b/src/screen.ts @@ -1,4 +1,4 @@ -import {stdout, stderr} from './cli-ux/stream' +import {stderr, stdout} from './cli-ux/stream' import {settings} from './settings' function termwidth(stream: any): number { diff --git a/src/util.ts b/src/util.ts index 224089099..a5c0eab5b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,7 +1,11 @@ -import * as fs from 'fs' -import {join} from 'path' -import {Command} from './command' +import {access, stat} from 'node:fs/promises' +import {homedir, platform} from 'node:os' +import {readFile, readFileSync} from 'node:fs' import {ArgInput} from './interfaces/parser' +import {Command} from './command' +import {join} from 'node:path' + +const debug = require('debug') export function pickBy>(obj: T, fn: (i: T[keyof T]) => boolean): Partial { return Object.entries(obj) @@ -12,6 +16,7 @@ export function pickBy(a: (T | undefined)[]): T[] { + // eslint-disable-next-line unicorn/prefer-native-coercion-functions return a.filter((a): a is T => Boolean(a)) } @@ -24,28 +29,28 @@ export function uniqBy(arr: T[], fn: (cur: T) => any): T[] { export function last(arr?: T[]): T | undefined { if (!arr) return - return arr.slice(-1)[0] + return arr.at(-1) } type SortTypes = string | number | undefined | boolean -export function sortBy(arr: T[], fn: (i: T) => SortTypes | SortTypes[]): T[] { - function compare(a: SortTypes | SortTypes[], b: SortTypes | SortTypes[]): number { - a = a === undefined ? 0 : a - b = b === undefined ? 0 : b - - if (Array.isArray(a) && Array.isArray(b)) { - if (a.length === 0 && b.length === 0) return 0 - const diff = compare(a[0], b[0]) - if (diff !== 0) return diff - return compare(a.slice(1), b.slice(1)) - } - - if (a < b) return -1 - if (a > b) return 1 - return 0 +function compare(a: SortTypes | SortTypes[], b: SortTypes | SortTypes[]): number { + a = a === undefined ? 0 : a + b = b === undefined ? 0 : b + + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length === 0 && b.length === 0) return 0 + const diff = compare(a[0], b[0]) + if (diff !== 0) return diff + return compare(a.slice(1), b.slice(1)) } + if (a < b) return -1 + if (a > b) return 1 + return 0 +} + +export function sortBy(arr: T[], fn: (i: T) => SortTypes | SortTypes[]): T[] { return arr.sort((a, b) => compare(fn(a), fn(b))) } @@ -78,12 +83,22 @@ export function capitalize(s: string): string { return s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : '' } +export async function exists(path: string): Promise { + try { + await access(path) + return true + } catch { + return false + } +} + export const dirExists = async (input: string): Promise => { - if (!fs.existsSync(input)) { + if (!await exists(input)) { throw new Error(`No directory found at ${input}`) } - if (!(await fs.promises.stat(input)).isDirectory()) { + const fileStat = await stat(input) + if (!fileStat.isDirectory()) { throw new Error(`${input} exists but is not a directory`) } @@ -91,11 +106,12 @@ export const dirExists = async (input: string): Promise => { } export const fileExists = async (input: string): Promise => { - if (!fs.existsSync(input)) { + if (!await exists(input)) { throw new Error(`No file found at ${input}`) } - if (!(await fs.promises.stat(input)).isFile()) { + const fileStat = await stat(input) + if (!fileStat.isFile()) { throw new Error(`${input} exists but is not a file`) } @@ -111,7 +127,7 @@ export function isNotFalsy(input: string): boolean { } export function requireJson(...pathParts: string[]): T { - return JSON.parse(fs.readFileSync(join(...pathParts), 'utf8')) + return JSON.parse(readFileSync(join(...pathParts), 'utf8')) } /** @@ -122,7 +138,54 @@ export function requireJson(...pathParts: string[]): T { * @returns ArgInput */ export function ensureArgObject(args?: any[] | ArgInput | { [name: string]: Command.Arg.Cached}): ArgInput { - return (Array.isArray(args) ? (args ?? []).reduce((x, y) => { - return {...x, [y.name]: y} - }, {} as ArgInput) : args ?? {}) as ArgInput + return (Array.isArray(args) ? (args ?? []).reduce((x, y) => ({...x, [y.name]: y}), {} as ArgInput) : args ?? {}) as ArgInput +} + +export function uniq(arr: T[]): T[] { + return [...new Set(arr)].sort() +} + +/** + * Call os.homedir() and return the result + * + * Wrapping this allows us to stub these in tests since os.homedir() is + * non-configurable and non-writable. + * + * @returns The user's home directory + */ +export function getHomeDir(): string { + return homedir() +} + +/** + * Call os.platform() and return the result + * + * Wrapping this allows us to stub these in tests since os.platform() is + * non-configurable and non-writable. + * + * @returns The process' platform + */ +export function getPlatform(): NodeJS.Platform { + return platform() +} + +export function readJson(path: string): Promise { + debug('config')('readJson %s', path) + return new Promise((resolve, reject) => { + readFile(path, 'utf8', (err: any, d: any) => { + try { + if (err) reject(err) + else resolve(JSON.parse(d) as T) + } catch (error: any) { + reject(error) + } + }) + }) +} + +export function readJsonSync(path: string, parse: false): string +export function readJsonSync(path: string, parse?: true): T +export function readJsonSync(path: string, parse = true): T | string { + const contents = readFileSync(path, 'utf8') + return parse ? JSON.parse(contents) as T : contents } diff --git a/test/.eslintrc.json b/test/.eslintrc.json new file mode 100644 index 000000000..43284ce5e --- /dev/null +++ b/test/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": "../.eslintrc.json", + "rules": { + "unicorn/consistent-function-scoping": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/ban-ts-ignore": "off", + "@typescript-eslint/ban-ts-comment": "off", + "sort-imports": "off" + } +} diff --git a/test/cli-ux/export.test.ts b/test/cli-ux/export.test.ts index 4a50da411..4a62835b7 100644 --- a/test/cli-ux/export.test.ts +++ b/test/cli-ux/export.test.ts @@ -1,4 +1,4 @@ -import {ux, Table, IPromptOptions, Config, ActionBase, ExitError} from '../../src/cli-ux' +import {ActionBase, Config, ExitError, IPromptOptions, Table, ux} from '../../src/cli-ux' import {expect} from 'chai' type MyColumns = Record diff --git a/test/cli-ux/fancy.ts b/test/cli-ux/fancy.ts index 74d8c174f..f6709c0d3 100644 --- a/test/cli-ux/fancy.ts +++ b/test/cli-ux/fancy.ts @@ -1,14 +1,9 @@ -import {expect, fancy as base, FancyTypes} from 'fancy-test' +import {fancy as base} from 'fancy-test' import {rm} from 'node:fs/promises' import {join} from 'node:path' import {ux} from '../../src/cli-ux' -export { - expect, - FancyTypes, -} - let count = 0 export const fancy = base @@ -19,6 +14,9 @@ export const fancy = base const chalk = require('chalk') chalk.level = 0 }) +// eslint-disable-next-line unicorn/prefer-top-level-await .finally(async () => { await ux.done() }) + +export {FancyTypes, expect} from 'fancy-test' diff --git a/test/cli-ux/prompt.test.ts b/test/cli-ux/prompt.test.ts index fd8c6815c..59ae25ce6 100644 --- a/test/cli-ux/prompt.test.ts +++ b/test/cli-ux/prompt.test.ts @@ -1,6 +1,6 @@ import * as chai from 'chai' -const expect = chai.expect +const {expect} = chai import {ux} from '../../src/cli-ux' diff --git a/test/cli-ux/styled/header.test.ts b/test/cli-ux/styled/header.test.ts index 65fb7146b..92047983f 100644 --- a/test/cli-ux/styled/header.test.ts +++ b/test/cli-ux/styled/header.test.ts @@ -1,7 +1,7 @@ import {expect} from 'chai' import {stdout} from '../../../src' -import {stub, SinonStub} from 'sinon' +import {SinonStub, stub} from 'sinon' import {ux} from '../../../src/cli-ux' describe('styled/header', () => { diff --git a/test/command/command.test.ts b/test/command/command.test.ts index 4b2cae5d3..ddbad21bb 100644 --- a/test/command/command.test.ts +++ b/test/command/command.test.ts @@ -31,11 +31,11 @@ describe('command', () => { fancy .do(async () => { class Command extends Base { - static description = 'test command' + static description = 'test command' - async run() { - return 101 - } + async run() { + return 101 + } } expect(await Command.run([])).to.equal(101) @@ -73,45 +73,45 @@ describe('command', () => { fancy .do(async () => { class C extends Command { - static id = 'foo:bar' - static title = 'cmd title' - static type = 'mytype' - static usage = ['$ usage'] - static description = 'test command' - static aliases = ['alias1', 'alias2'] - static hidden = true - // @ts-ignore - static flags = { - flaga: Flags.boolean(), - flagb: Flags.string({ - char: 'b', - hidden: true, - required: false, - description: 'flagb desc', - options: ['a', 'b'], - default: async () => 'a', - }), - flagc: Flags.integer({ - char: 'c', - min: 1, - max: 10, - required: false, - description: 'flagc desc', - options: ['a', 'b'], - // @ts-expect-error: context is any - default: async context => context.options.min + 1, - }), - } - - static args = { - arg1: Args.string({ - description: 'arg1 desc', - required: true, - hidden: false, - options: ['af', 'b'], - default: async () => 'a', - }), - } + static id = 'foo:bar' + static title = 'cmd title' + static type = 'mytype' + static usage = ['$ usage'] + static description = 'test command' + static aliases = ['alias1', 'alias2'] + static hidden = true + // @ts-ignore + static flags = { + flaga: Flags.boolean(), + flagb: Flags.string({ + char: 'b', + hidden: true, + required: false, + description: 'flagb desc', + options: ['a', 'b'], + default: async () => 'a', + }), + flagc: Flags.integer({ + char: 'c', + min: 1, + max: 10, + required: false, + description: 'flagc desc', + options: ['a', 'b'], + // @ts-expect-error: context is any + default: async context => context.options.min + 1, + }), + } + + static args = { + arg1: Args.string({ + description: 'arg1 desc', + required: true, + hidden: false, + options: ['af', 'b'], + default: async () => 'a', + }), + } } const c = await toCached(C, undefined, false) @@ -137,6 +137,7 @@ describe('command', () => { flaga: { aliases: undefined, char: undefined, + charAliases: undefined, description: undefined, dependsOn: undefined, deprecateAliases: undefined, @@ -157,6 +158,7 @@ describe('command', () => { flagb: { aliases: undefined, char: 'b', + charAliases: undefined, description: 'flagb desc', dependsOn: undefined, deprecateAliases: undefined, @@ -180,6 +182,7 @@ describe('command', () => { flagc: { aliases: undefined, char: 'c', + charAliases: undefined, default: 2, delimiter: undefined, dependsOn: undefined, @@ -273,14 +276,14 @@ describe('command', () => { .stdout() .it('has a flag', async ctx => { class CMD extends Base { - static flags = { - foo: Flags.string(), - } - - async run() { - const {flags} = await this.parse(CMD) - this.log(flags.foo) - } + static flags = { + foo: Flags.string(), + } + + async run() { + const {flags} = await this.parse(CMD) + this.log(flags.foo) + } } await CMD.run(['--foo=bar']) @@ -370,20 +373,20 @@ describe('command', () => { .stderr() .do(async () => { class CMD extends Command { - static flags = { - name: Flags.string({ - deprecated: { - to: '--full-name', - version: '2.0.0', - }, - }), - force: Flags.boolean(), - } - - async run() { - await this.parse(CMD) - this.log('running command') - } + static flags = { + name: Flags.string({ + deprecated: { + to: '--full-name', + version: '2.0.0', + }, + }), + force: Flags.boolean(), + } + + async run() { + await this.parse(CMD) + this.log('running command') + } } await CMD.run(['--name', 'astro']) @@ -398,20 +401,20 @@ describe('command', () => { .stderr() .do(async () => { class CMD extends Command { - static flags = { - name: Flags.string({ - deprecated: { - to: '--full-name', - version: '2.0.0', - }, - }), - force: Flags.boolean(), - } - - async run() { - await this.parse(CMD) - this.log('running command') - } + static flags = { + name: Flags.string({ + deprecated: { + to: '--full-name', + version: '2.0.0', + }, + }), + force: Flags.boolean(), + } + + async run() { + await this.parse(CMD) + this.log('running command') + } } await CMD.run(['--force']) @@ -426,12 +429,12 @@ describe('command', () => { .stderr() .do(async () => { class CMD extends Command { - static id = 'my:command' - static state = 'deprecated' + static id = 'my:command' + static state = 'deprecated' - async run() { - this.log('running command') - } + async run() { + this.log('running command') + } } await CMD.run([]) @@ -446,16 +449,16 @@ describe('command', () => { .stderr() .do(async () => { class CMD extends Command { - static id = 'my:command' - static state = 'deprecated' - static deprecationOptions = { - version: '2.0.0', - to: 'my:other:command', - } - - async run() { - this.log('running command') - } + static id = 'my:command' + static state = 'deprecated' + static deprecationOptions = { + version: '2.0.0', + to: 'my:other:command', + } + + async run() { + this.log('running command') + } } await CMD.run([]) @@ -503,11 +506,11 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true + static enableJsonFlag = true - async run() { - this.log('not json output') - } + async run() { + this.log('not json output') + } } const cmd = new CMD([], {} as any) @@ -519,9 +522,9 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true + static enableJsonFlag = true - async run() {} + async run() {} } const cmd = new CMD(['--json'], {} as any) @@ -533,8 +536,8 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true - async run() {} + static enableJsonFlag = true + async run() {} } // mock a scopedEnvVar being set to JSON @@ -549,13 +552,13 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true - static '--' = true + static enableJsonFlag = true + static '--' = true - async run() { - const {flags} = await cmd.parse(CMD, ['--json']) - expect(flags.json).to.equal(true, 'json flag should be true') - } + async run() { + const {flags} = await cmd.parse(CMD, ['--json']) + expect(flags.json).to.equal(true, 'json flag should be true') + } } const cmd = new CMD(['--json'], {} as any) @@ -567,14 +570,17 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true - static '--' = true - - async run() { - const {flags} = await cmd.parse(CMD, ['--', '--json']) - expect(flags.json).to.equal(false, 'json flag should be false') - expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') - } + // static initialization block is required whenever using ES2022 + static { + this.enableJsonFlag = true + this['--'] = true + } + + async run() { + const {flags} = await cmd.parse(CMD, ['--', '--json']) + expect(flags.json).to.equal(false, 'json flag should be false') + expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + } } const cmd = new CMD(['--', '--json'], {} as any) @@ -586,13 +592,13 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true - static '--' = true + static enableJsonFlag = true + static '--' = true - async run() { - const {flags} = await cmd.parse(CMD, ['--foo', '--json']) - expect(flags.json).to.equal(true, 'json flag should be true') - } + async run() { + const {flags} = await cmd.parse(CMD, ['--foo', '--json']) + expect(flags.json).to.equal(true, 'json flag should be true') + } } const cmd = new CMD(['foo', '--json'], {} as any) @@ -604,14 +610,14 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true - static '--' = true - - async run() { - const {flags} = await cmd.parse(CMD, ['--foo', '--', '--json']) - expect(flags.json).to.equal(false, 'json flag should be false') - expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') - } + static enableJsonFlag = true + static '--' = true + + async run() { + const {flags} = await cmd.parse(CMD, ['--foo', '--', '--json']) + expect(flags.json).to.equal(false, 'json flag should be false') + expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + } } const cmd = new CMD(['--foo', '--', '--json'], {} as any) @@ -623,10 +629,10 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = true - static '--' = true + static enableJsonFlag = true + static '--' = true - async run() {} + async run() {} } const cmd = new CMD(['--json', '--'], {} as any) @@ -638,10 +644,10 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - static enableJsonFlag = false - static '--' = true + static enableJsonFlag = false + static '--' = true - async run() {} + async run() {} } const cmd = new CMD(['--json'], {} as any) diff --git a/test/command/helpers/test-help-in-src/src/test-help-plugin.ts b/test/command/helpers/test-help-in-src/src/test-help-plugin.ts index 721ee7a73..de25f5e50 100644 --- a/test/command/helpers/test-help-in-src/src/test-help-plugin.ts +++ b/test/command/helpers/test-help-in-src/src/test-help-plugin.ts @@ -1,10 +1,9 @@ -import {spy, SinonSpy} from 'sinon' -import {Interfaces, HelpBase} from '../../../../../src' +import {SinonSpy, spy} from 'sinon' +import {HelpBase, Interfaces} from '../../../../../src' export type TestHelpClassConfig = Interfaces.Config & { showCommandHelpSpy?: SinonSpy; showHelpSpy?: SinonSpy } export default class extends HelpBase { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types constructor(config: any, opts: any) { super(config, opts) config.showCommandHelpSpy = this.showCommandHelp diff --git a/test/command/main-esm.test.ts b/test/command/main-esm.test.ts index 9596116ce..d971a48aa 100644 --- a/test/command/main-esm.test.ts +++ b/test/command/main-esm.test.ts @@ -1,13 +1,13 @@ import {expect, fancy} from 'fancy-test' -import * as path from 'path' -import * as url from 'url' +import {resolve} from 'node:path' +import {pathToFileURL} from 'node:url' -import run from '../../src/main' +import {run} from '../../src/main' // This tests file URL / import.meta.url simulation. -const convertToFileURL = (filepath: string) => url.pathToFileURL(filepath).toString() +const convertToFileURL = (filepath: string) => pathToFileURL(filepath).toString() -let root = path.resolve(__dirname, '../../package.json') +let root = resolve(__dirname, '../../package.json') const pjson = require(root) const version = `@oclif/core/${pjson.version} ${process.platform}-${process.arch} node-${process.version}` @@ -16,7 +16,6 @@ root = convertToFileURL(root) describe('main-esm', () => { fancy .stdout() - .skip() // skip until oclif/test is on v3 .do(() => run(['plugins'], root)) .do((output: any) => expect(output.stdout).to.equal('No plugins installed.\n')) .it('runs plugins') @@ -50,7 +49,7 @@ COMMANDS fancy .stdout() - .do(() => run(['--help', 'foo'], convertToFileURL(path.resolve(__dirname, 'fixtures/esm/package.json')))) + .do(() => run(['--help', 'foo'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) .do((output: any) => expect(output.stdout).to.equal(`foo topic description USAGE @@ -67,7 +66,7 @@ COMMANDS fancy .stdout() - .do(() => run(['foo', 'bar', '--help'], convertToFileURL(path.resolve(__dirname, 'fixtures/esm/package.json')))) + .do(() => run(['foo', 'bar', '--help'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) .do((output: any) => expect(output.stdout).to.equal(`foo bar topic description USAGE @@ -82,13 +81,13 @@ COMMANDS fancy .stdout() - .do(() => run(['foo', 'baz'], convertToFileURL(path.resolve(__dirname, 'fixtures/esm/package.json')))) + .do(() => run(['foo', 'baz'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) .do((output: any) => expect(output.stdout).to.equal('running Baz\n')) .it('runs foo:baz with space separator') fancy .stdout() - .do(() => run(['foo', 'bar', 'succeed'], convertToFileURL(path.resolve(__dirname, 'fixtures/esm/package.json')))) + .do(() => run(['foo', 'bar', 'succeed'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) .do((output: any) => expect(output.stdout).to.equal('it works!\n')) .it('runs foo:bar:succeed with space separator') }) diff --git a/test/command/main.test.ts b/test/command/main.test.ts index 41116965d..1b9b45583 100644 --- a/test/command/main.test.ts +++ b/test/command/main.test.ts @@ -1,10 +1,10 @@ import {expect} from 'chai' -import * as path from 'path' -import {createSandbox, SinonSandbox, SinonStub} from 'sinon' +import {resolve} from 'node:path' +import {SinonSandbox, SinonStub, createSandbox} from 'sinon' import stripAnsi = require('strip-ansi') import {requireJson} from '../../src/util' -import run from '../../src/main' +import {run} from '../../src/main' import {Interfaces, stdout} from '../../src/index' const pjson = requireJson(__dirname, '..', '..', 'package.json') @@ -23,19 +23,18 @@ describe('main', () => { sandbox.restore() }) - // need to skip until the stdout change is merged and used in plugin-plugins - it.skip('should run plugins', async () => { - await run(['plugins'], path.resolve(__dirname, '../../package.json')) - expect(stdoutStub.firstCall.firstArg).to.equal('No plugins installed.\n') + it('should run plugins', async () => { + const result = await run(['plugins'], resolve(__dirname, '../../package.json')) + expect(result).to.deep.equal([]) }) it('should run version', async () => { - await run(['--version'], path.resolve(__dirname, '../../package.json')) + await run(['--version'], resolve(__dirname, '../../package.json')) expect(stdoutStub.firstCall.firstArg).to.equal(`${version}\n`) }) it('should run help', async () => { - await run(['--help'], path.resolve(__dirname, '../../package.json')) + await run(['--help'], resolve(__dirname, '../../package.json')) expect(stdoutStub.args.map(a => stripAnsi(a[0])).join('')).to.equal(`base library for oclif CLIs VERSION @@ -55,7 +54,7 @@ COMMANDS }) it('should show help for topics with spaces', async () => { - await run(['--help', 'foo'], path.resolve(__dirname, 'fixtures/typescript/package.json')) + await run(['--help', 'foo'], resolve(__dirname, 'fixtures/typescript/package.json')) expect(stdoutStub.args.map(a => stripAnsi(a[0])).join('')).to.equal(`foo topic description USAGE @@ -71,7 +70,7 @@ COMMANDS }) it('should run spaced topic help v2', async () => { - await run(['foo', 'bar', '--help'], path.resolve(__dirname, 'fixtures/typescript/package.json')) + await run(['foo', 'bar', '--help'], resolve(__dirname, 'fixtures/typescript/package.json')) expect(stdoutStub.args.map(a => stripAnsi(a[0])).join('')).to.equal(`foo bar topic description USAGE @@ -86,13 +85,13 @@ COMMANDS it('should run foo:baz with space separator', async () => { const consoleLogStub = sandbox.stub(console, 'log').returns() - await run(['foo', 'baz'], path.resolve(__dirname, 'fixtures/typescript/package.json')) + await run(['foo', 'baz'], resolve(__dirname, 'fixtures/typescript/package.json')) expect(consoleLogStub.firstCall.firstArg).to.equal('running Baz') }) it('should run foo:bar:succeed with space separator', async () => { const consoleLogStub = sandbox.stub(console, 'log').returns() - await run(['foo', 'bar', 'succeed'], path.resolve(__dirname, 'fixtures/typescript/package.json')) + await run(['foo', 'bar', 'succeed'], resolve(__dirname, 'fixtures/typescript/package.json')) expect(consoleLogStub.firstCall.firstArg).to.equal('it works!') }) }) diff --git a/test/config/config.flexible.test.ts b/test/config/config.flexible.test.ts index f8ea56f82..829b00463 100644 --- a/test/config/config.flexible.test.ts +++ b/test/config/config.flexible.test.ts @@ -1,6 +1,3 @@ -import * as os from 'os' -import * as path from 'path' - import {Config} from '../../src/config/config' import {Plugin as IPlugin} from '../../src/interfaces' @@ -8,6 +5,8 @@ import {expect, fancy} from './test' import {Flags, Interfaces} from '../../src' import {Command} from '../../src/command' import {getCommandIdPermutations} from '../../src/config/util' +import * as util from '../../src/util' +import {join} from 'node:path' interface Options { pjson?: any; @@ -45,13 +44,11 @@ describe('Config with flexible taxonomy', () => { let test = fancy .resetConfig() .env(env, {clear: true}) - .stub(os, 'homedir', () => path.join(homedir)) - .stub(os, 'platform', () => platform) + .stub(util, 'getHomeDir', stub => stub.returns(join(homedir))) + .stub(util, 'getPlatform', stub => stub.returns(platform)) const load = async (): Promise => {} - const findCommand = async (): Promise => { - return MyCommandClass - } + const findCommand = async (): Promise => MyCommandClass const commandPluginA: Command.Loadable = { strict: false, @@ -103,6 +100,7 @@ describe('Config with flexible taxonomy', () => { valid: true, tag: 'tag', moduleType: 'commonjs', + hasManifest: false, } const pluginB: IPlugin = { @@ -122,6 +120,7 @@ describe('Config with flexible taxonomy', () => { valid: true, tag: 'tag', moduleType: 'commonjs', + hasManifest: false, } const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) @@ -161,7 +160,7 @@ describe('Config with flexible taxonomy', () => { }) .it('has populated command permutation index', config => { // @ts-expect-error because private member - const commandPermutations = config.commandPermutations + const {commandPermutations} = config expect(commandPermutations.get('foo')).to.deep.equal(new Set(['foo:bar', 'foo:baz'])) expect(commandPermutations.get('foo:bar')).to.deep.equal(new Set(['foo:bar'])) expect(commandPermutations.get('bar')).to.deep.equal(new Set(['foo:bar'])) diff --git a/test/config/config.test.ts b/test/config/config.test.ts index bafbb852e..cd5aa9971 100644 --- a/test/config/config.test.ts +++ b/test/config/config.test.ts @@ -1,8 +1,7 @@ -import * as os from 'os' -import * as path from 'path' +import {join} from 'node:path' import {Plugin as IPlugin} from '../../src/interfaces' -import * as util from '../../src/config/util' +import * as util from '../../src/util' import {expect, fancy} from './test' import {Config, Interfaces} from '../../src' @@ -50,10 +49,9 @@ describe('Config', () => { let test = fancy .resetConfig() .env(env, {clear: true}) - .stub(os, 'homedir', () => path.join(homedir)) - .stub(os, 'platform', () => platform) - - if (pjson) test = test.stub(util, 'loadJSON', () => Promise.resolve(pjson)) + .stub(util, 'getHomeDir', stub => stub.returns(join(homedir))) + .stub(util, 'getPlatform', stub => stub.returns(platform)) + if (pjson) test = test.stub(util, 'readJson', stub => stub.resolves(pjson)) test = test.add('config', () => Config.load()) @@ -65,11 +63,10 @@ describe('Config', () => { // In order to allow prerelease branches to pass, we need to strip the prerelease // tag from the version and switch the channel to stable. // @ts-expect-error because readonly property - config.version = config.version.replace(/-beta\.\d/g, '') + config.version = config.version.replaceAll(/-beta\.\d/g, '') // @ts-expect-error because readonly property config.channel = 'stable' - // eslint-disable-next-line prefer-const let {ext, ...options} = extra options = { bin: 'oclif-cli', @@ -96,11 +93,11 @@ describe('Config', () => { describe('darwin', () => { testConfig() - .hasProperty('cacheDir', path.join('/my/home/Library/Caches/@oclif/core')) - .hasProperty('configDir', path.join('/my/home/.config/@oclif/core')) - .hasProperty('errlog', path.join('/my/home/Library/Caches/@oclif/core/error.log')) - .hasProperty('dataDir', path.join('/my/home/.local/share/@oclif/core')) - .hasProperty('home', path.join('/my/home')) + .hasProperty('cacheDir', join('/my/home/Library/Caches/@oclif/core')) + .hasProperty('configDir', join('/my/home/.config/@oclif/core')) + .hasProperty('errlog', join('/my/home/Library/Caches/@oclif/core/error.log')) + .hasProperty('dataDir', join('/my/home/.local/share/@oclif/core')) + .hasProperty('home', join('/my/home')) }) describe('binAliases', () => { @@ -160,11 +157,11 @@ describe('Config', () => { describe('linux', () => { testConfig({platform: 'linux'}) - .hasProperty('cacheDir', path.join('/my/home/.cache/@oclif/core')) - .hasProperty('configDir', path.join('/my/home/.config/@oclif/core')) - .hasProperty('errlog', path.join('/my/home/.cache/@oclif/core/error.log')) - .hasProperty('dataDir', path.join('/my/home/.local/share/@oclif/core')) - .hasProperty('home', path.join('/my/home')) + .hasProperty('cacheDir', join('/my/home/.cache/@oclif/core')) + .hasProperty('configDir', join('/my/home/.config/@oclif/core')) + .hasProperty('errlog', join('/my/home/.cache/@oclif/core/error.log')) + .hasProperty('dataDir', join('/my/home/.local/share/@oclif/core')) + .hasProperty('home', join('/my/home')) }) describe('win32', () => { @@ -172,11 +169,11 @@ describe('Config', () => { platform: 'win32', env: {LOCALAPPDATA: '/my/home/localappdata'}, }) - .hasProperty('cacheDir', path.join('/my/home/localappdata/@oclif\\core')) - .hasProperty('configDir', path.join('/my/home/localappdata/@oclif\\core')) - .hasProperty('errlog', path.join('/my/home/localappdata/@oclif\\core/error.log')) - .hasProperty('dataDir', path.join('/my/home/localappdata/@oclif\\core')) - .hasProperty('home', path.join('/my/home')) + .hasProperty('cacheDir', join('/my/home/localappdata/@oclif\\core')) + .hasProperty('configDir', join('/my/home/localappdata/@oclif\\core')) + .hasProperty('errlog', join('/my/home/localappdata/@oclif\\core/error.log')) + .hasProperty('dataDir', join('/my/home/localappdata/@oclif\\core')) + .hasProperty('home', join('/my/home')) }) describe('s3Key', () => { @@ -235,23 +232,21 @@ describe('Config', () => { types = [], }: Options = {}) => { class MyCommandClass extends Command { - _base = '' + _base = '' - aliases: string[] = [] + aliases: string[] = [] - hidden = false + hidden = false - id = 'foo:bar' + id = 'foo:bar' - run(): Promise { - return Promise.resolve() - } + run(): Promise { + return Promise.resolve() + } } const load = async (): Promise => {} - const findCommand = async (): Promise => { - return MyCommandClass - } + const findCommand = async (): Promise => MyCommandClass const commandPluginA: Command.Loadable = { strict: false, @@ -286,6 +281,7 @@ describe('Config', () => { valid: true, tag: 'tag', moduleType: 'commonjs', + hasManifest: false, } const pluginB: IPlugin = { @@ -305,15 +301,16 @@ describe('Config', () => { valid: true, tag: 'tag', moduleType: 'commonjs', + hasManifest: false, } const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) let test = fancy .resetConfig() .env(env, {clear: true}) - .stub(os, 'homedir', () => path.join(homedir)) - .stub(os, 'platform', () => platform) + .stub(util, 'getHomeDir', stub => stub.returns(join(homedir))) + .stub(util, 'getPlatform', stub => stub.returns(platform)) - if (pjson) test = test.stub(util, 'loadJSON', () => Promise.resolve(pjson)) + if (pjson) test = test.stub(util, 'readJson', stub => stub.resolves(pjson)) test = test.add('config', async () => { const config = await Config.load() config.plugins = plugins diff --git a/test/config/esm.test.ts b/test/config/esm.test.ts index 57bfc4709..2d3b1a0d2 100644 --- a/test/config/esm.test.ts +++ b/test/config/esm.test.ts @@ -1,12 +1,12 @@ -import * as url from 'url' -import * as path from 'path' +import * as url from 'node:url' +import {join, resolve} from 'node:path' import {Config} from '../../src/config' import {expect, fancy} from './test' -const root = path.resolve(__dirname, 'fixtures/esm') -const p = (p: string) => path.join(root, p) +const root = resolve(__dirname, 'fixtures/esm') +const p = (p: string) => join(root, p) // This tests file URL / import.meta.url simulation. const rootAsFileURL = url.pathToFileURL(root).toString() diff --git a/test/config/fixtures/typescript/src/hooks/postrun.ts b/test/config/fixtures/typescript/src/hooks/postrun.ts index e88ef9687..f78977809 100644 --- a/test/config/fixtures/typescript/src/hooks/postrun.ts +++ b/test/config/fixtures/typescript/src/hooks/postrun.ts @@ -1,4 +1,4 @@ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + export default function postrun(options: any): void { console.log('running ts postrun hook') if (options.Command.id === 'foo:bar:test-result') { diff --git a/test/config/help.config.test.ts b/test/config/help.config.test.ts index 469230102..3742c9455 100644 --- a/test/config/help.config.test.ts +++ b/test/config/help.config.test.ts @@ -1,5 +1,5 @@ -import * as url from 'url' -import * as path from 'path' +import {pathToFileURL} from 'node:url' +import {resolve} from 'node:path' import {Config} from '../../src/config' @@ -7,11 +7,11 @@ import {expect, fancy} from './test' import {getHelpFlagAdditions} from '../../src/help' import {helpAddition, versionAddition} from '../../src/main' -const root = path.resolve(__dirname, 'fixtures/help') +const root = resolve(__dirname, 'fixtures/help') // const p = (p: string) => path.join(root, p) // This tests file URL / import.meta.url simulation. -const rootAsFileURL = url.pathToFileURL(root).toString() +const rootAsFileURL = pathToFileURL(root).toString() const withConfig = fancy .add('config', () => Config.load(rootAsFileURL)) diff --git a/test/config/mixed-cjs-esm.test.ts b/test/config/mixed-cjs-esm.test.ts index 45053773f..411275fde 100644 --- a/test/config/mixed-cjs-esm.test.ts +++ b/test/config/mixed-cjs-esm.test.ts @@ -1,11 +1,11 @@ -import * as path from 'path' +import {join, resolve} from 'node:path' import {Config} from '../../src/config' import {expect, fancy} from './test' -const root = path.resolve(__dirname, 'fixtures/mixed-cjs-esm') -const p = (p: string) => path.join(root, p) +const root = resolve(__dirname, 'fixtures/mixed-cjs-esm') +const p = (p: string) => join(root, p) const withConfig = fancy .add('config', () => Config.load(root)) diff --git a/test/config/mixed-esm-cjs.test.ts b/test/config/mixed-esm-cjs.test.ts index c9f44daee..4d6ed3703 100644 --- a/test/config/mixed-esm-cjs.test.ts +++ b/test/config/mixed-esm-cjs.test.ts @@ -1,11 +1,11 @@ -import * as path from 'path' +import {join, resolve} from 'node:path' import {Config} from '../../src/config' import {expect, fancy} from './test' -const root = path.resolve(__dirname, 'fixtures/mixed-esm-cjs') -const p = (p: string) => path.join(root, p) +const root = resolve(__dirname, 'fixtures/mixed-esm-cjs') +const p = (p: string) => join(root, p) const withConfig = fancy .add('config', () => Config.load(root)) diff --git a/test/config/test.ts b/test/config/test.ts index a4ac4d66a..1e7866017 100644 --- a/test/config/test.ts +++ b/test/config/test.ts @@ -1,4 +1,4 @@ -import {expect, fancy as base, FancyTypes} from 'fancy-test' +import {fancy as base} from 'fancy-test' import {Interfaces} from '../../src' @@ -10,7 +10,4 @@ export const fancy = base }, })) -export { - expect, - FancyTypes, -} +export {FancyTypes, expect} from 'fancy-test' diff --git a/test/config/ts-node.test.ts b/test/config/ts-node.test.ts index 895c6e7fe..6742238e4 100644 --- a/test/config/ts-node.test.ts +++ b/test/config/ts-node.test.ts @@ -1,18 +1,19 @@ -import * as path from 'path' -import * as proxyquire from 'proxyquire' +import {join, resolve} from 'node:path' +import * as fs from 'node:fs' import * as tsNode from 'ts-node' +import {SinonSandbox, createSandbox} from 'sinon' import {Interfaces, settings} from '../../src' +import * as configTsNode from '../../src/config/ts-node' +import * as util from '../../src/util' +import {expect} from 'chai' -import {expect, fancy} from './test' - -const root = path.resolve(__dirname, 'fixtures/typescript') +const root = resolve(__dirname, 'fixtures/typescript') const tsSource = 'src/hooks/init.ts' // ts-node can load the file as a module (without ts) const tsModule = 'src/hooks/init' const jsCompiledModule = 'lib/hooks/init' const jsCompiled = 'lib/hooks/init.js' -let tsNodeRegisterCallArguments: any[] = [] // Typical root and out options of a typescript project const DEFAULT_TS_CONFIG: Interfaces.TSConfig = { @@ -22,73 +23,69 @@ const DEFAULT_TS_CONFIG: Interfaces.TSConfig = { }, } -const withMockTsConfig = (config: Interfaces.TSConfig = DEFAULT_TS_CONFIG) => { - const tsNodePlugin = proxyquire('../../src/config/ts-node', {fs: { - existsSync: () => true, - readFileSync: () => JSON.stringify(config), - }}) +describe('tsPath', () => { + let sandbox: SinonSandbox - // This prints "loadInterfaces.TSConfig unstubbed" not "loadInterfaces.TSConfig proxyquire"! - tsNodePlugin.tsPath('qwerty', 'asdf') + beforeEach(() => { + sandbox = createSandbox() + sandbox.stub(fs, 'existsSync').returns(true) + sandbox.stub(tsNode, 'register') + }) - return fancy - .add('tsNodePlugin', () => tsNodePlugin) - .stub(tsNode, 'register', ((arg: any) => { - tsNodeRegisterCallArguments.push(arg) - }) as unknown as () => void) - .finally(() => { - tsNodeRegisterCallArguments = [] + afterEach(() => { + sandbox.restore() + // Clear caches so that unit tests don't affect each other + // @ts-expect-error because TS_CONFIGS is not exported + // eslint-disable-next-line import/namespace + configTsNode.TS_CONFIGS = {} + // @ts-expect-error because REGISTERED is not exported + // eslint-disable-next-line import/namespace + configTsNode.REGISTERED = new Set() }) -} -describe('tsPath', () => { - withMockTsConfig() - .it('should resolve a .js file to ts src', (ctx: any) => { - const result = ctx.tsNodePlugin.tsPath(root, jsCompiled) - expect(result).to.equal(path.join(root, tsModule)) + it('should resolve a .js file to ts src', () => { + sandbox.stub(util, 'readJsonSync').returns(JSON.stringify(DEFAULT_TS_CONFIG)) + const result = configTsNode.tsPath(root, jsCompiled) + expect(result).to.equal(join(root, tsModule)) }) - withMockTsConfig() - .it('should resolve a module file to ts src', (ctx: any) => { - const result = ctx.tsNodePlugin.tsPath(root, jsCompiledModule) - expect(result).to.equal(path.join(root, tsModule)) + it('should resolve a module file to ts src', () => { + sandbox.stub(util, 'readJsonSync').returns(JSON.stringify(DEFAULT_TS_CONFIG)) + const result = configTsNode.tsPath(root, jsCompiledModule) + expect(result).to.equal(join(root, tsModule)) }) - withMockTsConfig() - .it('should resolve a .ts file', (ctx: any) => { - const result = ctx.tsNodePlugin.tsPath(root, tsSource) - expect(result).to.equal(path.join(root, tsSource)) + it('should resolve a .ts file', () => { + sandbox.stub(util, 'readJsonSync').returns(JSON.stringify(DEFAULT_TS_CONFIG)) + const result = configTsNode.tsPath(root, tsSource) + expect(result).to.equal(join(root, tsSource)) }) - withMockTsConfig({compilerOptions: {}}) - .it('should resolve .js with no rootDir or outDir', (ctx: any) => { - const result = ctx.tsNodePlugin.tsPath(root, jsCompiled) - expect(result).to.equal(path.join(root, jsCompiled)) + it('should resolve .js with no rootDir or outDir', () => { + sandbox.stub(util, 'readJsonSync').returns({compilerOptions: {}}) + const result = configTsNode.tsPath(root, jsCompiled) + expect(result).to.equal(join(root, jsCompiled)) }) - withMockTsConfig() - .do(() => { + it('should resolve to .ts file if enabled and prod', () => { + sandbox.stub(util, 'readJsonSync').returns(JSON.stringify(DEFAULT_TS_CONFIG)) settings.tsnodeEnabled = true + const originalNodeEnv = process.env.NODE_ENV delete process.env.NODE_ENV - }) - .finally(() => { + + const result = configTsNode.tsPath(root, jsCompiled) + expect(result).to.equal(join(root, tsModule)) + + process.env.NODE_ENV = originalNodeEnv delete settings.tsnodeEnabled - process.env.NODE_ENV = 'development' - }) - .it('should resolve to .ts file if enabled and prod', (ctx: any) => { - const result = ctx.tsNodePlugin.tsPath(root, jsCompiled) - expect(result).to.equal(path.join(root, tsModule)) }) - withMockTsConfig() - .do(() => { + it('should resolve to js if disabled', () => { + sandbox.stub(util, 'readJsonSync').returns(JSON.stringify(DEFAULT_TS_CONFIG)) settings.tsnodeEnabled = false - }) - .finally(() => { + const result = configTsNode.tsPath(root, jsCompiled) + expect(result).to.equal(join(root, jsCompiled)) + delete settings.tsnodeEnabled }) - .it('should resolve to js if disabled', (ctx: any) => { - const result = ctx.tsNodePlugin.tsPath(root, jsCompiled) - expect(result).to.equal(path.join(root, jsCompiled)) - }) }) diff --git a/test/config/typescript.test.ts b/test/config/typescript.test.ts index b8da2b69a..27d20c5cb 100644 --- a/test/config/typescript.test.ts +++ b/test/config/typescript.test.ts @@ -1,11 +1,11 @@ -import * as path from 'path' +import {join, resolve} from 'node:path' import {Config} from '../../src/config' import {expect, fancy} from './test' -const root = path.resolve(__dirname, 'fixtures/typescript') -const p = (p: string) => path.join(root, p) +const root = resolve(__dirname, 'fixtures/typescript') +const p = (p: string) => join(root, p) const withConfig = fancy .add('config', () => Config.load(root)) diff --git a/test/errors/handle.test.ts b/test/errors/handle.test.ts index 29536b888..9582cde10 100644 --- a/test/errors/handle.test.ts +++ b/test/errors/handle.test.ts @@ -1,95 +1,56 @@ import {expect, fancy} from 'fancy-test' import {readFileSync} from 'node:fs' -import * as path from 'path' -import * as process from 'process' +import {join} from 'node:path' +import * as process from 'node:process' -import {CLIError, config, ExitError} from '../../src/errors' -import {handle} from '../../src/errors/handle' -import {exit as exitErrorThrower} from '../../src/errors' +import {CLIError, ExitError, config, exit as exitErrorThrower} from '../../src/errors' +import {Exit, handle} from '../../src/errors/handle' +import {SinonSandbox, SinonStub, createSandbox} from 'sinon' -const errlog = path.join(__dirname, '../tmp/mytest/error.log') +const errlog = join(__dirname, '../tmp/mytest/error.log') const x = process.platform === 'win32' ? 'ยป' : 'โ€บ' -const originalExit = process.exit -const originalExitCode = process.exitCode - describe('handle', () => { + let sandbox: SinonSandbox + let exitStub: SinonStub + beforeEach(() => { - (process as any).exitCode = undefined; - (process as any).exit = (code: any) => { - (process as any).exitCode = code - } + sandbox = createSandbox() + exitStub = sandbox.stub(Exit, 'exit') }) + afterEach(() => { - (process as any).exit = originalExit; - (process as any).exitCode = originalExitCode + sandbox.restore() }) - // fancy - // .stderr() - // .finally(() => delete process.exitCode) - // .it('displays an error from root handle module', ctx => { - // handle(new Error('x')) - // expect(ctx.stderr).to.contain('Error: x') - // expect(process.exitCode).to.equal(1) - // }) - - // fancy - // .stderr() - // .finally(() => delete process.exitCode) - // .it('shows an unhandled error', ctx => { - // handle(new Error('x')) - // expect(ctx.stderr).to.contain('Error: x') - // expect(process.exitCode).to.equal(1) - // }) - - // fancy - // .stderr() - // .finally(() => delete process.exitCode) - // .it('handles a badly formed error object', () => { - // handle({status: 400} as any) - // expect(process.exitCode).to.equal(1) - // }) - - // fancy - // .stderr() - // .finally(() => delete process.exitCode) - // .it('shows a cli error', ctx => { - // handle(new CLIError('x')) - // expect(ctx.stderr).to.equal(` ${x} Error: x\n`) - // expect(process.exitCode).to.equal(2) - // }) - fancy .stdout() .stderr() - .it('hides an exit error', ctx => { - handle(new ExitError(0)) + .it('hides an exit error', async ctx => { + await handle(new ExitError(0)) expect(ctx.stdout).to.equal('') expect(ctx.stderr).to.equal('') - expect(process.exitCode).to.equal(0) + expect(exitStub.firstCall.firstArg).to.equal(0) }) fancy - .skip() .stdout() .stderr() - .it('prints error', ctx => { + .it('prints error', async ctx => { const error = new Error('foo bar baz') as Error & {skipOclifErrorHandling: boolean} error.skipOclifErrorHandling = false - handle(error) + await handle(error) expect(ctx.stdout).to.equal('') expect(ctx.stderr).to.include('foo bar baz') }) fancy .stdout() - .skip() .stderr() - .it('should not print error when skipOclifErrorHandling is true', ctx => { + .it('should not print error when skipOclifErrorHandling is true', async ctx => { const error = new Error('foo bar baz') as Error & {skipOclifErrorHandling: boolean} error.skipOclifErrorHandling = true - handle(error) + await handle(error) expect(ctx.stdout).to.equal('') expect(ctx.stderr).to.equal('') }) @@ -103,28 +64,71 @@ describe('handle', () => { config.errlog = undefined }) .it('logs when errlog is set', async ctx => { - handle(new CLIError('uh oh!')) + await handle(new CLIError('uh oh!')) expect(ctx.stderr).to.equal(` ${x} Error: uh oh!\n`) await config.errorLogger!.flush() expect(readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!') - expect(process.exitCode).to.equal(2) + expect(exitStub.firstCall.firstArg).to.equal(2) + }) + + fancy + .stdout() + .stderr() + .it('should use default exit code for Error (1)', async ctx => { + const error = new Error('foo bar baz') + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(1) + }) + + fancy + .stdout() + .stderr() + .it('should use default exit code for CLIError (2)', async ctx => { + const error = new CLIError('foo bar baz') + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(2) + }) + + fancy + .stdout() + .stderr() + .it('should use exit code provided by CLIError (0)', async ctx => { + const error = new CLIError('foo bar baz', {exit: 0}) + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(0) + }) + + fancy + .stdout() + .stderr() + .it('should use exit code provided by CLIError (9999)', async ctx => { + const error = new CLIError('foo bar baz', {exit: 9999}) + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(9999) }) describe('exit', () => { fancy .stderr() - .skip() .stdout() - .it('exits without displaying anything', ctx => { + .it('exits without displaying anything', async ctx => { try { exitErrorThrower(9000) } catch (error: any) { - handle(error) + await handle(error) } expect(ctx.stdout).to.equal('') expect(ctx.stderr).to.equal('') - expect(process.exitCode).to.be.equal(9000) + expect(exitStub.firstCall.firstArg).to.equal(9000) }) }) }) diff --git a/test/errors/warn.test.ts b/test/errors/warn.test.ts index 3f807a620..90fb1b7ea 100644 --- a/test/errors/warn.test.ts +++ b/test/errors/warn.test.ts @@ -18,7 +18,6 @@ describe('warn', () => { .it('warns', async ctx => { warn('foo!') expect(ctx.stderr).to.contain('Warning: foo!') - expect(process.exitCode).to.be.undefined await config.errorLogger!.flush() expect(await readFile(errlog, 'utf8')).to.contain('Warning: foo!') }) diff --git a/test/help/fixtures/fixtures.ts b/test/help/fixtures/fixtures.ts index 3a4497f29..4a1661891 100644 --- a/test/help/fixtures/fixtures.ts +++ b/test/help/fixtures/fixtures.ts @@ -8,11 +8,11 @@ export class AppsCreate extends Command { static summary = 'Create an app' - static description = 'this only shows up in command help under DESCRIPTION'; + static description = 'this only shows up in command help under DESCRIPTION' - static flags = {}; + static flags = {} - static args = {}; + static args = {} async run(): Promise { 'run' @@ -23,11 +23,11 @@ export class AppsDestroy extends Command { static id = 'apps:destroy' static description = `Destroy an app - this only shows up in command help under DESCRIPTION`; + this only shows up in command help under DESCRIPTION` - static flags: Record = {}; + static flags: Record = {} - static args = {}; + static args = {} async run(): Promise { 'run' @@ -37,11 +37,11 @@ export class AppsDestroy extends Command { export class AppsIndex extends Command { static id = 'apps' - static summary = 'List all apps (app index command)'; + static summary = 'List all apps (app index command)' - static flags: Record = {}; + static flags: Record = {} - static args = {}; + static args = {} async run(): Promise { 'run' @@ -52,11 +52,11 @@ export class AppsIndexWithDesc extends Command { static id = 'apps' static description = `List all apps (app index command) -this only shows up in command help under DESCRIPTION`; +this only shows up in command help under DESCRIPTION` - static flags: Record = {}; + static flags: Record = {} - static args = {}; + static args = {} async run(): Promise { 'run' @@ -79,11 +79,11 @@ export class AppsAdminIndex extends Command { static id = 'apps:admin' static description = `List of admins for an app - this only shows up in command help under DESCRIPTION`; + this only shows up in command help under DESCRIPTION` - static flags: Record = {}; + static flags: Record = {} - static args = {}; + static args = {} async run(): Promise { 'run' @@ -94,11 +94,11 @@ export class AppsAdminAdd extends Command { static id = 'apps:admin:add' static description = `Add user to an app - this only shows up in command help under DESCRIPTION`; + this only shows up in command help under DESCRIPTION` - static flags: Record = {}; + static flags: Record = {} - static args = {}; + static args = {} async run(): Promise { 'run' @@ -111,11 +111,11 @@ export class DbCreate extends Command { static id = 'db:create' static description = `Create a db - this only shows up in command help under DESCRIPTION`; + this only shows up in command help under DESCRIPTION` - static flags = {}; + static flags = {} - static args = {}; + static args = {} async run(): Promise { 'run' diff --git a/test/help/format-command-with-options.test.ts b/test/help/format-command-with-options.test.ts index 5554567e5..f3a816dc6 100644 --- a/test/help/format-command-with-options.test.ts +++ b/test/help/format-command-with-options.test.ts @@ -1,7 +1,7 @@ -import {expect, test as base} from '@oclif/test' +import {test as base, expect} from '@oclif/test' import {Args, Command as Base, Flags as flags} from '../../src' -import {commandHelp, TestHelpWithOptions as TestHelp} from './help-test-utils' +import {TestHelpWithOptions as TestHelp, commandHelp} from './help-test-utils' const g: any = global g.oclif.columns = 80 @@ -20,26 +20,26 @@ const test = base describe('formatCommand', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static aliases = ['app:init', 'create'] + static aliases = ['app:init', 'create'] - static description = `first line + static description = `first line multiline help` - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'}), - } - - static flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), - force: flags.boolean({description: 'force it '.repeat(15)}), - ss: flags.boolean({description: 'newliney\n'.repeat(4)}), - remote: flags.string({char: 'r'}), - label: flags.string({char: 'l', helpLabel: '-l'}), - } + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'}), + } + + static flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), + force: flags.boolean({description: 'force it '.repeat(15)}), + ss: flags.boolean({description: 'newliney\n'.repeat(4)}), + remote: flags.string({char: 'r'}), + label: flags.string({char: 'l', helpLabel: '-l'}), + } }) .it('handles multi-line help output', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [-f ] [--force] [--ss] [-r @@ -71,24 +71,24 @@ ALIASES describe('arg and flag multiline handling', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static description = 'description of apps:create' + static description = 'description of apps:create' - static aliases = ['app:init', 'create'] + static aliases = ['app:init', 'create'] - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'.repeat(35)}), - } + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'.repeat(35)}), + } - static flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), - force: flags.boolean({description: 'force it '.repeat(15)}), - ss: flags.boolean({description: 'newliney\n'.repeat(4)}), - remote: flags.string({char: 'r'}), - } + static flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), + force: flags.boolean({description: 'force it '.repeat(15)}), + ss: flags.boolean({description: 'newliney\n'.repeat(4)}), + remote: flags.string({char: 'r'}), + } }) .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [-f ] [--force] [--ss] [-r @@ -120,24 +120,24 @@ ALIASES test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static description = 'description of apps:create' + static description = 'description of apps:create' - static aliases = ['app:init', 'create'] + static aliases = ['app:init', 'create'] - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'.repeat(35)}), - } + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'.repeat(35)}), + } - static flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), - force: flags.boolean({description: 'force it '.repeat(29)}), - ss: flags.boolean({description: 'newliney\n'.repeat(5)}), - remote: flags.string({char: 'r'}), - } + static flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), + force: flags.boolean({description: 'force it '.repeat(29)}), + ss: flags.boolean({description: 'newliney\n'.repeat(5)}), + remote: flags.string({char: 'r'}), + } }) .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [-f ] [--force] [--ss] [-r @@ -179,20 +179,20 @@ ALIASES describe('description', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static description = 'description of apps:create\nthese values are after and will show up in the command description' + static description = 'description of apps:create\nthese values are after and will show up in the command description' - static aliases = ['app:init', 'create'] + static aliases = ['app:init', 'create'] - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'}), - } + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'}), + } - static flags = { - force: flags.boolean({description: 'forces'}), - } + static flags = { + force: flags.boolean({description: 'forces'}), + } }) .it('outputs command description with values after a \\n newline character', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--force] @@ -212,9 +212,9 @@ ALIASES test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static description = 'root part of the description\nThe <%= config.bin %> CLI has <%= command.id %>' + static description = 'root part of the description\nThe <%= config.bin %> CLI has <%= command.id %>' }) .it('renders template string from description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create @@ -226,13 +226,13 @@ DESCRIPTION describe(('flags'), () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - myenum: flags.string({ - options: ['a', 'b', 'c'], - }), - } + static flags = { + myenum: flags.string({ + options: ['a', 'b', 'c'], + }), + } }) .it('outputs flag enum', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--myenum a|b|c] @@ -271,11 +271,11 @@ OPTIONS test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - opt: flags.boolean({allowNo: true}), - } + static flags = { + opt: flags.boolean({allowNo: true}), + } }) .it('outputs with with no options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--opt] @@ -287,11 +287,11 @@ OPTIONS describe('args', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static args = { - arg1: Args.string({description: 'Show the options', options: ['option1', 'option2']}), - } + static args = { + arg1: Args.string({description: 'Show the options', options: ['option1', 'option2']}), + } }) .it('outputs with arg options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [ARG1] @@ -303,18 +303,18 @@ ARGUMENTS describe('usage', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static usage = '<%= config.bin %> <%= command.id %> usage' + static usage = '<%= config.bin %> <%= command.id %> usage' }) .it('outputs usage with templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif apps:create usage`)) test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static usage = ['<%= config.bin %>', '<%= command.id %> usage'] + static usage = ['<%= config.bin %>', '<%= command.id %> usage'] }) .it('outputs usage arrays with templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif @@ -333,7 +333,7 @@ ARGUMENTS describe('examples', () => { test .commandHelp(class extends Command { - static examples = ['it handles a list of examples', 'more example text'] + static examples = ['it handles a list of examples', 'more example text'] }) .it('outputs multiple examples', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif @@ -345,7 +345,7 @@ EXAMPLES test .commandHelp(class extends Command { - static examples = ['it handles a single example'] + static examples = ['it handles a single example'] }) .it('outputs a single example', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif @@ -355,9 +355,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['the bin is <%= config.bin %>', 'the command id is <%= command.id %>'] + static examples = ['the bin is <%= config.bin %>', 'the command id is <%= command.id %>'] }) .it('outputs examples using templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -369,9 +369,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['<%= config.bin %> <%= command.id %> --help'] + static examples = ['<%= config.bin %> <%= command.id %> --help'] }) .it('formats if command', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -381,9 +381,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help'] + static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help'] }) .it('formats if command with description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -395,9 +395,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> --help'}] + static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> --help'}] }) .it('formats example object', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command diff --git a/test/help/format-command.test.ts b/test/help/format-command.test.ts index 3b401412d..72bcfb258 100644 --- a/test/help/format-command.test.ts +++ b/test/help/format-command.test.ts @@ -1,7 +1,7 @@ -import {expect, test as base} from '@oclif/test' +import {test as base, expect} from '@oclif/test' import {Args, Command as Base, Flags as flags} from '../../src' -import {commandHelp, TestHelp} from './help-test-utils' +import {TestHelp, commandHelp} from './help-test-utils' const g: any = global g.oclif.columns = 80 @@ -20,22 +20,23 @@ const test = base describe('formatCommand', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static { + this.id = 'apps:create' - static aliases = ['app:init', 'create'] + this.aliases = ['app:init', 'create'] - static description = `first line + this.description = `first line -multiline help` + multiline help` - static enableJsonFlag = true + this.enableJsonFlag = true - static args = { + this.args = { // eslint-disable-next-line camelcase app_name: Args.string({description: 'app to use'}), } - static flags = { + this.flags = { app: flags.string({char: 'a', hidden: true}), foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), force: flags.boolean({description: 'force it '.repeat(15)}), @@ -43,6 +44,7 @@ multiline help` remote: flags.string({char: 'r'}), label: flags.string({char: 'l', helpLabel: '-l'}), } + } }) .it('handles multi-line help output', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [-f ] [--force] [--ss] @@ -79,26 +81,28 @@ ALIASES describe('arg and flag multiline handling', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static { + this.id = 'apps:create' - static description = 'description of apps:create' + this.description = 'description of apps:create' - static aliases = ['app:init', 'create'] + this.aliases = ['app:init', 'create'] - static enableJsonFlag = true + this.enableJsonFlag = true - static args = { + this.args = { // eslint-disable-next-line camelcase app_name: Args.string({description: 'app to use'.repeat(35)}), } - static flags = { + this.flags = { app: flags.string({char: 'a', hidden: true}), foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), force: flags.boolean({description: 'force it '.repeat(15)}), ss: flags.boolean({description: 'newliney\n'.repeat(4)}), remote: flags.string({char: 'r'}), } + } }) .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [-f ] [--force] [--ss] @@ -136,26 +140,28 @@ ALIASES test .commandHelp(class extends Command { - static id = 'apps:create' + static { + this.id = 'apps:create' - static description = 'description of apps:create' + this.description = 'description of apps:create' - static aliases = ['app:init', 'create'] + this.aliases = ['app:init', 'create'] - static enableJsonFlag = true + this.enableJsonFlag = true - static args = { + this.args = { // eslint-disable-next-line camelcase app_name: Args.string({description: 'app to use'.repeat(35)}), } - static flags = { + this.flags = { app: flags.string({char: 'a', hidden: true}), foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), force: flags.boolean({description: 'force it '.repeat(29)}), ss: flags.boolean({description: 'newliney\n'.repeat(5)}), remote: flags.string({char: 'r'}), } + } }) .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [-f ] [--force] [--ss] @@ -230,22 +236,24 @@ DESCRIPTION describe('description', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static { + this.id = 'apps:create' - static description = 'description of apps:create\n\nthese values are after and will show up in the command description' + this.description = 'description of apps:create\n\nthese values are after and will show up in the command description' - static aliases = ['app:init', 'create'] + this.aliases = ['app:init', 'create'] - static enableJsonFlag = true + this.enableJsonFlag = true - static args = { + this.args = { // eslint-disable-next-line camelcase app_name: Args.string({description: 'app to use'}), } - static flags = { + this.flags = { force: flags.boolean({description: 'forces'}), } + } }) .it('outputs command description with values after a \\n newline character', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [--force] @@ -270,9 +278,9 @@ ALIASES test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static description = 'root part of the description\n\nThe <%= config.bin %> CLI has <%= command.id %>' + static description = 'root part of the description\n\nThe <%= config.bin %> CLI has <%= command.id %>' }) .it('renders template string from description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create @@ -284,9 +292,9 @@ DESCRIPTION test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static description = 'root part of the description\r\n\nusing both carriage \n\nreturn and new line' + static description = 'root part of the description\r\n\nusing both carriage \n\nreturn and new line' }) .it('splits on carriage return and new lines', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create @@ -303,14 +311,16 @@ DESCRIPTION describe(('flags'), () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static { + this.id = 'apps:create' - static flags = { + this.flags = { myenum: flags.string({ description: 'the description', options: myEnumValues, }), } + } }) .it('outputs flag enum', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--myenum a|b|c] @@ -321,14 +331,14 @@ FLAGS test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - myenum: flags.string({ - options: myEnumValues, - helpValue: myEnumValues.join('|'), - }), - } + static flags = { + myenum: flags.string({ + options: myEnumValues, + helpValue: myEnumValues.join('|'), + }), + } }) .it('outputs flag enum with helpValue', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--myenum a|b|c] @@ -367,11 +377,11 @@ FLAGS test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - opt: flags.boolean({allowNo: true}), - } + static flags = { + opt: flags.boolean({allowNo: true}), + } }) .it('outputs with with no options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--opt] @@ -381,14 +391,14 @@ FLAGS test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - opt: flags.string({ - summary: 'one line summary', - description: 'multiline\ndescription', - }), - } + static flags = { + opt: flags.string({ + summary: 'one line summary', + description: 'multiline\ndescription', + }), + } }) .it('outputs flag summary and description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--opt ] @@ -404,14 +414,14 @@ FLAG DESCRIPTIONS test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - opt: flags.string({ - summary: 'one line summary', - description: 'single line description', - }), - } + static flags = { + opt: flags.string({ + summary: 'one line summary', + description: 'single line description', + }), + } }) .it('outputs flag summary and single line description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--opt ] @@ -426,14 +436,14 @@ FLAG DESCRIPTIONS test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static flags = { - opt: flags.string({ - summary: 'one line summary'.repeat(15), - description: 'single line description', - }), - } + static flags = { + opt: flags.string({ + summary: 'one line summary'.repeat(15), + description: 'single line description', + }), + } }) .it('outputs long flag summary and single line description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--opt ] @@ -458,11 +468,11 @@ FLAG DESCRIPTIONS describe('args', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static args = { - arg1: Args.string({description: 'Show the options', options: ['option1', 'option2']}), - } + static args = { + arg1: Args.string({description: 'Show the options', options: ['option1', 'option2']}), + } }) .it('outputs with arg options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [ARG1] @@ -474,18 +484,18 @@ ARGUMENTS describe('usage', () => { test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static usage = '<%= config.bin %> <%= command.id %> usage' + static usage = '<%= config.bin %> <%= command.id %> usage' }) .it('outputs usage with templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif apps:create usage`)) test .commandHelp(class extends Command { - static id = 'apps:create' + static id = 'apps:create' - static usage = ['<%= config.bin %>', '<%= command.id %> usage'] + static usage = ['<%= config.bin %>', '<%= command.id %> usage'] }) .it('outputs usage arrays with templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif @@ -504,7 +514,7 @@ ARGUMENTS describe('examples', () => { test .commandHelp(class extends Command { - static examples = ['it handles a list of examples', 'more example text'] + static examples = ['it handles a list of examples', 'more example text'] }) .it('outputs multiple examples', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif @@ -516,7 +526,7 @@ EXAMPLES test .commandHelp(class extends Command { - static examples = ['it handles a single example'] + static examples = ['it handles a single example'] }) .it('outputs a single example', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif @@ -526,9 +536,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['the bin is <%= config.bin %>', 'the command id is <%= command.id %>'] + static examples = ['the bin is <%= config.bin %>', 'the command id is <%= command.id %>'] }) .it('outputs examples using templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -540,9 +550,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['<%= config.bin %> <%= command.id %> --help'] + static examples = ['<%= config.bin %> <%= command.id %> --help'] }) .it('formats if command', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -552,9 +562,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help'] + static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help'] }) .it('formats if command with description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -566,9 +576,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help\n<%= config.bin %> <%= command.id %> --help'] + static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help\n<%= config.bin %> <%= command.id %> --help'] }) .it('formats if multiple command with description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -581,9 +591,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> --help'}] + static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> --help'}] }) .it('formats example object', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -595,9 +605,9 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = [{description: 'force it '.repeat(15), command: '<%= config.bin %> <%= command.id %> --help'}] + static examples = [{description: 'force it '.repeat(15), command: '<%= config.bin %> <%= command.id %> --help'}] }) .it('formats example object with long description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command @@ -610,15 +620,15 @@ EXAMPLES test .commandHelp(class extends Command { - static id = 'oclif:command' + static id = 'oclif:command' - static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> ' + 'force it '.repeat(15)}] + static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> ' + 'force it '.repeat(15)}] }) .it('formats example object with long command', (ctx: any) => { - const multilineSeparator = - ctx.config.platform === 'win32' ? - (ctx.config.shell.includes('powershell') ? '`' : '^') : - '\\' + const multilineSeparator + = ctx.config.platform === 'win32' + ? (ctx.config.shell.includes('powershell') ? '`' : '^') + : '\\' expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command diff --git a/test/help/format-commands.test.ts b/test/help/format-commands.test.ts index ec88b81a3..9ae47a0ba 100644 --- a/test/help/format-commands.test.ts +++ b/test/help/format-commands.test.ts @@ -1,11 +1,11 @@ import {Command} from '../../src/command' -import {expect, test as base} from '@oclif/test' +import {test as base, expect} from '@oclif/test' import stripAnsi = require('strip-ansi') const g: any = global g.oclif.columns = 80 import {Help} from '../../src/help' -import {AppsDestroy, AppsCreate} from './fixtures/fixtures' +import {AppsCreate, AppsDestroy} from './fixtures/fixtures' // extensions to expose method as public for testing class TestHelp extends Help { diff --git a/test/help/format-root.test.ts b/test/help/format-root.test.ts index 017039a9c..c8d18a956 100644 --- a/test/help/format-root.test.ts +++ b/test/help/format-root.test.ts @@ -1,4 +1,4 @@ -import {expect, test as base} from '@oclif/test' +import {test as base, expect} from '@oclif/test' import stripAnsi = require('strip-ansi') import {Help} from '../../src/help' @@ -48,15 +48,13 @@ USAGE describe('description', () => { test - .rootHelp(config => { - return { - ...config, - pjson: { - ...config.pjson, - description: 'This is the top-level description that appears in the root\nThis appears in the description section after usage', - }, - } - }) + .rootHelp(config => ({ + ...config, + pjson: { + ...config.pjson, + description: 'This is the top-level description that appears in the root\nThis appears in the description section after usage', + }, + })) .it('splits on \\n for the description into the top-level and description sections', ctx => { expect(ctx.commandHelp).to.equal(`This is the top-level description that appears in the root @@ -71,15 +69,13 @@ DESCRIPTION }) test - .rootHelp(config => { - return { - ...config, - pjson: { - ...config.pjson, - description: 'This is the top-level description for <%= config.bin %>\nThis <%= config.bin %> appears in the description section after usage', - }, - } - }) + .rootHelp(config => ({ + ...config, + pjson: { + ...config.pjson, + description: 'This is the top-level description for <%= config.bin %>\nThis <%= config.bin %> appears in the description section after usage', + }, + })) .it('shows description from a template', ctx => { expect(ctx.commandHelp).to.equal(`This is the top-level description for oclif @@ -94,19 +90,17 @@ DESCRIPTION }) test - .rootHelp(config => { - return { - ...config, - pjson: { - ...config.pjson, - description: 'THIS IS THE PJSON DESCRIPTION', - oclif: { - ...config.pjson?.oclif, - description: 'THIS IS THE OCLIF DESCRIPTION IN PJSON', - }, + .rootHelp(config => ({ + ...config, + pjson: { + ...config.pjson, + description: 'THIS IS THE PJSON DESCRIPTION', + oclif: { + ...config.pjson?.oclif, + description: 'THIS IS THE OCLIF DESCRIPTION IN PJSON', }, - } - }) + }, + })) .it('prefers the oclif description over the package.json description', ctx => { expect(ctx.commandHelp).to.equal(`THIS IS THE OCLIF DESCRIPTION IN PJSON @@ -118,19 +112,17 @@ USAGE }) test - .rootHelp(config => { - return { - ...config, - pjson: { - ...config.pjson, - description: 'THIS IS THE PJSON DESCRIPTION', - oclif: { - ...config.pjson?.oclif, - description: undefined, - }, + .rootHelp(config => ({ + ...config, + pjson: { + ...config.pjson, + description: 'THIS IS THE PJSON DESCRIPTION', + oclif: { + ...config.pjson?.oclif, + description: undefined, }, - } - }) + }, + })) .it('uses package.json description when the oclif description is not set', ctx => { expect(ctx.commandHelp).to.equal(`THIS IS THE PJSON DESCRIPTION diff --git a/test/help/format-topic.test.ts b/test/help/format-topic.test.ts index 73a331164..4dfcf01ed 100644 --- a/test/help/format-topic.test.ts +++ b/test/help/format-topic.test.ts @@ -1,4 +1,4 @@ -import {expect, test as base} from '@oclif/test' +import {test as base, expect} from '@oclif/test' import {TestHelp, topicHelp} from './help-test-utils' const g: any = global @@ -6,9 +6,7 @@ g.oclif.columns = 80 const test = base .loadConfig() -.add('help', ctx => { - return new TestHelp(ctx.config as any) -}) +.add('help', ctx => new TestHelp(ctx.config as any)) .register('topicHelp', topicHelp) describe('formatHelp', () => { diff --git a/test/help/format-topics.test.ts b/test/help/format-topics.test.ts index 22f064f6f..4787c4f77 100644 --- a/test/help/format-topics.test.ts +++ b/test/help/format-topics.test.ts @@ -1,4 +1,4 @@ -import {expect, test as base} from '@oclif/test' +import {test as base, expect} from '@oclif/test' import {TestHelp, topicsHelp} from './help-test-utils' diff --git a/test/help/help-test-utils.ts b/test/help/help-test-utils.ts index e9542dc16..cf94558b2 100644 --- a/test/help/help-test-utils.ts +++ b/test/help/help-test-utils.ts @@ -1,9 +1,9 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + import {Command} from '../../src/command' import stripAnsi = require('strip-ansi') import {Interfaces, toCached} from '../../src' -import {Help, CommandHelp} from '../../src/help' +import {CommandHelp, Help} from '../../src/help' export class TestCommandHelp extends CommandHelp { protected sections() { diff --git a/test/help/show-customized-help.test.ts b/test/help/show-customized-help.test.ts index e8d5586a6..1b0b47547 100644 --- a/test/help/show-customized-help.test.ts +++ b/test/help/show-customized-help.test.ts @@ -1,10 +1,10 @@ -import {expect, test as base} from '@oclif/test' -import {stub, SinonStub} from 'sinon' -import * as path from 'path' +import {test as base, expect} from '@oclif/test' +import {SinonStub, stub} from 'sinon' +import {resolve} from 'node:path' import {CommandHelp, Help} from '../../src/help' -import {AppsIndexWithDesc, AppsDestroy, AppsCreate, AppsTopic, AppsAdminTopic, AppsAdminAdd} from './fixtures/fixtures' -import {Interfaces, Config} from '../../src' +import {AppsAdminAdd, AppsAdminTopic, AppsCreate, AppsDestroy, AppsIndexWithDesc, AppsTopic} from './fixtures/fixtures' +import {Config, Interfaces} from '../../src' import {Command} from '../../src/command' import {monkeyPatchCommands} from './help-test-utils' @@ -32,7 +32,7 @@ ${this.indent(this.wrap('force it '.repeat(29)))}`, class TestHelp extends Help { CommandHelpClass = TestCommandHelp - public config: any; + public declare config: any constructor(config: Interfaces.Config, opts: Partial = {}) { super(config, opts) @@ -63,7 +63,7 @@ const test = base } // use devPlugins: true to bring in plugins-plugin with topic commands for testing - const config = await Config.load({devPlugins: true, root: path.resolve(__dirname, '..')}) + const config = await Config.load({devPlugins: true, root: resolve(__dirname, '..')}) ctx.help = new TestHelp(config) }, finally(ctx) { @@ -87,7 +87,7 @@ describe('showHelp for root', () => { .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -119,7 +119,7 @@ COMMANDS .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -150,7 +150,7 @@ describe('showHelp for a command', () => { .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -180,7 +180,7 @@ CUSTOM .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', diff --git a/test/help/show-help.test.ts b/test/help/show-help.test.ts index 976089a32..2f7617369 100644 --- a/test/help/show-help.test.ts +++ b/test/help/show-help.test.ts @@ -1,10 +1,10 @@ import {test as base} from '@oclif/test' -import {stub, SinonStub} from 'sinon' -import * as path from 'path' +import {SinonStub, stub} from 'sinon' +import {resolve} from 'node:path' import {Help} from '../../src/help' -import {AppsIndex, AppsDestroy, AppsCreate, AppsTopic, AppsAdminTopic, AppsAdminAdd, AppsAdminIndex, DbCreate, DbTopic} from './fixtures/fixtures' -import {Interfaces, Config} from '../../src' +import {AppsAdminAdd, AppsAdminIndex, AppsAdminTopic, AppsCreate, AppsDestroy, AppsIndex, AppsTopic, DbCreate, DbTopic} from './fixtures/fixtures' +import {Config, Interfaces} from '../../src' import {monkeyPatchCommands} from './help-test-utils' import {expect} from 'chai' @@ -13,7 +13,7 @@ g.oclif.columns = 80 // extension makes previously protected methods public class TestHelp extends Help { - public config: any; + public declare config: any public async showRootHelp() { return super.showRootHelp() @@ -34,7 +34,7 @@ const test = base } // use devPlugins: true to bring in plugins-plugin with topic commands for testing - const config = await Config.load({devPlugins: true, root: path.resolve(__dirname, '..')}) + const config = await Config.load({devPlugins: true, root: resolve(__dirname, '..')}) ctx.help = new TestHelp(config) }, finally(ctx) { @@ -58,7 +58,7 @@ describe('showHelp for root', () => { .loadConfig() .stdout() .do(async () => { - const config = await Config.load({root: path.resolve(__dirname, '..')}); + const config = await Config.load({root: resolve(__dirname, '..')}); (config as any).plugins = [{ commands: [AppsIndex, AppsCreate, AppsDestroy], @@ -97,7 +97,7 @@ COMMANDS .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -127,7 +127,7 @@ describe('showHelp for a topic', () => { .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -153,7 +153,7 @@ COMMANDS .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -182,7 +182,7 @@ COMMANDS .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', @@ -212,7 +212,7 @@ COMMANDS .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', commands: [AppsCreate, AppsDestroy, AppsAdminAdd, DbCreate], @@ -242,7 +242,7 @@ describe('showHelp for a command', () => { .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', commands: [AppsCreate], @@ -268,7 +268,7 @@ DESCRIPTION .loadConfig() .stdout() .do(async ctx => { - const config = ctx.config + const {config} = ctx monkeyPatchCommands(config, [{ name: 'plugin-1', commands: [AppsIndex, AppsCreate, AppsAdminAdd], diff --git a/test/help/util.test.ts b/test/help/util.test.ts index 29cfc8fa0..c25aa0b3f 100644 --- a/test/help/util.test.ts +++ b/test/help/util.test.ts @@ -1,4 +1,4 @@ -import {resolve} from 'path' +import {resolve} from 'node:path' import {Config, Interfaces} from '../../src' import {test} from '@oclif/test' import {loadHelpClass, standardizeIDFromArgv} from '../../src/help' diff --git a/test/integration/esm-cjs.ts b/test/integration/esm-cjs.ts index afbd5c7eb..c557cee2a 100644 --- a/test/integration/esm-cjs.ts +++ b/test/integration/esm-cjs.ts @@ -6,11 +6,11 @@ * Instead of spending more time diagnosing the root cause, we are just going to * run these integration tests using ts-node and a lightweight homemade test runner. */ -import * as fs from 'fs/promises' -import * as path from 'path' +import * as fs from 'node:fs/promises' +import * as path from 'node:path' import {Executor, setup} from './util' import {expect} from 'chai' -import {bold, green, red} from 'chalk' +import chalk from 'chalk' const FAILED: string[] = [] const PASSED: string[] = [] @@ -19,28 +19,28 @@ async function test(name: string, fn: () => Promise) { try { await fn() PASSED.push(name) - console.log(green('โœ“'), name) + console.log(chalk.green('โœ“'), name) } catch (error) { FAILED.push(name) - console.log(red('๐„‚'), name) + console.log(chalk.red('๐„‚'), name) console.log(error) } } function exit(): never { console.log() - console.log(bold('#### Summary ####')) + console.log(chalk.bold('#### Summary ####')) for (const name of PASSED) { - console.log(green('โœ“'), name) + console.log(chalk.green('โœ“'), name) } for (const name of FAILED) { - console.log(red('๐„‚'), name) + console.log(chalk.red('๐„‚'), name) } - console.log(`${green('Passed:')} ${PASSED.length}`) - console.log(`${red('Failed:')} ${FAILED.length}`) + console.log(`${chalk.green('Passed:')} ${PASSED.length}`) + console.log(`${chalk.red('Failed:')} ${FAILED.length}`) // eslint-disable-next-line no-process-exit, unicorn/no-process-exit process.exit(FAILED.length) @@ -65,14 +65,17 @@ type LinkPluginOptions = { executor: Executor; plugin: Plugin; script: Script; + noLinkCore?: boolean; } type RunCommandOptions = { executor: Executor; plugin: Plugin; script: Script; - expectStrings?: string[]; + expectStrings?: string[] + expectJson?: Record env?: Record; + args?: Array; } type ModifyCommandOptions = { @@ -88,8 +91,57 @@ type CleanUpOptions = { plugin: Plugin; } +type PluginConfig = { + name: string; + command: string; + package: string; + repo: string; + commandText: string; + hookText: string; + expectJson: { + whenProvided: { + args: Record; + flags: Record; + }; + whenNotProvided: { + args: Record; + flags: Record; + }; + } +} + +// eslint-disable-next-line unicorn/prefer-top-level-await (async () => { - const PLUGINS = { + const commonProps = { + expectJson: { + whenProvided: { + args: { + optionalArg: 'arg1', + defaultArg: 'arg2', + defaultFnArg: 'arg3', + }, + flags: { + optionalString: 'flag1', + defaultString: 'flag2', + defaultFnString: 'flag3', + json: true, + }, + }, + whenNotProvided: { + args: { + defaultArg: 'simple string default', + defaultFnArg: 'async fn default', + }, + flags: { + defaultString: 'simple string default', + defaultFnString: 'async fn default', + json: true, + }, + }, + }, + } + + const PLUGINS: Record = { esm1: { name: 'plugin-test-esm-1', command: 'esm1', @@ -97,6 +149,7 @@ type CleanUpOptions = { repo: 'https://github.com/oclif/plugin-test-esm-1', commandText: 'hello I am an ESM plugin', hookText: 'Greetings! from plugin-test-esm-1 init hook', + ...commonProps, }, esm2: { name: 'plugin-test-esm-2', @@ -105,6 +158,7 @@ type CleanUpOptions = { repo: 'https://github.com/oclif/plugin-test-esm-2', commandText: 'hello I am an ESM plugin', hookText: 'Greetings! from plugin-test-esm-2 init hook', + ...commonProps, }, cjs1: { name: 'plugin-test-cjs-1', @@ -113,6 +167,7 @@ type CleanUpOptions = { repo: 'https://github.com/oclif/plugin-test-cjs-1', commandText: 'hello I am a CJS plugin', hookText: 'Greetings! from plugin-test-cjs-1 init hook', + ...commonProps, }, cjs2: { name: 'plugin-test-cjs-2', @@ -121,6 +176,47 @@ type CleanUpOptions = { repo: 'https://github.com/oclif/plugin-test-cjs-2', commandText: 'hello I am a CJS plugin', hookText: 'Greetings! from plugin-test-cjs-2 init hook', + ...commonProps, + }, + precore: { + name: 'plugin-test-pre-core', + command: 'pre-core', + package: '@oclif/plugin-test-pre-core', + repo: 'https://github.com/oclif/plugin-test-pre-core', + commandText: 'hello I am a pre-core plugin', + hookText: 'Greetings! from plugin-test-pre-core init hook', + expectJson: { + whenProvided: commonProps.expectJson.whenProvided, + whenNotProvided: { + args: { + defaultArg: 'simple string default', + defaultFnArg: 'fn default', + }, + flags: { + defaultString: 'simple string default', + defaultFnString: 'fn default', + json: true, + }, + }, + }, + }, + coreV1: { + name: 'plugin-test-core-v1', + command: 'core-v1', + package: '@oclif/plugin-test-core-v1', + repo: 'https://github.com/oclif/plugin-test-core-v1', + commandText: 'hello I am an @oclif/core@v1 plugin', + hookText: 'Greetings! from plugin-test-core-v1 init hook', + ...commonProps, + }, + coreV2: { + name: 'plugin-test-core-v2', + command: 'core-v2', + package: '@oclif/plugin-test-core-v2', + repo: 'https://github.com/oclif/plugin-test-core-v2', + commandText: 'hello I am an @oclif/core@v2 plugin', + hookText: 'Greetings! from plugin-test-core-v2 init hook', + ...commonProps, }, } @@ -136,6 +232,7 @@ type CleanUpOptions = { const pluginExecutor = await setup(__filename, { repo: options.plugin.repo, subDir: options.executor.parentDir, + noLinkCore: options.noLinkCore ?? false, }) const result = await options.executor.executeCommand(`plugins:link ${pluginExecutor.pluginDir}`, options.script) @@ -156,7 +253,11 @@ type CleanUpOptions = { async function runCommand(options: RunCommandOptions): Promise { const env = {...process.env, ...options.env} - const result = await options.executor.executeCommand(options.plugin.command, options.script, {env}) + const result = await options.executor.executeCommand( + `${options.plugin.command} ${options.args?.join(' ') ?? ''}`, + options.script, + {env}, + ) expect(result.code).to.equal(0) if (options.expectStrings) { @@ -164,24 +265,39 @@ type CleanUpOptions = { expect(result.stdout).to.include(expectString) } } + + if (options.expectJson && options.args?.includes('--json')) { + // clear any non-json output from hooks + const split = result.stdout?.split('\n') ?? [] + const idx = split.findIndex(i => i.startsWith('{')) + const json = JSON.parse(split.slice(idx).join('\n')) + expect(json).to.deep.equal(options.expectJson) + } } async function cleanUp(options: CleanUpOptions): Promise { - await options.executor.executeCommand(`plugins:uninstall @oclif/${options.plugin.name}`) - expect((await options.executor.executeCommand('plugins')).stdout).to.not.include(options.plugin.name) + await options.executor.executeCommand(`plugins:uninstall ${options.plugin.package}`) + const {stdout} = await options.executor.executeCommand('plugins') + expect(stdout).to.not.include(options.plugin.package) } const args = process.argv.slice(process.argv.indexOf(__filename) + 1) - const runInParallel = args.includes('--parallel') - const skip = args.find(arg => arg.startsWith('--skip=')) - - const skips = skip ? skip.split('=')[1].split(',') : [] - const runEsmTests = !skips.includes('esm') - const runCjsTests = !skips.includes('cjs') + const providedSkips = args.find(arg => arg.startsWith('--skip=')) + const providedTests = args.find(arg => arg.startsWith('--test=')) ?? '=cjs,esm,precore,coreV1,coreV2' + + const skips = providedSkips ? providedSkips.split('=')[1].split(',') : [] + const tests = providedTests ? providedTests.split('=')[1].split(',') : [] + + const runTests = { + esm: tests.includes('esm') && !skips.includes('esm'), + cjs: tests.includes('cjs') && !skips.includes('cjs'), + precore: tests.includes('precore') && !skips.includes('precore'), + coreV1: tests.includes('coreV1') && !skips.includes('coreV1'), + coreV2: tests.includes('coreV2') && !skips.includes('coreV2'), + } console.log('Node version:', process.version) - console.log(runInParallel ? '๐Ÿ‡ Running tests in parallel' : '๐Ÿข Running tests sequentially') - if (skips.length > 0) console.log(`๐Ÿšจ Skipping ${skips.join(', ')} tests ๐Ÿšจ`) + console.log('Running tests:', runTests) let cjsExecutor: Executor let esmExecutor: Executor @@ -194,79 +310,116 @@ type CleanUpOptions = { esmExecutor = await setup(__filename, {repo: PLUGINS.esm1.repo, subDir: 'esm'}) } - const cjsTests = async () => { - await test('Install CJS plugin to CJS root plugin', async () => { - const plugin = PLUGINS.cjs2 + const precoreBefore = async () => { + if (!cjsExecutor) await cjsBefore() + if (!esmExecutor) await esmBefore() + } - await installPlugin({executor: cjsExecutor, plugin, script: 'run'}) - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'run', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'dev', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + const coreV1Before = async () => { + if (!cjsExecutor) await cjsBefore() + if (!esmExecutor) await esmBefore() + } + + const coreV2Before = async () => { + if (!cjsExecutor) await cjsBefore() + if (!esmExecutor) await esmBefore() + } + + const installTest = async (plugin: PluginConfig, executor: Executor) => { + await installPlugin({executor, plugin, script: 'run'}) + + // test that the root plugin's bin/run can execute the installed plugin + await runCommand({ + executor, + plugin, + script: 'run', + expectStrings: [plugin.commandText], }) - await test('Install ESM plugin to CJS root plugin', async () => { - const plugin = PLUGINS.esm1 + // test that the root plugin's bin/run can execute the installed plugin + // and that args and flags work as expected when no values are provided + await runCommand({ + executor, + plugin, + script: 'run', + args: ['--json'], + expectJson: plugin.expectJson.whenNotProvided, + }) - await installPlugin({executor: cjsExecutor, plugin, script: 'run'}) - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'run', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'dev', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + // test that the root plugin's bin/run can execute the installed plugin + // and that args and flags work as expected when values are provided + await runCommand({ + executor, + plugin, + script: 'run', + args: [ + ...Object.values(plugin.expectJson.whenProvided.args), + ...Object.entries(plugin.expectJson.whenProvided.flags).map(([flag, value]) => { + if (flag === 'json') return '--json' + return `--${flag} ${value}` + }), + ], + expectJson: plugin.expectJson.whenProvided, }) - await test('Link CJS plugin to CJS root plugin', async () => { - const plugin = PLUGINS.cjs2 + // test that the root plugin's bin/dev can execute the installed plugin + await runCommand({ + executor, + plugin, + script: 'dev', + expectStrings: [plugin.commandText], + }) - const linkedPlugin = await linkPlugin({executor: cjsExecutor, plugin, script: 'run'}) + await cleanUp({executor, plugin, script: 'run'}) + } - // test bin/run - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'run', - expectStrings: [plugin.commandText, plugin.hookText], - }) - // test un-compiled changes with bin/run - await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'run', - expectStrings: ['howdy', plugin.hookText], - }) + const linkTest = async (plugin: PluginConfig, executor: Executor, noLinkCore = false) => { + const linkedPlugin = await linkPlugin({executor, plugin, script: 'run', noLinkCore}) - // test un-compiled changes with bin/dev - await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) - await runCommand({ - executor: cjsExecutor, - plugin, - script: 'dev', - expectStrings: ['cheers', plugin.hookText], - }) + // test bin/run + await runCommand({ + executor, + plugin, + script: 'run', + expectStrings: [plugin.commandText, plugin.hookText], + }) + // test un-compiled changes with bin/run + await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) + await runCommand({ + executor, + plugin, + script: 'run', + expectStrings: ['howdy', plugin.hookText], + }) - await cleanUp({executor: cjsExecutor, plugin, script: 'run'}) + // test un-compiled changes with bin/dev + await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) + await runCommand({ + executor, + plugin, + script: 'dev', + expectStrings: ['cheers', plugin.hookText], + }) + + await cleanUp({executor, plugin, script: 'run'}) + } + + const cjsTests = async () => { + await test('Install CJS plugin to CJS root plugin', async () => { + await installTest(PLUGINS.cjs2, cjsExecutor) + }) + + await test('Install ESM plugin to CJS root plugin', async () => { + await installTest(PLUGINS.esm1, cjsExecutor) + }) + + await test('Link CJS plugin to CJS root plugin', async () => { + await linkTest(PLUGINS.cjs2, cjsExecutor) }) await test('Link ESM plugin to CJS root plugin', async () => { + // We don't use linkTest here because that would test that the + // ESM plugin is auto-transpiled which we're not supporting at the moment. const plugin = PLUGINS.esm2 await linkPlugin({executor: cjsExecutor, plugin, script: 'run'}) @@ -293,73 +446,15 @@ type CleanUpOptions = { const esmTests = async () => { await test('Install CJS plugin to ESM root plugin', async () => { - const plugin = PLUGINS.cjs1 - - await installPlugin({executor: esmExecutor, plugin, script: 'run'}) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'run', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'dev', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + await installTest(PLUGINS.cjs1, esmExecutor) }) await test('Install ESM plugin to ESM root plugin', async () => { - const plugin = PLUGINS.esm2 - - await installPlugin({executor: esmExecutor, plugin, script: 'run'}) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'run', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'dev', - expectStrings: [plugin.commandText, plugin.hookText], - }) - await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + await installTest(PLUGINS.esm2, esmExecutor) }) await test('Link CJS plugin to ESM root plugin', async () => { - const plugin = PLUGINS.cjs1 - - const linkedPlugin = await linkPlugin({executor: esmExecutor, plugin, script: 'run'}) - // test bin/run - await runCommand({ - executor: esmExecutor, - plugin, - script: 'run', - expectStrings: [plugin.commandText, plugin.hookText], - }) - // test un-compiled changes with bin/run - await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'}) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'run', - expectStrings: ['howdy', plugin.hookText], - }) - - // test un-compiled changes with bin/dev - await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'}) - await runCommand({ - executor: esmExecutor, - plugin, - script: 'dev', - expectStrings: ['cheers', plugin.hookText], - }) - - await cleanUp({executor: esmExecutor, plugin, script: 'run'}) + await linkTest(PLUGINS.cjs1, esmExecutor) }) await test('Link ESM plugin to ESM root plugin', async () => { @@ -398,24 +493,84 @@ type CleanUpOptions = { }) } - if (runInParallel) { - await Promise.all([ - runCjsTests ? cjsBefore() : Promise.resolve(), - runEsmTests ? esmBefore() : Promise.resolve(), - ]) - - await Promise.all([ - runCjsTests ? cjsTests() : Promise.resolve(), - runEsmTests ? esmTests() : Promise.resolve(), - ]) - } else { - if (runCjsTests) await cjsBefore() - if (runEsmTests) await esmBefore() - - if (runCjsTests) await cjsTests() - if (runEsmTests) await esmTests() + const preCoreTests = async () => { + await test('Install pre-core plugin to ESM root plugin', async () => { + await installTest(PLUGINS.precore, esmExecutor) + }) + + await test('Install pre-core plugin to CJS root plugin', async () => { + await installTest(PLUGINS.precore, cjsExecutor) + }) + + await test('Link pre-core plugin to CJS root plugin', async () => { + // Pass in true to skip linking the local version of @oclif/core + // to the test pre-core plugin since it doesn't use core. + await linkTest(PLUGINS.precore, cjsExecutor, true) + }) + + await test('Link pre-core plugin to ESM root plugin', async () => { + // Pass in true to skip linking the local version of @oclif/core + // to the test pre-core plugin since it doesn't use core. + await linkTest(PLUGINS.precore, esmExecutor, true) + }) + } + + const coreV1Tests = async () => { + await test('Install core v1 plugin to ESM root plugin', async () => { + await installTest(PLUGINS.coreV1, esmExecutor) + }) + + await test('Install core v1 plugin to CJS root plugin', async () => { + await installTest(PLUGINS.coreV1, cjsExecutor) + }) + + await test('Link core v1 plugin to CJS root plugin', async () => { + // Pass in true to skip linking the local version of @oclif/core + // to plugin-test-core-v1. There are breaking changes to how + // args are defined in a command so the plugin won't compile if + // we link the local version of core. + await linkTest(PLUGINS.coreV1, cjsExecutor, true) + }) + + await test('Link core v1 plugin to ESM root plugin', async () => { + // Pass in true to skip linking the local version of @oclif/core + // to plugin-test-core-v1. There are breaking changes to how + // args are defined in a command so the plugin won't compile if + // we link the local version of core. + await linkTest(PLUGINS.coreV1, esmExecutor, true) + }) } + const coreV2Tests = async () => { + await test('Install core v2 plugin to ESM root plugin', async () => { + await installTest(PLUGINS.coreV2, esmExecutor) + }) + + await test('Install core v2 plugin to CJS root plugin', async () => { + await installTest(PLUGINS.coreV2, cjsExecutor) + }) + + await test('Link core v2 plugin to CJS root plugin', async () => { + await linkTest(PLUGINS.coreV2, cjsExecutor) + }) + + await test('Link core v2 plugin to ESM root plugin', async () => { + await linkTest(PLUGINS.coreV2, esmExecutor) + }) + } + + if (runTests.cjs) await cjsBefore() + if (runTests.esm) await esmBefore() + if (runTests.precore) await precoreBefore() + if (runTests.coreV1) await coreV1Before() + if (runTests.coreV2) await coreV2Before() + + if (runTests.cjs) await cjsTests() + if (runTests.esm) await esmTests() + if (runTests.precore) await preCoreTests() + if (runTests.coreV1) await coreV1Tests() + if (runTests.coreV2) await coreV2Tests() + exit() })() diff --git a/test/integration/plugins.e2e.ts b/test/integration/plugins.e2e.ts index b8206320e..56c42940f 100644 --- a/test/integration/plugins.e2e.ts +++ b/test/integration/plugins.e2e.ts @@ -1,5 +1,5 @@ -import * as os from 'os' -import {expect, config as chaiConfig} from 'chai' +import {arch} from 'node:os' +import {config as chaiConfig, expect} from 'chai' import {Executor, Result, setup} from './util' chaiConfig.truncateThreshold = 0 @@ -197,7 +197,7 @@ describe('oclif plugins', () => { it('should install the plugin', async () => { const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available 2>&1') expect(result.code).to.equal(0) - expect(result.stdout).to.include('@oclif/plugin-warn-if-update-available... installed v') + expect(result.stdout).to.include('@oclif/plugin-warn-if-update-available@latest... installed v') const pluginsResult = await executor.executeCommand('plugins') expect(pluginsResult.code).to.equal(0) @@ -224,7 +224,7 @@ describe('oclif plugins', () => { it('should install the plugin', async () => { const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available --force 2>&1') expect(result.code).to.equal(0) - expect(result.stdout).to.include('@oclif/plugin-warn-if-update-available... installed v') + expect(result.stdout).to.include('@oclif/plugin-warn-if-update-available@latest... installed v') const pluginsResult = await executor.executeCommand('plugins') expect(pluginsResult.code).to.equal(0) @@ -257,7 +257,7 @@ describe('oclif plugins', () => { it('should show version', () => expect(version.stdout).to.include('oclif-hello-world/0.0.0')) it('should show platform', () => expect(version.stdout).to.include(process.platform)) - it('should show arch', () => expect(version.stdout).to.include(os.arch())) + it('should show arch', () => expect(version.stdout).to.include(arch())) it('should show node version', () => expect(version.stdout).to.include(process.version)) }) diff --git a/test/integration/sf.e2e.ts b/test/integration/sf.e2e.ts index 69ea75431..27636093c 100644 --- a/test/integration/sf.e2e.ts +++ b/test/integration/sf.e2e.ts @@ -1,4 +1,4 @@ -import * as os from 'os' +import {arch} from 'node:os' import {expect} from 'chai' import {Executor, setup} from './util' import StripAnsi = require('strip-ansi') @@ -17,12 +17,7 @@ describe('Salesforce CLI (sf)', () => { process.env.SFDX_TELEMETRY_DISABLE_ACKNOWLEDGEMENT = 'true' executor = await setup(__filename, { repo: 'https://github.com/salesforcecli/cli', - // Allowing failed install here because it let's attempt to run the tests - // even if there's a linting error caused by the code changes. This is only - // acceptable since we're crossing major versions at the moment. It should - // be removed as soon as sf uses v3. - allowFailedInstall: true, - compileCmd: 'yarn compile', + branch: 'mdonnalley/esm', }) }) @@ -87,7 +82,7 @@ describe('Salesforce CLI (sf)', () => { const version = await executor.executeCommand('-v') expect(version.stdout).to.include('@salesforce/cli') expect(version.stdout).to.include(process.platform) - expect(version.stdout).to.include(os.arch()) + expect(version.stdout).to.include(arch()) expect(version.stdout).to.include(process.version) }) diff --git a/test/integration/util.ts b/test/integration/util.ts index 98d4e588f..d051f15ef 100644 --- a/test/integration/util.ts +++ b/test/integration/util.ts @@ -1,16 +1,15 @@ -import {rm} from 'shelljs' -import {mkdir} from 'node:fs/promises' -import * as cp from 'child_process' -import * as chalk from 'chalk' -import * as fs from 'fs' -import * as os from 'os' -import * as path from 'path' +import {mkdir, rm} from 'node:fs/promises' +import {ExecException, ExecSyncOptionsWithBufferEncoding, execSync} from 'node:child_process' +import chalk from 'chalk' +import {existsSync, readFileSync, writeFileSync} from 'node:fs' +import {tmpdir} from 'node:os' +import {basename, dirname, join, resolve} from 'node:path' import {Interfaces} from '../../src' const debug = require('debug')('e2e') -export type ExecError = cp.ExecException & { stderr: string; stdout: string }; +export type ExecError = ExecException & { stderr: string; stdout: string }; export type Result = { code: number; @@ -21,10 +20,10 @@ export type Result = { export type SetupOptions = { repo: string; + branch?: string; plugins?: string[]; subDir?: string; - allowFailedInstall?: boolean; - compileCmd?: string; + noLinkCore?: boolean; } export type ExecutorOptions = { @@ -32,15 +31,15 @@ export type ExecutorOptions = { testFileName: string; } -export type ExecOptions = cp.ExecSyncOptionsWithBufferEncoding & {silent?: boolean} +export type ExecOptions = ExecSyncOptionsWithBufferEncoding & {silent?: boolean} function updatePkgJson(testDir: string, obj: Record): Interfaces.PJSON { - const pkgJsonFile = path.join(testDir, 'package.json') - const pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf-8')) + const pkgJsonFile = join(testDir, 'package.json') + const pkgJson = JSON.parse(readFileSync(pkgJsonFile, 'utf8')) obj.dependencies = Object.assign(pkgJson.dependencies || {}, obj.dependencies || {}) obj.resolutions = Object.assign(pkgJson.resolutions || {}, obj.resolutions || {}) const updated = Object.assign(pkgJson, obj) - fs.writeFileSync(pkgJsonFile, JSON.stringify(updated, null, 2)) + writeFileSync(pkgJsonFile, JSON.stringify(updated, null, 2)) return updated } @@ -55,15 +54,16 @@ export class Executor { public constructor(options: ExecutorOptions) { this.pluginDir = options.pluginDir this.testFileName = options.testFileName - this.parentDir = path.basename(path.dirname(this.pluginDir)) - this.pluginName = path.basename(this.pluginDir) - + this.parentDir = basename(dirname(this.pluginDir)) + this.pluginName = basename(this.pluginDir) + this.usesJsScript = existsSync(join(this.pluginDir, 'bin', 'run.js')) this.debug = debug.extend(`${this.testFileName}:${this.parentDir}:${this.pluginName}`) } - public clone(repo: string): Promise { - const result = this.exec(`git clone ${repo} ${this.pluginDir} --depth 1`) - this.usesJsScript = fs.existsSync(path.join(this.pluginDir, 'bin', 'run.js')) + public clone(repo: string, branch?: string): Promise { + const cmd = branch ? `git clone --branch ${branch} ${repo} ${this.pluginDir} --depth 1` : `git clone ${repo} ${this.pluginDir} --depth 1` + const result = this.exec(cmd) + this.usesJsScript = existsSync(join(this.pluginDir, 'bin', 'run.js')) return result } @@ -72,9 +72,9 @@ export class Executor { } public executeCommand(cmd: string, script: 'run' | 'dev' = 'run', options: ExecOptions = {}): Promise { - const executable = process.platform === 'win32' ? - path.join('bin', `${script}.cmd`) : - path.join('bin', `${script}${this.usesJsScript ? '.js' : ''}`) + const executable = process.platform === 'win32' + ? join('bin', `${script}.cmd`) + : join('bin', `${script}${this.usesJsScript ? '.js' : ''}`) return this.executeInTestDir(`${executable} ${cmd}`, options) } @@ -85,11 +85,7 @@ export class Executor { this.debug(cmd, chalk.dim(`(cwd: ${cwd})`)) if (silent) { try { - const r = cp.execSync(cmd, { - stdio: 'pipe', - ...options, - cwd, - }) + const r = execSync(cmd, {...options, stdio: 'pipe', cwd}) const stdout = r.toString() this.debug(stdout) resolve({code: 0, stdout}) @@ -105,7 +101,7 @@ export class Executor { }) } } else { - cp.execSync(cmd, {stdio: 'inherit', cwd}) + execSync(cmd, {...options, stdio: 'inherit', cwd}) resolve({code: 0}) } }) @@ -128,12 +124,12 @@ export class Executor { * - OCLIF_CORE_E2E_SKIP_SETUP: skip all the setup steps (useful if iterating on tests) */ export async function setup(testFile: string, options: SetupOptions): Promise { - const testFileName = path.basename(testFile) - const dir = process.env.OCLIF_CORE_E2E_TEST_DIR || os.tmpdir() - const testDir = options.subDir ? path.join(dir, testFileName, options.subDir) : path.join(dir, testFileName) + const testFileName = basename(testFile) + const dir = process.env.OCLIF_CORE_E2E_TEST_DIR || tmpdir() + const testDir = options.subDir ? join(dir, testFileName, options.subDir) : join(dir, testFileName) const name = options.repo.slice(options.repo.lastIndexOf('/') + 1) - const pluginDir = path.join(testDir, name) + const pluginDir = join(testDir, name) const executor = new Executor({pluginDir, testFileName}) executor.debug('plugin directory:', pluginDir) @@ -144,25 +140,25 @@ export async function setup(testFile: string, options: SetupOptions): Promise ({...x, [y]: 'latest'}), {}) pjson = updatePkgJson(pluginDir, { - resolutions: {'@oclif/core': path.resolve('.')}, - dependencies: Object.assign(dependencies, pluginDeps), + ...(options.noLinkCore ? {} : {resolutions: {'@oclif/core': resolve('.')}}), + dependencies: {...dependencies, ...pluginDeps}, oclif: {plugins: options.plugins}, }) } else { pjson = updatePkgJson(pluginDir, { - resolutions: {'@oclif/core': path.resolve('.')}, + ...(options.noLinkCore ? {} : {resolutions: {'@oclif/core': resolve('.')}}), dependencies, }) } @@ -171,10 +167,10 @@ export async function setup(testFile: string, options: SetupOptions): Promise diff --git a/test/interfaces/flags.test-types.ts b/test/interfaces/flags.test-types.ts index cfd0c4e23..141a01dcd 100644 --- a/test/interfaces/flags.test-types.ts +++ b/test/interfaces/flags.test-types.ts @@ -3,8 +3,9 @@ */ import {Command, Flags, Interfaces} from '../../src' -import {expectType, expectNotType} from 'tsd' -import {URL} from 'url' + +import {expectNotType, expectType} from 'tsd' +import {URL} from 'node:url' abstract class BaseCommand extends Command { static enableJsonFlag = true @@ -23,9 +24,9 @@ type MyType = { } export const customFlagWithRequiredProp = Flags.custom({ - parse: async (input, _, opts) => { + async parse(input, _, opts) { const value = opts.unit === 'minutes' ? new Date(input).getMinutes() : new Date(input).getSeconds() - return Promise.resolve(value) + return value }, default: async _ctx => _ctx.options.unit === 'minutes' ? 1 : 2, defaultHelp: async _ctx => _ctx.options.unit === 'minutes' ? '1 minute' : '2 seconds', @@ -37,6 +38,61 @@ export const arrayFlag = Flags.custom({ multiple: true, }) +const options = ['foo', 'bar'] as const + +Flags.option({ + options, + multiple: true, + // @ts-expect-error because multiple is true, default must be an array + default: 'foo', +}) + +Flags.option({options})({ + multiple: true, + // @ts-expect-error because multiple is true, default must be an array + default: 'foo', +}) + +// @ts-expect-error because multiple is false, default must be a single value +Flags.option({ + options, + default: ['foo'], +}) + +// @ts-expect-error because multiple is false, default must be a single value +Flags.option({options, multiple: false})({ + default: ['foo'], +}) + +Flags.custom({ + options, + multiple: true, + // @ts-expect-error because multiple is true, default must be an array + default: 'foo', +}) + +Flags.custom()({ + multiple: true, + // @ts-expect-error because multiple is true, default must be an array + default: 'foo', +}) + +Flags.custom({multiple: true})({ + // @ts-expect-error because multiple is true, default must be an array + default: 'foo', +}) + +// @ts-expect-error because multiple is false, default must be a single value +Flags.custom({ + options, + default: ['foo'], +}) + +// @ts-expect-error because multiple is false, default must be a single value +Flags.custom({multiple: false})({ + default: ['foo'], +}) + class MyCommand extends BaseCommand { static description = 'describe the command here' @@ -45,85 +101,242 @@ class MyCommand extends BaseCommand { ] static flags = { - requiredString: Flags.string({required: true}), - optionalString: Flags.string(), - defaultString: Flags.string({default: 'default'}), + string: Flags.string(), + 'string#opts:required': Flags.string({required: true}), + 'string#opts:default': Flags.string({default: 'default'}), - requiredMultiString: Flags.string({required: true, multiple: true}), - optionalMultiString: Flags.string({multiple: true}), - defaultMultiString: Flags.string({ + 'string#opts:multiple,required': Flags.string({required: true, multiple: true}), + 'string#opts:multiple': Flags.string({multiple: true}), + 'string#opts:multiple,default': Flags.string({ multiple: true, default: ['default'], defaultHelp: async _ctx => 'defaultHelp', }), - requiredBoolean: Flags.boolean({required: true}), - optionalBoolean: Flags.boolean(), - defaultBoolean: Flags.boolean({default: true}), + boolean: Flags.boolean(), + 'boolean#opts:required': Flags.boolean({required: true}), + 'boolean#opts:default': Flags.boolean({default: true}), - optionalInteger: Flags.integer(), - requiredInteger: Flags.integer({required: true}), - defaultInteger: Flags.integer({default: 1}), + integer: Flags.integer(), + 'integer#opts:required': Flags.integer({required: true}), + 'integer#opts:default': Flags.integer({default: 1}), - optionalMultiInteger: Flags.integer({multiple: true}), - requiredMultiInteger: Flags.integer({multiple: true, required: true}), - defaultMultiInteger: Flags.integer({multiple: true, default: [1]}), + 'integer#opts:multiple': Flags.integer({multiple: true}), + 'integer#opts:multiple,required': Flags.integer({multiple: true, required: true}), + 'integer#opts:multiple,default': Flags.integer({multiple: true, default: [1]}), - optionalDirectory: Flags.directory(), - requiredDirectory: Flags.directory({required: true}), - defaultDirectory: Flags.directory({default: 'my-dir'}), + directory: Flags.directory(), + 'directory#opts:required': Flags.directory({required: true}), + 'directory#opts:default': Flags.directory({default: 'my-dir'}), - optionalMultiDirectory: Flags.directory({multiple: true}), - requiredMultiDirectory: Flags.directory({multiple: true, required: true}), - defaultMultiDirectory: Flags.directory({multiple: true, default: ['my-dir']}), + 'directory#opts:multiple': Flags.directory({multiple: true}), + 'directory#opts:multiple,required': Flags.directory({multiple: true, required: true}), + 'directory#opts:multiple,default': Flags.directory({multiple: true, default: ['my-dir']}), - optionalFile: Flags.file(), - requiredFile: Flags.file({required: true}), - defaultFile: Flags.file({default: 'my-file.json'}), + file: Flags.file(), + 'file#opts:required': Flags.file({required: true}), + 'file#opts:default': Flags.file({default: 'my-file.json'}), - optionalMultiFile: Flags.file({multiple: true}), - requiredMultiFile: Flags.file({multiple: true, required: true}), - defaultMultiFile: Flags.file({multiple: true, default: ['my-file.json']}), + 'file#opts:multiple': Flags.file({multiple: true}), + 'file#opts:multiple,required': Flags.file({multiple: true, required: true}), + 'file#opts:multiple,default': Flags.file({multiple: true, default: ['my-file.json']}), - optionalUrl: Flags.url(), - requiredUrl: Flags.url({required: true}), - defaultUrl: Flags.url({ + url: Flags.url(), + 'url#opts:required': Flags.url({required: true}), + 'url#opts:default': Flags.url({ default: new URL('http://example.com'), defaultHelp: async _ctx => 'Example URL', }), - optionalMultiUrl: Flags.url({multiple: true}), - requiredMultiUrl: Flags.url({multiple: true, required: true}), - defaultMultiUrl: Flags.url({multiple: true, default: [new URL('http://example.com')]}), + 'url#opts:multiple': Flags.url({multiple: true}), + 'url#opts:multiple,required': Flags.url({multiple: true, required: true}), + 'url#opts:multiple,default': Flags.url({multiple: true, default: [new URL('http://example.com')]}), - optionalCustom: Flags.custom({ + custom: Flags.custom({ parse: async () => ({foo: true}), })(), - requiredCustom: Flags.custom({ + 'custom#opts:required': Flags.custom({ parse: async () => ({foo: true}), })({required: true}), - defaultCustom: Flags.custom({ + 'custom#opts:default': Flags.custom({ parse: async () => ({foo: true}), + })({ default: async _ctx => ({foo: true}), - })({default: {foo: true}}), + }), - optionalMultiCustom: Flags.custom({ + 'custom#opts:multiple': Flags.custom({ parse: async () => ({foo: true}), })({multiple: true}), - requiredMultiCustom: Flags.custom({ + 'custom#opts:multiple,required': Flags.custom({ parse: async () => ({foo: true}), })({required: true, multiple: true}), - defaultMultiCustom: Flags.custom({ + 'custom#opts:multiple,default': Flags.custom({ parse: async () => ({foo: true}), })({default: [{foo: true}], multiple: true}), - optionalCustomFlagWithRequiredProp: customFlagWithRequiredProp({unit: 'minutes'}), - requiredCustomFlagWithRequiredProp: customFlagWithRequiredProp({unit: 'minutes', required: true}), - defaultCustomFlagWithRequiredProp: customFlagWithRequiredProp({unit: 'minutes', default: 23}), + 'custom#opts:custom-prop': customFlagWithRequiredProp({unit: 'minutes'}), + 'custom#opts:custom-prop,required': customFlagWithRequiredProp({unit: 'minutes', required: true}), + 'custom#opts:custom-prop,default': customFlagWithRequiredProp({unit: 'minutes', default: 23}), + + 'custom#defs:multiple,delimiter': arrayFlag(), + 'custom#defs:multiple,delimiter;opts:required': arrayFlag({required: true}), + 'custom#defs:multiple,delimiter;opts:default': arrayFlag({default: ['foo', 'bar']}), + + option: Flags.option({ + options, + })(), + 'option#opts:required': Flags.option({ + options, + })({required: true}), + 'option#opts:default': Flags.option({ + options, + })({default: 'foo'}), + + 'option#opts:multiple': Flags.option({ + options, + })({multiple: true}), + 'option#opts:multiple,required': Flags.option({ + options, + })({required: true, multiple: true}), + 'option#opts:multiple,default': Flags.option({ + options, + })({default: async _ctx => ['foo'], multiple: true}), + + 'custom#defs:required': Flags.custom({ + required: true, + })(), + 'custom#defs:default': Flags.custom({ + default: 'foo', + })(), + 'custom#defs:multiple': Flags.custom({ + multiple: true, + })(), + 'custom#defs:multiple,required': Flags.custom({ + multiple: true, + required: true, + })(), + 'custom#defs:multiple,default': Flags.custom({ + multiple: true, + default: ['foo'], + })(), + + 'option#defs:required': Flags.option({ + options, + required: true, + })(), + 'option#defs:default': Flags.option({ + options, + default: async _ctx => 'foo', + })(), + 'option#defs:multiple': Flags.option({ + options, + multiple: true, + })(), + 'option#defs:multiple,required': Flags.option({ + options, + multiple: true, + required: true, + })(), + 'option#defs,multiple,default': Flags.option({ + options, + multiple: true, + default: async _ctx => ['foo'], + })(), + + 'option#defs:multiple;opts:default': Flags.option({ + options, + multiple: true, + })({ + default: ['foo'], + }), + + 'option#defs:multiple;opts:default-callback': Flags.option({ + options, + multiple: true, + })({ + default: async _ctx => ['foo'], + }), + + 'custom#defs:multiple;opts:default-callback': Flags.custom({ + options, + multiple: true, + })({ + default: async _ctx => ['foo'], + }), + + 'custom#defs:multiple,parse': Flags.custom({ + multiple: true, + parse: async (input, _ctx, _opts) => input, + })(), + + 'option#defs:multiple,prase': Flags.option({ + options, + multiple: true, + parse: async (input, _ctx, _opts) => input as typeof options[number], + })(), - optionalArrayFlag: arrayFlag(), - requiredArrayFlag: arrayFlag({required: true}), - defaultArrayFlag: arrayFlag({default: ['foo', 'bar']}), + 'custom#defs:multiple=true;opts:multiple=false': Flags.custom({ + multiple: true, + })({ + multiple: false, + }), + 'custom#defs:multiple=false;opts:multiple=true': Flags.custom({ + multiple: false, + })({ + multiple: true, + }), + 'custom#defs:required=true;opts:required=false': Flags.custom({ + required: true, + })({ + required: false, + }), + 'custom#defs:required=false;opts:required=true': Flags.custom({ + required: false, + })({ + required: true, + }), + 'custom#defs:multiple=true;opts:multiple=false,required=true': Flags.custom({ + multiple: true, + })({ + multiple: false, + required: true, + }), + 'custom#defs:required=true;opts:multiple=true,required=false': Flags.custom({ + required: true, + })({ + multiple: true, + required: false, + }), + 'custom#defs:required=false;opts:multiple=true,required=true': Flags.custom({ + required: false, + })({ + multiple: true, + required: true, + }), + + 'custom#defs:multiple=true,required=true;opts:multiple=false,required=false': Flags.custom({ + multiple: true, + required: true, + })({ + multiple: false, + required: false, + }), + + 'custom#defs:multiple=false,required=false;opts:multiple=true,required=true': Flags.custom({ + multiple: false, + required: false, + })({ + multiple: true, + required: true, + }), + + 'custom#defs:multiple=true;opts:multiple=false,default': Flags.custom({ + multiple: true, + })({ + multiple: false, + // TODO: THIS IS A BUG. It should enforce a single value instead of allowing a single value or an array + default: ['foo'], + }), } public static '--' = true @@ -141,92 +354,161 @@ class MyCommand extends BaseCommand { expectNotType(this.flags.defaultGlobalFlag) expectType(this.flags.optionalGlobalFlag) - expectType(this.flags.requiredString) - expectNotType(this.flags.requiredString) - - expectType(this.flags.defaultString) - expectNotType(this.flags.defaultString) - - expectType(this.flags.optionalString) - - expectType(this.flags.requiredMultiString) - expectNotType(this.flags.requiredMultiString) - - expectType(this.flags.optionalMultiString) - expectType(this.flags.defaultMultiString) - expectNotType(this.flags.defaultMultiString) - - expectType(this.flags.requiredBoolean) - expectNotType(this.flags.requiredBoolean) - expectType(this.flags.defaultBoolean) - expectNotType(this.flags.defaultBoolean) - expectType(this.flags.optionalBoolean) - - expectType(this.flags.requiredInteger) - expectNotType(this.flags.requiredInteger) - expectType(this.flags.defaultInteger) - expectNotType(this.flags.defaultInteger) - expectType(this.flags.optionalInteger) - - expectType(this.flags.requiredMultiInteger) - expectNotType(this.flags.requiredMultiInteger) - expectType(this.flags.defaultMultiInteger) - expectNotType(this.flags.defaultMultiInteger) - expectType(this.flags.optionalMultiInteger) - - expectType(this.flags.requiredDirectory) - expectNotType(this.flags.requiredDirectory) - expectType(this.flags.defaultDirectory) - expectNotType(this.flags.defaultDirectory) - expectType(this.flags.optionalDirectory) - - expectType(this.flags.requiredMultiDirectory) - expectNotType(this.flags.requiredMultiDirectory) - expectType(this.flags.defaultMultiDirectory) - expectNotType(this.flags.defaultMultiDirectory) - expectType(this.flags.optionalMultiDirectory) - - expectType(this.flags.requiredFile) - expectNotType(this.flags.requiredFile) - expectType(this.flags.defaultFile) - expectNotType(this.flags.defaultFile) - expectType(this.flags.optionalFile) - - expectType(this.flags.requiredMultiFile) - expectNotType(this.flags.requiredMultiFile) - expectType(this.flags.defaultMultiFile) - expectNotType(this.flags.defaultMultiFile) - expectType(this.flags.optionalMultiFile) - - expectType(this.flags.requiredUrl) - expectNotType(this.flags.requiredUrl) - expectType(this.flags.defaultUrl) - expectNotType(this.flags.defaultUrl) - expectType(this.flags.optionalUrl) - - expectType(this.flags.requiredMultiUrl) - expectNotType(this.flags.requiredMultiUrl) - expectType(this.flags.defaultMultiUrl) - expectNotType(this.flags.defaultMultiUrl) - expectType(this.flags.optionalMultiUrl) - - expectType(this.flags.requiredCustom) - expectNotType(this.flags.requiredCustom) - expectType(this.flags.defaultCustom) - expectNotType(this.flags.defaultCustom) - expectType(this.flags.optionalCustom) - - expectType(this.flags.requiredMultiCustom) - expectNotType(this.flags.requiredMultiCustom) - expectType(this.flags.defaultMultiCustom) - expectNotType(this.flags.defaultMultiCustom) - expectType(this.flags.optionalMultiCustom) - - expectType(this.flags.optionalCustomFlagWithRequiredProp) - expectType(this.flags.requiredCustomFlagWithRequiredProp) - expectNotType(this.flags.requiredCustomFlagWithRequiredProp) - expectType(this.flags.defaultCustomFlagWithRequiredProp) - expectNotType(this.flags.defaultCustomFlagWithRequiredProp) + expectType(this.flags['string#opts:required']) + expectNotType(this.flags['string#opts:required']) + + expectType(this.flags['string#opts:default']) + expectNotType(this.flags['string#opts:default']) + + expectType(this.flags.string) + + expectType(this.flags['string#opts:multiple,required']) + expectNotType(this.flags['string#opts:multiple,required']) + + expectType(this.flags['string#opts:multiple']) + expectType(this.flags['string#opts:multiple,default']) + expectNotType(this.flags['string#opts:multiple,default']) + + expectType(this.flags['boolean#opts:required']) + expectNotType(this.flags['boolean#opts:required']) + expectType(this.flags['boolean#opts:default']) + expectNotType(this.flags['boolean#opts:default']) + expectType(this.flags.boolean) + + expectType(this.flags['integer#opts:required']) + expectNotType(this.flags['integer#opts:required']) + expectType(this.flags['integer#opts:default']) + expectNotType(this.flags['integer#opts:default']) + expectType(this.flags.integer) + + expectType(this.flags['integer#opts:multiple,required']) + expectNotType(this.flags['integer#opts:multiple,required']) + expectType(this.flags['integer#opts:multiple,default']) + expectNotType(this.flags['integer#opts:multiple,default']) + expectType(this.flags['integer#opts:multiple']) + + expectType(this.flags['directory#opts:required']) + expectNotType(this.flags['directory#opts:required']) + expectType(this.flags['directory#opts:default']) + expectNotType(this.flags['directory#opts:default']) + expectType(this.flags.directory) + + expectType(this.flags['directory#opts:multiple,required']) + expectNotType(this.flags['directory#opts:multiple,required']) + expectType(this.flags['directory#opts:multiple,default']) + expectNotType(this.flags['directory#opts:multiple,default']) + expectType(this.flags['directory#opts:multiple']) + + expectType(this.flags['file#opts:required']) + expectNotType(this.flags['file#opts:required']) + expectType(this.flags['file#opts:default']) + expectNotType(this.flags['file#opts:default']) + expectType(this.flags.file) + + expectType(this.flags['file#opts:multiple,required']) + expectNotType(this.flags['file#opts:multiple,required']) + expectType(this.flags['file#opts:multiple,default']) + expectNotType(this.flags['file#opts:multiple,default']) + expectType(this.flags['file#opts:multiple']) + + expectType(this.flags['url#opts:required']) + expectNotType(this.flags['url#opts:required']) + expectType(this.flags['url#opts:default']) + expectNotType(this.flags['url#opts:default']) + expectType(this.flags.url) + + expectType(this.flags['url#opts:multiple,required']) + expectNotType(this.flags['url#opts:multiple,required']) + expectType(this.flags['url#opts:multiple,default']) + expectNotType(this.flags['url#opts:multiple,default']) + expectType(this.flags['url#opts:multiple']) + + expectType(this.flags['custom#opts:required']) + expectNotType(this.flags['custom#opts:required']) + expectType(this.flags['custom#opts:default']) + expectNotType(this.flags['custom#opts:default']) + expectType(this.flags.custom) + + expectType(this.flags['custom#opts:multiple,required']) + expectNotType(this.flags['custom#opts:multiple,required']) + expectType(this.flags['custom#opts:multiple,default']) + expectNotType(this.flags['custom#opts:multiple,default']) + expectType(this.flags['custom#opts:multiple']) + + expectType(this.flags['custom#opts:custom-prop']) + expectType(this.flags['custom#opts:custom-prop,required']) + expectNotType(this.flags['custom#opts:custom-prop,required']) + expectType(this.flags['custom#opts:custom-prop,default']) + expectNotType(this.flags['custom#opts:custom-prop,default']) + + expectType(this.flags['custom#defs:multiple,delimiter;opts:required']) + expectNotType(this.flags['custom#defs:multiple,delimiter;opts:required']) + expectType(this.flags['custom#defs:multiple,delimiter;opts:default']) + expectNotType(this.flags['custom#defs:multiple,delimiter;opts:default']) + expectType(this.flags['custom#defs:multiple,delimiter']) + + expectType(this.flags['option#opts:required']) + expectNotType(this.flags['option#opts:required']) + expectType(this.flags['option#opts:default']) + expectNotType(this.flags['option#opts:default']) + expectType(this.flags.option) + + expectType(this.flags['option#opts:multiple,required']) + expectNotType(this.flags['option#opts:multiple,required']) + expectType(this.flags['option#opts:multiple,default']) + expectNotType(this.flags['option#opts:multiple,default']) + expectType(this.flags['option#opts:multiple']) + + expectType(this.flags['custom#defs:required']) + expectNotType(this.flags['custom#defs:required']) + expectType(this.flags['custom#defs:default']) + expectNotType(this.flags['custom#defs:default']) + expectType(this.flags['custom#defs:multiple']) + expectNotType(this.flags['custom#defs:multiple']) + expectType(this.flags['custom#defs:multiple,required']) + expectNotType(this.flags['custom#defs:multiple,required']) + expectType(this.flags['custom#defs:multiple,default']) + expectNotType(this.flags['custom#defs:multiple,default']) + + expectType(this.flags['option#defs:required']) + expectNotType(this.flags['option#defs:required']) + expectType(this.flags['option#defs:default']) + expectNotType(this.flags['option#defs:default']) + expectType(this.flags['option#defs:multiple']) + expectNotType(this.flags['option#defs:multiple']) + expectType(this.flags['option#defs:multiple,required']) + expectNotType(this.flags['option#defs:multiple,required']) + expectType(this.flags['option#defs,multiple,default']) + expectNotType(this.flags['option#defs,multiple,default']) + + expectType(this.flags['option#defs:multiple;opts:default']) + expectNotType(this.flags['option#defs:multiple;opts:default']) + + expectType(this.flags['option#defs:multiple;opts:default-callback']) + expectNotType(this.flags['option#defs:multiple;opts:default-callback']) + + expectType(this.flags['custom#defs:multiple;opts:default-callback']) + + expectType(this.flags['custom#defs:multiple,parse']) + + expectType<(typeof options[number])[] | undefined>(this.flags['option#defs:multiple,prase']) + + expectType(this.flags['custom#defs:multiple=true;opts:multiple=false']) + expectType(this.flags['custom#defs:multiple=false;opts:multiple=true']) + expectType(this.flags['custom#defs:required=true;opts:required=false']) + expectType(this.flags['custom#defs:required=false;opts:required=true']) + expectNotType(this.flags['custom#defs:required=false;opts:required=true']) + expectType(this.flags['custom#defs:multiple=true;opts:multiple=false,required=true']) + expectNotType(this.flags['custom#defs:multiple=true;opts:multiple=false,required=true']) + expectType(this.flags['custom#defs:required=true;opts:multiple=true,required=false']) + expectType(this.flags['custom#defs:required=false;opts:multiple=true,required=true']) + expectNotType(this.flags['custom#defs:required=false;opts:multiple=true,required=true']) + expectType(this.flags['custom#defs:multiple=true,required=true;opts:multiple=false,required=false']) + expectType(this.flags['custom#defs:multiple=false,required=false;opts:multiple=true,required=true']) + expectNotType(this.flags['custom#defs:multiple=false,required=false;opts:multiple=true,required=true']) + + // TODO: Known issue with `default` not enforcing the correct type whenever multiple is defaulted to true but then overridden to false + // expectType(this.flags['custom#defs:multiple=true;opts:multiple=false,default']) return result.flags } diff --git a/test/module-loader/module-loader.test.ts b/test/module-loader/module-loader.test.ts index f22035049..47135c0d1 100644 --- a/test/module-loader/module-loader.test.ts +++ b/test/module-loader/module-loader.test.ts @@ -1,9 +1,9 @@ -import * as path from 'path' +import {resolve} from 'node:path' import {assert, expect} from 'chai' import {Config} from '../../src' -import ModuleLoader from '../../src/module-loader' +import {isPathModule, load, loadWithData} from '../../src/module-loader' import {ModuleLoadError} from '../../src/errors' // The following data object contains an array of module loading data for errors and successful loading conditions and @@ -15,14 +15,14 @@ const data = { { path: './test/module-loader/fixtures/esm/errors/bad_path.js', type: ModuleLoadError, - message: `[MODULE_NOT_FOUND] import() failed to load ${path.resolve('./test/module-loader/fixtures/esm/errors/bad_path.js')}`, + message: `[MODULE_NOT_FOUND] import() failed to load ${resolve('./test/module-loader/fixtures/esm/errors/bad_path.js')}`, isESM: true, }, // Non-existent path / no extension { path: './test/module-loader/fixtures/esm/errors/bad_path', type: ModuleLoadError, - message: `[MODULE_NOT_FOUND] require failed to load ${path.resolve('./test/module-loader/fixtures/esm/errors/bad_path')}`, + message: `[MODULE_NOT_FOUND] require failed to load ${resolve('./test/module-loader/fixtures/esm/errors/bad_path')}`, isESM: true, }, @@ -30,14 +30,14 @@ const data = { { path: './test/module-loader/fixtures/cjs/errors/bad_path.cjs', type: ModuleLoadError, - message: `[MODULE_NOT_FOUND] require failed to load ${path.resolve('./test/module-loader/fixtures/cjs/errors/bad_path.cjs')}`, + message: `[MODULE_NOT_FOUND] require failed to load ${resolve('./test/module-loader/fixtures/cjs/errors/bad_path.cjs')}`, isESM: false, }, // Non-existent path / no extension { path: './test/module-loader/fixtures/cjs/errors/bad_path', type: ModuleLoadError, - message: `[MODULE_NOT_FOUND] require failed to load ${path.resolve('./test/module-loader/fixtures/cjs/errors/bad_path')}`, + message: `[MODULE_NOT_FOUND] require failed to load ${resolve('./test/module-loader/fixtures/cjs/errors/bad_path')}`, isESM: false, }, @@ -62,21 +62,21 @@ const data = { { path: './test/module-loader/fixtures/esm/success.js', defaultModule: '{"default":"SUCCESS","namedExport":"SUCCESS_NAMED"}', - filePath: `${path.resolve('./test/module-loader/fixtures/esm/success.js')}`, + filePath: `${resolve('./test/module-loader/fixtures/esm/success.js')}`, isESM: true, }, // ESM source file loaded due to mjs file type. { path: './test/module-loader/fixtures/esm/empty-package/success-ext.mjs', defaultModule: '{"default":"SUCCESS_MJS","namedExport":"SUCCESS_NAMED_MJS"}', - filePath: `${path.resolve('./test/module-loader/fixtures/esm/empty-package/success-ext.mjs')}`, + filePath: `${resolve('./test/module-loader/fixtures/esm/empty-package/success-ext.mjs')}`, isESM: true, }, // No extension / ESM source file. Loads package.json in './test/module-loader/fixtures/esm/' for getPackageType check. { path: './test/module-loader/fixtures/esm/success', defaultModule: '{"default":"SUCCESS","namedExport":"SUCCESS_NAMED"}', - filePath: `${path.resolve('./test/module-loader/fixtures/esm/success.js')}`, + filePath: `${resolve('./test/module-loader/fixtures/esm/success.js')}`, isESM: true, isESMOverride: false, // With no extension `ModuleLoader.isPathModule` will return CJS }, @@ -84,7 +84,7 @@ const data = { { path: './test/module-loader/fixtures/esm/empty-package/success-ext', defaultModule: '{"default":"SUCCESS_MJS","namedExport":"SUCCESS_NAMED_MJS"}', - filePath: `${path.resolve('./test/module-loader/fixtures/esm/empty-package/success-ext.mjs')}`, + filePath: `${resolve('./test/module-loader/fixtures/esm/empty-package/success-ext.mjs')}`, isESM: true, isESMOverride: false, // With no extension `ModuleLoader.isPathModule` will return CJS }, @@ -93,28 +93,28 @@ const data = { { path: './test/module-loader/fixtures/cjs/success.js', defaultModule: '["SUCCESS"]', - filePath: `${path.resolve('./test/module-loader/fixtures/cjs/success.js')}`, + filePath: `${resolve('./test/module-loader/fixtures/cjs/success.js')}`, isESM: false, }, // CJS source file loaded due to cjs file type. { path: './test/module-loader/fixtures/cjs/success-ext.cjs', defaultModule: '["SUCCESS_CJS"]', - filePath: `${path.resolve('./test/module-loader/fixtures/cjs/success-ext.cjs')}`, + filePath: `${resolve('./test/module-loader/fixtures/cjs/success-ext.cjs')}`, isESM: false, }, // No extension / CJS source loaded from package.json in './test/module-loader/fixtures/cjs/' which doesn't have "type": "module". { path: './test/module-loader/fixtures/cjs/success', defaultModule: '["SUCCESS"]', - filePath: `${path.resolve('./test/module-loader/fixtures/cjs/success.js')}`, + filePath: `${resolve('./test/module-loader/fixtures/cjs/success.js')}`, isESM: false, }, // No extension / CJS source file loaded due to cjs file type. { path: './test/module-loader/fixtures/cjs/success-ext', defaultModule: '["SUCCESS_CJS"]', - filePath: `${path.resolve('./test/module-loader/fixtures/cjs/success-ext.cjs')}`, + filePath: `${resolve('./test/module-loader/fixtures/cjs/success-ext.cjs')}`, isESM: false, }, @@ -122,7 +122,7 @@ const data = { { path: './test/module-loader/fixtures/esm/index/js', defaultModule: '{"default":"SUCCESS","namedExport":"SUCCESS_NAMED"}', - filePath: `${path.resolve('./test/module-loader/fixtures/esm/index/js/index.js')}`, + filePath: `${resolve('./test/module-loader/fixtures/esm/index/js/index.js')}`, isESM: true, isESMOverride: false, // With no extension `ModuleLoader.isPathModule` will return CJS }, @@ -130,7 +130,7 @@ const data = { { path: './test/module-loader/fixtures/esm/index/mjs', defaultModule: '{"default":"SUCCESS","namedExport":"SUCCESS_NAMED"}', - filePath: `${path.resolve('./test/module-loader/fixtures/esm/index/mjs/index.mjs')}`, + filePath: `${resolve('./test/module-loader/fixtures/esm/index/mjs/index.mjs')}`, isESM: true, isESMOverride: false, // With no extension `ModuleLoader.isPathModule` will return CJS }, @@ -151,7 +151,7 @@ describe('ModuleLoader:', () => { const config = new Config({root: process.cwd()}) await config.load() - const result = await ModuleLoader.load(config, module.path) + const result = await load(config, module.path) // Test that the default module as a string. if (module.defaultModule) { @@ -167,7 +167,7 @@ describe('ModuleLoader:', () => { const config = new Config({root: process.cwd()}) await config.load() - const result = await ModuleLoader.loadWithData(config, module.path) + const result = await loadWithData(config, module.path) // Test the exported module as a string. if (module.defaultModule) { @@ -188,7 +188,7 @@ describe('ModuleLoader:', () => { describe('isPathModule:', () => { for (const module of data.modules) { it(`${module.path}`, () => { - const result = ModuleLoader.isPathModule(module.path) + const result = isPathModule(module.path) // For extensionless ESM data `isPathModule` will return false const test = typeof module.isESMOverride === 'boolean' ? module.isESMOverride : module.isESM @@ -208,7 +208,7 @@ describe('ModuleLoader Failures:', () => { const config = new Config({root: process.cwd()}) await config.load() - await expect(ModuleLoader.load( + await expect(load( config, error.path)).to.eventually.be.rejectedWith(error.message).and.be.an.instanceOf(error.type) }) } @@ -220,7 +220,7 @@ describe('ModuleLoader Failures:', () => { const config = new Config({root: process.cwd()}) await config.load() - await expect(ModuleLoader.loadWithData( + await expect(loadWithData( config, error.path)).to.eventually.be.rejectedWith(error.message).and.be.an.instanceOf(error.type) }) } diff --git a/test/parser/parse.test.ts b/test/parser/parse.test.ts index fb28808c1..642a46f91 100644 --- a/test/parser/parse.test.ts +++ b/test/parser/parse.test.ts @@ -1,11 +1,11 @@ -import {assert, expect, config} from 'chai' -import * as fs from 'fs' +import {assert, config, expect} from 'chai' +import * as fs from 'node:fs' import {parse} from '../../src/parser' import {Args, Flags} from '../../src' import {FlagDefault} from '../../src/interfaces/parser' -import {URL} from 'url' -import * as sinon from 'sinon' +import {URL} from 'node:url' +import {createSandbox, SinonStub} from 'sinon' import {CLIError} from '../../src/errors' config.truncateThreshold = 0 @@ -109,7 +109,7 @@ describe('parse', () => { const out = await parse(['--foo', 'baz'], { flags: { foo: Flags.custom({ - defaultHelp: async () => { + async defaultHelp() { throw new Error('failed to get default help value') }, })(), @@ -831,7 +831,7 @@ See more help with --help`) describe('parse with a default/value of another type (class)', async () => { class TestClass { - public prop: string; + public prop: string constructor(input: string) { this.prop = input } @@ -1310,11 +1310,17 @@ See more help with --help`) }) }) - it('parses multiple flags', async () => { - const out = await parse(['--foo=a', '--foo', 'b'], { - flags: {foo: Flags.string()}, - }) - expect(out.flags.foo).to.equal('b') + it('throws an error when multiple flags of non-multiple flag is provided', async () => { + let message = '' + try { + await parse(['--foo=a', '--foo', 'b'], { + flags: {foo: Flags.string()}, + }) + } catch (error: any) { + message = error.message + } + + expect(message).to.include('can only be specified once') }) describe('dependsOn', () => { @@ -1555,12 +1561,12 @@ See more help with --help`) }) describe('fs flags', () => { - const sandbox = sinon.createSandbox() - let existsStub: sinon.SinonStub - let statStub: sinon.SinonStub + const sandbox = createSandbox() + let accessStub: SinonStub + let statStub: SinonStub beforeEach(() => { - existsStub = sandbox.stub(fs, 'existsSync') + accessStub = sandbox.stub(fs.promises, 'access') statStub = sandbox.stub(fs.promises, 'stat') }) @@ -1574,18 +1580,18 @@ See more help with --help`) const out = await parse([`--dir=${testDir}`], { flags: {dir: Flags.directory({exists: false})}, }) - expect(existsStub.callCount).to.equal(0) + expect(accessStub.callCount).to.equal(0) expect(out.flags).to.deep.include({dir: testDir}) }) it('passes if dir !exists but exists not defined', async () => { const out = await parse([`--dir=${testDir}`], { flags: {dir: Flags.directory()}, }) - expect(existsStub.callCount).to.equal(0) + expect(accessStub.callCount).to.equal(0) expect(out.flags).to.deep.include({dir: testDir}) }) it('passes when dir exists', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isDirectory: () => true}) const out = await parse([`--dir=${testDir}`], { flags: {dir: Flags.directory({exists: true})}, @@ -1593,7 +1599,7 @@ See more help with --help`) expect(out.flags).to.deep.include({dir: testDir}) }) it("fails when dir doesn't exist", async () => { - existsStub.returns(false) + accessStub.throws() try { const out = await parse([`--dir=${testDir}`], { flags: {dir: Flags.directory({exists: true})}, @@ -1607,7 +1613,7 @@ See more help with --help`) } }) it('fails when dir exists but is not a dir', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isDirectory: () => false}) try { const out = await parse([`--dir=${testDir}`], { @@ -1623,7 +1629,7 @@ See more help with --help`) describe('custom parse functions', () => { const customParseException = 'NOT_OK' it('accepts custom parse that passes', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isDirectory: () => true}) const out = await parse([`--dir=${testDir}`], { flags: {dir: Flags.directory({exists: true, parse: async input => input.includes('some') ? input : assert.fail(customParseException)})}, @@ -1632,7 +1638,7 @@ See more help with --help`) }) it('accepts custom parse that fails', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isDirectory: () => true}) try { const out = await parse([`--dir=${testDir}`], { @@ -1655,17 +1661,17 @@ See more help with --help`) flags: {file: Flags.file({exists: false})}, }) expect(out.flags).to.deep.include({file: testFile}) - expect(existsStub.callCount).to.equal(0) + expect(accessStub.callCount).to.equal(0) }) it('passes if file doesn\'t exist but not exists not defined', async () => { const out = await parse([`--file=${testFile}`], { flags: {file: Flags.file()}, }) expect(out.flags).to.deep.include({file: testFile}) - expect(existsStub.callCount).to.equal(0) + expect(accessStub.callCount).to.equal(0) }) it('passes when file exists', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isFile: () => true}) const out = await parse([`--file=${testFile}`], { flags: {file: Flags.file({exists: true})}, @@ -1673,7 +1679,7 @@ See more help with --help`) expect(out.flags).to.deep.include({file: testFile}) }) it("fails when dir doesn't exist", async () => { - existsStub.returns(false) + accessStub.throws() try { const out = await parse([`--file=${testFile}`], { flags: {file: Flags.file({exists: true})}, @@ -1685,7 +1691,7 @@ See more help with --help`) } }) it('fails when file exists but is not a file', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isFile: () => false}) try { const out = await parse([`--file=${testFile}`], { @@ -1700,7 +1706,7 @@ See more help with --help`) describe('custom parse functions', () => { const customParseException = 'NOT_OK' it('accepts custom parse that passes', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isFile: () => true}) const out = await parse([`--dir=${testFile}`], { flags: {dir: Flags.file({exists: false, parse: async input => input.includes('some') ? input : assert.fail(customParseException)})}, @@ -1709,7 +1715,7 @@ See more help with --help`) }) it('accepts custom parse that fails', async () => { - existsStub.returns(true) + accessStub.resolves() statStub.returns({isFile: () => true}) try { const out = await parse([`--dir=${testFile}`], { @@ -1748,5 +1754,71 @@ See more help with --help`) }) expect(out.flags.foo).to.equal(true) }) + + describe('aliased short char', () => { + it('boolean', async () => { + const out = await parse(['-b'], { + flags: { + foo: Flags.boolean({ + charAliases: ['b'], + }), + }, + }) + expect(out.flags.foo).to.equal(true) + }) + it('string', async () => { + const out = await parse(['-b', 'hello'], { + flags: { + foo: Flags.string({ + charAliases: ['b'], + }), + }, + }) + expect(out.flags.foo).to.equal('hello') + }) + it('empty charAliases', async () => { + const out = await parse(['--foo', 'hello'], { + flags: { + foo: Flags.string({ + charAliases: [], + }), + }, + }) + expect(out.flags.foo).to.equal('hello') + }) + it('duplicated flag via charAliases and full name throws error', async () => { + let message = '' + try { + await parse(['--foo', 'hello', '--foo', 'hi'], { + flags: { + foo: Flags.string({ + charAliases: ['b'], + }), + }, + }) + } catch (error: any) { + message = error.message + } + + expect(message).to.include('can only be specified once') + }) + it('duplicated via aliases charAliases throws error', async () => { + let message = '' + try { + await parse(['-b', 'hello', '-b', 'hi'], { + flags: { + foo: Flags.string({ + aliases: ['b'], + charAliases: ['b'], + }), + }, + }) + } catch (error: any) { + message = error.message + } + + expect(message).to.include('can only be specified once') + }) + }) }) }) diff --git a/test/parser/validate.test.ts b/test/parser/validate.test.ts index c637dc311..c5c1513c9 100644 --- a/test/parser/validate.test.ts +++ b/test/parser/validate.test.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert' +import {fail} from 'node:assert' import {expect} from 'chai' import {CLIError} from '../../src/errors' @@ -53,7 +53,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('--dessert=cheesecake cannot also be provided when using --dinner') @@ -145,7 +145,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('Missing required flag') @@ -207,7 +207,7 @@ describe('validate', () => { type: 'all', flags: [ 'cookies', - {name: 'sprinkles', when: async () => Promise.resolve(false)}, + {name: 'sprinkles', when: async () => false}, ], }, ], @@ -268,7 +268,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('All of the following must be provided when using --dessert: --cookies, --sprinkles') @@ -292,7 +292,7 @@ describe('validate', () => { 'cookies', { name: 'sprinkles', - when: async (flags: {birthday: boolean}) => Promise.resolve(flags.birthday), + when: async (flags: {birthday: boolean}) => flags.birthday, }, ], }, @@ -316,7 +316,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('All of the following must be provided when using --dessert: --cookies, --sprinkles') @@ -340,7 +340,7 @@ describe('validate', () => { 'cookies', { name: 'sprinkles', - when: async (flags: {birthday: boolean}) => Promise.resolve(flags.birthday), + when: async (flags: {birthday: boolean}) => flags.birthday, }, ], }, @@ -364,7 +364,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('All of the following must be provided when using --dessert: --cookies') @@ -445,7 +445,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('One of the following must be provided when using --dessert: --cookies, --sprinkles') @@ -469,7 +469,7 @@ describe('validate', () => { 'cookies', { name: 'sprinkles', - when: async (flags: {birthday: boolean}) => Promise.resolve(flags.birthday), + when: async (flags: {birthday: boolean}) => flags.birthday, }, ], }, @@ -493,7 +493,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('One of the following must be provided when using --dessert: --cookies, --sprinkles') @@ -517,7 +517,7 @@ describe('validate', () => { 'cookies', { name: 'sprinkles', - when: async (flags: {birthday: boolean}) => Promise.resolve(flags.birthday), + when: async (flags: {birthday: boolean}) => flags.birthday, }, ], }, @@ -541,7 +541,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('One of the following must be provided when using --dessert: --cookies') @@ -629,7 +629,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('--sprinkles=true cannot also be provided when using --dessert') @@ -652,7 +652,7 @@ describe('validate', () => { flags: [ { name: 'sprinkles', - when: async (flags: {birthday: boolean}) => Promise.resolve(flags.birthday), + when: async (flags: {birthday: boolean}) => flags.birthday, }, ], }, @@ -686,7 +686,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('--sprinkles=true cannot also be provided when using --dessert') @@ -709,7 +709,7 @@ describe('validate', () => { flags: [ { name: 'sprinkles', - when: async (flags: {birthday: boolean}) => Promise.resolve(flags.birthday), + when: async (flags: {birthday: boolean}) => flags.birthday, }, ], }, @@ -759,7 +759,7 @@ describe('validate', () => { type: 'all', flags: [ 'sprinkles', - {name: 'cookies', when: async () => Promise.resolve(true)}, + {name: 'cookies', when: async () => true}, ], }, ], @@ -796,7 +796,7 @@ describe('validate', () => { dessert: { input: [], name: 'dessert', - exclusive: [{name: 'cookies', when: async () => Promise.resolve(true)}], + exclusive: [{name: 'cookies', when: async () => true}], }, }, args: [], @@ -820,7 +820,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('--cookies=false cannot also be provided when using --dessert') @@ -848,24 +848,24 @@ describe('validate', () => { type: 'all', flags: [ 'cookies', - {name: 'sprinkles', when: async () => Promise.resolve(false)}, - {name: 'cake', when: async () => Promise.resolve(true)}, + {name: 'sprinkles', when: async () => false}, + {name: 'cake', when: async () => true}, ], }, { type: 'some', flags: [ 'brownies', - {name: 'pie', when: async () => Promise.resolve(false)}, - {name: 'fudge', when: async () => Promise.resolve(true)}, + {name: 'pie', when: async () => false}, + {name: 'fudge', when: async () => true}, ], }, { type: 'none', flags: [ 'cupcake', - {name: 'muffin', when: async () => Promise.resolve(false)}, - {name: 'scone', when: async () => Promise.resolve(true)}, + {name: 'muffin', when: async () => false}, + {name: 'scone', when: async () => true}, ], }, ], @@ -926,7 +926,7 @@ describe('validate', () => { try { // @ts-expect-error await validate({input, output}) - assert.fail('should have thrown') + fail('should have thrown') } catch (error) { const err = error as CLIError expect(err.message).to.include('All of the following must be provided when using --dessert: --cookies, --cake') diff --git a/test/perf/parser.perf.ts b/test/perf/parser.perf.ts index da529f3c1..55e0ec213 100644 --- a/test/perf/parser.perf.ts +++ b/test/perf/parser.perf.ts @@ -17,7 +17,7 @@ suite .add('simple', { defer: true, - fn: function (deferred: { resolve: () => any }) { + fn(deferred: { resolve: () => any }) { parse(['--bool'], { flags: { bool: Flags.boolean(), @@ -27,17 +27,17 @@ suite }) .add('multiple async flags that take time', { defer: true, - fn: function (deferred: { resolve: () => any }) { + fn(deferred: { resolve: () => any }) { parse(['--flagA', 'foo', '--flagA', 'bar'], { flags: { flagA: Flags.string({ - parse: async input => { + async parse(input) { await delay100() return input }, }), flagB: Flags.string({ - parse: async input => { + async parse(input) { await delay100() return input }, @@ -49,7 +49,7 @@ suite .add('flagstravaganza', { defer: true, - fn: function (deferred: { resolve: () => any }) { + fn(deferred: { resolve: () => any }) { const flags = [ ['--bool'], ['-S', 'foo'], diff --git a/test/util.test.ts b/test/util.test.ts index 42a42e0f8..4820f23a6 100644 --- a/test/util.test.ts +++ b/test/util.test.ts @@ -1,5 +1,5 @@ import {expect} from 'chai' -import {maxBy, sumBy, capitalize, ensureArgObject, last} from '../src/util' +import {capitalize, ensureArgObject, last, maxBy, sumBy} from '../src/util' describe('capitalize', () => { it('capitalizes the string', () => { diff --git a/tsconfig.json b/tsconfig.json index cd1e62b7d..0aed879ee 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,14 +3,14 @@ "declaration": true, "forceConsistentCasingInFileNames": true, "importHelpers": true, - "module": "commonjs", + "module": "Node16", "outDir": "./lib", "pretty": true, "rootDirs": [ "./src" ], "strict": true, - "target": "es2020", + "target": "ES2021", "allowSyntheticDefaultImports": true, "noErrorTruncation": true, "moduleResolution": "Node16" diff --git a/yarn.lock b/yarn.lock index 5da8d009e..16a9fccd4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,176 +2,29 @@ # yarn lockfile v1 -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.14.5": +"@babel/code-frame@^7.0.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== dependencies: "@babel/highlight" "^7.14.5" -"@babel/compat-data@^7.15.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" - integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== - -"@babel/core@^7.12.16": - version "7.15.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/eslint-parser@^7.12.16": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.15.7.tgz#2dc3d0ff0ea22bb1e08d93b4eeb1149bf1c75f2d" - integrity sha512-yJkHyomClm6A2Xzb8pdAo4HzYMSXFn1O5zrCYvbFP0yQFvHueLedV8WiEno8yJOKStjUXzBZzJFeWQ7b3YMsqQ== - dependencies: - eslint-scope "^5.1.1" - eslint-visitor-keys "^2.1.0" - semver "^6.3.0" - -"@babel/generator@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.4.tgz#85acb159a267ca6324f9793986991ee2022a05b0" - integrity sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw== - dependencies: - "@babel/types" "^7.15.4" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-compilation-targets@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" - integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - -"@babel/helper-function-name@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" - integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== - dependencies: - "@babel/helper-get-function-arity" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-get-function-arity@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" - integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-hoist-variables@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" - integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-member-expression-to-functions@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" - integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-imports@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" - integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-transforms@^7.15.4": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz#7da80c8cbc1f02655d83f8b79d25866afe50d226" - integrity sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw== - dependencies: - "@babel/helper-module-imports" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-simple-access" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.6" - -"@babel/helper-optimise-call-expression@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" - integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-replace-supers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" - integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-simple-access@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" - integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-split-export-declaration@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" - integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": +"@babel/helper-validator-identifier@^7.14.5": version "7.15.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helpers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" - integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== - dependencies: - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": +"@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== @@ -180,43 +33,6 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.15.4", "@babel/parser@^7.15.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae" - integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== - -"@babel/template@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" - integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/traverse@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" - integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.15.4", "@babel/types@^7.15.6": - version "7.15.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" - integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== - dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - to-fast-properties "^2.0.0" - "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -366,34 +182,56 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" + integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== + +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== dependencies: - "@humanwhocodes/object-schema" "^1.2.0" + "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@isaacs/cliui@^8.0.2": version "8.0.2" @@ -412,6 +250,13 @@ resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" @@ -443,7 +288,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -747,6 +592,11 @@ "@sigstore/protobuf-specs" "^0.2.0" tuf-js "^1.1.7" +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -754,6 +604,27 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== + dependencies: + type-detect "4.0.8" + +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2", "@sinonjs/fake-timers@^10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^7.0.4", "@sinonjs/fake-timers@^7.1.0", "@sinonjs/fake-timers@^7.1.2": version "7.1.2" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" @@ -770,6 +641,15 @@ lodash.get "^4.4.2" type-detect "^4.0.8" +"@sinonjs/samsam@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.0.tgz#0d488c91efb3fa1442e26abea81759dfc8b5ac60" + integrity sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + "@sinonjs/text-encoding@^0.7.1": version "0.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" @@ -800,10 +680,10 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@tsd/typescript@~4.9.3": - version "4.9.3" - resolved "https://registry.yarnpkg.com/@tsd/typescript/-/typescript-4.9.3.tgz#87beef743237707b02b88b10056fcf9f58840954" - integrity sha512-iyi45620s9QN62rW90KelRJLG7p15g4NhTezxYCHmE5RY/BI270Sn7E8vWtiLzB+9mQgy71sYXcgyDKjtJamSQ== +"@tsd/typescript@~5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@tsd/typescript/-/typescript-5.2.2.tgz#b3a11006737a41abb492fe5a16de17a1c3a0126e" + integrity sha512-VtjHPAKJqLJoHHKBDNofzvQB2+ZVxjXU/Gw6INAS9aINLQYVsxfzrQ2s84huCeYWZRTtrr7R0J7XgpZHjNwBCw== "@tufjs/canonical-json@1.0.0": version "1.0.0" @@ -879,22 +759,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== -"@types/glob@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" - integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== - dependencies: - "@types/minimatch" "^5.1.2" - "@types/node" "*" - -"@types/glob@~7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" - integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - "@types/indent-string@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/indent-string/-/indent-string-4.0.1.tgz#fb4e6b8cdd8e94f70c105e78fb5d357a767b7193" @@ -907,26 +771,26 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.7.tgz#330c5d97a3500e9c903210d6e49f02964af04a0e" integrity sha512-S6+8JAYTE1qdsc9HMVsfY7+SgSuUU/Tp6TYTmITW0PZxiyIMvol3Gy//y69Wkhs0ti4py5qgR3uZH6uz/DNzJQ== -"@types/json-schema@*", "@types/json-schema@^7.0.7": +"@types/json-schema@*": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@^7.0.12": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/lodash@*": version "4.14.182" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== -"@types/minimatch@*": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== - -"@types/minimatch@^5.1.2": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" - integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== - "@types/minimist@^1.2.0": version "1.2.2" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" @@ -951,11 +815,16 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^16": +"@types/node@*": version "16.18.31" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.31.tgz#7de39c2b9363f0d95b129cc969fcbf98e870251c" integrity sha512-KPXltf4z4g517OlVJO9XQ2357CYw7fvuJ3ZuBynjXC5Jos9i+K7LvFb7bUIwtJXSZj0vTp9Q6NJBSQpkwwO8Zw== +"@types/node@^18": + version "18.17.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.17.tgz#53cc07ce582c9d7c5850702a3c2cb0af0d7b0ca1" + integrity sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw== + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -966,18 +835,10 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/proxyquire@^1.3.28": - version "1.3.28" - resolved "https://registry.yarnpkg.com/@types/proxyquire/-/proxyquire-1.3.28.tgz#05a647bb0d8fe48fc8edcc193e43cc79310faa7d" - integrity sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q== - -"@types/shelljs@^0.8.12": - version "0.8.12" - resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.12.tgz#79dc9632af7d5ca1b5afb65a6bfc1422d79b5fa0" - integrity sha512-ZA8U81/gldY+rR5zl/7HSHrG2KDfEb3lzG6uCUDhW1DTQE9yC/VBQ45fXnXq8f3CgInfhZmjtdu/WOUlrXRQUg== - dependencies: - "@types/glob" "~7.2.0" - "@types/node" "*" +"@types/semver@^7.5.0": + version "7.5.2" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" + integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== "@types/sinon@*": version "10.0.2" @@ -1013,74 +874,90 @@ resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== -"@typescript-eslint/eslint-plugin@^4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.31.2.tgz#9f41efaee32cdab7ace94b15bd19b756dd099b0a" - integrity sha512-w63SCQ4bIwWN/+3FxzpnWrDjQRXVEGiTt9tJTRptRXeFvdZc/wLiz3FQUwNQ2CVoRGI6KUWMNUj/pk63noUfcA== +"@typescript-eslint/eslint-plugin@^6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz#f18cc75c9cceac8080a9dc2e7d166008c5207b9f" + integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q== dependencies: - "@typescript-eslint/experimental-utils" "4.31.2" - "@typescript-eslint/scope-manager" "4.31.2" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - regexpp "^3.1.0" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/experimental-utils@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz#98727a9c1e977dd5d20c8705e69cd3c2a86553fa" - integrity sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q== - dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.31.2" - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/typescript-estree" "4.31.2" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/type-utils" "6.7.2" + "@typescript-eslint/utils" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.31.2.tgz#54aa75986e3302d91eff2bbbaa6ecfa8084e9c34" - integrity sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw== - dependencies: - "@typescript-eslint/scope-manager" "4.31.2" - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/typescript-estree" "4.31.2" - debug "^4.3.1" - -"@typescript-eslint/scope-manager@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz#1d528cb3ed3bcd88019c20a57c18b897b073923a" - integrity sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w== - dependencies: - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/visitor-keys" "4.31.2" - -"@typescript-eslint/types@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.31.2.tgz#2aea7177d6d744521a168ed4668eddbd912dfadf" - integrity sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w== - -"@typescript-eslint/typescript-estree@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz#abfd50594d8056b37e7428df3b2d185ef2d0060c" - integrity sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA== - dependencies: - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/visitor-keys" "4.31.2" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" +"@typescript-eslint/parser@^6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.2.tgz#e0ae93771441b9518e67d0660c79e3a105497af4" + integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw== + dependencies: + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + debug "^4.3.4" -"@typescript-eslint/visitor-keys@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz#7d5b4a4705db7fe59ecffb273c1d082760f635cc" - integrity sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug== +"@typescript-eslint/scope-manager@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz#cf59a2095d2f894770c94be489648ad1c78dc689" + integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw== dependencies: - "@typescript-eslint/types" "4.31.2" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + +"@typescript-eslint/type-utils@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz#ed921c9db87d72fa2939fee242d700561454f367" + integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ== + dependencies: + "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/utils" "6.7.2" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066" + integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg== + +"@typescript-eslint/typescript-estree@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz#ce5883c23b581a5caf878af641e49dd0349238c7" + integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ== + dependencies: + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf" + integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.2" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c" + integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ== + dependencies: + "@typescript-eslint/types" "6.7.2" + eslint-visitor-keys "^3.4.1" JSONStream@^1.0.4: version "1.3.5" @@ -1107,7 +984,7 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== @@ -1117,16 +994,16 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - acorn@^8.4.1: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -1149,7 +1026,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1159,17 +1036,7 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.6.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" - integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ansi-colors@4.1.1, ansi-colors@^4.1.1: +ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== @@ -1215,6 +1082,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" @@ -1276,16 +1148,79 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= +array-includes@^3.1.6: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" + 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.prototype.findlastindex@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -1311,6 +1246,11 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1371,17 +1311,6 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.16.6: - version "4.17.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.1.tgz#a98d104f54af441290b7d592626dd541fa642eb9" - integrity sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ== - dependencies: - caniuse-lite "^1.0.30001259" - electron-to-chromium "^1.3.846" - escalade "^3.1.1" - nanocolors "^0.1.5" - node-releases "^1.1.76" - buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -1390,10 +1319,10 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -builtin-modules@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" - integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== builtins@^5.0.0: version "5.0.1" @@ -1420,6 +1349,14 @@ cacache@^17.0.0, cacache@^17.0.4, cacache@^17.1.3: tar "^6.1.11" unique-filename "^3.0.0" +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1444,13 +1381,6 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001259: - version "1.0.30001260" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001260.tgz#e3be3f34ddad735ca4a2736fa9e768ef34316270" - integrity sha512-Fhjc/k8725ItmrvW5QomzxLeojewxvqiYCKeFcfFEhut28IVLdpHU19dneOmltZQIE5HNbawj1HYD+1f2bM1Dg== - dependencies: - nanocolors "^0.1.0" - cardinal@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" @@ -1526,11 +1456,6 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -ci-info@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" - integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== - ci-info@^3.6.1, ci-info@^3.7.1, ci-info@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" @@ -1675,10 +1600,10 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -confusing-browser-globals@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" - integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== +confusing-browser-globals@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== console-control-strings@^1.1.0: version "1.1.0" @@ -1720,13 +1645,6 @@ conventional-commits-parser@^3.0.0: through2 "^4.0.0" trim-off-newlines "^1.0.0" -convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - cosmiconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" @@ -1780,13 +1698,20 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -1824,11 +1749,34 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +define-data-property@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.0.tgz#0db13540704e1d8d479a0656cf781267531b9451" + integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diff@5.0.0, diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -1851,6 +1799,13 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1877,11 +1832,6 @@ ejs@^3.1.8: dependencies: jake "^10.8.5" -electron-to-chromium@^1.3.846: - version "1.3.848" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.848.tgz#94cc196e496f33c0d71cd98561448f10018584cc" - integrity sha512-wchRyBcdcmibioggdO7CbMT5QQ4lXlN/g7Mkpf1K2zINidnqij6EVu94UIZ+h5nB2S9XD4bykqFv9LonAWLFyw== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1899,12 +1849,13 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== +enhanced-resolve@^5.12.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: - ansi-colors "^4.1.1" + graceful-fs "^4.2.4" + tapable "^2.2.0" env-paths@^2.2.0: version "2.2.1" @@ -1923,6 +1874,76 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" + integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.11" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -1943,54 +1964,42 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-config-oclif-typescript@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-1.0.3.tgz#0061a810bf8f69571ad3c70368badcc018c3358e" - integrity sha512-TeJKXWBQ3uKMtzgz++UFNWpe1WCx8mfqRuzZy1LirREgRlVv656SkVG4gNZat5rRNIQgfDmTS+YebxK02kfylA== - dependencies: - "@typescript-eslint/eslint-plugin" "^4.31.2" - "@typescript-eslint/parser" "^4.31.2" - eslint-config-xo-space "^0.29.0" - eslint-plugin-mocha "^9.0.0" +eslint-config-oclif-typescript@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-2.0.1.tgz#bdaca00f53ee27ff6930673082a00a03d6cf8dd1" + integrity sha512-Z0U0KVNXGcTzYdSoDsrHulkspS1ZW/NrNgv/IAvpd7F2ZdrLUEmRlJn7Kwnk8CdUufJb/GsW+qVKIG/fPhwKpg== + dependencies: + "@typescript-eslint/eslint-plugin" "^6.7.2" + "@typescript-eslint/parser" "^6.7.2" + eslint-config-xo-space "^0.34.0" + eslint-import-resolver-typescript "^3.6.0" + eslint-plugin-import "^2.28.1" + eslint-plugin-mocha "^10.1.0" eslint-plugin-node "^11.1.0" -eslint-config-oclif@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-oclif/-/eslint-config-oclif-4.0.0.tgz#90a07587f7be7c92ae3ce750ae11cf1e864239eb" - integrity sha512-5tkUQeC33rHAhJxaGeBGYIflDLumeV2qD/4XLBdXhB/6F/+Jnwdce9wYHSvkx0JUqUQShpQv8JEVkBp/zzD7hg== +eslint-config-oclif@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-oclif/-/eslint-config-oclif-5.0.0.tgz#69c5cc8a19025e71fc49a10f47475bb8dd18ba24" + integrity sha512-yPxtUzU6eFL+WoW8DbRf7uoHxgiu0B/uY7k7rgHwFHij66WoI3qhBNhKI5R5FS5JeTuBOXKrNqQVDsSH0D/JvA== dependencies: - eslint-config-xo-space "^0.27.0" - eslint-plugin-mocha "^9.0.0" + eslint-config-xo-space "^0.34.0" + eslint-plugin-mocha "^10.1.0" eslint-plugin-node "^11.1.0" - eslint-plugin-unicorn "^36.0.0" - -eslint-config-xo-space@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/eslint-config-xo-space/-/eslint-config-xo-space-0.27.0.tgz#9663e41d7bedc0f345488377770565aa9b0085e0" - integrity sha512-b8UjW+nQyOkhiANVpIptqlKPyE7XRyQ40uQ1NoBhzVfu95gxfZGrpliq8ZHBpaOF2wCLZaexTSjg7Rvm99vj4A== - dependencies: - eslint-config-xo "^0.35.0" - -eslint-config-xo-space@^0.29.0: - version "0.29.0" - resolved "https://registry.yarnpkg.com/eslint-config-xo-space/-/eslint-config-xo-space-0.29.0.tgz#5bbd2d0ecb172c4e65022b8543ecb1f7d199b8e2" - integrity sha512-emUZVHjmzl3I1aO2M/2gEpqa/GHXTl7LF/vQeAX4W+mQIU+2kyqY97FkMnSc2J8Osoq+vCSXCY/HjFUmFIF/Ag== - dependencies: - eslint-config-xo "^0.38.0" + eslint-plugin-unicorn "^48.0.1" -eslint-config-xo@^0.35.0: - version "0.35.0" - resolved "https://registry.yarnpkg.com/eslint-config-xo/-/eslint-config-xo-0.35.0.tgz#8b5afca244c44129c32386c28602f973ad5cb762" - integrity sha512-+WyZTLWUJlvExFrBU/Ldw8AB/S0d3x+26JQdBWbcqig2ZaWh0zinYcHok+ET4IoPaEcRRf3FE9kjItNVjBwnAg== +eslint-config-xo-space@^0.34.0: + version "0.34.0" + resolved "https://registry.yarnpkg.com/eslint-config-xo-space/-/eslint-config-xo-space-0.34.0.tgz#974db7f7091edc23e2f29cc98acaa1be78ac87e5" + integrity sha512-8ZI0Ta/loUIL1Wk/ouWvk2ZWN8X6Un49MqnBf2b6uMjC9c5Pcfr1OivEOrvd3niD6BKgMNH2Q9nG0CcCWC+iVA== dependencies: - confusing-browser-globals "1.0.10" + eslint-config-xo "^0.43.0" -eslint-config-xo@^0.38.0: - version "0.38.0" - resolved "https://registry.yarnpkg.com/eslint-config-xo/-/eslint-config-xo-0.38.0.tgz#50cbe676a90d5582e1bbf1de750286e7cf09378e" - integrity sha512-G2jL+VyfkcZW8GoTmqLsExvrWssBedSoaQQ11vyhflDeT3csMdBVp0On+AVijrRuvgmkWeDwwUL5Rj0qDRHK6g== +eslint-config-xo@^0.43.0: + version "0.43.1" + resolved "https://registry.yarnpkg.com/eslint-config-xo/-/eslint-config-xo-0.43.1.tgz#c2ac8993f6e429048c813f599265d1636a0bc060" + integrity sha512-azv1L2PysRA0NkZOgbndUpN+581L7wPqkgJOgxxw3hxwXAbJgD6Hqb/SjHRiACifXt/AvxCzE/jIKFAlI7XjvQ== dependencies: - confusing-browser-globals "1.0.10" + confusing-browser-globals "1.0.11" eslint-formatter-pretty@^4.1.0: version "4.1.0" @@ -2006,6 +2015,35 @@ eslint-formatter-pretty@^4.1.0: string-width "^4.2.0" supports-hyperlinks "^2.0.0" +eslint-import-resolver-node@^0.3.7: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.0.tgz#36f93e1eb65a635e688e16cae4bead54552e3bbd" + integrity sha512-QTHR9ddNnn35RTxlaEnx2gCxqFlF2SEN0SE2d17SqwyM7YOSI2GHWRYp5BiRkObTUNYPupC/3Fq2a0PpT+EKpg== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" + get-tsconfig "^4.5.0" + is-core-module "^2.11.0" + is-glob "^4.0.3" + +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + eslint-plugin-es@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" @@ -2014,13 +2052,36 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-mocha@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-9.0.0.tgz#b4457d066941eecb070dc06ed301c527d9c61b60" - integrity sha512-d7knAcQj1jPCzZf3caeBIn3BnW6ikcvfz0kSqQpwPYcVGLoJV5sz0l0OJB2LR8I7dvTDbqq1oV6ylhSgzA10zg== +eslint-plugin-import@^2.28.1: + version "2.28.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz#63b8b5b3c409bfc75ebaf8fb206b07ab435482c4" + integrity sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A== + dependencies: + array-includes "^3.1.6" + array.prototype.findlastindex "^1.2.2" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.8.0" + has "^1.0.3" + is-core-module "^2.13.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.6" + object.groupby "^1.0.0" + object.values "^1.1.6" + semver "^6.3.1" + tsconfig-paths "^3.14.2" + +eslint-plugin-mocha@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz#69325414f875be87fb2cb00b2ef33168d4eb7c8d" + integrity sha512-xLqqWUF17llsogVOC+8C6/jvQ+4IoOREbN7ZCHuOHuD6cT5cDD4h7f2LgsZuzMAiwswWE21tO7ExaknHVDrSkw== dependencies: eslint-utils "^3.0.0" - ramda "^0.27.1" + rambda "^7.1.0" eslint-plugin-node@^11.1.0: version "11.1.0" @@ -2034,49 +2095,41 @@ eslint-plugin-node@^11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-unicorn@^36.0.0: - version "36.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-36.0.0.tgz#db50e1426839e401d33c5a279f49d4a5bbb640d8" - integrity sha512-xxN2vSctGWnDW6aLElm/LKIwcrmk6mdiEcW55Uv5krcrVcIFSWMmEgc/hwpemYfZacKZ5npFERGNz4aThsp1AA== +eslint-plugin-unicorn@^48.0.1: + version "48.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz#a6573bc1687ae8db7121fdd8f92394b6549a6959" + integrity sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw== dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - ci-info "^3.2.0" + "@babel/helper-validator-identifier" "^7.22.5" + "@eslint-community/eslint-utils" "^4.4.0" + ci-info "^3.8.0" clean-regexp "^1.0.0" - eslint-template-visitor "^2.3.2" - eslint-utils "^3.0.0" - is-builtin-module "^3.1.0" + esquery "^1.5.0" + indent-string "^4.0.0" + is-builtin-module "^3.2.1" + jsesc "^3.0.2" lodash "^4.17.21" pluralize "^8.0.0" read-pkg-up "^7.0.1" - regexp-tree "^0.1.23" - safe-regex "^2.1.1" - semver "^7.3.5" + regexp-tree "^0.1.27" + regjsparser "^0.10.0" + semver "^7.5.4" + strip-indent "^3.0.0" eslint-rule-docs@^1.1.5: version "1.1.235" resolved "https://registry.yarnpkg.com/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz#be6ef1fc3525f17b3c859ae2997fedadc89bfb9b" integrity sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A== -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-template-visitor@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-2.3.2.tgz#b52f96ff311e773a345d79053ccc78275bbc463d" - integrity sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA== - dependencies: - "@babel/core" "^7.12.16" - "@babel/eslint-parser" "^7.12.16" - eslint-visitor-keys "^2.0.0" - esquery "^1.3.1" - multimap "^1.1.0" + estraverse "^5.2.0" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: +eslint-utils@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -2090,80 +2143,82 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: +eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: +eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.32.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.49.0: + version "8.49.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" + optionator "^0.9.3" + strip-ansi "^6.0.1" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.3.1, esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2, esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -2174,11 +2229,6 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" @@ -2204,10 +2254,10 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -fancy-test@^2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.18.tgz#36f938a7207c90b2bf2f85b9c7d0cb6de8444635" - integrity sha512-wA9xzWMJ4L51Jcr9k06koPwi58bbUkTZrqqNYd6z7DHky1jW+D5jc/q86zPmkVdnjOrCg91VOeEzyOjTLIlD8A== +fancy-test@^2.0.34: + version "2.0.35" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.35.tgz#18c0ccd767a7332bea186369a7e7a79a3b4582bb" + integrity sha512-XE0L7yAFOKY2jDnkBDDQ3JBD7xbBFwAFl1Z/5WNKBeVNlaEP08wuRTPE3xj2k+fnUp2CMUfD+6aiIS+4pcrjwg== dependencies: "@types/chai" "*" "@types/lodash" "*" @@ -2215,13 +2265,13 @@ fancy-test@^2.0.16: "@types/sinon" "*" lodash "^4.17.13" mock-stdin "^1.0.0" - nock "^13.3.0" + nock "^13.3.3" stdout-stderr "^0.1.9" -fancy-test@^2.0.34: - version "2.0.35" - resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.35.tgz#18c0ccd767a7332bea186369a7e7a79a3b4582bb" - integrity sha512-XE0L7yAFOKY2jDnkBDDQ3JBD7xbBFwAFl1Z/5WNKBeVNlaEP08wuRTPE3xj2k+fnUp2CMUfD+6aiIS+4pcrjwg== +fancy-test@^3.0.0-beta.2: + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-3.0.0-beta.2.tgz#a6b6b4f3ae30200ce64ff8dd79c73fcefba0058f" + integrity sha512-bXffX78q50U/dm9E0RVesZUQ0IZPmRkMhKJdbipuVOm/WuzdoYXZnT/sr0uyzyGaxtjFcatKhZgPRDk49DlXTw== dependencies: "@types/chai" "*" "@types/lodash" "*" @@ -2230,6 +2280,7 @@ fancy-test@^2.0.34: lodash "^4.17.13" mock-stdin "^1.0.0" nock "^13.3.3" + sinon "^16.0.0" stdout-stderr "^0.1.9" fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: @@ -2248,6 +2299,17 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -2284,14 +2346,6 @@ filelist@^1.0.1: dependencies: minimatch "^3.0.4" -fill-keys@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" - integrity sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA= - dependencies: - is-object "~1.0.1" - merge-descriptors "~1.0.0" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -2333,6 +2387,13 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -2380,10 +2441,20 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gauge@^4.0.3: version "4.0.4" @@ -2413,11 +2484,6 @@ gauge@^5.0.0: strip-ansi "^6.0.1" wide-align "^1.1.5" -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -2428,11 +2494,36 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-tsconfig@^4.5.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.0.tgz#06ce112a1463e93196aa90320c35df5039147e34" + integrity sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw== + dependencies: + resolve-pkg-maps "^1.0.0" + git-raw-commits@^2.0.0: version "2.0.10" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" @@ -2451,6 +2542,13 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob@7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -2505,19 +2603,21 @@ global-dirs@^0.1.1: dependencies: ini "^1.3.4" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.6.0, globals@^13.9.0: - version "13.10.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676" - integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g== +globals@^13.19.0: + version "13.21.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.21.0.tgz#163aae12f34ef502f5153cfbdd3600f36c63c571" + integrity sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg== dependencies: type-fest "^0.20.2" -globby@^11.0.1, globby@^11.0.3, globby@^11.1.0: +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -2529,21 +2629,38 @@ globby@^11.0.1, globby@^11.0.3, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== -graceful-fs@^4.2.11, graceful-fs@^4.2.6: +graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2554,6 +2671,30 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2660,16 +2801,16 @@ ignore-walk@^6.0.0: dependencies: minimatch "^9.0.0" -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - ignore@^5.1.1, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -2729,6 +2870,15 @@ init-package-json@^5.0.0: validate-npm-package-license "^3.0.4" validate-npm-package-name "^5.0.0" +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -2749,11 +2899,27 @@ irregular-plurals@^3.2.0: resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.3.0.tgz#67d0715d4361a60d9fd9ee80af3881c631a31ee2" integrity sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g== +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -2761,12 +2927,25 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-builtin-module@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.1.0.tgz#6fdb24313b1c03b75f8b9711c0feb8c30b903b00" - integrity sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg== +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== dependencies: - builtin-modules "^3.0.0" + builtin-modules "^3.3.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-cidr@^4.0.2: version "4.0.2" @@ -2775,6 +2954,13 @@ is-cidr@^4.0.2: dependencies: cidr-regex "^3.1.1" +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.8.1: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + is-core-module@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" @@ -2782,12 +2968,12 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" -is-core-module@^2.8.1: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: - has "^1.0.3" + has-tostringtag "^1.0.0" is-docker@^2.0.0: version "2.2.1" @@ -2811,11 +2997,30 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -2826,10 +3031,10 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-object@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" - integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-obj@^1.1.0: version "1.1.0" @@ -2841,16 +3046,45 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + is-text-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" @@ -2858,11 +3092,25 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -2875,6 +3123,11 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2899,19 +3152,34 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" +jest-diff@^29.0.3: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.1.0: +js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -js-yaml@^3.13.1, js-yaml@^3.14.1: +js-yaml@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -2919,10 +3187,15 @@ js-yaml@^3.13.1, js-yaml@^3.14.1: argparse "^1.0.7" esprima "^4.0.0" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-parse-better-errors@^1.0.1: version "1.0.2" @@ -2944,11 +3217,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -2964,10 +3232,12 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@^2.1.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" - integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" jsonfile@^6.0.1: version "6.1.0" @@ -3154,11 +3424,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -3169,11 +3434,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -3282,11 +3542,6 @@ meow@^9.0.0: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-descriptors@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -3319,7 +3574,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.1.1: +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -3342,6 +3597,11 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minimist@^1.2.3: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" @@ -3456,36 +3716,21 @@ mock-stdin@^1.0.0: resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" integrity sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q== -module-not-found-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" - integrity sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA= - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.2: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multimap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multimap/-/multimap-1.1.0.tgz#5263febc085a1791c33b59bb3afc6a76a2a10ca8" - integrity sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw== - mute-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== -nanocolors@^0.1.0, nanocolors@^0.1.5: - version "0.1.12" - resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6" - integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ== - nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -3522,6 +3767,17 @@ nise@^5.1.0: just-extend "^4.0.2" path-to-regexp "^1.7.0" +nise@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.4.tgz#491ce7e7307d4ec546f5a659b2efe94a18b4bbc0" + integrity sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg== + dependencies: + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^10.0.2" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + nock@*, nock@^13.3.0: version "13.3.0" resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" @@ -3559,11 +3815,6 @@ node-gyp@^9.0.0, node-gyp@^9.4.0: tar "^6.1.2" which "^2.0.2" -node-releases@^1.1.76: - version "1.1.76" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e" - integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA== - nopt@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" @@ -3791,11 +4042,59 @@ npmlog@^7.0.1: gauge "^5.0.0" set-blocking "^2.0.0" +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + object-treeify@^1.1.33: version "1.1.33" resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.fromentries@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.groupby@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.values@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3803,17 +4102,17 @@ once@^1.3.0: dependencies: wrappy "1" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" p-limit@^2.2.0: version "2.3.0" @@ -3941,7 +4240,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -4011,6 +4310,15 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + proc-log@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" @@ -4021,11 +4329,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" @@ -4061,15 +4364,6 @@ propagate@^2.0.0: resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== -proxyquire@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" - integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== - dependencies: - fill-keys "^1.0.2" - module-not-found-error "^1.0.1" - resolve "^1.11.1" - punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -4095,10 +4389,10 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -ramda@^0.27.1: - version "0.27.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" - integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== +rambda@^7.1.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/rambda/-/rambda-7.5.0.tgz#1865044c59bc0b16f63026c6e5a97e4b1bbe98fe" + integrity sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA== randombytes@^2.1.0: version "2.1.0" @@ -4107,6 +4401,11 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + read-cmd-shim@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" @@ -4214,26 +4513,37 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -regexp-tree@^0.1.23, regexp-tree@~0.1.1: - version "0.1.24" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.24.tgz#3d6fa238450a4d66e5bc9c4c14bb720e2196829d" - integrity sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw== +regexp-tree@^0.1.27: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== -regexpp@^3.0.0, regexpp@^3.1.0: +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + +regexpp@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +regjsparser@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.10.0.tgz#b1ed26051736b436f22fdec1c8f72635f9f44892" + integrity sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA== + dependencies: + jsesc "~0.5.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -4251,7 +4561,12 @@ resolve-global@1.0.0, resolve-global@^1.0.0: dependencies: global-dirs "^0.1.1" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.20.0: +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -4259,6 +4574,15 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.20 is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.22.4: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -4283,22 +4607,29 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-regex@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" - integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== dependencies: - regexp-tree "~0.1.1" + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" @@ -4317,11 +4648,16 @@ semver@7.3.5: dependencies: lru-cache "^6.0.0" -semver@^6.1.0, semver@^6.3.0: +semver@^6.1.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + semver@^7.0.0, semver@^7.1.1, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -4329,7 +4665,7 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.5.4: dependencies: lru-cache "^6.0.0" -semver@^7.2.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: +semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== @@ -4348,6 +4684,15 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -4389,6 +4734,15 @@ shx@^0.3.4: minimist "^1.2.3" shelljs "^0.8.5" +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -4422,6 +4776,18 @@ sinon@^11.1.2: nise "^5.1.0" supports-color "^7.2.0" +sinon@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-16.0.0.tgz#06da4e63624b946c9d7e67cce21c2f67f40f23a9" + integrity sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^10.3.0" + "@sinonjs/samsam" "^8.0.0" + diff "^5.1.0" + nise "^5.1.4" + supports-color "^7.2.0" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -4458,11 +4824,6 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -4534,6 +4895,33 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -4574,7 +4962,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -4613,17 +5001,15 @@ supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0: has-flag "^4.0.0" supports-color "^7.0.0" -table@^6.0.9: - version "6.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== tar@^6.1.11, tar@^6.1.13, tar@^6.1.15, tar@^6.1.2: version "6.1.15" @@ -4664,11 +5050,6 @@ tiny-relative-date@^1.3.0: resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07" integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A== -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4691,6 +5072,11 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -4710,23 +5096,29 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsd@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.25.0.tgz#bed2a937ab414e1c5ae1ca687bb44ea3a6f549ba" - integrity sha512-liUlvKtsdr+70XEZP/kkF6U8+Q9URZi4Pw58ih7a9x3kjJblG8rdVgvG62xcvkgRva1q3yWX5qAxfYZuYiC5CA== +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: - "@tsd/typescript" "~4.9.3" + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tsd@^0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.29.0.tgz#eed29647bf67ae77e4d686e9fdde5dd27bd1529f" + integrity sha512-5B7jbTj+XLMg6rb9sXRBGwzv7h8KJlGOkTHxY63eWpZJiQ5vJbXEjL0u7JkIxwi5EsrRE1kRVUWmy6buK/ii8A== + dependencies: + "@tsd/typescript" "~5.2.2" eslint-formatter-pretty "^4.1.0" globby "^11.0.1" + jest-diff "^29.0.3" meow "^9.0.0" path-exists "^4.0.0" read-pkg-up "^7.0.0" -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" @@ -4737,13 +5129,6 @@ tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - tuf-js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" @@ -4802,10 +5187,59 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typescript@^5: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" unique-filename@^3.0.0: version "3.0.0" @@ -4843,11 +5277,6 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -4875,6 +5304,28 @@ wcwidth@^1.0.0: dependencies: defaults "^1.0.3" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -4910,11 +5361,6 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" From 090f675e1e43a5f13d6f3d52794c5fc79c356cac Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Fri, 22 Sep 2023 18:07:43 +0000 Subject: [PATCH 27/55] chore(release): 3.0.0-beta.14 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c3867d28d..0df03df8b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.13", + "version": "3.0.0-beta.14", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 878da238504976ce3752ec7a1e39e8494b3c6a4f Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 22 Sep 2023 14:06:18 -0600 Subject: [PATCH 28/55] fix: allow undefined from flag parser --- src/cli-ux/styled/table.ts | 8 ++++---- src/interfaces/parser.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli-ux/styled/table.ts b/src/cli-ux/styled/table.ts index 5d173d268..dc4f7f7d6 100644 --- a/src/cli-ux/styled/table.ts +++ b/src/cli-ux/styled/table.ts @@ -309,11 +309,11 @@ export namespace table { columns: Interfaces.OptionFlag; sort: Interfaces.OptionFlag; filter: Interfaces.OptionFlag; - csv: Interfaces.Flag; + csv: Interfaces.BooleanFlag; output: Interfaces.OptionFlag; - extended: Interfaces.Flag; - 'no-truncate': Interfaces.Flag; - 'no-header': Interfaces.Flag; + extended: Interfaces.BooleanFlag; + 'no-truncate': Interfaces.BooleanFlag; + 'no-header': Interfaces.BooleanFlag; } = { columns: F.string({exclusive: ['extended'], description: 'only show provided columns (comma-separated)'}), sort: F.string({description: 'property to sort by (prepend \'-\' for descending)'}), diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts index 7fc4ded0f..c95bfec7a 100644 --- a/src/interfaces/parser.ts +++ b/src/interfaces/parser.ts @@ -215,7 +215,7 @@ export type OptionFlagProps = FlagProps & { export type FlagParserContext = Command & {token: FlagToken} export type FlagParser = (input: I, context: FlagParserContext, opts: P & OptionFlag) => - T extends Array ? Promise : Promise + T extends Array ? Promise : Promise export type ArgParserContext = Command & {token: ArgToken} From 29cdf738a67942b9da6ee8acda14aacf806317c6 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Fri, 22 Sep 2023 20:06:47 +0000 Subject: [PATCH 29/55] chore(release): 3.0.0-beta.15 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0df03df8b..afa7bcf4b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.14", + "version": "3.0.0-beta.15", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 7103fa5ea30672e6e3cfab7673e31ea1d4b77755 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 22 Sep 2023 14:37:29 -0600 Subject: [PATCH 30/55] feat: set spinner style --- src/cli-ux/action/base.ts | 9 +++------ src/cli-ux/action/simple.ts | 24 ++++++++++-------------- src/cli-ux/action/spinner.ts | 25 +++++++++++++++++-------- src/cli-ux/action/types.ts | 6 ++++++ 4 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 src/cli-ux/action/types.ts diff --git a/src/cli-ux/action/base.ts b/src/cli-ux/action/base.ts index bd2a5e54e..a29bfc809 100644 --- a/src/cli-ux/action/base.ts +++ b/src/cli-ux/action/base.ts @@ -1,4 +1,5 @@ import {stderr, stdout} from '../stream' +import {Options} from './types' import {castArray} from '../../util' import {inspect} from 'node:util' @@ -10,10 +11,6 @@ export interface ITask { export type ActionType = 'spinner' | 'simple' | 'debug' -export interface Options { - stdout?: boolean; -} - export class ActionBase { type!: ActionType @@ -31,7 +28,7 @@ export class ActionBase { const task = {action, status, active: Boolean(this.task && this.task.active)} this.task = task - this._start() + this._start(opts) task.active = true this._stdout(true) } @@ -127,7 +124,7 @@ export class ActionBase { return ret } - protected _start(): void { + protected _start(_opts: Options): void { throw new Error('not implemented') } diff --git a/src/cli-ux/action/simple.ts b/src/cli-ux/action/simple.ts index 30db51f2f..8018aeb9e 100644 --- a/src/cli-ux/action/simple.ts +++ b/src/cli-ux/action/simple.ts @@ -4,9 +4,8 @@ export default class SimpleAction extends ActionBase { public type: ActionType = 'simple' protected _start(): void { - const {task} = this - if (!task) return - this._render(task.action, task.status) + if (!this.task) return + this._render(this.task.action, this.task.status) } protected _pause(icon?: string): void { @@ -19,24 +18,21 @@ export default class SimpleAction extends ActionBase { } protected _updateStatus(status: string, prevStatus?: string, newline = false): void { - const {task, std} = this - if (!task) return - if (task.active && !prevStatus) this._write(std, ` ${status}`) - else this._write(std, `${task.action}... ${status}`) + if (!this.task) return + if (this.task.active && !prevStatus) this._write(this.std, ` ${status}`) + else this._write(this.std, `${this.task.action}... ${status}`) if (newline || !prevStatus) this._flush() } protected _stop(status: string): void { - const {task} = this - if (!task) return - this._updateStatus(status, task.status, true) + if (!this.task) return + this._updateStatus(status, this.task.status, true) } private _render(action: string, status?: string) { - const {task, std} = this - if (!task) return - if (task.active) this._flush() - this._write(std, status ? `${action}... ${status}` : `${action}...`) + if (!this.task) return + if (this.task.active) this._flush() + this._write(this.std, status ? `${action}... ${status}` : `${action}...`) } private _flush() { diff --git a/src/cli-ux/action/spinner.ts b/src/cli-ux/action/spinner.ts index 4965c54f2..d462514b3 100644 --- a/src/cli-ux/action/spinner.ts +++ b/src/cli-ux/action/spinner.ts @@ -1,5 +1,6 @@ import * as supportsColor from 'supports-color' import {ActionBase, ActionType} from './base' +import {Options} from './types' import ansiStyles from 'ansi-styles' import chalk from 'chalk' import {errtermwidth} from '../../screen' @@ -17,17 +18,19 @@ export default class SpinnerAction extends ActionBase { spinner?: NodeJS.Timeout - frames: any + frames: string[] frameIndex: number constructor() { super() - this.frames = spinners[process.platform === 'win32' ? 'line' : 'dots2'].frames + this.frames = this.getFrames() this.frameIndex = 0 } - protected _start(): void { + protected _start(opts: Options): void { + if (opts.style) this.frames = this.getFrames(opts) + this._reset() if (this.spinner) clearInterval(this.spinner) this._render() @@ -60,15 +63,21 @@ export default class SpinnerAction extends ActionBase { return color(frame) } + private getFrames(opts?: Options) { + if (opts?.style) return spinners[process.platform === 'win32' ? 'line' : opts.style].frames + + return spinners[process.platform === 'win32' ? 'line' : 'dots2'].frames + } + private _render(icon?: string) { - const {task, std, output} = this - if (!task) return + if (!this.task) return this._reset() this._flushStdout() const frame = icon === 'spinner' ? ` ${this._frame()}` : icon || '' - const status = task.status ? ` ${task.status}` : '' - this.output = `${task.action}...${frame}${status}\n` - this._write(std, output!) + const status = this.task.status ? ` ${this.task.status}` : '' + this.output = `${this.task.action}...${frame}${status}\n` + + this._write(this.std, this.output) } private _reset() { diff --git a/src/cli-ux/action/types.ts b/src/cli-ux/action/types.ts new file mode 100644 index 000000000..0e5eb9179 --- /dev/null +++ b/src/cli-ux/action/types.ts @@ -0,0 +1,6 @@ +import spinners from './spinners' + +export type Options = { + stdout?: boolean; + style?: keyof typeof spinners; +} From 8413a86423557011b44a7737e7eaef90a9bd965f Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Fri, 22 Sep 2023 20:38:55 +0000 Subject: [PATCH 31/55] chore(release): 3.0.0-beta.16 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afa7bcf4b..4841001c1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.15", + "version": "3.0.0-beta.16", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From e1632db0c1f3da84dd32a7b1dbde5b60e929f0e6 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 22 Sep 2023 15:00:07 -0600 Subject: [PATCH 32/55] test: unset config vars after setting them --- test/integration/sf.e2e.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integration/sf.e2e.ts b/test/integration/sf.e2e.ts index 27636093c..7e423d206 100644 --- a/test/integration/sf.e2e.ts +++ b/test/integration/sf.e2e.ts @@ -114,5 +114,9 @@ describe('Salesforce CLI (sf)', () => { } expect(results.failures).to.be.empty + + const unset = await executor.executeCommand('config unset disable-telemetry org-api-version --global') + const unsetParsed = parseJson(unset.stdout!) + expect(unsetParsed.status).to.equal(0) }) }) From a4cb2cbd790cbe69313545f1c334cc2f1557a353 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Mon, 25 Sep 2023 14:50:01 -0700 Subject: [PATCH 33/55] feat: remove ts-node (#794) * feat: drop ts-node dependency * test: use --json for config unset --- package.json | 4 ++-- src/config/ts-node.ts | 16 ++++++++++++++-- test/integration/sf.e2e.ts | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4841001c1..71f798bed 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,6 @@ "strip-ansi": "^6.0.1", "supports-color": "^8.1.1", "supports-hyperlinks": "^2.2.0", - "ts-node": "^10.9.1", - "tslib": "^2.5.0", "widest-line": "^3.1.0", "wordwrap": "^1.0.0", "wrap-ansi": "^7.0.0" @@ -72,6 +70,8 @@ "shx": "^0.3.4", "sinon": "^11.1.2", "tsd": "^0.29.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", "typescript": "^5" }, "engines": { diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index 67093f80e..2addcf5a9 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -23,7 +23,11 @@ function loadTSConfig(root: string): TSConfig | undefined { } catch { try { typescript = require(join(root, 'node_modules', 'typescript')) - } catch {} + } catch { + debug(`Could not find typescript dependency. Skipping ts-node registration for ${root}.`) + memoizedWarn('Could not find typescript. Please ensure that typescript is a devDependency. Falling back to compiled source.') + return + } } if (existsSync(tsconfigPath) && typescript) { @@ -49,7 +53,15 @@ function registerTSNode(root: string): TSConfig | undefined { debug('registering ts-node at', root) const tsNodePath = require.resolve('ts-node', {paths: [root, __dirname]}) debug('ts-node path:', tsNodePath) - const tsNode: typeof TSNode = require(tsNodePath) + let tsNode: typeof TSNode + + try { + tsNode = require(tsNodePath) + } catch { + debug(`Could not find ts-node at ${tsNodePath}. Skipping ts-node registration for ${root}.`) + memoizedWarn(`Could not find ts-node at ${tsNodePath}. Please ensure that ts-node is a devDependency. Falling back to compiled source.`) + return + } const typeRoots = [ join(root, 'node_modules', '@types'), diff --git a/test/integration/sf.e2e.ts b/test/integration/sf.e2e.ts index 7e423d206..bd2786122 100644 --- a/test/integration/sf.e2e.ts +++ b/test/integration/sf.e2e.ts @@ -115,7 +115,7 @@ describe('Salesforce CLI (sf)', () => { expect(results.failures).to.be.empty - const unset = await executor.executeCommand('config unset disable-telemetry org-api-version --global') + const unset = await executor.executeCommand('config unset disable-telemetry org-api-version --global --json') const unsetParsed = parseJson(unset.stdout!) expect(unsetParsed.status).to.equal(0) }) From 6702a535500cfe8a700fd18f41f920d809c7c5d8 Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Mon, 25 Sep 2023 21:50:17 +0000 Subject: [PATCH 34/55] chore(release): 3.0.0-beta.17 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71f798bed..f4f508356 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.16", + "version": "3.0.0-beta.17", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 2a3b0846aad89b6e475b1a1ea4547695609ca09c Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 28 Sep 2023 11:18:50 -0700 Subject: [PATCH 35/55] fix: use ES2022 (#793) * fix: use ES2022 * test: use --json for config unset * feat: stop using getters and setters for flags * chore: clean up * feat: expose json flag * feat: remove pass through getter and setter * fix: correct order of flags in toCached * chore: clean up * fix: flag merge order * chore: documentation * test: use oclif/test v3 * feat: set spinner style on windows too * fix: handle cmd with baseFlags but no flags * fix: some circular deps * fix: circular deps in help * fix: ts-node and config circular deps * fix: toCached circular dep in help * chore: organize utils * test: enforce no circular deps * chore: remove Flags.json * chore: add prettier config * test: add nyc * test: improve test coverage * test: windows unit tests * chore: revert change to automerge.yml * chore: code review * perf: parallelize cacheCommand --- .nycrc.json | 11 + .prettierrc.json | 1 + guides/PRE_CORE_MIGRATION.md | 45 + guides/V3_MIGRATION.md | 31 +- package.json | 12 +- src/cli-ux/action/spinner.ts | 2 +- src/cli-ux/flush.ts | 5 +- src/cli-ux/styled/json.ts | 8 +- src/command.ts | 112 +- src/config/config.ts | 153 +-- src/config/index.ts | 1 - src/config/plugin-loader.ts | 2 +- src/config/plugin.ts | 17 +- src/config/ts-node.ts | 12 +- src/config/util.ts | 12 - src/errors/handle.ts | 2 +- src/flags.ts | 6 +- src/help/index.ts | 42 +- src/help/root.ts | 2 +- src/help/util.ts | 28 +- src/index.ts | 4 +- src/interfaces/parser.ts | 15 +- src/interfaces/plugin.ts | 6 + src/module-loader.ts | 2 +- src/parser/parse.ts | 7 +- src/util/aggregate-flags.ts | 18 + src/util/cache-command.ts | 111 ++ src/util/cache-default-value.ts | 23 + src/{util.ts => util/index.ts} | 30 +- test/command/command.test.ts | 218 +--- test/config/config.flexible.test.ts | 2 + test/config/config.test.ts | 2 + test/help/help-test-utils.ts | 5 +- test/help/util.test.ts | 23 +- test/util.test.ts | 70 -- test/util/cache-command.test.ts | 298 +++++ test/util/cache-default-value.test.ts | 42 + test/util/index.test.ts | 141 +++ tsconfig.json | 2 +- yarn.lock | 1476 ++++++++++++++++++++++++- 40 files changed, 2326 insertions(+), 673 deletions(-) create mode 100644 .nycrc.json create mode 100644 .prettierrc.json create mode 100644 src/util/aggregate-flags.ts create mode 100644 src/util/cache-command.ts create mode 100644 src/util/cache-default-value.ts rename src/{util.ts => util/index.ts} (88%) delete mode 100644 test/util.test.ts create mode 100644 test/util/cache-command.test.ts create mode 100644 test/util/cache-default-value.test.ts create mode 100644 test/util/index.test.ts diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 000000000..d6c706920 --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,11 @@ +{ + "check-coverage": true, + "lines": 80, + "statements": 70, + "functions": 70, + "branches": 60, + "reporter": ["lcov", "text"], + "extension": [".ts"], + "include": ["**/*.ts"], + "exclude": ["**/*.d.ts", "test/**"] +} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..631433570 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +"@oclif/prettier-config" diff --git a/guides/PRE_CORE_MIGRATION.md b/guides/PRE_CORE_MIGRATION.md index 16a6fdc20..649af2cda 100644 --- a/guides/PRE_CORE_MIGRATION.md +++ b/guides/PRE_CORE_MIGRATION.md @@ -5,6 +5,7 @@ Migrating to `@oclif/core` from the deprecated oclif libraries (`@oclif/config`, - [Migrating to @oclif/core from deprecated libraries](#migrating-to-oclifcore-from-deprecated-libraries) - [Update Imports](#update-imports) + - [Update Command Args](#update-command-args) - [Update your bin scripts](#update-your-bin-scripts) - [Add `main` to your package.json](#add-main-to-your-packagejson) - [Restore `-h`, `-v`, and `version`](#restore--h--v-and-version) @@ -30,6 +31,50 @@ With this import: import {Command, Flags, Topic, Help} from '@oclif/core'; ``` +## Update Command Args + +We updated the `Command.args` to more closely resemble flags + +**Before** + +```typescript +import { Command } from '@oclif/core' + +export default MyCommand extends Command { + static args = [{name: arg1, description: 'an argument', required: true}] + + public async run(): Promise { + const {args} = await this.parse(MyCommand) // args is useless {[name: string]: any} + } +} +``` + +**After** + +```typescript +import { Command, Args } from '@oclif/core' + +export default MyCommand extends Command { + static args = { + arg1: Args.string({description: 'an argument', required: true}) + } + + public async run(): Promise { + const {args} = await this.parse(MyCommand) // args is { arg1: string } + } +} +``` + +These are the available Args: +- string +- integer +- boolean +- url +- file +- directory +- custom + + ## Update your bin scripts `@oclif/core` now supports separate bin scripts for production and development. diff --git a/guides/V3_MIGRATION.md b/guides/V3_MIGRATION.md index 9b83abc6f..82eaa7ba1 100644 --- a/guides/V3_MIGRATION.md +++ b/guides/V3_MIGRATION.md @@ -5,16 +5,19 @@ Migrating to @oclif/core@V3 - [BREAKING CHANGES โ—](#breaking-changes-) - [Dropping node 14 and node 16 support](#dropping-node-14-and-node-16-support) - [Bin Scripts for ESM/CJS Interoperability](#bin-scripts-for-esmcjs-interoperability) + - [Dropped `ts-node` as a dependency](#dropped-ts-node-as-a-dependency) - [`Config.plugins`](#configplugins) - [Readonly properties on `Config`](#readonly-properties-on-config) - [Private methods on `Plugin`](#private-methods-on-plugin) - [`global['cli-ux']` -\> `global.ux`](#globalcli-ux---globalux) - [`handle`](#handle) - [`noCacheDefault` flag property replaces `isWritingManifest`](#nocachedefault-flag-property-replaces-iswritingmanifest) + - [Removed `toCached` export](#removed-tocached-export) - [Features ๐ŸŽ‰](#features-) - - [Cache Flexible taxonomy Command Permutations](#cache-flexible-taxonomy-command-permutations) + - [Performance Improvements](#performance-improvements) - [charAliases Flag Property](#charaliases-flag-property) - [Flags.option](#flagsoption) + - [Set spinner styles](#set-spinner-styles) ## BREAKING CHANGES โ— @@ -36,6 +39,10 @@ In order to support ESM and CommonJS plugin interoperability you will need to up If you'd like to migrate your plugin to ESM, please read our guide [here](https://oclif.io/docs/esm) +### Dropped `ts-node` as a dependency + +We removed `ts-node` as a dependency to reduce the package size. By doing this, it means that linked plugin **must** have `ts-node` as a `devDependency` in order for auto-transpilation to work. + ### `Config.plugins` `Config.plugins` is now a `Map` where the keys are the plugin names and the values are the loaded `Plugin` instances. Previously it was an array of loaded `Plugin` instances. @@ -110,12 +117,18 @@ export const mySensitiveFlag = Flags.string({ }); ``` +### Removed `toCached` export + +We removed the `toCached` export since there's no need for consumers of `@oclif/core` to use this function. ## Features ๐ŸŽ‰ -### Cache Flexible taxonomy Command Permutations +### Performance Improvements -The command permutations for flexible taxonomy are now cached in the oclif.manifest.json allowing for quicker startup times. +- Cache command permutations for flexible taxonomy in the `oclif.manifest.json` +- Cache additional command properties (`isESM`, `relativePath`) in the `oclif.manifest.json` +- Improved accuracy in the `DEBUG=perf` output. +- Remove `ts-node` from `dependencies` to reduce the package size. ### charAliases Flag Property @@ -149,3 +162,15 @@ export default class MyCommand extends Command { } } ``` + +### Set spinner styles + +You can now configure the style of the spinner when using `ux.action.start`. See [spinners](https://github.com/oclif/core/blob/main/src/cli-ux/action/spinners.ts) for all the different options. + +```typescript +ux.action.start('starting spinner', 'spinning', {style: 'arc'}) +await ux.wait(2500) +ux.action.status = 'still going' +await ux.wait(2500) +ux.action.stop() +``` diff --git a/package.json b/package.json index f4f508356..d9cb3af3a 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "@commitlint/config-conventional": "^12.1.4", "@oclif/plugin-help": "^5.2.8", "@oclif/plugin-plugins": "^3.3.0", - "@oclif/test": "^2.4.7", + "@oclif/prettier-config": "^0.1.1", + "@oclif/test": "^3.0.0-beta.1", "@types/ansi-styles": "^3.2.1", "@types/benchmark": "^2.1.2", "@types/chai": "^4.3.4", @@ -65,8 +66,10 @@ "fancy-test": "^3.0.0-beta.2", "globby": "^11.1.0", "husky": "6", + "madge": "^6.1.0", "mocha": "^10.2.0", "nock": "^13.3.0", + "nyc": "^15.1.0", "shx": "^0.3.4", "sinon": "^11.1.2", "tsd": "^0.29.0", @@ -105,13 +108,14 @@ "commitlint": "commitlint", "compile": "tsc", "lint": "eslint . --ext .ts", - "posttest": "yarn lint", + "posttest": "yarn lint && yarn test:circular-deps", "prepack": "yarn run build", - "pretest": "yarn build --noEmit && tsc -p test --noEmit --skipLibCheck", + "pretest": "yarn build && tsc -p test --noEmit --skipLibCheck", + "test:circular-deps": "madge lib/ -c", "test:e2e": "mocha --forbid-only \"test/**/*.e2e.ts\" --parallel --timeout 1200000", "test:esm-cjs": "cross-env DEBUG=e2e:* ts-node test/integration/esm-cjs.ts", "test:perf": "ts-node test/perf/parser.perf.ts", - "test": "mocha --forbid-only \"test/**/*.test.ts\"" + "test": "nyc mocha --forbid-only \"test/**/*.test.ts\"" }, "types": "lib/index.d.ts" } diff --git a/src/cli-ux/action/spinner.ts b/src/cli-ux/action/spinner.ts index d462514b3..7ac05c9ef 100644 --- a/src/cli-ux/action/spinner.ts +++ b/src/cli-ux/action/spinner.ts @@ -64,7 +64,7 @@ export default class SpinnerAction extends ActionBase { } private getFrames(opts?: Options) { - if (opts?.style) return spinners[process.platform === 'win32' ? 'line' : opts.style].frames + if (opts?.style) return spinners[opts.style].frames return spinners[process.platform === 'win32' ? 'line' : 'dots2'].frames } diff --git a/src/cli-ux/flush.ts b/src/cli-ux/flush.ts index c6a0c152d..33abd784f 100644 --- a/src/cli-ux/flush.ts +++ b/src/cli-ux/flush.ts @@ -1,4 +1,5 @@ -import {Errors, stdout} from '..' +import {error} from '../errors' +import {stdout} from './stream' function timeout(p: Promise, ms: number) { function wait(ms: number, unref = false) { @@ -8,7 +9,7 @@ function timeout(p: Promise, ms: number) { }) } - return Promise.race([p, wait(ms, true).then(() => Errors.error('timed out'))]) + return Promise.race([p, wait(ms, true).then(() => error('timed out'))]) } async function _flush() { diff --git a/src/cli-ux/styled/json.ts b/src/cli-ux/styled/json.ts index c7db532d0..cbdc4892a 100644 --- a/src/cli-ux/styled/json.ts +++ b/src/cli-ux/styled/json.ts @@ -1,15 +1,17 @@ import chalk from 'chalk' +import {format} from 'node:util' +import {stdout} from '../stream' -import {ux} from '../../index' +const info = (output: string) => stdout.write(format(output) + '\n') export default function styledJSON(obj: unknown): void { const json = JSON.stringify(obj, null, 2) if (!chalk.level) { - ux.info(json) + info(json) return } const cardinal = require('cardinal') const theme = require('cardinal/themes/jq') - ux.info(cardinal.highlight(json, {json: true, theme})) + info(cardinal.highlight(json, {json: true, theme})) } diff --git a/src/command.ts b/src/command.ts index 7a54e2136..9b365af69 100644 --- a/src/command.ts +++ b/src/command.ts @@ -25,7 +25,7 @@ import {LoadOptions} from './interfaces/config' import {PJSON} from './interfaces' import {Plugin} from './interfaces/plugin' import {PrettyPrintableError} from './errors' -import {boolean} from './flags' +import {aggregateFlags} from './util/aggregate-flags' import chalk from 'chalk' import {fileURLToPath} from 'node:url' import {ux} from './cli-ux' @@ -42,13 +42,6 @@ stdout.on('error', (err: any) => { throw err }) -const jsonFlag = { - json: boolean({ - description: 'Format output as json.', - helpGroup: 'GLOBAL', - }), -} - /** * An abstract class which acts as the base for each command * in your project. @@ -126,37 +119,7 @@ export abstract class Command { public static hasDynamicHelp = false - protected static '_--' = false - - protected static _enableJsonFlag = false - - public static get enableJsonFlag(): boolean { - return this._enableJsonFlag - } - - public static set enableJsonFlag(value: boolean) { - this._enableJsonFlag = value - if (value === true) { - this.baseFlags = jsonFlag - } else { - delete this.baseFlags?.json - this.flags = {} // force the flags setter to run - delete this.flags?.json - } - } - - public static get '--'(): boolean { - return Command['_--'] - } - - public static set '--'(value: boolean) { - Command['_--'] = value - } - - public get passThroughEnabled(): boolean { - return Command['_--'] - } - + public static enableJsonFlag = false /** * instantiate and run the command * @@ -184,29 +147,10 @@ export abstract class Command { return cmd._run>() } - protected static _baseFlags: FlagInput - - static get baseFlags(): FlagInput { - return this._baseFlags - } - - static set baseFlags(flags: FlagInput) { - // eslint-disable-next-line prefer-object-spread - this._baseFlags = Object.assign({}, this.baseFlags, flags) - this.flags = {} // force the flags setter to run - } + public static baseFlags: FlagInput /** A hash of flags for the command */ - protected static _flags: FlagInput - - public static get flags(): FlagInput { - return this._flags - } - - public static set flags(flags: FlagInput) { - // eslint-disable-next-line prefer-object-spread - this._flags = Object.assign({}, this._flags ?? {}, this.baseFlags, flags) - } + public static flags: FlagInput public id: string | undefined @@ -284,16 +228,19 @@ export abstract class Command { * @returns {boolean} true if the command supports json and the --json flag is present */ public jsonEnabled(): boolean { - // if the command doesn't support json, return false + // If the command doesn't support json, return false if (!this.ctor.enableJsonFlag) return false - // if the command parameter pass through is enabled, return true if the --json flag is before the '--' separator - if (this.passThroughEnabled) { - const ptIndex = this.argv.indexOf('--') - const jsonIndex = this.argv.indexOf('--json') - return jsonIndex > -1 && (ptIndex === -1 || jsonIndex < ptIndex) - } - return this.argv.includes('--json') || this.config.scopedEnvVar?.('CONTENT_TYPE')?.toLowerCase() === 'json' + // If the CONTENT_TYPE env var is set to json, return true + if (this.config.scopedEnvVar?.('CONTENT_TYPE')?.toLowerCase() === 'json') return true + + const passThroughIndex = this.argv.indexOf('--') + const jsonIndex = this.argv.indexOf('--json') + return passThroughIndex === -1 + // If '--' is not present, then check for `--json` in this.argv + ? jsonIndex > -1 + // If '--' is present, return true only the --json flag exists and is before the '--' + : jsonIndex > -1 && jsonIndex < passThroughIndex } /** @@ -312,8 +259,13 @@ export abstract class Command { } protected warnIfFlagDeprecated(flags: Record): void { + const allFlags = aggregateFlags( + this.ctor.flags, + this.ctor.baseFlags, + this.ctor.enableJsonFlag, + ) for (const flag of Object.keys(flags)) { - const flagDef = this.ctor.flags[flag] + const flagDef = allFlags[flag] const deprecated = flagDef?.deprecated if (deprecated) { this.warn(formatFlagDeprecationWarning(flag, deprecated)) @@ -352,12 +304,22 @@ export abstract class Command { } } - protected async parse(options?: Input, argv = this.argv): Promise> { + protected async parse( + options?: Input, + argv = this.argv, + ): Promise> { if (!options) options = this.ctor as Input - const opts = {context: this, ...options} - // the spread operator doesn't work with getters so we have to manually add it here - opts.flags = options?.flags - opts.args = options?.args + + const opts = { + context: this, + ...options, + flags: aggregateFlags( + options.flags, + options.baseFlags, + options.enableJsonFlag, + ), + } + const results = await Parser.parse(argv, opts) this.warnIfFlagDeprecated(results.flags ?? {}) @@ -449,7 +411,7 @@ export namespace Command { export type Flag = IFlag export namespace Flag { - export type Cached = Omit & (BooleanFlagProps | OptionFlagProps) + export type Cached = Omit & (BooleanFlagProps | OptionFlagProps) & {hasDynamicHelp?: boolean} export type Any = Flag | Cached } diff --git a/src/config/config.ts b/src/config/config.ts index 83428679a..41795375d 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,20 +1,19 @@ import * as ejs from 'ejs' import {ArchTypes, Config as IConfig, LoadOptions, PlatformTypes, VersionDetails} from '../interfaces/config' -import {Arg, OptionFlag} from '../interfaces/parser' import {CLIError, error, exit, warn} from '../errors' import {Debug, collectUsableIds, getCommandIdPermutations} from './util' import {Hook, Hooks, PJSON, Topic} from '../interfaces' import {Plugin as IPlugin, Options} from '../interfaces/plugin' import {URL, fileURLToPath} from 'node:url' import {arch, userInfo as osUserInfo, release, tmpdir, type} from 'node:os' -import {compact, ensureArgObject, getHomeDir, getPlatform, isProd, requireJson} from '../util' +import {compact, getHomeDir, getPlatform, isProd, requireJson} from '../util' import {join, sep} from 'node:path' - import {Command} from '../command' import {Performance} from '../performance' import PluginLoader from './plugin-loader' +import WSL from 'is-wsl' import {format} from 'node:util' -import {getHelpFlagAdditions} from '../help' +import {getHelpFlagAdditions} from '../help/util' import {loadWithData} from '../module-loader' import {settings} from '../settings' import {stdout} from '../cli-ux/stream' @@ -30,8 +29,6 @@ function channelFromVersion(version: string) { return (m && m[1]) || 'stable' } -const WSL = require('is-wsl') - function isConfig(o: any): o is Config { return o && Boolean(o._base) } @@ -812,147 +809,3 @@ export class Config implements IConfig { } } -// when no manifest exists, the default is calculated. This may throw, so we need to catch it -const defaultFlagToCached = async (flag: OptionFlag, respectNoCacheDefault: boolean) => { - if (respectNoCacheDefault && flag.noCacheDefault) return - // Prefer the defaultHelp function (returns a friendly string for complex types) - if (typeof flag.defaultHelp === 'function') { - try { - return await flag.defaultHelp({options: flag, flags: {}}) - } catch { - return - } - } - - // if not specified, try the default function - if (typeof flag.default === 'function') { - try { - return await flag.default({options: flag, flags: {}}) - } catch {} - } else { - return flag.default - } -} - -const defaultArgToCached = async (arg: Arg, respectNoCacheDefault: boolean): Promise => { - if (respectNoCacheDefault && arg.noCacheDefault) return - // Prefer the defaultHelp function (returns a friendly string for complex types) - if (typeof arg.defaultHelp === 'function') { - try { - return await arg.defaultHelp({options: arg, flags: {}}) - } catch { - return - } - } - - // if not specified, try the default function - if (typeof arg.default === 'function') { - try { - return await arg.default({options: arg, flags: {}}) - } catch {} - } else { - return arg.default - } -} - -export async function toCached(c: Command.Class, plugin?: IPlugin, respectNoCacheDefault = false): Promise { - const flags = {} as {[k: string]: Command.Flag.Cached} - - for (const [name, flag] of Object.entries(c.flags || {})) { - if (flag.type === 'boolean') { - flags[name] = { - name, - type: flag.type, - char: flag.char, - summary: flag.summary, - description: flag.description, - hidden: flag.hidden, - required: flag.required, - helpLabel: flag.helpLabel, - helpGroup: flag.helpGroup, - allowNo: flag.allowNo, - dependsOn: flag.dependsOn, - relationships: flag.relationships, - exclusive: flag.exclusive, - deprecated: flag.deprecated, - deprecateAliases: c.deprecateAliases, - aliases: flag.aliases, - charAliases: flag.charAliases, - delimiter: flag.delimiter, - noCacheDefault: flag.noCacheDefault, - } - } else { - flags[name] = { - name, - type: flag.type, - char: flag.char, - summary: flag.summary, - description: flag.description, - hidden: flag.hidden, - required: flag.required, - helpLabel: flag.helpLabel, - helpValue: flag.helpValue, - helpGroup: flag.helpGroup, - multiple: flag.multiple, - options: flag.options, - dependsOn: flag.dependsOn, - relationships: flag.relationships, - exclusive: flag.exclusive, - default: await defaultFlagToCached(flag, respectNoCacheDefault), - deprecated: flag.deprecated, - deprecateAliases: c.deprecateAliases, - aliases: flag.aliases, - charAliases: flag.charAliases, - delimiter: flag.delimiter, - noCacheDefault: flag.noCacheDefault, - } - // a command-level placeholder in the manifest so that oclif knows it should regenerate the command during help-time - if (typeof flag.defaultHelp === 'function') { - c.hasDynamicHelp = true - } - } - } - - const args = {} as {[k: string]: Command.Arg.Cached} - for (const [name, arg] of Object.entries(ensureArgObject(c.args))) { - args[name] = { - name, - description: arg.description, - required: arg.required, - options: arg.options, - default: await defaultArgToCached(arg, respectNoCacheDefault), - hidden: arg.hidden, - noCacheDefault: arg.noCacheDefault, - } - } - - const stdProperties = { - id: c.id, - summary: c.summary, - description: c.description, - strict: c.strict, - usage: c.usage, - pluginName: plugin && plugin.name, - pluginAlias: plugin && plugin.alias, - pluginType: plugin && plugin.type, - hidden: c.hidden, - state: c.state, - aliases: c.aliases || [], - examples: c.examples || (c as any).example, - deprecationOptions: c.deprecationOptions, - deprecateAliases: c.deprecateAliases, - flags, - args, - } - - // do not include these properties in manifest - const ignoreCommandProperties = ['plugin', '_flags', '_enableJsonFlag', '_globalFlags', '_baseFlags'] - const stdKeys = Object.keys(stdProperties) - const keysToAdd = Object.keys(c).filter(property => ![...stdKeys, ...ignoreCommandProperties].includes(property)) - const additionalProperties: Record = {} - for (const key of keysToAdd) { - additionalProperties[key] = (c as any)[key] - } - - return {...stdProperties, ...additionalProperties} -} diff --git a/src/config/index.ts b/src/config/index.ts index a79d4c3bf..748162de7 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,5 +1,4 @@ export {Config} from './config' -export {toCached} from './config' export {Plugin} from './plugin' export {tsPath} from './ts-node' diff --git a/src/config/plugin-loader.ts b/src/config/plugin-loader.ts index 7965a08bb..ed008a708 100644 --- a/src/config/plugin-loader.ts +++ b/src/config/plugin-loader.ts @@ -43,7 +43,7 @@ export default class PluginLoader { rootPlugin = plugins.find(p => p.root === this.options.root) ?? plugins[0] } else { const marker = Performance.mark('plugin.load#root') - rootPlugin = new Plugin.Plugin({root: this.options.root}) + rootPlugin = new Plugin.Plugin({root: this.options.root, isRoot: true}) await rootPlugin.load() marker?.addDetails({ hasManifest: rootPlugin.hasManifest ?? false, diff --git a/src/config/plugin.ts b/src/config/plugin.ts index 8f623db75..cd229e86a 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -1,13 +1,11 @@ import {CLIError, error} from '../errors' import { Debug, - flatMap, getCommandIdPermutations, - mapValues, resolvePackage, } from './util' import {Plugin as IPlugin, PluginOptions} from '../interfaces/plugin' -import {compact, exists, isProd, readJson, requireJson} from '../util' +import {compact, exists, isProd, mapValues, readJson, requireJson} from '../util' import {dirname, join, parse, relative, sep} from 'node:path' import {loadWithData, loadWithDataFromManifest} from '../module-loader' import {Command} from '../command' @@ -15,9 +13,9 @@ import {Manifest} from '../interfaces/manifest' import {PJSON} from '../interfaces/pjson' import {Performance} from '../performance' import {Topic} from '../interfaces/topic' +import {cacheCommand} from '../util/cache-command' import {inspect} from 'node:util' import {sync} from 'globby' -import {toCached} from './config' import {tsPath} from './ts-node' const _pjson = requireJson(__dirname, '..', '..', 'package.json') @@ -26,10 +24,10 @@ function topicsToArray(input: any, base?: string): Topic[] { if (!input) return [] base = base ? `${base}:` : '' if (Array.isArray(input)) { - return [...input, ...flatMap(input, t => topicsToArray(t.subtopics, `${base}${t.name}`))] + return [...input, input.flatMap(t => topicsToArray(t.subtopics, `${base}${t.name}`))] } - return flatMap(Object.keys(input), k => { + return Object.keys(input).flatMap(k => { input[k].name = k return [{...input[k], name: `${base}${k}`}, ...topicsToArray(input[k].subtopics, `${base}${input[k].name}`)] }) @@ -141,6 +139,8 @@ export class Plugin implements IPlugin { hasManifest = false + isRoot = false + private _commandsDir!: string | undefined private flexibleTaxonomy!: boolean @@ -155,6 +155,7 @@ export class Plugin implements IPlugin { public async load(): Promise { this.type = this.options.type || 'core' this.tag = this.options.tag + this.isRoot = this.options.isRoot ?? false if (this.options.parent) this.parent = this.options.parent as Plugin // Linked plugins already have a root so there's no need to search for it. // However there could be child plugins nested inside the linked plugin, in which @@ -304,7 +305,7 @@ export class Plugin implements IPlugin { version: this.version, commands: (await Promise.all(this.commandIDs.map(async id => { try { - const cached = await toCached(await this.findCommand(id, {must: true}), this, respectNoCacheDefault) + const cached = await cacheCommand(await this.findCommand(id, {must: true}), this, respectNoCacheDefault) if (this.flexibleTaxonomy) { const permutations = getCommandIdPermutations(id) const aliasPermutations = cached.aliases.flatMap(a => getCommandIdPermutations(a)) @@ -313,7 +314,7 @@ export class Plugin implements IPlugin { return [id, cached] } catch (error: any) { - const scope = 'toCached' + const scope = 'cacheCommand' if (Boolean(errorOnManifestCreate) === false) this.warn(error, scope) else throw this.addErrorScope(error, scope) } diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index 2addcf5a9..80b82d47e 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -2,7 +2,6 @@ import * as TSNode from 'ts-node' import {Plugin, TSConfig} from '../interfaces' import {isProd, readJsonSync} from '../util' import {join, relative as pathRelative} from 'node:path' -import {Config} from './config' import {Debug} from './util' import {existsSync} from 'node:fs' import {memoizedWarn} from '../errors' @@ -13,6 +12,11 @@ const debug = Debug('ts-node') export const TS_CONFIGS: Record = {} const REGISTERED = new Set() +/** + * Cache the root plugin so that we can reference it later when determining if + * we should skip ts-node registration for an ESM plugin. + */ +let ROOT_PLUGIN: Plugin | undefined function loadTSConfig(root: string): TSConfig | undefined { if (TS_CONFIGS[root]) return TS_CONFIGS[root] @@ -123,6 +127,8 @@ function registerTSNode(root: string): TSConfig | undefined { export function tsPath(root: string, orig: string, plugin: Plugin): string export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined { + if (plugin?.isRoot) ROOT_PLUGIN = plugin + if (!orig) return orig orig = orig.startsWith(root) ? orig : join(root, orig) @@ -146,8 +152,8 @@ export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): * We still register ts-node for ESM plugins when NODE_ENV is "test" or "development" and root plugin is also ESM. * In other words, this allows plugins to be auto-transpiled when developing locally using `bin/dev.js`. */ - if ((isProduction || Config.rootPlugin?.moduleType === 'commonjs') && plugin?.moduleType === 'module') { - debug(`Skipping ts-node registration for ${root} because it's an ESM module (NODE_ENV: ${process.env.NODE_ENV}, root plugin module type: ${Config.rootPlugin?.moduleType})))`) + if ((isProduction || ROOT_PLUGIN?.moduleType === 'commonjs') && plugin?.moduleType === 'module') { + debug(`Skipping ts-node registration for ${root} because it's an ESM module (NODE_ENV: ${process.env.NODE_ENV}, root plugin module type: ${ROOT_PLUGIN?.moduleType})))`) if (plugin.type === 'link') memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`) diff --git a/src/config/util.ts b/src/config/util.ts index 182b336a5..482ce4554 100644 --- a/src/config/util.ts +++ b/src/config/util.ts @@ -1,17 +1,5 @@ const debug = require('debug') -export function flatMap(arr: T[], fn: (i: T) => U[]): U[] { - return arr.reduce((arr, i) => [...arr, ...fn(i)], [] as U[]) -} - -export function mapValues, TResult>(obj: {[P in keyof T]: T[P]}, fn: (i: T[keyof T], k: keyof T) => TResult): {[P in keyof T]: TResult} { - return Object.entries(obj) - .reduce((o, [k, v]) => { - o[k] = fn(v as any, k as any) - return o - }, {} as any) -} - export function resolvePackage(id: string, paths: { paths: string[] }): string { return require.resolve(id, paths) } diff --git a/src/errors/handle.ts b/src/errors/handle.ts index 138e73d8e..ab1ff2c2c 100644 --- a/src/errors/handle.ts +++ b/src/errors/handle.ts @@ -2,7 +2,7 @@ /* eslint-disable unicorn/no-process-exit */ import {OclifError, PrettyPrintableError} from '../interfaces' import {CLIError} from './errors/cli' -import {ExitError} from '.' +import {ExitError} from './errors/exit' import clean from 'clean-stack' import {config} from './config' import prettyPrint from './errors/pretty-print' diff --git a/src/flags.ts b/src/flags.ts index 61df7db68..699dde32e 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -9,14 +9,14 @@ type NotArray = T extends Array ? never: T; export function custom( defaults: Partial> & { - multiple: true + multiple: true; } & ( {required: true} | {default: OptionFlag['default']} ), ): FlagDefinition export function custom( - defaults: Partial> & { + defaults: Partial, P>> & { multiple?: false | undefined; } & ( {required: true} | {default: OptionFlag, P>['default']} @@ -24,7 +24,7 @@ export function custom( ): FlagDefinition export function custom( - defaults: Partial> & { + defaults: Partial, P>> & { default?: OptionFlag, P>['default'] | undefined; multiple?: false | undefined; required?: false | undefined; diff --git a/src/help/index.ts b/src/help/index.ts index 871d6c2f5..1a8d1f076 100644 --- a/src/help/index.ts +++ b/src/help/index.ts @@ -5,15 +5,15 @@ import {Command} from '../command' import {CommandHelp} from './command' import {HelpFormatter} from './formatter' import RootHelp from './root' +import {cacheDefaultValue} from '../util/cache-default-value' import {error} from '../errors' import {format} from 'node:util' +import {load} from '../module-loader' import {stdout} from '../cli-ux/stream' -import {toCached} from '../config/config' - -import stripAnsi = require('strip-ansi') +import stripAnsi from 'strip-ansi' export {CommandHelp} from './command' -export {standardizeIDFromArgv, loadHelpClass, getHelpFlagAdditions, normalizeArgv} from './util' +export {standardizeIDFromArgv, getHelpFlagAdditions, normalizeArgv} from './util' function getHelpSubject(args: string[], config: Interfaces.Config): string | undefined { // for each help flag that starts with '--' create a new flag with same name sans '--' @@ -108,8 +108,14 @@ export class Help extends HelpBase { const command = this.config.findCommand(subject) if (command) { if (command.hasDynamicHelp && command.pluginType !== 'jit') { - const dynamicCommand = await toCached(await command.load()) - await this.showCommandHelp(dynamicCommand) + const loaded = await command.load() + for (const [name, flag] of Object.entries(loaded.flags)) { + if (flag.type === 'boolean' || !command.flags[name].hasDynamicHelp) continue + // eslint-disable-next-line no-await-in-loop + command.flags[name].default = await cacheDefaultValue(flag, false) + } + + await this.showCommandHelp(command) } else { await this.showCommandHelp(command) } @@ -327,3 +333,27 @@ export class Help extends HelpBase { stdout.write(format.apply(this, args) + '\n') } } + +interface HelpBaseDerived { + new(config: Interfaces.Config, opts?: Partial): HelpBase; +} + +function extractClass(exported: any): HelpBaseDerived { + return exported && exported.default ? exported.default : exported +} + +export async function loadHelpClass(config: Interfaces.Config): Promise { + const {pjson} = config + const configuredClass = pjson.oclif?.helpClass + + if (configuredClass) { + try { + const exported = await load(config, configuredClass) as HelpBaseDerived + return extractClass(exported) as HelpBaseDerived + } catch (error: any) { + throw new Error(`Unable to load configured help class "${configuredClass}", failed with message:\n${error.message}`) + } + } + + return Help +} diff --git a/src/help/root.ts b/src/help/root.ts index 6aaa587f2..1d1ed0d69 100644 --- a/src/help/root.ts +++ b/src/help/root.ts @@ -1,7 +1,7 @@ import * as Interfaces from '../interfaces' import {HelpFormatter} from './formatter' import {compact} from '../util' -import stripAnsi = require('strip-ansi') +import stripAnsi from 'strip-ansi' export default class RootHelp extends HelpFormatter { constructor(public config: Interfaces.Config, public opts: Interfaces.HelpOptions) { diff --git a/src/help/util.ts b/src/help/util.ts index 278c26050..71c07573a 100644 --- a/src/help/util.ts +++ b/src/help/util.ts @@ -1,32 +1,6 @@ import * as ejs from 'ejs' -import {Deprecation, HelpOptions, Config as IConfig} from '../interfaces' -import {Help, HelpBase} from '.' +import {Deprecation, Config as IConfig} from '../interfaces' import {collectUsableIds} from '../config/util' -import {load} from '../module-loader' - -interface HelpBaseDerived { - new(config: IConfig, opts?: Partial): HelpBase; -} - -function extractClass(exported: any): HelpBaseDerived { - return exported && exported.default ? exported.default : exported -} - -export async function loadHelpClass(config: IConfig): Promise { - const {pjson} = config - const configuredClass = pjson && pjson.oclif && pjson.oclif.helpClass - - if (configuredClass) { - try { - const exported = await load(config, configuredClass) as HelpBaseDerived - return extractClass(exported) as HelpBaseDerived - } catch (error: any) { - throw new Error(`Unable to load configured help class "${configuredClass}", failed with message:\n${error.message}`) - } - } - - return Help -} export function template(context: any): (t: string) => string { function render(t: string): string { diff --git a/src/index.ts b/src/index.ts index 4eea78bfa..6093911a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,12 +14,12 @@ checkCWD() export * as Args from './args' export * as Errors from './errors' -export * as Flags from './flags' export * as Interfaces from './interfaces' +export * as Flags from './flags' export * as Parser from './parser' export * as ux from './cli-ux' export {CommandHelp, HelpBase, Help, loadHelpClass} from './help' -export {Config, toCached, Plugin, tsPath} from './config' +export {Config, Plugin, tsPath} from './config' export {HelpSection, HelpSectionRenderer, HelpSectionKeyValueTable} from './help/formatter' export {Settings, settings} from './settings' export {stdout, stderr} from './cli-ux/stream' diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts index c95bfec7a..a201323d4 100644 --- a/src/interfaces/parser.ts +++ b/src/interfaces/parser.ts @@ -161,12 +161,6 @@ export type FlagProps = { * Emit deprecation warning when a flag alias is provided */ deprecateAliases?: boolean - /** - * Delimiter to separate the values for a multiple value flag. - * Only respected if multiple is set to true. Default behavior is to - * separate on spaces. - */ - delimiter?: ',', /** * If true, the value returned by defaultHelp will not be cached in the oclif.manifest.json. * This is helpful if the default value contains sensitive data that shouldn't be published to npm. @@ -210,6 +204,12 @@ export type OptionFlagProps = FlagProps & { helpValue?: string; options?: readonly string[]; multiple?: boolean; + /** + * Delimiter to separate the values for a multiple value flag. + * Only respected if multiple is set to true. Default behavior is to + * separate on spaces. + */ + delimiter?: ',', } export type FlagParserContext = Command & {token: FlagToken} @@ -243,7 +243,7 @@ export type BooleanFlag = FlagProps & BooleanFlagProps & { } export type OptionFlag = FlagProps & OptionFlagProps & { - parse: FlagParser + parse: FlagParser defaultHelp?: FlagDefaultHelp; input: string[]; default?: FlagDefault; @@ -361,6 +361,7 @@ export type Flag = BooleanFlag | OptionFlag export type Input = { flags?: FlagInput; baseFlags?: FlagInput; + enableJsonFlag?: true | false; args?: ArgInput; strict?: boolean; context?: ParserContext; diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index 50207bcba..772fbc10c 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -13,6 +13,7 @@ export interface PluginOptions { parent?: Plugin; children?: Plugin[]; flexibleTaxonomy?: boolean; + isRoot?: boolean; } export interface Options extends PluginOptions { @@ -73,6 +74,11 @@ export interface Plugin { */ valid: boolean; + /** + * True if the plugin is the root plugin. + */ + isRoot: boolean; + commands: Command.Loadable[]; hooks: { [k: string]: string[] }; readonly commandIDs: string[]; diff --git a/src/module-loader.ts b/src/module-loader.ts index c3105d027..ce715ed8a 100644 --- a/src/module-loader.ts +++ b/src/module-loader.ts @@ -4,7 +4,7 @@ import {extname, join, sep} from 'node:path' import {Command} from './command' import {ModuleLoadError} from './errors' import {pathToFileURL} from 'node:url' -import {tsPath} from './config' +import {tsPath} from './config/ts-node' const getPackageType = require('get-package-type') diff --git a/src/parser/parse.ts b/src/parser/parse.ts index 6376ec80c..fffcd68a7 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -250,7 +250,7 @@ export class Parser parseFlagOrThrowError( + valueFunction: async i => parseFlagOrThrowError( last(i.tokens)?.input !== `--no-${i.inputFlag.name}`, i.inputFlag.flag, this.context, @@ -262,8 +262,9 @@ export class Parser (await Promise.all( - ((i.tokens ?? []).flatMap(token => (token.input as string).split(i.inputFlag.flag.delimiter as string))) + ...fws, + valueFunction: async i => (await Promise.all( + ((i.tokens ?? []).flatMap(token => token.input.split((i.inputFlag.flag as OptionFlag).delimiter ?? ','))) // trim, and remove surrounding doubleQuotes (which would hav been needed if the elements contain spaces) .map(v => v.trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1')) .map(async v => parseFlagOrThrowError(v, i.inputFlag.flag, this.context, {...last(i.tokens) as FlagToken, input: v})), diff --git a/src/util/aggregate-flags.ts b/src/util/aggregate-flags.ts new file mode 100644 index 000000000..bd13b9fd7 --- /dev/null +++ b/src/util/aggregate-flags.ts @@ -0,0 +1,18 @@ +import {FlagInput, FlagOutput} from '../interfaces/parser' +import {boolean} from '../flags' + +const json = boolean({ + description: 'Format output as json.', + helpGroup: 'GLOBAL', +}) + +export function aggregateFlags( + flags: FlagInput | undefined, + baseFlags: FlagInput | undefined, + enableJsonFlag: boolean | undefined, +): FlagInput { + const combinedFlags = {...baseFlags, ...flags} + return (enableJsonFlag + ? {json, ...combinedFlags} + : combinedFlags) as FlagInput +} diff --git a/src/util/cache-command.ts b/src/util/cache-command.ts new file mode 100644 index 000000000..176f98516 --- /dev/null +++ b/src/util/cache-command.ts @@ -0,0 +1,111 @@ +import {ArgInput, FlagInput} from '../interfaces/parser' +import {ensureArgObject, pickBy} from './index' +import {Command} from '../command' +import {Plugin as IPlugin} from '../interfaces/plugin' +import {aggregateFlags} from './aggregate-flags' +import {cacheDefaultValue} from './cache-default-value' + +// In order to collect static properties up the inheritance chain, we need to recursively +// access the prototypes until there's nothing left. This allows us to combine baseFlags +// and flags as well as add in the json flag if enableJsonFlag is enabled. +function mergePrototype(result: Command.Class, cmd: Command.Class): Command.Class { + const proto = Object.getPrototypeOf(cmd) + const filteredProto = pickBy(proto, v => v !== undefined) as Command.Class + return Object.keys(proto).length > 0 ? mergePrototype({...filteredProto, ...result} as Command.Class, proto) : result +} + +async function cacheFlags(cmdFlags: FlagInput, respectNoCacheDefault: boolean): Promise> { + const promises = Object.entries(cmdFlags).map(async ([name, flag]) => ([name, { + name, + char: flag.char, + summary: flag.summary, + hidden: flag.hidden, + required: flag.required, + helpLabel: flag.helpLabel, + helpGroup: flag.helpGroup, + description: flag.description, + dependsOn: flag.dependsOn, + relationships: flag.relationships, + exclusive: flag.exclusive, + deprecated: flag.deprecated, + deprecateAliases: flag.deprecateAliases, + aliases: flag.aliases, + charAliases: flag.charAliases, + noCacheDefault: flag.noCacheDefault, + ...flag.type === 'boolean' ? { + allowNo: flag.allowNo, + type: flag.type, + } : { + type: flag.type, + helpValue: flag.helpValue, + multiple: flag.multiple, + options: flag.options, + delimiter: flag.delimiter, + default: await cacheDefaultValue(flag, respectNoCacheDefault), + hasDynamicHelp: typeof flag.defaultHelp === 'function', + }, + }])) + return Object.fromEntries(await Promise.all(promises)) +} + +async function cacheArgs(cmdArgs: ArgInput, respectNoCacheDefault: boolean): Promise> { + const promises = Object.entries(cmdArgs).map(async ([name, arg]) => ([name, { + name, + description: arg.description, + required: arg.required, + options: arg.options, + default: await cacheDefaultValue(arg, respectNoCacheDefault), + hidden: arg.hidden, + noCacheDefault: arg.noCacheDefault, + }])) + return Object.fromEntries(await Promise.all(promises)) +} + +export async function cacheCommand(uncachedCmd: Command.Class, plugin?: IPlugin, respectNoCacheDefault = false): Promise { + const cmd = mergePrototype(uncachedCmd, uncachedCmd) + + const flags = await cacheFlags( + aggregateFlags(cmd.flags, cmd.baseFlags, cmd.enableJsonFlag), + respectNoCacheDefault, + ) + const args = await cacheArgs(ensureArgObject(cmd.args), respectNoCacheDefault) + + const stdProperties = { + id: cmd.id, + summary: cmd.summary, + description: cmd.description, + strict: cmd.strict, + usage: cmd.usage, + pluginName: plugin && plugin.name, + pluginAlias: plugin && plugin.alias, + pluginType: plugin && plugin.type, + hidden: cmd.hidden, + state: cmd.state, + aliases: cmd.aliases || [], + examples: cmd.examples || (cmd as any).example, + deprecationOptions: cmd.deprecationOptions, + deprecateAliases: cmd.deprecateAliases, + flags, + args, + hasDynamicHelp: Object.values(flags).some(f => f.hasDynamicHelp), + } + + // do not include these properties in manifest + const ignoreCommandProperties = [ + 'plugin', + '_flags', + '_enableJsonFlag', + '_globalFlags', + '_baseFlags', + 'baseFlags', + '_--', + '_base', + ] + + // Add in any additional properties that are not standard command properties. + const stdKeysAndIgnored = new Set([...Object.keys(stdProperties), ...ignoreCommandProperties]) + const keysToAdd = Object.keys(cmd).filter(property => !stdKeysAndIgnored.has(property)) + const additionalProperties = Object.fromEntries(keysToAdd.map(key => [key, (cmd as any)[key]])) + + return {...stdProperties, ...additionalProperties} +} diff --git a/src/util/cache-default-value.ts b/src/util/cache-default-value.ts new file mode 100644 index 000000000..990eb5179 --- /dev/null +++ b/src/util/cache-default-value.ts @@ -0,0 +1,23 @@ +import {Arg, OptionFlag} from '../interfaces/parser' + +// when no manifest exists, the default is calculated. This may throw, so we need to catch it +export const cacheDefaultValue = async (flagOrArg: OptionFlag | Arg, respectNoCacheDefault: boolean) => { + if (respectNoCacheDefault && flagOrArg.noCacheDefault) return + // Prefer the defaultHelp function (returns a friendly string for complex types) + if (typeof flagOrArg.defaultHelp === 'function') { + try { + return await flagOrArg.defaultHelp({options: flagOrArg, flags: {}}) + } catch { + return + } + } + + // if not specified, try the default function + if (typeof flagOrArg.default === 'function') { + try { + return await flagOrArg.default({options: flagOrArg, flags: {}}) + } catch {} + } else { + return flagOrArg.default + } +} diff --git a/src/util.ts b/src/util/index.ts similarity index 88% rename from src/util.ts rename to src/util/index.ts index a5c0eab5b..df4a90ec4 100644 --- a/src/util.ts +++ b/src/util/index.ts @@ -1,9 +1,9 @@ -import {access, stat} from 'node:fs/promises' +import {access, readFile, stat} from 'node:fs/promises' import {homedir, platform} from 'node:os' -import {readFile, readFileSync} from 'node:fs' -import {ArgInput} from './interfaces/parser' -import {Command} from './command' +import {ArgInput} from '../interfaces/parser' +import {Command} from '../command' import {join} from 'node:path' +import {readFileSync} from 'node:fs' const debug = require('debug') @@ -169,18 +169,10 @@ export function getPlatform(): NodeJS.Platform { return platform() } -export function readJson(path: string): Promise { +export async function readJson(path: string): Promise { debug('config')('readJson %s', path) - return new Promise((resolve, reject) => { - readFile(path, 'utf8', (err: any, d: any) => { - try { - if (err) reject(err) - else resolve(JSON.parse(d) as T) - } catch (error: any) { - reject(error) - } - }) - }) + const contents = await readFile(path, 'utf8') + return JSON.parse(contents) as T } export function readJsonSync(path: string, parse: false): string @@ -189,3 +181,11 @@ export function readJsonSync(path: string, parse = true): T | strin const contents = readFileSync(path, 'utf8') return parse ? JSON.parse(contents) as T : contents } + +export function mapValues, TResult>(obj: {[P in keyof T]: T[P]}, fn: (i: T[keyof T], k: keyof T) => TResult): {[P in keyof T]: TResult} { + return Object.entries(obj) + .reduce((o, [k, v]) => { + o[k] = fn(v as any, k as any) + return o + }, {} as any) +} diff --git a/test/command/command.test.ts b/test/command/command.test.ts index ddbad21bb..ee8991892 100644 --- a/test/command/command.test.ts +++ b/test/command/command.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' // import path = require('path') -import {Args, Command as Base, Flags, toCached} from '../../src' +import {Command as Base, Flags} from '../../src' // import {TestHelpClassConfig} from './helpers/test-help-in-src/src/test-help-plugin' // const pjson = require('../package.json') @@ -69,208 +69,6 @@ describe('command', () => { .catch(/EEXIT: 0/) .it('exits with 0') - describe('toCached', () => { - fancy - .do(async () => { - class C extends Command { - static id = 'foo:bar' - static title = 'cmd title' - static type = 'mytype' - static usage = ['$ usage'] - static description = 'test command' - static aliases = ['alias1', 'alias2'] - static hidden = true - // @ts-ignore - static flags = { - flaga: Flags.boolean(), - flagb: Flags.string({ - char: 'b', - hidden: true, - required: false, - description: 'flagb desc', - options: ['a', 'b'], - default: async () => 'a', - }), - flagc: Flags.integer({ - char: 'c', - min: 1, - max: 10, - required: false, - description: 'flagc desc', - options: ['a', 'b'], - // @ts-expect-error: context is any - default: async context => context.options.min + 1, - }), - } - - static args = { - arg1: Args.string({ - description: 'arg1 desc', - required: true, - hidden: false, - options: ['af', 'b'], - default: async () => 'a', - }), - } - } - - const c = await toCached(C, undefined, false) - - expect(c).to.deep.equal({ - id: 'foo:bar', - type: 'mytype', - hidden: true, - pluginName: undefined, - pluginAlias: undefined, - pluginType: undefined, - state: undefined, - description: 'test command', - aliases: ['alias1', 'alias2'], - title: 'cmd title', - usage: ['$ usage'], - examples: undefined, - deprecationOptions: undefined, - deprecateAliases: undefined, - summary: undefined, - strict: true, - flags: { - flaga: { - aliases: undefined, - char: undefined, - charAliases: undefined, - description: undefined, - dependsOn: undefined, - deprecateAliases: undefined, - deprecated: undefined, - exclusive: undefined, - helpGroup: undefined, - helpLabel: undefined, - summary: undefined, - name: 'flaga', - hidden: undefined, - required: undefined, - relationships: undefined, - allowNo: false, - type: 'boolean', - delimiter: undefined, - noCacheDefault: undefined, - }, - flagb: { - aliases: undefined, - char: 'b', - charAliases: undefined, - description: 'flagb desc', - dependsOn: undefined, - deprecateAliases: undefined, - deprecated: undefined, - exclusive: undefined, - helpGroup: undefined, - helpLabel: undefined, - summary: undefined, - name: 'flagb', - hidden: true, - required: false, - multiple: false, - relationships: undefined, - type: 'option', - helpValue: undefined, - default: 'a', - options: ['a', 'b'], - delimiter: undefined, - noCacheDefault: undefined, - }, - flagc: { - aliases: undefined, - char: 'c', - charAliases: undefined, - default: 2, - delimiter: undefined, - dependsOn: undefined, - deprecateAliases: undefined, - deprecated: undefined, - description: 'flagc desc', - exclusive: undefined, - helpGroup: undefined, - helpLabel: undefined, - helpValue: undefined, - hidden: undefined, - multiple: false, - name: 'flagc', - options: [ - 'a', - 'b', - ], - relationships: undefined, - required: false, - summary: undefined, - type: 'option', - noCacheDefault: undefined, - }, - - }, - args: { - arg1: { - description: 'arg1 desc', - name: 'arg1', - hidden: false, - required: true, - options: ['af', 'b'], - default: 'a', - noCacheDefault: undefined, - }, - }, - }) - }) - .it('converts to cached with everything set') - - fancy - // .skip() - .do(async () => { - // const c = class extends Command { - // }.convertToCached() - // expect(await c.load()).to.have.property('run') - // delete c.load - // expect(c).to.deep.equal({ - // _base: `@oclif/command@${pjson.version}`, - // id: undefined, - // type: undefined, - // hidden: undefined, - // pluginName: undefined, - // description: 'test command', - // aliases: [], - // title: undefined, - // usage: undefined, - // flags: {}, - // args: [], - // }) - }) - - .it('adds plugin name') - - fancy - // .skip() - // .do(async () => { - // const c = class extends Command { - // }.convertToCached({pluginName: 'myplugin'}) - // expect(await c.load()).to.have.property('run') - // delete c.load - // expect(c).to.deep.equal({ - // _base: `@oclif/command@${pjson.version}`, - // type: undefined, - // id: undefined, - // hidden: undefined, - // pluginName: 'myplugin', - // description: 'test command', - // aliases: [], - // title: undefined, - // usage: undefined, - // flags: {}, - // args: [], - // }) - // }) - .it('converts to cached with nothing set') - }) - describe('parse', () => { fancy .stdout() @@ -553,7 +351,6 @@ describe('command', () => { .do(async () => { class CMD extends Command { static enableJsonFlag = true - static '--' = true async run() { const {flags} = await cmd.parse(CMD, ['--json']) @@ -570,16 +367,12 @@ describe('command', () => { .stdout() .do(async () => { class CMD extends Command { - // static initialization block is required whenever using ES2022 - static { - this.enableJsonFlag = true - this['--'] = true - } + static enableJsonFlag = true async run() { const {flags} = await cmd.parse(CMD, ['--', '--json']) expect(flags.json).to.equal(false, 'json flag should be false') - expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + // expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') } } @@ -593,7 +386,6 @@ describe('command', () => { .do(async () => { class CMD extends Command { static enableJsonFlag = true - static '--' = true async run() { const {flags} = await cmd.parse(CMD, ['--foo', '--json']) @@ -611,12 +403,11 @@ describe('command', () => { .do(async () => { class CMD extends Command { static enableJsonFlag = true - static '--' = true async run() { const {flags} = await cmd.parse(CMD, ['--foo', '--', '--json']) expect(flags.json).to.equal(false, 'json flag should be false') - expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + // expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') } } @@ -630,7 +421,6 @@ describe('command', () => { .do(async () => { class CMD extends Command { static enableJsonFlag = true - static '--' = true async run() {} } diff --git a/test/config/config.flexible.test.ts b/test/config/config.flexible.test.ts index 829b00463..6b451da95 100644 --- a/test/config/config.flexible.test.ts +++ b/test/config/config.flexible.test.ts @@ -101,6 +101,7 @@ describe('Config with flexible taxonomy', () => { tag: 'tag', moduleType: 'commonjs', hasManifest: false, + isRoot: false, } const pluginB: IPlugin = { @@ -121,6 +122,7 @@ describe('Config with flexible taxonomy', () => { tag: 'tag', moduleType: 'commonjs', hasManifest: false, + isRoot: false, } const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) diff --git a/test/config/config.test.ts b/test/config/config.test.ts index cd5aa9971..1b821d793 100644 --- a/test/config/config.test.ts +++ b/test/config/config.test.ts @@ -282,6 +282,7 @@ describe('Config', () => { tag: 'tag', moduleType: 'commonjs', hasManifest: false, + isRoot: false, } const pluginB: IPlugin = { @@ -302,6 +303,7 @@ describe('Config', () => { tag: 'tag', moduleType: 'commonjs', hasManifest: false, + isRoot: false, } const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) let test = fancy diff --git a/test/help/help-test-utils.ts b/test/help/help-test-utils.ts index cf94558b2..76db5c203 100644 --- a/test/help/help-test-utils.ts +++ b/test/help/help-test-utils.ts @@ -2,8 +2,9 @@ import {Command} from '../../src/command' import stripAnsi = require('strip-ansi') -import {Interfaces, toCached} from '../../src' +import {Interfaces} from '../../src' import {CommandHelp, Help} from '../../src/help' +import {cacheCommand} from '../../src/util/cache-command' export class TestCommandHelp extends CommandHelp { protected sections() { @@ -47,7 +48,7 @@ export class TestHelp extends Help { export const commandHelp = (command?: any) => ({ async run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) { - const cached = await toCached(command!, {} as any, false) + const cached = await cacheCommand(command!, {} as any, false) const help = ctx.help.formatCommand(cached) if (process.env.TEST_OUTPUT === '1') { console.log(help) diff --git a/test/help/util.test.ts b/test/help/util.test.ts index c25aa0b3f..cd02b70ce 100644 --- a/test/help/util.test.ts +++ b/test/help/util.test.ts @@ -4,6 +4,7 @@ import {test} from '@oclif/test' import {loadHelpClass, standardizeIDFromArgv} from '../../src/help' import configuredHelpClass from './_test-help-class' import {expect} from 'chai' +import * as util from '../../src/config/util' describe('util', () => { let config: Interfaces.Config @@ -52,7 +53,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space', () => { config.topicSeparator = ' ' const actual = standardizeIDFromArgv(['foo', 'bar', '--baz'], config) @@ -60,7 +61,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and command is misspelled', () => { config.topicSeparator = ' ' const actual = standardizeIDFromArgv(['foo', 'ba', '--baz'], config) @@ -68,7 +69,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has args and command is misspelled', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -81,7 +82,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has args', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -94,7 +95,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has variable arguments', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -107,7 +108,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has variable arguments and flags', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -120,7 +121,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return full id when topic separator is a space and does not have arguments', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -134,7 +135,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has arg with value', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -144,7 +145,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has variable args with value', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -154,7 +155,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has flags', () => { config.topicSeparator = ' ' // @ts-expect-error private member @@ -164,7 +165,7 @@ describe('util', () => { }) test - .stub(Config.prototype, 'collectUsableIds', () => ['foo', 'foo:bar']) + .stub(util, 'collectUsableIds', stub => stub.returns(new Set(['foo', 'foo:bar']))) .it('should return standardized id when topic separator is a space and has flags, arg, and arg with value', () => { config.topicSeparator = ' ' // @ts-expect-error private member diff --git a/test/util.test.ts b/test/util.test.ts deleted file mode 100644 index 4820f23a6..000000000 --- a/test/util.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {expect} from 'chai' -import {capitalize, ensureArgObject, last, maxBy, sumBy} from '../src/util' - -describe('capitalize', () => { - it('capitalizes the string', () => { - expect(capitalize('dominik')).to.equal('Dominik') - }) - it('works with an empty string', () => { - expect(capitalize('')).to.equal('') - }) -}) - -type Item = { x: number } - -describe('sumBy', () => { - it('returns zero for empty array', () => { - const arr: Item[] = [] - expect(sumBy(arr, i => i.x)).to.equal(0) - }) - it('returns sum for non-empty array', () => { - const arr: Item[] = [{x: 1}, {x: 2}, {x: 3}] - expect(sumBy(arr, i => i.x)).to.equal(6) - }) -}) - -describe('maxBy', () => { - it('returns undefined for empty array', () => { - const arr: Item[] = [] - expect(maxBy(arr, i => i.x)).to.be.undefined - }) - it('returns max value in the array', () => { - const arr: Item[] = [{x: 1}, {x: 3}, {x: 2}] - expect(maxBy(arr, i => i.x)).to.equal(arr[1]) - }) -}) - -describe('last', () => { - it('returns undefined for empty array', () => { - expect(last([])).to.be.undefined - }) - it('returns undefined for undefined', () => { - expect(last()).to.be.undefined - }) - it('returns last value in the array', () => { - const arr: Item[] = [{x: 1}, {x: 3}, {x: 2}] - expect(last(arr)).to.equal(arr[2]) - }) - it('returns only item in array', () => { - expect(last([6])).to.equal(6) - }) -}) - -describe('ensureArgObject', () => { - it('should convert array of arguments to an object', () => { - const args = [ - {name: 'foo', description: 'foo desc', required: true}, - {name: 'bar', description: 'bar desc'}, - ] - const expected = {foo: args[0], bar: args[1]} - expect(ensureArgObject(args)).to.deep.equal(expected) - }) - - it('should do nothing to an arguments object', () => { - const args = { - foo: {name: 'foo', description: 'foo desc', required: true}, - bar: {name: 'bar', description: 'bar desc'}, - } - expect(ensureArgObject(args)).to.deep.equal(args) - }) -}) diff --git a/test/util/cache-command.test.ts b/test/util/cache-command.test.ts new file mode 100644 index 000000000..b46a878fe --- /dev/null +++ b/test/util/cache-command.test.ts @@ -0,0 +1,298 @@ +import {expect} from 'chai' +import {Args, Command, Flags} from '../../src/index' +import {cacheCommand} from '../../src/util/cache-command' + +describe('cacheCommand', () => { + it('should return a cached command with every thing set', async () => { + class C extends Command { + static id = 'foo:bar' + static title = 'cmd title' + static type = 'mytype' + static usage = ['$ usage'] + static description = 'test command' + static aliases = ['alias1', 'alias2'] + static hidden = true + static flags = { + flaga: Flags.boolean(), + flagb: Flags.string({ + char: 'b', + hidden: true, + required: false, + description: 'flagb desc', + options: ['a', 'b'], + default: async () => 'a', + }), + flagc: Flags.integer({ + char: 'c', + min: 1, + max: 10, + required: false, + description: 'flagc desc', + options: ['a', 'b'], + default: async context => (context.options.min ?? 1) + 1, + }), + } + + static args = { + arg1: Args.string({ + description: 'arg1 desc', + required: true, + hidden: false, + options: ['af', 'b'], + default: async () => 'a', + }), + } + + public async run(): Promise {} + } + const c = await cacheCommand(C, undefined, false) + expect(c).to.deep.equal({ + id: 'foo:bar', + type: 'mytype', + hidden: true, + pluginName: undefined, + pluginAlias: undefined, + pluginType: undefined, + state: undefined, + description: 'test command', + aliases: ['alias1', 'alias2'], + title: 'cmd title', + usage: ['$ usage'], + examples: undefined, + deprecationOptions: undefined, + deprecateAliases: undefined, + summary: undefined, + strict: true, + enableJsonFlag: false, + hasDynamicHelp: false, + flags: { + flaga: { + aliases: undefined, + char: undefined, + charAliases: undefined, + description: undefined, + dependsOn: undefined, + deprecateAliases: undefined, + deprecated: undefined, + exclusive: undefined, + helpGroup: undefined, + helpLabel: undefined, + summary: undefined, + name: 'flaga', + hidden: undefined, + required: undefined, + relationships: undefined, + allowNo: false, + type: 'boolean', + noCacheDefault: undefined, + }, + flagb: { + aliases: undefined, + char: 'b', + charAliases: undefined, + description: 'flagb desc', + dependsOn: undefined, + deprecateAliases: undefined, + deprecated: undefined, + exclusive: undefined, + helpGroup: undefined, + helpLabel: undefined, + summary: undefined, + name: 'flagb', + hidden: true, + required: false, + multiple: false, + relationships: undefined, + type: 'option', + helpValue: undefined, + default: 'a', + options: ['a', 'b'], + delimiter: undefined, + noCacheDefault: undefined, + hasDynamicHelp: false, + }, + flagc: { + aliases: undefined, + char: 'c', + charAliases: undefined, + default: 2, + delimiter: undefined, + dependsOn: undefined, + deprecateAliases: undefined, + deprecated: undefined, + description: 'flagc desc', + exclusive: undefined, + helpGroup: undefined, + helpLabel: undefined, + helpValue: undefined, + hidden: undefined, + multiple: false, + name: 'flagc', + options: [ + 'a', + 'b', + ], + relationships: undefined, + required: false, + summary: undefined, + type: 'option', + noCacheDefault: undefined, + hasDynamicHelp: false, + }, + + }, + args: { + arg1: { + description: 'arg1 desc', + name: 'arg1', + hidden: false, + required: true, + options: ['af', 'b'], + default: 'a', + noCacheDefault: undefined, + }, + }, + }) + }) + + it('should return a cached command with multiple Command classes in inheritance chain', async () => { + class Base extends Command { + public static enableJsonFlag = true + public static aliases = ['base'] + public static state = 'beta' + public static summary = 'base summary' + public static baseFlags = { + parentFlag: Flags.boolean(), + } + + public async run(): Promise {} + } + + class Child extends Base { + static id = 'command' + public static summary = 'child summary' + static flags = { + childFlag: Flags.boolean(), + } + + public async run(): Promise {} + } + + const cached = await cacheCommand(Child, undefined, false) + + expect(cached).to.deep.equal({ + id: 'command', + summary: 'child summary', + description: undefined, + strict: true, + usage: undefined, + pluginName: undefined, + pluginAlias: undefined, + pluginType: undefined, + hidden: undefined, + state: 'beta', + aliases: ['base'], + examples: undefined, + deprecationOptions: undefined, + deprecateAliases: undefined, + flags: { + json: { + name: 'json', + type: 'boolean', + char: undefined, + summary: undefined, + description: 'Format output as json.', + hidden: undefined, + required: undefined, + helpLabel: undefined, + helpGroup: 'GLOBAL', + allowNo: false, + dependsOn: undefined, + relationships: undefined, + exclusive: undefined, + deprecated: undefined, + deprecateAliases: undefined, + aliases: undefined, + charAliases: undefined, + noCacheDefault: undefined, + }, + childFlag: { + name: 'childFlag', + type: 'boolean', + char: undefined, + summary: undefined, + description: undefined, + hidden: undefined, + required: undefined, + helpLabel: undefined, + helpGroup: undefined, + allowNo: false, + dependsOn: undefined, + relationships: undefined, + exclusive: undefined, + deprecated: undefined, + deprecateAliases: undefined, + aliases: undefined, + charAliases: undefined, + noCacheDefault: undefined, + }, + parentFlag: { + name: 'parentFlag', + type: 'boolean', + char: undefined, + summary: undefined, + description: undefined, + hidden: undefined, + required: undefined, + helpLabel: undefined, + helpGroup: undefined, + allowNo: false, + dependsOn: undefined, + relationships: undefined, + exclusive: undefined, + deprecated: undefined, + deprecateAliases: undefined, + aliases: undefined, + charAliases: undefined, + noCacheDefault: undefined, + }, + }, + args: {}, + hasDynamicHelp: false, + enableJsonFlag: true, + }) + }) + + it('should set dynamicHelp to true if defaultHelp is a function', async () => { + class C extends Command { + static id = 'foo:bar' + static flags = { + flaga: Flags.boolean(), + flagb: Flags.string({ + defaultHelp: async () => 'foo', + }), + } + + public async run(): Promise {} + } + const c = await cacheCommand(C, undefined, false) + expect(c.hasDynamicHelp).to.be.true + expect(c.flags.flagb.hasDynamicHelp).to.be.true + }) + + it('should add additional command properties', async () => { + class C extends Command { + static id = 'foo:bar' + static flags = { + flaga: Flags.boolean(), + } + + static envVars = ['FOO_BAR'] + + public async run(): Promise {} + } + + const c = await cacheCommand(C, undefined, false) + expect(c.envVars).to.deep.equal(['FOO_BAR']) + }) +}) diff --git a/test/util/cache-default-value.test.ts b/test/util/cache-default-value.test.ts new file mode 100644 index 000000000..9f7a2b6f9 --- /dev/null +++ b/test/util/cache-default-value.test.ts @@ -0,0 +1,42 @@ +import {expect} from 'chai' +import {cacheDefaultValue} from '../../src/util/cache-default-value' + +describe('cacheDefaultValue', () => { + it('should do nothing if noCacheDefault is true', async () => { + const flag = {noCacheDefault: true} + const result = await cacheDefaultValue(flag as any, true) + expect(result).to.be.undefined + }) + + it('should do nothing if respectNoCacheDefault is true', async () => { + const result = await cacheDefaultValue({} as any, true) + expect(result).to.be.undefined + }) + + it('should return the result of defaultHelp if it exists', async () => { + const flag = {defaultHelp: async () => 'foo'} + const result = await cacheDefaultValue(flag as any, false) + expect(result).to.equal('foo') + }) + + it('should return undefined if defaultHelp throws', async () => { + const flag = {async defaultHelp() { + throw new Error('foo') + }} + const result = await cacheDefaultValue(flag as any, false) + expect(result).to.be.undefined + }) + + it('should return the result of the default if it\'s a function', async () => { + const flag = {default: async () => 'foo'} + const result = await cacheDefaultValue(flag as any, false) + expect(result).to.equal('foo') + }) + + it('should return the result of the default if it\'s a simple value', async () => { + const flag = {default: 'foo'} + const result = await cacheDefaultValue(flag as any, false) + expect(result).to.equal('foo') + }) +}) + diff --git a/test/util/index.test.ts b/test/util/index.test.ts new file mode 100644 index 000000000..815e45f39 --- /dev/null +++ b/test/util/index.test.ts @@ -0,0 +1,141 @@ +import {expect} from 'chai' +import {homedir} from 'node:os' +import {capitalize, castArray, ensureArgObject, getHomeDir, isNotFalsy, isTruthy, last, maxBy, readJson, sumBy} from '../../src/util/index' + +describe('capitalize', () => { + it('capitalizes the string', () => { + expect(capitalize('dominik')).to.equal('Dominik') + }) + it('works with an empty string', () => { + expect(capitalize('')).to.equal('') + }) +}) + +type Item = { x: number } + +describe('sumBy', () => { + it('returns zero for empty array', () => { + const arr: Item[] = [] + expect(sumBy(arr, i => i.x)).to.equal(0) + }) + it('returns sum for non-empty array', () => { + const arr: Item[] = [{x: 1}, {x: 2}, {x: 3}] + expect(sumBy(arr, i => i.x)).to.equal(6) + }) +}) + +describe('maxBy', () => { + it('returns undefined for empty array', () => { + const arr: Item[] = [] + expect(maxBy(arr, i => i.x)).to.be.undefined + }) + it('returns max value in the array', () => { + const arr: Item[] = [{x: 1}, {x: 3}, {x: 2}] + expect(maxBy(arr, i => i.x)).to.equal(arr[1]) + }) +}) + +describe('last', () => { + it('returns undefined for empty array', () => { + expect(last([])).to.be.undefined + }) + it('returns undefined for undefined', () => { + expect(last()).to.be.undefined + }) + it('returns last value in the array', () => { + const arr: Item[] = [{x: 1}, {x: 3}, {x: 2}] + expect(last(arr)).to.equal(arr[2]) + }) + it('returns only item in array', () => { + expect(last([6])).to.equal(6) + }) +}) + +describe('ensureArgObject', () => { + it('should convert array of arguments to an object', () => { + const args = [ + {name: 'foo', description: 'foo desc', required: true}, + {name: 'bar', description: 'bar desc'}, + ] + const expected = {foo: args[0], bar: args[1]} + expect(ensureArgObject(args)).to.deep.equal(expected) + }) + + it('should do nothing to an arguments object', () => { + const args = { + foo: {name: 'foo', description: 'foo desc', required: true}, + bar: {name: 'bar', description: 'bar desc'}, + } + expect(ensureArgObject(args)).to.deep.equal(args) + }) +}) + +describe('isNotFalsy', () => { + it('should return true for truthy values', () => { + expect(isNotFalsy('true')).to.be.true + expect(isNotFalsy('1')).to.be.true + expect(isNotFalsy('yes')).to.be.true + expect(isNotFalsy('y')).to.be.true + }) + + it('should return false for falsy values', () => { + expect(isNotFalsy('false')).to.be.false + expect(isNotFalsy('0')).to.be.false + expect(isNotFalsy('no')).to.be.false + expect(isNotFalsy('n')).to.be.false + }) +}) + +describe('isTruthy', () => { + it('should return true for truthy values', () => { + expect(isTruthy('true')).to.be.true + expect(isTruthy('1')).to.be.true + expect(isTruthy('yes')).to.be.true + expect(isTruthy('y')).to.be.true + }) + + it('should return false for falsy values', () => { + expect(isTruthy('false')).to.be.false + expect(isTruthy('0')).to.be.false + expect(isTruthy('no')).to.be.false + expect(isTruthy('n')).to.be.false + }) +}) + +describe('getHomeDir', () => { + it('should return the home directory', () => { + expect(getHomeDir()).to.equal(homedir()) + }) +}) + +describe('readJson', () => { + it('should return parsed JSON', async () => { + const json = await readJson<{name: string}>('package.json') + expect(json.name).to.equal('@oclif/core') + }) + + it('should throw an error if the file does not exist', async () => { + try { + await readJson('does-not-exist.json') + throw new Error('Expected an error to be thrown') + } catch (error) { + const err = error as Error + expect(err.message).to.include('ENOENT: no such file or directory') + } + }) +}) + +describe('castArray', () => { + it('should cast a value to an array', () => { + expect(castArray('foo')).to.deep.equal(['foo']) + }) + + it('should return an array if the value is an array', () => { + expect(castArray(['foo'])).to.deep.equal(['foo']) + }) + + it('should return an empty array if the value is undefined', () => { + expect(castArray()).to.deep.equal([]) + }) +}) + diff --git a/tsconfig.json b/tsconfig.json index 0aed879ee..6e71ac339 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "./src" ], "strict": true, - "target": "ES2021", + "target": "ES2022", "allowSyntheticDefaultImports": true, "noErrorTruncation": true, "moduleResolution": "Node16" diff --git a/yarn.lock b/yarn.lock index 16a9fccd4..30b5a6612 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,14 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@babel/code-frame@^7.0.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" @@ -14,16 +22,142 @@ dependencies: "@babel/highlight" "^7.14.5" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.9": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.20.tgz#8df6e96661209623f1975d66c35ffca66f3306d0" + integrity sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw== + +"@babel/core@^7.7.5": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.0.tgz#f8259ae0e52a123eb40f552551e647b506a94d83" + integrity sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.0" + "@babel/helpers" "^7.23.0" + "@babel/parser" "^7.23.0" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.0" + "@babel/types" "^7.23.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz#3ec246457f6c842c0aee62a01f60739906f7047e" + integrity sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.14.5": version "7.15.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== -"@babel/helper-validator-identifier@^7.22.5": +"@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.22.5": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + +"@babel/helpers@^7.23.0": + version "7.23.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.1.tgz#44e981e8ce2b9e99f8f0b703f3326a4636c16d15" + integrity sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.0" + "@babel/types" "^7.23.0" + "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -33,6 +167,54 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.0.0", "@babel/parser@^7.21.4", "@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.0.tgz#18196ddfbcf4ccea324b7f6d3ada00d8c5a99c53" + integrity sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -182,6 +364,14 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@dependents/detective-less@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@dependents/detective-less/-/detective-less-3.0.2.tgz#c6e46997010fe03a5dc98351a7e99a46d34f5832" + integrity sha512-1YUvQ+e0eeTWAHoN8Uz2x2U37jZs6IGutiIE5LXId7cxfUGhtZjzxE06FdUiuiRrW+UE0vNCdSNPH2lY4dQCOQ== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^5.0.1" + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -250,6 +440,22 @@ resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -257,16 +463,40 @@ dependencies: "@sinclair/typebox" "^0.27.8" +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -275,6 +505,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -524,6 +762,38 @@ wordwrap "^1.0.0" wrap-ansi "^7.0.0" +"@oclif/core@^3.0.0-beta.17": + version "3.0.0-beta.17" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.0.0-beta.17.tgz#a6f0d22703647743aba6c9e53f0560724aba3ccb" + integrity sha512-knoDpfAVYgbVeuoUpX/es8os1GeVp//gNczB3mgNPotENXMALOZKgIv7rBbKpHa1xmpGdEf0n31WrBq3HigKwQ== + dependencies: + "@types/cli-progress" "^3.11.0" + ansi-escapes "^4.3.2" + ansi-styles "^4.3.0" + cardinal "^2.1.1" + chalk "^4.1.2" + clean-stack "^3.0.1" + cli-progress "^3.12.0" + debug "^4.3.4" + ejs "^3.1.8" + get-package-type "^0.1.0" + globby "^11.1.0" + hyperlinker "^1.0.0" + indent-string "^4.0.0" + is-wsl "^2.2.0" + js-yaml "^3.14.1" + natural-orderby "^2.0.3" + object-treeify "^1.1.33" + password-prompt "^1.1.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + supports-color "^8.1.1" + supports-hyperlinks "^2.2.0" + widest-line "^3.1.0" + wordwrap "^1.0.0" + wrap-ansi "^7.0.0" + "@oclif/plugin-help@^5.2.8": version "5.2.8" resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-5.2.8.tgz#25821ea8588b59e5554187611ce58b8a95d4e90d" @@ -550,13 +820,18 @@ validate-npm-package-name "^5.0.0" yarn "^1.22.18" -"@oclif/test@^2.4.7": - version "2.4.7" - resolved "https://registry.yarnpkg.com/@oclif/test/-/test-2.4.7.tgz#4372270157aa6598c0d1b61349b2eff4e2003193" - integrity sha512-r18sKGNUm/VGQ8BkSF9Kn7QeMGjGMDUrLxTDPzL5ERaBF5YSi+O9CT3mKhcFdrMwGnCqPVvlAdX4U/6gtYPy1A== +"@oclif/prettier-config@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@oclif/prettier-config/-/prettier-config-0.1.1.tgz#424b7f631b96917efe8b9a925b068cd2218a00f2" + integrity sha512-fxsIwtHCu8V6+WmsN0w/s6QM3z/VJtvTEXTiY8F8l1Oj9S+nOC0eTaQp2YSDFlgpFvDJ5NwC5ssdZAdFE3PyDg== + +"@oclif/test@^3.0.0-beta.1": + version "3.0.0-beta.1" + resolved "https://registry.yarnpkg.com/@oclif/test/-/test-3.0.0-beta.1.tgz#37beca545657eaf04413652f3b3db4f946c9804a" + integrity sha512-f/cuHO1GboGWBvBCxApkm+EtNjTLIT710XANPChdqPrQ5zF2zLicU6Kt7asH4HPsRKYl+LjJUgF1299t6333LA== dependencies: - "@oclif/core" "^2.11.10" - fancy-test "^2.0.34" + "@oclif/core" "^3.0.0-beta.17" + fancy-test "^3.0.1" "@pkgjs/parseargs@^0.11.0": version "0.11.0" @@ -920,6 +1195,16 @@ debug "^4.3.4" ts-api-utils "^1.0.1" +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + "@typescript-eslint/types@6.7.2": version "6.7.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066" @@ -938,6 +1223,32 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/typescript-estree@^4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@^5.55.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@6.7.2": version "6.7.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf" @@ -951,6 +1262,22 @@ "@typescript-eslint/typescript-estree" "6.7.2" semver "^7.5.4" +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + "@typescript-eslint/visitor-keys@6.7.2": version "6.7.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c" @@ -1097,6 +1424,11 @@ ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -1105,12 +1437,24 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== + +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + "aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -archy@~1.0.0: +archy@^1.0.0, archy@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== @@ -1231,6 +1575,21 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +ast-module-types@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3" + integrity sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw== + +ast-module-types@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-3.0.0.tgz#9a6d8a80f438b6b8fe4995699d700297f398bf81" + integrity sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ== + +ast-module-types@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-4.0.0.tgz#17e1cadd5b5b108e7295b0cf0cff21ccc226b639" + integrity sha512-Kd0o8r6CDazJGCRzs8Ivpn0xj19oNKrULhoJFzhGjRsLpekF2zyZs9Ukz+JvZhWD6smszfepakTFhAaYpsI12g== + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -1284,6 +1643,15 @@ binary-extensions@^2.0.0, binary-extensions@^2.2.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1311,6 +1679,24 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browserslist@^4.21.9: + version "4.22.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.0.tgz#6adc8116589ccea8a99d0df79c5de2436199abdb" + integrity sha512-v+Jcv64L2LbfTC6OnRcaxtqJNJuQAVhZKSJfR/6hn7lhnChUXl4amwVviqN1k411BB+3rRoKMitELRn1CojeRA== + dependencies: + caniuse-lite "^1.0.30001539" + electron-to-chromium "^1.4.530" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -1349,6 +1735,16 @@ cacache@^17.0.0, cacache@^17.0.4, cacache@^17.1.3: tar "^6.1.11" unique-filename "^3.0.0" +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -1371,7 +1767,7 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" -camelcase@^5.3.1: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -1381,6 +1777,11 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== +caniuse-lite@^1.0.30001539: + version "1.0.30001540" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001540.tgz#a316ca4f2ae673ab02ff0ec533334016d56ff658" + integrity sha512-9JL38jscuTJBTcuETxm8QLsFr/F6v0CYYTEU6r5+qSM98P2Q0Hmu0eG1dTG5GBUmywU3UlcVOUSIJYY47rdFSw== + cardinal@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" @@ -1409,7 +1810,7 @@ chai@^4.3.7: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1418,7 +1819,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1502,6 +1903,13 @@ cli-columns@^4.0.0: string-width "^4.2.3" strip-ansi "^6.0.1" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + cli-progress@^3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942" @@ -1509,6 +1917,11 @@ cli-progress@^3.12.0: dependencies: string-width "^4.2.3" +cli-spinners@^2.5.0: + version "2.9.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.1.tgz#9c0b9dad69a6d47cbb4333c14319b060ed395a35" + integrity sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ== + cli-table3@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" @@ -1518,6 +1931,15 @@ cli-table3@^0.6.3: optionalDependencies: "@colors/colors" "1.5.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1556,7 +1978,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@~1.1.4: +color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -1574,6 +1996,21 @@ columnify@^1.6.0: strip-ansi "^6.0.1" wcwidth "^1.0.0" +commander@^2.16.0, commander@^2.20.3, commander@^2.8.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + commitlint@^12.1.4: version "12.1.4" resolved "https://registry.yarnpkg.com/commitlint/-/commitlint-12.1.4.tgz#afc185957afdd07d2ebb6c78b5956d407fc09ad0" @@ -1587,6 +2024,11 @@ common-ancestor-path@^1.0.1: resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -1645,6 +2087,16 @@ conventional-commits-parser@^3.0.0: through2 "^4.0.0" trim-off-newlines "^1.0.0" +convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cosmiconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" @@ -1679,7 +2131,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1698,7 +2150,7 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1737,11 +2189,23 @@ deep-eql@^4.1.2: dependencies: type-detect "^4.0.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +default-require-extensions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.1.tgz#bfae00feeaeada68c2ae256c62540f60b80625bd" + integrity sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw== + dependencies: + strip-bom "^4.0.0" + defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -1772,6 +2236,162 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +dependency-tree@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-9.0.0.tgz#9288dd6daf35f6510c1ea30d9894b75369aa50a2" + integrity sha512-osYHZJ1fBSon3lNLw70amAXsQ+RGzXsPvk9HbBgTLbp/bQBmpH5mOmsUvqXU+YEWVU0ZLewsmzOET/8jWswjDQ== + dependencies: + commander "^2.20.3" + debug "^4.3.1" + filing-cabinet "^3.0.1" + precinct "^9.0.0" + typescript "^4.0.0" + +detective-amd@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.2.tgz#bf55eb5291c218b76d6224a3d07932ef13a9a357" + integrity sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ== + dependencies: + ast-module-types "^3.0.0" + escodegen "^2.0.0" + get-amd-module-type "^3.0.0" + node-source-walk "^4.2.0" + +detective-amd@^4.0.1, detective-amd@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-4.2.0.tgz#21c43465669f06cf894eef047a27e6e72ba6bc13" + integrity sha512-RbuEJHz78A8nW7CklkqTzd8lDCN42En53dgEIsya0DilpkwslamSZDasLg8dJyxbw46OxhSQeY+C2btdSkCvQQ== + dependencies: + ast-module-types "^4.0.0" + escodegen "^2.0.0" + get-amd-module-type "^4.1.0" + node-source-walk "^5.0.1" + +detective-cjs@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-3.1.3.tgz#50e107d67b37f459b0ec02966ceb7e20a73f268b" + integrity sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.0.0" + +detective-cjs@^4.0.0, detective-cjs@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-4.1.0.tgz#56b1558ca0910933c7fc47c740b957f0280ff302" + integrity sha512-QxzMwt5MfPLwS7mG30zvnmOvHLx5vyVvjsAV6gQOyuMoBR5G1DhS1eJZ4P10AlH+HSnk93mTcrg3l39+24XCtg== + dependencies: + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + +detective-es6@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-2.2.2.tgz#ee5f880981d9fecae9a694007029a2f6f26d8d28" + integrity sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw== + dependencies: + node-source-walk "^4.0.0" + +detective-es6@^3.0.0, detective-es6@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-3.0.1.tgz#53a15fbb2f298c4a106d9fe7427da8a57162dde6" + integrity sha512-evPeYIEdK1jK3Oji5p0hX4sPV/1vK+o4ihcWZkMQE6voypSW/cIBiynOLxQk5KOOQbdP8oOAsYqouMTYO5l1sw== + dependencies: + node-source-walk "^5.0.0" + +detective-less@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/detective-less/-/detective-less-1.0.2.tgz#a68af9ca5f69d74b7d0aa190218b211d83b4f7e3" + integrity sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA== + dependencies: + debug "^4.0.0" + gonzales-pe "^4.2.3" + node-source-walk "^4.0.0" + +detective-postcss@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-4.0.0.tgz#24e69b465e5fefe7a6afd05f7e894e34595dbf51" + integrity sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A== + dependencies: + debug "^4.1.1" + is-url "^1.2.4" + postcss "^8.1.7" + postcss-values-parser "^2.0.1" + +detective-postcss@^6.1.0, detective-postcss@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-6.1.3.tgz#51a2d4419327ad85d0af071c7054c79fafca7e73" + integrity sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw== + dependencies: + is-url "^1.2.4" + postcss "^8.4.23" + postcss-values-parser "^6.0.2" + +detective-sass@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-3.0.2.tgz#e0f35aac79a4d2f6409c284d95b8f7ecd5973afd" + integrity sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^4.0.0" + +detective-sass@^4.0.1, detective-sass@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-4.1.3.tgz#6cdcc27ae8a90d15704e0ba83683048f77f10b75" + integrity sha512-xGRbwGaGte57gvEqM8B9GDiURY3El/H49vA6g9wFkxq9zalmTlTAuqWu+BsH0iwonGPruLt55tZZDEZqPc6lag== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^5.0.1" + +detective-scss@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-2.0.2.tgz#7d2a642616d44bf677963484fa8754d9558b8235" + integrity sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^4.0.0" + +detective-scss@^3.0.0, detective-scss@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-3.1.1.tgz#b49f05cadfb0837b04e23baba292581b7c7f65e1" + integrity sha512-FWkfru1jZBhUeuBsOeGKXKAVDrzYFSQFK2o2tuG/nCCFQ0U/EcXC157MNAcR5mmj+mCeneZzlkBOFJTesDjrww== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^5.0.1" + +detective-stylus@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.3.tgz#20a702936c9fd7d4203fd7a903314b5dd43ac713" + integrity sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q== + +detective-stylus@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-2.0.1.tgz#d528dfa7ef3c4eb2fbc9a7249d54906ec4e05d09" + integrity sha512-/Tvs1pWLg8eYwwV6kZQY5IslGaYqc/GACxjcaGudiNtN5nKCH6o2WnJK3j0gA3huCnoQcbv8X7oz/c1lnvE3zQ== + +detective-stylus@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-3.0.0.tgz#c869795a7d6df7043ab6aee8b1a6f3dd66764232" + integrity sha512-1xYTzbrduExqMYmte7Qk99IRA3Aa6oV7PYzd+3yDcQXkmENvyGF/arripri6lxRDdNYEb4fZFuHtNRAXbz3iAA== + +detective-typescript@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-7.0.2.tgz#c6e00b4c28764741ef719662250e6b014a5f3c8e" + integrity sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA== + dependencies: + "@typescript-eslint/typescript-estree" "^4.33.0" + ast-module-types "^2.7.1" + node-source-walk "^4.2.0" + typescript "^3.9.10" + +detective-typescript@^9.0.0, detective-typescript@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-9.1.1.tgz#b99c0122cbb35b39de2c5f58447f1e93ac28c6d5" + integrity sha512-Uc1yVutTF0RRm1YJ3g//i1Cn2vx1kwHj15cnzQP6ff5koNzQ0idc1zAC73ryaWEulA0ElRXFTq6wOqe8vUQ3MA== + dependencies: + "@typescript-eslint/typescript-estree" "^5.55.0" + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + typescript "^4.9.5" + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -1832,6 +2452,11 @@ ejs@^3.1.8: dependencies: jake "^10.8.5" +electron-to-chromium@^1.4.530: + version "1.4.531" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.531.tgz#22966d894c4680726c17cf2908ee82ff5d26ac25" + integrity sha512-H6gi5E41Rn3/mhKlPaT1aIMg/71hTAqn0gYEllSuw9igNWtvQwu185jiCZoZD29n7Zukgh7GVZ3zGf0XvkhqjQ== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1849,7 +2474,7 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -enhanced-resolve@^5.12.0: +enhanced-resolve@^5.12.0, enhanced-resolve@^5.8.3: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -1944,6 +2569,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -1964,6 +2594,17 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-config-oclif-typescript@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-2.0.1.tgz#bdaca00f53ee27ff6930673082a00a03d6cf8dd1" @@ -2210,7 +2851,7 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -2254,10 +2895,10 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -fancy-test@^2.0.34: - version "2.0.35" - resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.35.tgz#18c0ccd767a7332bea186369a7e7a79a3b4582bb" - integrity sha512-XE0L7yAFOKY2jDnkBDDQ3JBD7xbBFwAFl1Z/5WNKBeVNlaEP08wuRTPE3xj2k+fnUp2CMUfD+6aiIS+4pcrjwg== +fancy-test@^3.0.0-beta.2: + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-3.0.0-beta.2.tgz#a6b6b4f3ae30200ce64ff8dd79c73fcefba0058f" + integrity sha512-bXffX78q50U/dm9E0RVesZUQ0IZPmRkMhKJdbipuVOm/WuzdoYXZnT/sr0uyzyGaxtjFcatKhZgPRDk49DlXTw== dependencies: "@types/chai" "*" "@types/lodash" "*" @@ -2266,12 +2907,13 @@ fancy-test@^2.0.34: lodash "^4.17.13" mock-stdin "^1.0.0" nock "^13.3.3" + sinon "^16.0.0" stdout-stderr "^0.1.9" -fancy-test@^3.0.0-beta.2: - version "3.0.0-beta.2" - resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-3.0.0-beta.2.tgz#a6b6b4f3ae30200ce64ff8dd79c73fcefba0058f" - integrity sha512-bXffX78q50U/dm9E0RVesZUQ0IZPmRkMhKJdbipuVOm/WuzdoYXZnT/sr0uyzyGaxtjFcatKhZgPRDk49DlXTw== +fancy-test@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-3.0.1.tgz#8b19ed4ccc8b0625475eabf36fd743e1d0168abb" + integrity sha512-Ke1IFOGEBxP2dNg0X7ZYPUSwKSRr5GNn3xM/2DpHkP86riF3MFDpesXJuD1TGm7gcfwBtYpuSzuw3m704bThVg== dependencies: "@types/chai" "*" "@types/lodash" "*" @@ -2346,6 +2988,25 @@ filelist@^1.0.1: dependencies: minimatch "^3.0.4" +filing-cabinet@^3.0.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-3.3.1.tgz#45d87bb273a0e0a7dd6ac6bac9111059186e2e9c" + integrity sha512-renEK4Hh6DUl9Vl22Y3cxBq1yh8oNvbAdXnhih0wVpmea+uyKjC9K4QeRjUaybIiIewdzfum+Fg15ZqJ/GyCaA== + dependencies: + app-module-path "^2.2.0" + commander "^2.20.3" + debug "^4.3.3" + enhanced-resolve "^5.8.3" + is-relative-path "^1.0.2" + module-definition "^3.3.1" + module-lookup-amd "^7.0.1" + resolve "^1.21.0" + resolve-dependency-path "^2.0.0" + sass-lookup "^3.0.0" + stylus-lookup "^3.0.1" + tsconfig-paths "^3.10.1" + typescript "^3.9.7" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -2353,6 +3014,15 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-cache-dir@^3.2.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -2361,7 +3031,7 @@ find-up@5.0.0, find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -2387,6 +3057,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== +flatten@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -2394,6 +3069,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -2402,6 +3085,11 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +fromentries@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" + integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + fs-extra@^9.0, fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -2484,7 +3172,28 @@ gauge@^5.0.0: strip-ansi "^6.0.1" wide-align "^1.1.5" -get-caller-file@^2.0.5: +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-amd-module-type@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0" + integrity sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.2.2" + +get-amd-module-type@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-4.1.0.tgz#af1396d02cd935cb6fafdc4a5282395db3422db6" + integrity sha512-0e/eK6vTGCnSfQ6eYs3wtH05KotJYIP7ZIZEueP/KlA+0dIAEs8bYFvOd/U56w1vfjhJqBagUxVMyy9Tr/cViQ== + dependencies: + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -2504,6 +3213,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2584,7 +3298,7 @@ glob@^7.0.0, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.4: +glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -2603,6 +3317,11 @@ global-dirs@^0.1.1: dependencies: ini "^1.3.4" +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^13.19.0: version "13.21.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.21.0.tgz#163aae12f34ef502f5153cfbdd3600f36c63c571" @@ -2617,7 +3336,7 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.0.1, globby@^11.1.0: +globby@^11.0.1, globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -2629,6 +3348,13 @@ globby@^11.0.1, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gonzales-pe@^4.2.3, gonzales-pe@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -2707,6 +3433,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasha@^5.0.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -2731,6 +3465,11 @@ hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: dependencies: lru-cache "^7.5.1" +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" @@ -2789,7 +3528,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -2834,6 +3573,11 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2842,12 +3586,12 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -3004,6 +3748,11 @@ is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -3026,6 +3775,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + is-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" @@ -3054,6 +3808,16 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-relative-path@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" + integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA== + is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" @@ -3099,11 +3863,26 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: dependencies: which-typed-array "^1.1.11" +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-url-superb@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" + integrity sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA== + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -3111,6 +3890,11 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -3133,6 +3917,66 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-processinfo@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" + integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.3" + istanbul-lib-coverage "^3.2.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^8.3.2" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jackspeak@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.0.tgz#aa228a94de830f31d4e4f0184427ce91c4ff1493" @@ -3179,7 +4023,7 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.14.1: +js-yaml@^3.13.1, js-yaml@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -3187,6 +4031,11 @@ js-yaml@^3.14.1: argparse "^1.0.7" esprima "^4.0.0" +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + jsesc@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" @@ -3239,6 +4088,11 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -3424,6 +4278,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -3439,7 +4298,7 @@ lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.1.0, log-symbols@^4.0.0: +log-symbols@4.1.0, log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -3454,6 +4313,13 @@ loupe@^2.3.1: dependencies: get-func-name "^2.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3471,6 +4337,48 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== +madge@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/madge/-/madge-6.1.0.tgz#9835bb53f2e00d184914c2b007b50736a964d297" + integrity sha512-irWhT5RpFOc6lkzGHKLihonCVgM0YtfNUh4IrFeW3EqHpnt/JHUG3z26j8PeJEktCGB4tmGOOOJi1Rl/ACWucQ== + dependencies: + chalk "^4.1.1" + commander "^7.2.0" + commondir "^1.0.1" + debug "^4.3.1" + dependency-tree "^9.0.0" + detective-amd "^4.0.1" + detective-cjs "^4.0.0" + detective-es6 "^3.0.0" + detective-less "^1.0.2" + detective-postcss "^6.1.0" + detective-sass "^4.0.1" + detective-scss "^3.0.0" + detective-stylus "^2.0.1" + detective-typescript "^9.0.0" + ora "^5.4.1" + pluralize "^8.0.0" + precinct "^8.1.0" + pretty-ms "^7.0.1" + rc "^1.2.7" + stream-to-array "^2.3.0" + ts-graphviz "^1.5.0" + walkdir "^0.4.1" + +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -3555,6 +4463,11 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -3597,7 +4510,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -3716,6 +4629,33 @@ mock-stdin@^1.0.0: resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" integrity sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q== +module-definition@^3.3.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.4.0.tgz#953a3861f65df5e43e80487df98bb35b70614c2b" + integrity sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA== + dependencies: + ast-module-types "^3.0.0" + node-source-walk "^4.0.0" + +module-definition@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-4.1.0.tgz#148ff9348e3401867229dcbe5947f4f6d5ccd3a2" + integrity sha512-rHXi/DpMcD2qcKbPCTklDbX9lBKJrUSl971TW5l6nMpqKCIlzJqmQ8cfEF5M923h2OOLHPDVlh5pJxNyV+AJlw== + dependencies: + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + +module-lookup-amd@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz#d67c1a93f2ff8e38b8774b99a638e9a4395774b2" + integrity sha512-w9mCNlj0S8qviuHzpakaLVc+/7q50jl9a/kmJ/n8bmXQZgDPkQHnPBb8MUOYh3WpAYkXuNc2c+khsozhIp/amQ== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + glob "^7.1.6" + requirejs "^2.3.5" + requirejs-config-file "^4.0.0" + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -3736,6 +4676,11 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3815,6 +4760,32 @@ node-gyp@^9.0.0, node-gyp@^9.4.0: tar "^6.1.2" which "^2.0.2" +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.3.0.tgz#8336b56cfed23ac5180fe98f1e3bb6b11fd5317c" + integrity sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA== + dependencies: + "@babel/parser" "^7.0.0" + +node-source-walk@^5.0.0, node-source-walk@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-5.0.2.tgz#0eb439ce378946ce531e07a6a0073d06288396dd" + integrity sha512-Y4jr/8SRS5hzEdZ7SGuvZGwfORvNsSsNRwDXx5WisiqzsVfeftDvRgfeqWNgZvWSJbgubTRVRYBzK6UO+ErqjA== + dependencies: + "@babel/parser" "^7.21.4" + nopt@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" @@ -4042,6 +5013,39 @@ npmlog@^7.0.1: gauge "^5.0.0" set-blocking "^2.0.0" +nyc@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" + integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" @@ -4102,6 +5106,13 @@ once@^1.3.0: dependencies: wrappy "1" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -4114,6 +5125,21 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -4142,6 +5168,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -4154,6 +5187,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + pacote@^15.0.0, pacote@^15.0.8, pacote@^15.2.0: version "15.2.0" resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" @@ -4212,6 +5255,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== + password-prompt@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.2.tgz#85b2f93896c5bd9e9f2d6ff0627fa5af3dc00923" @@ -4270,6 +5318,11 @@ pathval@^1.1.1: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -4280,6 +5333,13 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + platform@^1.3.3: version "1.3.6" resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" @@ -4305,6 +5365,70 @@ postcss-selector-parser@^6.0.10: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-values-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-values-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz#636edc5b86c953896f1bb0d7a7a6615df00fb76f" + integrity sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw== + dependencies: + color-name "^1.1.4" + is-url-superb "^4.0.0" + quote-unquote "^1.0.0" + +postcss@^8.1.7, postcss@^8.4.23: + version "8.4.30" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.30.tgz#0e0648d551a606ef2192a26da4cabafcc09c1aa7" + integrity sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +precinct@^8.1.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-8.3.1.tgz#94b99b623df144eed1ce40e0801c86078466f0dc" + integrity sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q== + dependencies: + commander "^2.20.3" + debug "^4.3.3" + detective-amd "^3.1.0" + detective-cjs "^3.1.1" + detective-es6 "^2.2.1" + detective-less "^1.0.2" + detective-postcss "^4.0.0" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^7.0.0" + module-definition "^3.3.1" + node-source-walk "^4.2.0" + +precinct@^9.0.0: + version "9.2.1" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-9.2.1.tgz#db0a67abff7b0a9a3b2b1ac33d170e8a5fcac7b2" + integrity sha512-uzKHaTyiVejWW7VJtHInb9KBUq9yl9ojxXGujhjhDmPon2wgZPBKQIKR+6csGqSlUeGXAA4MEFnU6DesxZib+A== + dependencies: + "@dependents/detective-less" "^3.0.1" + commander "^9.5.0" + detective-amd "^4.1.0" + detective-cjs "^4.1.0" + detective-es6 "^3.0.1" + detective-postcss "^6.1.1" + detective-sass "^4.1.1" + detective-scss "^3.0.1" + detective-stylus "^3.0.0" + detective-typescript "^9.1.1" + module-definition "^4.1.0" + node-source-walk "^5.0.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -4319,11 +5443,25 @@ pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-ms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + proc-log@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -4389,6 +5527,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quote-unquote@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" + integrity sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg== + rambda@^7.1.0: version "7.5.0" resolved "https://registry.yarnpkg.com/rambda/-/rambda-7.5.0.tgz#1865044c59bc0b16f63026c6e5a97e4b1bbe98fe" @@ -4401,6 +5544,16 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -4464,7 +5617,7 @@ readable-stream@3, readable-stream@^3.0.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.6.0: +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -4539,11 +5692,41 @@ regjsparser@^0.10.0: dependencies: jsesc "~0.5.0" +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== + dependencies: + es6-error "^4.0.1" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requirejs-config-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" + integrity sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw== + dependencies: + esprima "^4.0.0" + stringify-object "^3.2.1" + +requirejs@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" + integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== + +resolve-dependency-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" + integrity sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w== + resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -4574,7 +5757,7 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.22.4: +resolve@^1.21.0, resolve@^1.22.4: version "1.22.6" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== @@ -4583,6 +5766,14 @@ resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -4593,7 +5784,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -4636,6 +5827,13 @@ safe-regex-test@^1.0.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass-lookup@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac" + integrity sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg== + dependencies: + commander "^2.16.0" + "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -4648,16 +5846,16 @@ semver@7.3.5: dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + semver@^6.1.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - semver@^7.0.0, semver@^7.1.1, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -4743,7 +5941,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -4824,6 +6022,28 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -4877,6 +6097,13 @@ stdout-stderr@^0.1.9: debug "^4.1.1" strip-ansi "^6.0.0" +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== + dependencies: + any-promise "^1.1.0" + "string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -4929,6 +6156,15 @@ string_decoder@^1.1.1, string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" +stringify-object@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -4955,6 +6191,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -4967,6 +6208,19 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +stylus-lookup@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" + integrity sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + supports-color@8.1.1, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -5023,6 +6277,15 @@ tar@^6.1.11, tar@^6.1.13, tar@^6.1.15, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -5050,6 +6313,11 @@ tiny-relative-date@^1.3.0: resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07" integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A== +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -5077,6 +6345,11 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== +ts-graphviz@^1.5.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/ts-graphviz/-/ts-graphviz-1.8.1.tgz#5d95e58120be8b571847331516327d4840cc44f7" + integrity sha512-54/fe5iu0Jb6X0pmDmzsA2UHLfyHjUEUwfHtZcEOR0fZ6Myf+dFoO6eNsyL8CBDMJ9u7WWEewduVaiaXlvjSVw== + ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -5096,7 +6369,7 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.14.2: +tsconfig-paths@^3.10.1, tsconfig-paths@^3.14.2: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== @@ -5119,6 +6392,11 @@ tsd@^0.29.0: path-exists "^4.0.0" read-pkg-up "^7.0.0" +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" @@ -5129,6 +6407,13 @@ tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + tuf-js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" @@ -5182,7 +6467,7 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -5226,6 +6511,23 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typescript@^3.9.10, typescript@^3.9.7: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + +typescript@^4.0.0, typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + typescript@^5: version "5.2.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" @@ -5241,6 +6543,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== + unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" @@ -5260,6 +6567,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -5272,6 +6587,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -5297,7 +6617,12 @@ walk-up-path@^3.0.1: resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-3.0.1.tgz#c8d78d5375b4966c717eb17ada73dbd41490e886" integrity sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA== -wcwidth@^1.0.0: +walkdir@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" + integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== + +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== @@ -5315,6 +6640,11 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + which-typed-array@^1.1.11: version "1.1.11" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" @@ -5380,6 +6710,15 @@ workerpool@6.2.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -5394,6 +6733,16 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + write-file-atomic@^5.0.0, write-file-atomic@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" @@ -5402,11 +6751,21 @@ write-file-atomic@^5.0.0, write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -5422,6 +6781,14 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" @@ -5450,6 +6817,23 @@ yargs@16.2.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^15.0.2: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yarn@^1.22.18: version "1.22.19" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" From b38f222d9cc581eb75ff012689d4b125fc9f9b6e Mon Sep 17 00:00:00 2001 From: svc-cli-bot Date: Thu, 28 Sep 2023 18:19:09 +0000 Subject: [PATCH 36/55] chore(release): 3.0.0-beta.18 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d9cb3af3a..43c7814d8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@oclif/core", "description": "base library for oclif CLIs", - "version": "3.0.0-beta.17", + "version": "3.0.0-beta.18", "author": "Salesforce", "bugs": "https://github.com/oclif/core/issues", "dependencies": { From 2a8770b18eef488834ba5d56b71b36e721bbffac Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 29 Sep 2023 13:03:16 -0700 Subject: [PATCH 37/55] chore: use prettier and lint-staged (#798) * chore: update prettier config * chore: prettier and lint-staged * chore: reformat everything * chore: deal with reformat aftermath * chore: update configs and deps * perf: move ansi-escapes require to top level * chore: automerge.yml * chore: unpin eslint-plugin-prettier * chore: remove prettier plugin * chore: update lint-staged config --- .commitlintrc.json | 3 + .editorconfig | 11 - .eslintrc.json | 3 +- .github/workflows/automerge.yml | 7 +- .husky/pre-commit | 4 + .lintstagedrc.json | 5 + commitlint.config.js | 1 - package.json | 22 +- src/args.ts | 8 +- src/cli-ux/action/base.ts | 32 +- src/cli-ux/action/spinner.ts | 15 +- src/cli-ux/action/spinners.ts | 2 +- src/cli-ux/action/types.ts | 4 +- src/cli-ux/config.ts | 18 +- src/cli-ux/exit.ts | 2 +- src/cli-ux/flush.ts | 4 +- src/cli-ux/global.d.ts | 2 +- src/cli-ux/index.ts | 3 +- src/cli-ux/list.ts | 4 +- src/cli-ux/prompt.ts | 91 +- src/cli-ux/styled/index.ts | 1 - src/cli-ux/styled/object.ts | 9 +- src/cli-ux/styled/table.ts | 175 ++-- src/cli-ux/styled/tree.ts | 4 +- src/cli-ux/wait.ts | 7 +- src/command.ts | 107 +-- src/config/config.ts | 152 +-- src/config/index.ts | 1 - src/config/plugin-loader.ts | 123 +-- src/config/plugin.ts | 111 +-- src/config/ts-node.ts | 30 +- src/config/util.ts | 13 +- src/errors/errors/cli.ts | 6 +- src/errors/errors/pretty-print.ts | 10 +- src/errors/handle.ts | 7 +- src/errors/logger.ts | 11 +- src/execute.ts | 24 +- src/flags.ts | 124 ++- src/help/command.ts | 186 ++-- src/help/docopts.ts | 37 +- src/help/formatter.ts | 37 +- src/help/index.ts | 116 +-- src/help/root.ts | 12 +- src/help/util.ts | 2 +- src/interfaces/config.ts | 119 +-- src/interfaces/errors.ts | 14 +- src/interfaces/flags.ts | 2 +- src/interfaces/help.ts | 16 +- src/interfaces/hooks.ts | 96 +- src/interfaces/index.ts | 10 +- src/interfaces/manifest.ts | 4 +- src/interfaces/parser.ts | 359 ++++---- src/interfaces/pjson.ts | 149 +-- src/interfaces/plugin.ts | 72 +- src/interfaces/s3-manifest.ts | 22 +- src/interfaces/topic.ts | 6 +- src/interfaces/ts-config.ts | 30 +- src/module-loader.ts | 67 +- src/parser/errors.ts | 48 +- src/parser/help.ts | 3 +- src/parser/index.ts | 3 +- src/parser/parse.ts | 304 +++--- src/parser/validate.ts | 136 +-- src/performance.ts | 106 ++- src/settings.ts | 12 +- src/util/aggregate-flags.ts | 4 +- src/util/cache-command.ts | 109 ++- src/util/index.ts | 28 +- test/cli-ux/export.test.ts | 1 - test/cli-ux/fancy.ts | 22 +- test/cli-ux/index.test.ts | 15 +- test/cli-ux/prompt.test.ts | 105 ++- test/cli-ux/styled/object.test.ts | 4 +- test/cli-ux/styled/progress.test.ts | 11 +- test/cli-ux/styled/table.e2e.ts | 22 +- test/cli-ux/styled/table.test.ts | 176 ++-- test/cli-ux/styled/tree.test.ts | 4 +- test/command/command.test.ts | 575 ++++++------ .../command/fixtures/typescript/tsconfig.json | 8 +- .../test-help-in-lib/lib/test-help-plugin.js | 30 +- .../test-help-in-src/src/test-help-plugin.ts | 2 +- test/command/main-esm.test.ts | 68 +- test/command/main.test.ts | 7 +- test/config/config.flexible.test.ts | 180 ++-- test/config/config.test.ts | 231 ++--- test/config/esm.test.ts | 22 +- test/config/fixtures/help/package.json | 11 +- .../src/commands/foo/bar/test-result.js | 1 - .../fixtures/typescript/src/hooks/postrun.ts | 1 - test/config/fixtures/typescript/tsconfig.json | 8 +- test/config/help.config.test.ts | 36 +- test/config/mixed-cjs-esm.test.ts | 26 +- test/config/mixed-esm-cjs.test.ts | 26 +- test/config/test.ts | 3 +- test/config/typescript.test.ts | 22 +- test/config/util.test.ts | 41 +- test/errors/error.test.ts | 158 ++-- test/errors/handle.test.ts | 176 ++-- test/errors/pretty-print.test.ts | 34 +- test/errors/warn.test.ts | 26 +- test/help/_test-help-class.ts | 2 +- test/help/docopts.test.ts | 384 ++++---- test/help/fixtures/fixtures.ts | 17 + test/help/format-command-with-options.test.ts | 470 ++++++---- test/help/format-command.test.ts | 743 ++++++++------- test/help/format-commands.test.ts | 50 +- test/help/format-root.test.ts | 113 +-- test/help/format-topic.test.ts | 69 +- test/help/format-topics.test.ts | 52 +- test/help/help-test-utils.ts | 23 +- test/help/show-customized-help.test.ts | 200 ++-- test/help/show-help.test.ts | 460 +++++----- test/help/util.test.ts | 222 ++--- test/integration/esm-cjs.ts | 79 +- test/integration/plugins.e2e.ts | 15 +- test/integration/sf.e2e.ts | 12 +- test/integration/util.ts | 40 +- test/interfaces/args.test-types.ts | 7 +- test/interfaces/flags.test-types.ts | 47 +- test/module-loader/module-loader.test.ts | 36 +- test/parser/help.test.ts | 2 +- test/parser/parse.test.ts | 165 ++-- test/parser/validate.test.ts | 74 +- test/perf/parser.perf.ts | 137 +-- test/tsconfig.json | 5 +- test/util/cache-command.test.ts | 8 +- test/util/cache-default-value.test.ts | 13 +- test/util/index.test.ts | 24 +- yarn.lock | 862 ++++++++++++------ 129 files changed, 5301 insertions(+), 4347 deletions(-) create mode 100644 .commitlintrc.json delete mode 100644 .editorconfig create mode 100755 .husky/pre-commit create mode 100644 .lintstagedrc.json delete mode 100644 commitlint.config.js diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 000000000..c30e5a970 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["@commitlint/config-conventional"] +} diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index beffa3084..000000000 --- a/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/.eslintrc.json b/.eslintrc.json index 2887d4e87..5971db05c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,8 @@ { "extends": [ "oclif", - "oclif-typescript" + "oclif-typescript", + "prettier" ], "rules": { "sort-imports": "error", diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 796eafae2..b61a8843d 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -2,9 +2,10 @@ name: automerge on: workflow_dispatch: schedule: - - cron: '17 2,5,8,11 * * *' + - cron: "17 2,5,8,11 * * *" jobs: automerge: - uses: oclif/github-workflows/.github/workflows/automerge.yml@main - secrets: inherit + uses: salesforcecli/github-workflows/.github/workflows/automerge.yml@main + secrets: + SVC_CLI_BOT_GITHUB_TOKEN: ${{ secrets.SVC_CLI_BOT_GITHUB_TOKEN }} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..4abc587ad --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged --concurrent false diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 000000000..c31470780 --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,5 @@ +{ + "*.json": ["prettier --write"], + "*.md": ["prettier --write"], + "+(src|test)/**/*.+(ts|js)": ["eslint --fix", "prettier --write"] +} diff --git a/commitlint.config.js b/commitlint.config.js deleted file mode 100644 index 28fe5c5bf..000000000 --- a/commitlint.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {extends: ['@commitlint/config-conventional']} diff --git a/package.json b/package.json index 43c7814d8..973fb79ba 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,11 @@ "wrap-ansi": "^7.0.0" }, "devDependencies": { - "@commitlint/config-conventional": "^12.1.4", + "@commitlint/config-conventional": "^17.7.0", "@oclif/plugin-help": "^5.2.8", "@oclif/plugin-plugins": "^3.3.0", - "@oclif/prettier-config": "^0.1.1", - "@oclif/test": "^3.0.0-beta.1", + "@oclif/prettier-config": "^0.2.1", + "@oclif/test": "^3.0.1", "@types/ansi-styles": "^3.2.1", "@types/benchmark": "^2.1.2", "@types/chai": "^4.3.4", @@ -46,8 +46,7 @@ "@types/ejs": "^3.1.2", "@types/indent-string": "^4.0.1", "@types/js-yaml": "^3.12.7", - "@types/mocha": "^8.2.3", - "@types/nock": "^11.1.0", + "@types/mocha": "^10.0.2", "@types/node": "^18", "@types/node-notifier": "^8.0.2", "@types/slice-ansi": "^4.0.0", @@ -58,22 +57,24 @@ "benchmark": "^2.1.4", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", - "commitlint": "^12.1.4", + "commitlint": "^17.7.2", "cross-env": "^7.0.3", "eslint": "^8.49.0", "eslint-config-oclif": "^5.0.0", "eslint-config-oclif-typescript": "^2.0.1", - "fancy-test": "^3.0.0-beta.2", + "eslint-config-prettier": "^9.0.0", + "fancy-test": "^3.0.1", "globby": "^11.1.0", - "husky": "6", + "husky": "^8", + "lint-staged": "^14.0.1", "madge": "^6.1.0", "mocha": "^10.2.0", - "nock": "^13.3.0", "nyc": "^15.1.0", + "prettier": "^3.0.3", "shx": "^0.3.4", "sinon": "^11.1.2", - "tsd": "^0.29.0", "ts-node": "^10.9.1", + "tsd": "^0.29.0", "tslib": "^2.5.0", "typescript": "^5" }, @@ -107,6 +108,7 @@ "build": "shx rm -rf lib && tsc", "commitlint": "commitlint", "compile": "tsc", + "format": "prettier --write \"+(src|test)/**/*.+(ts|js|json)\"", "lint": "eslint . --ext .ts", "posttest": "yarn lint && yarn test:circular-deps", "prepack": "yarn run build", diff --git a/src/args.ts b/src/args.ts index 861bf8954..22f811c13 100644 --- a/src/args.ts +++ b/src/args.ts @@ -1,4 +1,3 @@ - import {Arg, ArgDefinition} from './interfaces/parser' import {dirExists, fileExists, isNotFalsy} from './util' import {Command} from './command' @@ -33,13 +32,12 @@ export function custom>(defaults: Partial({ - parse: async b => Boolean(b) && isNotFalsy(b), + parse: async (b) => Boolean(b) && isNotFalsy(b), }) -export const integer = custom({ +export const integer = custom({ async parse(input, _, opts) { - if (!/^-?\d+$/.test(input)) - throw new Error(`Expected an integer but received: ${input}`) + if (!/^-?\d+$/.test(input)) throw new Error(`Expected an integer but received: ${input}`) const num = Number.parseInt(input, 10) if (opts.min !== undefined && num < opts.min) throw new Error(`Expected an integer greater than or equal to ${opts.min} but received: ${input}`) diff --git a/src/cli-ux/action/base.ts b/src/cli-ux/action/base.ts index a29bfc809..38aa2cdb9 100644 --- a/src/cli-ux/action/base.ts +++ b/src/cli-ux/action/base.ts @@ -4,9 +4,9 @@ import {castArray} from '../../util' import {inspect} from 'node:util' export interface ITask { - action: string; - status: string | undefined; - active: boolean; + action: string + status: string | undefined + active: boolean } export type ActionType = 'spinner' | 'simple' | 'debug' @@ -45,8 +45,8 @@ export class ActionBase { this._stdout(false) } - private get globals(): { action: { task?: ITask }; output: string | undefined } { - (global as any).ux = (global as any).ux || {} + private get globals(): {action: {task?: ITask}; output: string | undefined} { + ;(global as any).ux = (global as any).ux || {} const globals = (global as any).ux globals.action = globals.action || {} return globals @@ -201,19 +201,19 @@ export class ActionBase { // write to the real stdout/stderr protected _write(std: 'stdout' | 'stderr', s: string | string[]): void { switch (std) { - case 'stdout': { - this.stdmockOrigs.stdout.apply(stdout, castArray(s) as [string]) - break - } + case 'stdout': { + this.stdmockOrigs.stdout.apply(stdout, castArray(s) as [string]) + break + } - case 'stderr': { - this.stdmockOrigs.stderr.apply(stderr, castArray(s) as [string]) - break - } + case 'stderr': { + this.stdmockOrigs.stderr.apply(stderr, castArray(s) as [string]) + break + } - default: { - throw new Error(`invalid std: ${std}`) - } + default: { + throw new Error(`invalid std: ${std}`) + } } } } diff --git a/src/cli-ux/action/spinner.ts b/src/cli-ux/action/spinner.ts index 7ac05c9ef..b85caf7f3 100644 --- a/src/cli-ux/action/spinner.ts +++ b/src/cli-ux/action/spinner.ts @@ -7,6 +7,8 @@ import {errtermwidth} from '../../screen' import spinners from './spinners' import stripAnsi from 'strip-ansi' +const ansiEscapes = require('ansi-escapes') + function color(s: string): string { if (!supportsColor) return s const has256 = supportsColor.stdout ? supportsColor.stdout.has256 : (process.env.TERM || '').includes('256') @@ -34,10 +36,10 @@ export default class SpinnerAction extends ActionBase { this._reset() if (this.spinner) clearInterval(this.spinner) this._render() - this.spinner = setInterval(icon => - this._render.bind(this)(icon), - process.platform === 'win32' ? 500 : 100, - 'spinner', + this.spinner = setInterval( + (icon) => this._render.bind(this)(icon), + process.platform === 'win32' ? 500 : 100, + 'spinner', ) const interval = this.spinner interval.unref() @@ -82,15 +84,12 @@ export default class SpinnerAction extends ActionBase { private _reset() { if (!this.output) return - const ansiEscapes = require('ansi-escapes') const lines = this._lines(this.output) this._write(this.std, ansiEscapes.cursorLeft + ansiEscapes.cursorUp(lines) + ansiEscapes.eraseDown) this.output = undefined } private _lines(s: string): number { - return (stripAnsi(s).split('\n') as any[]) - .map(l => Math.ceil(l.length / errtermwidth)) - .reduce((c, i) => c + i, 0) + return (stripAnsi(s).split('\n') as any[]).map((l) => Math.ceil(l.length / errtermwidth)).reduce((c, i) => c + i, 0) } } diff --git a/src/cli-ux/action/spinners.ts b/src/cli-ux/action/spinners.ts index d38cc75f7..e7ba12bad 100644 --- a/src/cli-ux/action/spinners.ts +++ b/src/cli-ux/action/spinners.ts @@ -157,7 +157,7 @@ export default { }, flip: { interval: 70, - frames: ['_', '_', '_', '-', '`', '`', '\'', 'ยด', '-', '_', '_', '_'], + frames: ['_', '_', '_', '-', '`', '`', "'", 'ยด', '-', '_', '_', '_'], }, hamburger: { interval: 100, diff --git a/src/cli-ux/action/types.ts b/src/cli-ux/action/types.ts index 0e5eb9179..7fe562ed1 100644 --- a/src/cli-ux/action/types.ts +++ b/src/cli-ux/action/types.ts @@ -1,6 +1,6 @@ import spinners from './spinners' export type Options = { - stdout?: boolean; - style?: keyof typeof spinners; + stdout?: boolean + style?: keyof typeof spinners } diff --git a/src/cli-ux/config.ts b/src/cli-ux/config.ts index 5718f9775..bbf2e5483 100644 --- a/src/cli-ux/config.ts +++ b/src/cli-ux/config.ts @@ -7,20 +7,20 @@ import spinner from './action/spinner' export type Levels = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' export interface ConfigMessage { - type: 'config'; - prop: string; - value: any; + type: 'config' + prop: string + value: any } const g: any = global const globals = g.ux || (g.ux = {}) -const actionType = ( - Boolean(process.stderr.isTTY) - && !process.env.CI - && !['dumb', 'emacs-color'].includes(process.env.TERM!) - && 'spinner' -) || 'simple' +const actionType = + (Boolean(process.stderr.isTTY) && + !process.env.CI && + !['dumb', 'emacs-color'].includes(process.env.TERM!) && + 'spinner') || + 'simple' const Action = actionType === 'spinner' ? spinner : simple diff --git a/src/cli-ux/exit.ts b/src/cli-ux/exit.ts index a7ce32f27..8d154caef 100644 --- a/src/cli-ux/exit.ts +++ b/src/cli-ux/exit.ts @@ -1,6 +1,6 @@ export class ExitError extends Error { public ux: { - exit: number; + exit: number } public code: 'EEXIT' diff --git a/src/cli-ux/flush.ts b/src/cli-ux/flush.ts index 33abd784f..63713eea1 100644 --- a/src/cli-ux/flush.ts +++ b/src/cli-ux/flush.ts @@ -3,7 +3,7 @@ import {stdout} from './stream' function timeout(p: Promise, ms: number) { function wait(ms: number, unref = false) { - return new Promise(resolve => { + return new Promise((resolve) => { const t: any = setTimeout(() => resolve(null), ms) if (unref) t.unref() }) @@ -13,7 +13,7 @@ function timeout(p: Promise, ms: number) { } async function _flush() { - const p = new Promise(resolve => { + const p = new Promise((resolve) => { stdout.once('drain', () => resolve(null)) }) const flushed = stdout.write('') diff --git a/src/cli-ux/global.d.ts b/src/cli-ux/global.d.ts index 08e1e9e13..503b2265d 100644 --- a/src/cli-ux/global.d.ts +++ b/src/cli-ux/global.d.ts @@ -1,5 +1,5 @@ declare namespace NodeJS { interface Global { - ux: any; + ux: any } } diff --git a/src/cli-ux/index.ts b/src/cli-ux/index.ts index 1b7811f7a..96257a1e2 100644 --- a/src/cli-ux/index.ts +++ b/src/cli-ux/index.ts @@ -1,4 +1,3 @@ - import * as Errors from '../errors' import * as styled from './styled' import * as uxPrompt from './prompt' @@ -169,7 +168,7 @@ const uxProcessExitHandler = async () => { // to avoid MaxListenersExceededWarning // only attach named listener once -const uxListener = process.listeners('exit').find(fn => fn.name === uxProcessExitHandler.name) +const uxListener = process.listeners('exit').find((fn) => fn.name === uxProcessExitHandler.name) if (!uxListener) { process.once('exit', uxProcessExitHandler) } diff --git a/src/cli-ux/list.ts b/src/cli-ux/list.ts index 46773cb7d..af08c1cc1 100644 --- a/src/cli-ux/list.ts +++ b/src/cli-ux/list.ts @@ -15,8 +15,8 @@ export function renderList(items: IListItem[]): string { return '' } - const maxLength = maxBy(items, item => item[0].length)?.[0].length ?? 0 - const lines = items.map(i => { + const maxLength = maxBy(items, (item) => item[0].length)?.[0].length ?? 0 + const lines = items.map((i) => { let left = i[0] let right = i[1] if (!right) { diff --git a/src/cli-ux/prompt.ts b/src/cli-ux/prompt.ts index 9af137f84..a218e1231 100644 --- a/src/cli-ux/prompt.ts +++ b/src/cli-ux/prompt.ts @@ -4,24 +4,24 @@ import {config} from './config' import {stderr} from './stream' export interface IPromptOptions { - prompt?: string; - type?: 'normal' | 'mask' | 'hide' | 'single'; - timeout?: number; + prompt?: string + type?: 'normal' | 'mask' | 'hide' | 'single' + timeout?: number /** * Requires user input if true, otherwise allows empty input */ - required?: boolean; - default?: string; + required?: boolean + default?: string } interface IPromptConfig { - name: string; - prompt: string; - type: 'normal' | 'mask' | 'hide' | 'single'; - isTTY: boolean; - required: boolean; - default?: string; - timeout?: number; + name: string + prompt: string + type: 'normal' | 'mask' | 'hide' | 'single' + isTTY: boolean + required: boolean + default?: string + timeout?: number } function normal(options: IPromptConfig, retries = 100): Promise { @@ -39,14 +39,14 @@ function normal(options: IPromptConfig, retries = 100): Promise { process.stdin.setEncoding('utf8') stderr.write(options.prompt) process.stdin.resume() - process.stdin.once('data', b => { + process.stdin.once('data', (b) => { if (timer) clearTimeout(timer) process.stdin.pause() const data: string = (typeof b === 'string' ? b : b.toString()).trim() if (!options.default && options.required && data === '') { resolve(normal(options, retries - 1)) } else { - resolve(data || options.default as string) + resolve(data || (options.default as string)) } }) }) @@ -76,8 +76,15 @@ async function single(options: IPromptConfig): Promise { function replacePrompt(prompt: string) { const ansiEscapes = require('ansi-escapes') - stderr.write(ansiEscapes.cursorHide + ansiEscapes.cursorUp(1) + ansiEscapes.cursorLeft + prompt - + ansiEscapes.cursorDown(1) + ansiEscapes.cursorLeft + ansiEscapes.cursorShow) + stderr.write( + ansiEscapes.cursorHide + + ansiEscapes.cursorUp(1) + + ansiEscapes.cursorLeft + + prompt + + ansiEscapes.cursorDown(1) + + ansiEscapes.cursorLeft + + ansiEscapes.cursorShow, + ) } async function _prompt(name: string, inputOptions: Partial = {}): Promise { @@ -94,36 +101,36 @@ async function _prompt(name: string, inputOptions: Partial = {}) const passwordPrompt = require('password-prompt') switch (options.type) { - case 'normal': { - return normal(options) - } + case 'normal': { + return normal(options) + } - case 'single': { - return single(options) - } + case 'single': { + return single(options) + } - case 'mask': { - return passwordPrompt(options.prompt, { - method: options.type, - required: options.required, - default: options.default, - }).then((value: string) => { - replacePrompt(getPrompt(name, 'hide', inputOptions.default)) - return value - }) - } + case 'mask': { + return passwordPrompt(options.prompt, { + method: options.type, + required: options.required, + default: options.default, + }).then((value: string) => { + replacePrompt(getPrompt(name, 'hide', inputOptions.default)) + return value + }) + } - case 'hide': { - return passwordPrompt(options.prompt, { - method: options.type, - required: options.required, - default: options.default, - }) - } + case 'hide': { + return passwordPrompt(options.prompt, { + method: options.type, + required: options.required, + default: options.default, + }) + } - default: { - throw new Error(`unexpected type ${options.type}`) - } + default: { + throw new Error(`unexpected type ${options.type}`) + } } } diff --git a/src/cli-ux/styled/index.ts b/src/cli-ux/styled/index.ts index 93ce7faa2..1340c6e7f 100644 --- a/src/cli-ux/styled/index.ts +++ b/src/cli-ux/styled/index.ts @@ -1,4 +1,3 @@ - export * as Table from './table' export {default as progress} from './progress' export {default as styledJSON} from './json' diff --git a/src/cli-ux/styled/object.ts b/src/cli-ux/styled/object.ts index 96345c016..272f66bcc 100644 --- a/src/cli-ux/styled/object.ts +++ b/src/cli-ux/styled/object.ts @@ -3,20 +3,21 @@ import {inspect} from 'node:util' export default function styledObject(obj: any, keys?: string[]): string { const output: string[] = [] - const keyLengths = Object.keys(obj).map(key => key.toString().length) + const keyLengths = Object.keys(obj).map((key) => key.toString().length) const maxKeyLength = Math.max(...keyLengths) + 2 function pp(obj: any) { if (typeof obj === 'string' || typeof obj === 'number') return obj if (typeof obj === 'object') { return Object.keys(obj) - .map(k => k + ': ' + inspect(obj[k])) - .join(', ') + .map((k) => k + ': ' + inspect(obj[k])) + .join(', ') } return inspect(obj) } - const logKeyValue = (key: string, value: any): string => `${chalk.blue(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value) + const logKeyValue = (key: string, value: any): string => + `${chalk.blue(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value) for (const key of keys || Object.keys(obj).sort()) { const value = obj[key] diff --git a/src/cli-ux/styled/table.ts b/src/cli-ux/styled/table.ts index dc4f7f7d6..e22ac2f97 100644 --- a/src/cli-ux/styled/table.ts +++ b/src/cli-ux/styled/table.ts @@ -11,11 +11,15 @@ import {stdtermwidth} from '../../screen' import sw from 'string-width' class Table> { - options: table.Options & { printLine(s: any): any } + options: table.Options & {printLine(s: any): any} - columns: (table.Column & { key: string; width?: number; maxWidth?: number })[] + columns: (table.Column & {key: string; width?: number; maxWidth?: number})[] - constructor(private data: T[], columns: table.Columns, options: table.Options = {}) { + constructor( + private data: T[], + columns: table.Columns, + options: table.Options = {}, + ) { // assign columns this.columns = Object.keys(columns).map((key: string) => { const col = columns[key] @@ -52,7 +56,7 @@ class Table> { display() { // build table rows from input array data - let rows = this.data.map(d => { + let rows = this.data.map((d) => { const row: any = {} for (const col of this.columns) { let val = col.get(d) @@ -81,9 +85,9 @@ class Table> { // sort rows if (this.options.sort) { const sorters = this.options.sort!.split(',') - const sortHeaders = sorters.map(k => k[0] === '-' ? k.slice(1) : k) - const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map(c => ((v: any) => v[c.key])) - const sortKeysOrder = sorters.map(k => k[0] === '-' ? 'desc' : 'asc') + const sortHeaders = sorters.map((k) => (k[0] === '-' ? k.slice(1) : k)) + const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map((c) => (v: any) => v[c.key]) + const sortKeysOrder = sorters.map((k) => (k[0] === '-' ? 'desc' : 'asc')) rows = orderBy(rows, sortKeys, sortKeysOrder) } @@ -93,43 +97,47 @@ class Table> { this.columns = this.filterColumnsFromHeaders(filters) } else if (!this.options.extended) { // show extented columns/properties - this.columns = this.columns.filter(c => !c.extended) + this.columns = this.columns.filter((c) => !c.extended) } this.data = rows switch (this.options.output) { - case 'csv': { - this.outputCSV() - break - } + case 'csv': { + this.outputCSV() + break + } - case 'json': { - this.outputJSON() - break - } + case 'json': { + this.outputJSON() + break + } - case 'yaml': { - this.outputYAML() - break - } + case 'yaml': { + this.outputYAML() + break + } - default: { - this.outputTable() - } + default: { + this.outputTable() + } } } - private findColumnFromHeader(header: string): (table.Column & { key: string; width?: number; maxWidth?: number }) | undefined { - return this.columns.find(c => c.header.toLowerCase() === header.toLowerCase()) + private findColumnFromHeader( + header: string, + ): (table.Column & {key: string; width?: number; maxWidth?: number}) | undefined { + return this.columns.find((c) => c.header.toLowerCase() === header.toLowerCase()) } - private filterColumnsFromHeaders(filters: string[]): (table.Column & { key: string; width?: number; maxWidth?: number })[] { + private filterColumnsFromHeaders( + filters: string[], + ): (table.Column & {key: string; width?: number; maxWidth?: number})[] { // unique - filters = [...(new Set(filters))] + filters = [...new Set(filters)] const cols: (table.Column & {key: string; width?: number; maxWidth?: number})[] = [] for (const f of filters) { - const c = this.columns.find(c => c.header.toLowerCase() === f.toLowerCase()) + const c = this.columns.find((c) => c.header.toLowerCase() === f.toLowerCase()) if (c) cols.push(c) } @@ -137,17 +145,16 @@ class Table> { } private getCSVRow(d: any): string[] { - const values = this.columns.map(col => d[col.key] || '') - const lineToBeEscaped = values.find((e: string) => e.includes('"') || e.includes('\n') || e.includes('\r\n') || e.includes('\r') || e.includes(',')) - return values.map(e => lineToBeEscaped ? `"${e.replace('"', '""')}"` : e) + const values = this.columns.map((col) => d[col.key] || '') + const lineToBeEscaped = values.find( + (e: string) => e.includes('"') || e.includes('\n') || e.includes('\r\n') || e.includes('\r') || e.includes(','), + ) + return values.map((e) => (lineToBeEscaped ? `"${e.replace('"', '""')}"` : e)) } private resolveColumnsToObjectArray() { const {data, columns} = this - return data.map((d: any) => - - Object.fromEntries(columns.map(col => [col.key, d[col.key] ?? ''])), - ) + return data.map((d: any) => Object.fromEntries(columns.map((col) => [col.key, d[col.key] ?? '']))) } private outputJSON() { @@ -162,7 +169,7 @@ class Table> { const {data, columns, options} = this if (!options['no-header']) { - options.printLine(columns.map(c => c.header).join(',')) + options.printLine(columns.map((c) => c.header).join(',')) } for (const d of data) { @@ -176,7 +183,7 @@ class Table> { // column truncation // // find max width for each column - const columns = this.columns.map(c => { + const columns = this.columns.map((c) => { const maxWidth = Math.max(sw('.'.padEnd(c.minWidth! - 1)), sw(c.header), getWidestColumnWith(data, c.key)) + 1 return { ...c, @@ -193,7 +200,7 @@ class Table> { if (options['no-truncate'] || (!stdout.isTTY && !process.env.CLI_UX_SKIP_TTY_CHECK)) return // don't shorten if there is enough screen width - const dataMaxWidth = sumBy(columns, c => c.width!) + const dataMaxWidth = sumBy(columns, (c) => c.width!) const overWidth = dataMaxWidth - maxWidth if (overWidth <= 0) return @@ -205,15 +212,17 @@ class Table> { // if sum(minWidth's) is greater than term width // nothing can be done so // display all as minWidth - const dataMinWidth = sumBy(columns, c => c.minWidth!) + const dataMinWidth = sumBy(columns, (c) => c.minWidth!) if (dataMinWidth >= maxWidth) return // some wiggle room left, add it back to "needy" columns let wiggleRoom = maxWidth - dataMinWidth - const needyCols = columns.map(c => ({key: c.key, needs: c.maxWidth! - c.width!})).sort((a, b) => a.needs - b.needs) + const needyCols = columns + .map((c) => ({key: c.key, needs: c.maxWidth! - c.width!})) + .sort((a, b) => a.needs - b.needs) for (const {key, needs} of needyCols) { if (!needs) continue - const col = columns.find(c => key === c.key) + const col = columns.find((c) => key === c.key) if (!col) continue if (wiggleRoom > needs) { col.width = col.width! + needs @@ -231,7 +240,12 @@ class Table> { if (options.title) { options.printLine(options.title) // print title divider - options.printLine(''.padEnd(columns.reduce((sum, col) => sum + col.width!, 1), '=')) + options.printLine( + ''.padEnd( + columns.reduce((sum, col) => sum + col.width!, 1), + '=', + ), + ) options.rowStart = '| ' } @@ -280,9 +294,9 @@ class Table> { let d = (row as any)[col.key] d = d.split('\n')[i] || '' const visualWidth = sw(d) - const colorWidth = (d.length - visualWidth) + const colorWidth = d.length - visualWidth let cell = d.padEnd(width + colorWidth) - if ((cell.length - colorWidth) > width || visualWidth === width) { + if (cell.length - colorWidth > width || visualWidth === width) { // truncate the cell, preserving ANSI escape sequences, and keeping // into account the width of fullwidth unicode characters cell = sliceAnsi(cell, 0, width - 2) + 'โ€ฆ ' @@ -300,23 +314,27 @@ class Table> { } } -export function table>(data: T[], columns: table.Columns, options: table.Options = {}): void { +export function table>( + data: T[], + columns: table.Columns, + options: table.Options = {}, +): void { new Table(data, columns, options).display() } export namespace table { export const Flags: { - columns: Interfaces.OptionFlag; - sort: Interfaces.OptionFlag; - filter: Interfaces.OptionFlag; - csv: Interfaces.BooleanFlag; - output: Interfaces.OptionFlag; - extended: Interfaces.BooleanFlag; - 'no-truncate': Interfaces.BooleanFlag; - 'no-header': Interfaces.BooleanFlag; + columns: Interfaces.OptionFlag + sort: Interfaces.OptionFlag + filter: Interfaces.OptionFlag + csv: Interfaces.BooleanFlag + output: Interfaces.OptionFlag + extended: Interfaces.BooleanFlag + 'no-truncate': Interfaces.BooleanFlag + 'no-header': Interfaces.BooleanFlag } = { columns: F.string({exclusive: ['extended'], description: 'only show provided columns (comma-separated)'}), - sort: F.string({description: 'property to sort by (prepend \'-\' for descending)'}), + sort: F.string({description: "property to sort by (prepend '-' for descending)"}), filter: F.string({description: 'filter property by partial string matching, ex: name=foo'}), csv: F.boolean({exclusive: ['no-truncate'], description: 'output is csv format [alias: --output=csv]'}), output: F.string({ @@ -334,8 +352,8 @@ export namespace table { type IncludeFlags = Pick export function flags(): IFlags - export function flags(opts: { except: Z | Z[] }): ExcludeFlags - export function flags(opts: { only: K | K[] }): IncludeFlags + export function flags(opts: {except: Z | Z[]}): ExcludeFlags + export function flags(opts: {only: K | K[]}): IncludeFlags export function flags(opts?: any): any { if (opts) { @@ -344,7 +362,7 @@ export namespace table { const e = (opts.except && typeof opts.except === 'string' ? [opts.except] : opts.except) || [] for (const key of o) { if (!(e as any[]).includes(key)) { - (f as any)[key] = (Flags as any)[key] + ;(f as any)[key] = (Flags as any)[key] } } @@ -355,33 +373,34 @@ export namespace table { } export interface Column> { - header: string; - extended: boolean; - minWidth: number; - get(row: T): any; + header: string + extended: boolean + minWidth: number + get(row: T): any } - export type Columns> = { [key: string]: Partial> } + export type Columns> = {[key: string]: Partial>} // export type OutputType = 'csv' | 'json' | 'yaml' export interface Options { - [key: string]: any; - sort?: string; - filter?: string; - columns?: string; - extended?: boolean; - 'no-truncate'?: boolean; - output?: string; - 'no-header'?: boolean; - printLine?(s: any): any; + [key: string]: any + sort?: string + filter?: string + columns?: string + extended?: boolean + 'no-truncate'?: boolean + output?: string + 'no-header'?: boolean + printLine?(s: any): any } } -const getWidestColumnWith = (data: any[], columnKey: string): number => data.reduce((previous, current) => { - const d = current[columnKey] - // convert multi-line cell to single longest line - // for width calculations - const manyLines = (d as string).split('\n') - return Math.max(previous, manyLines.length > 1 ? Math.max(...manyLines.map((r: string) => sw(r))) : sw(d)) -}, 0) +const getWidestColumnWith = (data: any[], columnKey: string): number => + data.reduce((previous, current) => { + const d = current[columnKey] + // convert multi-line cell to single longest line + // for width calculations + const manyLines = (d as string).split('\n') + return Math.max(previous, manyLines.length > 1 ? Math.max(...manyLines.map((r: string) => sw(r))) : sw(d)) + }, 0) diff --git a/src/cli-ux/styled/tree.ts b/src/cli-ux/styled/tree.ts index 75b2dbc4e..916261506 100644 --- a/src/cli-ux/styled/tree.ts +++ b/src/cli-ux/styled/tree.ts @@ -1,7 +1,7 @@ const treeify = require('object-treeify') export class Tree { - nodes: { [key: string]: Tree } = {} + nodes: {[key: string]: Tree} = {} insert(child: string, value: Tree = new Tree()): Tree { this.nodes[child] = value @@ -21,7 +21,7 @@ export class Tree { display(logger: any = console.log): void { const addNodes = function (nodes: any) { - const tree: { [key: string]: any } = {} + const tree: {[key: string]: any} = {} for (const p of Object.keys(nodes)) { tree[p] = addNodes(nodes[p].nodes) } diff --git a/src/cli-ux/wait.ts b/src/cli-ux/wait.ts index 7514385ff..ecf28356f 100644 --- a/src/cli-ux/wait.ts +++ b/src/cli-ux/wait.ts @@ -1,3 +1,4 @@ -export default (ms = 1000): Promise => new Promise(resolve => { - setTimeout(resolve, ms) -}) +export default (ms = 1000): Promise => + new Promise((resolve) => { + setTimeout(resolve, ms) + }) diff --git a/src/command.ts b/src/command.ts index 9b365af69..a0480bc59 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,4 +1,3 @@ - import * as Errors from './errors' import * as Parser from './parser' import { @@ -37,8 +36,7 @@ const pjson = requireJson(__dirname, '..', 'package.json') * this occurs when stdout closes such as when piping to head */ stdout.on('error', (err: any) => { - if (err && err.code === 'EPIPE') - return + if (err && err.code === 'EPIPE') return throw err }) @@ -128,7 +126,11 @@ export abstract class Command { * @param {LoadOptions} opts options * @returns {Promise} result */ - public static async run(this: new(argv: string[], config: Config) => T, argv?: string[], opts?: LoadOptions): Promise> { + public static async run( + this: new (argv: string[], config: Config) => T, + argv?: string[], + opts?: LoadOptions, + ): Promise> { if (!argv) argv = process.argv.slice(2) // Handle the case when a file URL string is passed in such as 'import.meta.url'; covert to file path. @@ -156,7 +158,10 @@ export abstract class Command { protected debug: (...args: any[]) => void - public constructor(public argv: string[], public config: Config) { + public constructor( + public argv: string[], + public config: Config, + ) { this.id = this.ctor.id try { this.debug = require('debug')(this.id ? `${this.config.bin}:${this.id}` : this.config.bin) @@ -204,7 +209,10 @@ export abstract class Command { public error(input: string | Error, options?: {code?: string; exit?: number} & PrettyPrintableError): never - public error(input: string | Error, options: {code?: string; exit?: number | false} & PrettyPrintableError = {}): void { + public error( + input: string | Error, + options: {code?: string; exit?: number | false} & PrettyPrintableError = {}, + ): void { return Errors.error(input, options as any) } @@ -237,10 +245,10 @@ export abstract class Command { const passThroughIndex = this.argv.indexOf('--') const jsonIndex = this.argv.indexOf('--json') return passThroughIndex === -1 - // If '--' is not present, then check for `--json` in this.argv - ? jsonIndex > -1 - // If '--' is present, return true only the --json flag exists and is before the '--' - : jsonIndex > -1 && jsonIndex < passThroughIndex + ? // If '--' is not present, then check for `--json` in this.argv + jsonIndex > -1 + : // If '--' is present, return true only the --json flag exists and is before the '--' + jsonIndex > -1 && jsonIndex < passThroughIndex } /** @@ -259,11 +267,7 @@ export abstract class Command { } protected warnIfFlagDeprecated(flags: Record): void { - const allFlags = aggregateFlags( - this.ctor.flags, - this.ctor.baseFlags, - this.ctor.enableJsonFlag, - ) + const allFlags = aggregateFlags(this.ctor.flags, this.ctor.baseFlags, this.ctor.enableJsonFlag) for (const flag of Object.keys(flags)) { const flagDef = allFlags[flag] const deprecated = flagDef?.deprecated @@ -273,10 +277,12 @@ export abstract class Command { const deprecateAliases = flagDef?.deprecateAliases if (deprecateAliases) { - const aliases = uniq([...flagDef?.aliases ?? [], ...flagDef?.charAliases ?? []]).map(a => a.length === 1 ? `-${a}` : `--${a}`) + const aliases = uniq([...(flagDef?.aliases ?? []), ...(flagDef?.charAliases ?? [])]).map((a) => + a.length === 1 ? `-${a}` : `--${a}`, + ) if (aliases.length === 0) return - const foundAliases = aliases.filter(alias => this.argv.some(a => a.startsWith(alias))) + const foundAliases = aliases.filter((alias) => this.argv.some((a) => a.startsWith(alias))) for (const alias of foundAliases) { let preferredUsage = `--${flagDef?.name}` if (flagDef?.char) { @@ -313,11 +319,7 @@ export abstract class Command { const opts = { context: this, ...options, - flags: aggregateFlags( - options.flags, - options.baseFlags, - options.enableJsonFlag, - ), + flags: aggregateFlags(options.flags, options.baseFlags, options.enableJsonFlag), } const results = await Parser.parse(argv, opts) @@ -369,14 +371,14 @@ export abstract class Command { keys.push(this.config.scopedEnvVarKey(envVar)) } - keys.map(key => delete process.env[key]) + keys.map((key) => delete process.env[key]) } } export namespace Command { export type Class = typeof Command & { - id: string; - run(argv?: string[], config?: LoadOptions): Promise; + id: string + run(argv?: string[], config?: LoadOptions): Promise } export interface Loadable extends Cached { @@ -384,34 +386,35 @@ export namespace Command { } export type Cached = { - [key: string]: unknown; - id: string; - hidden: boolean; - state?: 'beta' | 'deprecated' | string; - deprecationOptions?: Deprecation; - aliases: string[]; - summary?: string; - description?: string; - usage?: string | string[]; - examples?: Example[]; - strict?: boolean; - type?: string; - pluginName?: string; - pluginType?: string; - pluginAlias?: string; - flags: {[name: string]: Flag.Cached}; - args: {[name: string]: Arg.Cached}; - hasDynamicHelp?: boolean; + [key: string]: unknown + id: string + hidden: boolean + state?: 'beta' | 'deprecated' | string + deprecationOptions?: Deprecation + aliases: string[] + summary?: string + description?: string + usage?: string | string[] + examples?: Example[] + strict?: boolean + type?: string + pluginName?: string + pluginType?: string + pluginAlias?: string + flags: {[name: string]: Flag.Cached} + args: {[name: string]: Arg.Cached} + hasDynamicHelp?: boolean permutations?: string[] - aliasPermutations?: string[]; - isESM?: boolean; - relativePath?: string[]; + aliasPermutations?: string[] + isESM?: boolean + relativePath?: string[] } export type Flag = IFlag export namespace Flag { - export type Cached = Omit & (BooleanFlagProps | OptionFlagProps) & {hasDynamicHelp?: boolean} + export type Cached = Omit & + (BooleanFlagProps | OptionFlagProps) & {hasDynamicHelp?: boolean} export type Any = Flag | Cached } @@ -422,8 +425,10 @@ export namespace Command { export type Any = Arg | Cached } - export type Example = string | { - description: string; - command: string; - } + export type Example = + | string + | { + description: string + command: string + } } diff --git a/src/config/config.ts b/src/config/config.ts index 41795375d..66da7a0db 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -93,7 +93,7 @@ export class Config implements IConfig { public version!: string public windows!: boolean public binAliases?: string[] - public nsisCustomization?:string + public nsisCustomization?: string protected warned = false @@ -151,7 +151,8 @@ export class Config implements IConfig { // eslint-disable-next-line complexity public async load(): Promise { - settings.performanceEnabled = (settings.performanceEnabled === undefined ? this.options.enablePerf : settings.performanceEnabled) ?? false + settings.performanceEnabled = + (settings.performanceEnabled === undefined ? this.options.enablePerf : settings.performanceEnabled) ?? false const marker = Performance.mark('config.load') this.pluginLoader = new PluginLoader({root: this.options.root, plugins: this.options.plugins}) Config._rootPlugin = await this.pluginLoader.loadRoot() @@ -167,7 +168,7 @@ export class Config implements IConfig { this.channel = this.options.channel || channelFromVersion(this.version) this.valid = Config._rootPlugin.valid - this.arch = (arch() === 'ia32' ? 'x86' : arch() as any) + this.arch = arch() === 'ia32' ? 'x86' : (arch() as any) this.platform = WSL ? 'wsl' : getPlatform() this.windows = this.platform === 'win32' this.bin = this.pjson.oclif.bin || this.name @@ -176,7 +177,8 @@ export class Config implements IConfig { this.dirname = this.pjson.oclif.dirname || this.name this.flexibleTaxonomy = this.pjson.oclif.flexibleTaxonomy || false // currently, only colons or spaces are valid separators - if (this.pjson.oclif.topicSeparator && [':', ' '].includes(this.pjson.oclif.topicSeparator)) this.topicSeparator = this.pjson.oclif.topicSeparator! + if (this.pjson.oclif.topicSeparator && [':', ' '].includes(this.pjson.oclif.topicSeparator)) + this.topicSeparator = this.pjson.oclif.topicSeparator! if (this.platform === 'win32') this.dirname = this.dirname.replace('/', '\\') this.userAgent = `${this.name}/${this.version} ${this.platform}-${this.arch} node-${process.version}` this.shell = this._shell() @@ -201,17 +203,20 @@ export class Config implements IConfig { ...s3.templates, target: { baseDir: '<%- bin %>', - unversioned: "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %>-<%- platform %>-<%- arch %><%- ext %>", - versioned: "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %>-v<%- version %>/<%- bin %>-v<%- version %>-<%- platform %>-<%- arch %><%- ext %>", + unversioned: + "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %>-<%- platform %>-<%- arch %><%- ext %>", + versioned: + "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %>-v<%- version %>/<%- bin %>-v<%- version %>-<%- platform %>-<%- arch %><%- ext %>", manifest: "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- platform %>-<%- arch %>", - ...s3.templates && s3.templates.target, + ...(s3.templates && s3.templates.target), }, vanilla: { unversioned: "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %><%- ext %>", - versioned: "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %>-v<%- version %>/<%- bin %>-v<%- version %><%- ext %>", + versioned: + "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %><%- bin %>-v<%- version %>/<%- bin %>-v<%- version %><%- ext %>", baseDir: '<%- bin %>', manifest: "<%- channel === 'stable' ? '' : 'channels/' + channel + '/' %>version", - ...s3.templates && s3.templates.vanilla, + ...(s3.templates && s3.templates.vanilla), }, } @@ -275,7 +280,7 @@ export class Config implements IConfig { }, ms).unref() }) - return Promise.race([promise, timeout]).then(result => { + return Promise.race([promise, timeout]).then((result) => { clearTimeout(id) return result }) @@ -285,7 +290,7 @@ export class Config implements IConfig { successes: [], failures: [], } as Hook.Result - const promises = [...this.plugins.values()].map(async p => { + const promises = [...this.plugins.values()].map(async (p) => { const debug = require('debug')([this.bin, p.name, 'hooks', event].join(':')) const context: Hook.Context = { config: this, @@ -296,7 +301,7 @@ export class Config implements IConfig { log(message?: any, ...args: any[]) { stdout.write(format(message, ...args) + '\n') }, - error(message, options: { code?: string; exit?: number } = {}) { + error(message, options: {code?: string; exit?: number} = {}) { error(message, options) }, warn(message: string) { @@ -314,8 +319,8 @@ export class Config implements IConfig { debug('start', isESM ? '(import)' : '(require)', filePath) const result = timeout - ? await withTimeout(timeout, search(module).call(context, {...opts as any, config: this})) - : await search(module).call(context, {...opts as any, config: this}) + ? await withTimeout(timeout, search(module).call(context, {...(opts as any), config: this})) + : await search(module).call(context, {...(opts as any), config: this}) final.successes.push({plugin: p, result}) if (p.name === '@oclif/plugin-legacy' && event === 'init') { @@ -346,15 +351,20 @@ export class Config implements IConfig { return final } - public async runCommand(id: string, argv: string[] = [], cachedCommand: Command.Loadable | null = null): Promise { + public async runCommand( + id: string, + argv: string[] = [], + cachedCommand: Command.Loadable | null = null, + ): Promise { const marker = Performance.mark(`config.runCommand#${id}`) debug('runCommand %s %o', id, argv) let c = cachedCommand ?? this.findCommand(id) if (!c) { const matches = this.flexibleTaxonomy ? this.findMatches(id, argv) : [] - const hookResult = this.flexibleTaxonomy && matches.length > 0 - ? await this.runHook('command_incomplete', {id, argv, matches}) - : await this.runHook('command_not_found', {id, argv}) + const hookResult = + this.flexibleTaxonomy && matches.length > 0 + ? await this.runHook('command_incomplete', {id, argv, matches}) + : await this.runHook('command_not_found', {id, argv}) if (hookResult.successes[0]) return hookResult.successes[0].result as T if (hookResult.failures[0]) throw hookResult.failures[0].error @@ -396,11 +406,11 @@ export class Config implements IConfig { } public scopedEnvVar(k: string): string | undefined { - return process.env[this.scopedEnvVarKeys(k).find(k => process.env[k]) as string] + return process.env[this.scopedEnvVarKeys(k).find((k) => process.env[k]) as string] } public scopedEnvVarTrue(k: string): boolean { - const v = process.env[this.scopedEnvVarKeys(k).find(k => process.env[k]) as string] + const v = process.env[this.scopedEnvVarKeys(k).find((k) => process.env[k]) as string] return v === '1' || v === 'true' } @@ -411,9 +421,9 @@ export class Config implements IConfig { */ public scopedEnvVarKey(k: string): string { return [this.bin, k] - .map(p => p.replaceAll('@', '').replaceAll(/[/-]/g, '_')) - .join('_') - .toUpperCase() + .map((p) => p.replaceAll('@', '').replaceAll(/[/-]/g, '_')) + .join('_') + .toUpperCase() } /** @@ -422,26 +432,27 @@ export class Config implements IConfig { * @returns {string[]} e.g. ['SF_DEBUG', 'SFDX_DEBUG'] */ public scopedEnvVarKeys(k: string): string[] { - return [this.bin, ...this.binAliases ?? []].filter(Boolean).map(alias => - [alias.replaceAll('@', '').replaceAll(/[/-]/g, '_'), k].join('_').toUpperCase()) + return [this.bin, ...(this.binAliases ?? [])] + .filter(Boolean) + .map((alias) => [alias.replaceAll('@', '').replaceAll(/[/-]/g, '_'), k].join('_').toUpperCase()) } - public findCommand(id: string, opts: { must: true }): Command.Loadable + public findCommand(id: string, opts: {must: true}): Command.Loadable - public findCommand(id: string, opts?: { must: boolean }): Command.Loadable | undefined + public findCommand(id: string, opts?: {must: boolean}): Command.Loadable | undefined - public findCommand(id: string, opts: { must?: boolean } = {}): Command.Loadable | undefined { + public findCommand(id: string, opts: {must?: boolean} = {}): Command.Loadable | undefined { const lookupId = this.getCmdLookupId(id) const command = this._commands.get(lookupId) if (opts.must && !command) error(`command ${lookupId} not found`) return command } - public findTopic(id: string, opts: { must: true }): Topic + public findTopic(id: string, opts: {must: true}): Topic - public findTopic(id: string, opts?: { must: boolean }): Topic | undefined + public findTopic(id: string, opts?: {must: boolean}): Topic | undefined - public findTopic(name: string, opts: { must?: boolean } = {}): Topic | undefined { + public findTopic(name: string, opts: {must?: boolean} = {}): Topic | undefined { const lookupId = this.getTopicLookupId(name) const topic = this._topics.get(lookupId) if (topic) return topic @@ -462,14 +473,18 @@ export class Config implements IConfig { * @returns string[] */ public findMatches(partialCmdId: string, argv: string[]): Command.Loadable[] { - const flags = argv.filter(arg => !getHelpFlagAdditions(this).includes(arg) && arg.startsWith('-')).map(a => a.replaceAll('-', '')) - const possibleMatches = [...this.commandPermutations.get(partialCmdId)].map(k => this._commands.get(k)!) + const flags = argv + .filter((arg) => !getHelpFlagAdditions(this).includes(arg) && arg.startsWith('-')) + .map((a) => a.replaceAll('-', '')) + const possibleMatches = [...this.commandPermutations.get(partialCmdId)].map((k) => this._commands.get(k)!) - const matches = possibleMatches.filter(command => { - const cmdFlags = Object.entries(command.flags).flatMap(([flag, def]) => def.char ? [def.char, flag] : [flag]) as string[] + const matches = possibleMatches.filter((command) => { + const cmdFlags = Object.entries(command.flags).flatMap(([flag, def]) => + def.char ? [def.char, flag] : [flag], + ) as string[] // A command is a match if the provided flags belong to the full command - return flags.every(f => cmdFlags.includes(f)) + return flags.every((f) => cmdFlags.includes(f)) }) return matches @@ -497,7 +512,7 @@ export class Config implements IConfig { * @returns string[] */ public getAllCommandIDs(): string[] { - return this.getAllCommands().map(c => c.id) + return this.getAllCommands().map((c) => c.id) } public get commands(): Command.Loadable[] { @@ -506,7 +521,7 @@ export class Config implements IConfig { public get commandIDs(): string[] { if (this._commandIDs) return this._commandIDs - this._commandIDs = this.commands.map(c => c.id) + this._commandIDs = this.commands.map((c) => c.id) return this._commandIDs } @@ -520,18 +535,24 @@ export class Config implements IConfig { cliVersion, architecture, nodeVersion, - pluginVersions: Object.fromEntries([...this.plugins.values()].map(p => [p.name, {version: p.version, type: p.type, root: p.root}])), + pluginVersions: Object.fromEntries( + [...this.plugins.values()].map((p) => [p.name, {version: p.version, type: p.type, root: p.root}]), + ), osVersion: `${type()} ${release()}`, shell: this.shell, rootPath: this.root, } } - public s3Key(type: keyof PJSON.S3.Templates, ext?: '.tar.gz' | '.tar.xz' | IConfig.s3Key.Options, options: IConfig.s3Key.Options = {}): string { + public s3Key( + type: keyof PJSON.S3.Templates, + ext?: '.tar.gz' | '.tar.xz' | IConfig.s3Key.Options, + options: IConfig.s3Key.Options = {}, + ): string { if (typeof ext === 'object') options = ext else if (ext) options.ext = ext const template = this.pjson.oclif.update.s3.templates[options.platform ? 'target' : 'vanilla'][type] ?? '' - return ejs.render(template, {...this as any, ...options}) + return ejs.render(template, {...(this as any), ...options}) } public s3Url(key: string): string { @@ -547,9 +568,10 @@ export class Config implements IConfig { } protected dir(category: 'cache' | 'data' | 'config'): string { - const base = process.env[`XDG_${category.toUpperCase()}_HOME`] - || (this.windows && process.env.LOCALAPPDATA) - || join(this.home, category === 'data' ? '.local/share' : '.' + category) + const base = + process.env[`XDG_${category.toUpperCase()}_HOME`] || + (this.windows && process.env.LOCALAPPDATA) || + join(this.home, category === 'data' ? '.local/share' : '.' + category) return join(base, this.dirname) } @@ -558,7 +580,7 @@ export class Config implements IConfig { } protected windowsHomedriveHome(): string | undefined { - return (process.env.HOMEDRIVE && process.env.HOMEPATH && join(process.env.HOMEDRIVE!, process.env.HOMEPATH!)) + return process.env.HOMEDRIVE && process.env.HOMEPATH && join(process.env.HOMEDRIVE!, process.env.HOMEPATH!) } protected windowsUserprofileHome(): string | undefined { @@ -594,7 +616,7 @@ export class Config implements IConfig { return 0 } - protected warn(err: string | Error | { name: string; detail: string }, scope?: string): void { + protected warn(err: string | Error | {name: string; detail: string}, scope?: string): void { if (this.warned) return if (typeof err === 'string') { @@ -638,7 +660,10 @@ export class Config implements IConfig { private isJitPluginCommand(c: Command.Loadable): boolean { // Return true if the command's plugin is listed under oclif.jitPlugins AND if the plugin hasn't been loaded to this.plugins - return Object.keys(this.pjson.oclif.jitPlugins ?? {}).includes(c.pluginName ?? '') && Boolean(c?.pluginName && !this.plugins.has(c.pluginName)) + return ( + Object.keys(this.pjson.oclif.jitPlugins ?? {}).includes(c.pluginName ?? '') && + Boolean(c?.pluginName && !this.plugins.has(c.pluginName)) + ) } private getCmdLookupId(id: string): string { @@ -666,9 +691,10 @@ export class Config implements IConfig { // v3 moved command id permutations to the manifest, but some plugins may not have // the new manifest yet. For those, we need to calculate the permutations here. - const permutations = this.flexibleTaxonomy && command.permutations === undefined - ? getCommandIdPermutations(command.id) - : command.permutations ?? [command.id] + const permutations = + this.flexibleTaxonomy && command.permutations === undefined + ? getCommandIdPermutations(command.id) + : command.permutations ?? [command.id] // set every permutation for (const permutation of permutations) { this.commandPermutations.add(permutation, command.id) @@ -687,9 +713,10 @@ export class Config implements IConfig { // v3 moved command alias permutations to the manifest, but some plugins may not have // the new manifest yet. For those, we need to calculate the permutations here. - const aliasPermutations = this.flexibleTaxonomy && command.aliasPermutations === undefined - ? getCommandIdPermutations(alias) - : command.permutations ?? [alias] + const aliasPermutations = + this.flexibleTaxonomy && command.aliasPermutations === undefined + ? getCommandIdPermutations(alias) + : command.permutations ?? [alias] // set every permutation for (const permutation of aliasPermutations) { this.commandPermutations.add(permutation, command.id) @@ -719,7 +746,7 @@ export class Config implements IConfig { } // Add missing topics for displaying help when partial commands are entered. - for (const c of plugin.commands.filter(c => !c.hidden)) { + for (const c of plugin.commands.filter((c) => !c.hidden)) { const parts = c.id.split(':') while (parts.length > 0) { const name = parts.join(':') @@ -793,14 +820,14 @@ export class Config implements IConfig { } /** - * Insert legacy plugins - * - * Replace invalid CLI plugins (cli-engine plugins, mostly Heroku) loaded via `this.loadPlugins` - * with oclif-compatible ones returned by @oclif/plugin-legacy init hook. - * - * @param plugins array of oclif-compatible plugins - * @returns void - */ + * Insert legacy plugins + * + * Replace invalid CLI plugins (cli-engine plugins, mostly Heroku) loaded via `this.loadPlugins` + * with oclif-compatible ones returned by @oclif/plugin-legacy init hook. + * + * @param plugins array of oclif-compatible plugins + * @returns void + */ private insertLegacyPlugins(plugins: IPlugin[]) { for (const plugin of plugins) { this.plugins.set(plugin.name, plugin) @@ -808,4 +835,3 @@ export class Config implements IConfig { } } } - diff --git a/src/config/index.ts b/src/config/index.ts index 748162de7..e3e23deca 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,4 +1,3 @@ export {Config} from './config' export {Plugin} from './plugin' export {tsPath} from './ts-node' - diff --git a/src/config/plugin-loader.ts b/src/config/plugin-loader.ts index ed008a708..a72fa8f27 100644 --- a/src/config/plugin-loader.ts +++ b/src/config/plugin-loader.ts @@ -10,16 +10,16 @@ import {join} from 'node:path' const debug = Debug() type PluginLoaderOptions = { - root: string; - plugins?: IPlugin[] | PluginsMap; + root: string + plugins?: IPlugin[] | PluginsMap } type LoadOpts = { - devPlugins?: boolean; - userPlugins?: boolean; - dataDir: string; - rootPlugin: IPlugin; - force?: boolean; + devPlugins?: boolean + userPlugins?: boolean + dataDir: string + rootPlugin: IPlugin + force?: boolean } type PluginsMap = Map @@ -32,7 +32,7 @@ export default class PluginLoader { constructor(public options: PluginLoaderOptions) { if (options.plugins) { this.pluginsProvided = true - this.plugins = Array.isArray(options.plugins) ? new Map(options.plugins.map(p => [p.name, p])) : options.plugins + this.plugins = Array.isArray(options.plugins) ? new Map(options.plugins.map((p) => [p.name, p])) : options.plugins } } @@ -40,7 +40,7 @@ export default class PluginLoader { let rootPlugin: IPlugin if (this.pluginsProvided) { const plugins = [...this.plugins.values()] - rootPlugin = plugins.find(p => p.root === this.options.root) ?? plugins[0] + rootPlugin = plugins.find((p) => p.root === this.options.root) ?? plugins[0] } else { const marker = Performance.mark('plugin.load#root') rootPlugin = new Plugin.Plugin({root: this.options.root, isRoot: true}) @@ -97,61 +97,76 @@ export default class PluginLoader { const pjson = await readJson(userPJSONPath) if (!pjson.oclif) pjson.oclif = {schema: 1} if (!pjson.oclif.plugins) pjson.oclif.plugins = [] - await this.loadPlugins(userPJSONPath, 'user', pjson.oclif.plugins.filter((p: any) => p.type === 'user')) - await this.loadPlugins(userPJSONPath, 'link', pjson.oclif.plugins.filter((p: any) => p.type === 'link')) + await this.loadPlugins( + userPJSONPath, + 'user', + pjson.oclif.plugins.filter((p: any) => p.type === 'user'), + ) + await this.loadPlugins( + userPJSONPath, + 'link', + pjson.oclif.plugins.filter((p: any) => p.type === 'link'), + ) } catch (error: any) { if (error.code !== 'ENOENT') process.emitWarning(error) } } } - private async loadPlugins(root: string, type: string, plugins: (string | { root?: string; name?: string; tag?: string })[], parent?: Plugin.Plugin): Promise { + private async loadPlugins( + root: string, + type: string, + plugins: (string | {root?: string; name?: string; tag?: string})[], + parent?: Plugin.Plugin, + ): Promise { if (!plugins || plugins.length === 0) return const mark = Performance.mark(`config.loadPlugins#${type}`) debug('loading plugins', plugins) - await Promise.all((plugins || []).map(async plugin => { - try { - const name = typeof plugin === 'string' ? plugin : plugin.name! - const opts: Options = { - name, - type, - root, - } - if (typeof plugin !== 'string') { - opts.tag = plugin.tag || opts.tag - opts.root = plugin.root || opts.root + await Promise.all( + (plugins || []).map(async (plugin) => { + try { + const name = typeof plugin === 'string' ? plugin : plugin.name! + const opts: Options = { + name, + type, + root, + } + if (typeof plugin !== 'string') { + opts.tag = plugin.tag || opts.tag + opts.root = plugin.root || opts.root + } + + if (parent) { + opts.parent = parent + } + + if (this.plugins.has(name)) return + const pluginMarker = Performance.mark(`plugin.load#${name}`) + const instance = new Plugin.Plugin(opts) + await instance.load() + pluginMarker?.addDetails({ + hasManifest: instance.hasManifest, + commandCount: instance.commands.length, + topicCount: instance.topics.length, + type: instance.type, + usesMain: Boolean(instance.pjson.main), + name: instance.name, + }) + pluginMarker?.stop() + + this.plugins.set(instance.name, instance) + if (parent) { + instance.parent = parent + if (!parent.children) parent.children = [] + parent.children.push(instance) + } + + await this.loadPlugins(instance.root, type, instance.pjson.oclif.plugins || [], instance) + } catch (error: any) { + this.errors.push(error) } - - if (parent) { - opts.parent = parent - } - - if (this.plugins.has(name)) return - const pluginMarker = Performance.mark(`plugin.load#${name}`) - const instance = new Plugin.Plugin(opts) - await instance.load() - pluginMarker?.addDetails({ - hasManifest: instance.hasManifest, - commandCount: instance.commands.length, - topicCount: instance.topics.length, - type: instance.type, - usesMain: Boolean(instance.pjson.main), - name: instance.name, - }) - pluginMarker?.stop() - - this.plugins.set(instance.name, instance) - if (parent) { - instance.parent = parent - if (!parent.children) parent.children = [] - parent.children.push(instance) - } - - await this.loadPlugins(instance.root, type, instance.pjson.oclif.plugins || [], instance) - } catch (error: any) { - this.errors.push(error) - } - })) + }), + ) mark?.addDetails({pluginCount: plugins.length}) mark?.stop() diff --git a/src/config/plugin.ts b/src/config/plugin.ts index cd229e86a..da3c73ce5 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -1,9 +1,5 @@ import {CLIError, error} from '../errors' -import { - Debug, - getCommandIdPermutations, - resolvePackage, -} from './util' +import {Debug, getCommandIdPermutations, resolvePackage} from './util' import {Plugin as IPlugin, PluginOptions} from '../interfaces/plugin' import {compact, exists, isProd, mapValues, readJson, requireJson} from '../util' import {dirname, join, parse, relative, sep} from 'node:path' @@ -24,17 +20,17 @@ function topicsToArray(input: any, base?: string): Topic[] { if (!input) return [] base = base ? `${base}:` : '' if (Array.isArray(input)) { - return [...input, input.flatMap(t => topicsToArray(t.subtopics, `${base}${t.name}`))] + return [...input, input.flatMap((t) => topicsToArray(t.subtopics, `${base}${t.name}`))] } - return Object.keys(input).flatMap(k => { + return Object.keys(input).flatMap((k) => { input[k].name = k return [{...input[k], name: `${base}${k}`}, ...topicsToArray(input[k].subtopics, `${base}${input[k].name}`)] }) } // essentially just "cd .." -function * up(from: string) { +function* up(from: string) { while (dirname(from) !== from) { yield from from = dirname(from) @@ -96,7 +92,7 @@ async function findRoot(name: string | undefined, root: string) { } const cachedCommandCanBeUsed = (manifest: Manifest | undefined, id: string): boolean => - Boolean(manifest?.commands[id] && ('isESM' in manifest.commands[id] && 'relativePath' in manifest.commands[id])) + Boolean(manifest?.commands[id] && 'isESM' in manifest.commands[id] && 'relativePath' in manifest.commands[id]) const search = (cmd: any) => { if (typeof cmd.run === 'function') return cmd @@ -160,7 +156,8 @@ export class Plugin implements IPlugin { // Linked plugins already have a root so there's no need to search for it. // However there could be child plugins nested inside the linked plugin, in which // case we still need to search for the child plugin's root. - const root = this.type === 'link' && !this.parent ? this.options.root : await findRoot(this.options.name, this.options.root) + const root = + this.type === 'link' && !this.parent ? this.options.root : await findRoot(this.options.name, this.options.root) if (!root) throw new CLIError(`could not find package.json with ${inspect(this.options)}`) this.root = root this._debug('reading %s plugin %s', this.type, root) @@ -181,18 +178,17 @@ export class Plugin implements IPlugin { this.pjson.oclif = this.pjson['cli-engine'] || {} } - this.hooks = mapValues(this.pjson.oclif.hooks || {}, i => Array.isArray(i) ? i : [i]) + this.hooks = mapValues(this.pjson.oclif.hooks || {}, (i) => (Array.isArray(i) ? i : [i])) this.manifest = await this._manifest() - this.commands = Object - .entries(this.manifest.commands) - .map(([id, c]) => ({ - ...c, - pluginAlias: this.alias, - pluginType: c.pluginType === 'jit' ? 'jit' : this.type, - load: async () => this.findCommand(id, {must: true}), - })) - .sort((a, b) => a.id.localeCompare(b.id)) + this.commands = Object.entries(this.manifest.commands) + .map(([id, c]) => ({ + ...c, + pluginAlias: this.alias, + pluginType: c.pluginType === 'jit' ? 'jit' : this.type, + load: async () => this.findCommand(id, {must: true}), + })) + .sort((a, b) => a.id.localeCompare(b.id)) } public get topics(): Topic[] { @@ -211,12 +207,8 @@ export class Plugin implements IPlugin { const marker = Performance.mark(`plugin.commandIDs#${this.name}`, {plugin: this.name}) this._debug(`loading IDs from ${this.commandsDir}`) - const patterns = [ - '**/*.+(js|cjs|mjs|ts|tsx)', - '!**/*.+(d.ts|test.ts|test.js|spec.ts|spec.js)?(x)', - ] - const ids = sync(patterns, {cwd: this.commandsDir}) - .map(file => { + const patterns = ['**/*.+(js|cjs|mjs|ts|tsx)', '!**/*.+(d.ts|test.ts|test.js|spec.ts|spec.js)?(x)'] + const ids = sync(patterns, {cwd: this.commandsDir}).map((file) => { const p = parse(file) const topics = p.dir.split('/') const command = p.name !== 'index' && p.name @@ -242,7 +234,7 @@ export class Plugin implements IPlugin { let isESM: boolean | undefined let filePath: string | undefined try { - ({isESM, module, filePath} = cachedCommandCanBeUsed(this.manifest, id) + ;({isESM, module, filePath} = cachedCommandCanBeUsed(this.manifest, id) ? await loadWithDataFromManifest(this.manifest.commands[id], this.root) : await loadWithData(this, join(this.commandsDir ?? this.pjson.oclif.commands, ...id.split(':')))) this._debug(isESM ? '(import)' : '(require)', filePath) @@ -276,7 +268,9 @@ export class Plugin implements IPlugin { const p = join(this.root, `${dotfile ? '.' : ''}oclif.manifest.json`) const manifest = await readJson(p) if (!process.env.OCLIF_NEXT_VERSION && manifest.version.split('-')[0] !== this.version.split('-')[0]) { - process.emitWarning(`Mismatched version in ${this.name} plugin manifest. Expected: ${this.version} Received: ${manifest.version}\nThis usually means you have an oclif.manifest.json file that should be deleted in development. This file should be automatically generated when publishing.`) + process.emitWarning( + `Mismatched version in ${this.name} plugin manifest. Expected: ${this.version} Received: ${manifest.version}\nThis usually means you have an oclif.manifest.json file that should be deleted in development. This file should be automatically generated when publishing.`, + ) } else { this._debug('using manifest from', p) this.hasManifest = true @@ -303,28 +297,35 @@ export class Plugin implements IPlugin { const manifest = { version: this.version, - commands: (await Promise.all(this.commandIDs.map(async id => { - try { - const cached = await cacheCommand(await this.findCommand(id, {must: true}), this, respectNoCacheDefault) - if (this.flexibleTaxonomy) { - const permutations = getCommandIdPermutations(id) - const aliasPermutations = cached.aliases.flatMap(a => getCommandIdPermutations(a)) - return [id, {...cached, permutations, aliasPermutations} as Command.Cached] - } - - return [id, cached] - } catch (error: any) { - const scope = 'cacheCommand' - if (Boolean(errorOnManifestCreate) === false) this.warn(error, scope) - else throw this.addErrorScope(error, scope) - } - }))) - // eslint-disable-next-line unicorn/no-await-expression-member, unicorn/prefer-native-coercion-functions - .filter((f): f is [string, Command.Cached] => Boolean(f)) - .reduce((commands, [id, c]) => { - commands[id] = c - return commands - }, {} as {[k: string]: Command.Cached}), + commands: ( + await Promise.all( + this.commandIDs.map(async (id) => { + try { + const cached = await cacheCommand(await this.findCommand(id, {must: true}), this, respectNoCacheDefault) + if (this.flexibleTaxonomy) { + const permutations = getCommandIdPermutations(id) + const aliasPermutations = cached.aliases.flatMap((a) => getCommandIdPermutations(a)) + return [id, {...cached, permutations, aliasPermutations} as Command.Cached] + } + + return [id, cached] + } catch (error: any) { + const scope = 'cacheCommand' + if (Boolean(errorOnManifestCreate) === false) this.warn(error, scope) + else throw this.addErrorScope(error, scope) + } + }), + ) + ) + // eslint-disable-next-line unicorn/no-await-expression-member, unicorn/prefer-native-coercion-functions + .filter((f): f is [string, Command.Cached] => Boolean(f)) + .reduce( + (commands, [id, c]) => { + commands[id] = c + return commands + }, + {} as {[k: string]: Command.Cached}, + ), } marker?.addDetails({fromCache: false, commandCount: Object.keys(manifest.commands).length}) marker?.stop() @@ -339,8 +340,14 @@ export class Plugin implements IPlugin { private addErrorScope(err: any, scope?: string) { err.name = `${err.name} Plugin: ${this.name}` - err.detail = compact([err.detail, `module: ${this._base}`, scope && `task: ${scope}`, `plugin: ${this.name}`, `root: ${this.root}`, 'See more details with DEBUG=*']).join('\n') + err.detail = compact([ + err.detail, + `module: ${this._base}`, + scope && `task: ${scope}`, + `plugin: ${this.name}`, + `root: ${this.root}`, + 'See more details with DEBUG=*', + ]).join('\n') return err } } - diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index 80b82d47e..f94c61566 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -29,20 +29,20 @@ function loadTSConfig(root: string): TSConfig | undefined { typescript = require(join(root, 'node_modules', 'typescript')) } catch { debug(`Could not find typescript dependency. Skipping ts-node registration for ${root}.`) - memoizedWarn('Could not find typescript. Please ensure that typescript is a devDependency. Falling back to compiled source.') + memoizedWarn( + 'Could not find typescript. Please ensure that typescript is a devDependency. Falling back to compiled source.', + ) return } } if (existsSync(tsconfigPath) && typescript) { - const tsconfig = typescript.parseConfigFileTextToJson( - tsconfigPath, - readJsonSync(tsconfigPath, false), - ).config + const tsconfig = typescript.parseConfigFileTextToJson(tsconfigPath, readJsonSync(tsconfigPath, false)).config if (!tsconfig || !tsconfig.compilerOptions) { throw new Error( - `Could not read and parse tsconfig.json at ${tsconfigPath}, or it ` - + 'did not contain a "compilerOptions" section.') + `Could not read and parse tsconfig.json at ${tsconfigPath}, or it ` + + 'did not contain a "compilerOptions" section.', + ) } TS_CONFIGS[root] = tsconfig @@ -63,13 +63,13 @@ function registerTSNode(root: string): TSConfig | undefined { tsNode = require(tsNodePath) } catch { debug(`Could not find ts-node at ${tsNodePath}. Skipping ts-node registration for ${root}.`) - memoizedWarn(`Could not find ts-node at ${tsNodePath}. Please ensure that ts-node is a devDependency. Falling back to compiled source.`) + memoizedWarn( + `Could not find ts-node at ${tsNodePath}. Please ensure that ts-node is a devDependency. Falling back to compiled source.`, + ) return } - const typeRoots = [ - join(root, 'node_modules', '@types'), - ] + const typeRoots = [join(root, 'node_modules', '@types')] const rootDirs: string[] = [] @@ -153,9 +153,13 @@ export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): * In other words, this allows plugins to be auto-transpiled when developing locally using `bin/dev.js`. */ if ((isProduction || ROOT_PLUGIN?.moduleType === 'commonjs') && plugin?.moduleType === 'module') { - debug(`Skipping ts-node registration for ${root} because it's an ESM module (NODE_ENV: ${process.env.NODE_ENV}, root plugin module type: ${ROOT_PLUGIN?.moduleType})))`) + debug( + `Skipping ts-node registration for ${root} because it's an ESM module (NODE_ENV: ${process.env.NODE_ENV}, root plugin module type: ${ROOT_PLUGIN?.moduleType})))`, + ) if (plugin.type === 'link') - memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`) + memoizedWarn( + `${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`, + ) return orig } diff --git a/src/config/util.ts b/src/config/util.ts index 482ce4554..bbcb2bed6 100644 --- a/src/config/util.ts +++ b/src/config/util.ts @@ -1,6 +1,6 @@ const debug = require('debug') -export function resolvePackage(id: string, paths: { paths: string[] }): string { +export function resolvePackage(id: string, paths: {paths: string[]}): string { return require.resolve(id, paths) } @@ -13,9 +13,10 @@ function displayWarnings() { } export function Debug(...scope: string[]): (..._: any) => void { - if (!debug) return (..._: any[]) => { - // noop - } + if (!debug) + return (..._: any[]) => { + // noop + } const d = debug(['config', ...scope].join(':')) if (d.enabled) displayWarnings() @@ -47,7 +48,7 @@ export function getPermutations(arr: string[]): Array { } export function getCommandIdPermutations(commandId: string): string[] { - return getPermutations(commandId.split(':')).flatMap(c => c.join(':')) + return getPermutations(commandId.split(':')).flatMap((c) => c.join(':')) } /** @@ -70,4 +71,4 @@ export function getCommandIdPermutations(commandId: string): string[] { * @returns string[] */ export const collectUsableIds = (commandIds: string[]): Set => - new Set(commandIds.flatMap(id => id.split(':').map((_, i, a) => a.slice(0, i + 1).join(':')))) + new Set(commandIds.flatMap((id) => id.split(':').map((_, i, a) => a.slice(0, i + 1).join(':')))) diff --git a/src/errors/errors/cli.ts b/src/errors/errors/cli.ts index b4aa7e869..cb1bca95f 100644 --- a/src/errors/errors/cli.ts +++ b/src/errors/errors/cli.ts @@ -10,9 +10,9 @@ import wrap from 'wrap-ansi' * properties specific to internal oclif error handling */ -export function addOclifExitCode(error: Record, options?: { exit?: number | false }): OclifError { +export function addOclifExitCode(error: Record, options?: {exit?: number | false}): OclifError { if (!('oclif' in error)) { - (error as unknown as OclifError).oclif = {} + ;(error as unknown as OclifError).oclif = {} } error.oclif.exit = options?.exit === undefined ? 2 : options.exit @@ -25,7 +25,7 @@ export class CLIError extends Error implements OclifError { code?: string suggestions?: string[] - constructor(error: string | Error, options: { exit?: number | false } & PrettyPrintableError = {}) { + constructor(error: string | Error, options: {exit?: number | false} & PrettyPrintableError = {}) { super(error instanceof Error ? error.message : error) addOclifExitCode(this, options) this.code = options.code diff --git a/src/errors/errors/pretty-print.ts b/src/errors/errors/pretty-print.ts index 3067501db..46c6efb4e 100644 --- a/src/errors/errors/pretty-print.ts +++ b/src/errors/errors/pretty-print.ts @@ -5,7 +5,7 @@ import indent from 'indent-string' import wrap from 'wrap-ansi' // These exist for backwards compatibility with CLIError -type CLIErrorDisplayOptions = { name?: string; bang?: string } +type CLIErrorDisplayOptions = {name?: string; bang?: string} export function applyPrettyPrintOptions(error: Error, options: PrettyPrintableError): PrettyPrintableError { const prettyErrorKeys: (keyof PrettyPrintableError)[] = ['message', 'code', 'ref', 'suggestions'] @@ -13,7 +13,7 @@ export function applyPrettyPrintOptions(error: Error, options: PrettyPrintableEr for (const key of prettyErrorKeys) { const applyOptionsKey = !(key in error) && options[key] if (applyOptionsKey) { - (error as any)[key] = options[key] + ;(error as any)[key] = options[key] } } @@ -25,7 +25,7 @@ const formatSuggestions = (suggestions?: string[]): string | undefined => { if (!suggestions || suggestions.length === 0) return undefined if (suggestions.length === 1) return `${label} ${suggestions[0]}` - const multiple = suggestions.map(suggestion => `* ${suggestion}`).join('\n') + const multiple = suggestions.map((suggestion) => `* ${suggestion}`).join('\n') return `${label}\n${indent(multiple, 2)}` } @@ -44,8 +44,8 @@ export default function prettyPrint(error: Error & PrettyPrintableError & CLIErr const formattedReference = ref ? `Reference: ${ref}` : undefined const formatted = [formattedHeader, formattedCode, formattedSuggestions, formattedReference] - .filter(Boolean) - .join('\n') + .filter(Boolean) + .join('\n') let output = wrap(formatted, errtermwidth - 6, {trim: false, hard: true} as any) output = indent(output, 3) diff --git a/src/errors/handle.ts b/src/errors/handle.ts index ab1ff2c2c..c359c0e41 100644 --- a/src/errors/handle.ts +++ b/src/errors/handle.ts @@ -40,9 +40,10 @@ export async function handle(err: ErrorToHandle): Promise { config.errorLogger.log(stack) } - await config.errorLogger.flush() - .then(() => Exit.exit(exitCode)) - .catch(console.error) + await config.errorLogger + .flush() + .then(() => Exit.exit(exitCode)) + .catch(console.error) } else Exit.exit(exitCode) } catch (error: any) { console.error(err.stack) diff --git a/src/errors/logger.ts b/src/errors/logger.ts index f2a79c329..83549ed32 100644 --- a/src/errors/logger.ts +++ b/src/errors/logger.ts @@ -4,10 +4,11 @@ import stripAnsi = require('strip-ansi') const timestamp = () => new Date().toISOString() let timer: any -const wait = (ms: number) => new Promise(resolve => { - if (timer) timer.unref() - timer = setTimeout(() => resolve(null), ms) -}) +const wait = (ms: number) => + new Promise((resolve) => { + if (timer) timer.unref() + timer = setTimeout(() => resolve(null), ms) + }) function chomp(s: string): string { if (s.endsWith('\n')) return s.replace(/\n$/, '') @@ -23,7 +24,7 @@ export class Logger { log(msg: string): void { msg = stripAnsi(chomp(msg)) - const lines = msg.split('\n').map(l => `${timestamp()} ${l}`.trimEnd()) + const lines = msg.split('\n').map((l) => `${timestamp()} ${l}`.trimEnd()) this.buffer.push(...lines) this.flush(50).catch(console.error) } diff --git a/src/execute.ts b/src/execute.ts index d5e95aa25..29180f46c 100644 --- a/src/execute.ts +++ b/src/execute.ts @@ -46,14 +46,12 @@ import {settings} from './settings' * })() * ``` */ -export async function execute( - options: { - dir: string; - args?: string[]; - loadOptions?: LoadOptions; - development?: boolean; - }, -): Promise { +export async function execute(options: { + dir: string + args?: string[] + loadOptions?: LoadOptions + development?: boolean +}): Promise { if (options.development) { // In dev mode -> use ts-node and dev plugins process.env.NODE_ENV = 'development' @@ -61,9 +59,9 @@ export async function execute( } return run(options.args ?? process.argv.slice(2), options.loadOptions ?? options.dir) - .then(async result => { - flush() - return result - }) - .catch(async error => handle(error)) + .then(async (result) => { + flush() + return result + }) + .catch(async (error) => handle(error)) } diff --git a/src/flags.ts b/src/flags.ts index 699dde32e..90ff5406d 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -5,41 +5,41 @@ import {CLIError} from './errors' import {URL} from 'node:url' import {loadHelpClass} from './help' -type NotArray = T extends Array ? never: T; +type NotArray = T extends Array ? never : T export function custom( defaults: Partial> & { - multiple: true; - } & ( - {required: true} | {default: OptionFlag['default']} - ), + multiple: true + } & ({required: true} | {default: OptionFlag['default']}), ): FlagDefinition export function custom( defaults: Partial, P>> & { - multiple?: false | undefined; - } & ( - {required: true} | {default: OptionFlag, P>['default']} - ), + multiple?: false | undefined + } & ({required: true} | {default: OptionFlag, P>['default']}), ): FlagDefinition export function custom( defaults: Partial, P>> & { - default?: OptionFlag, P>['default'] | undefined; - multiple?: false | undefined; - required?: false | undefined; + default?: OptionFlag, P>['default'] | undefined + multiple?: false | undefined + required?: false | undefined }, ): FlagDefinition export function custom( defaults: Partial> & { - multiple: true; - default?: OptionFlag['default'] | undefined; - required?: false | undefined; + multiple: true + default?: OptionFlag['default'] | undefined + required?: false | undefined }, ): FlagDefinition -export function custom(): FlagDefinition +export function custom(): FlagDefinition< + T, + P, + {multiple: false; requiredOrDefaulted: false} +> /** * Create a custom flag. * @@ -70,9 +70,7 @@ export function custom( }) } -export function boolean( - options: Partial> = {}, -): BooleanFlag { +export function boolean(options: Partial> = {}): BooleanFlag { return { parse: async (b, _) => b, ...options, @@ -81,10 +79,9 @@ export function boolean( } as BooleanFlag } -export const integer = custom({ +export const integer = custom({ async parse(input, _, opts) { - if (!/^-?\d+$/.test(input)) - throw new CLIError(`Expected an integer but received: ${input}`) + if (!/^-?\d+$/.test(input)) throw new CLIError(`Expected an integer but received: ${input}`) const num = Number.parseInt(input, 10) if (opts.min !== undefined && num < opts.min) throw new CLIError(`Expected an integer greater than or equal to ${opts.min} but received: ${input}`) @@ -126,64 +123,65 @@ export const url = custom({ export const string = custom() -export const version = (opts: Partial> = {}): BooleanFlag => boolean({ - description: 'Show CLI version.', - ...opts, - async parse(_, ctx) { - ctx.log(ctx.config.userAgent) - ctx.exit(0) - }, -}) +export const version = (opts: Partial> = {}): BooleanFlag => + boolean({ + description: 'Show CLI version.', + ...opts, + async parse(_, ctx) { + ctx.log(ctx.config.userAgent) + ctx.exit(0) + }, + }) -export const help = (opts: Partial> = {}): BooleanFlag => boolean({ - description: 'Show CLI help.', - ...opts, - async parse(_, cmd) { - const Help = await loadHelpClass(cmd.config) - await new Help(cmd.config, cmd.config.pjson.helpOptions).showHelp(cmd.id ? [cmd.id, ...cmd.argv] : cmd.argv) - cmd.exit(0) - }, -}) +export const help = (opts: Partial> = {}): BooleanFlag => + boolean({ + description: 'Show CLI help.', + ...opts, + async parse(_, cmd) { + const Help = await loadHelpClass(cmd.config) + await new Help(cmd.config, cmd.config.pjson.helpOptions).showHelp(cmd.id ? [cmd.id, ...cmd.argv] : cmd.argv) + cmd.exit(0) + }, + }) -type ElementType> = T[number]; +type ElementType> = T[number] export function option( defaults: Partial[], P>> & { - options: T; + options: T multiple: true } & ( - {required: true} | { - default: OptionFlag[], P>['default'] | undefined; - } - ), -): FlagDefinition + | {required: true} + | { + default: OptionFlag[], P>['default'] | undefined + } + ), +): FlagDefinition<(typeof defaults.options)[number], P, {multiple: true; requiredOrDefaulted: true}> export function option( defaults: Partial, P>> & { - options: T; - multiple?: false | undefined; - } & ( - {required: true} | {default: OptionFlag, P>['default']} - ), -): FlagDefinition + options: T + multiple?: false | undefined + } & ({required: true} | {default: OptionFlag, P>['default']}), +): FlagDefinition<(typeof defaults.options)[number], P, {multiple: false; requiredOrDefaulted: true}> export function option( defaults: Partial, P>> & { - options: T; - default?: OptionFlag, P>['default'] | undefined; - multiple?: false | undefined; - required?: false | undefined; + options: T + default?: OptionFlag, P>['default'] | undefined + multiple?: false | undefined + required?: false | undefined }, -): FlagDefinition +): FlagDefinition<(typeof defaults.options)[number], P, {multiple: false; requiredOrDefaulted: false}> export function option( defaults: Partial[], P>> & { - options: T; - multiple: true; - default?: OptionFlag[], P>['default'] | undefined; - required?: false | undefined; + options: T + multiple: true + default?: OptionFlag[], P>['default'] | undefined + required?: false | undefined }, -): FlagDefinition +): FlagDefinition<(typeof defaults.options)[number], P, {multiple: true; requiredOrDefaulted: false}> /** * Create a custom flag that infers the flag type from the provided options. @@ -199,7 +197,7 @@ export function option( */ export function option( defaults: Partial, P>> & {options: T}, -): FlagDefinition { +): FlagDefinition<(typeof defaults.options)[number], P, {multiple: boolean; requiredOrDefaulted: boolean}> { return (options: any = {}) => ({ parse: async (input, _ctx, _opts) => input, ...defaults, diff --git a/src/help/command.ts b/src/help/command.ts index 7faae8825..6eda6034b 100644 --- a/src/help/command.ts +++ b/src/help/command.ts @@ -11,9 +11,7 @@ import stripAnsi from 'strip-ansi' // split on any platform, not just the os specific EOL at runtime. const POSSIBLE_LINE_FEED = /\r\n|\n/ -let { - dim, -} = chalk +let {dim} = chalk if (process.env.ConEmuANSI === 'ON') { // eslint-disable-next-line unicorn/consistent-destructuring @@ -24,35 +22,46 @@ export class CommandHelp extends HelpFormatter { constructor( public command: Command.Class | Command.Loadable | Command.Cached, public config: Interfaces.Config, - public opts: Interfaces.HelpOptions) { + public opts: Interfaces.HelpOptions, + ) { super(config, opts) } generate(): string { const cmd = this.command - const flags = sortBy(Object.entries(cmd.flags || {}) - .filter(([, v]) => !v.hidden) - .map(([k, v]) => { - v.name = k - return v - }), f => [!f.char, f.char, f.name]) - - const args = Object.values(ensureArgObject(cmd.args)).filter(a => !a.hidden) - const output = compact(this.sections().map(({header, generate}) => { - const body = generate({cmd, flags, args}, header) - // Generate can return a list of sections - if (Array.isArray(body)) { - return body.map(helpSection => helpSection && helpSection.body && this.section(helpSection.header, helpSection.body)).join('\n\n') - } + const flags = sortBy( + Object.entries(cmd.flags || {}) + .filter(([, v]) => !v.hidden) + .map(([k, v]) => { + v.name = k + return v + }), + (f) => [!f.char, f.char, f.name], + ) + + const args = Object.values(ensureArgObject(cmd.args)).filter((a) => !a.hidden) + const output = compact( + this.sections().map(({header, generate}) => { + const body = generate({cmd, flags, args}, header) + // Generate can return a list of sections + if (Array.isArray(body)) { + return body + .map((helpSection) => helpSection && helpSection.body && this.section(helpSection.header, helpSection.body)) + .join('\n\n') + } - return body && this.section(header, body) - })).join('\n\n') + return body && this.section(header, body) + }), + ).join('\n\n') return output } - protected groupFlags(flags: Array): {mainFlags: Array; flagGroups: {[name: string]: Array}} { + protected groupFlags(flags: Array): { + mainFlags: Array + flagGroups: {[name: string]: Array} + } { const mainFlags: Array = [] - const flagGroups: { [index: string]: Array } = {} + const flagGroups: {[index: string]: Array} = {} for (const flag of flags) { const group = flag.helpGroup @@ -68,7 +77,7 @@ export class CommandHelp extends HelpFormatter { return {mainFlags, flagGroups} } - protected sections(): Array<{ header: string; generate: HelpSectionRenderer }> { + protected sections(): Array<{header: string; generate: HelpSectionRenderer}> { return [ { header: this.opts.usageHeader || 'USAGE', @@ -121,18 +130,21 @@ export class CommandHelp extends HelpFormatter { protected usage(): string { const {usage} = this.command const body = (usage ? castArray(usage) : [this.defaultUsage()]) - .map(u => { - const allowedSpacing = this.opts.maxWidth - this.indentSpacing - const line = `$ ${this.config.bin} ${u}`.trim() - if (line.length > allowedSpacing) { - const splitIndex = line.slice(0, Math.max(0, allowedSpacing)).lastIndexOf(' ') - return line.slice(0, Math.max(0, splitIndex)) + '\n' - + this.indent(this.wrap(line.slice(Math.max(0, splitIndex)), this.indentSpacing * 2)) - } + .map((u) => { + const allowedSpacing = this.opts.maxWidth - this.indentSpacing + const line = `$ ${this.config.bin} ${u}`.trim() + if (line.length > allowedSpacing) { + const splitIndex = line.slice(0, Math.max(0, allowedSpacing)).lastIndexOf(' ') + return ( + line.slice(0, Math.max(0, splitIndex)) + + '\n' + + this.indent(this.wrap(line.slice(Math.max(0, splitIndex)), this.indentSpacing * 2)) + ) + } - return this.wrap(line) - }) - .join('\n') + return this.wrap(line) + }) + .join('\n') return body } @@ -144,7 +156,10 @@ export class CommandHelp extends HelpFormatter { return compact([ this.command.id, - Object.values(this.command.args ?? {})?.filter(a => !a.hidden).map(a => this.arg(a)).join(' '), + Object.values(this.command.args ?? {}) + ?.filter((a) => !a.hidden) + .map((a) => this.arg(a)) + .join(' '), ]).join(' ') } @@ -156,10 +171,9 @@ export class CommandHelp extends HelpFormatter { description = (cmd.description || '').split(POSSIBLE_LINE_FEED).slice(1) } else if (cmd.description) { const summary = cmd.summary ? `${cmd.summary}\n` : null - description = summary ? [ - ...summary.split(POSSIBLE_LINE_FEED), - ...(cmd.description || '').split(POSSIBLE_LINE_FEED), - ] : (cmd.description || '').split(POSSIBLE_LINE_FEED) + description = summary + ? [...summary.split(POSSIBLE_LINE_FEED), ...(cmd.description || '').split(POSSIBLE_LINE_FEED)] + : (cmd.description || '').split(POSSIBLE_LINE_FEED) } if (description) { @@ -169,55 +183,56 @@ export class CommandHelp extends HelpFormatter { protected aliases(aliases: string[] | undefined): string | undefined { if (!aliases || aliases.length === 0) return - const body = aliases.map(a => ['$', this.config.bin, a].join(' ')).join('\n') + const body = aliases.map((a) => ['$', this.config.bin, a].join(' ')).join('\n') return body } protected examples(examples: Command.Example[] | undefined | string): string | undefined { if (!examples || examples.length === 0) return - const body = castArray(examples).map(a => { - let description - let commands - if (typeof a === 'string') { - const lines = a - .split(POSSIBLE_LINE_FEED) - .filter(Boolean) - // If the example is \n then format correctly - if (lines.length >= 2 && !this.isCommand(lines[0]) && lines.slice(1).every(i => this.isCommand(i))) { - description = lines[0] - commands = lines.slice(1) + const body = castArray(examples) + .map((a) => { + let description + let commands + if (typeof a === 'string') { + const lines = a.split(POSSIBLE_LINE_FEED).filter(Boolean) + // If the example is \n then format correctly + if (lines.length >= 2 && !this.isCommand(lines[0]) && lines.slice(1).every((i) => this.isCommand(i))) { + description = lines[0] + commands = lines.slice(1) + } else { + return lines.map((line) => this.formatIfCommand(line)).join('\n') + } } else { - return lines.map(line => this.formatIfCommand(line)).join('\n') + description = a.description + commands = [a.command] } - } else { - description = a.description - commands = [a.command] - } - const multilineSeparator - = this.config.platform === 'win32' - ? (this.config.shell.includes('powershell') ? '`' : '^') - : '\\' - - // The command will be indented in the section, which is also indented - const finalIndentedSpacing = this.indentSpacing * 2 - const multilineCommands = commands.map(c => - // First indent keeping room for escaped newlines - this.indent(this.wrap(this.formatIfCommand(c), finalIndentedSpacing + 4)) - // Then add the escaped newline - .split(POSSIBLE_LINE_FEED).join(` ${multilineSeparator}\n `), - ).join('\n') - - return `${this.wrap(description, finalIndentedSpacing)}\n\n${multilineCommands}` - }).join('\n\n') + const multilineSeparator = + this.config.platform === 'win32' ? (this.config.shell.includes('powershell') ? '`' : '^') : '\\' + + // The command will be indented in the section, which is also indented + const finalIndentedSpacing = this.indentSpacing * 2 + const multilineCommands = commands + .map((c) => + // First indent keeping room for escaped newlines + this.indent(this.wrap(this.formatIfCommand(c), finalIndentedSpacing + 4)) + // Then add the escaped newline + .split(POSSIBLE_LINE_FEED) + .join(` ${multilineSeparator}\n `), + ) + .join('\n') + + return `${this.wrap(description, finalIndentedSpacing)}\n\n${multilineCommands}` + }) + .join('\n\n') return body } protected args(args: Command.Arg.Any[]): [string, string | undefined][] | undefined { - if (args.filter(a => a.description).length === 0) return + if (args.filter((a) => a.description).length === 0) return - return args.map(a => { + return args.map((a) => { const name = a.name.toUpperCase() let description = a.description || '' if (a.default) description = `[default: ${a.default}] ${description}` @@ -266,7 +281,7 @@ export class CommandHelp extends HelpFormatter { protected flags(flags: Array): [string, string | undefined][] | undefined { if (flags.length === 0) return - return flags.map(flag => { + return flags.map((flag) => { const left = this.flagHelpLabel(flag) let right = flag.summary || flag.description || '' @@ -285,16 +300,21 @@ export class CommandHelp extends HelpFormatter { } protected flagsDescriptions(flags: Array): string | undefined { - const flagsWithExtendedDescriptions = flags.filter(flag => flag.summary && flag.description) + const flagsWithExtendedDescriptions = flags.filter((flag) => flag.summary && flag.description) if (flagsWithExtendedDescriptions.length === 0) return - const body = flagsWithExtendedDescriptions.map(flag => { - // Guaranteed to be set because of the filter above, but make ts happy - const summary = flag.summary || '' - let flagHelp = this.flagHelpLabel(flag, true) - flagHelp += flagHelp.length + summary.length + 2 < this.opts.maxWidth ? ' ' + summary : '\n\n' + this.indent(this.wrap(summary, this.indentSpacing * 2)) - return `${flagHelp}\n\n${this.indent(this.wrap(flag.description || '', this.indentSpacing * 2))}` - }).join('\n\n') + const body = flagsWithExtendedDescriptions + .map((flag) => { + // Guaranteed to be set because of the filter above, but make ts happy + const summary = flag.summary || '' + let flagHelp = this.flagHelpLabel(flag, true) + flagHelp += + flagHelp.length + summary.length + 2 < this.opts.maxWidth + ? ' ' + summary + : '\n\n' + this.indent(this.wrap(summary, this.indentSpacing * 2)) + return `${flagHelp}\n\n${this.indent(this.wrap(flag.description || '', this.indentSpacing * 2))}` + }) + .join('\n\n') return body } diff --git a/src/help/docopts.ts b/src/help/docopts.ts index 3ee8f8299..247854fa9 100644 --- a/src/help/docopts.ts +++ b/src/help/docopts.ts @@ -65,11 +65,11 @@ export class DocOpts { // Create a new map with references to the flags that we can manipulate. this.flagMap = {} this.flagList = Object.entries(cmd.flags || {}) - .filter(([_, flag]) => !flag.hidden) - .map(([name, flag]) => { - this.flagMap[name] = flag - return flag - }) + .filter(([_, flag]) => !flag.hidden) + .map(([name, flag]) => { + this.flagMap[name] = flag + return flag + }) } public static generate(cmd: Command.Class | Command.Loadable | Command.Cached): string { @@ -79,7 +79,10 @@ export class DocOpts { public toString(): string { const opts = this.cmd.id === '.' || this.cmd.id === '' ? [] : ['<%= command.id %>'] if (this.cmd.args) { - const a = Object.values(ensureArgObject(this.cmd.args)).map(arg => arg.required ? arg.name.toUpperCase() : `[${arg.name.toUpperCase()}]`) || [] + const a = + Object.values(ensureArgObject(this.cmd.args)).map((arg) => + arg.required ? arg.name.toUpperCase() : `[${arg.name.toUpperCase()}]`, + ) || [] opts.push(...a) } @@ -87,11 +90,13 @@ export class DocOpts { opts.push(...Object.values(this.groupFlagElements())) } catch { // If there is an error, just return no usage so we don't fail command help. - opts.push(...this.flagList.map(flag => { - const name = flag.char ? `-${flag.char}` : `--${flag.name}` - if (flag.type === 'boolean') return name - return `${name}=` - })) + opts.push( + ...this.flagList.map((flag) => { + const name = flag.char ? `-${flag.char}` : `--${flag.name}` + if (flag.type === 'boolean') return name + return `${name}=` + }), + ) } return opts.join(' ') @@ -102,9 +107,15 @@ export class DocOpts { // Generate all doc opt elements for combining // Show required flags first - this.generateElements(elementMap, this.flagList.filter(flag => flag.required)) + this.generateElements( + elementMap, + this.flagList.filter((flag) => flag.required), + ) // Then show optional flags - this.generateElements(elementMap, this.flagList.filter(flag => !flag.required)) + this.generateElements( + elementMap, + this.flagList.filter((flag) => !flag.required), + ) for (const flag of this.flagList) { if (Array.isArray(flag.dependsOn)) { diff --git a/src/help/formatter.ts b/src/help/formatter.ts index ad039f851..a2d09239f 100644 --- a/src/help/formatter.ts +++ b/src/help/formatter.ts @@ -10,8 +10,13 @@ import width from 'string-width' import wrap from 'wrap-ansi' export type HelpSectionKeyValueTable = {name: string; description: string}[] -export type HelpSection = {header: string; body: string | HelpSectionKeyValueTable | [string, string | undefined][] | undefined} | undefined; -export type HelpSectionRenderer = (data: {cmd: Command.Class | Command.Loadable | Command.Cached; flags: Command.Flag.Any[]; args: Command.Arg.Any[]}, header: string) => HelpSection | HelpSection[] | string | undefined; +export type HelpSection = + | {header: string; body: string | HelpSectionKeyValueTable | [string, string | undefined][] | undefined} + | undefined +export type HelpSectionRenderer = ( + data: {cmd: Command.Class | Command.Loadable | Command.Cached; flags: Command.Flag.Any[]; args: Command.Arg.Any[]}, + header: string, +) => HelpSection | HelpSection[] | string | undefined export class HelpFormatter { indentSpacing = 2 @@ -101,7 +106,10 @@ export class HelpFormatter { return indent(body, spacing) } - public renderList(input: (string | undefined)[][], opts: {indentation: number; multiline?: boolean; stripAnsi?: boolean; spacer?: string}): string { + public renderList( + input: (string | undefined)[][], + opts: {indentation: number; multiline?: boolean; stripAnsi?: boolean; spacer?: string}, + ): string { if (input.length === 0) { return '' } @@ -128,7 +136,7 @@ export class HelpFormatter { } if (opts.multiline) return renderMultiline() - const maxLength = widestLine(input.map(i => i[0]).join('\n')) + const maxLength = widestLine(input.map((i) => i[0]).join('\n')) let output = '' let spacer = opts.spacer || '\n' let cur = '' @@ -149,7 +157,7 @@ export class HelpFormatter { if (opts.stripAnsi) right = stripAnsi(right) right = this.wrap(right.trim(), opts.indentation + maxLength + 2) - const [first, ...lines] = right!.split('\n').map(s => s.trim()) + const [first, ...lines] = right!.split('\n').map((s) => s.trim()) cur += ' '.repeat(maxLength - width(cur) + 2) cur += first if (lines.length === 0) { @@ -172,32 +180,37 @@ export class HelpFormatter { return output.trim() } - public section(header: string, body: string | HelpSection | HelpSectionKeyValueTable | [string, string | undefined][]): string { + public section( + header: string, + body: string | HelpSection | HelpSectionKeyValueTable | [string, string | undefined][], + ): string { // Always render template strings with the provided render function before wrapping and indenting let newBody: any if (typeof body! === 'string') { newBody = this.render(body!) } else if (Array.isArray(body)) { - newBody = (body! as [string, string | undefined | HelpSectionKeyValueTable][]).map(entry => { + newBody = (body! as [string, string | undefined | HelpSectionKeyValueTable][]).map((entry) => { if ('name' in entry) { const tableEntry = entry as unknown as {name: string; description: string} - return ([this.render(tableEntry.name), this.render(tableEntry.description)]) + return [this.render(tableEntry.name), this.render(tableEntry.description)] } const [left, right] = entry - return ([this.render(left), right && this.render(right as string)]) + return [this.render(left), right && this.render(right as string)] }) } else if ('header' in body!) { return this.section(body!.header, body!.body) } else { newBody = (body! as unknown as HelpSectionKeyValueTable) - .map((entry: { name: string; description: string }) => ([entry.name, entry.description])) - .map(([left, right]) => ([this.render(left), right && this.render(right)])) + .map((entry: {name: string; description: string}) => [entry.name, entry.description]) + .map(([left, right]) => [this.render(left), right && this.render(right)]) } const output = [ chalk.bold(header), - this.indent(Array.isArray(newBody) ? this.renderList(newBody, {stripAnsi: this.opts.stripAnsi, indentation: 2}) : newBody), + this.indent( + Array.isArray(newBody) ? this.renderList(newBody, {stripAnsi: this.opts.stripAnsi, indentation: 2}) : newBody, + ), ].join('\n') return this.opts.stripAnsi ? stripAnsi(output) : output } diff --git a/src/help/index.ts b/src/help/index.ts index 1a8d1f076..24bb92f06 100644 --- a/src/help/index.ts +++ b/src/help/index.ts @@ -36,14 +36,14 @@ export abstract class HelpBase extends HelpFormatter { * Show help, used in multi-command CLIs * @param args passed into your command, useful for determining which type of help to display */ - public abstract showHelp(argv: string[]): Promise; + public abstract showHelp(argv: string[]): Promise /** * Show help for an individual command * @param command * @param topics */ - public abstract showCommandHelp(command: Command.Class, topics: Interfaces.Topic[]): Promise; + public abstract showCommandHelp(command: Command.Class, topics: Interfaces.Topic[]): Promise } export class Help extends HelpBase { @@ -58,7 +58,7 @@ export class Help extends HelpBase { private get _topics(): Interfaces.Topic[] { return this.config.topics.filter((topic: Interfaces.Topic) => { // it is assumed a topic has a child if it has children - const hasChild = this.config.topics.some(subTopic => subTopic.name.includes(`${topic.name}:`)) + const hasChild = this.config.topics.some((subTopic) => subTopic.name.includes(`${topic.name}:`)) return hasChild }) } @@ -66,18 +66,18 @@ export class Help extends HelpBase { protected get sortedCommands(): Command.Loadable[] { let {commands} = this.config - commands = commands.filter(c => this.opts.all || !c.hidden) - commands = sortBy(commands, c => c.id) - commands = uniqBy(commands, c => c.id) + commands = commands.filter((c) => this.opts.all || !c.hidden) + commands = sortBy(commands, (c) => c.id) + commands = uniqBy(commands, (c) => c.id) return commands } protected get sortedTopics(): Interfaces.Topic[] { let topics = this._topics - topics = topics.filter(t => this.opts.all || !t.hidden) - topics = sortBy(topics, t => t.name) - topics = uniqBy(topics, t => t.name) + topics = topics.filter((t) => this.opts.all || !t.hidden) + topics = sortBy(topics, (t) => t.name) + topics = uniqBy(topics, (t) => t.name) return topics } @@ -88,7 +88,7 @@ export class Help extends HelpBase { public async showHelp(argv: string[]): Promise { const originalArgv = argv.slice(1) - argv = argv.filter(arg => !getHelpFlagAdditions(this.config).includes(arg)) + argv = argv.filter((arg) => !getHelpFlagAdditions(this.config).includes(arg)) if (this.config.topicSeparator !== ':') argv = standardizeIDFromArgv(argv, this.config) const subject = getHelpSubject(argv, this.config) @@ -134,7 +134,7 @@ export class Help extends HelpBase { if (matches.length > 0) { const result = await this.config.runHook('command_incomplete', { id: subject, - argv: originalArgv.filter(o => !subject.split(':').includes(o)), + argv: originalArgv.filter((o) => !subject.split(':').includes(o)), matches, }) if (result.successes.length > 0) return @@ -148,8 +148,12 @@ export class Help extends HelpBase { const name = command.id const depth = name.split(':').length - const subTopics = this.sortedTopics.filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1) - const subCommands = this.sortedCommands.filter(c => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1) + const subTopics = this.sortedTopics.filter( + (t) => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1, + ) + const subCommands = this.sortedCommands.filter( + (c) => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1, + ) const plugin = this.config.plugins.get(command.pluginName!) const state = this.config.pjson?.oclif?.state || plugin?.pjson?.oclif?.state || command.state @@ -157,11 +161,17 @@ export class Help extends HelpBase { if (state) { this.log( state === 'deprecated' - ? `${formatCommandDeprecationWarning(toConfiguredId(name, this.config), command.deprecationOptions)}` + ? `${formatCommandDeprecationWarning(toConfiguredId(name, this.config), command.deprecationOptions)}\n` : `This command is in ${state}.\n`, ) } + if (command.deprecateAliases && command.aliases.includes(name)) { + const actualCmd = this.config.commands.find((c) => c.aliases.includes(name)) + const opts = {...command.deprecationOptions, ...(actualCmd ? {to: actualCmd.id} : {})} + this.log(`${formatCommandDeprecationWarning(toConfiguredId(name, this.config), opts)}\n`) + } + const summary = this.summary(command) if (summary) { this.log(summary + '\n') @@ -176,8 +186,8 @@ export class Help extends HelpBase { } if (subCommands.length > 0) { - const aliases:string[] = [] - const uniqueSubCommands: Command.Loadable[] = subCommands.filter(p => { + const aliases: string[] = [] + const uniqueSubCommands: Command.Loadable[] = subCommands.filter((p) => { aliases.push(...p.aliases) return !aliases.includes(p.id) }) @@ -192,19 +202,15 @@ export class Help extends HelpBase { const state = this.config.pjson?.oclif?.state if (state) { - this.log( - state === 'deprecated' - ? `${this.config.bin} is deprecated` - : `${this.config.bin} is in ${state}.\n`, - ) + this.log(state === 'deprecated' ? `${this.config.bin} is deprecated` : `${this.config.bin} is in ${state}.\n`) } this.log(this.formatRoot()) this.log('') if (!this.opts.all) { - rootTopics = rootTopics.filter(t => !t.name.includes(':')) - rootCommands = rootCommands.filter(c => !c.id.includes(':')) + rootTopics = rootTopics.filter((t) => !t.name.includes(':')) + rootCommands = rootCommands.filter((c) => !c.id.includes(':')) } if (rootTopics.length > 0) { @@ -213,7 +219,7 @@ export class Help extends HelpBase { } if (rootCommands.length > 0) { - rootCommands = rootCommands.filter(c => c.id) + rootCommands = rootCommands.filter((c) => c.id) this.log(this.formatCommands(rootCommands)) this.log('') } @@ -223,8 +229,12 @@ export class Help extends HelpBase { const {name} = topic const depth = name.split(':').length - const subTopics = this.sortedTopics.filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1) - const commands = this.sortedCommands.filter(c => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1) + const subTopics = this.sortedTopics.filter( + (t) => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1, + ) + const commands = this.sortedCommands.filter( + (c) => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1, + ) const state = this.config.pjson?.oclif?.state if (state) this.log(`This topic is in ${state}.\n`) @@ -250,7 +260,7 @@ export class Help extends HelpBase { protected formatCommand(command: Command.Class | Command.Loadable | Command.Cached): string { if (this.config.topicSeparator !== ':') { command.id = command.id.replaceAll(':', this.config.topicSeparator) - command.aliases = command.aliases && command.aliases.map(a => a.replaceAll(':', this.config.topicSeparator)) + command.aliases = command.aliases && command.aliases.map((a) => a.replaceAll(':', this.config.topicSeparator)) } const help = this.getCommandHelpClass(command) @@ -264,17 +274,17 @@ export class Help extends HelpBase { protected formatCommands(commands: Array): string { if (commands.length === 0) return '' - const body = this.renderList(commands.map(c => { - if (this.config.topicSeparator !== ':') c.id = c.id.replaceAll(':', this.config.topicSeparator) - return [ - c.id, - this.summary(c), - ] - }), { - spacer: '\n', - stripAnsi: this.opts.stripAnsi, - indentation: 2, - }) + const body = this.renderList( + commands.map((c) => { + if (this.config.topicSeparator !== ':') c.id = c.id.replaceAll(':', this.config.topicSeparator) + return [c.id, this.summary(c)] + }), + { + spacer: '\n', + stripAnsi: this.opts.stripAnsi, + indentation: 2, + }, + ) return this.section('COMMANDS', body) } @@ -311,17 +321,17 @@ export class Help extends HelpBase { protected formatTopics(topics: Interfaces.Topic[]): string { if (topics.length === 0) return '' - const body = this.renderList(topics.map(c => { - if (this.config.topicSeparator !== ':') c.name = c.name.replaceAll(':', this.config.topicSeparator) - return [ - c.name, - c.description && this.render(c.description.split('\n')[0]), - ] - }), { - spacer: '\n', - stripAnsi: this.opts.stripAnsi, - indentation: 2, - }) + const body = this.renderList( + topics.map((c) => { + if (this.config.topicSeparator !== ':') c.name = c.name.replaceAll(':', this.config.topicSeparator) + return [c.name, c.description && this.render(c.description.split('\n')[0])] + }), + { + spacer: '\n', + stripAnsi: this.opts.stripAnsi, + indentation: 2, + }, + ) return this.section('TOPICS', body) } @@ -335,7 +345,7 @@ export class Help extends HelpBase { } interface HelpBaseDerived { - new(config: Interfaces.Config, opts?: Partial): HelpBase; + new (config: Interfaces.Config, opts?: Partial): HelpBase } function extractClass(exported: any): HelpBaseDerived { @@ -348,10 +358,12 @@ export async function loadHelpClass(config: Interfaces.Config): Promise ids.has(id) - const finalizeId = (s?: string) => s ? [...final, s].join(':') : final.join(':') + const finalizeId = (s?: string) => (s ? [...final, s].join(':') : final.join(':')) const hasArgs = () => { const id = finalizeId() diff --git a/src/interfaces/config.ts b/src/interfaces/config.ts index df7eebb5e..c476c9525 100644 --- a/src/interfaces/config.ts +++ b/src/interfaces/config.ts @@ -9,137 +9,142 @@ export type PlatformTypes = NodeJS.Platform | 'wsl' export type ArchTypes = 'arm' | 'arm64' | 'mips' | 'mipsel' | 'ppc' | 'ppc64' | 's390' | 's390x' | 'x32' | 'x64' | 'x86' export type PluginVersionDetail = { - version: string; - type: string; + version: string + type: string root: string -}; +} export type VersionDetails = { - cliVersion: string; - architecture: string; - nodeVersion: string; - pluginVersions?: Record; - osVersion?: string; - shell?: string; - rootPath?: string; + cliVersion: string + architecture: string + nodeVersion: string + pluginVersions?: Record + osVersion?: string + shell?: string + rootPath?: string } export interface Config { - readonly name: string; - readonly version: string; - readonly channel: string; - readonly pjson: PJSON.CLI; - readonly root: string; + readonly name: string + readonly version: string + readonly channel: string + readonly pjson: PJSON.CLI + readonly root: string /** * process.arch */ - readonly arch: ArchTypes; + readonly arch: ArchTypes /** * bin name of CLI command */ - readonly bin: string; + readonly bin: string /** * cache directory to use for CLI * * example ~/Library/Caches/mycli or ~/.cache/mycli */ - readonly cacheDir: string; + readonly cacheDir: string /** * config directory to use for CLI * * example: ~/.config/mycli */ - readonly configDir: string; + readonly configDir: string /** * data directory to use for CLI * * example: ~/.local/share/mycli */ - readonly dataDir: string; + readonly dataDir: string /** * base dirname to use in cacheDir/configDir/dataDir */ - readonly dirname: string; + readonly dirname: string /** * points to a file that should be appended to for error logs * * example: ~/Library/Caches/mycli/error.log */ - readonly errlog: string; + readonly errlog: string /** * path to home directory * * example: /home/myuser */ - readonly home: string; + readonly home: string /** * process.platform */ - readonly platform: PlatformTypes; + readonly platform: PlatformTypes /** * active shell */ - readonly shell: string; + readonly shell: string /** * user agent to use for http calls * * example: mycli/1.2.3 (darwin-x64) node-9.0.0 */ - readonly userAgent: string; + readonly userAgent: string /** * if windows */ - readonly windows: boolean; + readonly windows: boolean /** * debugging level * * set by ${BIN}_DEBUG or DEBUG=$BIN */ - readonly debug: number; + readonly debug: number /** * npm registry to use for installing plugins */ - readonly npmRegistry?: string; - readonly plugins: Map; - readonly binPath?: string; + readonly npmRegistry?: string + readonly plugins: Map + readonly binPath?: string /** * name of any bin aliases that will execute the cli */ - readonly binAliases?: string[]; - readonly nsisCustomization?: string; - readonly valid: boolean; - readonly flexibleTaxonomy?: boolean; - topicSeparator: ':' | ' '; - readonly commands: Command.Loadable[]; - readonly topics: Topic[]; - readonly commandIDs: string[]; + readonly binAliases?: string[] + readonly nsisCustomization?: string + readonly valid: boolean + readonly flexibleTaxonomy?: boolean + topicSeparator: ':' | ' ' + readonly commands: Command.Loadable[] + readonly topics: Topic[] + readonly commandIDs: string[] readonly versionDetails: VersionDetails - runCommand(id: string, argv?: string[], cachedCommand?: Command.Loadable): Promise; - runHook(event: T, opts: Hooks[T]['options'], timeout?: number, captureErrors?: boolean): Promise>; + runCommand(id: string, argv?: string[], cachedCommand?: Command.Loadable): Promise + runHook( + event: T, + opts: Hooks[T]['options'], + timeout?: number, + captureErrors?: boolean, + ): Promise> getAllCommandIDs(): string[] getAllCommands(): Command.Loadable[] - findCommand(id: string, opts: { must: true }): Command.Loadable; - findCommand(id: string, opts?: { must: boolean }): Command.Loadable | undefined; - findTopic(id: string, opts: { must: true }): Topic; - findTopic(id: string, opts?: { must: boolean }): Topic | undefined; - findMatches(id: string, argv: string[]): Command.Loadable[]; - scopedEnvVar(key: string): string | undefined; - scopedEnvVarKey(key: string): string; - scopedEnvVarKeys(key: string): string[]; - scopedEnvVarTrue(key: string): boolean; - s3Url(key: string): string; - s3Key(type: 'versioned' | 'unversioned', ext: '.tar.gz' | '.tar.xz', options?: Config.s3Key.Options): string; - s3Key(type: keyof PJSON.S3.Templates, options?: Config.s3Key.Options): string; - getPluginsList(): Plugin[]; + findCommand(id: string, opts: {must: true}): Command.Loadable + findCommand(id: string, opts?: {must: boolean}): Command.Loadable | undefined + findTopic(id: string, opts: {must: true}): Topic + findTopic(id: string, opts?: {must: boolean}): Topic | undefined + findMatches(id: string, argv: string[]): Command.Loadable[] + scopedEnvVar(key: string): string | undefined + scopedEnvVarKey(key: string): string + scopedEnvVarKeys(key: string): string[] + scopedEnvVarTrue(key: string): boolean + s3Url(key: string): string + s3Key(type: 'versioned' | 'unversioned', ext: '.tar.gz' | '.tar.xz', options?: Config.s3Key.Options): string + s3Key(type: keyof PJSON.S3.Templates, options?: Config.s3Key.Options): string + getPluginsList(): Plugin[] } export namespace Config { export namespace s3Key { export interface Options { - platform?: PlatformTypes; - arch?: ArchTypes; - [key: string]: any; + platform?: PlatformTypes + arch?: ArchTypes + [key: string]: any } } } diff --git a/src/interfaces/errors.ts b/src/interfaces/errors.ts index e0c083e3a..4486b7561 100644 --- a/src/interfaces/errors.ts +++ b/src/interfaces/errors.ts @@ -1,30 +1,30 @@ -export type CommandError = Error & {exitCode?: number}; +export type CommandError = Error & {exitCode?: number} export interface OclifError { oclif: { - exit?: number; - }; + exit?: number + } } export interface PrettyPrintableError { /** * message to display related to the error */ - message?: string; + message?: string /** * a unique error code for this error class */ - code?: string; + code?: string /** * a url to find out more information related to this error * or fixing the error */ - ref?: string; + ref?: string /** * a suggestion that may be useful or provide additional context */ - suggestions?: string[]; + suggestions?: string[] } diff --git a/src/interfaces/flags.ts b/src/interfaces/flags.ts index e8005e858..0fc8c4bb6 100644 --- a/src/interfaces/flags.ts +++ b/src/interfaces/flags.ts @@ -30,4 +30,4 @@ import {FlagInput} from './parser' * } * } */ -export type InferredFlags = T extends FlagInput ? F & { json: boolean | undefined; } : unknown +export type InferredFlags = T extends FlagInput ? F & {json: boolean | undefined} : unknown diff --git a/src/interfaces/help.ts b/src/interfaces/help.ts index 970aa9bea..8dacd3e70 100644 --- a/src/interfaces/help.ts +++ b/src/interfaces/help.ts @@ -1,20 +1,20 @@ export interface HelpOptions { - all?: boolean; - maxWidth: number; - stripAnsi?: boolean; + all?: boolean + maxWidth: number + stripAnsi?: boolean /** * By default, option values on flags are shown in the flag's description. This is because * long options list ruin the formatting of help. If a CLI knows all commands will not * do this, it can be turned off at a help level using this property. An individual flag * can set this using `flag.helpValue=options.join('|')`. */ - showFlagOptionsInTitle?: boolean; + showFlagOptionsInTitle?: boolean /** * By default, titles show flag values as ``. Some CLI developers may prefer titles * to show the flag name as the value. i.e. `--myflag=myflag` instead of `--myflag=`. * An individual flag can set this using `flag.helpValue=flag.name`. */ - showFlagNameInTitle?: boolean; + showFlagNameInTitle?: boolean /** * By default, the command summary is show at the top of the help and as the first line in * the command description. Repeating the summary in the command description improves readability @@ -22,14 +22,14 @@ export interface HelpOptions { * the description is treated as the summary. Some CLIs, especially with very simple commands, may * not want the duplication. */ - hideCommandSummaryInDescription?: boolean; + hideCommandSummaryInDescription?: boolean /** * Use USAGE, but some may want to use USAGE as used in gnu man pages. See help recommendations at * http://www.gnu.org/software/help2man/#--help-recommendations */ - usageHeader?: string; + usageHeader?: string /** * Use docopts as the usage. Defaults to true. */ - docopts?: boolean; + docopts?: boolean } diff --git a/src/interfaces/hooks.ts b/src/interfaces/hooks.ts index 928068c14..a6b526a5e 100644 --- a/src/interfaces/hooks.ts +++ b/src/interfaces/hooks.ts @@ -3,57 +3,60 @@ import {Config} from './config' import {Plugin} from './plugin' interface HookMeta { - options: Record; - return: any; + options: Record + return: any } export interface Hooks { - [event: string]: HookMeta; + [event: string]: HookMeta init: { - options: { id: string | undefined; argv: string[] }; - return: void; - }; + options: {id: string | undefined; argv: string[]} + return: void + } prerun: { - options: { Command: Command.Class; argv: string[] }; - return: void; - }; + options: {Command: Command.Class; argv: string[]} + return: void + } postrun: { options: { - Command: Command.Class; - result?: any; - argv: string[]; - }; - return: void; - }; + Command: Command.Class + result?: any + argv: string[] + } + return: void + } preupdate: { - options: {channel: string, version: string}; - return: void; - }; + options: {channel: string; version: string} + return: void + } update: { - options: {channel: string, version: string}; - return: void; - }; - 'command_not_found': { - options: {id: string; argv?: string[]}; - return: unknown; - }; - 'command_incomplete': { - options: {id: string; argv: string[], matches: Command.Loadable[]}; - return: unknown; - }; - 'jit_plugin_not_installed': { - options: {id: string; argv: string[]; command: Command.Loadable, pluginName: string; pluginVersion: string}; - return: unknown; - }; + options: {channel: string; version: string} + return: void + } + command_not_found: { + options: {id: string; argv?: string[]} + return: unknown + } + command_incomplete: { + options: {id: string; argv: string[]; matches: Command.Loadable[]} + return: unknown + } + jit_plugin_not_installed: { + options: {id: string; argv: string[]; command: Command.Loadable; pluginName: string; pluginVersion: string} + return: unknown + } 'plugins:preinstall': { options: { - plugin: { name: string; tag: string; type: 'npm' } | { url: string; type: 'repo' }; - }; - return: void; - }; + plugin: {name: string; tag: string; type: 'npm'} | {url: string; type: 'repo'} + } + return: void + } } -export type Hook = (this: Hook.Context, options: P[T]['options'] & {config: Config}) => Promise +export type Hook = ( + this: Hook.Context, + options: P[T]['options'] & {config: Config}, +) => Promise export namespace Hook { export type Init = Hook<'init'> @@ -67,17 +70,16 @@ export namespace Hook { export type JitPluginNotInstalled = Hook<'jit_plugin_not_installed'> export interface Context { - config: Config; - exit(code?: number): void; - error(message: string | Error, options?: {code?: string; exit?: number}): void; - warn(message: string): void; - log(message?: any, ...args: any[]): void; - debug(...args: any[]): void; + config: Config + exit(code?: number): void + error(message: string | Error, options?: {code?: string; exit?: number}): void + warn(message: string): void + log(message?: any, ...args: any[]): void + debug(...args: any[]): void } export interface Result { - successes: Array<{ result: T; plugin: Plugin }>; - failures: Array<{ error: Error; plugin: Plugin }>; + successes: Array<{result: T; plugin: Plugin}> + failures: Array<{error: Error; plugin: Plugin}> } } - diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 76e7ca5b4..bd22984ac 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -5,15 +5,7 @@ export type {HelpOptions} from './help' export type {Hook, Hooks} from './hooks' export type {Manifest} from './manifest' export type {S3Manifest} from './s3-manifest' -export type { - Arg, - BooleanFlag, - CustomOptions, - Deprecation, - Flag, - FlagDefinition, - OptionFlag, -} from './parser' +export type {Arg, BooleanFlag, CustomOptions, Deprecation, Flag, FlagDefinition, OptionFlag} from './parser' export type {PJSON} from './pjson' export type {Plugin, PluginOptions, Options} from './plugin' export type {Topic} from './topic' diff --git a/src/interfaces/manifest.ts b/src/interfaces/manifest.ts index 354a0d8ba..936068768 100644 --- a/src/interfaces/manifest.ts +++ b/src/interfaces/manifest.ts @@ -1,6 +1,6 @@ import {Command} from '../command' export type Manifest = { - version: string; - commands: {[id: string]: Command.Cached}; + version: string + commands: {[id: string]: Command.Cached} } diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts index a201323d4..07c472891 100644 --- a/src/interfaces/parser.ts +++ b/src/interfaces/parser.ts @@ -1,48 +1,48 @@ import {AlphabetLowercase, AlphabetUppercase} from './alphabet' import {Command} from '../command' -export type FlagOutput = { [name: string]: any } -export type ArgOutput = { [name: string]: any } +export type FlagOutput = {[name: string]: any} +export type ArgOutput = {[name: string]: any} export type CLIParseErrorOptions = { parse: { - input?: ParserInput; - output?: ParserOutput; - }; + input?: ParserInput + output?: ParserOutput + } } -export type OutputArgs = { [P in keyof T]: any } -export type OutputFlags = { [P in keyof T]: any } +export type OutputArgs = {[P in keyof T]: any} +export type OutputFlags = {[P in keyof T]: any} export type ParserOutput< TFlags extends OutputFlags = any, BFlags extends OutputFlags = any, - TArgs extends OutputFlags = any + TArgs extends OutputFlags = any, > = { // Add in the --json flag so that it shows up in the types. // This is necessary because there's no way to optionally add the json flag based // on wether enableJsonFlag is set in the command. - flags: TFlags & BFlags & { json: boolean | undefined }; - args: TArgs; - argv: unknown[]; - raw: ParsingToken[]; - metadata: Metadata; - nonExistentFlags: string[]; + flags: TFlags & BFlags & {json: boolean | undefined} + args: TArgs + argv: unknown[] + raw: ParsingToken[] + metadata: Metadata + nonExistentFlags: string[] } -export type ArgToken = { type: 'arg'; arg: string; input: string } -export type FlagToken = { type: 'flag'; flag: string; input: string } +export type ArgToken = {type: 'arg'; arg: string; input: string} +export type FlagToken = {type: 'flag'; flag: string; input: string} export type ParsingToken = ArgToken | FlagToken -export type FlagUsageOptions = { displayRequired?: boolean } +export type FlagUsageOptions = {displayRequired?: boolean} export type Metadata = { - flags: { [key: string]: MetadataFlag }; + flags: {[key: string]: MetadataFlag} } export type MetadataFlag = { - setFromDefault?: boolean; - defaultHelp?: unknown; + setFromDefault?: boolean + defaultHelp?: unknown } export type ListItem = [string, string | undefined] @@ -51,8 +51,8 @@ export type List = ListItem[] export type CustomOptions = Record export type DefaultContext = { - options: T; - flags: Record; + options: T + flags: Record } /** @@ -68,7 +68,9 @@ export type FlagDefault = T | ((context: DefaultContext

= T | ((context: DefaultContext

>) => Promise) +export type FlagDefaultHelp = + | T + | ((context: DefaultContext

>) => Promise) /** * Type to define a default value for an arg. @@ -80,83 +82,85 @@ export type ArgDefault = T | ((context: DefaultContext = T | ((context: DefaultContext>) => Promise) +export type ArgDefaultHelp = + | T + | ((context: DefaultContext>) => Promise) -export type FlagRelationship = string | {name: string; when: (flags: Record) => Promise}; +export type FlagRelationship = string | {name: string; when: (flags: Record) => Promise} export type Relationship = { - type: 'all' | 'some' | 'none'; - flags: FlagRelationship[]; + type: 'all' | 'some' | 'none' + flags: FlagRelationship[] } export type Deprecation = { - to?: string; - message?: string; - version?: string | number; + to?: string + message?: string + version?: string | number } export type FlagProps = { - name: string; - char?: AlphabetLowercase | AlphabetUppercase; + name: string + char?: AlphabetLowercase | AlphabetUppercase /** * A short summary of flag usage to show in the flag list. * If not provided, description will be used. */ - summary?: string; + summary?: string /** * A description of flag usage. If summary is provided, the description * is assumed to be a longer description and will be shown in a separate * section within help. */ - description?: string; + description?: string /** * The flag label to show in help. Defaults to "[-] --" where - is * only displayed if the char is defined. */ - helpLabel?: string; + helpLabel?: string /** * Shows this flag in a separate list in the help. */ - helpGroup?: string; + helpGroup?: string /** * Accept an environment variable as input */ - env?: string; + env?: string /** * If true, the flag will not be shown in the help. */ - hidden?: boolean; + hidden?: boolean /** * If true, the flag will be required. */ - required?: boolean; + required?: boolean /** * List of flags that this flag depends on. */ - dependsOn?: string[]; + dependsOn?: string[] /** * List of flags that cannot be used with this flag. */ - exclusive?: string[]; + exclusive?: string[] /** * Exactly one of these flags must be provided. */ - exactlyOne?: string[]; + exactlyOne?: string[] /** * Define complex relationships between flags. */ - relationships?: Relationship[]; + relationships?: Relationship[] /** * Make the flag as deprecated. */ - deprecated?: true | Deprecation; + deprecated?: true | Deprecation /** * Alternate names that can be used for this flag. */ - aliases?: string[]; + aliases?: string[] /** - * Alternate short chars that can be used for this flag. - */ - charAliases?: (AlphabetLowercase | AlphabetUppercase)[]; + * Alternate short chars that can be used for this flag. + */ + charAliases?: (AlphabetLowercase | AlphabetUppercase)[] /** * Emit deprecation warning when a flag alias is provided */ @@ -165,89 +169,98 @@ export type FlagProps = { * If true, the value returned by defaultHelp will not be cached in the oclif.manifest.json. * This is helpful if the default value contains sensitive data that shouldn't be published to npm. */ - noCacheDefault?: boolean; + noCacheDefault?: boolean } export type ArgProps = { - name: string; + name: string /** * A description of flag usage. If summary is provided, the description * is assumed to be a longer description and will be shown in a separate * section within help. */ - description?: string; + description?: string /** * If true, the flag will not be shown in the help. */ - hidden?: boolean; + hidden?: boolean /** * If true, the flag will be required. */ - required?: boolean; + required?: boolean - options?: string[]; - ignoreStdin?: boolean; + options?: string[] + ignoreStdin?: boolean /** * If true, the value returned by defaultHelp will not be cached in the oclif.manifest.json. * This is helpful if the default value contains sensitive data that shouldn't be published to npm. */ - noCacheDefault?: boolean; + noCacheDefault?: boolean } export type BooleanFlagProps = FlagProps & { - type: 'boolean'; - allowNo: boolean; + type: 'boolean' + allowNo: boolean } export type OptionFlagProps = FlagProps & { - type: 'option'; - helpValue?: string; - options?: readonly string[]; - multiple?: boolean; - /** + type: 'option' + helpValue?: string + options?: readonly string[] + multiple?: boolean + /** * Delimiter to separate the values for a multiple value flag. * Only respected if multiple is set to true. Default behavior is to * separate on spaces. */ - delimiter?: ',', + delimiter?: ',' } export type FlagParserContext = Command & {token: FlagToken} -export type FlagParser = (input: I, context: FlagParserContext, opts: P & OptionFlag) => - T extends Array ? Promise : Promise +export type FlagParser = ( + input: I, + context: FlagParserContext, + opts: P & OptionFlag, +) => T extends Array ? Promise : Promise export type ArgParserContext = Command & {token: ArgToken} -export type ArgParser = (input: string, context: ArgParserContext, opts: P & Arg) => Promise +export type ArgParser = ( + input: string, + context: ArgParserContext, + opts: P & Arg, +) => Promise export type Arg = ArgProps & { - options?: T[]; - defaultHelp?: ArgDefaultHelp; - input: string[]; - default?: ArgDefault; - parse: ArgParser; + options?: T[] + defaultHelp?: ArgDefaultHelp + input: string[] + default?: ArgDefault + parse: ArgParser } export type ArgDefinition = { - (options: P & ({ required: true } | { default: ArgDefault }) & Partial>): Arg; - (options?: P & Partial>): Arg; + (options: P & ({required: true} | {default: ArgDefault}) & Partial>): Arg + (options?: P & Partial>): Arg } -export type BooleanFlag = FlagProps & BooleanFlagProps & { - /** - * specifying a default of false is the same as not specifying a default - */ - default?: FlagDefault; - parse: (input: boolean, context: FlagParserContext, opts: FlagProps & BooleanFlagProps) => Promise -} - -export type OptionFlag = FlagProps & OptionFlagProps & { - parse: FlagParser - defaultHelp?: FlagDefaultHelp; - input: string[]; - default?: FlagDefault; -} +export type BooleanFlag = FlagProps & + BooleanFlagProps & { + /** + * specifying a default of false is the same as not specifying a default + */ + default?: FlagDefault + parse: (input: boolean, context: FlagParserContext, opts: FlagProps & BooleanFlagProps) => Promise + } + +export type OptionFlag = FlagProps & + OptionFlagProps & { + parse: FlagParser + defaultHelp?: FlagDefaultHelp + input: string[] + default?: FlagDefault + } type ReturnTypeSwitches = {multiple: boolean; requiredOrDefaulted: boolean} @@ -260,16 +273,17 @@ type ReturnTypeSwitches = {multiple: boolean; requiredOrDefaulted: boolean} * - It's possible that T extends an Array, if so we want to return T so that the return isn't T[][] * - If requiredOrDefaulted is false && multiple is false, then the return type is T | undefined */ -type FlagReturnType = - R['requiredOrDefaulted'] extends true ? - R['multiple'] extends true ? - [T] extends [Array] ? T : - T[] : - T : - R['multiple'] extends true ? - [T] extends [Array] ? T | undefined : - T[] | undefined : - T | undefined +type FlagReturnType = R['requiredOrDefaulted'] extends true + ? R['multiple'] extends true + ? [T] extends [Array] + ? T + : T[] + : T + : R['multiple'] extends true + ? [T] extends [Array] + ? T | undefined + : T[] | undefined + : T | undefined /** * FlagDefinition types a function that takes `options` and returns an OptionFlag. @@ -291,96 +305,125 @@ type FlagReturnType = export type FlagDefinition< T, P = CustomOptions, - R extends ReturnTypeSwitches = {multiple: false, requiredOrDefaulted: false} + R extends ReturnTypeSwitches = {multiple: false; requiredOrDefaulted: false}, > = { ( // `multiple` is set to false and `required` is set to true in options, potentially overriding the default - options: P & { multiple: false; required: true } & Partial, P>> - ): OptionFlag>; + options: P & {multiple: false; required: true} & Partial< + OptionFlag, P> + >, + ): OptionFlag> ( // `multiple` is set to true and `required` is set to false in options, potentially overriding the default - options: P & { multiple: true; required: false } & Partial, P>> - ): OptionFlag>; + options: P & {multiple: true; required: false} & Partial< + OptionFlag, P> + >, + ): OptionFlag> ( // `multiple` is set to true and `required` is set to false in options, potentially overriding the default - options: P & { multiple: false; required: false } & Partial, P>> - ): OptionFlag>; + options: P & {multiple: false; required: false} & Partial< + OptionFlag, P> + >, + ): OptionFlag> ( - options: R['multiple'] extends true ? - // `multiple` is defaulted to true and either `required=true` or `default` are provided in options - P & ( - { required: true } | - { default: OptionFlag, P>['default'] } - ) & Partial, P>> : - // `multiple` is NOT defaulted to true and either `required=true` or `default` are provided in options - P & { multiple?: false | undefined } & ( - { required: true } | - { default: OptionFlag, P>['default'] } - ) & Partial, P>> - ): OptionFlag>; + options: R['multiple'] extends true + ? // `multiple` is defaulted to true and either `required=true` or `default` are provided in options + P & + ( + | {required: true} + | { + default: OptionFlag< + FlagReturnType, + P + >['default'] + } + ) & + Partial, P>> + : // `multiple` is NOT defaulted to true and either `required=true` or `default` are provided in options + P & {multiple?: false | undefined} & ( + | {required: true} + | { + default: OptionFlag< + FlagReturnType, + P + >['default'] + } + ) & + Partial, P>>, + ): OptionFlag> ( - options: R['multiple'] extends true ? - // `multiple` is defaulted to true and either `required=true` or `default` are provided in options - P & ( - { required: true } | - { default: OptionFlag, P>['default'] } - ) & Partial, P>> : - // `multiple` is NOT defaulted to true but `multiple=true` and either `required=true` or `default` are provided in options - P & { multiple: true } & ( - { required: true } | - { default: OptionFlag, P>['default'] } - ) & Partial, P>> - ): OptionFlag>; + options: R['multiple'] extends true + ? // `multiple` is defaulted to true and either `required=true` or `default` are provided in options + P & + ( + | {required: true} + | {default: OptionFlag, P>['default']} + ) & + Partial, P>> + : // `multiple` is NOT defaulted to true but `multiple=true` and either `required=true` or `default` are provided in options + P & {multiple: true} & ( + | {required: true} + | {default: OptionFlag, P>['default']} + ) & + Partial, P>>, + ): OptionFlag> ( // `multiple` is not provided in options but either `required=true` or `default` are provided - options: P & { multiple?: false | undefined; } & ( - { required: true } | - { default: OptionFlag, P>['default'] } - ) & Partial, P>> - ): OptionFlag>; + options: P & {multiple?: false | undefined} & ( + | {required: true} + | {default: OptionFlag, P>['default']} + ) & + Partial, P>>, + ): OptionFlag> ( // `required` is set to false in options, potentially overriding the default - options: P & { required: false } & Partial, P>> - ): OptionFlag>; + options: P & {required: false} & Partial< + OptionFlag, P> + >, + ): OptionFlag> ( // `multiple` is set to false in options, potentially overriding the default - options: P & { multiple: false } & Partial, P>> - ): OptionFlag>; + options: P & {multiple: false} & Partial< + OptionFlag, P> + >, + ): OptionFlag> ( // Catch all for when `multiple` is not set in the options - options?: P & { multiple?: false | undefined } & Partial, P>> - ): OptionFlag>; + options?: P & {multiple?: false | undefined} & Partial, P>>, + ): OptionFlag> ( // `multiple` is set to true in options, potentially overriding the default - options: P & { multiple: true } & Partial, P>> - ): OptionFlag>; + options: P & {multiple: true} & Partial< + OptionFlag, P> + >, + ): OptionFlag> } export type Flag = BooleanFlag | OptionFlag export type Input = { - flags?: FlagInput; - baseFlags?: FlagInput; - enableJsonFlag?: true | false; - args?: ArgInput; - strict?: boolean; - context?: ParserContext; - '--'?: boolean; + flags?: FlagInput + baseFlags?: FlagInput + enableJsonFlag?: true | false + args?: ArgInput + strict?: boolean + context?: ParserContext + '--'?: boolean } export type ParserInput = { - argv: string[]; - flags: FlagInput; - args: ArgInput; - strict: boolean; - context: ParserContext | undefined; - '--'?: boolean; + argv: string[] + flags: FlagInput + args: ArgInput + strict: boolean + context: ParserContext | undefined + '--'?: boolean } export type ParserContext = Command & { - token?: FlagToken | ArgToken; + token?: FlagToken | ArgToken } -export type FlagInput = { [P in keyof T]: Flag } +export type FlagInput = {[P in keyof T]: Flag} -export type ArgInput = { [P in keyof T]: Arg } +export type ArgInput = {[P in keyof T]: Arg} diff --git a/src/interfaces/pjson.ts b/src/interfaces/pjson.ts index 22f0af21f..2da23f60f 100644 --- a/src/interfaces/pjson.ts +++ b/src/interfaces/pjson.ts @@ -1,115 +1,116 @@ import {HelpOptions} from './help' export interface PJSON { - [k: string]: any; - version: string; - dependencies?: {[name: string]: string}; - devDependencies?: {[name: string]: string}; + [k: string]: any + version: string + dependencies?: {[name: string]: string} + devDependencies?: {[name: string]: string} oclif: { - schema?: number; - bin?: string; - dirname?: string; - hooks?: Record; - plugins?: string[]; - }; + schema?: number + bin?: string + dirname?: string + hooks?: Record + plugins?: string[] + } } export namespace PJSON { export interface Plugin extends PJSON { - name: string; - version: string; + name: string + version: string oclif: PJSON['oclif'] & { - schema?: number; - description?: string; - topicSeparator?: ':' | ' '; - flexibleTaxonomy?: boolean; - hooks?: { [name: string]: (string | string[]) }; - commands?: string; - default?: string; - plugins?: string[]; - devPlugins?: string[]; - jitPlugins?: Record; - helpClass?: string; - helpOptions?: HelpOptions; - aliases?: { [name: string]: string | null }; - repositoryPrefix?: string; + schema?: number + description?: string + topicSeparator?: ':' | ' ' + flexibleTaxonomy?: boolean + hooks?: {[name: string]: string | string[]} + commands?: string + default?: string + plugins?: string[] + devPlugins?: string[] + jitPlugins?: Record + helpClass?: string + helpOptions?: HelpOptions + aliases?: {[name: string]: string | null} + repositoryPrefix?: string update: { - s3: S3; + s3: S3 autoupdate?: { - rollout?: number; - debounce?: number; - }; + rollout?: number + debounce?: number + } node: { - version?: string; - targets?: string[]; - }; - }; + version?: string + targets?: string[] + } + } topics?: { [k: string]: { - description?: string; - subtopics?: Plugin['oclif']['topics']; - hidden?: boolean; - }; - }; - additionalHelpFlags?: string[]; - additionalVersionFlags?: string[]; - state?: 'beta' | 'deprecated' | string; - }; + description?: string + subtopics?: Plugin['oclif']['topics'] + hidden?: boolean + } + } + additionalHelpFlags?: string[] + additionalVersionFlags?: string[] + state?: 'beta' | 'deprecated' | string + } } export interface S3 { - acl?: string; - bucket?: string; - host?: string; - xz?: boolean; - gz?: boolean; + acl?: string + bucket?: string + host?: string + xz?: boolean + gz?: boolean templates: { - target: S3.Templates; - vanilla: S3.Templates; - }; + target: S3.Templates + vanilla: S3.Templates + } } export namespace S3 { export interface Templates { - baseDir?: string; - versioned?: string; - unversioned?: string; - manifest?: string; + baseDir?: string + versioned?: string + unversioned?: string + manifest?: string } } export interface CLI extends Plugin { oclif: Plugin['oclif'] & { - schema?: number; - bin?: string; - binAliases?: string[]; - nsisCustomization?: string; - npmRegistry?: string; - scope?: string; - dirname?: string; - flexibleTaxonomy?: boolean; - jitPlugins?: Record; - }; + schema?: number + bin?: string + binAliases?: string[] + nsisCustomization?: string + npmRegistry?: string + scope?: string + dirname?: string + flexibleTaxonomy?: boolean + jitPlugins?: Record + } } export interface User extends PJSON { - private?: boolean; + private?: boolean oclif: PJSON['oclif'] & { - plugins?: (string | PluginTypes.User | PluginTypes.Link)[]; }; + plugins?: (string | PluginTypes.User | PluginTypes.Link)[] + } } export type PluginTypes = PluginTypes.User | PluginTypes.Link | {root: string} export namespace PluginTypes { export interface User { - type: 'user'; - name: string; - url?: string; - tag?: string; + type: 'user' + name: string + url?: string + tag?: string } export interface Link { - type: 'link'; - name: string; - root: string; + type: 'link' + name: string + root: string } } } diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index 772fbc10c..eb1d8f42b 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -3,26 +3,26 @@ import {PJSON} from './pjson' import {Topic} from './topic' export interface PluginOptions { - root: string; - name?: string; - type?: string; - tag?: string; - ignoreManifest?: boolean; - errorOnManifestCreate?: boolean; - respectNoCacheDefault?: boolean; - parent?: Plugin; - children?: Plugin[]; - flexibleTaxonomy?: boolean; - isRoot?: boolean; + root: string + name?: string + type?: string + tag?: string + ignoreManifest?: boolean + errorOnManifestCreate?: boolean + respectNoCacheDefault?: boolean + parent?: Plugin + children?: Plugin[] + flexibleTaxonomy?: boolean + isRoot?: boolean } export interface Options extends PluginOptions { - devPlugins?: boolean; - jitPlugins?: boolean; - userPlugins?: boolean; - channel?: string; - version?: string; - enablePerf?: boolean; + devPlugins?: boolean + jitPlugins?: boolean + userPlugins?: boolean + channel?: string + version?: string + enablePerf?: boolean plugins?: Map } @@ -30,62 +30,62 @@ export interface Plugin { /** * ../config version */ - _base: string; + _base: string /** * name from package.json */ - name: string; + name: string /** * aliases from package.json dependencies */ - alias: string; + alias: string /** * version from package.json * * example: 1.2.3 */ - version: string; + version: string /** * full package.json * * parsed with read-pkg */ - pjson: PJSON.Plugin | PJSON.CLI; + pjson: PJSON.Plugin | PJSON.CLI /** * used to tell the user how the plugin was installed * examples: core, link, user, dev */ - type: string; + type: string /** * Plugin is written in ESM or CommonJS */ - moduleType: 'module' | 'commonjs'; + moduleType: 'module' | 'commonjs' /** * base path of plugin */ - root: string; + root: string /** * npm dist-tag of plugin * only used for user plugins */ - tag?: string; + tag?: string /** * if it appears to be an npm package but does not look like it's really a CLI plugin, this is set to false */ - valid: boolean; + valid: boolean /** * True if the plugin is the root plugin. */ - isRoot: boolean; + isRoot: boolean - commands: Command.Loadable[]; - hooks: { [k: string]: string[] }; - readonly commandIDs: string[]; - readonly topics: Topic[]; - readonly hasManifest: boolean; + commands: Command.Loadable[] + hooks: {[k: string]: string[]} + readonly commandIDs: string[] + readonly topics: Topic[] + readonly hasManifest: boolean - findCommand(id: string, opts: { must: true }): Promise; - findCommand(id: string, opts?: { must: boolean }): Promise | undefined; - load(): Promise; + findCommand(id: string, opts: {must: true}): Promise + findCommand(id: string, opts?: {must: boolean}): Promise | undefined + load(): Promise } diff --git a/src/interfaces/s3-manifest.ts b/src/interfaces/s3-manifest.ts index 0c18366cb..d81e7c042 100644 --- a/src/interfaces/s3-manifest.ts +++ b/src/interfaces/s3-manifest.ts @@ -1,14 +1,14 @@ export interface S3Manifest { - version: string; - sha: string; - gz: string; - xz?: string; - sha256gz: string; - sha256xz?: string; - baseDir: string; - rollout?: number; + version: string + sha: string + gz: string + xz?: string + sha256gz: string + sha256xz?: string + baseDir: string + rollout?: number node: { - compatible: string; - recommended: string; - }; + compatible: string + recommended: string + } } diff --git a/src/interfaces/topic.ts b/src/interfaces/topic.ts index 3765d097c..4360f0247 100644 --- a/src/interfaces/topic.ts +++ b/src/interfaces/topic.ts @@ -1,5 +1,5 @@ export interface Topic { - name: string; - description?: string; - hidden?: boolean; + name: string + description?: string + hidden?: boolean } diff --git a/src/interfaces/ts-config.ts b/src/interfaces/ts-config.ts index d871120e2..31e88934d 100644 --- a/src/interfaces/ts-config.ts +++ b/src/interfaces/ts-config.ts @@ -1,20 +1,20 @@ export interface TSConfig { compilerOptions: { - rootDir?: string; - rootDirs?: string[]; - outDir?: string; - target?: string; - esModuleInterop?: boolean; - experimentalDecorators?: boolean; - emitDecoratorMetadata?: boolean; - module?: string; - moduleResolution?: string; - sourceMap?: boolean; - jsx?: boolean; - }; + rootDir?: string + rootDirs?: string[] + outDir?: string + target?: string + esModuleInterop?: boolean + experimentalDecorators?: boolean + emitDecoratorMetadata?: boolean + module?: string + moduleResolution?: string + sourceMap?: boolean + jsx?: boolean + } 'ts-node'?: { - esm?: boolean; - experimentalSpecifierResolution?: 'node' | 'explicit'; - scope?: boolean; + esm?: boolean + experimentalSpecifierResolution?: 'node' | 'explicit' + scope?: boolean } } diff --git a/src/module-loader.ts b/src/module-loader.ts index ce715ed8a..fc5ec8a42 100644 --- a/src/module-loader.ts +++ b/src/module-loader.ts @@ -14,7 +14,7 @@ const getPackageType = require('get-package-type') // eslint-disable-next-line camelcase const s_EXTENSIONS: string[] = ['.ts', '.js', '.mjs', '.cjs'] -const isPlugin = (config: IConfig|IPlugin): config is IPlugin => (config).type !== undefined +const isPlugin = (config: IConfig | IPlugin): config is IPlugin => (config).type !== undefined /** * Loads and returns a module. @@ -32,11 +32,11 @@ const isPlugin = (config: IConfig|IPlugin): config is IPlugin => (confi * * @returns {Promise<*>} The entire ESM module from dynamic import or CJS module by require. */ -export async function load(config: IConfig|IPlugin, modulePath: string): Promise { +export async function load(config: IConfig | IPlugin, modulePath: string): Promise { let filePath: string | undefined let isESM: boolean | undefined try { - ({isESM, filePath} = resolvePath(config, modulePath)) + ;({isESM, filePath} = resolvePath(config, modulePath)) return isESM ? await import(pathToFileURL(filePath).href) : require(filePath) } catch (error: any) { if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') { @@ -64,11 +64,14 @@ export async function load(config: IConfig|IPlugin, modulePath: string): Promise * @returns {Promise<{isESM: boolean, module: *, filePath: string}>} An object with the loaded module & data including * file path and whether the module is ESM. */ -export async function loadWithData(config: IConfig|IPlugin, modulePath: string): Promise<{isESM: boolean; module: any; filePath: string}> { +export async function loadWithData( + config: IConfig | IPlugin, + modulePath: string, +): Promise<{isESM: boolean; module: any; filePath: string}> { let filePath: string | undefined let isESM: boolean | undefined try { - ({isESM, filePath} = resolvePath(config, modulePath)) + ;({isESM, filePath} = resolvePath(config, modulePath)) const module = isESM ? await import(pathToFileURL(filePath).href) : require(filePath) return {isESM, module, filePath} } catch (error: any) { @@ -97,7 +100,10 @@ export async function loadWithData(config: IConfig|IPlugin, modulePath: string): * @returns {Promise<{isESM: boolean, module: *, filePath: string}>} An object with the loaded module & data including * file path and whether the module is ESM. */ -export async function loadWithDataFromManifest(cached: Command.Cached, modulePath: string): Promise<{isESM: boolean; module: any; filePath: string}> { +export async function loadWithDataFromManifest( + cached: Command.Cached, + modulePath: string, +): Promise<{isESM: boolean; module: any; filePath: string}> { const {isESM, relativePath, id} = cached if (!relativePath) { throw new ModuleLoadError(`Cached command ${id} does not have a relative path`) @@ -123,33 +129,33 @@ export async function loadWithDataFromManifest(cached: Command.Cached, modulePat } /** - * For `.js` files uses `getPackageType` to determine if `type` is set to `module` in associated `package.json`. If - * the `modulePath` provided ends in `.mjs` it is assumed to be ESM. - * - * @param {string} filePath - File path to test. - * - * @returns {boolean} The modulePath is an ES Module. - * @see https://www.npmjs.com/package/get-package-type - */ + * For `.js` files uses `getPackageType` to determine if `type` is set to `module` in associated `package.json`. If + * the `modulePath` provided ends in `.mjs` it is assumed to be ESM. + * + * @param {string} filePath - File path to test. + * + * @returns {boolean} The modulePath is an ES Module. + * @see https://www.npmjs.com/package/get-package-type + */ export function isPathModule(filePath: string): boolean { const extension = extname(filePath).toLowerCase() switch (extension) { - case '.js': - case '.jsx': - case '.ts': - case '.tsx': { - return getPackageType.sync(filePath) === 'module' - } + case '.js': + case '.jsx': + case '.ts': + case '.tsx': { + return getPackageType.sync(filePath) === 'module' + } - case '.mjs': - case '.mts': { - return true - } + case '.mjs': + case '.mts': { + return true + } - default: { - return false - } + default: { + return false + } } } @@ -164,7 +170,7 @@ export function isPathModule(filePath: string): boolean { * * @returns {{isESM: boolean, filePath: string}} An object including file path and whether the module is ESM. */ -function resolvePath(config: IConfig|IPlugin, modulePath: string): {isESM: boolean; filePath: string} { +function resolvePath(config: IConfig | IPlugin, modulePath: string): {isESM: boolean; filePath: string} { let isESM: boolean let filePath: string | undefined @@ -172,7 +178,8 @@ function resolvePath(config: IConfig|IPlugin, modulePath: string): {isESM: boole filePath = require.resolve(modulePath) isESM = isPathModule(filePath) } catch { - filePath = (isPlugin(config) ? tsPath(config.root, modulePath, config) : tsPath(config.root, modulePath)) ?? modulePath + filePath = + (isPlugin(config) ? tsPath(config.root, modulePath, config) : tsPath(config.root, modulePath)) ?? modulePath let fileExists = false let isDirectory = false @@ -212,7 +219,7 @@ function resolvePath(config: IConfig|IPlugin, modulePath: string): {isESM: boole * * @returns {string | null} Modified file path including extension or null if file is not found. */ -function findFile(filePath: string) : string | null { +function findFile(filePath: string): string | null { // eslint-disable-next-line camelcase for (const extension of s_EXTENSIONS) { const testPath = `${filePath}${extension}` diff --git a/src/parser/errors.ts b/src/parser/errors.ts index 499120dbf..53d85c9a7 100644 --- a/src/parser/errors.ts +++ b/src/parser/errors.ts @@ -9,16 +9,16 @@ import {uniq} from '../util' export {CLIError} from '../errors' export type Validation = { - name: string; - status: 'success' | 'failed'; - validationFn: string; - reason?: string; + name: string + status: 'success' | 'failed' + validationFn: string + reason?: string } export class CLIParseError extends CLIError { public parse: CLIParseErrorOptions['parse'] - constructor(options: CLIParseErrorOptions & { message: string }) { + constructor(options: CLIParseErrorOptions & {message: string}) { options.message += '\nSee more help with --help' super(options.message) this.parse = options.parse @@ -28,11 +28,15 @@ export class CLIParseError extends CLIError { export class InvalidArgsSpecError extends CLIParseError { public args: ArgInput - constructor({args, parse}: CLIParseErrorOptions & { args: ArgInput }) { + constructor({args, parse}: CLIParseErrorOptions & {args: ArgInput}) { let message = 'Invalid argument spec' - const namedArgs = Object.values(args).filter(a => a.name) + const namedArgs = Object.values(args).filter((a) => a.name) if (namedArgs.length > 0) { - const list = renderList(namedArgs.map(a => [`${a.name} (${a.required ? 'required' : 'optional'})`, a.description] as [string, string])) + const list = renderList( + namedArgs.map( + (a) => [`${a.name} (${a.required ? 'required' : 'optional'})`, a.description] as [string, string], + ), + ) message += `:\n${list}` } @@ -44,17 +48,25 @@ export class InvalidArgsSpecError extends CLIParseError { export class RequiredArgsError extends CLIParseError { public args: Arg[] - constructor({args, parse, flagsWithMultiple}: CLIParseErrorOptions & { args: Arg[]; flagsWithMultiple?: string[] }) { + constructor({ + args, + parse, + flagsWithMultiple, + }: CLIParseErrorOptions & {args: Arg[]; flagsWithMultiple?: string[]}) { let message = `Missing ${args.length} required arg${args.length === 1 ? '' : 's'}` - const namedArgs = args.filter(a => a.name) + const namedArgs = args.filter((a) => a.name) if (namedArgs.length > 0) { - const list = renderList(namedArgs.map(a => [a.name, a.description] as [string, string])) + const list = renderList(namedArgs.map((a) => [a.name, a.description] as [string, string])) message += `:\n${list}` } if (flagsWithMultiple?.length) { - const flags = flagsWithMultiple.map(f => `--${f}`).join(', ') - message += `\n\nNote: ${flags} allow${flagsWithMultiple.length === 1 ? 's' : ''} multiple values. Because of this you need to provide all arguments before providing ${flagsWithMultiple.length === 1 ? 'that flag' : 'those flags'}.` + const flags = flagsWithMultiple.map((f) => `--${f}`).join(', ') + message += `\n\nNote: ${flags} allow${ + flagsWithMultiple.length === 1 ? 's' : '' + } multiple values. Because of this you need to provide all arguments before providing ${ + flagsWithMultiple.length === 1 ? 'that flag' : 'those flags' + }.` message += '\nAlternatively, you can use "--" to signify the end of the flags and the beginning of arguments.' } @@ -66,7 +78,7 @@ export class RequiredArgsError extends CLIParseError { export class RequiredFlagError extends CLIParseError { public flag: Flag - constructor({flag, parse}: CLIParseErrorOptions & { flag: Flag }) { + constructor({flag, parse}: CLIParseErrorOptions & {flag: Flag}) { const usage = renderList(flagUsages([flag], {displayRequired: false})) const message = `Missing required flag:\n${usage}` super({parse, message}) @@ -77,7 +89,7 @@ export class RequiredFlagError extends CLIParseError { export class UnexpectedArgsError extends CLIParseError { public args: unknown[] - constructor({parse, args}: CLIParseErrorOptions & { args: unknown[] }) { + constructor({parse, args}: CLIParseErrorOptions & {args: unknown[]}) { const message = `Unexpected argument${args.length === 1 ? '' : 's'}: ${args.join(', ')}` super({parse, message}) this.args = args @@ -87,7 +99,7 @@ export class UnexpectedArgsError extends CLIParseError { export class NonExistentFlagsError extends CLIParseError { public flags: string[] - constructor({parse, flags}: CLIParseErrorOptions & { flags: string[] }) { + constructor({parse, flags}: CLIParseErrorOptions & {flags: string[]}) { const message = `Nonexistent flag${flags.length === 1 ? '' : 's'}: ${flags.join(', ')}` super({parse, message}) this.flags = flags @@ -109,8 +121,8 @@ export class ArgInvalidOptionError extends CLIParseError { } export class FailedFlagValidationError extends CLIParseError { - constructor({parse, failed}: CLIParseErrorOptions & { failed: Validation[] }) { - const reasons = failed.map(r => r.reason) + constructor({parse, failed}: CLIParseErrorOptions & {failed: Validation[]}) { + const reasons = failed.map((r) => r.reason) const deduped = uniq(reasons) const errString = deduped.length === 1 ? 'error' : 'errors' const message = `The following ${errString} occurred:\n ${chalk.dim(deduped.join('\n '))}` diff --git a/src/parser/help.ts b/src/parser/help.ts index 7b6bfd396..09b5a90bf 100644 --- a/src/parser/help.ts +++ b/src/parser/help.ts @@ -23,6 +23,5 @@ export function flagUsage(flag: Flag, options: FlagUsageOptions = {}): [str export function flagUsages(flags: Flag[], options: FlagUsageOptions = {}): [string, string | undefined][] { if (flags.length === 0) return [] - return sortBy(flags, f => [f.char ? -1 : 1, f.char, f.name]) - .map(f => flagUsage(f, options)) + return sortBy(flags, (f) => [f.char ? -1 : 1, f.char, f.name]).map((f) => flagUsage(f, options)) } diff --git a/src/parser/index.ts b/src/parser/index.ts index 95a62791e..ef3bccf30 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -7,7 +7,7 @@ export {flagUsages} from './help' export async function parse< TFlags extends OutputFlags, BFlags extends OutputFlags, - TArgs extends OutputArgs + TArgs extends OutputArgs, >(argv: string[], options: Input): Promise> { const input = { argv, @@ -22,4 +22,3 @@ export async function parse< await validate({input, output}) return output as ParserOutput } - diff --git a/src/parser/parse.ts b/src/parser/parse.ts index fffcd68a7..1802e41ae 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -22,9 +22,12 @@ import {createInterface} from 'node:readline' let debug: any try { - debug = process.env.CLI_FLAGS_DEBUG === '1' ? require('debug')('../parser') : () => { - // noop - } + debug = + process.env.CLI_FLAGS_DEBUG === '1' + ? require('debug')('../parser') + : () => { + // noop + } } catch { debug = () => { // noop @@ -42,7 +45,7 @@ const readStdin = async (): Promise => { if (stdin.isTTY) return null - return new Promise(resolve => { + return new Promise((resolve) => { let result = '' const ac = new AbortController() const {signal} = ac @@ -54,7 +57,7 @@ const readStdin = async (): Promise => { terminal: false, }) - rl.on('line', line => { + rl.on('line', (line) => { result += line }) @@ -64,12 +67,16 @@ const readStdin = async (): Promise => { resolve(result) }) - signal.addEventListener('abort', () => { - debug('stdin aborted') - clearTimeout(timeout) - rl.close() - resolve(null) - }, {once: true}) + signal.addEventListener( + 'abort', + () => { + debug('stdin aborted') + clearTimeout(timeout) + rl.close() + resolve(null) + }, + {once: true}, + ) }) } @@ -77,30 +84,38 @@ function isNegativeNumber(input: string): boolean { return /^-\d/g.test(input) } -const validateOptions = (flag: OptionFlag, input: string): string => { - if (flag.options && !flag.options.includes(input)) - throw new FlagInvalidOptionError(flag, input) +const validateOptions = (flag: OptionFlag, input: string): string => { + if (flag.options && !flag.options.includes(input)) throw new FlagInvalidOptionError(flag, input) return input } -export class Parser, BFlags extends OutputFlags, TArgs extends OutputArgs> { +export class Parser< + T extends ParserInput, + TFlags extends OutputFlags, + BFlags extends OutputFlags, + TArgs extends OutputArgs, +> { private readonly argv: string[] private readonly raw: ParsingToken[] = [] - private readonly booleanFlags: { [k: string]: BooleanFlag } - private readonly flagAliases: { [k: string]: BooleanFlag | OptionFlag } + private readonly booleanFlags: {[k: string]: BooleanFlag} + private readonly flagAliases: {[k: string]: BooleanFlag | OptionFlag} private readonly context: ParserContext private currentFlag?: OptionFlag constructor(private readonly input: T) { - this.context = input.context ?? {} as ParserContext + this.context = input.context ?? ({} as ParserContext) this.argv = [...input.argv] this._setNames() - this.booleanFlags = pickBy(input.flags, f => f.type === 'boolean') as any - this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap(flag => ([...flag.aliases ?? [], ...flag.charAliases ?? []]).map(a => [a, flag]))) + this.booleanFlags = pickBy(input.flags, (f) => f.type === 'boolean') as any + this.flagAliases = Object.fromEntries( + Object.values(input.flags).flatMap((flag) => + [...(flag.aliases ?? []), ...(flag.charAliases ?? [])].map((a) => [a, flag]), + ), + ) } public async parse(): Promise> { @@ -128,14 +143,14 @@ export class Parser o.type === 'flag' && o.flag === name)) { + if (!flag.multiple && this.raw.some((o) => o.type === 'flag' && o.flag === name)) { throw new CLIError(`Flag --${name} can only be specified once`) } this.currentFlag = flag - const input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2) + const input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2) // if the value ends up being one of the command's flags, the user didn't provide an input - if ((typeof input !== 'string') || this.findFlag(input).name) { + if (typeof input !== 'string' || this.findFlag(input).name) { throw new CLIError(`Flag --${name} expects a value`) } @@ -210,11 +225,17 @@ export class Parser { type ValueFunction = (fws: FlagWithStrategy, flags?: Record) => Promise - const parseFlagOrThrowError = async (input: any, flag: BooleanFlag | OptionFlag, context: ParserContext | undefined, token?: FlagToken) => { + const parseFlagOrThrowError = async ( + input: any, + flag: BooleanFlag | OptionFlag, + context: ParserContext | undefined, + token?: FlagToken, + ) => { if (!flag.parse) return input const ctx = { @@ -240,8 +261,8 @@ export class Parser { const tokenLength = fws.tokens?.length // user provided some input @@ -250,12 +271,13 @@ export class Parser parseFlagOrThrowError( - last(i.tokens)?.input !== `--no-${i.inputFlag.name}`, - i.inputFlag.flag, - this.context, - last(i.tokens), - ), + valueFunction: async (i) => + parseFlagOrThrowError( + last(i.tokens)?.input !== `--no-${i.inputFlag.name}`, + i.inputFlag.flag, + this.context, + last(i.tokens), + ), } } @@ -263,13 +285,28 @@ export class Parser (await Promise.all( - ((i.tokens ?? []).flatMap(token => token.input.split((i.inputFlag.flag as OptionFlag).delimiter ?? ','))) - // trim, and remove surrounding doubleQuotes (which would hav been needed if the elements contain spaces) - .map(v => v.trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1')) - .map(async v => parseFlagOrThrowError(v, i.inputFlag.flag, this.context, {...last(i.tokens) as FlagToken, input: v})), - // eslint-disable-next-line unicorn/no-await-expression-member - )).map(v => validateOptions(i.inputFlag.flag as OptionFlag, v)), + valueFunction: async (i) => + ( + await Promise.all( + (i.tokens ?? []) + .flatMap((token) => token.input.split((i.inputFlag.flag as OptionFlag).delimiter ?? ',')) + // trim, and remove surrounding doubleQuotes (which would hav been needed if the elements contain spaces) + .map((v) => + v + .trim() + .replace(/^"(.*)"$/, '$1') + .replace(/^'(.*)'$/, '$1'), + ) + .map(async (v) => + parseFlagOrThrowError(v, i.inputFlag.flag, this.context, { + ...(last(i.tokens) as FlagToken), + input: v, + }), + ), + ) + ) + // eslint-disable-next-line unicorn/no-await-expression-member + .map((v) => validateOptions(i.inputFlag.flag as OptionFlag, v)), } } @@ -279,12 +316,14 @@ export class Parser Promise.all( - (fws.tokens ?? []).map(token => parseFlagOrThrowError( - validateOptions(i.inputFlag.flag as OptionFlag, token.input as string), - i.inputFlag.flag, - this.context, - token, - )), + (fws.tokens ?? []).map((token) => + parseFlagOrThrowError( + validateOptions(i.inputFlag.flag as OptionFlag, token.input as string), + i.inputFlag.flag, + this.context, + token, + ), + ), ), } } @@ -293,12 +332,13 @@ export class Parser parseFlagOrThrowError( - validateOptions(i.inputFlag.flag as OptionFlag, last(fws.tokens)?.input as string), - i.inputFlag.flag, - this.context, - last(fws.tokens), - ), + valueFunction: async (i: FlagWithStrategy) => + parseFlagOrThrowError( + validateOptions(i.inputFlag.flag as OptionFlag, last(fws.tokens)?.input as string), + i.inputFlag.flag, + this.context, + last(fws.tokens), + ), } } } @@ -309,18 +349,20 @@ export class Parser parseFlagOrThrowError( - validateOptions(i.inputFlag.flag as OptionFlag, valueFromEnv), - i.inputFlag.flag, - this.context, - ), + valueFunction: async (i: FlagWithStrategy) => + parseFlagOrThrowError( + validateOptions(i.inputFlag.flag as OptionFlag, valueFromEnv), + i.inputFlag.flag, + this.context, + ), } } if (fws.inputFlag.flag.type === 'boolean') { return { ...fws, - valueFunction: async (i: FlagWithStrategy) => isTruthy(process.env[i.inputFlag.flag.env as string] ?? 'false'), + valueFunction: async (i: FlagWithStrategy) => + isTruthy(process.env[i.inputFlag.flag.env as string] ?? 'false'), } } } @@ -329,10 +371,13 @@ export class Parser fws.inputFlag.flag.default({options: i.inputFlag.flag, flags: allFlags}) - : async () => fws.inputFlag.flag.default, + ...fws, + metadata: {setFromDefault: true}, + valueFunction: + typeof fws.inputFlag.flag.default === 'function' + ? (i: FlagWithStrategy, allFlags = {}) => + fws.inputFlag.flag.default({options: i.inputFlag.flag, flags: allFlags}) + : async () => fws.inputFlag.flag.default, } } @@ -343,11 +388,14 @@ export class Parser { if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.defaultHelp) { return { - ...fws, helpFunction: typeof fws.inputFlag.flag.defaultHelp === 'function' - // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there - ? (i: FlagWithStrategy, flags: Record, ...context) => i.inputFlag.flag.defaultHelp({options: i.inputFlag, flags}, ...context) - // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there - : (i: FlagWithStrategy) => i.inputFlag.flag.defaultHelp, + ...fws, + helpFunction: + typeof fws.inputFlag.flag.defaultHelp === 'function' + ? (i: FlagWithStrategy, flags: Record, ...context) => + // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there + i.inputFlag.flag.defaultHelp({options: i.inputFlag, flags}, ...context) + : // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there + (i: FlagWithStrategy) => i.inputFlag.flag.defaultHelp, } } @@ -355,69 +403,89 @@ export class Parser => { - const valueReferenceForHelp = fwsArrayToObject(flagsWithAllValues.filter(fws => !fws.metadata?.setFromDefault)) - return Promise.all(fwsArray.map(async fws => { - try { - if (fws.helpFunction) { - return { - ...fws, - metadata: { - ...fws.metadata, - defaultHelp: await fws.helpFunction?.(fws, valueReferenceForHelp, this.context), - }, + const valueReferenceForHelp = fwsArrayToObject(flagsWithAllValues.filter((fws) => !fws.metadata?.setFromDefault)) + return Promise.all( + fwsArray.map(async (fws) => { + try { + if (fws.helpFunction) { + return { + ...fws, + metadata: { + ...fws.metadata, + defaultHelp: await fws.helpFunction?.(fws, valueReferenceForHelp, this.context), + }, + } } + } catch { + // no-op } - } catch { - // no-op - } - return fws - })) + return fws + }), + ) } - const fwsArrayToObject = (fwsArray: FlagWithStrategy[]) => Object.fromEntries( - fwsArray.filter(fws => fws.value !== undefined) - .map(fws => [fws.inputFlag.name, fws.value]), - ) as TFlags & BFlags & { json: boolean | undefined } + const fwsArrayToObject = (fwsArray: FlagWithStrategy[]) => + Object.fromEntries( + fwsArray.filter((fws) => fws.value !== undefined).map((fws) => [fws.inputFlag.name, fws.value]), + ) as TFlags & BFlags & {json: boolean | undefined} type FlagWithStrategy = { inputFlag: { - name: string, + name: string flag: Flag } - tokens?: FlagToken[], - valueFunction?: ValueFunction; - helpFunction?: (fws: FlagWithStrategy, flags: Record, ...args: any) => Promise; + tokens?: FlagToken[] + valueFunction?: ValueFunction + helpFunction?: (fws: FlagWithStrategy, flags: Record, ...args: any) => Promise metadata?: MetadataFlag - value?: any; + value?: any } const flagTokenMap = this.mapAndValidateFlags() - const flagsWithValues = await Promise.all(Object.entries(this.input.flags) - // we check them if they have a token, or might have env, default, or defaultHelp. Also include booleans so they get their default value - .filter(([name, flag]) => flag.type === 'boolean' || flag.env || flag.default !== undefined || 'defaultHelp' in flag || flagTokenMap.has(name)) - // match each possible flag to its token, if there is one - .map(([name, flag]): FlagWithStrategy => ({inputFlag: {name, flag}, tokens: flagTokenMap.get(name)})) - .map(fws => addValueFunction(fws)) - .filter(fws => fws.valueFunction !== undefined) - .map(fws => addHelpFunction(fws)) - // we can't apply the default values until all the other flags are resolved because `flag.default` can reference other flags - .map(async fws => (fws.metadata?.setFromDefault ? fws : {...fws, value: await fws.valueFunction?.(fws)}))) + const flagsWithValues = await Promise.all( + Object.entries(this.input.flags) + // we check them if they have a token, or might have env, default, or defaultHelp. Also include booleans so they get their default value + .filter( + ([name, flag]) => + flag.type === 'boolean' || + flag.env || + flag.default !== undefined || + 'defaultHelp' in flag || + flagTokenMap.has(name), + ) + // match each possible flag to its token, if there is one + .map(([name, flag]): FlagWithStrategy => ({inputFlag: {name, flag}, tokens: flagTokenMap.get(name)})) + .map((fws) => addValueFunction(fws)) + .filter((fws) => fws.valueFunction !== undefined) + .map((fws) => addHelpFunction(fws)) + // we can't apply the default values until all the other flags are resolved because `flag.default` can reference other flags + .map(async (fws) => (fws.metadata?.setFromDefault ? fws : {...fws, value: await fws.valueFunction?.(fws)})), + ) - const valueReference = fwsArrayToObject(flagsWithValues.filter(fws => !fws.metadata?.setFromDefault)) + const valueReference = fwsArrayToObject(flagsWithValues.filter((fws) => !fws.metadata?.setFromDefault)) - const flagsWithAllValues = await Promise.all(flagsWithValues - .map(async fws => (fws.metadata?.setFromDefault ? {...fws, value: await fws.valueFunction?.(fws, valueReference)} : fws))) + const flagsWithAllValues = await Promise.all( + flagsWithValues.map(async (fws) => + fws.metadata?.setFromDefault ? {...fws, value: await fws.valueFunction?.(fws, valueReference)} : fws, + ), + ) - const finalFlags = (flagsWithAllValues.some(fws => typeof fws.helpFunction === 'function')) ? await addDefaultHelp(flagsWithAllValues) : flagsWithAllValues + const finalFlags = flagsWithAllValues.some((fws) => typeof fws.helpFunction === 'function') + ? await addDefaultHelp(flagsWithAllValues) + : flagsWithAllValues return { flags: fwsArrayToObject(finalFlags), - metadata: {flags: Object.fromEntries(finalFlags.filter(fws => fws.metadata).map(fws => [fws.inputFlag.name, fws.metadata as MetadataFlag]))}, + metadata: { + flags: Object.fromEntries( + finalFlags.filter((fws) => fws.metadata).map((fws) => [fws.inputFlag.name, fws.metadata as MetadataFlag]), + ), + }, } } - private async _args(): Promise<{ argv: unknown[]; args: Record }> { + private async _args(): Promise<{argv: unknown[]; args: Record}> { const argv: unknown[] = [] const args = {} as Record const tokens = this._argTokens @@ -425,7 +493,7 @@ export class Parser t.arg === name) + const token = tokens.find((t) => t.arg === name) ctx.token = token! if (token) { @@ -493,13 +561,13 @@ export class Parser `--${f}`) - .join(' '), + .map((f) => `--${f}`) + .join(' '), ) } private get _argTokens(): ArgToken[] { - return this.raw.filter(o => o.type === 'arg') as ArgToken[] + return this.raw.filter((o) => o.type === 'arg') as ArgToken[] } private _setNames() { @@ -512,9 +580,9 @@ export class Parser { + private mapAndValidateFlags(): Map { const flagTokenMap = new Map() - for (const token of (this.raw.filter(o => o.type === 'flag') as FlagToken[])) { + for (const token of this.raw.filter((o) => o.type === 'flag') as FlagToken[]) { // fail fast if there are any invalid flags if (!(token.flag in this.input.flags)) { throw new CLIError(`Unexpected flag ${token.flag}`) @@ -543,18 +611,20 @@ export class Parser (this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined)) + return Object.keys(this.input.flags).find( + (k) => this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined, + ) } - private findFlag(arg: string): { name?: string, isLong: boolean } { + private findFlag(arg: string): {name?: string; isLong: boolean} { const isLong = arg.startsWith('--') const short = isLong ? false : arg.startsWith('-') - const name = isLong ? this.findLongFlag(arg) : (short ? this.findShortFlag(arg) : undefined) + const name = isLong ? this.findLongFlag(arg) : short ? this.findShortFlag(arg) : undefined return {name, isLong} } } diff --git a/src/parser/validate.ts b/src/parser/validate.ts index 0f7d0ea02..95ae24b84 100644 --- a/src/parser/validate.ts +++ b/src/parser/validate.ts @@ -9,10 +9,7 @@ import { } from './errors' import {uniq} from '../util' -export async function validate(parse: { - input: ParserInput; - output: ParserOutput; -}): Promise { +export async function validate(parse: {input: ParserInput; output: ParserOutput}): Promise { let cachedResolvedFlags: Record | undefined function validateArgs() { @@ -45,44 +42,46 @@ export async function validate(parse: { if (missingRequiredArgs.length > 0) { const flagsWithMultiple = Object.entries(parse.input.flags) - .filter(([_, flagDef]) => flagDef.type === 'option' && Boolean(flagDef.multiple)) - .map(([name]) => name) + .filter(([_, flagDef]) => flagDef.type === 'option' && Boolean(flagDef.multiple)) + .map(([name]) => name) throw new RequiredArgsError({parse, args: missingRequiredArgs, flagsWithMultiple}) } } async function validateFlags() { - const promises = Object.entries(parse.input.flags).flatMap(([name, flag]): Array> => { - if (parse.output.flags[name] !== undefined) { - return [ - ...flag.relationships ? validateRelationships(name, flag) : [], - ...flag.dependsOn ? [validateDependsOn(name, flag.dependsOn)] : [], - ...flag.exclusive ? [validateExclusive(name, flag.exclusive)] : [], - ...flag.exactlyOne ? [validateExactlyOne(name, flag.exactlyOne)] : [], - ] - } - - if (flag.required) { - return [{status: 'failed', name, validationFn: 'required', reason: `Missing required flag ${name}`}] - } - - if (flag.exactlyOne && flag.exactlyOne.length > 0) { - return [validateAcrossFlags(flag)] - } - - return [] - }) - - const results = (await Promise.all(promises)) - - const failed = results.filter(r => r.status === 'failed') + const promises = Object.entries(parse.input.flags).flatMap( + ([name, flag]): Array> => { + if (parse.output.flags[name] !== undefined) { + return [ + ...(flag.relationships ? validateRelationships(name, flag) : []), + ...(flag.dependsOn ? [validateDependsOn(name, flag.dependsOn)] : []), + ...(flag.exclusive ? [validateExclusive(name, flag.exclusive)] : []), + ...(flag.exactlyOne ? [validateExactlyOne(name, flag.exactlyOne)] : []), + ] + } + + if (flag.required) { + return [{status: 'failed', name, validationFn: 'required', reason: `Missing required flag ${name}`}] + } + + if (flag.exactlyOne && flag.exactlyOne.length > 0) { + return [validateAcrossFlags(flag)] + } + + return [] + }, + ) + + const results = await Promise.all(promises) + + const failed = results.filter((r) => r.status === 'failed') if (failed.length > 0) throw new FailedFlagValidationError({parse, failed}) } async function resolveFlags(flags: FlagRelationship[]): Promise> { if (cachedResolvedFlags) return cachedResolvedFlags - const promises = flags.map(async flag => { + const promises = flags.map(async (flag) => { if (typeof flag === 'string') { return [flag, parse.output.flags[flag]] } @@ -91,21 +90,22 @@ export async function validate(parse: { return result ? [flag.name, parse.output.flags[flag.name]] : null }) const resolved = await Promise.all(promises) - cachedResolvedFlags = Object.fromEntries(resolved.filter(r => r !== null) as [string, unknown][]) + cachedResolvedFlags = Object.fromEntries(resolved.filter((r) => r !== null) as [string, unknown][]) return cachedResolvedFlags } - const getPresentFlags = (flags: Record): string[] => Object.keys(flags).filter(key => key !== undefined) + const getPresentFlags = (flags: Record): string[] => + Object.keys(flags).filter((key) => key !== undefined) function validateAcrossFlags(flag: Flag): Validation { const base = {name: flag.name, validationFn: 'validateAcrossFlags'} const intersection = Object.entries(parse.input.flags) - .map(entry => entry[0]) // array of flag names - .filter(flagName => parse.output.flags[flagName] !== undefined) // with values - .filter(flagName => flag.exactlyOne && flag.exactlyOne.includes(flagName)) // and in the exactlyOne list + .map((entry) => entry[0]) // array of flag names + .filter((flagName) => parse.output.flags[flagName] !== undefined) // with values + .filter((flagName) => flag.exactlyOne && flag.exactlyOne.includes(flagName)) // and in the exactlyOne list if (intersection.length === 0) { // the command's exactlyOne may or may not include itself, so we'll use Set to add + de-dupe - const deduped = uniq(flag.exactlyOne?.map(flag => `--${flag}`) ?? []).join(', ') + const deduped = uniq(flag.exactlyOne?.map((flag) => `--${flag}`) ?? []).join(', ') const reason = `Exactly one of the following must be provided: ${deduped}` return {...base, status: 'failed', reason} } @@ -119,13 +119,15 @@ export async function validate(parse: { const keys = getPresentFlags(resolved) for (const flag of keys) { // do not enforce exclusivity for flags that were defaulted - if (parse.output.metadata.flags && parse.output.metadata.flags[flag]?.setFromDefault) - continue - if (parse.output.metadata.flags && parse.output.metadata.flags[name]?.setFromDefault) - continue + if (parse.output.metadata.flags && parse.output.metadata.flags[flag]?.setFromDefault) continue + if (parse.output.metadata.flags && parse.output.metadata.flags[name]?.setFromDefault) continue if (parse.output.flags[flag] !== undefined) { const flagValue = parse.output.metadata.flags?.[flag]?.defaultHelp ?? parse.output.flags[flag] - return {...base, status: 'failed', reason: `--${flag}=${flagValue} cannot also be provided when using --${name}`} + return { + ...base, + status: 'failed', + reason: `--${flag}=${flagValue} cannot also be provided when using --${name}`, + } } } @@ -150,10 +152,16 @@ export async function validate(parse: { const base = {name, validationFn: 'validateDependsOn'} const resolved = await resolveFlags(flags) - const foundAll = Object.values(resolved).every(val => val !== undefined) + const foundAll = Object.values(resolved).every((val) => val !== undefined) if (!foundAll) { - const formattedFlags = Object.keys(resolved).map(f => `--${f}`).join(', ') - return {...base, status: 'failed', reason: `All of the following must be provided when using --${name}: ${formattedFlags}`} + const formattedFlags = Object.keys(resolved) + .map((f) => `--${f}`) + .join(', ') + return { + ...base, + status: 'failed', + reason: `All of the following must be provided when using --${name}: ${formattedFlags}`, + } } return {...base, status: 'success'} @@ -165,33 +173,39 @@ export async function validate(parse: { const resolved = await resolveFlags(flags) const foundAtLeastOne = Object.values(resolved).some(Boolean) if (!foundAtLeastOne) { - const formattedFlags = Object.keys(resolved).map(f => `--${f}`).join(', ') - return {...base, status: 'failed', reason: `One of the following must be provided when using --${name}: ${formattedFlags}`} + const formattedFlags = Object.keys(resolved) + .map((f) => `--${f}`) + .join(', ') + return { + ...base, + status: 'failed', + reason: `One of the following must be provided when using --${name}: ${formattedFlags}`, + } } return {...base, status: 'success'} } function validateRelationships(name: string, flag: Flag): Promise[] { - return ((flag.relationships ?? []).map(relationship => { + return (flag.relationships ?? []).map((relationship) => { switch (relationship.type) { - case 'all': { - return validateDependsOn(name, relationship.flags) - } + case 'all': { + return validateDependsOn(name, relationship.flags) + } - case 'some': { - return validateSome(name, relationship.flags) - } + case 'some': { + return validateSome(name, relationship.flags) + } - case 'none': { - return validateExclusive(name, relationship.flags) - } + case 'none': { + return validateExclusive(name, relationship.flags) + } - default: { - throw new Error(`Unknown relationship type: ${relationship.type}`) + default: { + throw new Error(`Unknown relationship type: ${relationship.type}`) + } } - } - })) + }) } validateArgs() diff --git a/src/performance.ts b/src/performance.ts index 284eb2af8..0792dd84d 100644 --- a/src/performance.ts +++ b/src/performance.ts @@ -3,25 +3,25 @@ import {settings} from './settings' type Details = Record type PerfResult = { - name: string; + name: string duration: number details: Details - module: string; - method: string | undefined; - scope: string | undefined; + module: string + method: string | undefined + scope: string | undefined } type PerfHighlights = { - configLoadTime: number; - runTime: number; - initTime: number; - commandLoadTime: number; - commandRunTime: number; - pluginLoadTimes: Record; - corePluginsLoadTime: number; - userPluginsLoadTime: number; - linkedPluginsLoadTime: number; - hookRunTimes: Record>; + configLoadTime: number + runTime: number + initTime: number + commandLoadTime: number + commandRunTime: number + pluginLoadTimes: Record + corePluginsLoadTime: number + userPluginsLoadTime: number + linkedPluginsLoadTime: number + hookRunTimes: Record> } class Marker { @@ -33,7 +33,10 @@ class Marker { private startMarker: string private stopMarker: string - constructor(public name: string, public details: Details = {}) { + constructor( + public name: string, + public details: Details = {}, + ) { this.startMarker = `${this.name}-start` this.stopMarker = `${this.name}-stop` const [caller, scope] = name.split('#') @@ -76,7 +79,7 @@ export class Performance { } public static getResult(name: string): PerfResult | undefined { - return Performance.results.find(r => r.name === name) + return Performance.results.find((r) => r.name === name) } public static get highlights(): PerfHighlights { @@ -115,12 +118,12 @@ export class Performance { const markers = Object.values(Performance.markers) if (markers.length === 0) return - for (const marker of markers.filter(m => !m.stopped)) { + for (const marker of markers.filter((m) => !m.stopped)) { marker.stop() } - return new Promise(resolve => { - const perfObserver = new PerformanceObserver(items => { + return new Promise((resolve) => { + const perfObserver = new PerformanceObserver((items) => { for (const entry of items.getEntries()) { if (Performance.markers[entry.name]) { const marker = Performance.markers[entry.name] @@ -135,36 +138,47 @@ export class Performance { } } - const command = Performance.results.find(r => r.name.startsWith('config.runCommand')) - const commandLoadTime = command ? Performance.getResult(`plugin.findCommand#${command.details.plugin}.${command.details.command}`)?.duration ?? 0 : 0 + const command = Performance.results.find((r) => r.name.startsWith('config.runCommand')) + const commandLoadTime = command + ? Performance.getResult(`plugin.findCommand#${command.details.plugin}.${command.details.command}`) + ?.duration ?? 0 + : 0 - const pluginLoadTimes = Object.fromEntries(Performance.results - .filter(({name}) => name.startsWith('plugin.load#')) - .sort((a, b) => b.duration - a.duration) - .map(({scope, duration, details}) => [scope, {duration, details}])) + const pluginLoadTimes = Object.fromEntries( + Performance.results + .filter(({name}) => name.startsWith('plugin.load#')) + .sort((a, b) => b.duration - a.duration) + .map(({scope, duration, details}) => [scope, {duration, details}]), + ) const hookRunTimes = Performance.results - .filter(({name}) => name.startsWith('config.runHook#')) - .reduce((acc, perfResult) => { - const event = perfResult.details.event as string - if (event) { - if (!acc[event]) acc[event] = {} - acc[event][perfResult.scope!] = perfResult.duration - } else { - const event = perfResult.scope! - if (!acc[event]) acc[event] = {} - acc[event].total = perfResult.duration - } - - return acc - }, {} as Record>) - - const pluginLoadTimeByType = Object.fromEntries(Performance.results - .filter(({name}) => name.startsWith('config.loadPlugins#')) - .sort((a, b) => b.duration - a.duration) - .map(({scope, duration}) => [scope, duration])) - - const commandRunTime = Performance.results.find(({name}) => name.startsWith('config.runCommand#'))?.duration ?? 0 + .filter(({name}) => name.startsWith('config.runHook#')) + .reduce( + (acc, perfResult) => { + const event = perfResult.details.event as string + if (event) { + if (!acc[event]) acc[event] = {} + acc[event][perfResult.scope!] = perfResult.duration + } else { + const event = perfResult.scope! + if (!acc[event]) acc[event] = {} + acc[event].total = perfResult.duration + } + + return acc + }, + {} as Record>, + ) + + const pluginLoadTimeByType = Object.fromEntries( + Performance.results + .filter(({name}) => name.startsWith('config.loadPlugins#')) + .sort((a, b) => b.duration - a.duration) + .map(({scope, duration}) => [scope, duration]), + ) + + const commandRunTime = + Performance.results.find(({name}) => name.startsWith('config.runCommand#'))?.duration ?? 0 Performance._highlights = { configLoadTime: Performance.getResult('config.load')?.duration ?? 0, diff --git a/src/settings.ts b/src/settings.ts index 9f797f800..41ce4c569 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -5,20 +5,20 @@ export type Settings = { * Useful to set in the ./bin/dev script. * oclif.settings.debug = true; */ - debug?: boolean; + debug?: boolean /** * The path to the error.log file. * * NOTE: This is read-only and setting it will have no effect. */ - errlog?: string; + errlog?: string /** * Set the terminal width to a specified number of columns (characters) * * Environment Variable: * OCLIF_COLUMNS=80 */ - columns?: number; + columns?: number /** * Try to use ts-node to load typescript source files instead of * javascript files. @@ -29,13 +29,13 @@ export type Settings = { * Environment Variable: * NODE_ENV=development */ - tsnodeEnabled?: boolean; + tsnodeEnabled?: boolean /** * Enable performance tracking. Resulting data is available in the `perf` property of the `Config` class. * This will be overridden by the `enablePerf` property passed into Config constructor. */ - performanceEnabled?: boolean; -}; + performanceEnabled?: boolean +} // Set global.oclif to the new object if it wasn't set before if (!(global as any).oclif) (global as any).oclif = {} diff --git a/src/util/aggregate-flags.ts b/src/util/aggregate-flags.ts index bd13b9fd7..704c9fd5c 100644 --- a/src/util/aggregate-flags.ts +++ b/src/util/aggregate-flags.ts @@ -12,7 +12,5 @@ export function aggregateFlags( enableJsonFlag: boolean | undefined, ): FlagInput { const combinedFlags = {...baseFlags, ...flags} - return (enableJsonFlag - ? {json, ...combinedFlags} - : combinedFlags) as FlagInput + return (enableJsonFlag ? {json, ...combinedFlags} : combinedFlags) as FlagInput } diff --git a/src/util/cache-command.ts b/src/util/cache-command.ts index 176f98516..6d3b0579e 100644 --- a/src/util/cache-command.ts +++ b/src/util/cache-command.ts @@ -10,64 +10,79 @@ import {cacheDefaultValue} from './cache-default-value' // and flags as well as add in the json flag if enableJsonFlag is enabled. function mergePrototype(result: Command.Class, cmd: Command.Class): Command.Class { const proto = Object.getPrototypeOf(cmd) - const filteredProto = pickBy(proto, v => v !== undefined) as Command.Class + const filteredProto = pickBy(proto, (v) => v !== undefined) as Command.Class return Object.keys(proto).length > 0 ? mergePrototype({...filteredProto, ...result} as Command.Class, proto) : result } -async function cacheFlags(cmdFlags: FlagInput, respectNoCacheDefault: boolean): Promise> { - const promises = Object.entries(cmdFlags).map(async ([name, flag]) => ([name, { +async function cacheFlags( + cmdFlags: FlagInput, + respectNoCacheDefault: boolean, +): Promise> { + const promises = Object.entries(cmdFlags).map(async ([name, flag]) => [ name, - char: flag.char, - summary: flag.summary, - hidden: flag.hidden, - required: flag.required, - helpLabel: flag.helpLabel, - helpGroup: flag.helpGroup, - description: flag.description, - dependsOn: flag.dependsOn, - relationships: flag.relationships, - exclusive: flag.exclusive, - deprecated: flag.deprecated, - deprecateAliases: flag.deprecateAliases, - aliases: flag.aliases, - charAliases: flag.charAliases, - noCacheDefault: flag.noCacheDefault, - ...flag.type === 'boolean' ? { - allowNo: flag.allowNo, - type: flag.type, - } : { - type: flag.type, - helpValue: flag.helpValue, - multiple: flag.multiple, - options: flag.options, - delimiter: flag.delimiter, - default: await cacheDefaultValue(flag, respectNoCacheDefault), - hasDynamicHelp: typeof flag.defaultHelp === 'function', + { + name, + char: flag.char, + summary: flag.summary, + hidden: flag.hidden, + required: flag.required, + helpLabel: flag.helpLabel, + helpGroup: flag.helpGroup, + description: flag.description, + dependsOn: flag.dependsOn, + relationships: flag.relationships, + exclusive: flag.exclusive, + deprecated: flag.deprecated, + deprecateAliases: flag.deprecateAliases, + aliases: flag.aliases, + charAliases: flag.charAliases, + noCacheDefault: flag.noCacheDefault, + ...(flag.type === 'boolean' + ? { + allowNo: flag.allowNo, + type: flag.type, + } + : { + type: flag.type, + helpValue: flag.helpValue, + multiple: flag.multiple, + options: flag.options, + delimiter: flag.delimiter, + default: await cacheDefaultValue(flag, respectNoCacheDefault), + hasDynamicHelp: typeof flag.defaultHelp === 'function', + }), }, - }])) + ]) return Object.fromEntries(await Promise.all(promises)) } -async function cacheArgs(cmdArgs: ArgInput, respectNoCacheDefault: boolean): Promise> { - const promises = Object.entries(cmdArgs).map(async ([name, arg]) => ([name, { +async function cacheArgs( + cmdArgs: ArgInput, + respectNoCacheDefault: boolean, +): Promise> { + const promises = Object.entries(cmdArgs).map(async ([name, arg]) => [ name, - description: arg.description, - required: arg.required, - options: arg.options, - default: await cacheDefaultValue(arg, respectNoCacheDefault), - hidden: arg.hidden, - noCacheDefault: arg.noCacheDefault, - }])) + { + name, + description: arg.description, + required: arg.required, + options: arg.options, + default: await cacheDefaultValue(arg, respectNoCacheDefault), + hidden: arg.hidden, + noCacheDefault: arg.noCacheDefault, + }, + ]) return Object.fromEntries(await Promise.all(promises)) } -export async function cacheCommand(uncachedCmd: Command.Class, plugin?: IPlugin, respectNoCacheDefault = false): Promise { +export async function cacheCommand( + uncachedCmd: Command.Class, + plugin?: IPlugin, + respectNoCacheDefault = false, +): Promise { const cmd = mergePrototype(uncachedCmd, uncachedCmd) - const flags = await cacheFlags( - aggregateFlags(cmd.flags, cmd.baseFlags, cmd.enableJsonFlag), - respectNoCacheDefault, - ) + const flags = await cacheFlags(aggregateFlags(cmd.flags, cmd.baseFlags, cmd.enableJsonFlag), respectNoCacheDefault) const args = await cacheArgs(ensureArgObject(cmd.args), respectNoCacheDefault) const stdProperties = { @@ -87,7 +102,7 @@ export async function cacheCommand(uncachedCmd: Command.Class, plugin?: IPlugin, deprecateAliases: cmd.deprecateAliases, flags, args, - hasDynamicHelp: Object.values(flags).some(f => f.hasDynamicHelp), + hasDynamicHelp: Object.values(flags).some((f) => f.hasDynamicHelp), } // do not include these properties in manifest @@ -104,8 +119,8 @@ export async function cacheCommand(uncachedCmd: Command.Class, plugin?: IPlugin, // Add in any additional properties that are not standard command properties. const stdKeysAndIgnored = new Set([...Object.keys(stdProperties), ...ignoreCommandProperties]) - const keysToAdd = Object.keys(cmd).filter(property => !stdKeysAndIgnored.has(property)) - const additionalProperties = Object.fromEntries(keysToAdd.map(key => [key, (cmd as any)[key]])) + const keysToAdd = Object.keys(cmd).filter((property) => !stdKeysAndIgnored.has(property)) + const additionalProperties = Object.fromEntries(keysToAdd.map((key) => [key, (cmd as any)[key]])) return {...stdProperties, ...additionalProperties} } diff --git a/src/util/index.ts b/src/util/index.ts index df4a90ec4..b7284ff4c 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -7,9 +7,11 @@ import {readFileSync} from 'node:fs' const debug = require('debug') -export function pickBy>(obj: T, fn: (i: T[keyof T]) => boolean): Partial { - return Object.entries(obj) - .reduce((o, [k, v]) => { +export function pickBy>( + obj: T, + fn: (i: T[keyof T]) => boolean, +): Partial { + return Object.entries(obj).reduce((o, [k, v]) => { if (fn(v)) o[k] = v return o }, {} as any) @@ -93,7 +95,7 @@ export async function exists(path: string): Promise { } export const dirExists = async (input: string): Promise => { - if (!await exists(input)) { + if (!(await exists(input))) { throw new Error(`No directory found at ${input}`) } @@ -106,7 +108,7 @@ export const dirExists = async (input: string): Promise => { } export const fileExists = async (input: string): Promise => { - if (!await exists(input)) { + if (!(await exists(input))) { throw new Error(`No file found at ${input}`) } @@ -137,8 +139,10 @@ export function requireJson(...pathParts: string[]): T { * @param args Either an array of args or an object of args * @returns ArgInput */ -export function ensureArgObject(args?: any[] | ArgInput | { [name: string]: Command.Arg.Cached}): ArgInput { - return (Array.isArray(args) ? (args ?? []).reduce((x, y) => ({...x, [y.name]: y}), {} as ArgInput) : args ?? {}) as ArgInput +export function ensureArgObject(args?: any[] | ArgInput | {[name: string]: Command.Arg.Cached}): ArgInput { + return ( + Array.isArray(args) ? (args ?? []).reduce((x, y) => ({...x, [y.name]: y}), {} as ArgInput) : args ?? {} + ) as ArgInput } export function uniq(arr: T[]): T[] { @@ -179,12 +183,14 @@ export function readJsonSync(path: string, parse: false): string export function readJsonSync(path: string, parse?: true): T export function readJsonSync(path: string, parse = true): T | string { const contents = readFileSync(path, 'utf8') - return parse ? JSON.parse(contents) as T : contents + return parse ? (JSON.parse(contents) as T) : contents } -export function mapValues, TResult>(obj: {[P in keyof T]: T[P]}, fn: (i: T[keyof T], k: keyof T) => TResult): {[P in keyof T]: TResult} { - return Object.entries(obj) - .reduce((o, [k, v]) => { +export function mapValues, TResult>( + obj: {[P in keyof T]: T[P]}, + fn: (i: T[keyof T], k: keyof T) => TResult, +): {[P in keyof T]: TResult} { + return Object.entries(obj).reduce((o, [k, v]) => { o[k] = fn(v as any, k as any) return o }, {} as any) diff --git a/test/cli-ux/export.test.ts b/test/cli-ux/export.test.ts index 4a62835b7..2e06dfb9d 100644 --- a/test/cli-ux/export.test.ts +++ b/test/cli-ux/export.test.ts @@ -20,4 +20,3 @@ describe('ux exports', () => { expect(typeof ExitError).to.be.equal('function') }) }) - diff --git a/test/cli-ux/fancy.ts b/test/cli-ux/fancy.ts index f6709c0d3..4cda54a82 100644 --- a/test/cli-ux/fancy.ts +++ b/test/cli-ux/fancy.ts @@ -7,16 +7,16 @@ import {ux} from '../../src/cli-ux' let count = 0 export const fancy = base -.do(async (ctx: {count: number; base: string}) => { - ctx.count = count++ - ctx.base = join(__dirname, '../tmp', `test-${ctx.count}`) - await rm(ctx.base, {recursive: true, force: true}) - const chalk = require('chalk') - chalk.level = 0 -}) -// eslint-disable-next-line unicorn/prefer-top-level-await -.finally(async () => { - await ux.done() -}) + .do(async (ctx: {count: number; base: string}) => { + ctx.count = count++ + ctx.base = join(__dirname, '../tmp', `test-${ctx.count}`) + await rm(ctx.base, {recursive: true, force: true}) + const chalk = require('chalk') + chalk.level = 0 + }) + // eslint-disable-next-line unicorn/prefer-top-level-await + .finally(async () => { + await ux.done() + }) export {FancyTypes, expect} from 'fancy-test' diff --git a/test/cli-ux/index.test.ts b/test/cli-ux/index.test.ts index 866449a9a..dbbe52e76 100644 --- a/test/cli-ux/index.test.ts +++ b/test/cli-ux/index.test.ts @@ -5,17 +5,16 @@ const hyperlinker = require('hyperlinker') describe('url', () => { fancy - .env({FORCE_HYPERLINK: '1'}, {clear: true}) - .stdout() - .do(() => ux.url('sometext', 'https://google.com')) - .it('renders hyperlink', ({stdout}) => { - expect(stdout).to.equal('sometext\n') - }) + .env({FORCE_HYPERLINK: '1'}, {clear: true}) + .stdout() + .do(() => ux.url('sometext', 'https://google.com')) + .it('renders hyperlink', ({stdout}) => { + expect(stdout).to.equal('sometext\n') + }) }) describe('hyperlinker', () => { - fancy - .it('renders hyperlink', () => { + fancy.it('renders hyperlink', () => { const link = hyperlinker('sometext', 'https://google.com', {}) // eslint-disable-next-line unicorn/escape-case const expected = '\u001b]8;;https://google.com\u0007sometext\u001b]8;;\u0007' diff --git a/test/cli-ux/prompt.test.ts b/test/cli-ux/prompt.test.ts index 59ae25ce6..25d399c37 100644 --- a/test/cli-ux/prompt.test.ts +++ b/test/cli-ux/prompt.test.ts @@ -8,68 +8,67 @@ import {fancy} from './fancy' describe('prompt', () => { fancy - .stdout() - .stderr() - .end('requires input', async () => { - const promptPromise = ux.prompt('Require input?') - process.stdin.emit('data', '') - process.stdin.emit('data', 'answer') - const answer = await promptPromise - await ux.done() - expect(answer).to.equal('answer') - }) + .stdout() + .stderr() + .end('requires input', async () => { + const promptPromise = ux.prompt('Require input?') + process.stdin.emit('data', '') + process.stdin.emit('data', 'answer') + const answer = await promptPromise + await ux.done() + expect(answer).to.equal('answer') + }) fancy - .stdout() - .stderr() - .stdin('y') - .end('confirm', async () => { - const promptPromise = ux.confirm('yes/no?') - const answer = await promptPromise - await ux.done() - expect(answer).to.equal(true) - }) + .stdout() + .stderr() + .stdin('y') + .end('confirm', async () => { + const promptPromise = ux.confirm('yes/no?') + const answer = await promptPromise + await ux.done() + expect(answer).to.equal(true) + }) fancy - .stdout() - .stderr() - .stdin('n') - .end('confirm', async () => { - const promptPromise = ux.confirm('yes/no?') - const answer = await promptPromise - await ux.done() - expect(answer).to.equal(false) - }) + .stdout() + .stderr() + .stdin('n') + .end('confirm', async () => { + const promptPromise = ux.confirm('yes/no?') + const answer = await promptPromise + await ux.done() + expect(answer).to.equal(false) + }) fancy - .stdout() - .stderr() - .stdin('x') - .end('gets anykey', async () => { - const promptPromise = ux.anykey() - const answer = await promptPromise - await ux.done() - expect(answer).to.equal('x') - }) + .stdout() + .stderr() + .stdin('x') + .end('gets anykey', async () => { + const promptPromise = ux.anykey() + const answer = await promptPromise + await ux.done() + expect(answer).to.equal('x') + }) fancy - .stdout() - .stderr() - .end('does not require input', async () => { - const promptPromise = ux.prompt('Require input?', { - required: false, + .stdout() + .stderr() + .end('does not require input', async () => { + const promptPromise = ux.prompt('Require input?', { + required: false, + }) + process.stdin.emit('data', '') + const answer = await promptPromise + await ux.done() + expect(answer).to.equal('') }) - process.stdin.emit('data', '') - const answer = await promptPromise - await ux.done() - expect(answer).to.equal('') - }) fancy - .stdout() - .stderr() - .it('timeouts with no input', async () => { - await expect(ux.prompt('Require input?', {timeout: 1})) - .to.eventually.be.rejectedWith('Prompt timeout') - }) + .stdout() + .stderr() + .it('timeouts with no input', async () => { + await expect(ux.prompt('Require input?', {timeout: 1})).to.eventually.be.rejectedWith('Prompt timeout') + }) }) diff --git a/test/cli-ux/styled/object.test.ts b/test/cli-ux/styled/object.test.ts index 8c35056bd..4e14957df 100644 --- a/test/cli-ux/styled/object.test.ts +++ b/test/cli-ux/styled/object.test.ts @@ -3,9 +3,7 @@ import {expect, fancy} from 'fancy-test' import {ux} from '../../../src/cli-ux' describe('styled/object', () => { - fancy - .stdout() - .end('shows a table', output => { + fancy.stdout().end('shows a table', (output) => { ux.styledObject([ {foo: 1, bar: 1}, {foo: 2, bar: 2}, diff --git a/test/cli-ux/styled/progress.test.ts b/test/cli-ux/styled/progress.test.ts index 63ecd754a..6233bfe86 100644 --- a/test/cli-ux/styled/progress.test.ts +++ b/test/cli-ux/styled/progress.test.ts @@ -3,8 +3,7 @@ import {ux} from '../../../src/cli-ux' describe('progress', () => { // single bar - fancy - .end('single bar has default settings', _ => { + fancy.end('single bar has default settings', (_) => { const b1 = ux.progress({format: 'Example 1: Progress {bar} | {percentage}%'}) // @ts-expect-error because private member expect(b1.options.format).to.contain('Example 1: Progress') @@ -13,8 +12,7 @@ describe('progress', () => { }) // testing no settings passed, default settings created - fancy - .end('single bar, no bars array', _ => { + fancy.end('single bar, no bars array', (_) => { const b1 = ux.progress({}) // @ts-expect-error because private member expect(b1.options.format).to.contain('progress') @@ -24,9 +22,8 @@ describe('progress', () => { expect(b1.options.noTTYOutput).to.not.be.null }) // testing getProgressBar returns correct type - fancy - .end('typeof progress bar is object', _ => { + fancy.end('typeof progress bar is object', (_) => { const b1 = ux.progress({format: 'Example 1: Progress {bar} | {percentage}%'}) - expect(typeof (b1)).to.equal('object') + expect(typeof b1).to.equal('object') }) }) diff --git a/test/cli-ux/styled/table.e2e.ts b/test/cli-ux/styled/table.e2e.ts index c590209f1..ad921f706 100644 --- a/test/cli-ux/styled/table.e2e.ts +++ b/test/cli-ux/styled/table.e2e.ts @@ -3,9 +3,7 @@ import {ux} from '../../../src/cli-ux' describe('styled/table', () => { describe('null/undefined handling', () => { - fancy - .stdout() - .end('omits nulls and undefined by default', output => { + fancy.stdout().end('omits nulls and undefined by default', (output) => { const data = [{a: 1, b: '2', c: null, d: undefined}] ux.table(data, {a: {}, b: {}, c: {}, d: {}}) expect(output.stdout).to.include('1') @@ -17,10 +15,11 @@ describe('styled/table', () => { describe('scale tests', () => { const bigRows = 150_000 - fancy - .stdout() - .end('very tall tables don\'t exceed stack depth', output => { - const data = Array.from({length: bigRows}).fill({id: '123', name: 'foo', value: 'bar'}) as Record[] + fancy.stdout().end("very tall tables don't exceed stack depth", (output) => { + const data = Array.from({length: bigRows}).fill({id: '123', name: 'foo', value: 'bar'}) as Record< + string, + unknown + >[] const tallColumns = { id: {header: 'ID'}, name: {}, @@ -31,17 +30,16 @@ describe('styled/table', () => { expect(output.stdout).to.include('ID') }) - fancy - .stdout() - .end('very tall, wide tables don\'t exceed stack depth', output => { + fancy.stdout().end("very tall, wide tables don't exceed stack depth", (output) => { const columns = 100 const row = Object.fromEntries(Array.from({length: columns}).map((_, i) => [`col${i}`, 'foo'])) const data = Array.from({length: bigRows}).fill(row) as Record[] - const bigColumns = Object.fromEntries(Array.from({length: columns}).map((_, i) => [`col${i}`, {header: `col${i}`.toUpperCase()}])) + const bigColumns = Object.fromEntries( + Array.from({length: columns}).map((_, i) => [`col${i}`, {header: `col${i}`.toUpperCase()}]), + ) ux.table(data, bigColumns) expect(output.stdout).to.include('COL1') }) }) }) - diff --git a/test/cli-ux/styled/table.test.ts b/test/cli-ux/styled/table.test.ts index 6fa148a51..15058461c 100644 --- a/test/cli-ux/styled/table.test.ts +++ b/test/cli-ux/styled/table.test.ts @@ -70,14 +70,12 @@ const extendedHeader = `ID Name${ws.padEnd(14)}Web url${ws.padEnd(34)}Stack${ws // truncation rules? describe('styled/table', () => { - fancy - .end('export flags and display()', () => { - expect(typeof (ux.table.flags())).to.eq('object') - expect(typeof (ux.table)).to.eq('function') + fancy.end('export flags and display()', () => { + expect(typeof ux.table.flags()).to.eq('object') + expect(typeof ux.table).to.eq('function') }) - fancy - .end('has optional flags', _ => { + fancy.end('has optional flags', (_) => { const flags = ux.table.flags() expect(flags.columns).to.exist expect(flags.sort).to.exist @@ -89,9 +87,7 @@ describe('styled/table', () => { expect(flags['no-header']).to.exist }) - fancy - .stdout() - .end('displays table', output => { + fancy.stdout().end('displays table', (output) => { ux.table(apps, columns) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${ws} @@ -100,16 +96,12 @@ describe('styled/table', () => { }) describe('columns', () => { - fancy - .stdout() - .end('use header value for id', output => { + fancy.stdout().end('use header value for id', (output) => { ux.table(apps, columns) expect(output.stdout.slice(1, 3)).to.equal('ID') }) - fancy - .stdout() - .end('shows extended columns/uses get() for value', output => { + fancy.stdout().end('shows extended columns/uses get() for value', (output) => { ux.table(apps, columns, {extended: true}) expect(output.stdout).to.equal(`${ws}${extendedHeader} โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${ws} @@ -119,16 +111,12 @@ describe('styled/table', () => { }) describe('options', () => { - fancy - .stdout() - .end('shows extended columns', output => { + fancy.stdout().end('shows extended columns', (output) => { ux.table(apps, columns, {extended: true}) expect(output.stdout).to.contain(extendedHeader) }) - fancy - .stdout() - .end('shows title with divider', output => { + fancy.stdout().end('shows title with divider', (output) => { ux.table(apps, columns, {title: 'testing'}) expect(output.stdout).to.equal(`testing ======================= @@ -138,17 +126,13 @@ describe('styled/table', () => { | 321 supertable-test-2${ws}\n`) }) - fancy - .stdout() - .end('skips header', output => { + fancy.stdout().end('skips header', (output) => { ux.table(apps, columns, {'no-header': true}) expect(output.stdout).to.equal(` 123 supertable-test-1${ws} 321 supertable-test-2${ws}\n`) }) - fancy - .stdout() - .end('only displays given columns', output => { + fancy.stdout().end('only displays given columns', (output) => { ux.table(apps, columns, {columns: 'id'}) expect(output.stdout).to.equal(` ID${ws}${ws} โ”€โ”€โ”€${ws} @@ -156,36 +140,36 @@ describe('styled/table', () => { 321${ws}\n`) }) - fancy - .stdout() - .end('outputs in csv', output => { + fancy.stdout().end('outputs in csv', (output) => { ux.table(apps, columns, {output: 'csv'}) expect(output.stdout).to.equal(`ID,Name 123,supertable-test-1 321,supertable-test-2\n`) }) - fancy - .stdout() - .end('outputs in csv with escaped values', output => { - ux.table([ - { - id: '123\n2', - name: 'supertable-test-1', - }, - { - id: '12"3', - name: 'supertable-test-2', - }, - { - id: '123', - name: 'supertable-test-3,comma', - }, - { - id: '123', - name: 'supertable-test-4', - }, - ], columns, {output: 'csv'}) + fancy.stdout().end('outputs in csv with escaped values', (output) => { + ux.table( + [ + { + id: '123\n2', + name: 'supertable-test-1', + }, + { + id: '12"3', + name: 'supertable-test-2', + }, + { + id: '123', + name: 'supertable-test-3,comma', + }, + { + id: '123', + name: 'supertable-test-4', + }, + ], + columns, + {output: 'csv'}, + ) expect(output.stdout).to.equal(`ID,Name "123\n2","supertable-test-1" "12""3","supertable-test-2" @@ -193,26 +177,20 @@ describe('styled/table', () => { 123,supertable-test-4\n`) }) - fancy - .stdout() - .end('outputs in csv without headers', output => { + fancy.stdout().end('outputs in csv without headers', (output) => { ux.table(apps, columns, {output: 'csv', 'no-header': true}) expect(output.stdout).to.equal(`123,supertable-test-1 321,supertable-test-2\n`) }) - fancy - .stdout() - .end('outputs in csv with alias flag', output => { + fancy.stdout().end('outputs in csv with alias flag', (output) => { ux.table(apps, columns, {csv: true}) expect(output.stdout).to.equal(`ID,Name 123,supertable-test-1 321,supertable-test-2\n`) }) - fancy - .stdout() - .end('outputs in json', output => { + fancy.stdout().end('outputs in json', (output) => { ux.table(apps, columns, {output: 'json'}) expect(output.stdout).to.equal(`[ { @@ -227,9 +205,7 @@ describe('styled/table', () => { `) }) - fancy - .stdout() - .end('outputs in yaml', output => { + fancy.stdout().end('outputs in yaml', (output) => { ux.table(apps, columns, {output: 'yaml'}) expect(output.stdout).to.equal(`- id: '123' name: supertable-test-1 @@ -239,9 +215,7 @@ describe('styled/table', () => { `) }) - fancy - .stdout() - .end('sorts by property', output => { + fancy.stdout().end('sorts by property', (output) => { ux.table(apps, columns, {sort: '-name'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${ws} @@ -249,18 +223,14 @@ describe('styled/table', () => { 123 supertable-test-1${ws}\n`) }) - fancy - .stdout() - .end('filters by property & value (partial string match)', output => { + fancy.stdout().end('filters by property & value (partial string match)', (output) => { ux.table(apps, columns, {filter: 'id=123'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${ws} 123 supertable-test-1${ws}\n`) }) - fancy - .stdout() - .end('does not truncate', output => { + fancy.stdout().end('does not truncate', (output) => { const three = {...apps[0], id: '0'.repeat(80), name: 'supertable-test-3'} ux.table([...apps, three], columns, {filter: 'id=0', 'no-truncate': true}) expect(output.stdout).to.equal(` ID${ws.padEnd(78)} Name${ws.padEnd(14)} @@ -270,15 +240,13 @@ describe('styled/table', () => { }) describe('#flags', () => { - fancy - .end('includes only flags', _ => { + fancy.end('includes only flags', (_) => { const flags = ux.table.flags({only: 'columns'}) expect(flags.columns).to.be.a('object') expect((flags as any).sort).to.be.undefined }) - fancy - .end('excludes except flags', _ => { + fancy.end('excludes except flags', (_) => { const flags = ux.table.flags({except: 'columns'}) expect((flags as any).columns).to.be.undefined expect(flags.sort).to.be.a('object') @@ -286,9 +254,7 @@ describe('styled/table', () => { }) describe('edge cases', () => { - fancy - .stdout() - .end('ignores header case', output => { + fancy.stdout().end('ignores header case', (output) => { ux.table(apps, columns, {columns: 'iD,Name', filter: 'nAMe=supertable-test', sort: '-ID'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${ws} @@ -296,9 +262,7 @@ describe('styled/table', () => { 123 supertable-test-1${ws}\n`) }) - fancy - .stdout() - .end('displays multiline cell', output => { + fancy.stdout().end('displays multiline cell', (output) => { /* eslint-disable camelcase */ const app3 = { build_stack: { @@ -325,31 +289,31 @@ describe('styled/table', () => { } fancy - .do(() => { - Object.assign(screen, {stdtermwidth: 9}) - process.env.CLI_UX_SKIP_TTY_CHECK = 'true' - }) - .finally(() => { - Object.assign(screen, {stdtermwidth: orig.stdtermwidth}) - process.env.CLI_UX_SKIP_TTY_CHECK = orig.CLI_UX_SKIP_TTY_CHECK - }) - .stdout({stripColor: false}) - .end('correctly truncates columns with fullwidth characters or ansi escape sequences', output => { - /* eslint-disable camelcase */ - const app4 = { - build_stack: { - name: 'heroku-16', - }, - id: '456', - name: '\u001B[31m่ถ…็บง่กจๆ ผโ€”ๆต‹่ฏ•\u001B[0m', - web_url: 'https://supertable-test-1.herokuapp.com/', - } - /* eslint-enable camelcase */ - - ux.table([...apps, app4 as any], {name: {}}, {'no-header': true}) - expect(output.stdout).to.equal(` superโ€ฆ${ws} + .do(() => { + Object.assign(screen, {stdtermwidth: 9}) + process.env.CLI_UX_SKIP_TTY_CHECK = 'true' + }) + .finally(() => { + Object.assign(screen, {stdtermwidth: orig.stdtermwidth}) + process.env.CLI_UX_SKIP_TTY_CHECK = orig.CLI_UX_SKIP_TTY_CHECK + }) + .stdout({stripColor: false}) + .end('correctly truncates columns with fullwidth characters or ansi escape sequences', (output) => { + /* eslint-disable camelcase */ + const app4 = { + build_stack: { + name: 'heroku-16', + }, + id: '456', + name: '\u001B[31m่ถ…็บง่กจๆ ผโ€”ๆต‹่ฏ•\u001B[0m', + web_url: 'https://supertable-test-1.herokuapp.com/', + } + /* eslint-enable camelcase */ + + ux.table([...apps, app4 as any], {name: {}}, {'no-header': true}) + expect(output.stdout).to.equal(` superโ€ฆ${ws} superโ€ฆ${ws} \u001B[31m่ถ…็บง\u001B[39mโ€ฆ${ws}${ws}\n`) - }) + }) }) }) diff --git a/test/cli-ux/styled/tree.test.ts b/test/cli-ux/styled/tree.test.ts index 0706d0649..c628bdec0 100644 --- a/test/cli-ux/styled/tree.test.ts +++ b/test/cli-ux/styled/tree.test.ts @@ -3,9 +3,7 @@ import {expect, fancy} from 'fancy-test' import {ux} from '../../../src/cli-ux' describe('styled/tree', () => { - fancy - .stdout() - .end('shows the tree', output => { + fancy.stdout().end('shows the tree', (output) => { const tree = ux.tree() tree.insert('foo') tree.insert('bar') diff --git a/test/command/command.test.ts b/test/command/command.test.ts index ee8991892..892284216 100644 --- a/test/command/command.test.ts +++ b/test/command/command.test.ts @@ -23,56 +23,54 @@ class CodeError extends Error { describe('command', () => { fancy - .stdout() - .do(() => Command.run([])) - .do(output => expect(output.stdout).to.equal('foo\n')) - .it('logs to stdout') + .stdout() + .do(() => Command.run([])) + .do((output) => expect(output.stdout).to.equal('foo\n')) + .it('logs to stdout') fancy - .do(async () => { - class Command extends Base { - static description = 'test command' + .do(async () => { + class Command extends Base { + static description = 'test command' - async run() { - return 101 + async run() { + return 101 + } } - } - expect(await Command.run([])).to.equal(101) - }) - .it('returns value') + expect(await Command.run([])).to.equal(101) + }) + .it('returns value') fancy - .do(() => { - class Command extends Base { - async run() { - throw new Error('new x error') + .do(() => { + class Command extends Base { + async run() { + throw new Error('new x error') + } } - } - return Command.run([]) - }) - .catch(/new x error/) - .it('errors out') + return Command.run([]) + }) + .catch(/new x error/) + .it('errors out') fancy - .stdout() - .do(() => { - class Command extends Base { - async run() { - this.exit(0) + .stdout() + .do(() => { + class Command extends Base { + async run() { + this.exit(0) + } } - } - return Command.run([]) - }) - .catch(/EEXIT: 0/) - .it('exits with 0') + return Command.run([]) + }) + .catch(/EEXIT: 0/) + .it('exits with 0') describe('parse', () => { - fancy - .stdout() - .it('has a flag', async ctx => { + fancy.stdout().it('has a flag', async (ctx) => { class CMD extends Base { static flags = { foo: Flags.string(), @@ -91,18 +89,18 @@ describe('command', () => { describe('.log()', () => { fancy - .stdout() - .do(async () => { - class CMD extends Command { - async run() { - this.log('json output: %j', {a: 'foobar'}) + .stdout() + .do(async () => { + class CMD extends Command { + async run() { + this.log('json output: %j', {a: 'foobar'}) + } } - } - await CMD.run([]) - }) - .do(ctx => expect(ctx.stdout).to.equal('json output: {"a":"foobar"}\n')) - .it('uses util.format()') + await CMD.run([]) + }) + .do((ctx) => expect(ctx.stdout).to.equal('json output: {"a":"foobar"}\n')) + .it('uses util.format()') }) describe('flags with deprecated aliases', () => { @@ -123,326 +121,331 @@ describe('command', () => { } fancy - .stdout() - .stderr() - .do(async () => CMD.run(['--username', 'astro'])) - .do(ctx => expect(ctx.stderr).to.include('Warning: The "--username" flag has been deprecated. Use "--name | -o"')) - .it('shows warning for deprecated flag alias') + .stdout() + .stderr() + .do(async () => CMD.run(['--username', 'astro'])) + .do((ctx) => + expect(ctx.stderr).to.include('Warning: The "--username" flag has been deprecated. Use "--name | -o"'), + ) + .it('shows warning for deprecated flag alias') fancy - .stdout() - .stderr() - .do(async () => CMD.run(['--target-user', 'astro'])) - .do(ctx => expect(ctx.stderr).to.include('Warning: The "--target-user" flag has been deprecated. Use "--name | -o"')) - .it('shows warning for deprecated flag alias') + .stdout() + .stderr() + .do(async () => CMD.run(['--target-user', 'astro'])) + .do((ctx) => + expect(ctx.stderr).to.include('Warning: The "--target-user" flag has been deprecated. Use "--name | -o"'), + ) + .it('shows warning for deprecated flag alias') fancy - .stdout() - .stderr() - .do(async () => CMD.run(['-u', 'astro'])) - .do(ctx => expect(ctx.stderr).to.include('Warning: The "-u" flag has been deprecated. Use "--name | -o"')) - .it('shows warning for deprecated short char flag alias') + .stdout() + .stderr() + .do(async () => CMD.run(['-u', 'astro'])) + .do((ctx) => expect(ctx.stderr).to.include('Warning: The "-u" flag has been deprecated. Use "--name | -o"')) + .it('shows warning for deprecated short char flag alias') fancy - .stdout() - .stderr() - .do(async () => CMD.run(['--name', 'username'])) - .do(ctx => expect(ctx.stderr).to.be.empty) - .it('shows no warning when using proper flag name with a value that matches a flag alias') + .stdout() + .stderr() + .do(async () => CMD.run(['--name', 'username'])) + .do((ctx) => expect(ctx.stderr).to.be.empty) + .it('shows no warning when using proper flag name with a value that matches a flag alias') fancy - .stdout() - .stderr() - .do(async () => CMD.run(['--other', 'target-user'])) - .do(ctx => expect(ctx.stderr).to.be.empty) - .it('shows no warning when using another flag with a value that matches a deprecated flag alias') + .stdout() + .stderr() + .do(async () => CMD.run(['--other', 'target-user'])) + .do((ctx) => expect(ctx.stderr).to.be.empty) + .it('shows no warning when using another flag with a value that matches a deprecated flag alias') fancy - .stdout() - .stderr() - .do(async () => CMD.run(['--name', 'u'])) - .do(ctx => expect(ctx.stderr).to.be.empty) - .it('shows no warning when proper flag name with a value that matches a short char flag alias') + .stdout() + .stderr() + .do(async () => CMD.run(['--name', 'u'])) + .do((ctx) => expect(ctx.stderr).to.be.empty) + .it('shows no warning when proper flag name with a value that matches a short char flag alias') }) describe('deprecated flags', () => { fancy - .stdout() - .stderr() - .do(async () => { - class CMD extends Command { - static flags = { - name: Flags.string({ - deprecated: { - to: '--full-name', - version: '2.0.0', - }, - }), - force: Flags.boolean(), - } - - async run() { - await this.parse(CMD) - this.log('running command') + .stdout() + .stderr() + .do(async () => { + class CMD extends Command { + static flags = { + name: Flags.string({ + deprecated: { + to: '--full-name', + version: '2.0.0', + }, + }), + force: Flags.boolean(), + } + + async run() { + await this.parse(CMD) + this.log('running command') + } } - } - await CMD.run(['--name', 'astro']) - }) - .do(ctx => expect(ctx.stderr).to.include('Warning: The "name" flag has been deprecated')) - .it('shows warning for deprecated flags') + await CMD.run(['--name', 'astro']) + }) + .do((ctx) => expect(ctx.stderr).to.include('Warning: The "name" flag has been deprecated')) + .it('shows warning for deprecated flags') }) describe('deprecated flags that are not provided', () => { fancy - .stdout() - .stderr() - .do(async () => { - class CMD extends Command { - static flags = { - name: Flags.string({ - deprecated: { - to: '--full-name', - version: '2.0.0', - }, - }), - force: Flags.boolean(), + .stdout() + .stderr() + .do(async () => { + class CMD extends Command { + static flags = { + name: Flags.string({ + deprecated: { + to: '--full-name', + version: '2.0.0', + }, + }), + force: Flags.boolean(), + } + + async run() { + await this.parse(CMD) + this.log('running command') + } } - async run() { - await this.parse(CMD) - this.log('running command') - } - } - - await CMD.run(['--force']) - }) - .do(ctx => expect(ctx.stderr).to.not.include('Warning: The "name" flag has been deprecated')) - .it('does not show warning for deprecated flags if they are not provided') + await CMD.run(['--force']) + }) + .do((ctx) => expect(ctx.stderr).to.not.include('Warning: The "name" flag has been deprecated')) + .it('does not show warning for deprecated flags if they are not provided') }) describe('deprecated state', () => { fancy - .stdout() - .stderr() - .do(async () => { - class CMD extends Command { - static id = 'my:command' - static state = 'deprecated' - - async run() { - this.log('running command') + .stdout() + .stderr() + .do(async () => { + class CMD extends Command { + static id = 'my:command' + static state = 'deprecated' + + async run() { + this.log('running command') + } } - } - await CMD.run([]) - }) - .do(ctx => expect(ctx.stderr).to.include('Warning: The "my:command" command has been deprecated')) - .it('shows warning for deprecated command') + await CMD.run([]) + }) + .do((ctx) => expect(ctx.stderr).to.include('Warning: The "my:command" command has been deprecated')) + .it('shows warning for deprecated command') }) describe('deprecated state with options', () => { fancy - .stdout() - .stderr() - .do(async () => { - class CMD extends Command { - static id = 'my:command' - static state = 'deprecated' - static deprecationOptions = { - version: '2.0.0', - to: 'my:other:command', + .stdout() + .stderr() + .do(async () => { + class CMD extends Command { + static id = 'my:command' + static state = 'deprecated' + static deprecationOptions = { + version: '2.0.0', + to: 'my:other:command', + } + + async run() { + this.log('running command') + } } - async run() { - this.log('running command') - } - } - - await CMD.run([]) - }) - .do(ctx => { - expect(ctx.stderr).to.include('Warning: The "my:command" command has been deprecated') - expect(ctx.stderr).to.include('in version 2.0.0') - expect(ctx.stderr).to.include('Use "my:other:command" instead') - }) - .it('shows warning for deprecated command with custom options') + await CMD.run([]) + }) + .do((ctx) => { + expect(ctx.stderr).to.include('Warning: The "my:command" command has been deprecated') + expect(ctx.stderr).to.include('in version 2.0.0') + expect(ctx.stderr).to.include('Use "my:other:command" instead') + }) + .it('shows warning for deprecated command with custom options') }) describe('stdout err', () => { fancy - .stdout() - .do(async () => { - class CMD extends Command { - async run() { - process.stdout.emit('error', new CodeError('dd')) + .stdout() + .do(async () => { + class CMD extends Command { + async run() { + process.stdout.emit('error', new CodeError('dd')) + } } - } - await CMD.run([]) - }) - .catch(/dd/) - .it('test stdout error throws') + await CMD.run([]) + }) + .catch(/dd/) + .it('test stdout error throws') fancy - .stdout() - .do(async () => { - class CMD extends Command { - async run() { - process.stdout.emit('error', new CodeError('EPIPE')) - this.log('json output: %j', {a: 'foobar'}) + .stdout() + .do(async () => { + class CMD extends Command { + async run() { + process.stdout.emit('error', new CodeError('EPIPE')) + this.log('json output: %j', {a: 'foobar'}) + } } - } - await CMD.run([]) - }) - .do(ctx => expect(ctx.stdout).to.equal('json output: {"a":"foobar"}\n')) - .it('test stdout EPIPE swallowed') + await CMD.run([]) + }) + .do((ctx) => expect(ctx.stdout).to.equal('json output: {"a":"foobar"}\n')) + .it('test stdout EPIPE swallowed') }) describe('json enabled and pass-through tests', () => { fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true - - async run() { - this.log('not json output') + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true + + async run() { + this.log('not json output') + } } - } - const cmd = new CMD([], {} as any) - expect(cmd.jsonEnabled()).to.equal(false) - }) - .it('json enabled/pass through disabled/no --json flag/jsonEnabled() should be false') + const cmd = new CMD([], {} as any) + expect(cmd.jsonEnabled()).to.equal(false) + }) + .it('json enabled/pass through disabled/no --json flag/jsonEnabled() should be false') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true - async run() {} - } + async run() {} + } - const cmd = new CMD(['--json'], {} as any) - expect(cmd.jsonEnabled()).to.equal(true) - }) - .it('json enabled/pass through disabled/--json flag before --/jsonEnabled() should be true') + const cmd = new CMD(['--json'], {} as any) + expect(cmd.jsonEnabled()).to.equal(true) + }) + .it('json enabled/pass through disabled/--json flag before --/jsonEnabled() should be true') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true - async run() {} - } + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true + async run() {} + } - // mock a scopedEnvVar being set to JSON - const cmd = new CMD([], { - bin: 'FOO', scopedEnvVar: (foo: string) => foo.includes('CONTENT_TYPE') ? 'json' : undefined, - } as any) - expect(cmd.jsonEnabled()).to.equal(true) - }) - .it('json enabled from env') + // mock a scopedEnvVar being set to JSON + const cmd = new CMD([], { + bin: 'FOO', + scopedEnvVar: (foo: string) => (foo.includes('CONTENT_TYPE') ? 'json' : undefined), + } as any) + expect(cmd.jsonEnabled()).to.equal(true) + }) + .it('json enabled from env') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true - - async run() { - const {flags} = await cmd.parse(CMD, ['--json']) - expect(flags.json).to.equal(true, 'json flag should be true') + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true + + async run() { + const {flags} = await cmd.parse(CMD, ['--json']) + expect(flags.json).to.equal(true, 'json flag should be true') + } } - } - const cmd = new CMD(['--json'], {} as any) - expect(cmd.jsonEnabled()).to.equal(true) - }) - .it('json enabled/pass through enabled/--json flag before --/jsonEnabled() should be true') + const cmd = new CMD(['--json'], {} as any) + expect(cmd.jsonEnabled()).to.equal(true) + }) + .it('json enabled/pass through enabled/--json flag before --/jsonEnabled() should be true') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true - - async run() { - const {flags} = await cmd.parse(CMD, ['--', '--json']) - expect(flags.json).to.equal(false, 'json flag should be false') - // expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true + + async run() { + const {flags} = await cmd.parse(CMD, ['--', '--json']) + expect(flags.json).to.equal(false, 'json flag should be false') + // expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + } } - } - const cmd = new CMD(['--', '--json'], {} as any) - expect(cmd.jsonEnabled()).to.equal(false) - }) - .it('json enabled/pass through enabled/--json flag after --/jsonEnabled() should be false') + const cmd = new CMD(['--', '--json'], {} as any) + expect(cmd.jsonEnabled()).to.equal(false) + }) + .it('json enabled/pass through enabled/--json flag after --/jsonEnabled() should be false') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true - - async run() { - const {flags} = await cmd.parse(CMD, ['--foo', '--json']) - expect(flags.json).to.equal(true, 'json flag should be true') + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true + + async run() { + const {flags} = await cmd.parse(CMD, ['--foo', '--json']) + expect(flags.json).to.equal(true, 'json flag should be true') + } } - } - const cmd = new CMD(['foo', '--json'], {} as any) - expect(cmd.jsonEnabled()).to.equal(true) - }) - .it('json enabled/pass through enabled/--json flag before --/extra param/jsonEnabled() should be true') + const cmd = new CMD(['foo', '--json'], {} as any) + expect(cmd.jsonEnabled()).to.equal(true) + }) + .it('json enabled/pass through enabled/--json flag before --/extra param/jsonEnabled() should be true') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true - - async run() { - const {flags} = await cmd.parse(CMD, ['--foo', '--', '--json']) - expect(flags.json).to.equal(false, 'json flag should be false') - // expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true + + async run() { + const {flags} = await cmd.parse(CMD, ['--foo', '--', '--json']) + expect(flags.json).to.equal(false, 'json flag should be false') + // expect(this.passThroughEnabled).to.equal(true, 'pass through should be true') + } } - } - const cmd = new CMD(['--foo', '--', '--json'], {} as any) - expect(cmd.jsonEnabled()).to.equal(false) - }) - .it('json enabled/pass through enabled/--json flag after --/extra param/jsonEnabled() should be false') + const cmd = new CMD(['--foo', '--', '--json'], {} as any) + expect(cmd.jsonEnabled()).to.equal(false) + }) + .it('json enabled/pass through enabled/--json flag after --/extra param/jsonEnabled() should be false') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = true + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = true - async run() {} - } + async run() {} + } - const cmd = new CMD(['--json', '--'], {} as any) - expect(cmd.jsonEnabled()).to.equal(true) - }) - .it('json enabled/pass through enabled/--json flag before --/jsonEnabled() should be true') + const cmd = new CMD(['--json', '--'], {} as any) + expect(cmd.jsonEnabled()).to.equal(true) + }) + .it('json enabled/pass through enabled/--json flag before --/jsonEnabled() should be true') fancy - .stdout() - .do(async () => { - class CMD extends Command { - static enableJsonFlag = false - static '--' = true + .stdout() + .do(async () => { + class CMD extends Command { + static enableJsonFlag = false + static '--' = true - async run() {} - } + async run() {} + } - const cmd = new CMD(['--json'], {} as any) - expect(cmd.jsonEnabled()).to.equal(false) - }) - .it('json disabled/pass through enable/--json flag before --/jsonEnabled() should be false') + const cmd = new CMD(['--json'], {} as any) + expect(cmd.jsonEnabled()).to.equal(false) + }) + .it('json disabled/pass through enable/--json flag before --/jsonEnabled() should be false') }) }) diff --git a/test/command/fixtures/typescript/tsconfig.json b/test/command/fixtures/typescript/tsconfig.json index 75c61de88..8d0083147 100644 --- a/test/command/fixtures/typescript/tsconfig.json +++ b/test/command/fixtures/typescript/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "outDir": "./lib", - "rootDirs": [ - "./src" - ] + "rootDirs": ["./src"] }, - "include": [ - "./src/**/*" - ] + "include": ["./src/**/*"] } diff --git a/test/command/helpers/test-help-in-lib/lib/test-help-plugin.js b/test/command/helpers/test-help-in-lib/lib/test-help-plugin.js index 77e76fd87..1bad603da 100644 --- a/test/command/helpers/test-help-in-lib/lib/test-help-plugin.js +++ b/test/command/helpers/test-help-in-lib/lib/test-help-plugin.js @@ -1,20 +1,20 @@ /* eslint-disable */ -'use strict'; -Object.defineProperty (exports, '__esModule', {value: true}); -const sinon_1 = require ('sinon'); +'use strict' +Object.defineProperty(exports, '__esModule', {value: true}) +const sinon_1 = require('sinon') class default_1 { - constructor (config, opts) { - this.showCommandHelp = sinon_1.spy (() => { - console.log ('hello from test-help-plugin #showCommandHelp in the lib folder and in compiled javascript'); - }); - this.showHelp = sinon_1.spy (() => { - console.log ('hello showHelp'); - }); - config.showCommandHelpSpy = this.showCommandHelp; - config.showHelpSpy = this.showHelp; + constructor(config, opts) { + this.showCommandHelp = sinon_1.spy(() => { + console.log('hello from test-help-plugin #showCommandHelp in the lib folder and in compiled javascript') + }) + this.showHelp = sinon_1.spy(() => { + console.log('hello showHelp') + }) + config.showCommandHelpSpy = this.showCommandHelp + config.showHelpSpy = this.showHelp } - command () { - throw new Error ('not needed for testing @oclif/command'); + command() { + throw new Error('not needed for testing @oclif/command') } } -exports.default = default_1; +exports.default = default_1 diff --git a/test/command/helpers/test-help-in-src/src/test-help-plugin.ts b/test/command/helpers/test-help-in-src/src/test-help-plugin.ts index de25f5e50..60ccafb70 100644 --- a/test/command/helpers/test-help-in-src/src/test-help-plugin.ts +++ b/test/command/helpers/test-help-in-src/src/test-help-plugin.ts @@ -1,7 +1,7 @@ import {SinonSpy, spy} from 'sinon' import {HelpBase, Interfaces} from '../../../../../src' -export type TestHelpClassConfig = Interfaces.Config & { showCommandHelpSpy?: SinonSpy; showHelpSpy?: SinonSpy } +export type TestHelpClassConfig = Interfaces.Config & {showCommandHelpSpy?: SinonSpy; showHelpSpy?: SinonSpy} export default class extends HelpBase { constructor(config: any, opts: any) { diff --git a/test/command/main-esm.test.ts b/test/command/main-esm.test.ts index d971a48aa..93525219d 100644 --- a/test/command/main-esm.test.ts +++ b/test/command/main-esm.test.ts @@ -15,21 +15,22 @@ root = convertToFileURL(root) describe('main-esm', () => { fancy - .stdout() - .do(() => run(['plugins'], root)) - .do((output: any) => expect(output.stdout).to.equal('No plugins installed.\n')) - .it('runs plugins') + .stdout() + .do(() => run(['plugins'], root)) + .do((output: any) => expect(output.stdout).to.equal('No plugins installed.\n')) + .it('runs plugins') fancy - .stdout() - .do(() => run(['--version'], root)) - .do((output: any) => expect(output.stdout).to.equal(version + '\n')) - .it('runs --version') + .stdout() + .do(() => run(['--version'], root)) + .do((output: any) => expect(output.stdout).to.equal(version + '\n')) + .it('runs --version') fancy - .stdout() - .do(() => run(['--help'], root)) - .do((output: any) => expect(output.stdout).to.equal(`base library for oclif CLIs + .stdout() + .do(() => run(['--help'], root)) + .do((output: any) => + expect(output.stdout).to.equal(`base library for oclif CLIs VERSION ${version} @@ -44,13 +45,15 @@ COMMANDS help Display help for oclif. plugins List installed plugins. -`)) - .it('runs --help') +`), + ) + .it('runs --help') fancy - .stdout() - .do(() => run(['--help', 'foo'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) - .do((output: any) => expect(output.stdout).to.equal(`foo topic description + .stdout() + .do(() => run(['--help', 'foo'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) + .do((output: any) => + expect(output.stdout).to.equal(`foo topic description USAGE $ oclif-esm foo COMMAND @@ -61,13 +64,15 @@ TOPICS COMMANDS foo baz foo baz description -`)) - .it('runs spaced topic help') +`), + ) + .it('runs spaced topic help') fancy - .stdout() - .do(() => run(['foo', 'bar', '--help'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) - .do((output: any) => expect(output.stdout).to.equal(`foo bar topic description + .stdout() + .do(() => run(['foo', 'bar', '--help'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) + .do((output: any) => + expect(output.stdout).to.equal(`foo bar topic description USAGE $ oclif-esm foo bar COMMAND @@ -76,18 +81,19 @@ COMMANDS foo bar fail fail description foo bar succeed succeed description -`)) - .it('runs spaced topic help v2') +`), + ) + .it('runs spaced topic help v2') fancy - .stdout() - .do(() => run(['foo', 'baz'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) - .do((output: any) => expect(output.stdout).to.equal('running Baz\n')) - .it('runs foo:baz with space separator') + .stdout() + .do(() => run(['foo', 'baz'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) + .do((output: any) => expect(output.stdout).to.equal('running Baz\n')) + .it('runs foo:baz with space separator') fancy - .stdout() - .do(() => run(['foo', 'bar', 'succeed'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) - .do((output: any) => expect(output.stdout).to.equal('it works!\n')) - .it('runs foo:bar:succeed with space separator') + .stdout() + .do(() => run(['foo', 'bar', 'succeed'], convertToFileURL(resolve(__dirname, 'fixtures/esm/package.json')))) + .do((output: any) => expect(output.stdout).to.equal('it works!\n')) + .it('runs foo:bar:succeed with space separator') }) diff --git a/test/command/main.test.ts b/test/command/main.test.ts index 1b9b45583..0aa5a1f40 100644 --- a/test/command/main.test.ts +++ b/test/command/main.test.ts @@ -1,4 +1,3 @@ - import {expect} from 'chai' import {resolve} from 'node:path' import {SinonSandbox, SinonStub, createSandbox} from 'sinon' @@ -35,7 +34,7 @@ describe('main', () => { it('should run help', async () => { await run(['--help'], resolve(__dirname, '../../package.json')) - expect(stdoutStub.args.map(a => stripAnsi(a[0])).join('')).to.equal(`base library for oclif CLIs + expect(stdoutStub.args.map((a) => stripAnsi(a[0])).join('')).to.equal(`base library for oclif CLIs VERSION ${version} @@ -55,7 +54,7 @@ COMMANDS it('should show help for topics with spaces', async () => { await run(['--help', 'foo'], resolve(__dirname, 'fixtures/typescript/package.json')) - expect(stdoutStub.args.map(a => stripAnsi(a[0])).join('')).to.equal(`foo topic description + expect(stdoutStub.args.map((a) => stripAnsi(a[0])).join('')).to.equal(`foo topic description USAGE $ oclif foo COMMAND @@ -71,7 +70,7 @@ COMMANDS it('should run spaced topic help v2', async () => { await run(['foo', 'bar', '--help'], resolve(__dirname, 'fixtures/typescript/package.json')) - expect(stdoutStub.args.map(a => stripAnsi(a[0])).join('')).to.equal(`foo bar topic description + expect(stdoutStub.args.map((a) => stripAnsi(a[0])).join('')).to.equal(`foo bar topic description USAGE $ oclif foo bar COMMAND diff --git a/test/config/config.flexible.test.ts b/test/config/config.flexible.test.ts index 6b451da95..be6944696 100644 --- a/test/config/config.flexible.test.ts +++ b/test/config/config.flexible.test.ts @@ -9,12 +9,12 @@ import * as util from '../../src/util' import {join} from 'node:path' interface Options { - pjson?: any; - homedir?: string; - platform?: string; - env?: {[k: string]: string}; - commandIds?: string[]; - types?: string[]; + pjson?: any + homedir?: string + platform?: string + env?: {[k: string]: string} + commandIds?: string[] + types?: string[] } class MyCommandClass extends Command { @@ -42,10 +42,10 @@ describe('Config with flexible taxonomy', () => { types = [], }: Options = {}) => { let test = fancy - .resetConfig() - .env(env, {clear: true}) - .stub(util, 'getHomeDir', stub => stub.returns(join(homedir))) - .stub(util, 'getPlatform', stub => stub.returns(platform)) + .resetConfig() + .env(env, {clear: true}) + .stub(util, 'getHomeDir', (stub) => stub.returns(join(homedir))) + .stub(util, 'getPlatform', (stub) => stub.returns(platform)) const load = async (): Promise => {} const findCommand = async (): Promise => MyCommandClass @@ -144,117 +144,115 @@ describe('Config with flexible taxonomy', () => { // @ts-ignore return { it(expectation: string, fn: (config: Interfaces.Config) => any) { - test - .do(({config}) => fn(config)) - .it(expectation) + test.do(({config}) => fn(config)).it(expectation) return this }, } } testConfig() - .it('has populated topic index', config => { - // @ts-expect-error because private member - const topics = config._topics - expect(topics.has('foo')).to.be.true - expect(topics.has('foo:bar')).to.be.true - expect(topics.has('foo:baz')).to.be.true - }) - .it('has populated command permutation index', config => { - // @ts-expect-error because private member - const {commandPermutations} = config - expect(commandPermutations.get('foo')).to.deep.equal(new Set(['foo:bar', 'foo:baz'])) - expect(commandPermutations.get('foo:bar')).to.deep.equal(new Set(['foo:bar'])) - expect(commandPermutations.get('bar')).to.deep.equal(new Set(['foo:bar'])) - expect(commandPermutations.get('bar:foo')).to.deep.equal(new Set(['foo:bar'])) - expect(commandPermutations.get('foo:baz')).to.deep.equal(new Set(['foo:baz'])) - expect(commandPermutations.get('baz')).to.deep.equal(new Set(['foo:baz'])) - expect(commandPermutations.get('baz:foo')).to.deep.equal(new Set(['foo:baz'])) - }) - .it('has populated command index', config => { - // @ts-expect-error because private member - const commands = config._commands - expect(commands.has('foo:bar')).to.be.true - expect(commands.has('foo:baz')).to.be.true - }) - .it('has all command id permutations', config => { - expect(config.getAllCommandIDs()).to.deep.equal([ - 'foo:bar', - 'foo:baz', - 'bar:foo', - 'baz:foo', - ]) - }) - - describe('findMatches', () => { - testConfig() - .it('finds command that contains a partial id', config => { - const matches = config.findMatches('foo', []) - expect(matches.length).to.equal(2) + .it('has populated topic index', (config) => { + // @ts-expect-error because private member + const topics = config._topics + expect(topics.has('foo')).to.be.true + expect(topics.has('foo:bar')).to.be.true + expect(topics.has('foo:baz')).to.be.true + }) + .it('has populated command permutation index', (config) => { + // @ts-expect-error because private member + const {commandPermutations} = config + expect(commandPermutations.get('foo')).to.deep.equal(new Set(['foo:bar', 'foo:baz'])) + expect(commandPermutations.get('foo:bar')).to.deep.equal(new Set(['foo:bar'])) + expect(commandPermutations.get('bar')).to.deep.equal(new Set(['foo:bar'])) + expect(commandPermutations.get('bar:foo')).to.deep.equal(new Set(['foo:bar'])) + expect(commandPermutations.get('foo:baz')).to.deep.equal(new Set(['foo:baz'])) + expect(commandPermutations.get('baz')).to.deep.equal(new Set(['foo:baz'])) + expect(commandPermutations.get('baz:foo')).to.deep.equal(new Set(['foo:baz'])) }) - .it('finds command that contains a partial id and matching full flag', config => { - const matches = config.findMatches('foo', ['--flagB']) - expect(matches.length).to.equal(1) - expect(matches[0].id).to.equal('foo:baz') + .it('has populated command index', (config) => { + // @ts-expect-error because private member + const commands = config._commands + expect(commands.has('foo:bar')).to.be.true + expect(commands.has('foo:baz')).to.be.true }) - .it('finds command that contains a partial id and matching short flag', config => { - const matches = config.findMatches('foo', ['-a']) - expect(matches.length).to.equal(1) - expect(matches[0].id).to.equal('foo:bar') + .it('has all command id permutations', (config) => { + expect(config.getAllCommandIDs()).to.deep.equal(['foo:bar', 'foo:baz', 'bar:foo', 'baz:foo']) }) + + describe('findMatches', () => { + testConfig() + .it('finds command that contains a partial id', (config) => { + const matches = config.findMatches('foo', []) + expect(matches.length).to.equal(2) + }) + .it('finds command that contains a partial id and matching full flag', (config) => { + const matches = config.findMatches('foo', ['--flagB']) + expect(matches.length).to.equal(1) + expect(matches[0].id).to.equal('foo:baz') + }) + .it('finds command that contains a partial id and matching short flag', (config) => { + const matches = config.findMatches('foo', ['-a']) + expect(matches.length).to.equal(1) + expect(matches[0].id).to.equal('foo:bar') + }) }) describe('findCommand', () => { - testConfig() - .it('find command with no duplicates', config => { + testConfig().it('find command with no duplicates', (config) => { const command = config.findCommand('foo:bar', {must: true}) expect(command).to.have.property('pluginAlias', '@My/plugina') }) - testConfig({commandIds: ['foo:bar', 'foo:bar']}) - .it('find command with duplicates and choose the one that appears first in oclif.plugins', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('pluginAlias', '@My/pluginb') - }) + testConfig({commandIds: ['foo:bar', 'foo:bar']}).it( + 'find command with duplicates and choose the one that appears first in oclif.plugins', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('pluginAlias', '@My/pluginb') + }, + ) - testConfig({types: ['core', 'user']}) - .it('find command with no duplicates core/user', config => { + testConfig({types: ['core', 'user']}).it('find command with no duplicates core/user', (config) => { const command = config.findCommand('foo:bar', {must: true}) expect(command).to.have.property('id', 'foo:bar') expect(command).to.have.property('pluginType', 'core') expect(command).to.have.property('pluginAlias', '@My/plugina') }) - testConfig({types: ['user', 'core']}) - .it('find command with no duplicates user/core', config => { + testConfig({types: ['user', 'core']}).it('find command with no duplicates user/core', (config) => { const command = config.findCommand('foo:bar', {must: true}) expect(command).to.have.property('id', 'foo:bar') expect(command).to.have.property('pluginType', 'user') expect(command).to.have.property('pluginAlias', '@My/plugina') }) - testConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['core', 'user']}) - .it('find command with duplicates core/user', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('id', 'foo:bar') - expect(command).to.have.property('pluginType', 'core') - expect(command).to.have.property('pluginAlias', '@My/plugina') - }) + testConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['core', 'user']}).it( + 'find command with duplicates core/user', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('id', 'foo:bar') + expect(command).to.have.property('pluginType', 'core') + expect(command).to.have.property('pluginAlias', '@My/plugina') + }, + ) - testConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'core']}) - .it('find command with duplicates user/core', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('id', 'foo:bar') - expect(command).to.have.property('pluginType', 'core') - expect(command).to.have.property('pluginAlias', '@My/pluginb') - }) + testConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'core']}).it( + 'find command with duplicates user/core', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('id', 'foo:bar') + expect(command).to.have.property('pluginType', 'core') + expect(command).to.have.property('pluginAlias', '@My/pluginb') + }, + ) - testConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'user']}) - .it('find command with duplicates user/user', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('id', 'foo:bar') - expect(command).to.have.property('pluginType', 'user') - expect(command).to.have.property('pluginAlias', '@My/plugina') - }) + testConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'user']}).it( + 'find command with duplicates user/user', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('id', 'foo:bar') + expect(command).to.have.property('pluginType', 'user') + expect(command).to.have.property('pluginAlias', '@My/plugina') + }, + ) }) }) diff --git a/test/config/config.test.ts b/test/config/config.test.ts index 1b821d793..d92c9f2a7 100644 --- a/test/config/config.test.ts +++ b/test/config/config.test.ts @@ -8,12 +8,12 @@ import {Config, Interfaces} from '../../src' import {Command} from '../../src/command' interface Options { - pjson?: any; - homedir?: string; - platform?: string; - env?: {[k: string]: string}; - commandIds?: string[]; - types?: string[]; + pjson?: any + homedir?: string + platform?: string + env?: {[k: string]: string} + commandIds?: string[] + types?: string[] } const pjson = { @@ -47,18 +47,17 @@ const pjson = { describe('Config', () => { const testConfig = ({pjson, homedir = '/my/home', platform = 'darwin', env = {}}: Options = {}) => { let test = fancy - .resetConfig() - .env(env, {clear: true}) - .stub(util, 'getHomeDir', stub => stub.returns(join(homedir))) - .stub(util, 'getPlatform', stub => stub.returns(platform)) - if (pjson) test = test.stub(util, 'readJson', stub => stub.resolves(pjson)) + .resetConfig() + .env(env, {clear: true}) + .stub(util, 'getHomeDir', (stub) => stub.returns(join(homedir))) + .stub(util, 'getPlatform', (stub) => stub.returns(platform)) + if (pjson) test = test.stub(util, 'readJson', (stub) => stub.resolves(pjson)) test = test.add('config', () => Config.load()) return { hasS3Key(k: keyof Interfaces.PJSON.S3.Templates, expected: string, extra: any = {}) { - return this - .it(`renders ${k} template as ${expected}`, config => { + return this.it(`renders ${k} template as ${expected}`, (config) => { // Config.load reads the package.json to determine the version and channel // In order to allow prerelease branches to pass, we need to strip the prerelease // tag from the version and switch the channel to stable. @@ -79,13 +78,10 @@ describe('Config', () => { }) }, hasProperty(k: K | undefined, v: Interfaces.Config[K] | undefined) { - return this - .it(`has ${k}=${v}`, config => expect(config).to.have.property(k!, v)) + return this.it(`has ${k}=${v}`, (config) => expect(config).to.have.property(k!, v)) }, it(expectation: string, fn: (config: Interfaces.Config) => any) { - test - .do(({config}) => fn(config)) - .it(expectation) + test.do(({config}) => fn(config)).it(expectation) return this }, } @@ -93,62 +89,61 @@ describe('Config', () => { describe('darwin', () => { testConfig() - .hasProperty('cacheDir', join('/my/home/Library/Caches/@oclif/core')) - .hasProperty('configDir', join('/my/home/.config/@oclif/core')) - .hasProperty('errlog', join('/my/home/Library/Caches/@oclif/core/error.log')) - .hasProperty('dataDir', join('/my/home/.local/share/@oclif/core')) - .hasProperty('home', join('/my/home')) + .hasProperty('cacheDir', join('/my/home/Library/Caches/@oclif/core')) + .hasProperty('configDir', join('/my/home/.config/@oclif/core')) + .hasProperty('errlog', join('/my/home/Library/Caches/@oclif/core/error.log')) + .hasProperty('dataDir', join('/my/home/.local/share/@oclif/core')) + .hasProperty('home', join('/my/home')) }) describe('binAliases', () => { - testConfig({pjson}) - .it('will have binAliases set', config => { + testConfig({pjson}).it('will have binAliases set', (config) => { expect(config.binAliases).to.deep.equal(['bar', 'baz']) }) - testConfig({pjson}).it('will get scoped env vars with bin aliases', config => { + testConfig({pjson}).it('will get scoped env vars with bin aliases', (config) => { expect(config.scopedEnvVarKeys('abc')).to.deep.equal(['FOO_ABC', 'BAR_ABC', 'BAZ_ABC']) }) - testConfig({pjson}).it('will get scoped env vars', config => { + testConfig({pjson}).it('will get scoped env vars', (config) => { expect(config.scopedEnvVarKey('abc')).to.equal('FOO_ABC') }) - testConfig({pjson}).it('will get scopedEnvVar', config => { + testConfig({pjson}).it('will get scopedEnvVar', (config) => { process.env.FOO_ABC = 'find me' expect(config.scopedEnvVar('abc')).to.deep.equal('find me') delete process.env.FOO_ABC }) - testConfig({pjson}).it('will get scopedEnvVar via alias', config => { + testConfig({pjson}).it('will get scopedEnvVar via alias', (config) => { process.env.BAZ_ABC = 'find me' expect(config.scopedEnvVar('abc')).to.deep.equal('find me') delete process.env.BAZ_ABC }) - testConfig({pjson}).it('will get scoped env vars', config => { + testConfig({pjson}).it('will get scoped env vars', (config) => { expect(config.scopedEnvVarKey('abc')).to.equal('FOO_ABC') }) - testConfig({pjson}).it('will get scopedEnvVarTrue', config => { + testConfig({pjson}).it('will get scopedEnvVarTrue', (config) => { process.env.FOO_ABC = 'true' expect(config.scopedEnvVarTrue('abc')).to.equal(true) delete process.env.FOO_ABC }) - testConfig({pjson}).it('will get scopedEnvVarTrue via alias', config => { + testConfig({pjson}).it('will get scopedEnvVarTrue via alias', (config) => { process.env.BAR_ABC = 'true' expect(config.scopedEnvVarTrue('abc')).to.equal(true) delete process.env.BAR_ABC }) - testConfig({pjson}).it('will get scopedEnvVarTrue=1', config => { + testConfig({pjson}).it('will get scopedEnvVarTrue=1', (config) => { process.env.FOO_ABC = '1' expect(config.scopedEnvVarTrue('abc')).to.equal(true) delete process.env.FOO_ABC }) - testConfig({pjson}).it('will get scopedEnvVarTrue=1 via alias', config => { + testConfig({pjson}).it('will get scopedEnvVarTrue=1 via alias', (config) => { process.env.BAR_ABC = '1' expect(config.scopedEnvVarTrue('abc')).to.equal(true) delete process.env.BAR_ABC @@ -157,11 +152,11 @@ describe('Config', () => { describe('linux', () => { testConfig({platform: 'linux'}) - .hasProperty('cacheDir', join('/my/home/.cache/@oclif/core')) - .hasProperty('configDir', join('/my/home/.config/@oclif/core')) - .hasProperty('errlog', join('/my/home/.cache/@oclif/core/error.log')) - .hasProperty('dataDir', join('/my/home/.local/share/@oclif/core')) - .hasProperty('home', join('/my/home')) + .hasProperty('cacheDir', join('/my/home/.cache/@oclif/core')) + .hasProperty('configDir', join('/my/home/.config/@oclif/core')) + .hasProperty('errlog', join('/my/home/.cache/@oclif/core/error.log')) + .hasProperty('dataDir', join('/my/home/.local/share/@oclif/core')) + .hasProperty('home', join('/my/home')) }) describe('win32', () => { @@ -169,34 +164,37 @@ describe('Config', () => { platform: 'win32', env: {LOCALAPPDATA: '/my/home/localappdata'}, }) - .hasProperty('cacheDir', join('/my/home/localappdata/@oclif\\core')) - .hasProperty('configDir', join('/my/home/localappdata/@oclif\\core')) - .hasProperty('errlog', join('/my/home/localappdata/@oclif\\core/error.log')) - .hasProperty('dataDir', join('/my/home/localappdata/@oclif\\core')) - .hasProperty('home', join('/my/home')) + .hasProperty('cacheDir', join('/my/home/localappdata/@oclif\\core')) + .hasProperty('configDir', join('/my/home/localappdata/@oclif\\core')) + .hasProperty('errlog', join('/my/home/localappdata/@oclif\\core/error.log')) + .hasProperty('dataDir', join('/my/home/localappdata/@oclif\\core')) + .hasProperty('home', join('/my/home')) }) describe('s3Key', () => { const target = {platform: 'darwin', arch: 'x64'} const beta = {version: '2.0.0-beta', channel: 'beta'} testConfig() - .hasS3Key('baseDir', 'oclif-cli') - .hasS3Key('manifest', 'version') - .hasS3Key('manifest', 'channels/beta/version', beta) - .hasS3Key('manifest', 'darwin-x64', target) - .hasS3Key('manifest', 'channels/beta/darwin-x64', {...beta, ...target}) - .hasS3Key('unversioned', 'oclif-cli.tar.gz') - .hasS3Key('unversioned', 'oclif-cli.tar.gz') - .hasS3Key('unversioned', 'channels/beta/oclif-cli.tar.gz', beta) - .hasS3Key('unversioned', 'channels/beta/oclif-cli.tar.gz', beta) - .hasS3Key('unversioned', 'oclif-cli-darwin-x64.tar.gz', target) - .hasS3Key('unversioned', 'oclif-cli-darwin-x64.tar.gz', target) - .hasS3Key('unversioned', 'channels/beta/oclif-cli-darwin-x64.tar.gz', {...beta, ...target}) - .hasS3Key('unversioned', 'channels/beta/oclif-cli-darwin-x64.tar.gz', {...beta, ...target}) - .hasS3Key('versioned', 'oclif-cli-v1.0.0/oclif-cli-v1.0.0.tar.gz') - .hasS3Key('versioned', 'oclif-cli-v1.0.0/oclif-cli-v1.0.0-darwin-x64.tar.gz', target) - .hasS3Key('versioned', 'channels/beta/oclif-cli-v2.0.0-beta/oclif-cli-v2.0.0-beta.tar.gz', beta) - .hasS3Key('versioned', 'channels/beta/oclif-cli-v2.0.0-beta/oclif-cli-v2.0.0-beta-darwin-x64.tar.gz', {...beta, ...target}) + .hasS3Key('baseDir', 'oclif-cli') + .hasS3Key('manifest', 'version') + .hasS3Key('manifest', 'channels/beta/version', beta) + .hasS3Key('manifest', 'darwin-x64', target) + .hasS3Key('manifest', 'channels/beta/darwin-x64', {...beta, ...target}) + .hasS3Key('unversioned', 'oclif-cli.tar.gz') + .hasS3Key('unversioned', 'oclif-cli.tar.gz') + .hasS3Key('unversioned', 'channels/beta/oclif-cli.tar.gz', beta) + .hasS3Key('unversioned', 'channels/beta/oclif-cli.tar.gz', beta) + .hasS3Key('unversioned', 'oclif-cli-darwin-x64.tar.gz', target) + .hasS3Key('unversioned', 'oclif-cli-darwin-x64.tar.gz', target) + .hasS3Key('unversioned', 'channels/beta/oclif-cli-darwin-x64.tar.gz', {...beta, ...target}) + .hasS3Key('unversioned', 'channels/beta/oclif-cli-darwin-x64.tar.gz', {...beta, ...target}) + .hasS3Key('versioned', 'oclif-cli-v1.0.0/oclif-cli-v1.0.0.tar.gz') + .hasS3Key('versioned', 'oclif-cli-v1.0.0/oclif-cli-v1.0.0-darwin-x64.tar.gz', target) + .hasS3Key('versioned', 'channels/beta/oclif-cli-v2.0.0-beta/oclif-cli-v2.0.0-beta.tar.gz', beta) + .hasS3Key('versioned', 'channels/beta/oclif-cli-v2.0.0-beta/oclif-cli-v2.0.0-beta-darwin-x64.tar.gz', { + ...beta, + ...target, + }) }) describe('options', () => { @@ -208,8 +206,7 @@ describe('Config', () => { }) }) - testConfig() - .it('has s3Url', config => { + testConfig().it('has s3Url', (config) => { const orig = config.pjson.oclif.update.s3.host config.pjson.oclif.update.s3.host = 'https://bar.com/a/' expect(config.s3Url('/b/c')).to.equal('https://bar.com/a/b/c') @@ -218,13 +215,13 @@ describe('Config', () => { testConfig({ pjson, - }) - .it('has subtopics', config => { - expect(config.topics.map(t => t.name)).to.have.members(['t1', 't1:t1-1', 't1:t1-1:t1-1-1', 't1:t1-1:t1-1-2']) + }).it('has subtopics', (config) => { + expect(config.topics.map((t) => t.name)).to.have.members(['t1', 't1:t1-1', 't1:t1-1:t1-1-1', 't1:t1-1:t1-1-2']) }) describe('findCommand', () => { - const findCommandTestConfig = ({pjson, + const findCommandTestConfig = ({ + pjson, homedir = '/my/home', platform = 'darwin', env = {}, @@ -250,7 +247,12 @@ describe('Config', () => { const commandPluginA: Command.Loadable = { strict: false, - aliases: [], args: {}, flags: {}, hidden: false, id: commandIds[0], async load(): Promise { + aliases: [], + args: {}, + flags: {}, + hidden: false, + id: commandIds[0], + async load(): Promise { return MyCommandClass }, pluginType: types[0] ?? 'core', @@ -258,14 +260,20 @@ describe('Config', () => { } const commandPluginB: Command.Loadable = { strict: false, - aliases: [], args: {}, flags: {}, hidden: false, id: commandIds[1], async load(): Promise { + aliases: [], + args: {}, + flags: {}, + hidden: false, + id: commandIds[1], + async load(): Promise { return MyCommandClass }, pluginType: types[1] ?? 'core', pluginAlias: '@My/pluginb', } const hooks = {} - const pluginA: IPlugin = {load, + const pluginA: IPlugin = { + load, findCommand, name: '@My/plugina', alias: '@My/plugina', @@ -307,12 +315,12 @@ describe('Config', () => { } const plugins = new Map().set(pluginA.name, pluginA).set(pluginB.name, pluginB) let test = fancy - .resetConfig() - .env(env, {clear: true}) - .stub(util, 'getHomeDir', stub => stub.returns(join(homedir))) - .stub(util, 'getPlatform', stub => stub.returns(platform)) + .resetConfig() + .env(env, {clear: true}) + .stub(util, 'getHomeDir', (stub) => stub.returns(join(homedir))) + .stub(util, 'getPlatform', (stub) => stub.returns(platform)) - if (pjson) test = test.stub(util, 'readJson', stub => stub.resolves(pjson)) + if (pjson) test = test.stub(util, 'readJson', (stub) => stub.resolves(pjson)) test = test.add('config', async () => { const config = await Config.load() config.plugins = plugins @@ -330,58 +338,61 @@ describe('Config', () => { // @ts-ignore return { it(expectation: string, fn: (config: Interfaces.Config) => any) { - test - .do(({config}) => fn(config)) - .it(expectation) + test.do(({config}) => fn(config)).it(expectation) return this }, } } - findCommandTestConfig() - .it('find command with no duplicates', config => { + findCommandTestConfig().it('find command with no duplicates', (config) => { const command = config.findCommand('foo:bar', {must: true}) expect(command).to.have.property('pluginAlias', '@My/plugina') }) - findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar']}) - .it('find command with duplicates and choose the one that appears first in oclif.plugins', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('pluginAlias', '@My/pluginb') - }) - findCommandTestConfig({types: ['core', 'user']}) - .it('find command with no duplicates core/user', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('id', 'foo:bar') - expect(command).to.have.property('pluginType', 'core') - expect(command).to.have.property('pluginAlias', '@My/plugina') - }) - findCommandTestConfig({types: ['user', 'core']}) - .it('find command with no duplicates user/core', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('id', 'foo:bar') - expect(command).to.have.property('pluginType', 'user') - expect(command).to.have.property('pluginAlias', '@My/plugina') - }) - findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['core', 'user']}) - .it('find command with duplicates core/user', config => { + findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar']}).it( + 'find command with duplicates and choose the one that appears first in oclif.plugins', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('pluginAlias', '@My/pluginb') + }, + ) + findCommandTestConfig({types: ['core', 'user']}).it('find command with no duplicates core/user', (config) => { const command = config.findCommand('foo:bar', {must: true}) expect(command).to.have.property('id', 'foo:bar') expect(command).to.have.property('pluginType', 'core') expect(command).to.have.property('pluginAlias', '@My/plugina') }) - findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'core']}) - .it('find command with duplicates user/core', config => { - const command = config.findCommand('foo:bar', {must: true}) - expect(command).to.have.property('id', 'foo:bar') - expect(command).to.have.property('pluginType', 'core') - expect(command).to.have.property('pluginAlias', '@My/pluginb') - }) - findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'user']}) - .it('find command with duplicates user/user', config => { + findCommandTestConfig({types: ['user', 'core']}).it('find command with no duplicates user/core', (config) => { const command = config.findCommand('foo:bar', {must: true}) expect(command).to.have.property('id', 'foo:bar') expect(command).to.have.property('pluginType', 'user') expect(command).to.have.property('pluginAlias', '@My/plugina') }) + findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['core', 'user']}).it( + 'find command with duplicates core/user', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('id', 'foo:bar') + expect(command).to.have.property('pluginType', 'core') + expect(command).to.have.property('pluginAlias', '@My/plugina') + }, + ) + findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'core']}).it( + 'find command with duplicates user/core', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('id', 'foo:bar') + expect(command).to.have.property('pluginType', 'core') + expect(command).to.have.property('pluginAlias', '@My/pluginb') + }, + ) + findCommandTestConfig({commandIds: ['foo:bar', 'foo:bar'], types: ['user', 'user']}).it( + 'find command with duplicates user/user', + (config) => { + const command = config.findCommand('foo:bar', {must: true}) + expect(command).to.have.property('id', 'foo:bar') + expect(command).to.have.property('pluginType', 'user') + expect(command).to.have.property('pluginAlias', '@My/plugina') + }, + ) }) }) diff --git a/test/config/esm.test.ts b/test/config/esm.test.ts index 2d3b1a0d2..54dc4978d 100644 --- a/test/config/esm.test.ts +++ b/test/config/esm.test.ts @@ -11,27 +11,21 @@ const p = (p: string) => join(root, p) // This tests file URL / import.meta.url simulation. const rootAsFileURL = url.pathToFileURL(root).toString() -const withConfig = fancy -.add('config', () => Config.load(rootAsFileURL)) +const withConfig = fancy.add('config', () => Config.load(rootAsFileURL)) describe('esm', () => { - withConfig - .it('has commandsDir', ({config}) => { + withConfig.it('has commandsDir', ({config}) => { expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) - withConfig - .stdout() - .it('runs esm command and prerun & postrun hooks', async ctx => { + withConfig.stdout().it('runs esm command and prerun & postrun hooks', async (ctx) => { await ctx.config.runCommand('foo:bar:baz') expect(ctx.stdout).to.equal('running esm prerun hook\nit works!\nrunning esm postrun hook\n') }) - withConfig - .stdout() - .it('runs faulty command, only prerun hook triggers', async ctx => { + withConfig.stdout().it('runs faulty command, only prerun hook triggers', async (ctx) => { try { await ctx.config.runCommand('foo:bar:fail') } catch { @@ -41,16 +35,12 @@ describe('esm', () => { expect(ctx.stdout).to.equal('running esm prerun hook\nit fails!\ncaught error\n') }) - withConfig - .stdout() - .it('runs esm command, postrun hook captures command result', async ctx => { + withConfig.stdout().it('runs esm command, postrun hook captures command result', async (ctx) => { await ctx.config.runCommand('foo:bar:test-result') expect(ctx.stdout).to.equal('running esm prerun hook\nit works!\nrunning esm postrun hook\nreturned success!\n') }) - withConfig - .stdout() - .it('runs init hook', async ctx => { + withConfig.stdout().it('runs init hook', async (ctx) => { await (ctx.config.runHook as any)('init', {id: 'myid', argv: ['foo']}) expect(ctx.stdout).to.equal('running esm init hook\n') }) diff --git a/test/config/fixtures/help/package.json b/test/config/fixtures/help/package.json index 832fdfa34..a19b46139 100644 --- a/test/config/fixtures/help/package.json +++ b/test/config/fixtures/help/package.json @@ -6,7 +6,14 @@ "files": [], "oclif": { "commands": "./src/commands", - "additionalHelpFlags": ["-h", "--mycommandhelp"], - "additionalVersionFlags": ["-v", "myversion", "version"] + "additionalHelpFlags": [ + "-h", + "--mycommandhelp" + ], + "additionalVersionFlags": [ + "-v", + "myversion", + "version" + ] } } diff --git a/test/config/fixtures/mixed-esm-cjs/src/commands/foo/bar/test-result.js b/test/config/fixtures/mixed-esm-cjs/src/commands/foo/bar/test-result.js index 755ba23aa..5b93fd61e 100644 --- a/test/config/fixtures/mixed-esm-cjs/src/commands/foo/bar/test-result.js +++ b/test/config/fixtures/mixed-esm-cjs/src/commands/foo/bar/test-result.js @@ -4,4 +4,3 @@ export default class Command { return 'returned success!' } } - diff --git a/test/config/fixtures/typescript/src/hooks/postrun.ts b/test/config/fixtures/typescript/src/hooks/postrun.ts index f78977809..98a0d304a 100644 --- a/test/config/fixtures/typescript/src/hooks/postrun.ts +++ b/test/config/fixtures/typescript/src/hooks/postrun.ts @@ -1,4 +1,3 @@ - export default function postrun(options: any): void { console.log('running ts postrun hook') if (options.Command.id === 'foo:bar:test-result') { diff --git a/test/config/fixtures/typescript/tsconfig.json b/test/config/fixtures/typescript/tsconfig.json index 75c61de88..8d0083147 100644 --- a/test/config/fixtures/typescript/tsconfig.json +++ b/test/config/fixtures/typescript/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "outDir": "./lib", - "rootDirs": [ - "./src" - ] + "rootDirs": ["./src"] }, - "include": [ - "./src/**/*" - ] + "include": ["./src/**/*"] } diff --git a/test/config/help.config.test.ts b/test/config/help.config.test.ts index 3742c9455..71cf27819 100644 --- a/test/config/help.config.test.ts +++ b/test/config/help.config.test.ts @@ -13,16 +13,14 @@ const root = resolve(__dirname, 'fixtures/help') // This tests file URL / import.meta.url simulation. const rootAsFileURL = pathToFileURL(root).toString() -const withConfig = fancy -.add('config', () => Config.load(rootAsFileURL)) +const withConfig = fancy.add('config', () => Config.load(rootAsFileURL)) describe('help and version flag additions', () => { - withConfig - .it('has help and version additions', ({config}) => { + withConfig.it('has help and version additions', ({config}) => { expect(config.pjson.oclif.additionalHelpFlags).to.have.lengthOf(2) expect(config.pjson.oclif.additionalVersionFlags).to.have.lengthOf(3) const mergedHelpFlags = getHelpFlagAdditions(config) - expect(mergedHelpFlags).to.deep.equal(['--help', ...config.pjson.oclif.additionalHelpFlags as string[]]) + expect(mergedHelpFlags).to.deep.equal(['--help', ...(config.pjson.oclif.additionalHelpFlags as string[])]) expect(helpAddition(['-h'], config)).to.be.true expect(helpAddition(['help'], config)).to.be.false expect(helpAddition(['--mycommandhelp'], config)).to.be.true @@ -34,18 +32,18 @@ describe('help and version flag additions', () => { }) withConfig - .do(({config}) => delete config.pjson.oclif.additionalHelpFlags) - .it('has version additions', ({config}) => { - expect(config.pjson.oclif.additionalHelpFlags).to.not.be.ok - expect(config.pjson.oclif.additionalVersionFlags).to.have.lengthOf(3) - const mergedHelpFlags = getHelpFlagAdditions(config) - expect(mergedHelpFlags).to.deep.equal(['--help']) - expect(helpAddition(['-h'], config)).to.be.false - expect(helpAddition(['help'], config)).to.be.false - expect(helpAddition(['mycommandhelp'], config)).to.be.false - expect(versionAddition(['-v'], config)).to.be.true - expect(versionAddition(['version'], config)).to.be.true - expect(versionAddition(['myversion'], config)).to.be.true - expect(versionAddition(['notmyversion'], config)).to.be.false - }) + .do(({config}) => delete config.pjson.oclif.additionalHelpFlags) + .it('has version additions', ({config}) => { + expect(config.pjson.oclif.additionalHelpFlags).to.not.be.ok + expect(config.pjson.oclif.additionalVersionFlags).to.have.lengthOf(3) + const mergedHelpFlags = getHelpFlagAdditions(config) + expect(mergedHelpFlags).to.deep.equal(['--help']) + expect(helpAddition(['-h'], config)).to.be.false + expect(helpAddition(['help'], config)).to.be.false + expect(helpAddition(['mycommandhelp'], config)).to.be.false + expect(versionAddition(['-v'], config)).to.be.true + expect(versionAddition(['version'], config)).to.be.true + expect(versionAddition(['myversion'], config)).to.be.true + expect(versionAddition(['notmyversion'], config)).to.be.false + }) }) diff --git a/test/config/mixed-cjs-esm.test.ts b/test/config/mixed-cjs-esm.test.ts index 411275fde..b8f65f8fb 100644 --- a/test/config/mixed-cjs-esm.test.ts +++ b/test/config/mixed-cjs-esm.test.ts @@ -7,27 +7,21 @@ import {expect, fancy} from './test' const root = resolve(__dirname, 'fixtures/mixed-cjs-esm') const p = (p: string) => join(root, p) -const withConfig = fancy -.add('config', () => Config.load(root)) +const withConfig = fancy.add('config', () => Config.load(root)) describe('mixed-cjs-esm', () => { - withConfig - .it('has commandsDir', ({config}) => { + withConfig.it('has commandsDir', ({config}) => { expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) - withConfig - .stdout() - .it('runs mixed-cjs-esm command and prerun & postrun hooks', async ctx => { + withConfig.stdout().it('runs mixed-cjs-esm command and prerun & postrun hooks', async (ctx) => { await ctx.config.runCommand('foo:bar:baz') expect(ctx.stdout).to.equal('running mixed-cjs-esm prerun hook\nit works!\nrunning mixed-cjs-esm postrun hook\n') }) - withConfig - .stdout() - .it('runs faulty command, only prerun hook triggers', async ctx => { + withConfig.stdout().it('runs faulty command, only prerun hook triggers', async (ctx) => { try { await ctx.config.runCommand('foo:bar:fail') } catch { @@ -37,16 +31,14 @@ describe('mixed-cjs-esm', () => { expect(ctx.stdout).to.equal('running mixed-cjs-esm prerun hook\nit fails!\ncaught error\n') }) - withConfig - .stdout() - .it('runs mixed-cjs-esm command, postrun hook captures command result', async ctx => { + withConfig.stdout().it('runs mixed-cjs-esm command, postrun hook captures command result', async (ctx) => { await ctx.config.runCommand('foo:bar:test-result') - expect(ctx.stdout).to.equal('running mixed-cjs-esm prerun hook\nit works!\nrunning mixed-cjs-esm postrun hook\nreturned success!\n') + expect(ctx.stdout).to.equal( + 'running mixed-cjs-esm prerun hook\nit works!\nrunning mixed-cjs-esm postrun hook\nreturned success!\n', + ) }) - withConfig - .stdout() - .it('runs init hook', async ctx => { + withConfig.stdout().it('runs init hook', async (ctx) => { await (ctx.config.runHook as any)('init', {id: 'myid', argv: ['foo']}) expect(ctx.stdout).to.equal('running mixed-cjs-esm init hook\n') }) diff --git a/test/config/mixed-esm-cjs.test.ts b/test/config/mixed-esm-cjs.test.ts index 4d6ed3703..cceb363bf 100644 --- a/test/config/mixed-esm-cjs.test.ts +++ b/test/config/mixed-esm-cjs.test.ts @@ -7,27 +7,21 @@ import {expect, fancy} from './test' const root = resolve(__dirname, 'fixtures/mixed-esm-cjs') const p = (p: string) => join(root, p) -const withConfig = fancy -.add('config', () => Config.load(root)) +const withConfig = fancy.add('config', () => Config.load(root)) describe('mixed-cjs-esm', () => { - withConfig - .it('has commandsDir', ({config}) => { + withConfig.it('has commandsDir', ({config}) => { expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) - withConfig - .stdout() - .it('runs mixed-esm-cjs command and prerun & postrun hooks', async ctx => { + withConfig.stdout().it('runs mixed-esm-cjs command and prerun & postrun hooks', async (ctx) => { await ctx.config.runCommand('foo:bar:baz') expect(ctx.stdout).to.equal('running mixed-esm-cjs prerun hook\nit works!\nrunning mixed-esm-cjs postrun hook\n') }) - withConfig - .stdout() - .it('runs faulty command, only prerun hook triggers', async ctx => { + withConfig.stdout().it('runs faulty command, only prerun hook triggers', async (ctx) => { try { await ctx.config.runCommand('foo:bar:fail') } catch { @@ -37,16 +31,14 @@ describe('mixed-cjs-esm', () => { expect(ctx.stdout).to.equal('running mixed-esm-cjs prerun hook\nit fails!\ncaught error\n') }) - withConfig - .stdout() - .it('runs mixed-esm-cjs command, postrun hook captures command result', async ctx => { + withConfig.stdout().it('runs mixed-esm-cjs command, postrun hook captures command result', async (ctx) => { await ctx.config.runCommand('foo:bar:test-result') - expect(ctx.stdout).to.equal('running mixed-esm-cjs prerun hook\nit works!\nrunning mixed-esm-cjs postrun hook\nreturned success!\n') + expect(ctx.stdout).to.equal( + 'running mixed-esm-cjs prerun hook\nit works!\nrunning mixed-esm-cjs postrun hook\nreturned success!\n', + ) }) - withConfig - .stdout() - .it('runs init hook', async ctx => { + withConfig.stdout().it('runs init hook', async (ctx) => { await (ctx.config.runHook as any)('init', {id: 'myid', argv: ['foo']}) expect(ctx.stdout).to.equal('running mixed-esm-cjs init hook\n') }) diff --git a/test/config/test.ts b/test/config/test.ts index 1e7866017..fe843c7dd 100644 --- a/test/config/test.ts +++ b/test/config/test.ts @@ -2,8 +2,7 @@ import {fancy as base} from 'fancy-test' import {Interfaces} from '../../src' -export const fancy = base -.register('resetConfig', () => ({ +export const fancy = base.register('resetConfig', () => ({ run(ctx: {config: Interfaces.Config}) { // @ts-ignore delete ctx.config diff --git a/test/config/typescript.test.ts b/test/config/typescript.test.ts index 27d20c5cb..bfd8c85bd 100644 --- a/test/config/typescript.test.ts +++ b/test/config/typescript.test.ts @@ -7,27 +7,21 @@ import {expect, fancy} from './test' const root = resolve(__dirname, 'fixtures/typescript') const p = (p: string) => join(root, p) -const withConfig = fancy -.add('config', () => Config.load(root)) +const withConfig = fancy.add('config', () => Config.load(root)) describe('typescript', () => { - withConfig - .it('has commandsDir', ({config}) => { + withConfig.it('has commandsDir', ({config}) => { expect([...config.plugins.values()][0]).to.deep.include({ commandsDir: p('src/commands'), }) }) - withConfig - .stdout() - .it('runs ts command and prerun & postrun hooks', async ctx => { + withConfig.stdout().it('runs ts command and prerun & postrun hooks', async (ctx) => { await ctx.config.runCommand('foo:bar:baz') expect(ctx.stdout).to.equal('running ts prerun hook\nit works!\nrunning ts postrun hook\n') }) - withConfig - .stdout() - .it('runs faulty command, only prerun hook triggers', async ctx => { + withConfig.stdout().it('runs faulty command, only prerun hook triggers', async (ctx) => { try { await ctx.config.runCommand('foo:bar:fail') } catch { @@ -37,16 +31,12 @@ describe('typescript', () => { expect(ctx.stdout).to.equal('running ts prerun hook\nit fails!\ncaught error\n') }) - withConfig - .stdout() - .it('runs ts command, postrun hook captures command result', async ctx => { + withConfig.stdout().it('runs ts command, postrun hook captures command result', async (ctx) => { await ctx.config.runCommand('foo:bar:test-result') expect(ctx.stdout).to.equal('running ts prerun hook\nit works!\nrunning ts postrun hook\nreturned success!\n') }) - withConfig - .stdout() - .it('runs init hook', async ctx => { + withConfig.stdout().it('runs init hook', async (ctx) => { // to-do: fix union types await (ctx.config.runHook as any)('init', {id: 'myid', argv: ['foo']}) expect(ctx.stdout).to.equal('running ts init hook\n') diff --git a/test/config/util.test.ts b/test/config/util.test.ts index 6fad20e6a..3f05fcffe 100644 --- a/test/config/util.test.ts +++ b/test/config/util.test.ts @@ -3,23 +3,14 @@ import {collectUsableIds, getCommandIdPermutations} from '../../src/config/util' describe('util', () => { describe('collectUsableIds', () => { - test - .it('returns all usable command ids', async () => { + test.it('returns all usable command ids', async () => { const ids = collectUsableIds(['foo:bar:baz', 'one:two:three']) - expect(ids).to.deep.equal(new Set([ - 'foo', - 'foo:bar', - 'foo:bar:baz', - 'one', - 'one:two', - 'one:two:three', - ])) + expect(ids).to.deep.equal(new Set(['foo', 'foo:bar', 'foo:bar:baz', 'one', 'one:two', 'one:two:three'])) }) }) describe('getCommandIdPermutations', () => { - test - .it('returns all usable command ids', async () => { + test.it('returns all usable command ids', async () => { const permutations = getCommandIdPermutations('foo:bar:baz') expect(permutations).to.deep.equal([ 'foo:bar:baz', @@ -35,22 +26,30 @@ describe('util', () => { const numberOfPermutations = (commandID: string): number => { const num = commandID.split(':').length let result = 1 - for (let i = 2; i <= num; i++) - result *= i + for (let i = 2; i <= num; i++) result *= i return result } - test - .it('returns the correct number of permutations', async () => { + test.it('returns the correct number of permutations', async () => { expect(getCommandIdPermutations('one').length).to.equal(numberOfPermutations('one')) expect(getCommandIdPermutations('one:two').length).to.equal(numberOfPermutations('one:two')) expect(getCommandIdPermutations('one:two:three').length).to.equal(numberOfPermutations('one:two:three')) expect(getCommandIdPermutations('one:two:three:four').length).to.equal(numberOfPermutations('one:two:three:four')) - expect(getCommandIdPermutations('one:two:three:four:five').length).to.equal(numberOfPermutations('one:two:three:four:five')) - expect(getCommandIdPermutations('one:two:three:four:five:six').length).to.equal(numberOfPermutations('one:two:three:four:five:six')) - expect(getCommandIdPermutations('one:two:three:four:five:six:seven').length).to.equal(numberOfPermutations('one:two:three:four:five:six:seven')) - expect(getCommandIdPermutations('one:two:three:four:five:six:seven:eight').length).to.equal(numberOfPermutations('one:two:three:four:five:six:seven:eight')) - expect(getCommandIdPermutations('one:two:three:four:five:six:seven:eight:nine').length).to.equal(numberOfPermutations('one:two:three:four:five:six:seven:eight:nine')) + expect(getCommandIdPermutations('one:two:three:four:five').length).to.equal( + numberOfPermutations('one:two:three:four:five'), + ) + expect(getCommandIdPermutations('one:two:three:four:five:six').length).to.equal( + numberOfPermutations('one:two:three:four:five:six'), + ) + expect(getCommandIdPermutations('one:two:three:four:five:six:seven').length).to.equal( + numberOfPermutations('one:two:three:four:five:six:seven'), + ) + expect(getCommandIdPermutations('one:two:three:four:five:six:seven:eight').length).to.equal( + numberOfPermutations('one:two:three:four:five:six:seven:eight'), + ) + expect(getCommandIdPermutations('one:two:three:four:five:six:seven:eight:nine').length).to.equal( + numberOfPermutations('one:two:three:four:five:six:seven:eight:nine'), + ) }) }) }) diff --git a/test/errors/error.test.ts b/test/errors/error.test.ts index 15ab20a52..534b2d98a 100644 --- a/test/errors/error.test.ts +++ b/test/errors/error.test.ts @@ -4,98 +4,102 @@ import {PrettyPrintableError} from '../../src/interfaces/errors' describe('error', () => { fancy - .do(() => { - error('An error happened!') - }) - .catch((error: PrettyPrintableError) => { - expect(error.message).to.equal('An error happened!') - }) - .it('throws an error using a string argument') - - fancy - .do(() => { - error('An error happened!', {code: 'ERR', ref: 'https://oclif.com/error', suggestions: ['rm -rf node_modules']}) - }) - .catch((error: PrettyPrintableError) => { - expect(error.message).to.equal('An error happened!') - expect(error.code).to.equal('ERR') - expect(error.ref).to.equal('https://oclif.com/error') - expect(error.suggestions).to.deep.equal(['rm -rf node_modules']) - }) - .it('attaches pretty print properties to a new error from options') + .do(() => { + error('An error happened!') + }) + .catch((error: PrettyPrintableError) => { + expect(error.message).to.equal('An error happened!') + }) + .it('throws an error using a string argument') fancy - .do(() => { - error(new Error('An existing error object error!'), {code: 'ERR', ref: 'https://oclif.com/error', suggestions: ['rm -rf node_modules']}) - }) - .catch((error: PrettyPrintableError) => { - expect(error.message).to.equal('An existing error object error!') - expect(error.code).to.equal('ERR') - expect(error.ref).to.equal('https://oclif.com/error') - expect(error.suggestions).to.deep.equal(['rm -rf node_modules']) - }) - .it('attached pretty print properties from options to an existing error object') + .do(() => { + error('An error happened!', {code: 'ERR', ref: 'https://oclif.com/error', suggestions: ['rm -rf node_modules']}) + }) + .catch((error: PrettyPrintableError) => { + expect(error.message).to.equal('An error happened!') + expect(error.code).to.equal('ERR') + expect(error.ref).to.equal('https://oclif.com/error') + expect(error.suggestions).to.deep.equal(['rm -rf node_modules']) + }) + .it('attaches pretty print properties to a new error from options') fancy - .do(() => { - const e: any = new Error('An existing error object error!') - e.code = 'ORIG_ERR' - e.ref = 'ORIG_REF' - e.suggestions = ['ORIG_SUGGESTION'] - error(e, {code: 'ERR', ref: 'https://oclif.com/error', suggestions: ['rm -rf node_modules']}) - }) - .catch((error: PrettyPrintableError) => { - expect(error.code).to.equal('ORIG_ERR') - expect(error.ref).to.equal('ORIG_REF') - expect(error.suggestions).to.deep.equal(['ORIG_SUGGESTION']) - }) - .it('preserves original pretty printable properties and is not overwritten by options') + .do(() => { + error(new Error('An existing error object error!'), { + code: 'ERR', + ref: 'https://oclif.com/error', + suggestions: ['rm -rf node_modules'], + }) + }) + .catch((error: PrettyPrintableError) => { + expect(error.message).to.equal('An existing error object error!') + expect(error.code).to.equal('ERR') + expect(error.ref).to.equal('https://oclif.com/error') + expect(error.suggestions).to.deep.equal(['rm -rf node_modules']) + }) + .it('attached pretty print properties from options to an existing error object') fancy - .stdout() - .stderr() - .do(() => { - error('an error is reported but is not rethrown', {exit: false}) - }) - // there is no .catch here because the error is not rethrown - // however it should be outputted - .it('does not rethrow error when exit: false option is set', ctx => { - expect(ctx.stderr).to.contain('Error: an error is reported but is not rethrown') - expect(ctx.stdout).to.equal('') - }) - - describe('applying oclif errors', () => { - fancy .do(() => { - error(new Error('An existing error object error!')) + const e: any = new Error('An existing error object error!') + e.code = 'ORIG_ERR' + e.ref = 'ORIG_REF' + e.suggestions = ['ORIG_SUGGESTION'] + error(e, {code: 'ERR', ref: 'https://oclif.com/error', suggestions: ['rm -rf node_modules']}) }) - .catch((error: any) => { - const defaultErrorCode = 2 - expect(error.oclif.exit).to.equal(defaultErrorCode) + .catch((error: PrettyPrintableError) => { + expect(error.code).to.equal('ORIG_ERR') + expect(error.ref).to.equal('ORIG_REF') + expect(error.suggestions).to.deep.equal(['ORIG_SUGGESTION']) }) - .it('adds oclif exit code to errors by default') + .it('preserves original pretty printable properties and is not overwritten by options') - fancy + fancy + .stdout() + .stderr() .do(() => { - error(new Error('An existing error object error!'), {exit: 9001}) + error('an error is reported but is not rethrown', {exit: false}) }) - .catch((error: any) => { - expect(error.oclif.exit).to.equal(9001) + // there is no .catch here because the error is not rethrown + // however it should be outputted + .it('does not rethrow error when exit: false option is set', (ctx) => { + expect(ctx.stderr).to.contain('Error: an error is reported but is not rethrown') + expect(ctx.stdout).to.equal('') }) - .it('applies the exit property on options to the error object') + describe('applying oclif errors', () => { fancy - .do(() => { - const e: any = new Error('An existing error object error!') - e.oclif = { - code: 'ORIG_EXIT_CODE', - } + .do(() => { + error(new Error('An existing error object error!')) + }) + .catch((error: any) => { + const defaultErrorCode = 2 + expect(error.oclif.exit).to.equal(defaultErrorCode) + }) + .it('adds oclif exit code to errors by default') - error(e) - }) - .catch((error: any) => { - expect(error.oclif.code).to.equal('ORIG_EXIT_CODE') - }) - .it('preserves original oclif exitable error properties and is not overwritten by options') + fancy + .do(() => { + error(new Error('An existing error object error!'), {exit: 9001}) + }) + .catch((error: any) => { + expect(error.oclif.exit).to.equal(9001) + }) + .it('applies the exit property on options to the error object') + + fancy + .do(() => { + const e: any = new Error('An existing error object error!') + e.oclif = { + code: 'ORIG_EXIT_CODE', + } + + error(e) + }) + .catch((error: any) => { + expect(error.oclif.code).to.equal('ORIG_EXIT_CODE') + }) + .it('preserves original oclif exitable error properties and is not overwritten by options') }) }) diff --git a/test/errors/handle.test.ts b/test/errors/handle.test.ts index 9582cde10..df89a323f 100644 --- a/test/errors/handle.test.ts +++ b/test/errors/handle.test.ts @@ -24,111 +24,111 @@ describe('handle', () => { }) fancy - .stdout() - .stderr() - .it('hides an exit error', async ctx => { - await handle(new ExitError(0)) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.equal('') - expect(exitStub.firstCall.firstArg).to.equal(0) - }) + .stdout() + .stderr() + .it('hides an exit error', async (ctx) => { + await handle(new ExitError(0)) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.equal('') + expect(exitStub.firstCall.firstArg).to.equal(0) + }) fancy - .stdout() - .stderr() - .it('prints error', async ctx => { - const error = new Error('foo bar baz') as Error & {skipOclifErrorHandling: boolean} - error.skipOclifErrorHandling = false - await handle(error) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.include('foo bar baz') - }) + .stdout() + .stderr() + .it('prints error', async (ctx) => { + const error = new Error('foo bar baz') as Error & {skipOclifErrorHandling: boolean} + error.skipOclifErrorHandling = false + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + }) fancy - .stdout() - .stderr() - .it('should not print error when skipOclifErrorHandling is true', async ctx => { - const error = new Error('foo bar baz') as Error & {skipOclifErrorHandling: boolean} - error.skipOclifErrorHandling = true - await handle(error) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.equal('') - }) + .stdout() + .stderr() + .it('should not print error when skipOclifErrorHandling is true', async (ctx) => { + const error = new Error('foo bar baz') as Error & {skipOclifErrorHandling: boolean} + error.skipOclifErrorHandling = true + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.equal('') + }) fancy - .stderr() - .do(() => { - config.errlog = errlog - }) - .finally(() => { - config.errlog = undefined - }) - .it('logs when errlog is set', async ctx => { - await handle(new CLIError('uh oh!')) - expect(ctx.stderr).to.equal(` ${x} Error: uh oh!\n`) - await config.errorLogger!.flush() - expect(readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!') - expect(exitStub.firstCall.firstArg).to.equal(2) - }) + .stderr() + .do(() => { + config.errlog = errlog + }) + .finally(() => { + config.errlog = undefined + }) + .it('logs when errlog is set', async (ctx) => { + await handle(new CLIError('uh oh!')) + expect(ctx.stderr).to.equal(` ${x} Error: uh oh!\n`) + await config.errorLogger!.flush() + expect(readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!') + expect(exitStub.firstCall.firstArg).to.equal(2) + }) fancy - .stdout() - .stderr() - .it('should use default exit code for Error (1)', async ctx => { - const error = new Error('foo bar baz') - await handle(error) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.include('foo bar baz') - expect(exitStub.firstCall.firstArg).to.equal(1) - }) + .stdout() + .stderr() + .it('should use default exit code for Error (1)', async (ctx) => { + const error = new Error('foo bar baz') + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(1) + }) fancy - .stdout() - .stderr() - .it('should use default exit code for CLIError (2)', async ctx => { - const error = new CLIError('foo bar baz') - await handle(error) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.include('foo bar baz') - expect(exitStub.firstCall.firstArg).to.equal(2) - }) + .stdout() + .stderr() + .it('should use default exit code for CLIError (2)', async (ctx) => { + const error = new CLIError('foo bar baz') + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(2) + }) fancy - .stdout() - .stderr() - .it('should use exit code provided by CLIError (0)', async ctx => { - const error = new CLIError('foo bar baz', {exit: 0}) - await handle(error) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.include('foo bar baz') - expect(exitStub.firstCall.firstArg).to.equal(0) - }) + .stdout() + .stderr() + .it('should use exit code provided by CLIError (0)', async (ctx) => { + const error = new CLIError('foo bar baz', {exit: 0}) + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(0) + }) fancy - .stdout() - .stderr() - .it('should use exit code provided by CLIError (9999)', async ctx => { - const error = new CLIError('foo bar baz', {exit: 9999}) - await handle(error) - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.include('foo bar baz') - expect(exitStub.firstCall.firstArg).to.equal(9999) - }) + .stdout() + .stderr() + .it('should use exit code provided by CLIError (9999)', async (ctx) => { + const error = new CLIError('foo bar baz', {exit: 9999}) + await handle(error) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.include('foo bar baz') + expect(exitStub.firstCall.firstArg).to.equal(9999) + }) describe('exit', () => { fancy - .stderr() - .stdout() - .it('exits without displaying anything', async ctx => { - try { - exitErrorThrower(9000) - } catch (error: any) { - await handle(error) - } + .stderr() + .stdout() + .it('exits without displaying anything', async (ctx) => { + try { + exitErrorThrower(9000) + } catch (error: any) { + await handle(error) + } - expect(ctx.stdout).to.equal('') - expect(ctx.stderr).to.equal('') - expect(exitStub.firstCall.firstArg).to.equal(9000) - }) + expect(ctx.stdout).to.equal('') + expect(ctx.stderr).to.equal('') + expect(exitStub.firstCall.firstArg).to.equal(9000) + }) }) }) diff --git a/test/errors/pretty-print.test.ts b/test/errors/pretty-print.test.ts index 5b7259419..cea73bcd4 100644 --- a/test/errors/pretty-print.test.ts +++ b/test/errors/pretty-print.test.ts @@ -6,45 +6,39 @@ import {config} from '../../src/errors/config' const stripAnsi = require('strip-ansi') describe('pretty-print', () => { - fancy - .it('pretty prints an error', async () => { + fancy.it('pretty prints an error', async () => { const sampleError: Error & PrettyPrintableError = new Error('Something very serious has gone wrong with the flags!') sampleError.ref = 'https://oclif.io/docs/flags' sampleError.code = 'OCLIF_BAD_FLAG' sampleError.suggestions = ['Try using using a good flag'] - expect( - stripAnsi(prettyPrint(sampleError)), - ).to.equal(` Error: Something very serious has gone wrong with the flags! + expect(stripAnsi(prettyPrint(sampleError))).to + .equal(` Error: Something very serious has gone wrong with the flags! Code: OCLIF_BAD_FLAG Try this: Try using using a good flag Reference: https://oclif.io/docs/flags`) }) - fancy - .it('pretty prints multiple suggestions', async () => { + fancy.it('pretty prints multiple suggestions', async () => { const sampleError: Error & PrettyPrintableError = new Error('Something very serious has gone wrong with the flags!') sampleError.suggestions = ['Use a good flag', 'Use no flags'] - expect( - stripAnsi(prettyPrint(sampleError)), - ).to.equal(` Error: Something very serious has gone wrong with the flags! + expect(stripAnsi(prettyPrint(sampleError))).to + .equal(` Error: Something very serious has gone wrong with the flags! Try this: * Use a good flag * Use no flags`) }) - fancy - .it('pretty prints with omitted fields', async () => { + fancy.it('pretty prints with omitted fields', async () => { const sampleError = new Error('Something very serious has gone wrong with the flags!') - expect( - stripAnsi(prettyPrint(sampleError)), - ).to.equal(' Error: Something very serious has gone wrong with the flags!') + expect(stripAnsi(prettyPrint(sampleError))).to.equal( + ' Error: Something very serious has gone wrong with the flags!', + ) }) describe('CLI Error properties', () => { - fancy - .it('supports the bang property', async () => { + fancy.it('supports the bang property', async () => { class SampleCLIError extends CLIError { get bang() { return '>>>' @@ -55,8 +49,7 @@ describe('pretty-print', () => { expect(stripAnsi(prettyPrint(sampleError))).to.equal(' >>> Error: This is a CLI error') }) - fancy - .it('supports the \'name\' message prefix property', async () => { + fancy.it("supports the 'name' message prefix property", async () => { const defaultBang = process.platform === 'win32' ? 'ยป' : 'โ€บ' const sampleError = new CLIError('This is a CLI error') sampleError.name = 'Errorz' @@ -76,8 +69,7 @@ describe('pretty-print', () => { config.debug = initialConfigDebug }) - fancy - .it('shows the stack for an error', async () => { + fancy.it('shows the stack for an error', async () => { const error = new Error('oh no!') error.stack = 'this is the error stack property' expect(prettyPrint(error)).to.equal('this is the error stack property') diff --git a/test/errors/warn.test.ts b/test/errors/warn.test.ts index 90fb1b7ea..d51827aa5 100644 --- a/test/errors/warn.test.ts +++ b/test/errors/warn.test.ts @@ -8,17 +8,17 @@ const errlog = join(__dirname, '../tmp/mytest/warn.log') describe('warn', () => { fancy - .stderr() - .do(() => { - config.errlog = errlog - }) - .finally(() => { - config.errlog = undefined - }) - .it('warns', async ctx => { - warn('foo!') - expect(ctx.stderr).to.contain('Warning: foo!') - await config.errorLogger!.flush() - expect(await readFile(errlog, 'utf8')).to.contain('Warning: foo!') - }) + .stderr() + .do(() => { + config.errlog = errlog + }) + .finally(() => { + config.errlog = undefined + }) + .it('warns', async (ctx) => { + warn('foo!') + expect(ctx.stderr).to.contain('Warning: foo!') + await config.errorLogger!.flush() + expect(await readFile(errlog, 'utf8')).to.contain('Warning: foo!') + }) }) diff --git a/test/help/_test-help-class.ts b/test/help/_test-help-class.ts index be8391651..44eea87e0 100644 --- a/test/help/_test-help-class.ts +++ b/test/help/_test-help-class.ts @@ -5,7 +5,7 @@ import {HelpBase} from '../../src' -export default class extends HelpBase { +export default class extends HelpBase { async showHelp(): Promise { console.log('help') } diff --git a/test/help/docopts.test.ts b/test/help/docopts.test.ts index 12eaca7ff..658197747 100644 --- a/test/help/docopts.test.ts +++ b/test/help/docopts.test.ts @@ -4,90 +4,104 @@ import {Flags} from '../../src' describe('doc opts', () => { it('shows required string field', async () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.string({ - name: 'testFlag', - description: 'test', - required: true, - char: 'f', - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.string({ + name: 'testFlag', + description: 'test', + required: true, + char: 'f', + }), + }, + } as any) expect(usage).to.contain(' -f ') }) it('shows optional boolean field', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.boolean({ - name: 'testFlag', - description: 'test', - char: 'f', - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.boolean({ + name: 'testFlag', + description: 'test', + char: 'f', + }), + }, + } as any) // boolean fields don't have a value expect(usage).to.contain(' [-f]') }) it('shows no short char', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.string({ - name: 'testFlag', - description: 'test', - options: ['a', 'b'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.string({ + name: 'testFlag', + description: 'test', + options: ['a', 'b'], + }), + }, + } as any) expect(usage).to.contain(' [--testFlag a|b]') }) it('shows url type', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + }), + }, + } as any) expect(usage).to.contain(' [-s ]') }) it('does not show hidden type', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - hidden: true, - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + hidden: true, + }), + }, + } as any) expect(usage).to.not.contain(' [-s ]') }) it('shows optional one-way depended fields', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - dependsOn: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + dependsOn: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' [-f -s ]') }) it('shows one-way depended field on required field', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - required: true, - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - dependsOn: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + required: true, + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + dependsOn: ['testFlag'], + }), + }, + } as any) // If a flag depends on a required flag, then it is optional. // So this should technically be "(-f [-s ])" but // does that even make sense anymore since -f will always be there? @@ -95,143 +109,159 @@ describe('doc opts', () => { expect(usage).to.contain(' (-f -s )') }) it('shows required one-way depended field on optional field', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - required: true, - dependsOn: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + required: true, + dependsOn: ['testFlag'], + }), + }, + } as any) // If the required flag depends on an optional, it isn't really optional. expect(usage).to.contain(' (-f -s )') }) it('shows optional one-way exclusive fields', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - exclusive: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + exclusive: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' [-f | -s ]') }) it('shows one-way exclusive field on required field', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - required: true, - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - exclusive: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + required: true, + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + exclusive: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' (-f | -s )') }) it('shows required one-way exclusive field on optional field', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - required: true, - exclusive: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + required: true, + exclusive: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' (-f | -s )') }) it('shows option one-way exclusive field on optional field', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - required: true, - exclusive: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + required: true, + exclusive: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' (-f | -s )') }) it('shows optional exclusive fields defined twice', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - exclusive: ['testFlag2'], - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - exclusive: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + exclusive: ['testFlag2'], + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + exclusive: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' [-s | -f ]') }) it('shows optional two-way depended fields', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - dependsOn: ['testFlag2'], - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - dependsOn: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + dependsOn: ['testFlag2'], + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + dependsOn: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' [-s -f ]') }) it('shows required two-way depended fields', () => { - const usage = DocOpts.generate({flags: { - testFlag: Flags.url({ - name: 'testFlag', - description: 'test', - char: 's', - required: true, - dependsOn: ['testFlag2'], - }), - testFlag2: Flags.string({ - name: 'testFlag2', - description: 'test', - char: 'f', - required: true, - dependsOn: ['testFlag'], - }), - }} as any) + const usage = DocOpts.generate({ + flags: { + testFlag: Flags.url({ + name: 'testFlag', + description: 'test', + char: 's', + required: true, + dependsOn: ['testFlag2'], + }), + testFlag2: Flags.string({ + name: 'testFlag2', + description: 'test', + char: 'f', + required: true, + dependsOn: ['testFlag'], + }), + }, + } as any) expect(usage).to.contain(' (-s -f )') }) }) diff --git a/test/help/fixtures/fixtures.ts b/test/help/fixtures/fixtures.ts index 4a1661891..228b80a8a 100644 --- a/test/help/fixtures/fixtures.ts +++ b/test/help/fixtures/fixtures.ts @@ -126,3 +126,20 @@ export const DbTopic: Topic = { name: 'db', description: 'This topic is for the db topic', } + +// deprecateAliases +export class DeprecateAliases extends Command { + static id = 'foo:bar' + + static aliases = ['foo:bar:alias'] + + static deprecateAliases = true + + static flags = {} + + static args = {} + + async run(): Promise { + 'run' + } +} diff --git a/test/help/format-command-with-options.test.ts b/test/help/format-command-with-options.test.ts index f3a816dc6..ba0fd0e49 100644 --- a/test/help/format-command-with-options.test.ts +++ b/test/help/format-command-with-options.test.ts @@ -13,35 +13,38 @@ class Command extends Base { } const test = base -.loadConfig() -.add('help', ctx => new TestHelp(ctx.config as any)) -.register('commandHelp', commandHelp) + .loadConfig() + .add('help', (ctx) => new TestHelp(ctx.config as any)) + .register('commandHelp', commandHelp) describe('formatCommand', () => { test - .commandHelp(class extends Command { - static id = 'apps:create' + .commandHelp( + class extends Command { + static id = 'apps:create' - static aliases = ['app:init', 'create'] + static aliases = ['app:init', 'create'] - static description = `first line + static description = `first line multiline help` - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'}), - } - - static flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), - force: flags.boolean({description: 'force it '.repeat(15)}), - ss: flags.boolean({description: 'newliney\n'.repeat(4)}), - remote: flags.string({char: 'r'}), - label: flags.string({char: 'l', helpLabel: '-l'}), - } - }) - .it('handles multi-line help output', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'}), + } + + static flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), + force: flags.boolean({description: 'force it '.repeat(15)}), + ss: flags.boolean({description: 'newliney\n'.repeat(4)}), + remote: flags.string({char: 'r'}), + label: flags.string({char: 'l', helpLabel: '-l'}), + } + }, + ) + .it('handles multi-line help output', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [-f ] [--force] [--ss] [-r ] [-l ] @@ -66,31 +69,35 @@ DESCRIPTION ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) describe('arg and flag multiline handling', () => { test - .commandHelp(class extends Command { - static id = 'apps:create' - - static description = 'description of apps:create' - - static aliases = ['app:init', 'create'] - - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'.repeat(35)}), - } - - static flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), - force: flags.boolean({description: 'force it '.repeat(15)}), - ss: flags.boolean({description: 'newliney\n'.repeat(4)}), - remote: flags.string({char: 'r'}), - } - }) - .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static description = 'description of apps:create' + + static aliases = ['app:init', 'create'] + + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'.repeat(35)}), + } + + static flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), + force: flags.boolean({description: 'force it '.repeat(15)}), + ss: flags.boolean({description: 'newliney\n'.repeat(4)}), + remote: flags.string({char: 'r'}), + } + }, + ) + .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [-f ] [--force] [--ss] [-r ] @@ -116,30 +123,34 @@ OPTIONS ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static description = 'description of apps:create' - - static aliases = ['app:init', 'create'] - - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'.repeat(35)}), - } - - static flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), - force: flags.boolean({description: 'force it '.repeat(29)}), - ss: flags.boolean({description: 'newliney\n'.repeat(5)}), - remote: flags.string({char: 'r'}), - } - }) - .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static description = 'description of apps:create' + + static aliases = ['app:init', 'create'] + + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'.repeat(35)}), + } + + static flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), + force: flags.boolean({description: 'force it '.repeat(29)}), + ss: flags.boolean({description: 'newliney\n'.repeat(5)}), + remote: flags.string({char: 'r'}), + } + }, + ) + .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [-f ] [--force] [--ss] [-r ] @@ -173,28 +184,33 @@ OPTIONS ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) }) describe('description', () => { test - .commandHelp(class extends Command { - static id = 'apps:create' - - static description = 'description of apps:create\nthese values are after and will show up in the command description' - - static aliases = ['app:init', 'create'] - - static args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'}), - } - - static flags = { - force: flags.boolean({description: 'forces'}), - } - }) - .it('outputs command description with values after a \\n newline character', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static description = + 'description of apps:create\nthese values are after and will show up in the command description' + + static aliases = ['app:init', 'create'] + + static args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'}), + } + + static flags = { + force: flags.boolean({description: 'forces'}), + } + }, + ) + .it('outputs command description with values after a \\n newline character', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--force] ARGUMENTS @@ -208,54 +224,67 @@ DESCRIPTION ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static description = 'root part of the description\nThe <%= config.bin %> CLI has <%= command.id %>' - }) - .it('renders template string from description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static description = 'root part of the description\nThe <%= config.bin %> CLI has <%= command.id %>' + }, + ) + .it('renders template string from description', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create DESCRIPTION - The oclif CLI has apps:create`)) + The oclif CLI has apps:create`), + ) }) - describe(('flags'), () => { + describe('flags', () => { test - .commandHelp(class extends Command { - static id = 'apps:create' - - static flags = { - myenum: flags.string({ - options: ['a', 'b', 'c'], - }), - } - }) - .it('outputs flag enum', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static flags = { + myenum: flags.string({ + options: ['a', 'b', 'c'], + }), + } + }, + ) + .it('outputs flag enum', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--myenum a|b|c] OPTIONS - --myenum=a|b|c`)) + --myenum=a|b|c`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static args = { - arg1: Args.string({default: '.'}), - arg2: Args.string({default: '.', description: 'arg2 desc'}), - arg3: Args.string({description: 'arg3 desc'}), - } - - static flags = { - flag1: flags.string({default: '.'}), - flag2: flags.string({default: '.', description: 'flag2 desc'}), - flag3: flags.string({description: 'flag3 desc'}), - } - }).it('outputs with default flag options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static args = { + arg1: Args.string({default: '.'}), + arg2: Args.string({default: '.', description: 'arg2 desc'}), + arg3: Args.string({description: 'arg3 desc'}), + } + + static flags = { + flag1: flags.string({default: '.'}), + flag2: flags.string({default: '.', description: 'flag2 desc'}), + flag3: flags.string({description: 'flag3 desc'}), + } + }, + ) + .it('outputs with default flag options', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [ARG1] [ARG2] [ARG3] [--flag1 ] [--flag2 ] [--flag3 ] @@ -267,144 +296,189 @@ ARGUMENTS OPTIONS --flag1=flag1 [default: .] --flag2=flag2 [default: .] flag2 desc - --flag3=flag3 flag3 desc`)) + --flag3=flag3 flag3 desc`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static flags = { - opt: flags.boolean({allowNo: true}), - } - }) - .it('outputs with with no options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static flags = { + opt: flags.boolean({allowNo: true}), + } + }, + ) + .it('outputs with with no options', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--opt] OPTIONS - --[no-]opt`)) + --[no-]opt`), + ) }) - describe('args', () => { + describe('args', () => { test - .commandHelp(class extends Command { - static id = 'apps:create' - - static args = { - arg1: Args.string({description: 'Show the options', options: ['option1', 'option2']}), - } - }) - .it('outputs with arg options', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static args = { + arg1: Args.string({description: 'Show the options', options: ['option1', 'option2']}), + } + }, + ) + .it('outputs with arg options', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [ARG1] ARGUMENTS - ARG1 (option1|option2) Show the options`)) + ARG1 (option1|option2) Show the options`), + ) }) describe('usage', () => { test - .commandHelp(class extends Command { - static id = 'apps:create' - - static usage = '<%= config.bin %> <%= command.id %> usage' - }) - .it('outputs usage with templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE - $ oclif oclif apps:create usage`)) + .commandHelp( + class extends Command { + static id = 'apps:create' + + static usage = '<%= config.bin %> <%= command.id %> usage' + }, + ) + .it('outputs usage with templates', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE + $ oclif oclif apps:create usage`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static usage = ['<%= config.bin %>', '<%= command.id %> usage'] - }) - .it('outputs usage arrays with templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static usage = ['<%= config.bin %>', '<%= command.id %> usage'] + }, + ) + .it('outputs usage arrays with templates', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif - $ oclif apps:create usage`)) + $ oclif apps:create usage`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static usage = undefined - }) - .it('defaults usage when not specified', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE - $ oclif apps:create`)) + .commandHelp( + class extends Command { + static id = 'apps:create' + + static usage = undefined + }, + ) + .it('defaults usage when not specified', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE + $ oclif apps:create`), + ) }) describe('examples', () => { test - .commandHelp(class extends Command { - static examples = ['it handles a list of examples', 'more example text'] - }) - .it('outputs multiple examples', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static examples = ['it handles a list of examples', 'more example text'] + }, + ) + .it('outputs multiple examples', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif EXAMPLES it handles a list of examples - more example text`)) + more example text`), + ) test - .commandHelp(class extends Command { - static examples = ['it handles a single example'] - }) - .it('outputs a single example', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static examples = ['it handles a single example'] + }, + ) + .it('outputs a single example', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif EXAMPLES - it handles a single example`)) + it handles a single example`), + ) test - .commandHelp(class extends Command { - static id = 'oclif:command' - - static examples = ['the bin is <%= config.bin %>', 'the command id is <%= command.id %>'] - }) - .it('outputs examples using templates', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'oclif:command' + + static examples = ['the bin is <%= config.bin %>', 'the command id is <%= command.id %>'] + }, + ) + .it('outputs examples using templates', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command EXAMPLES the bin is oclif - the command id is oclif:command`)) + the command id is oclif:command`), + ) test - .commandHelp(class extends Command { - static id = 'oclif:command' - - static examples = ['<%= config.bin %> <%= command.id %> --help'] - }) - .it('formats if command', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'oclif:command' + + static examples = ['<%= config.bin %> <%= command.id %> --help'] + }, + ) + .it('formats if command', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command EXAMPLES - $ oclif oclif:command --help`)) + $ oclif oclif:command --help`), + ) test - .commandHelp(class extends Command { - static id = 'oclif:command' - - static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help'] - }) - .it('formats if command with description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'oclif:command' + + static examples = ['Prints out help.\n<%= config.bin %> <%= command.id %> --help'] + }, + ) + .it('formats if command with description', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command EXAMPLES Prints out help. - $ oclif oclif:command --help`)) + $ oclif oclif:command --help`), + ) test - .commandHelp(class extends Command { - static id = 'oclif:command' - - static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> --help'}] - }) - .it('formats example object', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'oclif:command' + + static examples = [{description: 'Prints out help.', command: '<%= config.bin %> <%= command.id %> --help'}] + }, + ) + .it('formats example object', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif oclif:command EXAMPLES Prints out help. - $ oclif oclif:command --help`)) + $ oclif oclif:command --help`), + ) }) }) diff --git a/test/help/format-command.test.ts b/test/help/format-command.test.ts index 72bcfb258..4df21cd93 100644 --- a/test/help/format-command.test.ts +++ b/test/help/format-command.test.ts @@ -13,40 +13,43 @@ class Command extends Base { } const test = base -.loadConfig() -.add('help', ctx => new TestHelp(ctx.config as any)) -.register('commandHelp', commandHelp) + .loadConfig() + .add('help', (ctx) => new TestHelp(ctx.config as any)) + .register('commandHelp', commandHelp) describe('formatCommand', () => { test - .commandHelp(class extends Command { - static { - this.id = 'apps:create' + .commandHelp( + class extends Command { + static { + this.id = 'apps:create' - this.aliases = ['app:init', 'create'] + this.aliases = ['app:init', 'create'] - this.description = `first line + this.description = `first line multiline help` - this.enableJsonFlag = true - - this.args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'}), - } - - this.flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), - force: flags.boolean({description: 'force it '.repeat(15)}), - ss: flags.boolean({description: 'newliney\n'.repeat(4)}), - remote: flags.string({char: 'r'}), - label: flags.string({char: 'l', helpLabel: '-l'}), - } - } - }) - .it('handles multi-line help output', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + this.enableJsonFlag = true + + this.args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'}), + } + + this.flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(18)}), + force: flags.boolean({description: 'force it '.repeat(15)}), + ss: flags.boolean({description: 'newliney\n'.repeat(4)}), + remote: flags.string({char: 'r'}), + label: flags.string({char: 'l', helpLabel: '-l'}), + } + } + }, + ) + .it('handles multi-line help output', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [-f ] [--force] [--ss] [-r ] [-l ] @@ -76,35 +79,39 @@ DESCRIPTION ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) describe('arg and flag multiline handling', () => { test - .commandHelp(class extends Command { - static { - this.id = 'apps:create' - - this.description = 'description of apps:create' - - this.aliases = ['app:init', 'create'] - - this.enableJsonFlag = true - - this.args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'.repeat(35)}), - } - - this.flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), - force: flags.boolean({description: 'force it '.repeat(15)}), - ss: flags.boolean({description: 'newliney\n'.repeat(4)}), - remote: flags.string({char: 'r'}), - } - } - }) - .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static { + this.id = 'apps:create' + + this.description = 'description of apps:create' + + this.aliases = ['app:init', 'create'] + + this.enableJsonFlag = true + + this.args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'.repeat(35)}), + } + + this.flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(15)}), + force: flags.boolean({description: 'force it '.repeat(15)}), + ss: flags.boolean({description: 'newliney\n'.repeat(4)}), + remote: flags.string({char: 'r'}), + } + } + }, + ) + .it('show args and flags side by side when their output do not exceed 4 lines ', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [-f ] [--force] [--ss] [-r ] @@ -136,34 +143,38 @@ DESCRIPTION ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) test - .commandHelp(class extends Command { - static { - this.id = 'apps:create' - - this.description = 'description of apps:create' - - this.aliases = ['app:init', 'create'] - - this.enableJsonFlag = true - - this.args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'.repeat(35)}), - } - - this.flags = { - app: flags.string({char: 'a', hidden: true}), - foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), - force: flags.boolean({description: 'force it '.repeat(29)}), - ss: flags.boolean({description: 'newliney\n'.repeat(5)}), - remote: flags.string({char: 'r'}), - } - } - }) - .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static { + this.id = 'apps:create' + + this.description = 'description of apps:create' + + this.aliases = ['app:init', 'create'] + + this.enableJsonFlag = true + + this.args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'.repeat(35)}), + } + + this.flags = { + app: flags.string({char: 'a', hidden: true}), + foo: flags.string({char: 'f', description: 'foobar'.repeat(20)}), + force: flags.boolean({description: 'force it '.repeat(29)}), + ss: flags.boolean({description: 'newliney\n'.repeat(5)}), + remote: flags.string({char: 'r'}), + } + } + }, + ) + .it('shows stacked args and flags when the lines exceed 4', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [-f ] [--force] [--ss] [-r ] @@ -203,59 +214,72 @@ DESCRIPTION ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) }) describe('summary', () => { test - .commandHelp(class extends Command { - static id = 'test:summary' - - static summary = 'one line summary' - }) - .it('no description header if only a summary', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE - $ oclif test:summary`)) + .commandHelp( + class extends Command { + static id = 'test:summary' + + static summary = 'one line summary' + }, + ) + .it('no description header if only a summary', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE + $ oclif test:summary`), + ) test - .commandHelp(class extends Command { - static id = 'test:summary' + .commandHelp( + class extends Command { + static id = 'test:summary' - static summary = 'one line summary' + static summary = 'one line summary' - static description = 'description that is much longer than the summary' - }) - .it('outputs the summary at the top of the help and description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + static description = 'description that is much longer than the summary' + }, + ) + .it('outputs the summary at the top of the help and description', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif test:summary DESCRIPTION one line summary - description that is much longer than the summary`)) + description that is much longer than the summary`), + ) }) describe('description', () => { test - .commandHelp(class extends Command { - static { - this.id = 'apps:create' - - this.description = 'description of apps:create\n\nthese values are after and will show up in the command description' - - this.aliases = ['app:init', 'create'] - - this.enableJsonFlag = true - - this.args = { - // eslint-disable-next-line camelcase - app_name: Args.string({description: 'app to use'}), - } - - this.flags = { - force: flags.boolean({description: 'forces'}), - } - } - }) - .it('outputs command description with values after a \\n newline character', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static { + this.id = 'apps:create' + + this.description = + 'description of apps:create\n\nthese values are after and will show up in the command description' + + this.aliases = ['app:init', 'create'] + + this.enableJsonFlag = true + + this.args = { + // eslint-disable-next-line camelcase + app_name: Args.string({description: 'app to use'}), + } + + this.flags = { + force: flags.boolean({description: 'forces'}), + } + } + }, + ) + .it('outputs command description with values after a \\n newline character', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [APP_NAME] [--json] [--force] ARGUMENTS @@ -274,29 +298,37 @@ DESCRIPTION ALIASES $ oclif app:init - $ oclif create`)) + $ oclif create`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static description = 'root part of the description\n\nThe <%= config.bin %> CLI has <%= command.id %>' - }) - .it('renders template string from description', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static description = 'root part of the description\n\nThe <%= config.bin %> CLI has <%= command.id %>' + }, + ) + .it('renders template string from description', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create DESCRIPTION root part of the description - The oclif CLI has apps:create`)) + The oclif CLI has apps:create`), + ) test - .commandHelp(class extends Command { - static id = 'apps:create' - - static description = 'root part of the description\r\n\nusing both carriage \n\nreturn and new line' - }) - .it('splits on carriage return and new lines', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static id = 'apps:create' + + static description = 'root part of the description\r\n\nusing both carriage \n\nreturn and new line' + }, + ) + .it('splits on carriage return and new lines', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create DESCRIPTION @@ -304,64 +336,77 @@ DESCRIPTION using both carriage - return and new line`)) + return and new line`), + ) }) const myEnumValues = ['a', 'b', 'c'] - describe(('flags'), () => { + describe('flags', () => { test - .commandHelp(class extends Command { - static { - this.id = 'apps:create' - - this.flags = { - myenum: flags.string({ - description: 'the description', - options: myEnumValues, - }), - } - } - }) - .it('outputs flag enum', (ctx: any) => expect(ctx.commandHelp).to.equal(`USAGE + .commandHelp( + class extends Command { + static { + this.id = 'apps:create' + + this.flags = { + myenum: flags.string({ + description: 'the description', + options: myEnumValues, + }), + } + } + }, + ) + .it('outputs flag enum', (ctx: any) => + expect(ctx.commandHelp).to.equal(`USAGE $ oclif apps:create [--myenum a|b|c] FLAGS --myenum=