Browse Source

Fixed the input and output utilities (#808)

* Fixed the input and output utilities

* Stop swallowing errors
master
Leo Lamprecht 7 years ago
committed by GitHub
parent
commit
f67af44f21
  1. 4
      src/now.js
  2. 24
      src/providers/sh/commands/alias.js
  3. 22
      src/providers/sh/commands/billing.js
  4. 7
      src/providers/sh/commands/billing/add.js
  5. 38
      src/providers/sh/commands/certs.js
  6. 40
      src/providers/sh/commands/deploy.js
  7. 18
      src/providers/sh/commands/dns.js
  8. 14
      src/providers/sh/commands/domains.js
  9. 15
      src/providers/sh/commands/domains/buy.js
  10. 2
      src/providers/sh/commands/list.js
  11. 3
      src/providers/sh/commands/logout.js
  12. 6
      src/providers/sh/commands/remove.js
  13. 10
      src/providers/sh/commands/scale.js
  14. 22
      src/providers/sh/commands/secrets.js
  15. 8
      src/providers/sh/commands/teams.js
  16. 4
      src/providers/sh/commands/teams/add.js
  17. 2
      src/providers/sh/commands/teams/invite.js
  18. 2
      src/providers/sh/commands/teams/list.js
  19. 2
      src/providers/sh/commands/teams/switch.js
  20. 14
      src/providers/sh/commands/upgrade.js
  21. 4
      src/providers/sh/util/alias.js
  22. 18
      src/providers/sh/util/error.js
  23. 8
      src/providers/sh/util/re-alias.js
  24. 35
      src/util/input/list.js
  25. 2
      src/util/input/patch-inquirer.js
  26. 24
      src/util/input/prompt-bool.js
  27. 276
      src/util/input/text.js

4
src/now.js

@ -73,11 +73,11 @@ const main = async (argv_) => {
try {
await mkdirp(NOW_DIR)
} catch (err) {
error(
console.error(error(
'An unexpected error occurred while trying to create the ' +
`now global directory "${hp(NOW_DIR)}" ` +
err.message
)
))
}
}

24
src/providers/sh/commands/alias.js

@ -143,9 +143,9 @@ const main = async ctx => {
await run({ token, sh })
} catch (err) {
if (err.userError) {
error(err.message)
console.error(error(err.message))
} else {
error(`Unknown error: ${err}\n${err.stack}`)
console.error(error(`Unknown error: ${err}\n${err.stack}`))
}
exit(1)
@ -175,7 +175,7 @@ async function run({ token, sh: { currentTeam, user } }) {
e => e.uid === argv._[1] || e.alias === argv._[1]
)
if (!item || !item.rules) {
error(`Could not match path alias for: ${argv._[1]}`)
console.error(error(`Could not match path alias for: ${argv._[1]}`))
return exit(1)
}
@ -210,9 +210,9 @@ async function run({ token, sh: { currentTeam, user } }) {
}
break
} else if (args.length !== 0) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now alias ls`')}`
)
))
return exit(1)
}
@ -313,11 +313,11 @@ async function run({ token, sh: { currentTeam, user } }) {
}
if (args.length !== 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now alias rm <id>`'
)}`
)
))
return exit(1)
}
@ -350,7 +350,7 @@ async function run({ token, sh: { currentTeam, user } }) {
)} removed [${elapsed}]`
)
} catch (err) {
error(err)
console.error(error(err))
exit(1)
}
@ -363,11 +363,11 @@ async function run({ token, sh: { currentTeam, user } }) {
break
}
if (args.length !== 2) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now alias set <id> <domain>`'
)}`
)
))
return exit(1)
}
await alias.set(
@ -424,11 +424,11 @@ async function run({ token, sh: { currentTeam, user } }) {
user
)
} else if (argv._.length >= 3) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
help()
exit(1)
} else {
error('Please specify a valid subcommand: ls | set | rm')
console.error(error('Please specify a valid subcommand: ls | set | rm'))
help()
exit(1)
}

22
src/providers/sh/commands/billing.js

@ -95,9 +95,9 @@ const main = async ctx => {
await run({ token, sh })
} catch (err) {
if (err.userError) {
error(err.message)
console.error(error(err.message))
} else {
error(`Unknown error: ${err.stack}`)
console.error(error(`Unknown error: ${err.stack}`))
}
exit(1)
@ -146,7 +146,7 @@ async function run({ token, sh: { currentTeam, user } }) {
try {
cards = await creditCards.ls()
} catch (err) {
error(err.message)
console.error(error(err.message))
return
}
const text = cards.cards
@ -201,7 +201,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'set-default': {
if (args.length > 1) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
return exit(1)
}
@ -211,12 +211,12 @@ async function run({ token, sh: { currentTeam, user } }) {
try {
cards = await creditCards.ls()
} catch (err) {
error(err.message)
console.error(error(err.message))
return
}
if (cards.cards.length === 0) {
error('You have no credit cards to choose from')
console.error(error('You have no credit cards to choose from'))
return exit(0)
}
@ -268,7 +268,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'rm':
case 'remove': {
if (args.length > 1) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
return exit(1)
}
@ -277,16 +277,16 @@ async function run({ token, sh: { currentTeam, user } }) {
try {
cards = await creditCards.ls()
} catch (err) {
error(err.message)
console.error(error(err.message))
return
}
if (cards.cards.length === 0) {
error(
console.error(error(
`You have no credit cards to choose from to delete under ${chalk.bold(
(currentTeam && currentTeam.slug) || user.username || user.email
)}`
)
))
return exit(0)
}
@ -365,7 +365,7 @@ async function run({ token, sh: { currentTeam, user } }) {
}
default:
error('Please specify a valid subcommand: ls | add | rm | set-default')
console.error(error('Please specify a valid subcommand: ls | add | rm | set-default'))
help()
exit(1)
}

7
src/providers/sh/commands/billing/add.js

@ -14,10 +14,9 @@ const success = require('../../../../util/output/success')
const wait = require('../../../../util/output/wait')
const { tick } = require('../../../../util/output/chars')
const rightPad = require('../../../../util/output/right-pad')
const error = require('../../../../util/output/error')
function expDateMiddleware(data) {
return data
}
const expDateMiddleware = data => data
module.exports = async function({
creditCards,
@ -205,7 +204,7 @@ module.exports = async function({
if (err.message === 'USER_ABORT') {
process.exit(1)
} else {
console.error(err)
console.error(error(err))
}
}
}

38
src/providers/sh/commands/certs.js

@ -130,9 +130,9 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'ls' || subcommand === 'list') {
if (args.length !== 0) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`
)
))
return exit(1)
}
@ -186,11 +186,11 @@ async function run({ token, sh: { currentTeam, user } }) {
}
} else if (subcommand === 'create') {
if (args.length !== 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs create <cn>`'
)}`
)
))
return exit(1)
}
const cn = args[0]
@ -199,11 +199,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (argv.crt || argv.key || argv.ca) {
// Issue a custom certificate
if (!argv.crt || !argv.key) {
error(
console.error(error(
`Missing required arguments for a custom certificate entry. Usage: ${chalk.cyan(
'`now certs create --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`'
)}`
)
))
return exit(1)
}
@ -228,11 +228,11 @@ async function run({ token, sh: { currentTeam, user } }) {
)
} else if (subcommand === 'renew') {
if (args.length !== 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs renew <id | cn>`'
)}`
)
))
return exit(1)
}
@ -246,7 +246,7 @@ async function run({ token, sh: { currentTeam, user } }) {
)
if (!yes) {
error('User abort')
console.error(error('User abort'))
return exit(0)
}
@ -259,11 +259,11 @@ async function run({ token, sh: { currentTeam, user } }) {
)
} else if (subcommand === 'replace') {
if (!argv.crt || !argv.key) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`'
)}`
)
))
return exit(1)
}
@ -280,7 +280,7 @@ async function run({ token, sh: { currentTeam, user } }) {
'The following certificate will be replaced permanently\n'
)
if (!yes) {
error('User abort')
console.error(error('User abort'))
return exit(0)
}
@ -293,11 +293,11 @@ async function run({ token, sh: { currentTeam, user } }) {
)
} else if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs rm <id | cn>`'
)}`
)
))
return exit(1)
}
@ -310,7 +310,7 @@ async function run({ token, sh: { currentTeam, user } }) {
'The following certificate will be removed permanently\n'
)
if (!yes) {
error('User abort')
console.error(error('User abort'))
return exit(0)
}
@ -322,9 +322,9 @@ async function run({ token, sh: { currentTeam, user } }) {
)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`
)
} else {
error(
console.error(error(
'Please specify a valid subcommand: ls | create | renew | replace | rm'
)
))
help()
exit(1)
}
@ -371,11 +371,11 @@ async function getCertIdCn(certs, idOrCn, currentTeam, user) {
})[0]
if (!thecert) {
error(
console.error(error(
`No certificate found by id or cn "${idOrCn}" under ${chalk.bold(
(currentTeam && currentTeam.slug) || user.username || user.email
)}`
)
))
return null
}

40
src/providers/sh/commands/deploy.js

@ -341,7 +341,7 @@ async function sync({ token, config: { currentTeam, user } }) {
)}" ${gitRef}on ${gitRepo.type}`
)
} else {
error(`The specified directory "${basename(path)}" doesn't exist.`)
console.error(error(`The specified directory "${basename(path)}" doesn't exist.`))
process.exit(1)
}
}
@ -350,8 +350,8 @@ async function sync({ token, config: { currentTeam, user } }) {
try {
await checkPath(path)
} catch (err) {
error(err)
return
console.error(error(err.message))
process.exit(1)
}
if (!quiet) {
@ -421,7 +421,7 @@ async function sync({ token, config: { currentTeam, user } }) {
typeof dotenvOption === 'string' ? dotenvOption : '.env'
if (!fs.existsSync(dotenvFileName)) {
error(`--dotenv flag is set but ${dotenvFileName} file is missing`)
console.error(error(`--dotenv flag is set but ${dotenvFileName} file is missing`))
return process.exit(1)
}
@ -460,7 +460,7 @@ async function sync({ token, config: { currentTeam, user } }) {
const env_ = await Promise.all(
envs.map(async kv => {
if (typeof kv !== 'string') {
error('Env key and value missing')
console.error(error('Env key and value missing'))
return process.exit(1)
}
@ -472,16 +472,16 @@ async function sync({ token, config: { currentTeam, user } }) {
}
if (/[^A-z0-9_]/i.test(key)) {
error(
console.error(error(
`Invalid ${chalk.dim('-e')} key ${chalk.bold(
`"${chalk.bold(key)}"`
)}. Only letters, digits and underscores are allowed.`
)
))
return process.exit(1)
}
if (!key) {
error(`Invalid env option ${chalk.bold(`"${kv}"`)}`)
console.error(error(`Invalid env option ${chalk.bold(`"${kv}"`)}`))
return process.exit(1)
}
@ -495,11 +495,11 @@ async function sync({ token, config: { currentTeam, user } }) {
// Escape value if it begins with @
val = process.env[key].replace(/^@/, '\\@')
} else {
error(
console.error(error(
`No value specified for env ${chalk.bold(
`"${chalk.bold(key)}"`
)} and it was not found in your env.`
)
))
return process.exit(1)
}
}
@ -509,23 +509,23 @@ async function sync({ token, config: { currentTeam, user } }) {
const _secrets = await findSecret(uidOrName)
if (_secrets.length === 0) {
if (uidOrName === '') {
error(
console.error(error(
`Empty reference provided for env key ${chalk.bold(
`"${chalk.bold(key)}"`
)}`
)
))
} else {
error(
console.error(error(
`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`
)
))
}
return process.exit(1)
} else if (_secrets.length > 1) {
error(
console.error(error(
`Ambiguous secret ${chalk.bold(
`"${uidOrName}"`
)} (matches ${chalk.bold(_secrets.length)} secrets)`
)
))
return process.exit(1)
}
@ -700,7 +700,7 @@ async function sync({ token, config: { currentTeam, user } }) {
now.on('complete', () => complete({ syncCount }))
now.on('error', err => {
error('Upload failed')
console.error(error('Upload failed'))
return stopDeployment(err)
})
} else {
@ -799,13 +799,13 @@ function printLogs(host, token) {
logger.on('error', async err => {
if (!quiet) {
if (err && err.type === 'BUILD_ERROR') {
error(
console.error(error(
`The build step of your project failed. To retry, run ${cmd(
'now --force'
)}.`
)
))
} else {
error('Deployment failed')
console.error(error('Deployment failed'))
}
}

18
src/providers/sh/commands/dns.js

@ -112,11 +112,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'ls' || subcommand === 'list') {
if (args.length > 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now dns ls [domain]`'
)}`
)
))
return exit(1)
}
@ -175,11 +175,11 @@ async function run({ token, sh: { currentTeam, user } }) {
} else if (subcommand === 'add') {
const param = parseAddArgs(args)
if (!param) {
error(
console.error(error(
`Invalid number of arguments. See: ${chalk.cyan(
'`now dns --help`'
)} for usage.`
)
))
return exit(1)
}
const record = await domainRecords.create(param.domain, param.data)
@ -195,15 +195,15 @@ async function run({ token, sh: { currentTeam, user } }) {
)
} else if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now dns rm <id>`')}`
)
))
return exit(1)
}
const record = await domainRecords.getRecord(args[0])
if (!record) {
error('DNS record not found')
console.error(error('DNS record not found'))
return exit(1)
}
@ -212,7 +212,7 @@ async function run({ token, sh: { currentTeam, user } }) {
'The following record will be removed permanently \n'
)
if (!yes) {
error('User abort')
console.error(error('User abort'))
return exit(0)
}
@ -224,7 +224,7 @@ async function run({ token, sh: { currentTeam, user } }) {
)} removed ${chalk.gray(`[${elapsed}]`)}`
)
} else {
error('Please specify a valid subcommand: ls | add | rm')
console.error(error('Please specify a valid subcommand: ls | add | rm'))
help()
exit(1)
}

14
src/providers/sh/commands/domains.js

@ -152,9 +152,9 @@ const main = async ctx => {
await run({ token, sh })
} catch (err) {
if (err.userError) {
error(err.message)
console.error(error(err.message))
} else {
error(`Unknown error: ${err}\n${err.stack}`)
console.error(error(`Unknown error: ${err}\n${err.stack}`))
}
exit(1)
@ -178,7 +178,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'ls':
case 'list': {
if (args.length !== 0) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
return exit(1)
}
@ -228,7 +228,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'rm':
case 'remove': {
if (args.length !== 1) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
return exit(1)
}
@ -271,7 +271,7 @@ async function run({ token, sh: { currentTeam, user } }) {
)} removed [${elapsed}]`
)
} catch (err) {
error(err)
console.error(error(err))
exit(1)
}
break
@ -279,7 +279,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'add':
case 'set': {
if (args.length !== 1) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
return exit(1)
}
const name = String(args[0])
@ -343,7 +343,7 @@ async function run({ token, sh: { currentTeam, user } }) {
break
}
default:
error('Please specify a valid subcommand: ls | add | rm')
console.error(error('Please specify a valid subcommand: ls | add | rm'))
help()
exit(1)
}

