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 { try {
await mkdirp(NOW_DIR) await mkdirp(NOW_DIR)
} catch (err) { } catch (err) {
error( console.error(error(
'An unexpected error occurred while trying to create the ' + 'An unexpected error occurred while trying to create the ' +
`now global directory "${hp(NOW_DIR)}" ` + `now global directory "${hp(NOW_DIR)}" ` +
err.message err.message
) ))
} }
} }

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

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

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

@ -95,9 +95,9 @@ const main = async ctx => {
await run({ token, sh }) await run({ token, sh })
} catch (err) { } catch (err) {
if (err.userError) { if (err.userError) {
error(err.message) console.error(error(err.message))
} else { } else {
error(`Unknown error: ${err.stack}`) console.error(error(`Unknown error: ${err.stack}`))
} }
exit(1) exit(1)
@ -146,7 +146,7 @@ async function run({ token, sh: { currentTeam, user } }) {
try { try {
cards = await creditCards.ls() cards = await creditCards.ls()
} catch (err) { } catch (err) {
error(err.message) console.error(error(err.message))
return return
} }
const text = cards.cards const text = cards.cards
@ -201,7 +201,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'set-default': { case 'set-default': {
if (args.length > 1) { if (args.length > 1) {
error('Invalid number of arguments') console.error(error('Invalid number of arguments'))
return exit(1) return exit(1)
} }
@ -211,12 +211,12 @@ async function run({ token, sh: { currentTeam, user } }) {
try { try {
cards = await creditCards.ls() cards = await creditCards.ls()
} catch (err) { } catch (err) {
error(err.message) console.error(error(err.message))
return return
} }
if (cards.cards.length === 0) { 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) return exit(0)
} }
@ -268,7 +268,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'rm': case 'rm':
case 'remove': { case 'remove': {
if (args.length > 1) { if (args.length > 1) {
error('Invalid number of arguments') console.error(error('Invalid number of arguments'))
return exit(1) return exit(1)
} }
@ -277,16 +277,16 @@ async function run({ token, sh: { currentTeam, user } }) {
try { try {
cards = await creditCards.ls() cards = await creditCards.ls()
} catch (err) { } catch (err) {
error(err.message) console.error(error(err.message))
return return
} }
if (cards.cards.length === 0) { if (cards.cards.length === 0) {
error( console.error(error(
`You have no credit cards to choose from to delete under ${chalk.bold( `You have no credit cards to choose from to delete under ${chalk.bold(
(currentTeam && currentTeam.slug) || user.username || user.email (currentTeam && currentTeam.slug) || user.username || user.email
)}` )}`
) ))
return exit(0) return exit(0)
} }
@ -365,7 +365,7 @@ async function run({ token, sh: { currentTeam, user } }) {
} }
default: 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() help()
exit(1) 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 wait = require('../../../../util/output/wait')
const { tick } = require('../../../../util/output/chars') const { tick } = require('../../../../util/output/chars')
const rightPad = require('../../../../util/output/right-pad') const rightPad = require('../../../../util/output/right-pad')
const error = require('../../../../util/output/error')
function expDateMiddleware(data) { const expDateMiddleware = data => data
return data
}
module.exports = async function({ module.exports = async function({
creditCards, creditCards,
@ -205,7 +204,7 @@ module.exports = async function({
if (err.message === 'USER_ABORT') { if (err.message === 'USER_ABORT') {
process.exit(1) process.exit(1)
} else { } 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 (subcommand === 'ls' || subcommand === 'list') {
if (args.length !== 0) { if (args.length !== 0) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}` `Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`
) ))
return exit(1) return exit(1)
} }
@ -186,11 +186,11 @@ async function run({ token, sh: { currentTeam, user } }) {
} }
} else if (subcommand === 'create') { } else if (subcommand === 'create') {
if (args.length !== 1) { if (args.length !== 1) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs create <cn>`' '`now certs create <cn>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
const cn = args[0] const cn = args[0]
@ -199,11 +199,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (argv.crt || argv.key || argv.ca) { if (argv.crt || argv.key || argv.ca) {
// Issue a custom certificate // Issue a custom certificate
if (!argv.crt || !argv.key) { if (!argv.crt || !argv.key) {
error( console.error(error(
`Missing required arguments for a custom certificate entry. Usage: ${chalk.cyan( `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>`' '`now certs create --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
@ -228,11 +228,11 @@ async function run({ token, sh: { currentTeam, user } }) {
) )
} else if (subcommand === 'renew') { } else if (subcommand === 'renew') {
if (args.length !== 1) { if (args.length !== 1) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs renew <id | cn>`' '`now certs renew <id | cn>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
@ -246,7 +246,7 @@ async function run({ token, sh: { currentTeam, user } }) {
) )
if (!yes) { if (!yes) {
error('User abort') console.error(error('User abort'))
return exit(0) return exit(0)
} }
@ -259,11 +259,11 @@ async function run({ token, sh: { currentTeam, user } }) {
) )
} else if (subcommand === 'replace') { } else if (subcommand === 'replace') {
if (!argv.crt || !argv.key) { if (!argv.crt || !argv.key) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`' '`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
@ -280,7 +280,7 @@ async function run({ token, sh: { currentTeam, user } }) {
'The following certificate will be replaced permanently\n' 'The following certificate will be replaced permanently\n'
) )
if (!yes) { if (!yes) {
error('User abort') console.error(error('User abort'))
return exit(0) return exit(0)
} }
@ -293,11 +293,11 @@ async function run({ token, sh: { currentTeam, user } }) {
) )
} else if (subcommand === 'rm' || subcommand === 'remove') { } else if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) { if (args.length !== 1) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now certs rm <id | cn>`' '`now certs rm <id | cn>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
@ -310,7 +310,7 @@ async function run({ token, sh: { currentTeam, user } }) {
'The following certificate will be removed permanently\n' 'The following certificate will be removed permanently\n'
) )
if (!yes) { if (!yes) {
error('User abort') console.error(error('User abort'))
return exit(0) return exit(0)
} }
@ -322,9 +322,9 @@ async function run({ token, sh: { currentTeam, user } }) {
)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}` )} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`
) )
} else { } else {
error( console.error(error(
'Please specify a valid subcommand: ls | create | renew | replace | rm' 'Please specify a valid subcommand: ls | create | renew | replace | rm'
) ))
help() help()
exit(1) exit(1)
} }
@ -371,11 +371,11 @@ async function getCertIdCn(certs, idOrCn, currentTeam, user) {
})[0] })[0]
if (!thecert) { if (!thecert) {
error( console.error(error(
`No certificate found by id or cn "${idOrCn}" under ${chalk.bold( `No certificate found by id or cn "${idOrCn}" under ${chalk.bold(
(currentTeam && currentTeam.slug) || user.username || user.email (currentTeam && currentTeam.slug) || user.username || user.email
)}` )}`
) ))
return null return null
} }

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

@ -341,7 +341,7 @@ async function sync({ token, config: { currentTeam, user } }) {
)}" ${gitRef}on ${gitRepo.type}` )}" ${gitRef}on ${gitRepo.type}`
) )
} else { } else {
error(`The specified directory "${basename(path)}" doesn't exist.`) console.error(error(`The specified directory "${basename(path)}" doesn't exist.`))
process.exit(1) process.exit(1)
} }
} }
@ -350,8 +350,8 @@ async function sync({ token, config: { currentTeam, user } }) {
try { try {
await checkPath(path) await checkPath(path)
} catch (err) { } catch (err) {
error(err) console.error(error(err.message))
return process.exit(1)
} }
if (!quiet) { if (!quiet) {
@ -421,7 +421,7 @@ async function sync({ token, config: { currentTeam, user } }) {
typeof dotenvOption === 'string' ? dotenvOption : '.env' typeof dotenvOption === 'string' ? dotenvOption : '.env'
if (!fs.existsSync(dotenvFileName)) { 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) return process.exit(1)
} }
@ -460,7 +460,7 @@ async function sync({ token, config: { currentTeam, user } }) {
const env_ = await Promise.all( const env_ = await Promise.all(
envs.map(async kv => { envs.map(async kv => {
if (typeof kv !== 'string') { if (typeof kv !== 'string') {
error('Env key and value missing') console.error(error('Env key and value missing'))
return process.exit(1) return process.exit(1)
} }
@ -472,16 +472,16 @@ async function sync({ token, config: { currentTeam, user } }) {
} }
if (/[^A-z0-9_]/i.test(key)) { if (/[^A-z0-9_]/i.test(key)) {
error( console.error(error(
`Invalid ${chalk.dim('-e')} key ${chalk.bold( `Invalid ${chalk.dim('-e')} key ${chalk.bold(
`"${chalk.bold(key)}"` `"${chalk.bold(key)}"`
)}. Only letters, digits and underscores are allowed.` )}. Only letters, digits and underscores are allowed.`
) ))
return process.exit(1) return process.exit(1)
} }
if (!key) { if (!key) {
error(`Invalid env option ${chalk.bold(`"${kv}"`)}`) console.error(error(`Invalid env option ${chalk.bold(`"${kv}"`)}`))
return process.exit(1) return process.exit(1)
} }
@ -495,11 +495,11 @@ async function sync({ token, config: { currentTeam, user } }) {
// Escape value if it begins with @ // Escape value if it begins with @
val = process.env[key].replace(/^@/, '\\@') val = process.env[key].replace(/^@/, '\\@')
} else { } else {
error( console.error(error(
`No value specified for env ${chalk.bold( `No value specified for env ${chalk.bold(
`"${chalk.bold(key)}"` `"${chalk.bold(key)}"`
)} and it was not found in your env.` )} and it was not found in your env.`
) ))
return process.exit(1) return process.exit(1)
} }
} }
@ -509,23 +509,23 @@ async function sync({ token, config: { currentTeam, user } }) {
const _secrets = await findSecret(uidOrName) const _secrets = await findSecret(uidOrName)
if (_secrets.length === 0) { if (_secrets.length === 0) {
if (uidOrName === '') { if (uidOrName === '') {
error( console.error(error(
`Empty reference provided for env key ${chalk.bold( `Empty reference provided for env key ${chalk.bold(
`"${chalk.bold(key)}"` `"${chalk.bold(key)}"`
)}` )}`
) ))
} else { } else {
error( console.error(error(
`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}` `No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`
) ))
} }
return process.exit(1) return process.exit(1)
} else if (_secrets.length > 1) { } else if (_secrets.length > 1) {
error( console.error(error(
`Ambiguous secret ${chalk.bold( `Ambiguous secret ${chalk.bold(
`"${uidOrName}"` `"${uidOrName}"`
)} (matches ${chalk.bold(_secrets.length)} secrets)` )} (matches ${chalk.bold(_secrets.length)} secrets)`
) ))
return process.exit(1) return process.exit(1)
} }
@ -700,7 +700,7 @@ async function sync({ token, config: { currentTeam, user } }) {
now.on('complete', () => complete({ syncCount })) now.on('complete', () => complete({ syncCount }))
now.on('error', err => { now.on('error', err => {
error('Upload failed') console.error(error('Upload failed'))
return stopDeployment(err) return stopDeployment(err)
}) })
} else { } else {
@ -799,13 +799,13 @@ function printLogs(host, token) {
logger.on('error', async err => { logger.on('error', async err => {
if (!quiet) { if (!quiet) {
if (err && err.type === 'BUILD_ERROR') { if (err && err.type === 'BUILD_ERROR') {
error( console.error(error(
`The build step of your project failed. To retry, run ${cmd( `The build step of your project failed. To retry, run ${cmd(
'now --force' 'now --force'
)}.` )}.`
) ))
} else { } 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 (subcommand === 'ls' || subcommand === 'list') {
if (args.length > 1) { if (args.length > 1) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now dns ls [domain]`' '`now dns ls [domain]`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
@ -175,11 +175,11 @@ async function run({ token, sh: { currentTeam, user } }) {
} else if (subcommand === 'add') { } else if (subcommand === 'add') {
const param = parseAddArgs(args) const param = parseAddArgs(args)
if (!param) { if (!param) {
error( console.error(error(
`Invalid number of arguments. See: ${chalk.cyan( `Invalid number of arguments. See: ${chalk.cyan(
'`now dns --help`' '`now dns --help`'
)} for usage.` )} for usage.`
) ))
return exit(1) return exit(1)
} }
const record = await domainRecords.create(param.domain, param.data) 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') { } else if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) { if (args.length !== 1) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now dns rm <id>`')}` `Invalid number of arguments. Usage: ${chalk.cyan('`now dns rm <id>`')}`
) ))
return exit(1) return exit(1)
} }
const record = await domainRecords.getRecord(args[0]) const record = await domainRecords.getRecord(args[0])
if (!record) { if (!record) {
error('DNS record not found') console.error(error('DNS record not found'))
return exit(1) return exit(1)
} }
@ -212,7 +212,7 @@ async function run({ token, sh: { currentTeam, user } }) {
'The following record will be removed permanently \n' 'The following record will be removed permanently \n'
) )
if (!yes) { if (!yes) {
error('User abort') console.error(error('User abort'))
return exit(0) return exit(0)
} }
@ -224,7 +224,7 @@ async function run({ token, sh: { currentTeam, user } }) {
)} removed ${chalk.gray(`[${elapsed}]`)}` )} removed ${chalk.gray(`[${elapsed}]`)}`
) )
} else { } else {
error('Please specify a valid subcommand: ls | add | rm') console.error(error('Please specify a valid subcommand: ls | add | rm'))
help() help()
exit(1) exit(1)
} }

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

@ -152,9 +152,9 @@ const main = async ctx => {
await run({ token, sh }) await run({ token, sh })
} catch (err) { } catch (err) {
if (err.userError) { if (err.userError) {
error(err.message) console.error(error(err.message))
} else { } else {
error(`Unknown error: ${err}\n${err.stack}`) console.error(error(`Unknown error: ${err}\n${err.stack}`))
} }
exit(1) exit(1)
@ -178,7 +178,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'ls': case 'ls':
case 'list': { case 'list': {
if (args.length !== 0) { if (args.length !== 0) {
error('Invalid number of arguments') console.error(error('Invalid number of arguments'))
return exit(1) return exit(1)
} }
@ -228,7 +228,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'rm': case 'rm':
case 'remove': { case 'remove': {
if (args.length !== 1) { if (args.length !== 1) {
error('Invalid number of arguments') console.error(error('Invalid number of arguments'))
return exit(1) return exit(1)
} }
@ -271,7 +271,7 @@ async function run({ token, sh: { currentTeam, user } }) {
)} removed [${elapsed}]` )} removed [${elapsed}]`
) )
} catch (err) { } catch (err) {
error(err) console.error(error(err))
exit(1) exit(1)
} }
break break
@ -279,7 +279,7 @@ async function run({ token, sh: { currentTeam, user } }) {
case 'add': case 'add':
case 'set': { case 'set': {
if (args.length !== 1) { if (args.length !== 1) {
error('Invalid number of arguments') console.error(error('Invalid number of arguments'))
return exit(1) return exit(1)
} }
const name = String(args[0]) const name = String(args[0])
@ -343,7 +343,7 @@ async function run({ token, sh: { currentTeam, user } }) {
break break
} }
default: default:
error('Please specify a valid subcommand: ls | add | rm') console.error(error('Please specify a valid subcommand: ls | add | rm'))
help() help()
exit(1) 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 let elapsed
if (!name) { 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) const nameParam = param(name)
@ -45,11 +45,11 @@ module.exports = async function({ domains, args, currentTeam, user, coupon }) {
stopSpinner() stopSpinner()
if (!couponInfo.isValid) { if (!couponInfo.isValid) {
return error(`The coupon ${param(coupon)} is invalid`) return console.error(error(`The coupon ${param(coupon)} is invalid`))
} }
if (!couponInfo.canBeUsed) { 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 validCoupon = true
@ -75,18 +75,19 @@ module.exports = async function({ domains, args, currentTeam, user, coupon }) {
period = json.period period = json.period
} catch (err) { } catch (err) {
stopSpinner() stopSpinner()
return error(err.message) return console.error(error(err.message))
} }
const available = await domains.status(name) const available = await domains.status(name)
stopSpinner() stopSpinner()
if (!available) { if (!available) {
return error( console.error(error(
`The domain ${nameParam} is ${italic('unavailable')}! ${elapsed()}` `The domain ${nameParam} is ${italic('unavailable')}! ${elapsed()}`
) ))
return
} }
const periodMsg = `${period}yr${period > 1 ? 's' : ''}` const periodMsg = `${period}yr${period > 1 ? 's' : ''}`
info( info(
`The domain ${nameParam} is ${italic('available')} to buy under ${bold( `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 { try {
await list({ token, sh }) await list({ token, sh })
} catch (err) { } catch (err) {
error(`Unknown error: ${err}\n${err.stack}`) console.error(error(`Unknown error: ${err}\n${err.stack}`))
process.exit(1) process.exit(1)
} }
} }

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

@ -15,6 +15,7 @@ const {
readAuthConfigFile, readAuthConfigFile,
writeToAuthConfigFile writeToAuthConfigFile
} = require('../../../util/config-files') } = require('../../../util/config-files')
const error = require('../../../util/output/error')
const help = () => { const help = () => {
console.log(` console.log(`
@ -101,7 +102,7 @@ const revokeToken = async (token, tokenId) => {
const result = await fetch(endpoint + encodeURIComponent(tokenId), details) const result = await fetch(endpoint + encodeURIComponent(tokenId), details)
if (!result.ok) { 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 { try {
await remove({ token, sh }) await remove({ token, sh })
} catch (err) { } catch (err) {
error(`Unknown error: ${err}\n${err.stack}`) console.error(error(`Unknown error: ${err}\n${err.stack}`))
process.exit(1) process.exit(1)
} }
} }
@ -171,13 +171,13 @@ async function remove({ token, sh: { currentTeam } }) {
}) })
if (matches.length === 0) { if (matches.length === 0) {
error( console.error(error(
`Could not find ${argv.safe `Could not find ${argv.safe
? 'unaliased' ? 'unaliased'
: 'any'} deployments matching ${ids : 'any'} deployments matching ${ids
.map(id => chalk.bold(`"${id}"`)) .map(id => chalk.bold(`"${id}"`))
.join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.` .join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.`
) ))
return process.exit(1) return process.exit(1)
} }

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

@ -100,9 +100,9 @@ const main = async ctx => {
await run({ token, sh }) await run({ token, sh })
} catch (err) { } catch (err) {
if (err.userError) { if (err.userError) {
error(err.message) console.error(error(err.message))
} else { } else {
error(`Unknown error: ${err}\n${err.stack}`) console.error(error(`Unknown error: ${err}\n${err.stack}`))
} }
exit(1) exit(1)
@ -162,7 +162,7 @@ async function run({ token, sh: { currentTeam } }) {
} }
} }
} else { } else {
error('Please specify a deployment: now scale <id|url>') console.error(error('Please specify a deployment: now scale <id|url>'))
help() help()
exit(1) exit(1)
} }
@ -186,7 +186,7 @@ async function run({ token, sh: { currentTeam } }) {
e => e.alias === id e => e.alias === id
) )
if (!aliasDeployment) { if (!aliasDeployment) {
error(`Could not find any deployments matching ${id}`) console.error(error(`Could not find any deployments matching ${id}`))
return process.exit(1) return process.exit(1)
} }
match = deployments.find(d => { match = deployments.find(d => {
@ -206,7 +206,7 @@ async function run({ token, sh: { currentTeam } }) {
if (match.type === 'STATIC') { if (match.type === 'STATIC') {
if (min === 0 && max === 0) { 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) return process.exit(1)
} }
console.log('> Static deployments are automatically scaled!') 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 (subcommand === 'ls' || subcommand === 'list') {
if (args.length !== 0) { if (args.length !== 0) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}` `Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`
) ))
return exit(1) return exit(1)
} }
@ -165,11 +165,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'rm' || subcommand === 'remove') { if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) { if (args.length !== 1) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now secret rm <name>`' '`now secret rm <name>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
const list = await secrets.ls() const list = await secrets.ls()
@ -178,11 +178,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (theSecret) { if (theSecret) {
const yes = await readConfirmation(theSecret) const yes = await readConfirmation(theSecret)
if (!yes) { if (!yes) {
error('User abort') console.error(error('User abort'))
return exit(0) return exit(0)
} }
} else { } else {
error(`No secret found by name "${args[0]}"`) console.error(error(`No secret found by name "${args[0]}"`))
return exit(1) return exit(1)
} }
@ -198,11 +198,11 @@ async function run({ token, sh: { currentTeam, user } }) {
if (subcommand === 'rename') { if (subcommand === 'rename') {
if (args.length !== 2) { if (args.length !== 2) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now secret rename <old-name> <new-name>`' '`now secret rename <old-name> <new-name>`'
)}` )}`
) ))
return exit(1) return exit(1)
} }
const secret = await secrets.rename(args[0], args[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 (subcommand === 'add' || subcommand === 'set') {
if (args.length !== 2) { if (args.length !== 2) {
error( console.error(error(
`Invalid number of arguments. Usage: ${chalk.cyan( `Invalid number of arguments. Usage: ${chalk.cyan(
'`now secret add <name> <value>`' '`now secret add <name> <value>`'
)}` )}`
) ))
if (args.length > 2) { if (args.length > 2) {
const example = chalk.cyan(`$ now secret add ${args[0]}`) const example = chalk.cyan(`$ now secret add ${args[0]}`)
@ -255,7 +255,7 @@ async function run({ token, sh: { currentTeam, user } }) {
return secrets.close() 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() help()
exit(1) exit(1)
} }

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

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

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

@ -75,7 +75,7 @@ module.exports = async function({ teams, config }) {
} catch (err) { } catch (err) {
stopSpinner() stopSpinner()
eraseLines(2) eraseLines(2)
error(err.message) console.error(error(err.message))
} }
} while (!team) } while (!team)
@ -105,7 +105,7 @@ module.exports = async function({ teams, config }) {
eraseLines(2) eraseLines(2)
if (res.error) { if (res.error) {
error(res.error.message) console.error(error(res.error.message))
console.log(`${chalk.red(`${teamNamePrefix}`)}${name}`) console.log(`${chalk.red(`${teamNamePrefix}`)}${name}`)
exit(1) exit(1)
// TODO: maybe we want to ask the user to retry? not sure if // 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) { } catch (err) {
stopSpinner() stopSpinner()
eraseLines(emails.length + 2) eraseLines(emails.length + 2)
error(err.message) console.error(error(err.message))
hasError = true hasError = true
for (const email of emails) { for (const email of emails) {
console.log(`${chalk.cyan(tick)} ${inviteUserPrefix}${email}`) 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 const count = teamList.length
if (!count) { if (!count) {
// Maybe should not happen // Maybe should not happen
error(`No team found`) console.error(error(`No team found`))
return 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!`) 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) return exit(1)
} }

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

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

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

@ -653,9 +653,9 @@ module.exports = class Alias extends Now {
if (!domainInfo.verified) { if (!domainInfo.verified) {
const tld = param(`.${parsed.tld}`) const tld = param(`.${parsed.tld}`)
error( console.error(error(
'The nameservers are pending propagation. Please try again shortly' 'The nameservers are pending propagation. Please try again shortly'
) ))
info( info(
`The ${tld} servers might take some extra time to reflect changes` `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) { if (err.status === 403) {
error( console.error(error(
'Authentication error. Run `now -L` or `now --login` to log-in again.' 'Authentication error. Run `now -L` or `now --login` to log-in again.'
) ))
} else if (err.status === 429) { } else if (err.status === 429) {
if (err.retryAfter === 'never') { if (err.retryAfter === 'never') {
error(err.message) console.error(error(err.message))
} else if (err.retryAfter === null) { } else if (err.retryAfter === null) {
error('Rate limit exceeded error. Please try later.') console.error(error('Rate limit exceeded error. Please try later.'))
} else { } else {
error( console.error(error(
'Rate limit exceeded error. Try again in ' + 'Rate limit exceeded error. Try again in ' +
ms(err.retryAfter * 1000, { long: true }) + ms(err.retryAfter * 1000, { long: true }) +
', or upgrade your account by running ' + ', or upgrade your account by running ' +
`${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}` `${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}`
) ))
} }
} else if (err.userError) { } else if (err.userError) {
error(err.message) console.error(error(err.message))
} else if (err.status === 500) { } 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') { } else if (err.code === 'USER_ABORT') {
info('Aborted') info('Aborted')
} else { } 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)) { 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` `Couldn't find a now.json or package.json file with an alias list in it`
) ))
return return
} }
@ -90,9 +90,9 @@ exports.reAlias = async (
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
pointers = pointers.concat(nowConfig.alias) pointers = pointers.concat(nowConfig.alias)
} else { } else {
error( console.error(error(
`Property ${chalk.grey('aliases')} is not a valid array or string` `Property ${chalk.grey('aliases')} is not a valid array or string`
) ))
return exit(1) return exit(1)
} }
} }

35
src/util/input/list.js

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

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

@ -2,6 +2,8 @@ const inquirer = require('inquirer')
const chalk = require('chalk') const chalk = require('chalk')
// Here we patch inquirer to use a `>` instead of the ugly green `?` // 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() { const getQuestion = function() {
var message = chalk.bold('> ' + this.opt.message) + ' ' var message = chalk.bold('> ' + this.opt.message) + ' '

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

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

276
src/util/input/text.js

@ -1,102 +1,262 @@
// inspired by https://github.com/zeit/email-prompt // Packages
// theirs
const ansiEscapes = require('ansi-escapes') const ansiEscapes = require('ansi-escapes')
const ansiRegex = require('ansi-regex')
const chalk = require('chalk')
const stripAnsi = require('strip-ansi') const stripAnsi = require('strip-ansi')
// ours // Utilities
const eraseLines = require('../output/erase-lines') const eraseLines = require('../output/erase-lines')
const ESCAPES = { const ESCAPES = {
LEFT: '\u001B[D', LEFT: '\u001B[D',
RIGHT: '\u001B[C', RIGHT: '\u001B[C',
CTRL_C: '\x03', CTRL_C: '\u0003',
BACKSPACE: '\u0008', BACKSPACE: '\u0008',
CTRL_H: '\u007F', CTRL_H: '\u007F',
CARRIAGE: '\r' CARRIAGE: '\r'
} }
const textInput = ({ const formatCC = data => {
label = 'Enter some text: ', return data.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim()
resolveChars = new Set([ESCAPES.CARRIAGE]), }
abortChars = new Set([ESCAPES.CTRL_C]),
// if `true`, `eraseLines(1)` will be `stdout.write`d before module.exports = function(
// `resolve`ing or `reject`ing {
clearWhenDone = true 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) => { return new Promise((resolve, reject) => {
if (!process.stdin.setRawMode) { const isRaw = process.stdin.isRaw
// Some environments (e.g., cygwin) don't provide a tty
const e = new Error('stdin lacks setRawMode support') let value
e.userError = true let caretOffset = 0
reject(e) 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) if (mask) {
process.stdin.resume() if (!value) {
process.stdout.write(label) 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 regex = placeholder
let caretOffset = 0 // Left/right keys .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() let data = buffer.toString()
if (abortChars.has(data)) { value = stripAnsi(value)
const e = new Error('User abort')
e.code = 'USER_ABORT' if (abortSequences.has(data)) {
restore() restore()
return reject(e) return reject(new Error('USER_ABORT'))
} }
if (data === ESCAPES.LEFT) { if (forceLowerCase) {
if (input.length > Math.abs(caretOffset)) { 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-- caretOffset--
} }
} else if (data === ESCAPES.RIGHT) { } else if (data === ESCAPES.RIGHT) {
if (caretOffset < 0) { if (caretOffset < 0) {
caretOffset++ caretOffset++
} }
} else if (data === '\x08' || data === '\x7f') { } else if (eraseSequences.has(data)) {
// Delete key needs splicing according to caret position let char
input = if (mask && value.length > Math.abs(caretOffset)) {
input.substr(0, input.length + caretOffset - 1) + if (value[value.length + caretOffset - 1] === ' ') {
input.substr(input.length + caretOffset) if (value[value.length + caretOffset - 2] === '/') {
} else { caretOffset -= 1
if (resolveChars.has(data)) { }
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() 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) { caretOffset += formattedData.length + 1
data = ''
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 = if (mask === 'cc' || mask === 'ccv') {
input.substr(0, input.length + caretOffset) + value = formatCC(value)
stripAnsi(data) + value = value.replace(regex, chalk.gray('$1'))
input.substr(input.length + caretOffset) } 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) { if (caretOffset) {
process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset)))
} }
} }
const restore = () => { stdin.on('data', onData)
if (clearWhenDone) {
process.stdout.write(eraseLines(1))
}
process.stdin.setRawMode(isRaw)
process.stdin.pause()
process.stdin.removeListener('data', onData)
}
process.stdin.on('data', onData)
}) })
} }
module.exports = textInput

Loading…
Cancel
Save