Skip to content

Commit

Permalink
feat: allowStdin option for flags (#894) (#895)
Browse files Browse the repository at this point in the history
* feat: allow stdin option for flags

* feat: add extra check in test

* feat: update test to have constant for input

* feat: make parseFlag async

* feat: improve test

* feat: tweak description of flag

Co-authored-by: Kyle Capehart <[email protected]>
  • Loading branch information
mdonnalley and Kyle Capehart authored Dec 7, 2023
1 parent 88f3933 commit 379e940
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 6 deletions.
5 changes: 5 additions & 0 deletions src/interfaces/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ export type OptionFlagProps = FlagProps & {
* separate on spaces.
*/
delimiter?: ','
/**
* Allow input value to be read from stdin.
* Should only be used on one flag at a time.
*/
allowStdin?: boolean
}

export type FlagParserContext = Command & {token: FlagToken}
Expand Down
17 changes: 12 additions & 5 deletions src/parser/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ try {
}
}

const readStdin = async (): Promise<null | string> => {
export const readStdin = async (): Promise<null | string> => {
const {stdin, stdout} = process

// process.stdin.isTTY is true whenever it's running in a terminal.
Expand Down Expand Up @@ -122,15 +122,15 @@ export class Parser<
public async parse(): Promise<ParserOutput<TFlags, BFlags, TArgs>> {
this._debugInput()

const parseFlag = (arg: string): boolean => {
const parseFlag = async (arg: string): Promise<boolean> => {
const {isLong, name} = this.findFlag(arg)
if (!name) {
const i = arg.indexOf('=')
if (i !== -1) {
const sliced = arg.slice(i + 1)
this.argv.unshift(sliced)

const equalsParsed = parseFlag(arg.slice(0, i))
const equalsParsed = await parseFlag(arg.slice(0, i))
if (!equalsParsed) {
this.argv.shift()
}
Expand All @@ -149,12 +149,19 @@ export class Parser<
}

this.currentFlag = flag
const input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2)
let 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) {
throw new CLIError(`Flag --${name} expects a value`)
}

if (flag.allowStdin && input === '-') {
const stdin = await readStdin()
if (stdin) {
input = stdin.trim()
}
}

this.raw.push({flag: flag.name, input, type: 'flag'})
} else {
this.raw.push({flag: flag.name, input: arg, type: 'flag'})
Expand All @@ -181,7 +188,7 @@ export class Parser<
continue
}

if (parseFlag(input)) {
if (await parseFlag(input)) {
continue
}

Expand Down
31 changes: 30 additions & 1 deletion test/parser/parse.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {assert, config, expect} from 'chai'
import * as fs from 'node:fs'
import {URL} from 'node:url'
import {SinonStub, createSandbox} from 'sinon'
import {SinonSandbox, SinonStub, createSandbox} from 'sinon'

import {Args, Flags} from '../../src'
import {CLIError} from '../../src/errors'
import {FlagDefault} from '../../src/interfaces/parser'
import {parse} from '../../src/parser'
import * as parser from '../../src/parser/parse'

config.truncateThreshold = 0
const stripAnsi = require('strip-ansi')
Expand Down Expand Up @@ -1855,3 +1856,31 @@ See more help with --help`)
})
})
})

describe('allowStdin', () => {
let sandbox: SinonSandbox
const stdinValue = 'x'
const stdinPromise = new Promise<null | string>((resolve) => {
resolve(stdinValue)
})

beforeEach(() => {
sandbox = createSandbox()
})

afterEach(() => {
sandbox.restore()
})

it('should read stdin as input for flag', async () => {
sandbox.stub(parser, 'readStdin').returns(stdinPromise)
const out = await parse(['--myflag', '-'], {
flags: {
myflag: Flags.string({allowStdin: true}),
},
})

expect(out.flags.myflag).to.equals(stdinValue)
expect(out.raw[0].input).to.equal('x')
})
})

0 comments on commit 379e940

Please sign in to comment.