15
src/providers/sh/commands/domains/buy.js

@ -20,7 +20,7 @@ module.exports = async function({ domains, args, currentTeam, user, coupon }) {
let elapsed
if (!name) {
return error(`Missing domain name. Run ${cmd('now domains help')}`)
return console.error(error(`Missing domain name. Run ${cmd('now domains help')}`))
}
const nameParam = param(name)
@ -45,11 +45,11 @@ module.exports = async function({ domains, args, currentTeam, user, coupon }) {
stopSpinner()
if (!couponInfo.isValid) {
return error(`The coupon ${param(coupon)} is invalid`)
return console.error(error(`The coupon ${param(coupon)} is invalid`))
}
if (!couponInfo.canBeUsed) {
return error(`The coupon ${param(coupon)} has already been used`)
return console.error(error(`The coupon ${param(coupon)} has already been used`))
}
validCoupon = true
@ -75,18 +75,19 @@ module.exports = async function({ domains, args, currentTeam, user, coupon }) {
period = json.period
} catch (err) {
stopSpinner()
return error(err.message)
return console.error(error(err.message))
}
const available = await domains.status(name)
stopSpinner()
if (!available) {
return error(
console.error(error(
`The domain ${nameParam} is ${italic('unavailable')}! ${elapsed()}`
)
))
return
}
const periodMsg = `${period}yr${period > 1 ? 's' : ''}`
info(
`The domain ${nameParam} is ${italic('available')} to buy under ${bold(

2
src/providers/sh/commands/list.js

@ -75,7 +75,7 @@ const main = async ctx => {
try {
await list({ token, sh })
} catch (err) {
error(`Unknown error: ${err}\n${err.stack}`)
console.error(error(`Unknown error: ${err}\n${err.stack}`))
process.exit(1)
}
}

3
src/providers/sh/commands/logout.js

@ -15,6 +15,7 @@ const {
readAuthConfigFile,
writeToAuthConfigFile
} = require('../../../util/config-files')
const error = require('../../../util/output/error')
const help = () => {
console.log(`
@ -101,7 +102,7 @@ const revokeToken = async (token, tokenId) => {
const result = await fetch(endpoint + encodeURIComponent(tokenId), details)
if (!result.ok) {
console.error('Not able to log out')
console.error(error('Not able to log out'))
}
}

6
src/providers/sh/commands/remove.js

@ -90,7 +90,7 @@ const main = async ctx => {
try {
await remove({ token, sh })
} catch (err) {
error(`Unknown error: ${err}\n${err.stack}`)
console.error(error(`Unknown error: ${err}\n${err.stack}`))
process.exit(1)
}
}
@ -171,13 +171,13 @@ async function remove({ token, sh: { currentTeam } }) {
})
if (matches.length === 0) {
error(
console.error(error(
`Could not find ${argv.safe
? 'unaliased'
: 'any'} deployments matching ${ids
.map(id => chalk.bold(`"${id}"`))
.join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.`
)
))
return process.exit(1)
}

10
src/providers/sh/commands/scale.js

@ -100,9 +100,9 @@ const main = async ctx => {
await run({ token, sh })
} catch (err) {
if (err.userError) {
error(err.message)
console.error(error(err.message))
} else {
error(`Unknown error: ${err}\n${err.stack}`)
console.error(error(`Unknown error: ${err}\n${err.stack}`))
}
exit(1)
@ -162,7 +162,7 @@ async function run({ token, sh: { currentTeam } }) {
}
}
} else {
error('Please specify a deployment: now scale <id|url>')
console.error(error('Please specify a deployment: now scale <id|url>'))
help()
exit(1)
}
@ -186,7 +186,7 @@ async function run({ token, sh: { currentTeam } }) {
e => e.alias === id
)
if (!aliasDeployment) {
error(`Could not find any deployments matching ${id}`)
console.error(error(`Could not find any deployments matching ${id}`))
return process.exit(1)
}
match = deployments.find(d => {
@ -206,7 +206,7 @@ async function run({ token, sh: { currentTeam } }) {
if (match.type === 'STATIC') {
if (min === 0 && max === 0) {
error("Static deployments can't be FROZEN. Use `now rm` to remove")
console.error(error("Static deployments can't be FROZEN. Use `now rm` to remove"))
return process.exit(1)
}
console.log('> Static deployments are automatically scaled!')

22
src/providers/sh/commands/secrets.js

@ -119,9 +119,9 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'ls' || subcommand === 'list') {
if (args.length !== 0) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`
)
))
return exit(1)
}
@ -165,11 +165,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now secret rm <name>`'
)}`
)
))
return exit(1)
}
const list = await secrets.ls()
@ -178,11 +178,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (theSecret) {
const yes = await readConfirmation(theSecret)
if (!yes) {
error('User abort')
console.error(error('User abort'))
return exit(0)
}
} else {
error(`No secret found by name "${args[0]}"`)
console.error(error(`No secret found by name "${args[0]}"`))
return exit(1)
}
@ -198,11 +198,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'rename') {
if (args.length !== 2) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now secret rename <old-name> <new-name>`'
)}`
)
))
return exit(1)
}
const secret = await secrets.rename(args[0], args[1])
@ -217,11 +217,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'add' || subcommand === 'set') {
if (args.length !== 2) {
error(
console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now secret add <name> <value>`'
)}`
)
))
if (args.length > 2) {
const example = chalk.cyan(`$ now secret add ${args[0]}`)
@ -255,7 +255,7 @@ async function run({ token, sh: { currentTeam, user } }) {
return secrets.close()
}
error('Please specify a valid subcommand: ls | add | rename | rm')
console.error(error('Please specify a valid subcommand: ls | add | rename | rm'))
help()
exit(1)
}

8
src/providers/sh/commands/teams.js

@ -104,9 +104,9 @@ const main = async ctx => {
await run({ token, config })
} catch (err) {
if (err.userError) {
error(err.message)
console.error(error(err.message))
} else {
error(`Unknown error: ${err.stack}`)
console.error(error(`Unknown error: ${err.stack}`))
}
exit(1)
@ -147,7 +147,7 @@ async function run({ token, config }) {
}
case 'add':
case 'create': {
await add({ teams, token })
await add({ teams, config })
break
}
@ -163,7 +163,7 @@ async function run({ token, config }) {
default: {
let code = 0
if (subcommand !== 'help') {
error('Please specify a valid subcommand: ls | add | rm | set-default')
console.error(error('Please specify a valid subcommand: ls | add | rm | set-default'))
code = 1
}
help()

4
src/providers/sh/commands/teams/add.js

@ -75,7 +75,7 @@ module.exports = async function({ teams, config }) {
} catch (err) {
stopSpinner()
eraseLines(2)
error(err.message)
console.error(error(err.message))
}
} while (!team)
@ -105,7 +105,7 @@ module.exports = async function({ teams, config }) {
eraseLines(2)
if (res.error) {
error(res.error.message)
console.error(error(res.error.message))
console.log(`${chalk.red(`${teamNamePrefix}`)}${name}`)
exit(1)
// TODO: maybe we want to ask the user to retry? not sure if

2
src/providers/sh/commands/teams/invite.js

@ -136,7 +136,7 @@ module.exports = async function(
} catch (err) {
stopSpinner()
eraseLines(emails.length + 2)
error(err.message)
console.error(error(err.message))
hasError = true
for (const email of emails) {
console.log(`${chalk.cyan(tick)} ${inviteUserPrefix}${email}`)

2
src/providers/sh/commands/teams/list.js

@ -49,7 +49,7 @@ module.exports = async function({ teams, config }) {
const count = teamList.length
if (!count) {
// Maybe should not happen
error(`No team found`)
console.error(error(`No team found`))
return
}

2
src/providers/sh/commands/teams/switch.js

@ -57,7 +57,7 @@ module.exports = async function({ teams, args, config }) {
return success(`Your account (${chalk.bold(desiredSlug)}) is now active!`)
}
error(`Could not find membership for team ${param(desiredSlug)}`)
console.error(error(`Could not find membership for team ${param(desiredSlug)}`))
return exit(1)
}

14
src/providers/sh/commands/upgrade.js

@ -89,9 +89,9 @@ const main = async ctx => {
await run({ token, sh })
} catch (err) {
if (err.userError) {
error(err.message)
console.error(error(err.message))
} else {
error(`Unknown error: ${err.stack}`)
console.error(error(`Unknown error: ${err.stack}`))
}
exit(1)
@ -170,7 +170,7 @@ function buildInquirerChoices(current, until) {
async function run({ token, sh: { currentTeam, user } }) {
const args = argv._
if (args.length > 1) {
error('Invalid number of arguments')
console.error(error('Invalid number of arguments'))
return exit(1)
}
@ -180,7 +180,7 @@ async function run({ token, sh: { currentTeam, user } }) {
let planId = args[0]
if (![undefined, 'oss', 'premium', 'pro', 'advanced'].includes(planId)) {
error(`Invalid plan name – should be ${code('oss')} or ${code('premium')}`)
console.error(error(`Invalid plan name – should be ${code('oss')} or ${code('premium')}`))
return exit(1)
}
@ -219,13 +219,13 @@ async function run({ token, sh: { currentTeam, user } }) {
newPlan = await plans.set(planId)
} catch (err) {
if (err.code === 'customer_not_found' || err.code === 'source_not_found') {
error(
console.error(error(
`You have no payment methods available. Run ${cmd(
'now billing add'
)} to add one`
)
))
} else {
error(`An unknow error occured. Please try again later ${err.message}`)
console.error(error(`An unknow error occured. Please try again later ${err.message}`))
}
plans.close()
return

4
src/providers/sh/util/alias.js

@ -653,9 +653,9 @@ module.exports = class Alias extends Now {
if (!domainInfo.verified) {
const tld = param(`.${parsed.tld}`)
error(
console.error(error(
'The nameservers are pending propagation. Please try again shortly'
)
))
info(
`The ${tld} servers might take some extra time to reflect changes`
)

18
src/providers/sh/util/error.js

@ -17,30 +17,30 @@ function handleError(err, { debug = false } = {}) {
}
if (err.status === 403) {
error(
console.error(error(
'Authentication error. Run `now -L` or `now --login` to log-in again.'
)
))
} else if (err.status === 429) {
if (err.retryAfter === 'never') {
error(err.message)
console.error(error(err.message))
} else if (err.retryAfter === null) {
error('Rate limit exceeded error. Please try later.')
console.error(error('Rate limit exceeded error. Please try later.'))
} else {
error(
console.error(error(
'Rate limit exceeded error. Try again in ' +
ms(err.retryAfter * 1000, { long: true }) +
', or upgrade your account by running ' +
`${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}`
)
))
}
} else if (err.userError) {
error(err.message)
console.error(error(err.message))
} else if (err.status === 500) {
error('Unexpected server error. Please retry.')
console.error(error('Unexpected server error. Please retry.'))
} else if (err.code === 'USER_ABORT') {
info('Aborted')
} else {
error(`Unexpected error. Please try again later. (${err.message})`)
console.error(error(`Unexpected error. Please try again later. (${err.message})`))
}
}

8
src/providers/sh/util/re-alias.js

@ -56,9 +56,9 @@ exports.reAlias = async (
}
if (!fs.existsSync(configFiles.pkg) && !fs.existsSync(configFiles.nowJSON)) {
error(
console.error(error(
`Couldn't find a now.json or package.json file with an alias list in it`
)
))
return
}
@ -90,9 +90,9 @@ exports.reAlias = async (
} else if (Array.isArray(value)) {
pointers = pointers.concat(nowConfig.alias)
} else {
error(
console.error(error(
`Property ${chalk.grey('aliases')} is not a valid array or string`
)
))
return exit(1)
}
}

