Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: work with 4.8.3 #493

Merged
merged 4 commits into from Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -73,9 +73,9 @@
"shelljs": "^0.8.5",
"shx": "^0.3.4",
"sinon": "^11.1.2",
"ts-node": "^9.1.1",
"ts-node": "^10.9.1",
"tsd": "^0.22.0",
"typescript": "4.5.5"
"typescript": "^4.8.3"
},
"engines": {
"node": ">=14.0.0"
Expand Down Expand Up @@ -111,7 +111,7 @@
"prepack": "yarn run build",
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
"test:e2e": "mocha \"test/**/*.e2e.ts\" --timeout 600000",
"pretest": "yarn build --noEmit && tsc -p test --noEmit"
"pretest": "yarn build --noEmit && tsc -p test --noEmit --skipLibCheck"
},
"types": "lib/index.d.ts"
}
4 changes: 1 addition & 3 deletions src/command.ts
Expand Up @@ -107,10 +107,8 @@ export default abstract class Command {
if (value === true) {
this.globalFlags = jsonFlag
} else {
// @ts-expect-error because this.globalFlags is typed as a plain object
delete this.globalFlags?.json
this.flags = {} // force the flags setter to run
// @ts-expect-error because this.flags is typed as a plain object
delete this.flags?.json
}
}
Expand Down Expand Up @@ -250,7 +248,7 @@ export default abstract class Command {
g['http-call']!.userAgent = this.config.userAgent
}

