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: populate correct value on yargs.parsed and stop warning on access #1412

Merged
merged 3 commits into from Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 0 additions & 1 deletion index.js
Expand Up @@ -32,7 +32,6 @@ function singletonify (inst) {
return inst.$0
})
Argv.__defineGetter__('parsed', () => {
console.warn('In future major releases of yargs, "parsed" will be a private field. Use the return value of ".parse()" or ".argv" instead')
return inst.parsed
})
}
Expand Down
17 changes: 11 additions & 6 deletions lib/command.js
Expand Up @@ -174,7 +174,7 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {
let numFiles = currentContext.files.length
const parentCommands = currentContext.commands.slice()

// what does yargs look like after the buidler is run?
// what does yargs look like after the builder is run?
let innerArgv = parsed.argv
let innerYargs = null
let positionalMap = {}
Expand Down Expand Up @@ -230,14 +230,19 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) {

innerArgv = applyMiddleware(innerArgv, yargs, middlewares, false)

const handlerResult = isPromise(innerArgv)
? innerArgv.then(argv => commandHandler.handler(argv))
: commandHandler.handler(innerArgv)
let handlerResult
if (isPromise(innerArgv)) {
yargs.getUsageInstance().cacheHelpMessage()
bcoe marked this conversation as resolved.
Show resolved Hide resolved
handlerResult = innerArgv.then(argv => commandHandler.handler(argv))
} else {
handlerResult = commandHandler.handler(innerArgv)
}

if (isPromise(handlerResult)) {
handlerResult.catch(error =>
yargs.getUsageInstance().cacheHelpMessage()
handlerResult.catch(error => {
yargs.getUsageInstance().fail(null, error)
bcoe marked this conversation as resolved.
Show resolved Hide resolved
)
})
}
}

Expand Down
8 changes: 8 additions & 0 deletions lib/usage.js
Expand Up @@ -148,6 +148,7 @@ module.exports = function usage (yargs, y18n) {

const defaultGroup = 'Options:'
self.help = function help () {
if (cachedHelpMessage) return cachedHelpMessage
normalizeAliases()

// handle old demanded API
Expand Down Expand Up @@ -403,6 +404,13 @@ module.exports = function usage (yargs, y18n) {
})
}

// if yargs is executing an async handler, we take a snapshot of the
// help message to display on failure:
let cachedHelpMessage
self.cacheHelpMessage = function () {
cachedHelpMessage = this.help()
}

// given a set of keys, place any keys that are
// ungrouped under the 'Options:' grouping.
function addUngroupedKeys (keys, aliases, groups) {
Expand Down
82 changes: 56 additions & 26 deletions test/command.js
Expand Up @@ -1426,36 +1426,66 @@ describe('Command', () => {
})
})

// addresses https://github.com/yargs/yargs/issues/510
it('fails when the promise returned by the command handler rejects', (done) => {
const error = new Error()
yargs('foo')
.command('foo', 'foo command', noop, (yargs) => Promise.reject(error))
.fail((msg, err) => {
expect(msg).to.equal(null)
expect(err).to.equal(error)
done()
describe('async', () => {
// addresses https://github.com/yargs/yargs/issues/510
it('fails when the promise returned by the command handler rejects', (done) => {
const error = new Error()
yargs('foo')
.command('foo', 'foo command', noop, (yargs) => Promise.reject(error))
.fail((msg, err) => {
expect(msg).to.equal(null)
expect(err).to.equal(error)
done()
})
.parse()
})

it('succeeds when the promise returned by the command handler resolves', (done) => {
const handler = new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(true)
}, 5)
})
.parse()
})
const parsed = yargs('foo hello')
.command('foo <pos>', 'foo command', () => {}, (yargs) => handler)
.fail((msg, err) => {
return done(Error('should not have been called'))
})
.parse()

it('succeeds when the promise returned by the command handler resolves', (done) => {
const handler = new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(true)
}, 5)
})
const parsed = yargs('foo hello')
.command('foo <pos>', 'foo command', () => {}, (yargs) => handler)
.fail((msg, err) => {
return done(Error('should not have been called'))
handler.then(called => {
called.should.equal(true)
parsed.pos.should.equal('hello')
return done()
})
.parse()
})

handler.then(called => {
called.should.equal(true)
parsed.pos.should.equal('hello')
return done()
// see: https://github.com/yargs/yargs/issues/1144
it('displays error and appropriate help message when handler fails', (done) => {
// TODO: debug why exception bubbles to unhandledRejection
mleguen marked this conversation as resolved.
Show resolved Hide resolved
// if we set exitProcess to false; currently we are taking
// advantage of this to determine when to call done().
let errorLog = ''
const check = (err) => {
process.removeListener('unhandledRejection', check)
err.message.should.include('foo error')
errorLog.should.include('foo command')
return done()
}
process.on('unhandledRejection', check)
const y = yargs('foo')
.command('foo', 'foo command', () => {}, (argv) => {
return Promise.reject(Error('foo error'))
})
.exitProcess(false)
// the bug reported in #1144 only happens when
// usage.help() is called, this does not occur when
// a failure handler is attached. We override the logger
// so that we can catch console output:
y._getLoggerInstance().error = (out) => {
errorLog += out
}
y.parse()
})
})

Expand Down
6 changes: 4 additions & 2 deletions yargs.js
Expand Up @@ -544,10 +544,12 @@ function Yargs (processArgs, cwd, parentRequire) {
argsert('[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length)
freeze()
if (typeof args === 'undefined') {
const parsed = self._parseArgs(processArgs)
const argv = self._parseArgs(processArgs)
const tmpParsed = self.parsed
unfreeze()
// TODO: remove this compatibility hack when we release yargs@15.x:
return (this.parsed = parsed)
self.parsed = tmpParsed
return argv
}

// a context object can optionally be provided, this allows
Expand Down