35
src/util/input/list.js

@ -16,7 +16,7 @@ function getLength(string) {
return biggestLength
}
module.exports = async function promptList({
module.exports = async function({
message = 'the question',
// eslint-disable-line no-unused-vars
choices = [
@ -28,9 +28,7 @@ module.exports = async function promptList({
],
pageSize = 15, // Show 15 lines without scrolling (~4 credit cards)
separator = true, // Puts a blank separator between each choice
// Wether the `abort` option will be at the `start` or the `end`
// can be `false`
abort = 'end'
abort = 'end' // Wether the `abort` option will be at the `start` or the `end`
}) {
let biggestLength = 0
@ -52,21 +50,20 @@ module.exports = async function promptList({
)
}
if (abort) {
const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength))
const _abort = {
name: 'Abort',
value: undefined
}
if (abort === 'start') {
const blankSep = choices.shift()
choices.unshift(abortSeparator)
choices.unshift(_abort)
choices.unshift(blankSep)
} else {
choices.push(abortSeparator)
choices.push(_abort)
}
const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength))
const _abort = {
name: 'Abort',
value: undefined
}
if (abort === 'start') {
const blankSep = choices.shift()
choices.unshift(abortSeparator)
choices.unshift(_abort)
choices.unshift(blankSep)
} else {
choices.push(abortSeparator)
choices.push(_abort)
}
const nonce = Date.now()

2
src/util/input/patch-inquirer.js

@ -2,6 +2,8 @@ const inquirer = require('inquirer')
const chalk = require('chalk')
// Here we patch inquirer to use a `>` instead of the ugly green `?`
/* eslint-disable no-multiple-empty-lines, no-var, no-undef, no-eq-null, eqeqeq, semi */
const getQuestion = function() {
var message = chalk.bold('> ' + this.opt.message) + ' '

24
src/util/input/prompt-bool.js

@ -1,34 +1,26 @@
// theirs
const chalk = require('chalk')
// ours
const eraseLines = require('../output/erase-lines')
module.exports = (
label,
{
defaultValue = false,
abortSequences = new Set(['\u0003', '\u001b']), // ctrl+c, esc
resolveChars = new Set(['\r']), // enter
abortSequences = new Set(['\u0003']),
resolveChars = new Set(['\r']),
yesChar = 'y',
noChar = 'n',
stdin = process.stdin,
stdout = process.stdout,
// if `true`, `eraseLines(1)` will be `stdout.write`d before
// `resolve`ing or `reject`ing
clearWhenDone = true
trailing = ''
} = {}
) => {
return new Promise((resolve, reject) => {
return new Promise(resolve => {
const isRaw = stdin.isRaw
stdin.setRawMode(true)
stdin.resume()
function restore() {
if (clearWhenDone) {
stdout.write(eraseLines(1))
}
stdout.write(trailing)
stdin.setRawMode(isRaw)
stdin.pause()
stdin.removeListener('data', onData)
@ -45,9 +37,7 @@ module.exports = (
resolve(false)
} else if (abortSequences.has(data)) {
restore()
const e = new Error('User abort')
e.code = 'USER_ABORT'
reject(e)
resolve(false)
} else if (resolveChars.has(data[0])) {
restore()
resolve(defaultValue)
@ -62,7 +52,7 @@ module.exports = (
: defaultValue
? `[${chalk.bold(yesChar.toUpperCase())}|${noChar}]`
: `[${yesChar}|${chalk.bold(noChar.toUpperCase())}]`
stdout.write(`${label} ${chalk.gray(defaultText)} `)
stdout.write(`${chalk.gray('>')} ${label} ${chalk.gray(defaultText)} `)
stdin.on('data', onData)
})
}

276
src/util/input/text.js

@ -1,102 +1,262 @@
// inspired by https://github.com/zeit/email-prompt
// theirs
// Packages
const ansiEscapes = require('ansi-escapes')
const ansiRegex = require('ansi-regex')
const chalk = require('chalk')
const stripAnsi = require('strip-ansi')
// ours
// Utilities
const eraseLines = require('../output/erase-lines')
const ESCAPES = {
LEFT: '\u001B[D',
RIGHT: '\u001B[C',
CTRL_C: '\x03',
CTRL_C: '\u0003',
BACKSPACE: '\u0008',
CTRL_H: '\u007F',
CARRIAGE: '\r'
}
const textInput = ({
label = 'Enter some text: ',
resolveChars = new Set([ESCAPES.CARRIAGE]),
abortChars = new Set([ESCAPES.CTRL_C]),
// if `true`, `eraseLines(1)` will be `stdout.write`d before
// `resolve`ing or `reject`ing
clearWhenDone = true
}) => {
const formatCC = data => {
return data.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim()
}
module.exports = function(
{
label = '',
initialValue = '',
// If false, the `- label` will be printed as `✖ label` in red
// Until the first keypress
valid = true,
// Can be:
// - `false`, which does nothing
// - `cc`, for credit cards
// - `date`, for dates in the mm / yyyy format
mask = false,
placeholder = '',
abortSequences = new Set(['\x03']),
eraseSequences = new Set([ESCAPES.BACKSPACE, ESCAPES.CTRL_H]),
resolveChars = new Set([ESCAPES.CARRIAGE]),
stdin = process.stdin,
stdout = process.stdout,
// Char to print before resolving/rejecting the promise
// If `false`, nothing will be printed
trailing = ansiEscapes.eraseLines(1),
// Gets called on each keypress;
// `data` contains the current keypress;
// `futureValue` contains the current value + the
// Keypress in the correct place
validateKeypress = (data, futureValue) => true, // eslint-disable-line no-unused-vars
// Get's called before the promise is resolved
// Returning `false` here will prevent the user from submiting the value
validateValue = data => true, // eslint-disable-line no-unused-vars
// Receives the value of the input and should return a string
// Or false if no autocomplion is available
autoComplete = value => false, // eslint-disable-line no-unused-vars
// Tab
// Right arrow
autoCompleteChars = new Set(['\t', '\x1b[C']),
// If true, converts everything the user types to to lowercase
forceLowerCase = false
} = {}
) {
return new Promise((resolve, reject) => {
if (!process.stdin.setRawMode) {
// Some environments (e.g., cygwin) don't provide a tty
const e = new Error('stdin lacks setRawMode support')
e.userError = true
reject(e)
const isRaw = process.stdin.isRaw
let value
let caretOffset = 0
let regex
let suggestion = ''
if (valid) {
stdout.write(label)
} else {
const _label = label.replace('-', '✖')
stdout.write(chalk.red(_label))
}
const isRaw = process.stdin.isRaw
value = initialValue
stdout.write(initialValue)
process.stdin.setRawMode(true)
process.stdin.resume()
process.stdout.write(label)
if (mask) {
if (!value) {
value = chalk.gray(placeholder)
caretOffset = 0 - stripAnsi(value).length
stdout.write(value)
stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset)))
}
let input = '' // Whatever the user types
let caretOffset = 0 // Left/right keys
regex = placeholder
.split('')
.reduce((prev, curr) => {
if (curr !== ' ' && !prev.includes(curr)) {
if (curr === '/') {
prev.push(' / ')
} else {
prev.push(curr)
}
}
return prev
}, [])
.join('|')
regex = new RegExp(`(${regex})`, 'g')
}
stdin.setRawMode(true)
stdin.resume()
const onData = buffer => {
function restore() {
stdin.setRawMode(isRaw)
stdin.pause()
stdin.removeListener('data', onData)
if (trailing) {
stdout.write(trailing)
}
}
async function onData(buffer) {
let data = buffer.toString()
if (abortChars.has(data)) {
const e = new Error('User abort')
e.code = 'USER_ABORT'
value = stripAnsi(value)
if (abortSequences.has(data)) {
restore()
return reject(e)
return reject(new Error('USER_ABORT'))
}
if (data === ESCAPES.LEFT) {
if (input.length > Math.abs(caretOffset)) {
if (forceLowerCase) {
data = data.toLowerCase()
}
if (suggestion !== '' && !caretOffset && autoCompleteChars.has(data)) {
value += stripAnsi(suggestion)
suggestion = ''
} else if (data === ESCAPES.LEFT) {
if (value.length > Math.abs(caretOffset)) {
caretOffset--
}
} else if (data === ESCAPES.RIGHT) {
if (caretOffset < 0) {
caretOffset++
}
} else if (data === '\x08' || data === '\x7f') {
// Delete key needs splicing according to caret position
input =
input.substr(0, input.length + caretOffset - 1) +
input.substr(input.length + caretOffset)
} else {
if (resolveChars.has(data)) {
} else if (eraseSequences.has(data)) {
let char
if (mask && value.length > Math.abs(caretOffset)) {
if (value[value.length + caretOffset - 1] === ' ') {
if (value[value.length + caretOffset - 2] === '/') {
caretOffset -= 1
}
char = placeholder[value.length + caretOffset]
value =
value.substr(0, value.length + caretOffset - 2) +
char +
value.substr(value.length + caretOffset - 1)
caretOffset--
} else {
char = placeholder[value.length + caretOffset - 1]
value =
value.substr(0, value.length + caretOffset - 1) +
char +
value.substr(value.length + caretOffset)
}
caretOffset--
} else {
value =
value.substr(0, value.length + caretOffset - 1) +
value.substr(value.length + caretOffset)
}
suggestion = ''
} else if (resolveChars.has(data)) {
if (validateValue(value)) {
restore()
resolve(input)
resolve(value)
} else {
if (mask === 'cc' || mask === 'ccv') {
value = formatCC(value)
value = value.replace(regex, chalk.gray('$1'))
} else if (mask === 'expDate') {
value = value.replace(regex, chalk.gray('$1'))
}
const l = chalk.red(label.replace('-', '✖'))
eraseLines(1)
stdout.write(l + value + ansiEscapes.beep)
if (caretOffset) {
process.stdout.write(
ansiEscapes.cursorBackward(Math.abs(caretOffset))
)
}
}
return
} else if (!ansiRegex().test(data)) {
let tmp =
value.substr(0, value.length + caretOffset) +
data +
value.substr(value.length + caretOffset)
if (mask) {
if (/\d/.test(data) && caretOffset !== 0) {
let formattedData = data
if (mask === 'cc' || mask === 'ccv') {
formattedData = formatCC(data)
}
if (value[value.length + caretOffset + 1] === ' ') {
tmp =
value.substr(0, value.length + caretOffset) +
formattedData +
value.substr(value.length + caretOffset + formattedData.length)
if (stripAnsi(data).length !== data.length) {
data = ''
caretOffset += formattedData.length + 1
if (value[value.length + caretOffset] === '/') {
caretOffset += formattedData.length + 1
}
} else {
tmp =
value.substr(0, value.length + caretOffset) +
formattedData +
value.substr(value.length + caretOffset + formattedData.length)
caretOffset += formattedData.length
}
} else if (/\s/.test(data) && caretOffset < 0) {
caretOffset++
tmp = value
} else {
return stdout.write(ansiEscapes.beep)
}
value = tmp
} else if (validateKeypress(data, value)) {
value = tmp
if (caretOffset === 0) {
const completion = await autoComplete(value)
if (completion) {
suggestion = chalk.gray(completion)
suggestion += ansiEscapes.cursorBackward(completion.length)
} else {
suggestion = ''
}
}
} else {
return stdout.write(ansiEscapes.beep)
}
}
input =
input.substr(0, input.length + caretOffset) +
stripAnsi(data) +
input.substr(input.length + caretOffset)
if (mask === 'cc' || mask === 'ccv') {
value = formatCC(value)
value = value.replace(regex, chalk.gray('$1'))
} else if (mask === 'expDate') {
value = value.replace(regex, chalk.gray('$1'))
}
process.stdout.write(eraseLines(1) + label + input)
eraseLines(1)
stdout.write(label + value + suggestion)
if (caretOffset) {
process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset)))
}
}
const restore = () => {
if (clearWhenDone) {
process.stdout.write(eraseLines(1))
}
process.stdin.setRawMode(isRaw)
process.stdin.pause()
process.stdin.removeListener('data', onData)
}
process.stdin.on('data', onData)
stdin.on('data', onData)
})
}
module.exports = textInput

Loading…
Cancel
Save