diff --git a/package.json b/package.json index 60a4fa26..d74b9e8e 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "shx": "^0.3.4", "sinon": "^11.1.2", "ts-node": "^9.1.1", + "tsd": "^0.22.0", "typescript": "4.5.5" }, "engines": { diff --git a/src/flags.ts b/src/flags.ts index 339584fb..24433967 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -1,23 +1,14 @@ -import {OptionFlag, Definition, BooleanFlag, EnumFlagOptions, Default} from './interfaces' -import * as Parser from './parser' +import {OptionFlag, BooleanFlag, EnumFlagOptions, Default} from './interfaces' +import {custom, boolean} from './parser' import Command from './command' +export {boolean, integer, url, directory, file, string, build, option, custom} from './parser' -export function build(defaults: {parse: OptionFlag['parse']} & Partial>): Definition -export function build(defaults: Partial>): Definition -export function build(defaults: Partial>): Definition { - return Parser.flags.build(defaults as any) -} - -export function option(options: {parse: OptionFlag['parse']} & Partial>) { - return build(options)() -} - -export function _enum(opts: EnumFlagOptions & {multiple: true} & ({required: true} | { default: Default })): OptionFlag -export function _enum(opts: EnumFlagOptions & {multiple: true}): OptionFlag +export function _enum(opts: EnumFlagOptions & {multiple: true} & ({required: true} | { default: Default })): OptionFlag +export function _enum(opts: EnumFlagOptions & {multiple: true}): OptionFlag export function _enum(opts: EnumFlagOptions & ({required: true} | { default: Default })): OptionFlag export function _enum(opts: EnumFlagOptions): OptionFlag export function _enum(opts: EnumFlagOptions): OptionFlag | OptionFlag | OptionFlag | OptionFlag { - return build({ + return custom>({ async parse(input) { if (!opts.options.includes(input)) throw new Error(`Expected --${this.name}=${input} to be one of: ${opts.options.join(', ')}`) return input as unknown as T @@ -29,12 +20,8 @@ export function _enum(opts: EnumFlagOptions): OptionFlag | Opt export {_enum as enum} -const stringFlag = build({}) -export {stringFlag as string} -export {boolean, integer, url, directory, file} from './parser' - export const version = (opts: Partial> = {}) => { - return Parser.flags.boolean({ + return boolean({ description: 'Show CLI version.', ...opts, parse: async (_: any, cmd: Command) => { @@ -45,7 +32,7 @@ export const version = (opts: Partial> = {}) => { } export const help = (opts: Partial> = {}) => { - return Parser.flags.boolean({ + return boolean({ description: 'Show CLI help.', ...opts, parse: async (_: any, cmd: Command) => { diff --git a/src/interfaces/flags.ts b/src/interfaces/flags.ts index e69de29b..5608e64d 100644 --- a/src/interfaces/flags.ts +++ b/src/interfaces/flags.ts @@ -0,0 +1,33 @@ +import {FlagInput} from './parser' + +/** + * Infer the flags that are returned by Command.parse. This is useful for when you want to assign the flags as a class property. + * + * @example + * export type StatusFlags = Interfaces.InferredFlags + * + * export abstract class BaseCommand extends Command { + * static enableJsonFlag = true + * + * static globalFlags = { + * config: Flags.string({ + * description: 'specify config file', + * }), + * } + * } + * + * export default class Status extends BaseCommand { + * static flags = { + * force: Flags.boolean({char: 'f', description: 'a flag'}), + * } + * + * public flags!: StatusFlags + * + * public async run(): Promise { + * const result = await this.parse(Status) + * this.flags = result.flags + * return result.flags + * } + * } + */ +export type InferredFlags = T extends FlagInput ? F & { json: boolean | undefined; } : unknown diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 8dd43c80..45c957cf 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -19,3 +19,4 @@ export {PJSON} from './pjson' export {Plugin, PluginOptions, Options} from './plugin' export {Topic} from './topic' export {TSConfig} from './ts-config' +export {InferredFlags} from './flags' diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts index 5e804a57..45da6451 100644 --- a/src/interfaces/parser.ts +++ b/src/interfaces/parser.ts @@ -78,13 +78,13 @@ type MetadataFlag = { export type ListItem = [string, string | undefined] export type List = ListItem[] -export type DefaultContext = { - options: OptionFlag; - flags: { [k: string]: string }; +export type DefaultContext = { + options: P & OptionFlag; + flags: Record; } -export type Default = T | ((context: DefaultContext) => Promise) -export type DefaultHelp = T | ((context: DefaultContext) => Promise) +export type Default> = T | ((context: DefaultContext) => Promise) +export type DefaultHelp> = T | ((context: DefaultContext) => Promise) export type FlagProps = { name: string; @@ -109,10 +109,30 @@ export type FlagProps = { * Shows this flag in a separate list in the help. */ helpGroup?: string; + /** + * Accept an environment variable as input + */ + env?: string; + /** + * If true, the flag will not be shown in the help. + */ hidden?: boolean; + /** + * If true, the flag will be required. + */ required?: boolean; + /** + * List of flags that this flag depends on. + */ dependsOn?: string[]; + /** + * List of flags that cannot be used with this flag. + */ exclusive?: string[]; + /** + * Exactly one of these flags must be provided. + */ + exactlyOne?: string[]; } export type BooleanFlagProps = FlagProps & { @@ -124,46 +144,57 @@ export type OptionFlagProps = FlagProps & { type: 'option'; helpValue?: string; options?: string[]; - multiple: boolean; + multiple?: boolean; } -export type FlagBase = FlagProps & { - exactlyOne?: string[]; - /** - * also accept an environment variable as input - */ - env?: string; - parse(input: I, context: any): Promise; +export type FlagParser = (input: I, context: any, opts: P & OptionFlag) => Promise + +export type FlagBase = FlagProps & { + parse: FlagParser; } export type BooleanFlag = FlagBase & BooleanFlagProps & { /** - * specifying a default of false is the same not specifying a default + * specifying a default of false is the same as not specifying a default */ default?: Default; } -export type OptionFlag = FlagBase & OptionFlagProps & { - default?: Default; + +export type CustomOptionFlag = FlagBase & OptionFlagProps & { defaultHelp?: DefaultHelp; input: string[]; + default?: M extends true ? Default : Default; } -export type Definition = { +export type OptionFlag = FlagBase & OptionFlagProps & { + defaultHelp?: DefaultHelp; + input: string[]; +} & ({ + default?: Default; + multiple: false; +} | { + default?: Default; + multiple: true; +}) + +export type Definition> = { ( - options: { multiple: true } & ({ required: true } | { default: Default }) & - Partial>, + options: P & { multiple: true } & ({ required: true } | { default: Default }) & Partial> ): OptionFlag; - (options: { multiple: true } & Partial>): OptionFlag; - ( - options: ({ required: true } | { default: Default }) & - Partial>, - ): OptionFlag; - (options?: Partial>): OptionFlag; + (options: P & { multiple: true } & Partial>): OptionFlag; + (options: P & ({ required: true } | { default: Default }) & Partial>): OptionFlag; + (options?: P & Partial>): OptionFlag; } -export type EnumFlagOptions = Partial> & { +export type EnumFlagOptions = Partial> & { options: T[]; -} +} & ({ + default?: Default; + multiple?: false; +} | { + default?: Default; + multiple: true; +}) export type Flag = BooleanFlag | OptionFlag diff --git a/src/parser/flags.ts b/src/parser/flags.ts index eaa8bcd8..06710614 100644 --- a/src/parser/flags.ts +++ b/src/parser/flags.ts @@ -1,20 +1,57 @@ -// tslint:disable interface-over-type-literal - import {URL} from 'url' -import {Definition, OptionFlag, BooleanFlag, Default} from '../interfaces' +import {Definition, OptionFlag, BooleanFlag} from '../interfaces' import * as fs from 'fs' +import {FlagParser, CustomOptionFlag} from '../interfaces/parser' + +/** + * Create a custom flag. + * + * @example + * type Id = string + * type IdOpts = { startsWith: string; length: number }; + * + * export const myFlag = custom({ + * parse: async (input, opts) => { + * if (input.startsWith(opts.startsWith) && input.length === opts.length) { + * return input + * } + * + * throw new Error('Invalid id') + * }, + * }) + */ +export function custom>( + defaults: {parse: FlagParser, multiple: true} & Partial>, +): Definition +export function custom>( + defaults: {parse: FlagParser} & Partial>, +): Definition +export function custom>(defaults: Partial>): Definition +export function custom>(defaults: Partial>): Definition { + return (options: any = {}) => { + return { + parse: async (i: string, _context: any, _opts: P) => i, + ...defaults, + ...options, + input: [] as string[], + multiple: Boolean(options.multiple === undefined ? defaults.multiple : options.multiple), + type: 'option', + } + } +} +/** + * @deprecated Use Flags.custom instead. + */ export function build( defaults: {parse: OptionFlag['parse']} & Partial>, ): Definition -export function build( - defaults: Partial>, -): Definition +export function build(defaults: Partial>): Definition export function build(defaults: Partial>): Definition { return (options: any = {}) => { return { - parse: async (i: string, _: any) => i, + parse: async (i: string, _context: any) => i, ...defaults, ...options, input: [] as string[], @@ -35,67 +72,40 @@ export function boolean( } as BooleanFlag } -export function integer(opts: Partial> & {min?: number; max?: number } & {multiple: true} & ({required: true} | { default: Default })): OptionFlag -export function integer(opts: Partial> & {min?: number; max?: number } & {multiple: true}): OptionFlag -export function integer(opts: Partial> & {min?: number; max?: number } & ({required: true} | { default: Default })): OptionFlag -export function integer(opts?: Partial> & {min?: number; max?: number }): OptionFlag -export function integer(opts: Partial> & {min?: number; max?: number } = {}): OptionFlag | OptionFlag | OptionFlag | OptionFlag { - return build({ - ...opts, - parse: async 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}`) - if (opts.max !== undefined && num > opts.max) - throw new Error(`Expected an integer less than or equal to ${opts.max} but received: ${input}`) - return opts.parse ? opts.parse(input, 1) : num - }, - })() -} +export const integer = custom({ + parse: async (input, _, opts) => { + 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}`) + if (opts.max !== undefined && num > opts.max) + throw new Error(`Expected an integer less than or equal to ${opts.max} but received: ${input}`) + return num + }, +}) -export function directory(opts: Partial> & { exists?: boolean } & {multiple: true} & ({required: true} | { default: Default })): OptionFlag -export function directory(opts: Partial> & { exists?: boolean } & {multiple: true}): OptionFlag -export function directory(opts: { exists?: boolean } & Partial> & ({required: true} | { default: Default })): OptionFlag -export function directory(opts?: { exists?: boolean } & Partial>): OptionFlag -export function directory(opts: { exists?: boolean } & Partial> = {}): OptionFlag | OptionFlag | OptionFlag | OptionFlag { - return build({ - ...opts, - parse: async (input: string) => { - if (opts.exists) { - // 2nd "context" arg is required but unused - return opts.parse ? opts.parse(await dirExists(input), true) : dirExists(input) - } - - return opts.parse ? opts.parse(input, true) : input - }, - })() -} +export const directory = custom({ + parse: async (input, _, opts) => { + if (opts.exists) return dirExists(input) -export function file(opts: Partial> & { exists?: boolean } & {multiple: true} & ({required: true} | { default: Default })): OptionFlag -export function file(opts: Partial> & { exists?: boolean } & {multiple: true}): OptionFlag -export function file(opts: { exists?: boolean } & Partial> & ({required: true} | { default: Default })): OptionFlag -export function file(opts?: { exists?: boolean } & Partial>): OptionFlag -export function file(opts: { exists?: boolean } & Partial> = {}): OptionFlag | OptionFlag | OptionFlag | OptionFlag { - return build({ - ...opts, - parse: async (input: string) => { - if (opts.exists) { - // 2nd "context" arg is required but unused - return opts.parse ? opts.parse(await fileExists(input), true) : fileExists(input) - } - - return opts.parse ? opts.parse(input, true) : input - }, - })() -} + return input + }, +}) + +export const file = custom({ + parse: async (input, _, opts) => { + if (opts.exists) return fileExists(input) + + return input + }, +}) /** * Initializes a string as a URL. Throws an error * if the string is not a valid URL. */ -export const url = build({ +export const url = custom({ parse: async input => { try { return new URL(input) @@ -106,12 +116,12 @@ export const url = build({ }) export function option( - options: {parse: OptionFlag['parse']} & Partial>, + options: {parse: OptionFlag['parse']} & Partial>, ) { - return build(options)() + return custom(options)() } -const stringFlag = build({}) +const stringFlag = custom({}) export {stringFlag as string} export const defaultFlags = { diff --git a/src/parser/index.ts b/src/parser/index.ts index dfbf21ed..0b322740 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -31,6 +31,4 @@ export async function parse } -const {boolean, integer, url, directory, file} = flags - -export {boolean, integer, url, directory, file} +export {boolean, integer, url, directory, file, string, build, option, custom} from './flags' diff --git a/src/parser/parse.ts b/src/parser/parse.ts index e385f08c..d0eb4bdd 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -169,6 +169,7 @@ export class Parser + +enum MyEnum { + 'A' = 'A', + 'B' = 'B', + 'C' = 'C', +} + +type MyType = { + foo: boolean; +} + +class MyCommand extends BaseCommand { + static description = 'describe the command here' + + static examples = [ + '<%= config.bin %> <%= command.id %>', + ] + + static flags = { + requiredString: Flags.string({required: true}), + optionalString: Flags.string(), + defaultString: Flags.string({default: 'default'}), + + requiredMultiString: Flags.string({required: true, multiple: true}), + optionalMultiString: Flags.string({multiple: true}), + defaultMultiString: Flags.string({multiple: true, default: ['default']}), + + requiredBoolean: Flags.boolean({required: true}), + optionalBoolean: Flags.boolean(), + defaultBoolean: Flags.boolean({default: true}), + + optionalEnum: Flags.enum({options: ['a', 'b', 'c']}), + requiredEnum: Flags.enum({options: ['a', 'b', 'c'], required: true}), + defaultEnum: Flags.enum({options: ['a', 'b', 'c'], default: 'a'}), + + optionalMultiEnum: Flags.enum({multiple: true, options: ['a', 'b', 'c']}), + requiredMultiEnum: Flags.enum({multiple: true, options: ['a', 'b', 'c'], required: true}), + defaultMultiEnum: Flags.enum({multiple: true, options: ['a', 'b', 'c'], default: ['a']}), + + optionalTypedEnum: Flags.enum({options: Object.values(MyEnum)}), + requiredTypedEnum: Flags.enum({options: Object.values(MyEnum), required: true}), + defaultTypedEnum: Flags.enum({options: Object.values(MyEnum), default: MyEnum.A}), + + optionalInteger: Flags.integer(), + requiredInteger: Flags.integer({required: true}), + defaultInteger: Flags.integer({default: 1}), + + optionalMultiInteger: Flags.integer({multiple: true}), + requiredMultiInteger: Flags.integer({multiple: true, required: true}), + defaultMultiInteger: Flags.integer({multiple: true, default: [1]}), + + optionalDirectory: Flags.directory(), + requiredDirectory: Flags.directory({required: true}), + defaultDirectory: 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']}), + + optionalFile: Flags.file(), + requiredFile: Flags.file({required: true}), + defaultFile: 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']}), + + optionalUrl: Flags.url(), + requiredUrl: Flags.url({required: true}), + defaultUrl: Flags.url({default: new URL('http://example.com')}), + + optionalMultiUrl: Flags.url({multiple: true}), + requiredMultiUrl: Flags.url({multiple: true, required: true}), + defaultMultiUrl: Flags.url({multiple: true, default: [new URL('http://example.com')]}), + + optionalBuild: Flags.build({ + parse: async () => ({foo: true}), + })(), + requiredBuild: Flags.build({ + parse: async () => ({foo: true}), + })({required: true}), + defaultBuild: Flags.build({ + parse: async () => ({foo: true}), + })({default: {foo: true}}), + + optionalCustom: Flags.custom({ + parse: async () => ({foo: true}), + })(), + requiredCustom: Flags.custom({ + parse: async () => ({foo: true}), + })({required: true}), + defaultCustom: Flags.custom({ + parse: async () => ({foo: true}), + })({default: {foo: true}}), + + optionalMultiCustom: Flags.custom({ + parse: async () => ({foo: true}), + })({multiple: true}), + requiredMultiCustom: Flags.custom({ + parse: async () => ({foo: true}), + })({required: true, multiple: true}), + defaultMultiCustom: Flags.custom({ + parse: async () => ({foo: true}), + })({default: [{foo: true}], multiple: true}), + } + + public flags!: MyFlags + + public async run(): Promise { + const result = await this.parse(MyCommand) + this.flags = result.flags + expectType(this.flags) + + expectType(this.flags.requiredGlobalFlag) + expectNotType(this.flags.requiredGlobalFlag) + expectType(this.flags.defaultGlobalFlag) + 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.requiredEnum) + expectNotType(this.flags.requiredEnum) + expectType(this.flags.defaultEnum) + expectNotType(this.flags.defaultEnum) + expectType(this.flags.optionalEnum) + + expectType(this.flags.requiredMultiEnum) + expectNotType(this.flags.requiredMultiEnum) + expectType(this.flags.defaultMultiEnum) + expectNotType(this.flags.defaultMultiEnum) + expectType(this.flags.optionalMultiEnum) + + expectType(this.flags.requiredTypedEnum) + expectNotType(this.flags.requiredTypedEnum) + expectType(this.flags.defaultTypedEnum) + expectNotType(this.flags.defaultTypedEnum) + expectType(this.flags.optionalTypedEnum) + + 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.requiredBuild) + expectNotType(this.flags.requiredBuild) + expectType(this.flags.defaultBuild) + expectNotType(this.flags.defaultBuild) + expectType(this.flags.optionalBuild) + + 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) + + return result.flags + } +} + diff --git a/tsconfig.json b/tsconfig.json index c994370d..42c61dcd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,8 @@ "strict": true, "target": "es2020", "lib": ["es2020"], - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "noErrorTruncation": true, }, "include": [ "./src/**/*" diff --git a/yarn.lock b/yarn.lock index 8169165d..09e6ab1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -520,6 +520,11 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== +"@tsd/typescript@~4.7.4": + version "4.7.4" + resolved "https://registry.yarnpkg.com/@tsd/typescript/-/typescript-4.7.4.tgz#f1e4e6c3099a174a0cb7aa51cf53f34f6494e528" + integrity sha512-jbtC+RgKZ9Kk65zuRZbKLTACf+tvFW4Rfq0JEMXrlmV3P3yme+Hm+pnb5fJRyt61SjIitcrC810wj7+1tgsEmg== + "@types/ansi-styles@^3.2.1": version "3.2.1" resolved "https://registry.yarnpkg.com/@types/ansi-styles/-/ansi-styles-3.2.1.tgz#49e996bb6e0b7957ca831205df31eb9a0702492c" @@ -563,6 +568,19 @@ resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.0.tgz#ab8109208106b5e764e5a6c92b2ba1c625b73020" integrity sha512-DCg+Ka+uDQ31lJ/UtEXVlaeV3d6t81gifaVWKJy4MYVVgvJttyX/viREy+If7fz+tK/gVxTGMtyrFPnm4gjrVA== +"@types/eslint@^7.2.13": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" + integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -590,6 +608,11 @@ 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@*": + 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.7": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -813,7 +836,7 @@ ansi-escapes@^3.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1258,7 +1281,7 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0: +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -1411,6 +1434,20 @@ eslint-config-xo@^0.38.0: dependencies: confusing-browser-globals "1.0.10" +eslint-formatter-pretty@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz#7a6877c14ffe2672066c853587d89603e97c7708" + integrity sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ== + dependencies: + "@types/eslint" "^7.2.13" + ansi-escapes "^4.2.1" + chalk "^4.1.0" + eslint-rule-docs "^1.1.5" + log-symbols "^4.0.0" + plur "^4.0.0" + string-width "^4.2.0" + supports-hyperlinks "^2.0.0" + 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" @@ -1457,6 +1494,11 @@ eslint-plugin-unicorn@^36.0.0: safe-regex "^2.1.1" semver "^7.3.5" +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" @@ -1824,7 +1866,7 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@^11.0.3, globby@^11.0.4, globby@^11.1.0: +globby@^11.0.1, globby@^11.0.3, globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -1963,6 +2005,11 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +irregular-plurals@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.3.0.tgz#67d0715d4361a60d9fd9ee80af3881c631a31ee2" + integrity sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2058,6 +2105,11 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +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-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -2246,6 +2298,14 @@ log-symbols@4.0.0: dependencies: chalk "^4.0.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== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + loupe@^2.3.1: version "2.3.4" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" @@ -2292,6 +2352,24 @@ meow@^8.0.0: type-fest "^0.18.0" yargs-parser "^20.2.3" +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.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" @@ -2617,6 +2695,13 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +plur@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" + integrity sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg== + dependencies: + irregular-plurals "^3.2.0" + pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" @@ -2678,7 +2763,7 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -read-pkg-up@^7.0.1: +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" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== @@ -3061,7 +3146,7 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.2.0: +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" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== @@ -3137,6 +3222,18 @@ ts-node@^9.1.1: source-map-support "^0.5.17" yn "3.1.1" +tsd@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.22.0.tgz#2ff75cf3adff5136896abee1f2c5540497bf3b54" + integrity sha512-NH+tfEDQ0Ze8gH7TorB6IxYybD+M68EYawe45YNVrbQcydNBfdQHP9IiD0QbnqmwNXrv+l9GAiULT68mo4q/xA== + dependencies: + "@tsd/typescript" "~4.7.4" + eslint-formatter-pretty "^4.1.0" + globby "^11.0.1" + 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"