protected async parse<F, G, A extends { [name: string]: any }>(options?: Interfaces.Input<F, G>, argv = this.argv): Promise<Interfaces.ParserOutput<F, G, A>> {
protected async parse<F extends Interfaces.FlagOutput, G extends Interfaces.FlagOutput, A extends { [name: string]: any }>(options?: Interfaces.Input<F, G>, argv = this.argv): Promise<Interfaces.ParserOutput<F, G, A>> {
if (!options) options = this.constructor as any
const opts = {context: this, ...options}
// the spread operator doesn't work with getters so we have to manually add it here
Expand Down
3 changes: 1 addition & 2 deletions src/interfaces/parser.ts
Expand Up @@ -246,5 +246,4 @@ export type CompletableOptionFlag<T> = OptionFlag<T> & {

export type CompletableFlag<T> = BooleanFlag<T> | CompletableOptionFlag<T>

// eslint-disable-next-line @typescript-eslint/ban-types
export type FlagInput<T extends FlagOutput = object> = { [P in keyof T]: CompletableFlag<T[P]> }
export type FlagInput<T extends FlagOutput = { [flag: string]: any }> = { [P in keyof T]: CompletableFlag<T[P]> }
4 changes: 2 additions & 2 deletions src/parser/index.ts
Expand Up @@ -2,7 +2,7 @@ import * as args from './args'
import Deps from './deps'
import * as flags from './flags'
import {Parser} from './parse'
import {FlagInput, Input, ParserOutput} from '../interfaces'
import {FlagInput, Input, ParserOutput, OutputFlags, FlagOutput} from '../interfaces'
import * as Validate from './validate'
export {args}
export {flags}
Expand All @@ -13,7 +13,7 @@ const m = Deps()
// eslint-disable-next-line node/no-missing-require
.add('validate', () => require('./validate').validate as typeof Validate.validate)

export async function parse<TFlags, GFlags, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags, GFlags>): Promise<ParserOutput<TFlags, GFlags, TArgs>> {
export async function parse<TFlags extends OutputFlags<any>, GFlags extends FlagOutput, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags, GFlags>): Promise<ParserOutput<TFlags, GFlags, TArgs>> {
const input = {
argv,
context: options.context,
Expand Down
2 changes: 1 addition & 1 deletion src/parser/util.ts
@@ -1,4 +1,4 @@
export function pickBy<T>(obj: T, fn: (i: T[keyof T]) => boolean): Partial<T> {
export function pickBy<T extends { [s: string]: T[keyof T]; } | ArrayLike<T[keyof T]>>(obj: T, fn: (i: T[keyof T]) => boolean): Partial<T> {
return Object.entries(obj)
.reduce((o, [k, v]) => {
if (fn(v)) o[k] = v
Expand Down
44 changes: 3 additions & 41 deletions test/help/format-command-with-options.test.ts
@@ -1,8 +1,7 @@
import {expect, test as base} from '@oclif/test'
import stripAnsi = require('strip-ansi')

import {Command as Base, Flags as flags, Interfaces, toCached} from '../../src'
import {Help, CommandHelp} from '../../src/help'
import {Command as Base, Flags as flags} from '../../src'
import {commandHelp, TestHelpWithOptions as TestHelp} from './help-test-utils'

const g: any = global
g.oclif.columns = 80
Expand All @@ -15,47 +14,10 @@ class Command extends Base {
}
}

// Allow overriding section headers
class TestCommandHelp extends CommandHelp {
protected sections() {
const sections = super.sections()
const flagSection = sections.find(section => section.header === 'FLAGS')
if (flagSection) flagSection.header = 'OPTIONS'
return sections
}
}

// extensions to expose method as public for testing
class TestHelp extends Help {
CommandHelpClass = TestCommandHelp

constructor(config: Interfaces.Config, opts: Partial<Interfaces.HelpOptions> = {}) {
super(config, opts)
this.opts.showFlagNameInTitle = true
this.opts.showFlagOptionsInTitle = true
this.opts.hideCommandSummaryInDescription = true
}

public formatCommand(command: Interfaces.Command) {
return super.formatCommand(command)
}
}

const test = base
.loadConfig()
.add('help', ctx => new TestHelp(ctx.config as any))
.register('commandHelp', (command?: any) => ({
async run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const cached = await toCached(command!, {} as any)
const help = ctx.help.formatCommand(cached)
if (process.env.TEST_OUTPUT === '1') {
console.log(help)
}

ctx.commandHelp = stripAnsi(help).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has commandHelp'
},
}))
.register('commandHelp', commandHelp)

describe('formatCommand', () => {
test
Expand Down
25 changes: 3 additions & 22 deletions test/help/format-command.test.ts
@@ -1,8 +1,7 @@
import {expect, test as base} from '@oclif/test'
import stripAnsi = require('strip-ansi')

import {Command as Base, Flags as flags, Interfaces, toCached} from '../../src'
import {Help} from '../../src/help'
import {Command as Base, Flags as flags} from '../../src'
import {commandHelp, TestHelp} from './help-test-utils'

const g: any = global
g.oclif.columns = 80
Expand All @@ -13,28 +12,10 @@ class Command extends Base {
}
}

// extensions to expose method as public for testing
class TestHelp extends Help {
public formatCommand(command: Interfaces.Command) {
return super.formatCommand(command)
}
}

const test = base
.loadConfig()
.add('help', ctx => new TestHelp(ctx.config as any))
.register('commandHelp', (command?: any) => ({
async run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const cached = await toCached(command!, {} as any)
const help = ctx.help.formatCommand(cached)
if (process.env.TEST_OUTPUT === '1') {
console.log(help)
}

ctx.commandHelp = stripAnsi(help).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has commandHelp'
},
}))
.register('commandHelp', commandHelp)

describe('formatCommand', () => {
test
Expand Down
14 changes: 7 additions & 7 deletions test/help/format-commands.test.ts
Expand Up @@ -14,10 +14,7 @@ class TestHelp extends Help {
}
}

const test = base
.loadConfig()
.add('help', ctx => new TestHelp(ctx.config as any))
.register('formatCommands', (commands: Interfaces.Command[] = []) => ({
const formatCommands = (commands: Interfaces.Command[]) => ({
run(ctx: {help: TestHelp; output: string}) {
const help = ctx.help.formatCommands(commands)
if (process.env.TEST_OUTPUT === '1') {
Expand All @@ -26,7 +23,12 @@ const test = base

ctx.output = stripAnsi(help).split('\n').map(s => s.trimEnd()).join('\n')
},
}))
})

const test = base
.loadConfig()
.add('help', ctx => new TestHelp(ctx.config as any))
.register('formatCommands', formatCommands)

describe('formatCommand', () => {
test
Expand All @@ -47,8 +49,6 @@ describe('formatCommand', () => {

static description = 'This is a very long command description that should wrap after too many characters have been entered'

static flags = {}

static args = []

async run() {
Expand Down
10 changes: 6 additions & 4 deletions test/help/format-root.test.ts
Expand Up @@ -17,9 +17,7 @@ class TestHelp extends Help {
}
}

const test = base
.loadConfig()
.register('rootHelp', (ctxOverride?: (config: Interfaces.Config) => Interfaces.Config) => ({
const rootHelp = (ctxOverride?: (config: Interfaces.Config) => Interfaces.Config) => ({
run(ctx: { config: Interfaces.Config; help: Help; commandHelp: string; expectation: string}) {
const config = ctxOverride ? ctxOverride(ctx.config) : ctx.config

Expand All @@ -31,7 +29,11 @@ const test = base

ctx.commandHelp = stripAnsi(root).split('\n').map(s => s.trimEnd()).join('\n')
},
}))
})

const test = base
.loadConfig()
.register('rootHelp', rootHelp)

describe('formatRoot', () => {
test
Expand Down
24 changes: 2 additions & 22 deletions test/help/format-topic.test.ts
@@ -1,35 +1,15 @@
import {expect, test as base} from '@oclif/test'
import stripAnsi = require('strip-ansi')

import {Help} from '../../src/help'
import {Interfaces} from '../../src'
import {TestHelp, topicHelp} from './help-test-utils'

const g: any = global
g.oclif.columns = 80

// extensions to expose method as public for testing
class TestHelp extends Help {
public formatTopic(topic: Interfaces.Topic) {
return super.formatTopic(topic)
}
}

const test = base
.loadConfig()
.add('help', ctx => {
return new TestHelp(ctx.config as any)
})
.register('topicHelp', (topic: Interfaces.Topic) => ({
run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const topicHelpOutput = ctx.help.formatTopic(topic)
if (process.env.TEST_OUTPUT === '1') {
console.log(topicHelpOutput)
}

ctx.commandHelp = stripAnsi(topicHelpOutput).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has topicHelp'
},
}))
.register('topicHelp', topicHelp)

describe('formatHelp', () => {
test
Expand Down
24 changes: 2 additions & 22 deletions test/help/format-topics.test.ts
@@ -1,34 +1,14 @@
import {expect, test as base} from '@oclif/test'
import stripAnsi = require('strip-ansi')

import {Help} from '../../src/help'
import {Interfaces} from '../../src'
import {TestHelp, topicsHelp} from './help-test-utils'

const g: any = global
g.oclif.columns = 80

// extensions to expose method as public for testing
class TestHelp extends Help {
public formatTopics(topics: Interfaces.Topic[]) {
return super.formatTopics(topics)
}
}

const test = base
.loadConfig()
.add('help', ctx => new TestHelp(ctx.config as any))
.register('topicsHelp', (topics: Interfaces.Topic[]) => ({
run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const topicsHelpOutput = ctx.help.formatTopics(topics) || ''

if (process.env.TEST_OUTPUT === '1') {
console.log(topicsHelpOutput)
}

ctx.commandHelp = stripAnsi(topicsHelpOutput).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has topicsHelp'
},
}))
.register('topicsHelp', topicsHelp)

describe('formatTopics', () => {
test
Expand Down
82 changes: 82 additions & 0 deletions test/help/help-test-utils.ts
@@ -0,0 +1,82 @@
import stripAnsi = require('strip-ansi')

import {Interfaces, toCached} from '../../src'
import {Help, CommandHelp} from '../../src/help'

export class TestCommandHelp extends CommandHelp {
protected sections() {
const sections = super.sections()
const flagSection = sections.find(section => section.header === 'FLAGS')
if (flagSection) flagSection.header = 'OPTIONS'
return sections
}
}

// extensions to expose method as public for testing
export class TestHelpWithOptions extends Help {
CommandHelpClass = TestCommandHelp

constructor(config: Interfaces.Config, opts: Partial<Interfaces.HelpOptions> = {}) {
super(config, opts)
this.opts.showFlagNameInTitle = true
this.opts.showFlagOptionsInTitle = true
this.opts.hideCommandSummaryInDescription = true
}

public formatCommand(command: Interfaces.Command) {
return super.formatCommand(command)
}
}

// extensions to expose method as public for testing
export class TestHelp extends Help {
public formatCommand(command: Interfaces.Command) {
return super.formatCommand(command)
}

public formatTopics(topics: Interfaces.Topic[]) {
return super.formatTopics(topics)
}

public formatTopic(topic: Interfaces.Topic) {
return super.formatTopic(topic)
}
}

export const commandHelp = (command?: any) => ({
async run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const cached = await toCached(command!, {} as any)
const help = ctx.help.formatCommand(cached)
if (process.env.TEST_OUTPUT === '1') {
console.log(help)
}

ctx.commandHelp = stripAnsi(help).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has commandHelp'
},
})

export const topicsHelp = (topics: Interfaces.Topic[]) => ({
run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const topicsHelpOutput = ctx.help.formatTopics(topics) || ''

if (process.env.TEST_OUTPUT === '1') {
console.log(topicsHelpOutput)
}

ctx.commandHelp = stripAnsi(topicsHelpOutput).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has topicsHelp'
},
})

export const topicHelp = (topic: Interfaces.Topic) => ({
run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const topicHelpOutput = ctx.help.formatTopic(topic)
if (process.env.TEST_OUTPUT === '1') {
console.log(topicHelpOutput)
}

ctx.commandHelp = stripAnsi(topicHelpOutput).split('\n').map(s => s.trimEnd()).join('\n')
ctx.expectation = 'has topicHelp'
},
})