diff --git a/bin/now-alias.js b/bin/now-alias.js index eefa81f..6c744d2 100755 --- a/bin/now-alias.js +++ b/bin/now-alias.js @@ -1,345 +1,397 @@ #!/usr/bin/env node // Packages -const chalk = require('chalk') -const minimist = require('minimist') -const table = require('text-table') -const ms = require('ms') +const chalk = require("chalk"); +const minimist = require("minimist"); +const table = require("text-table"); +const ms = require("ms"); // Ours -const strlen = require('../lib/strlen') -const NowAlias = require('../lib/alias') -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {error} = require('../lib/error') -const toHost = require('../lib/to-host') -const {reAlias} = require('../lib/re-alias') -const exit = require('../lib/utils/exit') -const logo = require('../lib/utils/output/logo') -const promptBool = require('../lib/utils/input/prompt-bool') +const strlen = require("../lib/strlen"); +const NowAlias = require("../lib/alias"); +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { error } = require("../lib/error"); +const toHost = require("../lib/to-host"); +const { reAlias } = require("../lib/re-alias"); +const exit = require("../lib/utils/exit"); +const logo = require("../lib/utils/output/logo"); +const promptBool = require("../lib/utils/input/prompt-bool"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token', 'rules'], - boolean: ['help', 'debug'], + string: ["config", "token", "rules"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - rules: 'r', - debug: 'd', - token: 't' + help: "h", + config: "c", + rules: "r", + debug: "d", + token: "t" } -}) +}); -const subcommand = argv._[0] +const subcommand = argv._[0]; // options const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now alias`)} - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file - -r ${chalk.bold.underline('RULES_FILE')}, --rules=${chalk.bold.underline('RULES_FILE')} Rules file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file + -r ${chalk.bold.underline("RULES_FILE")}, --rules=${chalk.bold.underline("RULES_FILE")} Rules file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Lists all your aliases: + ${chalk.gray("–")} Lists all your aliases: - ${chalk.cyan('$ now alias ls')} + ${chalk.cyan("$ now alias ls")} - ${chalk.gray('–')} Adds a new alias to ${chalk.underline('my-api.now.sh')}: + ${chalk.gray("–")} Adds a new alias to ${chalk.underline("my-api.now.sh")}: - ${chalk.cyan(`$ now alias set ${chalk.underline('api-ownv3nc9f8.now.sh')} ${chalk.underline('my-api.now.sh')}`)} + ${chalk.cyan(`$ now alias set ${chalk.underline("api-ownv3nc9f8.now.sh")} ${chalk.underline("my-api.now.sh")}`)} - The ${chalk.dim('`.now.sh`')} suffix can be ommited: + The ${chalk.dim("`.now.sh`")} suffix can be ommited: - ${chalk.cyan('$ now alias set api-ownv3nc9f8 my-api')} + ${chalk.cyan("$ now alias set api-ownv3nc9f8 my-api")} The deployment id can be used as the source: - ${chalk.cyan('$ now alias set deploymentId my-alias')} + ${chalk.cyan("$ now alias set deploymentId my-alias")} Custom domains work as alias targets: - ${chalk.cyan(`$ now alias set ${chalk.underline('api-ownv3nc9f8.now.sh')} ${chalk.underline('my-api.com')}`)} + ${chalk.cyan(`$ now alias set ${chalk.underline("api-ownv3nc9f8.now.sh")} ${chalk.underline("my-api.com")}`)} - ${chalk.dim('–')} The subcommand ${chalk.dim('`set`')} is the default and can be skipped. - ${chalk.dim('–')} ${chalk.dim('`http(s)://`')} in the URLs is unneeded / ignored. + ${chalk.dim("–")} The subcommand ${chalk.dim("`set`")} is the default and can be skipped. + ${chalk.dim("–")} ${chalk.dim("`http(s)://`")} in the URLs is unneeded / ignored. - ${chalk.gray('–')} Add and modify path based aliases for ${chalk.underline('zeit.ninja')}: + ${chalk.gray("–")} Add and modify path based aliases for ${chalk.underline("zeit.ninja")}: - ${chalk.cyan(`$ now alias ${chalk.underline('zeit.ninja')} -r ${chalk.underline('rules.json')}`)} + ${chalk.cyan(`$ now alias ${chalk.underline("zeit.ninja")} -r ${chalk.underline("rules.json")}`)} Export effective routing rules: - ${chalk.cyan(`$ now alias ls aliasId --json > ${chalk.underline('rules.json')}`)} + ${chalk.cyan(`$ now alias ls aliasId --json > ${chalk.underline("rules.json")}`)} ${chalk.cyan(`$ now alias ls zeit.ninja`)} - ${chalk.gray('–')} Removing an alias: + ${chalk.gray("–")} Removing an alias: - ${chalk.cyan('$ now alias rm aliasId')} + ${chalk.cyan("$ now alias rm aliasId")} - To get the list of alias ids, use ${chalk.dim('`now alias ls`')}. + To get the list of alias ids, use ${chalk.dim("`now alias ls`")}. - ${chalk.dim('Alias:')} ln -`) -} + ${chalk.dim("Alias:")} ln +` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } if (argv.help) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - if (err.userError) { - error(err.message) - } else { - error(`Unknown error: ${err}\n${err.stack}`) + .then(async token => { + try { + await run(token); + } catch (err) { + if (err.userError) { + error(err.message); + } else { + error(`Unknown error: ${err}\n${err.stack}`); + } + exit(1); } - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } async function run(token) { - const alias = new NowAlias(apiUrl, token, {debug}) - const args = argv._.slice(1) + const alias = new NowAlias(apiUrl, token, { debug }); + const args = argv._.slice(1); switch (subcommand) { - case 'ls': - case 'list': { + case "ls": + case "list": { if (args.length === 1) { - const list = await alias.listAliases() - const item = list.find(e => e.uid === argv._[1] || e.alias === argv._[1]) + const list = await alias.listAliases(); + const item = list.find( + e => e.uid === argv._[1] || e.alias === argv._[1] + ); if (!item || !item.rules) { - error(`Could not match path alias for: ${argv._[1]}`) - return exit(1) + error(`Could not match path alias for: ${argv._[1]}`); + return exit(1); } if (argv.json) { - console.log(JSON.stringify({rules: item.rules}, null, 2)) + console.log(JSON.stringify({ rules: item.rules }, null, 2)); } else { - const header = [['', 'pathname', 'method', 'dest'].map(s => chalk.dim(s))] - const text = list.length === 0 ? null : table(header.concat(item.rules.map(rule => { - return [ - '', - rule.pathname ? rule.pathname : '', - rule.method ? rule.method : '*', - rule.dest - ] - })), {align: ['l', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) - - console.log(text) + const header = [ + ["", "pathname", "method", "dest"].map(s => chalk.dim(s)) + ]; + const text = list.length === 0 + ? null + : table( + header.concat( + item.rules.map(rule => { + return [ + "", + rule.pathname ? rule.pathname : "", + rule.method ? rule.method : "*", + rule.dest + ]; + }) + ), + { + align: ["l", "l", "l", "l"], + hsep: " ".repeat(2), + stringLength: strlen + } + ); + + console.log(text); } - break + break; } else if (args.length !== 0) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias ls`')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now alias ls`")}` + ); + return exit(1); } - const start_ = new Date() - const list = await alias.list() - const urls = new Map(list.map(l => [l.uid, l.url])) - const aliases = await alias.ls() - aliases.sort((a, b) => new Date(b.created) - new Date(a.created)) - const current = new Date() - - const header = [['', 'id', 'source', 'url', 'created'].map(s => chalk.dim(s))] - const text = list.length === 0 ? null : table(header.concat(aliases.map(_alias => { - const _url = chalk.underline(`https://${_alias.alias}`) - const target = _alias.deploymentId - let _sourceUrl - if (urls.get(target)) { - _sourceUrl = chalk.underline(`https://${urls.get(target)}`) - } else if (_alias.rules) { - _sourceUrl = chalk.gray(`[${_alias.rules.length} custom rule${_alias.rules.length > 1 ? 's' : ''}]`) - } else { - _sourceUrl = chalk.gray('') - } - - const time = chalk.gray(ms(current - new Date(_alias.created)) + ' ago') - return [ - '', - // we default to `''` because some early aliases didn't - // have an uid associated - _alias.uid === null ? '' : _alias.uid, - _sourceUrl, - _url, - time - ] - })), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) - - const elapsed_ = ms(new Date() - start_) - console.log(`> ${aliases.length} alias${aliases.length === 1 ? '' : 'es'} found ${chalk.gray(`[${elapsed_}]`)}`) + const start_ = new Date(); + const list = await alias.list(); + const urls = new Map(list.map(l => [l.uid, l.url])); + const aliases = await alias.ls(); + aliases.sort((a, b) => new Date(b.created) - new Date(a.created)); + const current = new Date(); + + const header = [ + ["", "id", "source", "url", "created"].map(s => chalk.dim(s)) + ]; + const text = list.length === 0 + ? null + : table( + header.concat( + aliases.map(_alias => { + const _url = chalk.underline(`https://${_alias.alias}`); + const target = _alias.deploymentId; + let _sourceUrl; + if (urls.get(target)) { + _sourceUrl = chalk.underline(`https://${urls.get(target)}`); + } else if (_alias.rules) { + _sourceUrl = chalk.gray( + `[${_alias.rules.length} custom rule${_alias.rules.length > 1 ? "s" : ""}]` + ); + } else { + _sourceUrl = chalk.gray(""); + } + + const time = chalk.gray( + ms(current - new Date(_alias.created)) + " ago" + ); + return [ + "", + // we default to `''` because some early aliases didn't + // have an uid associated + _alias.uid === null ? "" : _alias.uid, + _sourceUrl, + _url, + time + ]; + }) + ), + { + align: ["l", "r", "l", "l"], + hsep: " ".repeat(2), + stringLength: strlen + } + ); + + const elapsed_ = ms(new Date() - start_); + console.log( + `> ${aliases.length} alias${aliases.length === 1 ? "" : "es"} found ${chalk.gray(`[${elapsed_}]`)}` + ); if (text) { - console.log('\n' + text + '\n') + console.log("\n" + text + "\n"); } - break + break; } - case 'rm': - case 'remove': { - const _target = String(args[0]) + case "rm": + case "remove": { + const _target = String(args[0]); if (!_target) { - const err = new Error('No alias id specified') - err.userError = true - throw err + const err = new Error("No alias id specified"); + err.userError = true; + throw err; } if (args.length !== 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias rm `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now alias rm `")}` + ); + return exit(1); } - const _aliases = await alias.ls() - const _alias = findAlias(_target, _aliases) + const _aliases = await alias.ls(); + const _alias = findAlias(_target, _aliases); if (!_alias) { - const err = new Error(`Alias not found by "${_target}". Run ${chalk.dim('`now alias ls`')} to see your aliases.`) - err.userError = true - throw err + const err = new Error( + `Alias not found by "${_target}". Run ${chalk.dim("`now alias ls`")} to see your aliases.` + ); + err.userError = true; + throw err; } try { - const confirmation = await confirmDeploymentRemoval(alias, _alias) + const confirmation = await confirmDeploymentRemoval(alias, _alias); if (!confirmation) { - console.log('\n> Aborted') - process.exit(0) + console.log("\n> Aborted"); + process.exit(0); } - const start = new Date() - await alias.rm(_alias) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Alias ${chalk.bold(_alias.uid)} removed [${elapsed}]`) + const start = new Date(); + await alias.rm(_alias); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Alias ${chalk.bold(_alias.uid)} removed [${elapsed}]` + ); } catch (err) { - error(err) - exit(1) + error(err); + exit(1); } - break + break; } - case 'add': - case 'set': { + case "add": + case "set": { if (argv.rules) { - await updatePathAlias(alias, argv._[0], argv.rules) - break + await updatePathAlias(alias, argv._[0], argv.rules); + break; } if (args.length !== 2) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias set `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now alias set `")}` + ); + return exit(1); } - await alias.set(String(args[0]), String(args[1])) - break + await alias.set(String(args[0]), String(args[1])); + break; } default: { if (argv._.length === 0) { - await reAlias(token, null, help, exit, apiUrl, debug, alias) - break + await reAlias(token, null, help, exit, apiUrl, debug, alias); + break; } if (argv.rules) { - await updatePathAlias(alias, argv._[0], argv.rules) + await updatePathAlias(alias, argv._[0], argv.rules); } else if (argv._.length === 2) { - await alias.set(String(argv._[0]), String(argv._[1])) + await alias.set(String(argv._[0]), String(argv._[1])); } else if (argv._.length >= 3) { - error('Invalid number of arguments') - help() - exit(1) + error("Invalid number of arguments"); + help(); + exit(1); } else { - error('Please specify a valid subcommand: ls | set | rm') - help() - exit(1) + error("Please specify a valid subcommand: ls | set | rm"); + help(); + exit(1); } } } - alias.close() + alias.close(); } async function confirmDeploymentRemoval(alias, _alias) { - const deploymentsList = await alias.list() - const urls = new Map(deploymentsList.map(l => [l.uid, l.url])) + const deploymentsList = await alias.list(); + const urls = new Map(deploymentsList.map(l => [l.uid, l.url])); - const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + ' ago') - const _sourceUrl = chalk.underline(`https://${urls.get(_alias.deploymentId)}`) + const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + " ago"); + const _sourceUrl = chalk.underline( + `https://${urls.get(_alias.deploymentId)}` + ); const tbl = table( - [[_alias.uid, _sourceUrl, chalk.underline(`https://${_alias.alias}`), time]], - {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} - ) - - const msg = '> The following alias will be removed permanently\n' + - ` ${tbl} \nAre you sure?` - return await promptBool(msg) + [ + [_alias.uid, _sourceUrl, chalk.underline(`https://${_alias.alias}`), time] + ], + { align: ["l", "r", "l"], hsep: " ".repeat(6) } + ); + + const msg = "> The following alias will be removed permanently\n" + + ` ${tbl} \nAre you sure?`; + return await promptBool(msg); } function findAlias(alias, list) { - let key - let val + let key; + let val; if (/\./.test(alias)) { - val = toHost(alias) - key = 'alias' + val = toHost(alias); + key = "alias"; } else { - val = alias - key = 'uid' + val = alias; + key = "uid"; } const _alias = list.find(d => { if (d[key] === val) { if (debug) { - console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`) + console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`); } - return true + return true; } // match prefix if (`${val}.now.sh` === d.alias) { if (debug) { - console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`) + console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`); } - return true + return true; } - return false - }) + return false; + }); - return _alias + return _alias; } async function updatePathAlias(alias, aliasName, rules) { - const start = new Date() - const res = await alias.updatePathBasedroutes(String(aliasName), rules) - const elapsed = ms(new Date() - start) + const start = new Date(); + const res = await alias.updatePathBasedroutes(String(aliasName), rules); + const elapsed = ms(new Date() - start); if (res.error) { - const err = new Error(res.error.message) - err.userError = true - throw err + const err = new Error(res.error.message); + err.userError = true; + throw err; } else { - console.log(`${chalk.cyan('> Success!')} ${res.ruleCount} rules configured for ${chalk.underline(res.alias)} [${elapsed}]`) + console.log( + `${chalk.cyan("> Success!")} ${res.ruleCount} rules configured for ${chalk.underline(res.alias)} [${elapsed}]` + ); } } diff --git a/bin/now-billing-add.js b/bin/now-billing-add.js index b36b5f6..1c9d89a 100644 --- a/bin/now-billing-add.js +++ b/bin/now-billing-add.js @@ -1,183 +1,185 @@ #!/usr/bin/env node // Packages -const ansiEscapes = require('ansi-escapes') -const chalk = require('chalk') -const ccValidator = require('credit-card') +const ansiEscapes = require("ansi-escapes"); +const chalk = require("chalk"); +const ccValidator = require("credit-card"); // Ours -const textInput = require('../lib/utils/input/text') -const countries = require('../lib/utils/billing/country-list') -const cardBrands = require('../lib/utils/billing/card-brands') -const geocode = require('../lib/utils/billing/geocode') -const success = require('../lib/utils/output/success') -const wait = require('../lib/utils/output/wait') +const textInput = require("../lib/utils/input/text"); +const countries = require("../lib/utils/billing/country-list"); +const cardBrands = require("../lib/utils/billing/card-brands"); +const geocode = require("../lib/utils/billing/geocode"); +const success = require("../lib/utils/output/success"); +const wait = require("../lib/utils/output/wait"); function rightPad(string, n = 12) { - n -= string.length - return string + ' '.repeat(n > -1 ? n : 0) + n -= string.length; + return string + " ".repeat(n > -1 ? n : 0); } function expDateMiddleware(data) { - return data + return data; } -module.exports = function (creditCards) { +module.exports = function(creditCards) { const state = { error: undefined, - cardGroupLabel: `> ${chalk.bold('Enter your card details')}`, + cardGroupLabel: `> ${chalk.bold("Enter your card details")}`, name: { - label: rightPad('Full Name'), - placeholder: 'John Appleseed', + label: rightPad("Full Name"), + placeholder: "John Appleseed", validateValue: data => data.trim().length > 0 }, cardNumber: { - label: rightPad('Number'), - mask: 'cc', - placeholder: '#### #### #### ####', - validateKeypress: (data, value) => ( - /\d/.test(data) && value.length < 19 - ), + label: rightPad("Number"), + mask: "cc", + placeholder: "#### #### #### ####", + validateKeypress: (data, value) => /\d/.test(data) && value.length < 19, validateValue: data => { - data = data.replace(/ /g, '') - const type = ccValidator.determineCardType(data) + data = data.replace(/ /g, ""); + const type = ccValidator.determineCardType(data); if (!type) { - return false + return false; } - return ccValidator.isValidCardNumber(data, type) + return ccValidator.isValidCardNumber(data, type); } }, ccv: { - label: rightPad('CCV'), - mask: 'ccv', - placeholder: '###', + label: rightPad("CCV"), + mask: "ccv", + placeholder: "###", validateValue: data => { - const brand = state.cardNumber.brand.toLowerCase() - return ccValidator.doesCvvMatchType(data, brand) + const brand = state.cardNumber.brand.toLowerCase(); + return ccValidator.doesCvvMatchType(data, brand); } }, expDate: { - label: rightPad('Exp. Date'), - mask: 'expDate', - placeholder: 'mm / yyyy', + label: rightPad("Exp. Date"), + mask: "expDate", + placeholder: "mm / yyyy", middleware: expDateMiddleware, - validateValue: data => !ccValidator.isExpired(...data.split(' / ')) + validateValue: data => !ccValidator.isExpired(...data.split(" / ")) }, - addressGroupLabel: `\n> ${chalk.bold('Enter your billing address')}`, + addressGroupLabel: `\n> ${chalk.bold("Enter your billing address")}`, country: { - label: rightPad('Country'), + label: rightPad("Country"), async autoComplete(value) { for (const country in countries) { if (!Object.hasOwnProperty.call(countries, country)) { - continue + continue; } if (country.startsWith(value)) { - return country.substr(value.length) + return country.substr(value.length); } } - return false + return false; }, validateValue: value => countries[value] !== undefined }, zipCode: { - label: rightPad('ZIP'), + label: rightPad("ZIP"), validadeKeypress: data => data.trim().length > 0, validateValue: data => data.trim().length > 0 }, state: { - label: rightPad('State'), + label: rightPad("State"), validateValue: data => data.trim().length > 0 }, city: { - label: rightPad('City'), + label: rightPad("City"), validateValue: data => data.trim().length > 0 }, address1: { - label: rightPad('Address'), + label: rightPad("Address"), validateValue: data => data.trim().length > 0 } - } + }; async function render() { for (const key in state) { if (!Object.hasOwnProperty.call(state, key)) { - continue + continue; } - const piece = state[key] - if (typeof piece === 'string') { - console.log(piece) - } else if (typeof piece === 'object') { - let result + const piece = state[key]; + if (typeof piece === "string") { + console.log(piece); + } else if (typeof piece === "object") { + let result; try { result = await textInput({ - label: '- ' + piece.label, + label: "- " + piece.label, initialValue: piece.initialValue || piece.value, placeholder: piece.placeholder, mask: piece.mask, validateKeypress: piece.validateKeypress, validateValue: piece.validateValue, autoComplete: piece.autoComplete - }) - piece.value = result - if (key === 'cardNumber') { - let brand = cardBrands[ccValidator.determineCardType(result)] - piece.brand = brand - if (brand === 'American Express') { - state.ccv.placeholder = '#'.repeat(4) + }); + piece.value = result; + if (key === "cardNumber") { + let brand = cardBrands[ccValidator.determineCardType(result)]; + piece.brand = brand; + if (brand === "American Express") { + state.ccv.placeholder = "#".repeat(4); } else { - state.ccv.placeholder = '#'.repeat(3) + state.ccv.placeholder = "#".repeat(3); } - brand = chalk.cyan(`[${brand}]`) - const masked = chalk.gray('#### '.repeat(3)) + result.split(' ')[3] + brand = chalk.cyan(`[${brand}]`); + const masked = chalk.gray("#### ".repeat(3)) + result.split(" ")[3]; process.stdout.write( - `${chalk.cyan('✓')} ${piece.label}${masked} ${brand}\n` - ) - } else if (key === 'ccv') { + `${chalk.cyan("✓")} ${piece.label}${masked} ${brand}\n` + ); + } else if (key === "ccv") { process.stdout.write( - `${chalk.cyan('✓')} ${piece.label}${'*'.repeat(result.length)}\n` - ) - } else if (key === 'expDate') { - let text = result.split(' / ') - text = text[0] + chalk.gray(' / ') + text[1] - process.stdout.write(`${chalk.cyan('✓')} ${piece.label}${text}\n`) - } else if (key === 'zipCode') { - const stopSpinner = wait(piece.label + result) + `${chalk.cyan("✓")} ${piece.label}${"*".repeat(result.length)}\n` + ); + } else if (key === "expDate") { + let text = result.split(" / "); + text = text[0] + chalk.gray(" / ") + text[1]; + process.stdout.write(`${chalk.cyan("✓")} ${piece.label}${text}\n`); + } else if (key === "zipCode") { + const stopSpinner = wait(piece.label + result); const addressInfo = await geocode({ country: state.country.value, zipCode: result - }) + }); if (addressInfo.state) { - state.state.initialValue = addressInfo.state + state.state.initialValue = addressInfo.state; } if (addressInfo.city) { - state.city.initialValue = addressInfo.city + state.city.initialValue = addressInfo.city; } - stopSpinner() - process.stdout.write(`${chalk.cyan('✓')} ${piece.label}${result}\n`) + stopSpinner(); + process.stdout.write( + `${chalk.cyan("✓")} ${piece.label}${result}\n` + ); } else { - process.stdout.write(`${chalk.cyan('✓')} ${piece.label}${result}\n`) + process.stdout.write( + `${chalk.cyan("✓")} ${piece.label}${result}\n` + ); } } catch (err) { - if (err.message === 'USER_ABORT') { - process.exit(1) + if (err.message === "USER_ABORT") { + process.exit(1); } else { - console.error(err) + console.error(err); } } } } - console.log('') // new line - const stopSpinner = wait('Saving card') + console.log(""); // new line + const stopSpinner = wait("Saving card"); try { const res = await creditCards.add({ @@ -190,17 +192,19 @@ module.exports = function (creditCards) { state: state.state.value, city: state.city.value, address1: state.address1.value - }) - stopSpinner() - success(`${state.cardNumber.brand} ending in ${res.last4} was added to your account`) + }); + stopSpinner(); + success( + `${state.cardNumber.brand} ending in ${res.last4} was added to your account` + ); } catch (err) { - stopSpinner() - const linesToClear = state.error ? 13 : 12 - process.stdout.write(ansiEscapes.eraseLines(linesToClear)) - state.error = `${chalk.red('> Error!')} ${err.message} Please make sure the info is correct` - await render() + stopSpinner(); + const linesToClear = state.error ? 13 : 12; + process.stdout.write(ansiEscapes.eraseLines(linesToClear)); + state.error = `${chalk.red("> Error!")} ${err.message} Please make sure the info is correct`; + await render(); } } - render().catch(console.error) -} + render().catch(console.error); +}; diff --git a/bin/now-billing.js b/bin/now-billing.js index 0cb9119..95c761b 100644 --- a/bin/now-billing.js +++ b/bin/now-billing.js @@ -1,78 +1,80 @@ #!/usr/bin/env node // Native -const {resolve} = require('path') +const { resolve } = require("path"); // Packages -const chalk = require('chalk') -const minimist = require('minimist') -const ms = require('ms') +const chalk = require("chalk"); +const minimist = require("minimist"); +const ms = require("ms"); // Ours -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {error} = require('../lib/error') -const NowCreditCards = require('../lib/credit-cards') -const indent = require('../lib/indent') -const listInput = require('../lib/utils/input/list') -const success = require('../lib/utils/output/success') -const promptBool = require('../lib/utils/input/prompt-bool') -const logo = require('../lib/utils/output/logo') +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { error } = require("../lib/error"); +const NowCreditCards = require("../lib/credit-cards"); +const indent = require("../lib/indent"); +const listInput = require("../lib/utils/input/list"); +const success = require("../lib/utils/output/success"); +const promptBool = require("../lib/utils/input/prompt-bool"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug'], + string: ["config", "token"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't' + help: "h", + config: "c", + debug: "d", + token: "t" } -}) +}); -const subcommand = argv._[0] +const subcommand = argv._[0]; const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now billing`)} - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Lists all your credit cards: + ${chalk.gray("–")} Lists all your credit cards: - ${chalk.cyan('$ now billing ls')} + ${chalk.cyan("$ now billing ls")} - ${chalk.gray('–')} Adds a credit card (interactively): + ${chalk.gray("–")} Adds a credit card (interactively): ${chalk.cyan(`$ now billing add`)} - ${chalk.gray('–')} Removes a credit card: + ${chalk.gray("–")} Removes a credit card: ${chalk.cyan(`$ now billing rm `)} - ${chalk.gray('–')} If the id is omitted, you can choose interactively + ${chalk.gray("–")} If the id is omitted, you can choose interactively - ${chalk.gray('–')} Selects your default credit card: + ${chalk.gray("–")} Selects your default credit card: ${chalk.cyan(`$ now billing set-default `)} - ${chalk.gray('–')} If the id is omitted, you can choose interactively - `) -} + ${chalk.gray("–")} If the id is omitted, you can choose interactively + ` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } const exit = code => { @@ -80,235 +82,247 @@ const exit = code => { // because there's a node bug where // stdout writes are asynchronous // https://github.com/nodejs/node/issues/6456 - setTimeout(() => process.exit(code || 0), 100) -} + setTimeout(() => process.exit(code || 0), 100); +}; if (argv.help || !subcommand) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - if (err.userError) { - error(err.message) - } else { - error(`Unknown error: ${err.stack}`) + .then(async token => { + try { + await run(token); + } catch (err) { + if (err.userError) { + error(err.message); + } else { + error(`Unknown error: ${err.stack}`); + } + exit(1); } - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } // Builds a `choices` object that can be passesd to inquirer.prompt() function buildInquirerChoices(cards) { return cards.cards.map(card => { - const _default = card.id === cards.defaultCardId ? ' ' + chalk.bold('(default)') : '' - const id = `${chalk.cyan(`ID: ${card.id}`)}${_default}` - const number = `${chalk.gray('#### ').repeat(3)}${card.last4}` + const _default = card.id === cards.defaultCardId + ? " " + chalk.bold("(default)") + : ""; + const id = `${chalk.cyan(`ID: ${card.id}`)}${_default}`; + const number = `${chalk.gray("#### ").repeat(3)}${card.last4}`; const str = [ id, indent(card.name, 2), indent(`${card.brand} ${number}`, 2) - ].join('\n') + ].join("\n"); return { name: str, // Will be displayed by Inquirer value: card.id, // Will be used to identify the answer short: card.id // Will be displayed after the users answers - } - }) + }; + }); } async function run(token) { - const start = new Date() - const creditCards = new NowCreditCards(apiUrl, token, {debug}) - const args = argv._.slice(1) + const start = new Date(); + const creditCards = new NowCreditCards(apiUrl, token, { debug }); + const args = argv._.slice(1); switch (subcommand) { - case 'ls': - case 'list': { - const cards = await creditCards.ls() - const text = cards.cards.map(card => { - const _default = card.id === cards.defaultCardId ? ' ' + chalk.bold('(default)') : '' - const id = `${chalk.gray('-')} ${chalk.cyan(`ID: ${card.id}`)}${_default}` - const number = `${chalk.gray('#### ').repeat(3)}${card.last4}` - let address = card.address_line1 - - if (card.address_line2) { - address += `, ${card.address_line2}.` - } else { - address += '.' - } + case "ls": + case "list": { + const cards = await creditCards.ls(); + const text = cards.cards + .map(card => { + const _default = card.id === cards.defaultCardId + ? " " + chalk.bold("(default)") + : ""; + const id = `${chalk.gray("-")} ${chalk.cyan(`ID: ${card.id}`)}${_default}`; + const number = `${chalk.gray("#### ").repeat(3)}${card.last4}`; + let address = card.address_line1; + + if (card.address_line2) { + address += `, ${card.address_line2}.`; + } else { + address += "."; + } - address += `\n${card.address_city}, ` + address += `\n${card.address_city}, `; - if (card.address_state) { - address += `${card.address_state}, ` - } + if (card.address_state) { + address += `${card.address_state}, `; + } - // Stripe is returning a two digit code for the country, - // but we want the full country name - address += `${card.address_zip}. ${card.address_country}` + // Stripe is returning a two digit code for the country, + // but we want the full country name + address += `${card.address_zip}. ${card.address_country}`; - return [ - id, - indent(card.name, 2), - indent(`${card.brand} ${number}`, 2), - indent(address, 2) - ].join('\n') - }).join('\n\n') + return [ + id, + indent(card.name, 2), + indent(`${card.brand} ${number}`, 2), + indent(address, 2) + ].join("\n"); + }) + .join("\n\n"); - const elapsed = ms(new Date() - start) - console.log(`> ${cards.cards.length} card${cards.cards.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) + const elapsed = ms(new Date() - start); + console.log( + `> ${cards.cards.length} card${cards.cards.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` + ); if (text) { - console.log(`\n${text}\n`) + console.log(`\n${text}\n`); } - break + break; } - case 'set-default': { + case "set-default": { if (args.length > 1) { - error('Invalid number of arguments') - return exit(1) + error("Invalid number of arguments"); + return exit(1); } - const start = new Date() - const cards = await creditCards.ls() + const start = new Date(); + const cards = await creditCards.ls(); if (cards.cards.length === 0) { - error('You have no credit cards to choose from') - return exit(0) + error("You have no credit cards to choose from"); + return exit(0); } - let cardId = args[0] + let cardId = args[0]; if (cardId === undefined) { - const elapsed = ms(new Date() - start) - const message = `Selecting a new default payment card from ${cards.cards.length} total ${chalk.gray(`[${elapsed}]`)}` - const choices = buildInquirerChoices(cards) + const elapsed = ms(new Date() - start); + const message = `Selecting a new default payment card from ${cards.cards.length} total ${chalk.gray(`[${elapsed}]`)}`; + const choices = buildInquirerChoices(cards); cardId = await listInput({ message, choices, separator: true, - abort: 'end' - }) + abort: "end" + }); } // Check if the provided cardId (in case the user // typed `now billing set-default `) is valid if (cardId) { - const label = `Are you sure that you to set this card as the default?` - const confirmation = await promptBool(label) + const label = `Are you sure that you to set this card as the default?`; + const confirmation = await promptBool(label); if (!confirmation) { - console.log('Aborted') - break + console.log("Aborted"); + break; } - const start = new Date() - await creditCards.setDefault(cardId) - - const card = cards.cards.find(card => card.id === cardId) - const elapsed = ms(new Date() - start) - success(`${card.brand} ending in ${card.last4} is now the default ${chalk.gray(`[${elapsed}]`)}`) + const start = new Date(); + await creditCards.setDefault(cardId); + + const card = cards.cards.find(card => card.id === cardId); + const elapsed = ms(new Date() - start); + success( + `${card.brand} ending in ${card.last4} is now the default ${chalk.gray(`[${elapsed}]`)}` + ); } else { - console.log('No changes made') + console.log("No changes made"); } - break + break; } - case 'rm': - case 'remove': { + case "rm": + case "remove": { if (args.length > 1) { - error('Invalid number of arguments') - return exit(1) + error("Invalid number of arguments"); + return exit(1); } - const start = new Date() - const cards = await creditCards.ls() + const start = new Date(); + const cards = await creditCards.ls(); if (cards.cards.length === 0) { - error('You have no credit cards to choose from to delete') - return exit(0) + error("You have no credit cards to choose from to delete"); + return exit(0); } - let cardId = args[0] + let cardId = args[0]; if (cardId === undefined) { - const elapsed = ms(new Date() - start) - const message = `Selecting a card to ${chalk.underline('remove')} from ${cards.cards.length} total ${chalk.gray(`[${elapsed}]`)}` - const choices = buildInquirerChoices(cards) + const elapsed = ms(new Date() - start); + const message = `Selecting a card to ${chalk.underline("remove")} from ${cards.cards.length} total ${chalk.gray(`[${elapsed}]`)}`; + const choices = buildInquirerChoices(cards); cardId = await listInput({ message, choices, separator: true, - abort: 'start' - }) + abort: "start" + }); } // Shoud check if the provided cardId (in case the user // typed `now billing rm `) is valid if (cardId) { - const label = `Are you sure that you want to remove this card?` - const confirmation = await promptBool(label) + const label = `Are you sure that you want to remove this card?`; + const confirmation = await promptBool(label); if (!confirmation) { - console.log('Aborted') - break + console.log("Aborted"); + break; } - const start = new Date() - await creditCards.rm(cardId) + const start = new Date(); + await creditCards.rm(cardId); - const deletedCard = cards.cards.find(card => card.id === cardId) - const remainingCards = cards.cards.filter(card => card.id !== cardId) + const deletedCard = cards.cards.find(card => card.id === cardId); + const remainingCards = cards.cards.filter(card => card.id !== cardId); - let text = `${deletedCard.brand} ending in ${deletedCard.last4} was deleted` + let text = `${deletedCard.brand} ending in ${deletedCard.last4} was deleted`; // ${chalk.gray(`[${elapsed}]`)} if (cardId === cards.defaultCardId) { if (remainingCards.length === 0) { // The user deleted the last card in their account - text += `\n${chalk.yellow('Warning!')} You have no default card` + text += `\n${chalk.yellow("Warning!")} You have no default card`; } else { // We can't guess the current default card – let's ask the API - const cards = await creditCards.ls() - const newDefaultCard = cards.cards.find(card => card.id === cards.defaultCardId) + const cards = await creditCards.ls(); + const newDefaultCard = cards.cards.find( + card => card.id === cards.defaultCardId + ); - text += `\n${newDefaultCard.brand} ending in ${newDefaultCard.last4} in now default` + text += `\n${newDefaultCard.brand} ending in ${newDefaultCard.last4} in now default`; } } - const elapsed = ms(new Date() - start) - text += ` ${chalk.gray(`[${elapsed}]`)}` - success(text) + const elapsed = ms(new Date() - start); + text += ` ${chalk.gray(`[${elapsed}]`)}`; + success(text); } else { - console.log('No changes made') + console.log("No changes made"); } - break + break; } - case 'add': { - require(resolve(__dirname, 'now-billing-add.js'))(creditCards) + case "add": { + require(resolve(__dirname, "now-billing-add.js"))(creditCards); - break + break; } default: - error('Please specify a valid subcommand: ls | add | rm | set-default') - help() - exit(1) + error("Please specify a valid subcommand: ls | add | rm | set-default"); + help(); + exit(1); } - creditCards.close() + creditCards.close(); } diff --git a/bin/now-certs.js b/bin/now-certs.js index 852dd92..1962af4 100755 --- a/bin/now-certs.js +++ b/bin/now-certs.js @@ -1,287 +1,342 @@ #!/usr/bin/env node // Native -const path = require('path') +const path = require("path"); // Packages -const chalk = require('chalk') -const table = require('text-table') -const minimist = require('minimist') -const fs = require('fs-promise') -const ms = require('ms') +const chalk = require("chalk"); +const table = require("text-table"); +const minimist = require("minimist"); +const fs = require("fs-promise"); +const ms = require("ms"); // Ours -const strlen = require('../lib/strlen') -const cfg = require('../lib/cfg') -const {handleError, error} = require('../lib/error') -const NowCerts = require('../lib/certs') -const login = require('../lib/login') -const exit = require('../lib/utils/exit') -const logo = require('../lib/utils/output/logo') +const strlen = require("../lib/strlen"); +const cfg = require("../lib/cfg"); +const { handleError, error } = require("../lib/error"); +const NowCerts = require("../lib/certs"); +const login = require("../lib/login"); +const exit = require("../lib/utils/exit"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token', 'crt', 'key', 'ca'], - boolean: ['help', 'debug'], + string: ["config", "token", "crt", "key", "ca"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't' + help: "h", + config: "c", + debug: "d", + token: "t" } -}) +}); -const subcommand = argv._[0] +const subcommand = argv._[0]; // options const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now certs`)} - ${chalk.dim('Note:')} + ${chalk.dim("Note:")} - This command is intended for advanced use only, normally ${chalk.bold('now')} manages your certificates automatically. + This command is intended for advanced use only, normally ${chalk.bold("now")} manages your certificates automatically. - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token - --crt ${chalk.bold.underline('FILE')} Certificate file - --key ${chalk.bold.underline('FILE')} Certificate key file - --ca ${chalk.bold.underline('FILE')} CA certificate chain file + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token + --crt ${chalk.bold.underline("FILE")} Certificate file + --key ${chalk.bold.underline("FILE")} Certificate key file + --ca ${chalk.bold.underline("FILE")} CA certificate chain file - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Listing all your certificates: + ${chalk.gray("–")} Listing all your certificates: - ${chalk.cyan('$ now certs ls')} + ${chalk.cyan("$ now certs ls")} - ${chalk.gray('–')} Creating a new certificate: + ${chalk.gray("–")} Creating a new certificate: - ${chalk.cyan('$ now certs create domain.com')} + ${chalk.cyan("$ now certs create domain.com")} - ${chalk.gray('–')} Renewing an existing certificate issued with ${chalk.bold('now')}: + ${chalk.gray("–")} Renewing an existing certificate issued with ${chalk.bold("now")}: - ${chalk.cyan('$ now certs renew domain.com')} + ${chalk.cyan("$ now certs renew domain.com")} - ${chalk.gray('–')} Replacing an existing certificate with a user-supplied certificate: + ${chalk.gray("–")} Replacing an existing certificate with a user-supplied certificate: - ${chalk.cyan('$ now certs replace --crt domain.crt --key domain.key --ca ca_chain.crt domain.com')} -`) -} + ${chalk.cyan("$ now certs replace --crt domain.crt --key domain.key --ca ca_chain.crt domain.com")} +` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } if (argv.help || !subcommand) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - handleError(err) - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + .then(async token => { + try { + await run(token); + } catch (err) { + handleError(err); + exit(1); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } function formatExpirationDate(date) { - const diff = date - Date.now() - return diff < 0 ? chalk.gray(ms(-diff) + ' ago') : chalk.gray('in ' + ms(diff)) + const diff = date - Date.now(); + return diff < 0 + ? chalk.gray(ms(-diff) + " ago") + : chalk.gray("in " + ms(diff)); } async function run(token) { - const certs = new NowCerts(apiUrl, token, {debug}) - const args = argv._.slice(1) - const start = Date.now() + const certs = new NowCerts(apiUrl, token, { debug }); + const args = argv._.slice(1); + const start = Date.now(); - if (subcommand === 'ls' || subcommand === 'list') { + if (subcommand === "ls" || subcommand === "list") { if (args.length !== 0) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now certs ls`")}` + ); + return exit(1); } - const list = await certs.ls() - const elapsed = ms(new Date() - start) + const list = await certs.ls(); + const elapsed = ms(new Date() - start); - console.log(`> ${list.length} certificate${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) + console.log( + `> ${list.length} certificate${list.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` + ); if (list.length > 0) { - const cur = Date.now() + const cur = Date.now(); list.sort((a, b) => { - return a.cn.localeCompare(b.cn) - }) - const header = [['', 'id', 'cn', 'created', 'expiration', 'auto-renew'].map(s => chalk.dim(s))] - const out = table(header.concat(list.map(cert => { - const cn = chalk.bold(cert.cn) - const time = chalk.gray(ms(cur - new Date(cert.created)) + ' ago') - const expiration = formatExpirationDate(new Date(cert.expiration)) - return [ - '', - cert.uid ? cert.uid : 'unknown', - cn, - time, - expiration, - cert.autoRenew ? 'yes' : 'no' - ] - })), {align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) + return a.cn.localeCompare(b.cn); + }); + const header = [ + ["", "id", "cn", "created", "expiration", "auto-renew"].map(s => + chalk.dim(s)) + ]; + const out = table( + header.concat( + list.map(cert => { + const cn = chalk.bold(cert.cn); + const time = chalk.gray(ms(cur - new Date(cert.created)) + " ago"); + const expiration = formatExpirationDate(new Date(cert.expiration)); + return [ + "", + cert.uid ? cert.uid : "unknown", + cn, + time, + expiration, + cert.autoRenew ? "yes" : "no" + ]; + }) + ), + { + align: ["l", "r", "l", "l", "l"], + hsep: " ".repeat(2), + stringLength: strlen + } + ); if (out) { - console.log('\n' + out + '\n') + console.log("\n" + out + "\n"); } } - } else if (subcommand === 'create') { + } else if (subcommand === "create") { if (args.length !== 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs create `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now certs create `")}` + ); + return exit(1); } - const cn = args[0] - let cert + const cn = args[0]; + let cert; - if (argv.crt || argv.key || argv.ca) { // Issue a custom certificate + if (argv.crt || argv.key || argv.ca) { + // Issue a custom certificate if (!argv.crt || !argv.key) { - error(`Missing required arguments for a custom certificate entry. Usage: ${chalk.cyan('`now certs create --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] `')}`) - return exit(1) + error( + `Missing required arguments for a custom certificate entry. Usage: ${chalk.cyan("`now certs create --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] `")}` + ); + return exit(1); } - const crt = readX509File(argv.crt) - const key = readX509File(argv.key) - const ca = argv.ca ? readX509File(argv.ca) : '' + const crt = readX509File(argv.crt); + const key = readX509File(argv.key); + const ca = argv.ca ? readX509File(argv.ca) : ""; - cert = await certs.put(cn, crt, key, ca) - } else { // Issue a standard certificate - cert = await certs.create(cn) + cert = await certs.put(cn, crt, key, ca); + } else { + // Issue a standard certificate + cert = await certs.create(cn); } if (!cert) { // Cert is undefined and "Cert is already issued" has been printed to stdout - return exit(1) + return exit(1); } - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Certificate entry ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} created ${chalk.gray(`[${elapsed}]`)}`) - } else if (subcommand === 'renew') { + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Certificate entry ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} created ${chalk.gray(`[${elapsed}]`)}` + ); + } else if (subcommand === "renew") { if (args.length !== 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs renew `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now certs renew `")}` + ); + return exit(1); } - const cert = await getCertIdCn(certs, args[0]) + const cert = await getCertIdCn(certs, args[0]); if (!cert) { - return exit(1) + return exit(1); } - const yes = await readConfirmation(cert, 'The following certificate will be renewed\n') + const yes = await readConfirmation( + cert, + "The following certificate will be renewed\n" + ); if (!yes) { - error('User abort') - return exit(0) + error("User abort"); + return exit(0); } - await certs.renew(cert.cn) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} renewed ${chalk.gray(`[${elapsed}]`)}`) - } else if (subcommand === 'replace') { + await certs.renew(cert.cn); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} renewed ${chalk.gray(`[${elapsed}]`)}` + ); + } else if (subcommand === "replace") { if (!argv.crt || !argv.key) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] `")}` + ); + return exit(1); } - const crt = readX509File(argv.crt) - const key = readX509File(argv.key) - const ca = argv.ca ? readX509File(argv.ca) : '' + const crt = readX509File(argv.crt); + const key = readX509File(argv.key); + const ca = argv.ca ? readX509File(argv.ca) : ""; - const cert = await getCertIdCn(certs, args[0]) + const cert = await getCertIdCn(certs, args[0]); if (!cert) { - return exit(1) + return exit(1); } - const yes = await readConfirmation(cert, 'The following certificate will be replaced permanently\n') + const yes = await readConfirmation( + cert, + "The following certificate will be replaced permanently\n" + ); if (!yes) { - error('User abort') - return exit(0) + error("User abort"); + return exit(0); } - await certs.put(cert.cn, crt, key, ca) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} replaced ${chalk.gray(`[${elapsed}]`)}`) - } else if (subcommand === 'rm' || subcommand === 'remove') { + await certs.put(cert.cn, crt, key, ca); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} replaced ${chalk.gray(`[${elapsed}]`)}` + ); + } else if (subcommand === "rm" || subcommand === "remove") { if (args.length !== 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs rm `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now certs rm `")}` + ); + return exit(1); } - const cert = await getCertIdCn(certs, args[0]) + const cert = await getCertIdCn(certs, args[0]); if (!cert) { - return exit(1) + return exit(1); } - const yes = await readConfirmation(cert, 'The following certificate will be removed permanently\n') + const yes = await readConfirmation( + cert, + "The following certificate will be removed permanently\n" + ); if (!yes) { - error('User abort') - return exit(0) + error("User abort"); + return exit(0); } - await certs.delete(cert.cn) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`) + await certs.delete(cert.cn); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Certificate ${chalk.bold(cert.cn)} ${chalk.gray(`(${cert.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}` + ); } else { - error('Please specify a valid subcommand: ls | create | renew | replace | rm') - help() - exit(1) + error( + "Please specify a valid subcommand: ls | create | renew | replace | rm" + ); + help(); + exit(1); } - return certs.close() + return certs.close(); } -process.on('uncaughtException', err => { - handleError(err) - exit(1) -}) +process.on("uncaughtException", err => { + handleError(err); + exit(1); +}); function readConfirmation(cert, msg) { return new Promise(resolve => { - const time = chalk.gray(ms(new Date() - new Date(cert.created)) + ' ago') - const tbl = table( - [[cert.uid, chalk.bold(cert.cn), time]], - {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} - ) - - process.stdout.write(`> ${msg}`) - process.stdout.write(' ' + tbl + '\n') - - process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) - - process.stdin.on('data', d => { - process.stdin.pause() - resolve(d.toString().trim().toLowerCase() === 'y') - }).resume() - }) + const time = chalk.gray(ms(new Date() - new Date(cert.created)) + " ago"); + const tbl = table([[cert.uid, chalk.bold(cert.cn), time]], { + align: ["l", "r", "l"], + hsep: " ".repeat(6) + }); + + process.stdout.write(`> ${msg}`); + process.stdout.write(" " + tbl + "\n"); + + process.stdout.write( + `${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` + ); + + process.stdin + .on("data", d => { + process.stdin.pause(); + resolve(d.toString().trim().toLowerCase() === "y"); + }) + .resume(); + }); } function readX509File(file) { - return fs.readFileSync(path.resolve(file), 'utf8') + return fs.readFileSync(path.resolve(file), "utf8"); } async function getCertIdCn(certs, idOrCn) { - const list = await certs.ls() + const list = await certs.ls(); const thecert = list.filter(cert => { - return cert.uid === idOrCn || cert.cn === idOrCn - })[0] + return cert.uid === idOrCn || cert.cn === idOrCn; + })[0]; if (!thecert) { - error(`No certificate found by id or cn "${idOrCn}"`) - return null + error(`No certificate found by id or cn "${idOrCn}"`); + return null; } - return thecert + return thecert; } diff --git a/bin/now-deploy.js b/bin/now-deploy.js index 0a5bf9f..2fe23af 100755 --- a/bin/now-deploy.js +++ b/bin/now-deploy.js @@ -1,85 +1,81 @@ #!/usr/bin/env node // Native -const {resolve} = require('path') +const { resolve } = require("path"); // Packages -const Progress = require('progress') -const fs = require('fs-promise') -const bytes = require('bytes') -const chalk = require('chalk') -const minimist = require('minimist') -const ms = require('ms') -const flatten = require('arr-flatten') -const dotenv = require('dotenv') +const Progress = require("progress"); +const fs = require("fs-promise"); +const bytes = require("bytes"); +const chalk = require("chalk"); +const minimist = require("minimist"); +const ms = require("ms"); +const flatten = require("arr-flatten"); +const dotenv = require("dotenv"); // Ours -const copy = require('../lib/copy') -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {version} = require('../lib/pkg') -const Logger = require('../lib/build-logger') -const Now = require('../lib') -const toHumanPath = require('../lib/utils/to-human-path') -const promptOptions = require('../lib/utils/prompt-options') -const {handleError, error} = require('../lib/error') -const {fromGit, isRepoPath, gitPathParts} = require('../lib/git') -const readMetaData = require('../lib/read-metadata') -const checkPath = require('../lib/utils/check-path') -const {reAlias, assignAlias} = require('../lib/re-alias') -const exit = require('../lib/utils/exit') -const logo = require('../lib/utils/output/logo') -const cmd = require('../lib/utils/output/cmd') +const copy = require("../lib/copy"); +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { version } = require("../lib/pkg"); +const Logger = require("../lib/build-logger"); +const Now = require("../lib"); +const toHumanPath = require("../lib/utils/to-human-path"); +const promptOptions = require("../lib/utils/prompt-options"); +const { handleError, error } = require("../lib/error"); +const { fromGit, isRepoPath, gitPathParts } = require("../lib/git"); +const readMetaData = require("../lib/read-metadata"); +const checkPath = require("../lib/utils/check-path"); +const { reAlias, assignAlias } = require("../lib/re-alias"); +const exit = require("../lib/utils/exit"); +const logo = require("../lib/utils/output/logo"); +const cmd = require("../lib/utils/output/cmd"); const argv = minimist(process.argv.slice(2), { - string: [ - 'config', - 'token', - 'name', - 'alias' - ], + string: ["config", "token", "name", "alias"], boolean: [ - 'help', - 'version', - 'debug', - 'force', - 'links', - 'login', - 'no-clipboard', - 'forward-npm', - 'docker', - 'npm', - 'static' + "help", + "version", + "debug", + "force", + "links", + "login", + "no-clipboard", + "forward-npm", + "docker", + "npm", + "static" ], alias: { - env: 'e', - dotenv: 'E', - help: 'h', - config: 'c', - debug: 'd', - version: 'v', - force: 'f', - token: 't', - forceSync: 'F', - links: 'l', - login: 'L', - public: 'p', - 'no-clipboard': 'C', - 'forward-npm': 'N', - name: 'n', - alias: 'a' + env: "e", + dotenv: "E", + help: "h", + config: "c", + debug: "d", + version: "v", + force: "f", + token: "t", + forceSync: "F", + links: "l", + login: "L", + public: "p", + "no-clipboard": "C", + "forward-npm": "N", + name: "n", + alias: "a" } -}) +}); const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now`)} [options] - ${chalk.dim('Commands:')} + ${chalk.dim("Commands:")} - ${chalk.dim('Cloud')} + ${chalk.dim("Cloud")} - deploy [path] Performs a deployment ${chalk.bold('(default)')} + deploy [path] Performs a deployment ${chalk.bold("(default)")} ls | list [app] List deployments rm | remove [id] Remove a deployment ln | alias [id] [url] Configures aliases for deployments @@ -90,97 +86,102 @@ const help = () => { open Open the latest deployment for the project help [cmd] Displays complete help for [cmd] - ${chalk.dim('Administrative')} + ${chalk.dim("Administrative")} billing | cc [cmd] Manages your credit cards and billing methods upgrade | downgrade [plan] Upgrades or downgrades your plan - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information -v, --version Output the version number -n, --name Set the name of the deployment - -c ${chalk.underline('FILE')}, --config=${chalk.underline('FILE')} Config file + -c ${chalk.underline("FILE")}, --config=${chalk.underline("FILE")} Config file -d, --debug Debug mode [off] -f, --force Force a new deployment even if nothing has changed - -t ${chalk.underline('TOKEN')}, --token=${chalk.underline('TOKEN')} Login token + -t ${chalk.underline("TOKEN")}, --token=${chalk.underline("TOKEN")} Login token -L, --login Configure login -l, --links Copy symlinks without resolving their target - -p, --public Deployment is public (${chalk.dim('`/_src`')} is exposed) [on for oss, off for premium] - -e, --env Include an env var (e.g.: ${chalk.dim('`-e KEY=value`')}). Can appear many times. - -E ${chalk.underline('FILE')}, --dotenv=${chalk.underline('FILE')} Include env vars from .env file. Defaults to '.env' + -p, --public Deployment is public (${chalk.dim("`/_src`")} is exposed) [on for oss, off for premium] + -e, --env Include an env var (e.g.: ${chalk.dim("`-e KEY=value`")}). Can appear many times. + -E ${chalk.underline("FILE")}, --dotenv=${chalk.underline("FILE")} Include env vars from .env file. Defaults to '.env' -C, --no-clipboard Do not attempt to copy URL to clipboard -N, --forward-npm Forward login information to install private npm modules - ${chalk.dim('Enforcable Types (when both package.json and Dockerfile exist):')} + ${chalk.dim("Enforcable Types (when both package.json and Dockerfile exist):")} --npm Node.js application --docker Docker container --static Static file hosting - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Deploys the current directory + ${chalk.gray("–")} Deploys the current directory - ${chalk.cyan('$ now')} + ${chalk.cyan("$ now")} - ${chalk.gray('–')} Deploys a custom path ${chalk.dim('`/usr/src/project`')} + ${chalk.gray("–")} Deploys a custom path ${chalk.dim("`/usr/src/project`")} - ${chalk.cyan('$ now /usr/src/project')} + ${chalk.cyan("$ now /usr/src/project")} - ${chalk.gray('–')} Deploys a GitHub repository + ${chalk.gray("–")} Deploys a GitHub repository - ${chalk.cyan('$ now user/repo#ref')} + ${chalk.cyan("$ now user/repo#ref")} - ${chalk.gray('–')} Deploys a GitHub, GitLab or Bitbucket repo using its URL + ${chalk.gray("–")} Deploys a GitHub, GitLab or Bitbucket repo using its URL - ${chalk.cyan('$ now https://gitlab.com/user/repo')} + ${chalk.cyan("$ now https://gitlab.com/user/repo")} - ${chalk.gray('–')} Deploys with ENV vars + ${chalk.gray("–")} Deploys with ENV vars - ${chalk.cyan('$ now -e NODE_ENV=production -e MYSQL_PASSWORD=@mysql-password')} + ${chalk.cyan("$ now -e NODE_ENV=production -e MYSQL_PASSWORD=@mysql-password")} - ${chalk.gray('–')} Displays comprehensive help for the subcommand ${chalk.dim('`list`')} + ${chalk.gray("–")} Displays comprehensive help for the subcommand ${chalk.dim("`list`")} - ${chalk.cyan('$ now help list')} -`) -} + ${chalk.cyan("$ now help list")} +` + ); +}; -let path = argv._[0] +let path = argv._[0]; if (path) { // if path is relative: resolve // if path is absolute: clear up strange `/` etc - path = resolve(process.cwd(), path) + path = resolve(process.cwd(), path); } else { - path = process.cwd() + path = process.cwd(); } // If the current deployment is a repo -const gitRepo = {} +const gitRepo = {}; // options -let forceNew = argv.force -const debug = argv.debug -const clipboard = !argv['no-clipboard'] -const forwardNpm = argv['forward-npm'] -const forceSync = argv.forceSync -const shouldLogin = argv.login -const followSymlinks = !argv.links -const wantsPublic = argv.public -const deploymentName = argv.name || false -const apiUrl = argv.url || 'https://api.zeit.co' -const isTTY = process.stdout.isTTY -const quiet = !isTTY -const autoAliases = typeof argv.alias === 'undefined' ? false : flatten([argv.alias]) +let forceNew = argv.force; +const debug = argv.debug; +const clipboard = !argv["no-clipboard"]; +const forwardNpm = argv["forward-npm"]; +const forceSync = argv.forceSync; +const shouldLogin = argv.login; +const followSymlinks = !argv.links; +const wantsPublic = argv.public; +const deploymentName = argv.name || false; +const apiUrl = argv.url || "https://api.zeit.co"; +const isTTY = process.stdout.isTTY; +const quiet = !isTTY; +const autoAliases = typeof argv.alias === "undefined" + ? false + : flatten([argv.alias]); if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } if (Array.isArray(autoAliases)) { - console.log(`${chalk.red('Deprecated!')} The option ${chalk.grey('--alias')} will be removed soon.`) - console.log('Read more about the new way here: http://bit.ly/2l2v5Fg\n') + console.log( + `${chalk.red("Deprecated!")} The option ${chalk.grey("--alias")} will be removed soon.` + ); + console.log("Read more about the new way here: http://bit.ly/2l2v5Fg\n"); } // Create a new deployment if user changed @@ -188,307 +189,348 @@ if (Array.isArray(autoAliases)) { // This should just work fine because it doesn't // force a new sync, it just forces a new deployment. if (deploymentName || wantsPublic) { - forceNew = true + forceNew = true; } -const config = cfg.read() -const alwaysForwardNpm = config.forwardNpm +const config = cfg.read(); +const alwaysForwardNpm = config.forwardNpm; if (argv.h || argv.help) { - help() - exit(0) + help(); + exit(0); } else if (argv.v || argv.version) { - console.log(version) - process.exit(0) + console.log(version); + process.exit(0); } else if (!(argv.token || config.token) || shouldLogin) { login(apiUrl) - .then(token => { - if (shouldLogin) { - console.log('> Logged in successfully. Token saved in ~/.now.json') - process.exit(0) - } else { - sync(token).catch(err => { - error(`Unknown error: ${err}\n${err.stack}`) - process.exit(1) - }) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - process.exit(1) - }) + .then(token => { + if (shouldLogin) { + console.log("> Logged in successfully. Token saved in ~/.now.json"); + process.exit(0); + } else { + sync(token).catch(err => { + error(`Unknown error: ${err}\n${err.stack}`); + process.exit(1); + }); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + process.exit(1); + }); } else { sync(argv.token || config.token).catch(err => { - error(`Unknown error: ${err}\n${err.stack}`) - process.exit(1) - }) + error(`Unknown error: ${err}\n${err.stack}`); + process.exit(1); + }); } async function sync(token) { - const start = Date.now() - const rawPath = argv._[0] + const start = Date.now(); + const rawPath = argv._[0]; const stopDeployment = msg => { - error(msg) - process.exit(1) - } + error(msg); + process.exit(1); + }; - const isValidRepo = isRepoPath(rawPath) + const isValidRepo = isRepoPath(rawPath); try { - await fs.stat(path) + await fs.stat(path); } catch (err) { - let repo + let repo; - if (isValidRepo && isValidRepo !== 'no-valid-url') { - const gitParts = gitPathParts(rawPath) - Object.assign(gitRepo, gitParts) + if (isValidRepo && isValidRepo !== "no-valid-url") { + const gitParts = gitPathParts(rawPath); + Object.assign(gitRepo, gitParts); - const searchMessage = setTimeout(() => { - console.log(`> Didn't find directory. Searching on ${gitRepo.type}...`) - }, 500) + const searchMessage = setTimeout( + () => { + console.log( + `> Didn't find directory. Searching on ${gitRepo.type}...` + ); + }, + 500 + ); try { - repo = await fromGit(rawPath, debug) + repo = await fromGit(rawPath, debug); } catch (err) {} - clearTimeout(searchMessage) + clearTimeout(searchMessage); } if (repo) { // Tell now which directory to deploy - path = repo.path + path = repo.path; // Set global variable for deleting tmp dir later // once the deployment has finished - Object.assign(gitRepo, repo) - } else if (isValidRepo === 'no-valid-url') { - stopDeployment(`This URL is neither a valid repository from GitHub, nor from GitLab.`) + Object.assign(gitRepo, repo); + } else if (isValidRepo === "no-valid-url") { + stopDeployment( + `This URL is neither a valid repository from GitHub, nor from GitLab.` + ); } else if (isValidRepo) { - const gitRef = gitRepo.ref ? `with "${chalk.bold(gitRepo.ref)}" ` : '' - stopDeployment(`There's no repository named "${chalk.bold(gitRepo.main)}" ${gitRef}on ${gitRepo.type}`) + const gitRef = gitRepo.ref ? `with "${chalk.bold(gitRepo.ref)}" ` : ""; + stopDeployment( + `There's no repository named "${chalk.bold(gitRepo.main)}" ${gitRef}on ${gitRepo.type}` + ); } else { - stopDeployment(`Could not read directory ${chalk.bold(path)}`) + stopDeployment(`Could not read directory ${chalk.bold(path)}`); } } // Make sure that directory is deployable - await checkPath(path) + await checkPath(path); if (!quiet) { if (gitRepo.main) { - const gitRef = gitRepo.ref ? ` at "${chalk.bold(gitRepo.ref)}" ` : '' - console.log(`> Deploying ${gitRepo.type} repository "${chalk.bold(gitRepo.main)}"` + gitRef) + const gitRef = gitRepo.ref ? ` at "${chalk.bold(gitRepo.ref)}" ` : ""; + console.log( + `> Deploying ${gitRepo.type} repository "${chalk.bold(gitRepo.main)}"` + + gitRef + ); } else { - console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`) + console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`); } } - let deploymentType + let deploymentType; - let hasPackage - let hasDockerfile - let isStatic + let hasPackage; + let hasDockerfile; + let isStatic; if (argv.docker) { if (debug) { - console.log(`> [debug] Forcing \`deploymentType\` = \`docker\``) + console.log(`> [debug] Forcing \`deploymentType\` = \`docker\``); } - deploymentType = 'docker' + deploymentType = "docker"; } else if (argv.npm) { - deploymentType = 'npm' + deploymentType = "npm"; } else if (argv.static) { if (debug) { - console.log(`> [debug] Forcing static deployment`) + console.log(`> [debug] Forcing static deployment`); } - deploymentType = 'npm' - isStatic = true + deploymentType = "npm"; + isStatic = true; } else { try { - await fs.stat(resolve(path, 'package.json')) + await fs.stat(resolve(path, "package.json")); } catch (err) { - hasPackage = true + hasPackage = true; } [hasPackage, hasDockerfile] = await Promise.all([ await (async () => { try { - await fs.stat(resolve(path, 'package.json')) + await fs.stat(resolve(path, "package.json")); } catch (err) { - return false + return false; } - return true + return true; })(), await (async () => { try { - await fs.stat(resolve(path, 'Dockerfile')) + await fs.stat(resolve(path, "Dockerfile")); } catch (err) { - return false + return false; } - return true + return true; })() - ]) + ]); if (hasPackage && hasDockerfile) { if (debug) { - console.log('[debug] multiple manifests found, disambiguating') + console.log("[debug] multiple manifests found, disambiguating"); } if (isTTY) { try { - console.log(`> Two manifests found. Press [${chalk.bold('n')}] to deploy or re-run with --flag`) + console.log( + `> Two manifests found. Press [${chalk.bold("n")}] to deploy or re-run with --flag` + ); deploymentType = await promptOptions([ - ['npm', `${chalk.bold('package.json')}\t${chalk.gray(' --npm')} `], - ['docker', `${chalk.bold('Dockerfile')}\t${chalk.gray('--docker')} `] - ]) + [ + "npm", + `${chalk.bold("package.json")}\t${chalk.gray(" --npm")} ` + ], + [ + "docker", + `${chalk.bold("Dockerfile")}\t${chalk.gray("--docker")} ` + ] + ]); } catch (err) { - error(err.message) - process.exit(1) + error(err.message); + process.exit(1); } } else { - error('Ambiguous deployment (`package.json` and `Dockerfile` found). ' + - 'Please supply `--npm` or `--docker` to disambiguate.') + error( + "Ambiguous deployment (`package.json` and `Dockerfile` found). " + + "Please supply `--npm` or `--docker` to disambiguate." + ); } } else if (hasPackage) { if (debug) { - console.log('> [debug] `package.json` found, assuming `deploymentType` = `npm`') + console.log( + "> [debug] `package.json` found, assuming `deploymentType` = `npm`" + ); } - deploymentType = 'npm' + deploymentType = "npm"; } else if (hasDockerfile) { if (debug) { - console.log('> [debug] `Dockerfile` found, assuming `deploymentType` = `docker`') + console.log( + "> [debug] `Dockerfile` found, assuming `deploymentType` = `docker`" + ); } - deploymentType = 'docker' + deploymentType = "docker"; } else { if (debug) { - console.log('> [debug] No manifest files found, assuming static deployment') + console.log( + "> [debug] No manifest files found, assuming static deployment" + ); } - isStatic = true + isStatic = true; } } - const {nowConfig} = await readMetaData(path, { + const { nowConfig } = await readMetaData(path, { deploymentType, deploymentName, isStatic, quiet: true - }) + }); - const now = new Now(apiUrl, token, {debug}) + const now = new Now(apiUrl, token, { debug }); - let dotenvConfig - let dotenvOption + let dotenvConfig; + let dotenvOption; if (argv.dotenv) { - dotenvOption = argv.dotenv + dotenvOption = argv.dotenv; } else if (nowConfig && nowConfig.dotenv) { - dotenvOption = nowConfig.dotenv + dotenvOption = nowConfig.dotenv; } if (dotenvOption) { - const dotenvFileName = typeof dotenvOption === 'string' ? dotenvOption : '.env' + const dotenvFileName = typeof dotenvOption === "string" + ? dotenvOption + : ".env"; if (!fs.existsSync(dotenvFileName)) { - error(`--dotenv flag is set but ${dotenvFileName} file is missing`) - return process.exit(1) + error(`--dotenv flag is set but ${dotenvFileName} file is missing`); + return process.exit(1); } - const dotenvFile = await fs.readFile(dotenvFileName) - dotenvConfig = dotenv.parse(dotenvFile) + const dotenvFile = await fs.readFile(dotenvFileName); + dotenvConfig = dotenv.parse(dotenvFile); } // Merge `now.env` from package.json with `-e` arguments. - const pkgEnv = nowConfig && nowConfig.env + const pkgEnv = nowConfig && nowConfig.env; const envs = [ ...Object.keys(dotenvConfig || {}).map(k => `${k}=${dotenvConfig[k]}`), ...Object.keys(pkgEnv || {}).map(k => `${k}=${pkgEnv[k]}`), ...[].concat(argv.env || []) - ] + ]; - let secrets + let secrets; const findSecret = async uidOrName => { if (!secrets) { - secrets = await now.listSecrets() + secrets = await now.listSecrets(); } return secrets.filter(secret => { - return secret.name === uidOrName || secret.uid === uidOrName - }) - } - - const env_ = await Promise.all(envs.map(async kv => { - if (typeof kv !== 'string') { - error('Env key and value missing') - return process.exit(1) - } - - const [key, ...rest] = kv.split('=') - let val + return secret.name === uidOrName || secret.uid === uidOrName; + }); + }; + + const env_ = await Promise.all( + envs.map(async kv => { + if (typeof kv !== "string") { + error("Env key and value missing"); + return process.exit(1); + } - if (rest.length > 0) { - val = rest.join('=') - } + const [key, ...rest] = kv.split("="); + let val; - if (/[^A-z0-9_]/i.test(key)) { - error(`Invalid ${chalk.dim('-e')} key ${chalk.bold(`"${chalk.bold(key)}"`)}. Only letters, digits and underscores are allowed.`) - return process.exit(1) - } + if (rest.length > 0) { + val = rest.join("="); + } - if (!key) { - error(`Invalid env option ${chalk.bold(`"${kv}"`)}`) - return process.exit(1) - } + if (/[^A-z0-9_]/i.test(key)) { + error( + `Invalid ${chalk.dim("-e")} key ${chalk.bold(`"${chalk.bold(key)}"`)}. Only letters, digits and underscores are allowed.` + ); + return process.exit(1); + } - if (val === undefined) { - if ((key in process.env)) { - console.log(`> Reading ${chalk.bold(`"${chalk.bold(key)}"`)} from your env (as no value was specified)`) - // escape value if it begins with @ - val = process.env[key].replace(/^@/, '\\@') - } else { - error(`No value specified for env ${chalk.bold(`"${chalk.bold(key)}"`)} and it was not found in your env.`) - return process.exit(1) + if (!key) { + error(`Invalid env option ${chalk.bold(`"${kv}"`)}`); + return process.exit(1); } - } - if (val[0] === '@') { - const uidOrName = val.substr(1) - const secrets = await findSecret(uidOrName) - if (secrets.length === 0) { - if (uidOrName === '') { - error(`Empty reference provided for env key ${chalk.bold(`"${chalk.bold(key)}"`)}`) + if (val === undefined) { + if (key in process.env) { + console.log( + `> Reading ${chalk.bold(`"${chalk.bold(key)}"`)} from your env (as no value was specified)` + ); + // escape value if it begins with @ + val = process.env[key].replace(/^@/, "\\@"); } else { - error(`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`) + error( + `No value specified for env ${chalk.bold(`"${chalk.bold(key)}"`)} and it was not found in your env.` + ); + return process.exit(1); } - return process.exit(1) - } else if (secrets.length > 1) { - error(`Ambiguous secret ${chalk.bold(`"${uidOrName}"`)} (matches ${chalk.bold(secrets.length)} secrets)`) - return process.exit(1) } - val = {uid: secrets[0].uid} - } + if (val[0] === "@") { + const uidOrName = val.substr(1); + const secrets = await findSecret(uidOrName); + if (secrets.length === 0) { + if (uidOrName === "") { + error( + `Empty reference provided for env key ${chalk.bold(`"${chalk.bold(key)}"`)}` + ); + } else { + error( + `No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}` + ); + } + return process.exit(1); + } else if (secrets.length > 1) { + error( + `Ambiguous secret ${chalk.bold(`"${uidOrName}"`)} (matches ${chalk.bold(secrets.length)} secrets)` + ); + return process.exit(1); + } - return [ - key, - typeof val === 'string' ? val.replace(/^\\@/, '@') : val - ] - })) + val = { uid: secrets[0].uid }; + } - const env = {} - env_ - .filter(v => Boolean(v)) - .forEach(([key, val]) => { + return [key, typeof val === "string" ? val.replace(/^\\@/, "@") : val]; + }) + ); + + const env = {}; + env_.filter(v => Boolean(v)).forEach(([key, val]) => { if (key in env) { - console.log(`> ${chalk.yellow('NOTE:')} Overriding duplicate env key ${chalk.bold(`"${key}"`)}`) + console.log( + `> ${chalk.yellow("NOTE:")} Overriding duplicate env key ${chalk.bold(`"${key}"`)}` + ); } - env[key] = val - }) + env[key] = val; + }); try { await now.create(path, { @@ -502,138 +544,146 @@ async function sync(token) { quiet, wantsPublic, isStatic - }) + }); } catch (err) { if (debug) { - console.log(`> [debug] error: ${err}\n${err.stack}`) + console.log(`> [debug] error: ${err}\n${err.stack}`); } - handleError(err) - process.exit(1) + handleError(err); + process.exit(1); } - const {url} = now - const elapsed = ms(new Date() - start) + const { url } = now; + const elapsed = ms(new Date() - start); if (isTTY) { if (clipboard) { try { - await copy(url) - console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} (copied to clipboard) [${elapsed}]`) + await copy(url); + console.log( + `${chalk.cyan("> Ready!")} ${chalk.bold(url)} (copied to clipboard) [${elapsed}]` + ); } catch (err) { - console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} [${elapsed}]`) + console.log( + `${chalk.cyan("> Ready!")} ${chalk.bold(url)} [${elapsed}]` + ); } } else { - console.log(`> ${url} [${elapsed}]`) + console.log(`> ${url} [${elapsed}]`); } } else { - process.stdout.write(url) + process.stdout.write(url); } - const startU = new Date() + const startU = new Date(); const complete = () => { if (!quiet) { - const elapsedU = ms(new Date() - startU) - console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsedU}] `) - console.log('> Initializing…') + const elapsedU = ms(new Date() - startU); + console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsedU}] `); + console.log("> Initializing…"); } // close http2 agent - now.close() + now.close(); // show build logs - printLogs(now.host, token) - } + printLogs(now.host, token); + }; if (now.syncAmount) { - const bar = new Progress('> Upload [:bar] :percent :etas', { + const bar = new Progress("> Upload [:bar] :percent :etas", { width: 20, - complete: '=', - incomplete: '', + complete: "=", + incomplete: "", total: now.syncAmount - }) + }); - now.upload() + now.upload(); - now.on('upload', ({names, data}) => { - const amount = data.length + now.on("upload", ({ names, data }) => { + const amount = data.length; if (debug) { - console.log(`> [debug] Uploaded: ${names.join(' ')} (${bytes(data.length)})`) + console.log( + `> [debug] Uploaded: ${names.join(" ")} (${bytes(data.length)})` + ); } - bar.tick(amount) - }) + bar.tick(amount); + }); - now.on('complete', complete) + now.on("complete", complete); - now.on('error', err => { - error('Upload failed') - handleError(err) - process.exit(1) - }) + now.on("error", err => { + error("Upload failed"); + handleError(err); + process.exit(1); + }); } else { if (!quiet) { - console.log(`> Initializing…`) + console.log(`> Initializing…`); } // close http2 agent - now.close() + now.close(); // show build logs - printLogs(now.host, token) + printLogs(now.host, token); } } function printLogs(host, token) { // log build - const logger = new Logger(host, {debug, quiet}) + const logger = new Logger(host, { debug, quiet }); - logger.on('error', async err => { + logger.on("error", async err => { if (!quiet) { - if (err && err.type === 'BUILD_ERROR') { - error(`The build step of your project failed. To retry, run ${cmd('now --force')}.`) + if (err && err.type === "BUILD_ERROR") { + error( + `The build step of your project failed. To retry, run ${cmd("now --force")}.` + ); } else { - error('Deployment failed') + error("Deployment failed"); } } if (gitRepo && gitRepo.cleanup) { // Delete temporary directory that contains repository - gitRepo.cleanup() + gitRepo.cleanup(); if (debug) { - console.log(`> [debug] Removed temporary repo directory`) + console.log(`> [debug] Removed temporary repo directory`); } } - process.exit(1) - }) + process.exit(1); + }); - logger.on('close', async () => { + logger.on("close", async () => { if (Array.isArray(autoAliases)) { - const aliasList = autoAliases.filter(item => item !== '') + const aliasList = autoAliases.filter(item => item !== ""); if (aliasList.length > 0) { for (const alias of aliasList) { - await assignAlias(alias, token, host, apiUrl, debug) + await assignAlias(alias, token, host, apiUrl, debug); } } else { - await reAlias(token, host, help, exit, apiUrl, debug) + await reAlias(token, host, help, exit, apiUrl, debug); } } if (!quiet) { - console.log(`${chalk.cyan('> Deployment complete!')}`) + console.log(`${chalk.cyan("> Deployment complete!")}`); } if (gitRepo && gitRepo.cleanup) { // Delete temporary directory that contains repository - gitRepo.cleanup() + gitRepo.cleanup(); if (debug) { - console.log(`> [debug] Removed temporary repo directory`) + console.log(`> [debug] Removed temporary repo directory`); } } - process.exit(0) - }) + process.exit(0); + }); } diff --git a/bin/now-dns.js b/bin/now-dns.js index 4eeff5b..c0de1cd 100755 --- a/bin/now-dns.js +++ b/bin/now-dns.js @@ -1,193 +1,224 @@ #!/usr/bin/env node // Packages -const chalk = require('chalk') -const minimist = require('minimist') -const ms = require('ms') -const table = require('text-table') +const chalk = require("chalk"); +const minimist = require("minimist"); +const ms = require("ms"); +const table = require("text-table"); // Ours -const cfg = require('../lib/cfg') -const DomainRecords = require('../lib/domain-records') -const indent = require('../lib/indent') -const login = require('../lib/login') -const strlen = require('../lib/strlen') -const {handleError, error} = require('../lib/error') -const exit = require('../lib/utils/exit') -const logo = require('../lib/utils/output/logo') +const cfg = require("../lib/cfg"); +const DomainRecords = require("../lib/domain-records"); +const indent = require("../lib/indent"); +const login = require("../lib/login"); +const strlen = require("../lib/strlen"); +const { handleError, error } = require("../lib/error"); +const exit = require("../lib/utils/exit"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config'], - boolean: ['help', 'debug'], + string: ["config"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't' + help: "h", + config: "c", + debug: "d", + token: "t" } -}) +}); -const subcommand = argv._[0] +const subcommand = argv._[0]; // options const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now dns ls`)} [domain] ${chalk.bold(`${logo} now dns add`)} ${chalk.bold(`${logo} now dns add`)} MX ${chalk.bold(`${logo} now dns add`)} SRV ${chalk.bold(`${logo} now dns rm`)} - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} config file -d, --debug debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} List all your DNS records + ${chalk.gray("–")} List all your DNS records - ${chalk.cyan('$ now dns ls')} + ${chalk.cyan("$ now dns ls")} - ${chalk.gray('–')} Add an A record for a subdomain + ${chalk.gray("–")} Add an A record for a subdomain - ${chalk.cyan('$ now dns add A ')} - ${chalk.cyan('$ now dns add zeit.rocks api A 198.51.100.100')} + ${chalk.cyan("$ now dns add A ")} + ${chalk.cyan("$ now dns add zeit.rocks api A 198.51.100.100")} - ${chalk.gray('–')} Add an MX record (@ as a name refers to the domain) + ${chalk.gray("–")} Add an MX record (@ as a name refers to the domain) - ${chalk.cyan('$ now dns add @ MX ')} - ${chalk.cyan('$ now dns add zeit.rocks @ MX mail.zeit.rocks 10')} -`) -} + ${chalk.cyan("$ now dns add @ MX ")} + ${chalk.cyan("$ now dns add zeit.rocks @ MX mail.zeit.rocks 10")} +` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } if (argv.help || !subcommand) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - handleError(err) - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + .then(async token => { + try { + await run(token); + } catch (err) { + handleError(err); + exit(1); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } async function run(token) { - const domainRecords = new DomainRecords(apiUrl, token, {debug}) - const args = argv._.slice(1) - const start = Date.now() + const domainRecords = new DomainRecords(apiUrl, token, { debug }); + const args = argv._.slice(1); + const start = Date.now(); - if (subcommand === 'ls' || subcommand === 'list') { + if (subcommand === "ls" || subcommand === "list") { if (args.length > 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now dns ls [domain]`')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now dns ls [domain]`")}` + ); + return exit(1); } - const elapsed = ms(new Date() - start) - const res = await domainRecords.ls(args[0]) - const text = [] - let count = 0 + const elapsed = ms(new Date() - start); + const res = await domainRecords.ls(args[0]); + const text = []; + let count = 0; res.forEach((records, domain) => { - count += records.length + count += records.length; if (records.length > 0) { - const cur = Date.now() - const header = [['', 'id', 'name', 'type', 'value', 'aux', 'created'].map(s => chalk.dim(s))] - const out = table(header.concat(records.map(record => { - const time = chalk.gray(ms(cur - new Date(Number(record.created))) + ' ago') - return [ - '', - record.id, - record.name, - record.type, - record.value, - record.mxPriority || record.priority || '', - time - ] - })), {align: ['l', 'r', 'l', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) - text.push(`\n\n${chalk.bold(domain)}\n${indent(out, 2)}`) + const cur = Date.now(); + const header = [ + ["", "id", "name", "type", "value", "aux", "created"].map(s => + chalk.dim(s)) + ]; + const out = table( + header.concat( + records.map(record => { + const time = chalk.gray( + ms(cur - new Date(Number(record.created))) + " ago" + ); + return [ + "", + record.id, + record.name, + record.type, + record.value, + record.mxPriority || record.priority || "", + time + ]; + }) + ), + { + align: ["l", "r", "l", "l", "l", "l"], + hsep: " ".repeat(2), + stringLength: strlen + } + ); + text.push(`\n\n${chalk.bold(domain)}\n${indent(out, 2)}`); } - }) - console.log(`> ${count} record${count === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) - console.log(text.join('')) - } else if (subcommand === 'add') { - const param = parseAddArgs(args) + }); + console.log( + `> ${count} record${count === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` + ); + console.log(text.join("")); + } else if (subcommand === "add") { + const param = parseAddArgs(args); if (!param) { - error(`Invalid number of arguments. See: ${chalk.cyan('`now dns --help`')} for usage.`) - return exit(1) + 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) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} A new DNS record for domain ${chalk.bold(param.domain)} ${chalk.gray(`(${record.uid})`)} created ${chalk.gray(`[${elapsed}]`)}`) - } else if (subcommand === 'rm' || subcommand === 'remove') { + const record = await domainRecords.create(param.domain, param.data); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} A new DNS record for domain ${chalk.bold(param.domain)} ${chalk.gray(`(${record.uid})`)} created ${chalk.gray(`[${elapsed}]`)}` + ); + } else if (subcommand === "rm" || subcommand === "remove") { if (args.length !== 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now dns rm `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now dns rm `")}` + ); + return exit(1); } - const record = await domainRecords.getRecord(args[0]) + const record = await domainRecords.getRecord(args[0]); if (!record) { - error('DNS record not found') - return exit(1) + error("DNS record not found"); + return exit(1); } - const yes = await readConfirmation(record, 'The following record will be removed permanently\n') + const yes = await readConfirmation( + record, + "The following record will be removed permanently\n" + ); if (!yes) { - error('User abort') - return exit(0) + error("User abort"); + return exit(0); } - await domainRecords.delete(record.domain, record.id) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Record ${chalk.gray(`${record.id}`)} removed ${chalk.gray(`[${elapsed}]`)}`) + await domainRecords.delete(record.domain, record.id); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Record ${chalk.gray(`${record.id}`)} removed ${chalk.gray(`[${elapsed}]`)}` + ); } else { - error('Please specify a valid subcommand: ls | add | rm') - help() - exit(1) + error("Please specify a valid subcommand: ls | add | rm"); + help(); + exit(1); } - return domainRecords.close() + return domainRecords.close(); } -process.on('uncaughtException', err => { - handleError(err) - exit(1) -}) +process.on("uncaughtException", err => { + handleError(err); + exit(1); +}); function parseAddArgs(args) { if (!args || args.length < 4) { - return null + return null; } - const domain = args[0] - const name = args[1] === '@' ? '' : args[1] - const type = args[2] - const value = args[3] + const domain = args[0]; + const name = args[1] === "@" ? "" : args[1]; + const type = args[2]; + const value = args[3]; - if (!(domain && typeof name === 'string' && type)) { - return null + if (!(domain && typeof name === "string" && type)) { + return null; } - if (type === 'MX') { + if (type === "MX") { if (args.length !== 5) { - return null + return null; } return { @@ -198,10 +229,10 @@ function parseAddArgs(args) { value, mxPriority: args[4] } - } - } else if (type === 'SRV') { + }; + } else if (type === "SRV") { if (args.length !== 7) { - return null + return null; } return { @@ -216,11 +247,11 @@ function parseAddArgs(args) { target: args[6] } } - } + }; } if (args.length !== 4) { - return null + return null; } return { @@ -230,27 +261,39 @@ function parseAddArgs(args) { type, value } - } + }; } function readConfirmation(record, msg) { return new Promise(resolve => { - const time = chalk.gray(ms(new Date() - new Date(Number(record.created))) + ' ago') + const time = chalk.gray( + ms(new Date() - new Date(Number(record.created))) + " ago" + ); const tbl = table( - [[record.id, - chalk.bold(`${record.name.length > 0 ? record.name + '.' : ''}${record.domain} ${record.type} ${record.value} ${record.mxPriority ? record.mxPriority : ''}`), - time]], - {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} - ) - - process.stdout.write(`> ${msg}`) - process.stdout.write(' ' + tbl + '\n') - - process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) - - process.stdin.on('data', d => { - process.stdin.pause() - resolve(d.toString().trim().toLowerCase() === 'y') - }).resume() - }) + [ + [ + record.id, + chalk.bold( + `${record.name.length > 0 ? record.name + "." : ""}${record.domain} ${record.type} ${record.value} ${record.mxPriority ? record.mxPriority : ""}` + ), + time + ] + ], + { align: ["l", "r", "l"], hsep: " ".repeat(6) } + ); + + process.stdout.write(`> ${msg}`); + process.stdout.write(" " + tbl + "\n"); + + process.stdout.write( + `${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` + ); + + process.stdin + .on("data", d => { + process.stdin.pause(); + resolve(d.toString().trim().toLowerCase() === "y"); + }) + .resume(); + }); } diff --git a/bin/now-domains.js b/bin/now-domains.js index c565167..539b340 100755 --- a/bin/now-domains.js +++ b/bin/now-domains.js @@ -1,291 +1,333 @@ #!/usr/bin/env node // Packages -const chalk = require('chalk') -const minimist = require('minimist') -const table = require('text-table') -const ms = require('ms') +const chalk = require("chalk"); +const minimist = require("minimist"); +const table = require("text-table"); +const ms = require("ms"); // Ours -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {error} = require('../lib/error') -const toHost = require('../lib/to-host') -const strlen = require('../lib/strlen') -const NowDomains = require('../lib/domains') -const exit = require('../lib/utils/exit') -const logo = require('../lib/utils/output/logo') +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { error } = require("../lib/error"); +const toHost = require("../lib/to-host"); +const strlen = require("../lib/strlen"); +const NowDomains = require("../lib/domains"); +const exit = require("../lib/utils/exit"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug', 'external', 'force'], + string: ["config", "token"], + boolean: ["help", "debug", "external", "force"], alias: { - help: 'h', - config: 'c', - debug: 'd', - external: 'e', - force: 'f', - token: 't' + help: "h", + config: "c", + debug: "d", + external: "e", + force: "f", + token: "t" } -}) +}); -const subcommand = argv._[0] +const subcommand = argv._[0]; // options const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now domains`)} - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] -e, --external Use external DNS server -f, --force Skip DNS verification - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Lists all your domains: + ${chalk.gray("–")} Lists all your domains: - ${chalk.cyan('$ now domains ls')} + ${chalk.cyan("$ now domains ls")} - ${chalk.gray('–')} Adds a domain name: + ${chalk.gray("–")} Adds a domain name: - ${chalk.cyan(`$ now domains add ${chalk.underline('my-app.com')}`)} + ${chalk.cyan(`$ now domains add ${chalk.underline("my-app.com")}`)} Make sure the domain's DNS nameservers are at least 2 of these: - ${chalk.gray('–')} ${chalk.underline('california.zeit.world')} ${chalk.dim('173.255.215.107')} - ${chalk.gray('–')} ${chalk.underline('london.zeit.world')} ${chalk.dim('178.62.47.76')} - ${chalk.gray('–')} ${chalk.underline('newark.zeit.world')} ${chalk.dim('173.255.231.87')} - ${chalk.gray('–')} ${chalk.underline('amsterdam.zeit.world')} ${chalk.dim('188.226.197.55')} - ${chalk.gray('–')} ${chalk.underline('dallas.zeit.world')} ${chalk.dim('173.192.101.194')} - ${chalk.gray('–')} ${chalk.underline('paris.zeit.world')} ${chalk.dim('37.123.115.172')} - ${chalk.gray('–')} ${chalk.underline('singapore.zeit.world')} ${chalk.dim('119.81.97.170')} - ${chalk.gray('–')} ${chalk.underline('sydney.zeit.world')} ${chalk.dim('52.64.171.200')} - ${chalk.gray('–')} ${chalk.underline('frankfurt.zeit.world')} ${chalk.dim('91.109.245.139')} - ${chalk.gray('–')} ${chalk.underline('iowa.zeit.world')} ${chalk.dim('23.236.59.22')} + ${chalk.gray("–")} ${chalk.underline("california.zeit.world")} ${chalk.dim("173.255.215.107")} + ${chalk.gray("–")} ${chalk.underline("london.zeit.world")} ${chalk.dim("178.62.47.76")} + ${chalk.gray("–")} ${chalk.underline("newark.zeit.world")} ${chalk.dim("173.255.231.87")} + ${chalk.gray("–")} ${chalk.underline("amsterdam.zeit.world")} ${chalk.dim("188.226.197.55")} + ${chalk.gray("–")} ${chalk.underline("dallas.zeit.world")} ${chalk.dim("173.192.101.194")} + ${chalk.gray("–")} ${chalk.underline("paris.zeit.world")} ${chalk.dim("37.123.115.172")} + ${chalk.gray("–")} ${chalk.underline("singapore.zeit.world")} ${chalk.dim("119.81.97.170")} + ${chalk.gray("–")} ${chalk.underline("sydney.zeit.world")} ${chalk.dim("52.64.171.200")} + ${chalk.gray("–")} ${chalk.underline("frankfurt.zeit.world")} ${chalk.dim("91.109.245.139")} + ${chalk.gray("–")} ${chalk.underline("iowa.zeit.world")} ${chalk.dim("23.236.59.22")} - ${chalk.yellow('NOTE:')} running ${chalk.dim('`now alias`')} will automatically register your domain - if it's configured with these nameservers (no need to ${chalk.dim('`domain add`')}). + ${chalk.yellow("NOTE:")} running ${chalk.dim("`now alias`")} will automatically register your domain + if it's configured with these nameservers (no need to ${chalk.dim("`domain add`")}). - For more details head to ${chalk.underline('https://zeit.world')}. + For more details head to ${chalk.underline("https://zeit.world")}. - ${chalk.gray('–')} Removing a domain: + ${chalk.gray("–")} Removing a domain: - ${chalk.cyan('$ now domain rm my-app.com')} + ${chalk.cyan("$ now domain rm my-app.com")} or - ${chalk.cyan('$ now domain rm domainId')} + ${chalk.cyan("$ now domain rm domainId")} - To get the list of domain ids, use ${chalk.dim('`now domains ls`')}. + To get the list of domain ids, use ${chalk.dim("`now domains ls`")}. - ${chalk.gray('–')} Adding and verifying a domain name using zeit.world nameservers: + ${chalk.gray("–")} Adding and verifying a domain name using zeit.world nameservers: - ${chalk.cyan('$ now domain add my-app.com')} + ${chalk.cyan("$ now domain add my-app.com")} The command will tell you if the domain was verified succesfully. In case the domain was not verified succesfully you should retry adding the domain after some time. - ${chalk.gray('–')} Adding and verifying a domain name using an external nameserver: + ${chalk.gray("–")} Adding and verifying a domain name using an external nameserver: - ${chalk.cyan('$ now domain add -e my-app.com')} + ${chalk.cyan("$ now domain add -e my-app.com")} and follow the verification instructions if requested. Finally, rerun the same command after completing the verification step. -`) -} +` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } if (argv.help || !subcommand) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - if (err.userError) { - error(err.message) - } else { - error(`Unknown error: ${err}\n${err.stack}`) + .then(async token => { + try { + await run(token); + } catch (err) { + if (err.userError) { + error(err.message); + } else { + error(`Unknown error: ${err}\n${err.stack}`); + } + exit(1); } - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } async function run(token) { - const domain = new NowDomains(apiUrl, token, {debug}) - const args = argv._.slice(1) + const domain = new NowDomains(apiUrl, token, { debug }); + const args = argv._.slice(1); switch (subcommand) { - case 'ls': - case 'list': { + case "ls": + case "list": { if (args.length !== 0) { - error('Invalid number of arguments') - return exit(1) + error("Invalid number of arguments"); + return exit(1); } - const start_ = new Date() - const domains = await domain.ls() - domains.sort((a, b) => new Date(b.created) - new Date(a.created)) - const current = new Date() - const header = [['', 'id', 'dns', 'url', 'verified', 'created'].map(s => chalk.dim(s))] - const out = domains.length === 0 ? null : table(header.concat(domains.map(domain => { - const ns = domain.isExternal ? 'external' : 'zeit.world' - const url = chalk.underline(`https://${domain.name}`) - const time = chalk.gray(ms(current - new Date(domain.created)) + ' ago') - return [ - '', - domain.uid, - ns, - url, - domain.verified, - time - ] - })), {align: ['l', 'r', 'l', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) - - const elapsed_ = ms(new Date() - start_) - console.log(`> ${domains.length} domain${domains.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed_}]`)}`) + const start_ = new Date(); + const domains = await domain.ls(); + domains.sort((a, b) => new Date(b.created) - new Date(a.created)); + const current = new Date(); + const header = [ + ["", "id", "dns", "url", "verified", "created"].map(s => chalk.dim(s)) + ]; + const out = domains.length === 0 + ? null + : table( + header.concat( + domains.map(domain => { + const ns = domain.isExternal ? "external" : "zeit.world"; + const url = chalk.underline(`https://${domain.name}`); + const time = chalk.gray( + ms(current - new Date(domain.created)) + " ago" + ); + return ["", domain.uid, ns, url, domain.verified, time]; + }) + ), + { + align: ["l", "r", "l", "l", "l", "l"], + hsep: " ".repeat(2), + stringLength: strlen + } + ); + + const elapsed_ = ms(new Date() - start_); + console.log( + `> ${domains.length} domain${domains.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed_}]`)}` + ); if (out) { - console.log('\n' + out + '\n') + console.log("\n" + out + "\n"); } - break + break; } - case 'rm': - case 'remove': { + case "rm": + case "remove": { if (args.length !== 1) { - error('Invalid number of arguments') - return exit(1) + error("Invalid number of arguments"); + return exit(1); } - const _target = String(args[0]) + const _target = String(args[0]); if (!_target) { - const err = new Error('No domain specified') - err.userError = true - throw err + const err = new Error("No domain specified"); + err.userError = true; + throw err; } - const _domains = await domain.ls() - const _domain = findDomain(_target, _domains) + const _domains = await domain.ls(); + const _domain = findDomain(_target, _domains); if (!_domain) { - const err = new Error(`Domain not found by "${_target}". Run ${chalk.dim('`now domains ls`')} to see your domains.`) - err.userError = true - throw err + const err = new Error( + `Domain not found by "${_target}". Run ${chalk.dim("`now domains ls`")} to see your domains.` + ); + err.userError = true; + throw err; } try { - const confirmation = (await readConfirmation(domain, _domain, _domains)).toLowerCase() - if (confirmation !== 'y' && confirmation !== 'yes') { - console.log('\n> Aborted') - process.exit(0) + const confirmation = (await readConfirmation( + domain, + _domain, + _domains + )).toLowerCase(); + if (confirmation !== "y" && confirmation !== "yes") { + console.log("\n> Aborted"); + process.exit(0); } - const start = new Date() - await domain.rm(_domain.name) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(_domain.uid)} removed [${elapsed}]`) + const start = new Date(); + await domain.rm(_domain.name); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Domain ${chalk.bold(_domain.uid)} removed [${elapsed}]` + ); } catch (err) { - error(err) - exit(1) + error(err); + exit(1); } - break + break; } - case 'add': - case 'set': { + case "add": + case "set": { if (args.length !== 1) { - error('Invalid number of arguments') - return exit(1) + error("Invalid number of arguments"); + return exit(1); } - const start = new Date() - const name = String(args[0]) - const {uid, code, created, verified} = await domain.add(name, argv.force, argv.external) - const elapsed = ms(new Date() - start) + const start = new Date(); + const name = String(args[0]); + const { uid, code, created, verified } = await domain.add( + name, + argv.force, + argv.external + ); + const elapsed = ms(new Date() - start); if (created) { - console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} added [${elapsed}]`) + console.log( + `${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} added [${elapsed}]` + ); } else if (verified) { - console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} verified [${elapsed}]`) - } else if (code === 'not_modified') { - console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} already exists [${elapsed}]`) + console.log( + `${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} verified [${elapsed}]` + ); + } else if (code === "not_modified") { + console.log( + `${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(name))} ${chalk.dim(`(${uid})`)} already exists [${elapsed}]` + ); } else { - console.log('> Verification required: Please rerun this command after some time') + console.log( + "> Verification required: Please rerun this command after some time" + ); } - break + break; } default: - error('Please specify a valid subcommand: ls | add | rm') - help() - exit(1) + error("Please specify a valid subcommand: ls | add | rm"); + help(); + exit(1); } - domain.close() + domain.close(); } async function readConfirmation(domain, _domain) { return new Promise(resolve => { - const time = chalk.gray(ms(new Date() - new Date(_domain.created)) + ' ago') + const time = chalk.gray( + ms(new Date() - new Date(_domain.created)) + " ago" + ); const tbl = table( [[_domain.uid, chalk.underline(`https://${_domain.name}`), time]], - {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} - ) + { align: ["l", "r", "l"], hsep: " ".repeat(6) } + ); - process.stdout.write('> The following domain will be removed permanently\n') - process.stdout.write(' ' + tbl + '\n') + process.stdout.write( + "> The following domain will be removed permanently\n" + ); + process.stdout.write(" " + tbl + "\n"); if (_domain.aliases.length > 0) { - process.stdout.write(`> ${chalk.yellow('Warning!')} This domain's ` + - `${chalk.bold(_domain.aliases.length + ' alias' + (_domain.aliases.length === 1 ? '' : 'es'))} ` + - `will be removed. Run ${chalk.dim('`now alias ls`')} to list.\n`) + process.stdout.write( + `> ${chalk.yellow("Warning!")} This domain's ` + + `${chalk.bold(_domain.aliases.length + " alias" + (_domain.aliases.length === 1 ? "" : "es"))} ` + + `will be removed. Run ${chalk.dim("`now alias ls`")} to list.\n` + ); } - process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) - - process.stdin.on('data', d => { - process.stdin.pause() - resolve(d.toString().trim()) - }).resume() - }) + process.stdout.write( + ` ${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` + ); + + process.stdin + .on("data", d => { + process.stdin.pause(); + resolve(d.toString().trim()); + }) + .resume(); + }); } function findDomain(val, list) { return list.find(d => { if (d.uid === val) { if (debug) { - console.log(`> [debug] matched domain ${d.uid} by uid`) + console.log(`> [debug] matched domain ${d.uid} by uid`); } - return true + return true; } // match prefix if (d.name === toHost(val)) { if (debug) { - console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`) + console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`); } - return true + return true; } - return false - }) + return false; + }); } diff --git a/bin/now-list.js b/bin/now-list.js index a810b27..eeb4f52 100755 --- a/bin/now-list.js +++ b/bin/now-list.js @@ -1,154 +1,163 @@ #!/usr/bin/env node // Packages -const fs = require('fs-promise') -const minimist = require('minimist') -const chalk = require('chalk') -const table = require('text-table') -const ms = require('ms') +const fs = require("fs-promise"); +const minimist = require("minimist"); +const chalk = require("chalk"); +const table = require("text-table"); +const ms = require("ms"); // Ours -const strlen = require('../lib/strlen') -const indent = require('../lib/indent') -const Now = require('../lib') -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {handleError, error} = require('../lib/error') -const logo = require('../lib/utils/output/logo') +const strlen = require("../lib/strlen"); +const indent = require("../lib/indent"); +const Now = require("../lib"); +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { handleError, error } = require("../lib/error"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug'], + string: ["config", "token"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't' + help: "h", + config: "c", + debug: "d", + token: "t" } -}) +}); const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now list`)} [app] - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} List all deployments + ${chalk.gray("–")} List all deployments - ${chalk.cyan('$ now ls')} + ${chalk.cyan("$ now ls")} - ${chalk.gray('–')} List all deployments for the app ${chalk.dim('`my-app`')} + ${chalk.gray("–")} List all deployments for the app ${chalk.dim("`my-app`")} - ${chalk.cyan('$ now ls my-app')} + ${chalk.cyan("$ now ls my-app")} - ${chalk.dim('Alias:')} ls -`) -} + ${chalk.dim("Alias:")} ls +` + ); +}; if (argv.help) { - help() - process.exit(0) + help(); + process.exit(0); } -const app = argv._[0] +const app = argv._[0]; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } -const config = cfg.read() +const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) -.then(async token => { - try { - await list(token) - } catch (err) { - error(`Unknown error: ${err}\n${err.stack}`) - process.exit(1) - } -}) -.catch(e => { - error(`Authentication error – ${e.message}`) - process.exit(1) -}) + .then(async token => { + try { + await list(token); + } catch (err) { + error(`Unknown error: ${err}\n${err.stack}`); + process.exit(1); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + process.exit(1); + }); async function list(token) { - const now = new Now(apiUrl, token, {debug}) - const start = new Date() + const now = new Now(apiUrl, token, { debug }); + const start = new Date(); - let deployments + let deployments; try { - deployments = await now.list(app) + deployments = await now.list(app); } catch (err) { - handleError(err) - process.exit(1) + handleError(err); + process.exit(1); } - now.close() + now.close(); - const apps = new Map() + const apps = new Map(); for (const dep of deployments) { - const deps = apps.get(dep.name) || [] - apps.set(dep.name, deps.concat(dep)) + const deps = apps.get(dep.name) || []; + apps.set(dep.name, deps.concat(dep)); } - const sorted = await sort([...apps]) - const current = Date.now() - - const text = sorted.map(([name, deps]) => { - const t = table(deps.map(({uid, url, created}) => { - const _url = url ? chalk.underline(`https://${url}`) : 'incomplete' - const time = chalk.gray(ms(current - created) + ' ago') - return [uid, _url, time] - }), {align: ['l', 'r', 'l'], hsep: ' '.repeat(6), stringLength: strlen}) - return chalk.bold(name) + '\n\n' + indent(t, 2) - }).join('\n\n') + const sorted = await sort([...apps]); + const current = Date.now(); + + const text = sorted + .map(([name, deps]) => { + const t = table( + deps.map(({ uid, url, created }) => { + const _url = url ? chalk.underline(`https://${url}`) : "incomplete"; + const time = chalk.gray(ms(current - created) + " ago"); + return [uid, _url, time]; + }), + { align: ["l", "r", "l"], hsep: " ".repeat(6), stringLength: strlen } + ); + return chalk.bold(name) + "\n\n" + indent(t, 2); + }) + .join("\n\n"); - const elapsed = ms(new Date() - start) - console.log(`> ${deployments.length} deployment${deployments.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) + const elapsed = ms(new Date() - start); + console.log( + `> ${deployments.length} deployment${deployments.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` + ); if (text) { - console.log('\n' + text + '\n') + console.log("\n" + text + "\n"); } } async function sort(apps) { - let pkg + let pkg; try { - const json = await fs.readFile('package.json') - pkg = JSON.parse(json) + const json = await fs.readFile("package.json"); + pkg = JSON.parse(json); } catch (err) { - pkg = {} + pkg = {}; } return apps - .map(([name, deps]) => { - deps = deps.slice().sort((a, b) => { - return b.created - a.created + .map(([name, deps]) => { + deps = deps.slice().sort((a, b) => { + return b.created - a.created; + }); + return [name, deps]; }) - return [name, deps] - }) - .sort(([nameA, depsA], [nameB, depsB]) => { - if (pkg.name === nameA) { - return -1 - } + .sort(([nameA, depsA], [nameB, depsB]) => { + if (pkg.name === nameA) { + return -1; + } - if (pkg.name === nameB) { - return 1 - } + if (pkg.name === nameB) { + return 1; + } - return depsB[0].created - depsA[0].created - }) + return depsB[0].created - depsA[0].created; + }); } diff --git a/bin/now-open.js b/bin/now-open.js index 296fc58..69f4eea 100644 --- a/bin/now-open.js +++ b/bin/now-open.js @@ -1,141 +1,143 @@ #!/usr/bin/env node // Packages -const fs = require('fs-promise') -const minimist = require('minimist') -const chalk = require('chalk') -const opn = require('opn') +const fs = require("fs-promise"); +const minimist = require("minimist"); +const chalk = require("chalk"); +const opn = require("opn"); // Ours -const Now = require('../lib') -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {handleError, error} = require('../lib/error') -const logo = require('../lib/utils/output/logo') +const Now = require("../lib"); +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { handleError, error } = require("../lib/error"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug'], + string: ["config", "token"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't' + help: "h", + config: "c", + debug: "d", + token: "t" } -}) +}); const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now open`)} - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Open latest deployment for current project + ${chalk.gray("–")} Open latest deployment for current project - ${chalk.cyan('$ now open')} + ${chalk.cyan("$ now open")} -`) -} +` + ); +}; if (argv.help) { - help() - process.exit(0) + help(); + process.exit(0); } -const app = argv._[0] +const app = argv._[0]; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } -const config = cfg.read() +const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await open(token) - } catch (err) { - error(`Unknown error: ${err}\n${err.stack}`) - process.exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - process.exit(1) - }) + .then(async token => { + try { + await open(token); + } catch (err) { + error(`Unknown error: ${err}\n${err.stack}`); + process.exit(1); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + process.exit(1); + }); async function open(token) { - const now = new Now(apiUrl, token, {debug}) + const now = new Now(apiUrl, token, { debug }); - let deployments + let deployments; try { - deployments = await now.list(app) + deployments = await now.list(app); } catch (err) { - handleError(err) - process.exit(1) + handleError(err); + process.exit(1); } - now.close() + now.close(); - const apps = new Map() + const apps = new Map(); for (const dep of deployments) { - const deps = apps.get(dep.name) || [] - apps.set(dep.name, deps.concat(dep)) + const deps = apps.get(dep.name) || []; + apps.set(dep.name, deps.concat(dep)); } - let pkg + let pkg; try { - const json = await fs.readFile('package.json') - pkg = JSON.parse(json) + const json = await fs.readFile("package.json"); + pkg = JSON.parse(json); } catch (err) { - pkg = {} + pkg = {}; } - const [currentProjectDeployments] = await getCurrentProjectDeployments([...apps], pkg) + const [currentProjectDeployments] = await getCurrentProjectDeployments( + [...apps], + pkg + ); - if (typeof currentProjectDeployments === 'undefined') { - console.log(`no deployments found for ${chalk.bold(pkg.name)}`) - process.exit(0) + if (typeof currentProjectDeployments === "undefined") { + console.log(`no deployments found for ${chalk.bold(pkg.name)}`); + process.exit(0); } - const sorted = await sortByCreation([...currentProjectDeployments]) - const latestDeploy = sorted[0] + const sorted = await sortByCreation([...currentProjectDeployments]); + const latestDeploy = sorted[0]; try { - const url = `https://${latestDeploy.url}` + const url = `https://${latestDeploy.url}`; - console.log(`Opening the latest deployment for ${chalk.bold(pkg.name)}...`) - console.log(`Here's the URL: ${chalk.underline(url)}`) + console.log(`Opening the latest deployment for ${chalk.bold(pkg.name)}...`); + console.log(`Here's the URL: ${chalk.underline(url)}`); - opn(url) - process.exit(0) + opn(url); + process.exit(0); } catch (err) { - error(`Unknown error: ${err}\n${err.stack}`) - process.exit(1) + error(`Unknown error: ${err}\n${err.stack}`); + process.exit(1); } } async function getCurrentProjectDeployments(apps, pkg) { - return apps - .filter(app => pkg.name === app[0]) - .map(app => app[1]) + return apps.filter(app => pkg.name === app[0]).map(app => app[1]); } async function sortByCreation(deps) { - return deps - .sort((depA, depB) => { - return depB.created - depA.created - }) + return deps.sort((depA, depB) => { + return depB.created - depA.created; + }); } diff --git a/bin/now-remove.js b/bin/now-remove.js index 398d30b..c83fdd4 100755 --- a/bin/now-remove.js +++ b/bin/now-remove.js @@ -1,184 +1,202 @@ #!/usr/bin/env node // Packages -const minimist = require('minimist') -const chalk = require('chalk') -const ms = require('ms') -const table = require('text-table') -const isURL = require('is-url') +const minimist = require("minimist"); +const chalk = require("chalk"); +const ms = require("ms"); +const table = require("text-table"); +const isURL = require("is-url"); // Ours -const Now = require('../lib') -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const {handleError, error} = require('../lib/error') -const logo = require('../lib/utils/output/logo') +const Now = require("../lib"); +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const { handleError, error } = require("../lib/error"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug', 'hard', 'yes'], + string: ["config", "token"], + boolean: ["help", "debug", "hard", "yes"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't', - yes: 'y' + help: "h", + config: "c", + debug: "d", + token: "t", + yes: "y" } -}) +}); -const ids = argv._ +const ids = argv._; // options const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now remove`)} deploymentId|deploymentName [...deploymentId|deploymentName] - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token -y, --yes Skip confirmation - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Remove a deployment identified by ${chalk.dim('`deploymentId`')}: + ${chalk.gray("–")} Remove a deployment identified by ${chalk.dim("`deploymentId`")}: - ${chalk.cyan('$ now rm deploymentId')} + ${chalk.cyan("$ now rm deploymentId")} - ${chalk.gray('–')} Remove all deployments with name ${chalk.dim('`my-app`')}: + ${chalk.gray("–")} Remove all deployments with name ${chalk.dim("`my-app`")}: - ${chalk.cyan('$ now rm my-app')} + ${chalk.cyan("$ now rm my-app")} - ${chalk.gray('–')} Remove two deployments with IDs ${chalk.dim('`eyWt6zuSdeus`')} and ${chalk.dim('`uWHoA9RQ1d1o`')}: + ${chalk.gray("–")} Remove two deployments with IDs ${chalk.dim("`eyWt6zuSdeus`")} and ${chalk.dim("`uWHoA9RQ1d1o`")}: - ${chalk.cyan('$ now rm eyWt6zuSdeus uWHoA9RQ1d1o')} + ${chalk.cyan("$ now rm eyWt6zuSdeus uWHoA9RQ1d1o")} - ${chalk.dim('Alias:')} rm -`) -} + ${chalk.dim("Alias:")} rm +` + ); +}; if (argv.help || ids.length === 0) { - help() - process.exit(0) + help(); + process.exit(0); } // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' -const hard = argv.hard || false -const skipConfirmation = argv.yes || false +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; +const hard = argv.hard || false; +const skipConfirmation = argv.yes || false; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } -const config = cfg.read() +const config = cfg.read(); function readConfirmation(matches) { return new Promise(resolve => { - process.stdout.write(`> The following deployment${matches.length === 1 ? '' : 's'} will be removed permanently:\n`) + process.stdout.write( + `> The following deployment${matches.length === 1 ? "" : "s"} will be removed permanently:\n` + ); const tbl = table( matches.map(depl => { - const time = chalk.gray(ms(new Date() - depl.created) + ' ago') - const url = depl.url ? chalk.underline(`https://${depl.url}`) : '' - return [depl.uid, url, time] + const time = chalk.gray(ms(new Date() - depl.created) + " ago"); + const url = depl.url ? chalk.underline(`https://${depl.url}`) : ""; + return [depl.uid, url, time]; }), - {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} - ) - process.stdout.write(tbl + '\n') + { align: ["l", "r", "l"], hsep: " ".repeat(6) } + ); + process.stdout.write(tbl + "\n"); for (const depl of matches) { for (const alias of depl.aliases) { process.stdout.write( - `> ${chalk.yellow('Warning!')} Deployment ${chalk.bold(depl.uid)} ` + - `is an alias for ${chalk.underline(`https://${alias.alias}`)} and will be removed.\n` - ) + `> ${chalk.yellow("Warning!")} Deployment ${chalk.bold(depl.uid)} ` + + `is an alias for ${chalk.underline(`https://${alias.alias}`)} and will be removed.\n` + ); } } - process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) - - process.stdin.on('data', d => { - process.stdin.pause() - resolve(d.toString().trim()) - }).resume() - }) + process.stdout.write( + `${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` + ); + + process.stdin + .on("data", d => { + process.stdin.pause(); + resolve(d.toString().trim()); + }) + .resume(); + }); } Promise.resolve(argv.token || config.token || login(apiUrl)) -.then(async token => { - try { - await remove(token) - } catch (err) { - error(`Unknown error: ${err}\n${err.stack}`) - process.exit(1) - } -}) -.catch(e => { - error(`Authentication error – ${e.message}`) - process.exit(1) -}) + .then(async token => { + try { + await remove(token); + } catch (err) { + error(`Unknown error: ${err}\n${err.stack}`); + process.exit(1); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + process.exit(1); + }); async function remove(token) { - const now = new Now(apiUrl, token, {debug}) + const now = new Now(apiUrl, token, { debug }); - const deployments = await now.list() + const deployments = await now.list(); const matches = deployments.filter(d => { return ids.find(id => { // Normalize URL by removing slash from the end - if (isURL(id) && id.slice(-1) === '/') { - id = id.slice(0, -1) + if (isURL(id) && id.slice(-1) === "/") { + id = id.slice(0, -1); } // `url` should match the hostname of the deployment - let u = id.replace(/^https:\/\//i, '') + let u = id.replace(/^https:\/\//i, ""); - if (u.indexOf('.') === -1) { + if (u.indexOf(".") === -1) { // `.now.sh` domain is implied if just the subdomain is given - u += '.now.sh' + u += ".now.sh"; } - return d.uid === id || d.name === id || d.url === u - }) - }) + return d.uid === id || d.name === id || d.url === u; + }); + }); if (matches.length === 0) { - error(`Could not find any deployments matching ${ids.map(id => chalk.bold(`"${id}"`)).join(', ')}. Run ${chalk.dim(`\`now ls\``)} to list.`) - return process.exit(1) + error( + `Could not find any deployments matching ${ids + .map(id => chalk.bold(`"${id}"`)) + .join(", ")}. Run ${chalk.dim(`\`now ls\``)} to list.` + ); + return process.exit(1); } - const aliases = await Promise.all(matches.map(depl => now.listAliases(depl.uid))) + const aliases = await Promise.all( + matches.map(depl => now.listAliases(depl.uid)) + ); for (let i = 0; i < matches.length; i++) { - matches[i].aliases = aliases[i] + matches[i].aliases = aliases[i]; } try { if (!skipConfirmation) { - const confirmation = (await readConfirmation(matches)).toLowerCase() + const confirmation = (await readConfirmation(matches)).toLowerCase(); - if (confirmation !== 'y' && confirmation !== 'yes') { - console.log('\n> Aborted') - process.exit(0) + if (confirmation !== "y" && confirmation !== "yes") { + console.log("\n> Aborted"); + process.exit(0); } } - const start = new Date() + const start = new Date(); - await Promise.all(matches.map(depl => now.remove(depl.uid, {hard}))) + await Promise.all(matches.map(depl => now.remove(depl.uid, { hard }))); - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} [${elapsed}]`) - console.log(table(matches.map(depl => { - return [`Deployment ${chalk.bold(depl.uid)} removed`] - }))) + const elapsed = ms(new Date() - start); + console.log(`${chalk.cyan("> Success!")} [${elapsed}]`); + console.log( + table( + matches.map(depl => { + return [`Deployment ${chalk.bold(depl.uid)} removed`]; + }) + ) + ); } catch (err) { - handleError(err) - process.exit(1) + handleError(err); + process.exit(1); } - now.close() + now.close(); } diff --git a/bin/now-secrets.js b/bin/now-secrets.js index a1c211e..a297bda 100755 --- a/bin/now-secrets.js +++ b/bin/now-secrets.js @@ -1,233 +1,268 @@ #!/usr/bin/env node // Packages -const chalk = require('chalk') -const table = require('text-table') -const minimist = require('minimist') -const ms = require('ms') +const chalk = require("chalk"); +const table = require("text-table"); +const minimist = require("minimist"); +const ms = require("ms"); // Ours -const strlen = require('../lib/strlen') -const cfg = require('../lib/cfg') -const {handleError, error} = require('../lib/error') -const NowSecrets = require('../lib/secrets') -const login = require('../lib/login') -const exit = require('../lib/utils/exit') -const logo = require('../lib/utils/output/logo') +const strlen = require("../lib/strlen"); +const cfg = require("../lib/cfg"); +const { handleError, error } = require("../lib/error"); +const NowSecrets = require("../lib/secrets"); +const login = require("../lib/login"); +const exit = require("../lib/utils/exit"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug', 'base64'], + string: ["config", "token"], + boolean: ["help", "debug", "base64"], alias: { - help: 'h', - config: 'c', - debug: 'd', - base64: 'b', - token: 't' + help: "h", + config: "c", + debug: "d", + base64: "b", + token: "t" } -}) +}); -const subcommand = argv._[0] +const subcommand = argv._[0]; // options const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now secrets`)} - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information -b, --base64 Treat value as base64-encoded - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} Lists all your secrets: + ${chalk.gray("–")} Lists all your secrets: - ${chalk.cyan('$ now secrets ls')} + ${chalk.cyan("$ now secrets ls")} - ${chalk.gray('–')} Adds a new secret: + ${chalk.gray("–")} Adds a new secret: ${chalk.cyan('$ now secrets add my-secret "my value"')} - ${chalk.gray('–')} Once added, a secret's value can't be retrieved in plaintext anymore - ${chalk.gray('–')} If the secret's value is more than one word, wrap it in quotes - ${chalk.gray('–')} Actually, when in doubt, wrap your value in quotes + ${chalk.gray("–")} Once added, a secret's value can't be retrieved in plaintext anymore + ${chalk.gray("–")} If the secret's value is more than one word, wrap it in quotes + ${chalk.gray("–")} Actually, when in doubt, wrap your value in quotes - ${chalk.gray('–')} Exposes a secret as an env variable: + ${chalk.gray("–")} Exposes a secret as an env variable: - ${chalk.cyan(`$ now -e MY_SECRET=${chalk.bold('@my-secret')}`)} + ${chalk.cyan(`$ now -e MY_SECRET=${chalk.bold("@my-secret")}`)} - Notice the ${chalk.cyan.bold('`@`')} symbol which makes the value a secret reference. + Notice the ${chalk.cyan.bold("`@`")} symbol which makes the value a secret reference. - ${chalk.gray('–')} Renames a secret: + ${chalk.gray("–")} Renames a secret: ${chalk.cyan(`$ now secrets rename my-secret my-renamed-secret`)} - ${chalk.gray('–')} Removes a secret: + ${chalk.gray("–")} Removes a secret: ${chalk.cyan(`$ now secrets rm my-secret`)} -`) -} +` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } if (argv.help || !subcommand) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - handleError(err) - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + .then(async token => { + try { + await run(token); + } catch (err) { + handleError(err); + exit(1); + } + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } async function run(token) { - const secrets = new NowSecrets(apiUrl, token, {debug}) - const args = argv._.slice(1) - const start = Date.now() + const secrets = new NowSecrets(apiUrl, token, { debug }); + const args = argv._.slice(1); + const start = Date.now(); - if (subcommand === 'ls' || subcommand === 'list') { + if (subcommand === "ls" || subcommand === "list") { if (args.length !== 0) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now secret ls`")}` + ); + return exit(1); } - const list = await secrets.ls() - const elapsed = ms(new Date() - start) + const list = await secrets.ls(); + const elapsed = ms(new Date() - start); - console.log(`> ${list.length} secret${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`) + console.log( + `> ${list.length} secret${list.length === 1 ? "" : "s"} found ${chalk.gray(`[${elapsed}]`)}` + ); if (list.length > 0) { - const cur = Date.now() - const header = [['', 'id', 'name', 'created'].map(s => chalk.dim(s))] - const out = table(header.concat(list.map(secret => { - return [ - '', - secret.uid, - chalk.bold(secret.name), - chalk.gray(ms(cur - new Date(secret.created)) + ' ago') - ] - })), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen}) + const cur = Date.now(); + const header = [["", "id", "name", "created"].map(s => chalk.dim(s))]; + const out = table( + header.concat( + list.map(secret => { + return [ + "", + secret.uid, + chalk.bold(secret.name), + chalk.gray(ms(cur - new Date(secret.created)) + " ago") + ]; + }) + ), + { + align: ["l", "r", "l", "l"], + hsep: " ".repeat(2), + stringLength: strlen + } + ); if (out) { - console.log('\n' + out + '\n') + console.log("\n" + out + "\n"); } } - return secrets.close() + return secrets.close(); } - if (subcommand === 'rm' || subcommand === 'remove') { + if (subcommand === "rm" || subcommand === "remove") { if (args.length !== 1) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now secret rm `")}` + ); + return exit(1); } - const list = await secrets.ls() + const list = await secrets.ls(); const theSecret = list.filter(secret => { - return secret.uid === args[0] || secret.name === args[0] - })[0] + return secret.uid === args[0] || secret.name === args[0]; + })[0]; if (theSecret) { - const yes = await readConfirmation(theSecret) + const yes = await readConfirmation(theSecret); if (!yes) { - error('User abort') - return exit(0) + error("User abort"); + return exit(0); } } else { - error(`No secret found by id or name "${args[0]}"`) - return exit(1) + error(`No secret found by id or name "${args[0]}"`); + return exit(1); } - const secret = await secrets.rm(args[0]) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}`) - return secrets.close() + const secret = await secrets.rm(args[0]); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Secret ${chalk.bold(secret.name)} ${chalk.gray(`(${secret.uid})`)} removed ${chalk.gray(`[${elapsed}]`)}` + ); + return secrets.close(); } - if (subcommand === 'rename') { + if (subcommand === "rename") { if (args.length !== 2) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename `')}`) - return exit(1) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now secret rename `")}` + ); + return exit(1); } - const secret = await secrets.rename(args[0], args[1]) - const elapsed = ms(new Date() - start) - console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(secret.oldName)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}`) - return secrets.close() + const secret = await secrets.rename(args[0], args[1]); + const elapsed = ms(new Date() - start); + console.log( + `${chalk.cyan("> Success!")} Secret ${chalk.bold(secret.oldName)} ${chalk.gray(`(${secret.uid})`)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}` + ); + return secrets.close(); } - if (subcommand === 'add' || subcommand === 'set') { + if (subcommand === "add" || subcommand === "set") { if (args.length !== 2) { - error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret add `')}`) + error( + `Invalid number of arguments. Usage: ${chalk.cyan("`now secret add `")}` + ); if (args.length > 2) { - const example = chalk.cyan(`$ now secret add ${args[0]}`) - console.log(`> If your secret has spaces, make sure to wrap it in quotes. Example: \n ${example} `) + const example = chalk.cyan(`$ now secret add ${args[0]}`); + console.log( + `> If your secret has spaces, make sure to wrap it in quotes. Example: \n ${example} ` + ); } - return exit(1) + return exit(1); } - const [name, value_] = args - let value + const [name, value_] = args; + let value; if (argv.base64) { - value = {base64: value_} + value = { base64: value_ }; } else { - value = value_ + value = value_; } - const secret = await secrets.add(name, value) - const elapsed = ms(new Date() - start) + const secret = await secrets.add(name, value); + const elapsed = ms(new Date() - start); - console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`) - return secrets.close() + console.log( + `${chalk.cyan("> Success!")} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}` + ); + return secrets.close(); } - error('Please specify a valid subcommand: ls | add | rename | rm') - help() - exit(1) + error("Please specify a valid subcommand: ls | add | rename | rm"); + help(); + exit(1); } -process.on('uncaughtException', err => { - handleError(err) - exit(1) -}) +process.on("uncaughtException", err => { + handleError(err); + exit(1); +}); function readConfirmation(secret) { return new Promise(resolve => { - const time = chalk.gray(ms(new Date() - new Date(secret.created)) + ' ago') - const tbl = table( - [[secret.uid, chalk.bold(secret.name), time]], - {align: ['l', 'r', 'l'], hsep: ' '.repeat(6)} - ) - - process.stdout.write('> The following secret will be removed permanently\n') - process.stdout.write(' ' + tbl + '\n') - - process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`) - - process.stdin.on('data', d => { - process.stdin.pause() - resolve(d.toString().trim().toLowerCase() === 'y') - }).resume() - }) + const time = chalk.gray(ms(new Date() - new Date(secret.created)) + " ago"); + const tbl = table([[secret.uid, chalk.bold(secret.name), time]], { + align: ["l", "r", "l"], + hsep: " ".repeat(6) + }); + + process.stdout.write( + "> The following secret will be removed permanently\n" + ); + process.stdout.write(" " + tbl + "\n"); + + process.stdout.write( + `${chalk.bold.red("> Are you sure?")} ${chalk.gray("[y/N] ")}` + ); + + process.stdin + .on("data", d => { + process.stdin.pause(); + resolve(d.toString().trim().toLowerCase() === "y"); + }) + .resume(); + }); } diff --git a/bin/now-upgrade.js b/bin/now-upgrade.js index 9314e32..081ea8c 100644 --- a/bin/now-upgrade.js +++ b/bin/now-upgrade.js @@ -1,67 +1,69 @@ #!/usr/bin/env node // Packages -const chalk = require('chalk') -const minimist = require('minimist') -const ms = require('ms') -const stripAnsi = require('strip-ansi') +const chalk = require("chalk"); +const minimist = require("minimist"); +const ms = require("ms"); +const stripAnsi = require("strip-ansi"); // Ours -const login = require('../lib/login') -const cfg = require('../lib/cfg') -const NowPlans = require('../lib/plans') -const indent = require('../lib/indent') -const listInput = require('../lib/utils/input/list') -const code = require('../lib/utils/output/code') -const error = require('../lib/utils/output/error') -const success = require('../lib/utils/output/success') -const cmd = require('../lib/utils/output/cmd') -const logo = require('../lib/utils/output/logo') +const login = require("../lib/login"); +const cfg = require("../lib/cfg"); +const NowPlans = require("../lib/plans"); +const indent = require("../lib/indent"); +const listInput = require("../lib/utils/input/list"); +const code = require("../lib/utils/output/code"); +const error = require("../lib/utils/output/error"); +const success = require("../lib/utils/output/success"); +const cmd = require("../lib/utils/output/cmd"); +const logo = require("../lib/utils/output/logo"); const argv = minimist(process.argv.slice(2), { - string: ['config', 'token'], - boolean: ['help', 'debug'], + string: ["config", "token"], + boolean: ["help", "debug"], alias: { - help: 'h', - config: 'c', - debug: 'd', - token: 't' + help: "h", + config: "c", + debug: "d", + token: "t" } -}) +}); const help = () => { - console.log(` + console.log( + ` ${chalk.bold(`${logo} now upgrade`)} [plan] - ${chalk.dim('Options:')} + ${chalk.dim("Options:")} -h, --help Output usage information - -c ${chalk.bold.underline('FILE')}, --config=${chalk.bold.underline('FILE')} Config file + -c ${chalk.bold.underline("FILE")}, --config=${chalk.bold.underline("FILE")} Config file -d, --debug Debug mode [off] - -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline('TOKEN')} Login token + -t ${chalk.bold.underline("TOKEN")}, --token=${chalk.bold.underline("TOKEN")} Login token - ${chalk.dim('Examples:')} + ${chalk.dim("Examples:")} - ${chalk.gray('–')} List available plans and pick one interactively + ${chalk.gray("–")} List available plans and pick one interactively - ${chalk.cyan('$ now upgrade')} + ${chalk.cyan("$ now upgrade")} - ${chalk.yellow('NOTE:')} ${chalk.gray('Make sure you have a payment method, or add one:')} + ${chalk.yellow("NOTE:")} ${chalk.gray("Make sure you have a payment method, or add one:")} ${chalk.cyan(`$ now billing add`)} - ${chalk.gray('–')} Pick a specific plan (premium): + ${chalk.gray("–")} Pick a specific plan (premium): ${chalk.cyan(`$ now upgrade premium`)} - `) -} + ` + ); +}; // options -const debug = argv.debug -const apiUrl = argv.url || 'https://api.zeit.co' +const debug = argv.debug; +const apiUrl = argv.url || "https://api.zeit.co"; if (argv.config) { - cfg.setConfigFile(argv.config) + cfg.setConfigFile(argv.config); } const exit = code => { @@ -69,141 +71,150 @@ const exit = code => { // because there's a node bug where // stdout writes are asynchronous // https://github.com/nodejs/node/issues/6456 - setTimeout(() => process.exit(code || 0), 100) -} + setTimeout(() => process.exit(code || 0), 100); +}; if (argv.help) { - help() - exit(0) + help(); + exit(0); } else { - const config = cfg.read() + const config = cfg.read(); Promise.resolve(argv.token || config.token || login(apiUrl)) - .then(async token => { - try { - await run(token) - } catch (err) { - if (err.userError) { - error(err.message) - } else { - error(`Unknown error: ${err.stack}`) + .then(async token => { + try { + await run(token); + } catch (err) { + if (err.userError) { + error(err.message); + } else { + error(`Unknown error: ${err.stack}`); + } + exit(1); } - exit(1) - } - }) - .catch(e => { - error(`Authentication error – ${e.message}`) - exit(1) - }) + }) + .catch(e => { + error(`Authentication error – ${e.message}`); + exit(1); + }); } function buildInquirerChoices(current, until) { if (until) { - until = until.split(' ') - until = ' for ' + chalk.bold(until[0]) + ' more ' + until[1] + until = until.split(" "); + until = " for " + chalk.bold(until[0]) + " more " + until[1]; } else { - until = '' + until = ""; } - const ossTitle = current === 'oss' ? - `oss FREE ${' '.repeat(28)} (current)` : - 'oss FREE' - const premiumTitle = current === 'premium' ? - `premium $15/mo ${' '.repeat(24 - stripAnsi(until).length)} (current${until})` : - 'premium $15/mo' + const ossTitle = current === "oss" + ? `oss FREE ${" ".repeat(28)} (current)` + : "oss FREE"; + const premiumTitle = current === "premium" + ? `premium $15/mo ${" ".repeat(24 - stripAnsi(until).length)} (current${until})` + : "premium $15/mo"; return [ { name: [ ossTitle, - indent('✓ All code is public and open-source', 2), - indent('✓ 20 deploys per month | 1GB monthly bandwidth', 2), - indent('✓ 1GB FREE storage | 1MB size limit per file', 2) - ].join('\n'), - value: 'oss', - short: 'oss FREE' + indent("✓ All code is public and open-source", 2), + indent("✓ 20 deploys per month | 1GB monthly bandwidth", 2), + indent("✓ 1GB FREE storage | 1MB size limit per file", 2) + ].join("\n"), + value: "oss", + short: "oss FREE" }, { name: [ premiumTitle, - indent('✓ All code is private and secure', 2), - indent('✓ 1000 deploys per month | 50GB monthly bandwidth', 2), - indent('✓ 100GB storage | No filesize limit', 2) - ].join('\n'), - value: 'premium', - short: 'premium $15/mo' + indent("✓ All code is private and secure", 2), + indent("✓ 1000 deploys per month | 50GB monthly bandwidth", 2), + indent("✓ 100GB storage | No filesize limit", 2) + ].join("\n"), + value: "premium", + short: "premium $15/mo" } - ] + ]; } async function run(token) { - const args = argv._ + const args = argv._; if (args.length > 1) { - error('Invalid number of arguments') - return exit(1) + error("Invalid number of arguments"); + return exit(1); } - const start = new Date() - const plans = new NowPlans(apiUrl, token, {debug}) + const start = new Date(); + const plans = new NowPlans(apiUrl, token, { debug }); - let planId = args[0] + let planId = args[0]; - if (![undefined, 'oss', 'premium'].includes(planId)) { - error(`Invalid plan name – should be ${code('oss')} or ${code('premium')}`) - return exit(1) + if (![undefined, "oss", "premium"].includes(planId)) { + error(`Invalid plan name – should be ${code("oss")} or ${code("premium")}`); + return exit(1); } - const currentPlan = await plans.getCurrent() + const currentPlan = await plans.getCurrent(); if (planId === undefined) { - const elapsed = ms(new Date() - start) + const elapsed = ms(new Date() - start); - let message = `To manage this from the web UI, head to https://zeit.co/account\n` - message += `> Selecting a plan for your account ${chalk.gray(`[${elapsed}]`)}` - const choices = buildInquirerChoices(currentPlan.id, currentPlan.until) + let message = `To manage this from the web UI, head to https://zeit.co/account\n`; + message += `> Selecting a plan for your account ${chalk.gray(`[${elapsed}]`)}`; + const choices = buildInquirerChoices(currentPlan.id, currentPlan.until); planId = await listInput({ message, choices, separator: true, - abort: 'end' - }) + abort: "end" + }); } - if (planId === undefined || (planId === currentPlan.id && currentPlan.until === undefined)) { - return console.log('No changes made') + if ( + planId === undefined || + (planId === currentPlan.id && currentPlan.until === undefined) + ) { + return console.log("No changes made"); } - let newPlan + let newPlan; try { - newPlan = await plans.set(planId) + newPlan = await plans.set(planId); } catch (err) { - let errorBody + let errorBody; if (err.res && err.res.status === 400) { - errorBody = err.res.json() + errorBody = err.res.json(); } else { - const message = 'A network error has occurred. Please retry.' - errorBody = {message} + const message = "A network error has occurred. Please retry."; + errorBody = { message }; } - const _err = (await errorBody).error - const {code, message} = _err + const _err = (await errorBody).error; + const { code, message } = _err; - if (code === 'customer_not_found' || code === 'source_not_found') { - error(`You have no payment methods available. Run ${cmd('now billing add')} to add one`) + if (code === "customer_not_found" || code === "source_not_found") { + 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 ${message}`) + error(`An unknow error occured. Please try again later ${message}`); } - plans.close() - return + plans.close(); + return; } - if (currentPlan.until && newPlan.id === 'premium') { - success(`The cancelation has been undone. You're back on the ${chalk.bold('Premium plan')}`) + if (currentPlan.until && newPlan.id === "premium") { + success( + `The cancelation has been undone. You're back on the ${chalk.bold("Premium plan")}` + ); } else if (newPlan.until) { - success(`Your plan will be switched to OSS in ${chalk.bold(newPlan.until)}. Your card will not be charged again`) + success( + `Your plan will be switched to OSS in ${chalk.bold(newPlan.until)}. Your card will not be charged again` + ); } else { - success(`You're now on the ${chalk.bold('Premium plan')}`) + success(`You're now on the ${chalk.bold("Premium plan")}`); } - plans.close() + plans.close(); } diff --git a/bin/now.js b/bin/now.js index 362e494..98c66ed 100755 --- a/bin/now.js +++ b/bin/now.js @@ -1,102 +1,102 @@ #!/usr/bin/env node // Native -const {resolve} = require('path') +const { resolve } = require("path"); // Packages -const nodeVersion = require('node-version') -const updateNotifier = require('update-notifier') -const chalk = require('chalk') +const nodeVersion = require("node-version"); +const updateNotifier = require("update-notifier"); +const chalk = require("chalk"); // Ours -const {error} = require('../lib/error') -const pkg = require('../lib/pkg') +const { error } = require("../lib/error"); +const pkg = require("../lib/pkg"); // Throw an error if node version is too low if (nodeVersion.major < 6) { - error('Now requires at least version 6 of Node. Please upgrade!') - process.exit(1) + error("Now requires at least version 6 of Node. Please upgrade!"); + process.exit(1); } if (!process.pkg) { - const notifier = updateNotifier({pkg}) - const update = notifier.update + const notifier = updateNotifier({ pkg }); + const update = notifier.update; if (update) { - let message = `Update available! ${chalk.red(update.current)} → ${chalk.green(update.latest)} \n` - message += `Run ${chalk.magenta('npm i -g now')} to update!\n` - message += `${chalk.magenta('Changelog:')} https://github.com/zeit/now-cli/releases/tag/${update.latest}` + let message = `Update available! ${chalk.red(update.current)} → ${chalk.green(update.latest)} \n`; + message += `Run ${chalk.magenta("npm i -g now")} to update!\n`; + message += `${chalk.magenta("Changelog:")} https://github.com/zeit/now-cli/releases/tag/${update.latest}`; - notifier.notify({message}) + notifier.notify({ message }); } } // This command will be run if no other sub command is specified -const defaultCommand = 'deploy' +const defaultCommand = "deploy"; const commands = new Set([ defaultCommand, - 'help', - 'list', - 'ls', - 'rm', - 'remove', - 'alias', - 'aliases', - 'ln', - 'domain', - 'domains', - 'dns', - 'cert', - 'certs', - 'secret', - 'secrets', - 'cc', - 'billing', - 'upgrade', - 'downgrade', - 'open' -]) + "help", + "list", + "ls", + "rm", + "remove", + "alias", + "aliases", + "ln", + "domain", + "domains", + "dns", + "cert", + "certs", + "secret", + "secrets", + "cc", + "billing", + "upgrade", + "downgrade", + "open" +]); const aliases = new Map([ - ['ls', 'list'], - ['rm', 'remove'], - ['ln', 'alias'], - ['aliases', 'alias'], - ['domain', 'domains'], - ['cert', 'certs'], - ['secret', 'secrets'], - ['cc', 'billing'], - ['downgrade', 'upgrade'] -]) - -let cmd = defaultCommand -const args = process.argv.slice(2) -const index = args.findIndex(a => commands.has(a)) + ["ls", "list"], + ["rm", "remove"], + ["ln", "alias"], + ["aliases", "alias"], + ["domain", "domains"], + ["cert", "certs"], + ["secret", "secrets"], + ["cc", "billing"], + ["downgrade", "upgrade"] +]); + +let cmd = defaultCommand; +const args = process.argv.slice(2); +const index = args.findIndex(a => commands.has(a)); if (index > -1) { - cmd = args[index] - args.splice(index, 1) + cmd = args[index]; + args.splice(index, 1); - if (cmd === 'help') { + if (cmd === "help") { if (index < args.length && commands.has(args[index])) { - cmd = args[index] - args.splice(index, 1) + cmd = args[index]; + args.splice(index, 1); } else { - cmd = defaultCommand + cmd = defaultCommand; } - args.unshift('--help') + args.unshift("--help"); } - cmd = aliases.get(cmd) || cmd + cmd = aliases.get(cmd) || cmd; } -const bin = resolve(__dirname, 'now-' + cmd + '.js') +const bin = resolve(__dirname, "now-" + cmd + ".js"); // Prepare process.argv for subcommand -process.argv = process.argv.slice(0, 2).concat(args) +process.argv = process.argv.slice(0, 2).concat(args); // Load sub command // With custom parameter to make "pkg" happy -require(bin, 'may-exclude') +require(bin, "may-exclude"); diff --git a/lib/agent.js b/lib/agent.js index f44b432..26ce492 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -1,10 +1,10 @@ // Native -const {parse} = require('url') -const http = require('http') -const https = require('https') +const { parse } = require("url"); +const http = require("http"); +const https = require("https"); // Packages -const fetch = require('node-fetch') +const fetch = require("node-fetch"); /** * Returns a `fetch` version with a similar @@ -18,66 +18,66 @@ const fetch = require('node-fetch') */ module.exports = class Agent { - constructor(url, {tls = true, debug} = {}) { - this._url = url - const parsed = parse(url) - this._protocol = parsed.protocol - this._debug = debug + constructor(url, { tls = true, debug } = {}) { + this._url = url; + const parsed = parse(url); + this._protocol = parsed.protocol; + this._debug = debug; if (tls) { - this._initAgent() + this._initAgent(); } } _initAgent() { - const module = this._protocol === 'https:' ? https : http - const agent = this._agent = new module.Agent({ + const module = this._protocol === "https:" ? https : http; + const agent = (this._agent = new module.Agent({ keepAlive: true, keepAliveMsecs: 10000, maxSockets: 8 - }).on('error', err => this._onError(err, agent)) + }).on("error", err => this._onError(err, agent))); } _onError(err, agent) { if (this._debug) { - console.log(`> [debug] agent connection error ${err}\n${err.stack}`) + console.log(`> [debug] agent connection error ${err}\n${err.stack}`); } if (this._agent === agent) { - this._agent = null + this._agent = null; } } fetch(path, opts = {}) { if (!this._agent) { if (this._debug) { - console.log('> [debug] re-initializing agent') + console.log("> [debug] re-initializing agent"); } - this._initAgent() + this._initAgent(); } - const {body} = opts + const { body } = opts; if (this._agent) { - opts.agent = this._agent + opts.agent = this._agent; } - if (body && typeof body === 'object' && typeof body.pipe !== 'function') { - opts.headers['Content-Type'] = 'application/json' - opts.body = JSON.stringify(body) + if (body && typeof body === "object" && typeof body.pipe !== "function") { + opts.headers["Content-Type"] = "application/json"; + opts.body = JSON.stringify(body); } - if (opts.body && typeof body.pipe !== 'function') { - opts.headers['Content-Length'] = Buffer.byteLength(opts.body) + if (opts.body && typeof body.pipe !== "function") { + opts.headers["Content-Length"] = Buffer.byteLength(opts.body); } - return fetch(this._url + path, opts) + return fetch(this._url + path, opts); } close() { if (this._debug) { - console.log('> [debug] closing agent') + console.log("> [debug] closing agent"); } if (this._agent) { - this._agent.destroy() + this._agent.destroy(); } } -} +}; diff --git a/lib/alias.js b/lib/alias.js index 4dd021f..aa74b1c 100644 --- a/lib/alias.js +++ b/lib/alias.js @@ -1,462 +1,513 @@ // Packages -const {readFileSync} = require('fs') -const publicSuffixList = require('psl') -const minimist = require('minimist') -const chalk = require('chalk') +const { readFileSync } = require("fs"); +const publicSuffixList = require("psl"); +const minimist = require("minimist"); +const chalk = require("chalk"); // Ours -const promptBool = require('../lib/utils/input/prompt-bool') -const exit = require('./utils/exit') -const copy = require('./copy') -const toHost = require('./to-host') -const resolve4 = require('./dns') -const isZeitWorld = require('./is-zeit-world') -const {DOMAIN_VERIFICATION_ERROR} = require('./errors') -const Now = require('./') +const promptBool = require("../lib/utils/input/prompt-bool"); +const exit = require("./utils/exit"); +const copy = require("./copy"); +const toHost = require("./to-host"); +const resolve4 = require("./dns"); +const isZeitWorld = require("./is-zeit-world"); +const { DOMAIN_VERIFICATION_ERROR } = require("./errors"); +const Now = require("./"); const argv = minimist(process.argv.slice(2), { - boolean: ['no-clipboard'], - alias: {'no-clipboard': 'C'} -}) + boolean: ["no-clipboard"], + alias: { "no-clipboard": "C" } +}); -const isTTY = process.stdout.isTTY -const clipboard = !argv['no-clipboard'] -const domainRegex = /^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/ +const isTTY = process.stdout.isTTY; +const clipboard = !argv["no-clipboard"]; +const domainRegex = /^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/; module.exports = class Alias extends Now { - async ls(deployment) { if (deployment) { - const target = await this.findDeployment(deployment) + const target = await this.findDeployment(deployment); if (!target) { - const err = new Error(`Aliases not found by "${deployment}". Run ${chalk.dim('`now alias ls`')} to see your aliases.`) - err.userError = true - throw err + const err = new Error( + `Aliases not found by "${deployment}". Run ${chalk.dim("`now alias ls`")} to see your aliases.` + ); + err.userError = true; + throw err; } - return this.listAliases(target.uid) + return this.listAliases(target.uid); } - return this.listAliases() + return this.listAliases(); } async rm(_alias) { return this.retry(async bail => { const res = await this._fetch(`/now/aliases/${_alias.uid}`, { - method: 'DELETE' - }) + method: "DELETE" + }); if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } if (res.status !== 200) { - const err = new Error('Deletion failed. Try again later.') - throw err + const err = new Error("Deletion failed. Try again later."); + throw err; } - }) + }); } async findDeployment(deployment) { - const list = await this.list() + const list = await this.list(); - let key - let val + let key; + let val; if (/\./.test(deployment)) { - val = toHost(deployment) - key = 'url' + val = toHost(deployment); + key = "url"; } else { - val = deployment - key = 'uid' + val = deployment; + key = "uid"; } const depl = list.find(d => { if (d[key] === val) { if (this._debug) { - console.log(`> [debug] matched deployment ${d.uid} by ${key} ${val}`) + console.log(`> [debug] matched deployment ${d.uid} by ${key} ${val}`); } - return true + return true; } // match prefix if (`${val}.now.sh` === d.url) { if (this._debug) { - console.log(`> [debug] matched deployment ${d.uid} by url ${d.url}`) + console.log(`> [debug] matched deployment ${d.uid} by url ${d.url}`); } - return true + return true; } - return false - }) + return false; + }); - return depl + return depl; } async updatePathBasedroutes(alias, rules) { - alias = await this.maybeSetUpDomain(alias) + alias = await this.maybeSetUpDomain(alias); - return await this.upsertPathAlias(alias, rules) + return await this.upsertPathAlias(alias, rules); } async upsertPathAlias(alias, rules) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] /now/aliases #${attempt}`) + console.time(`> [debug] /now/aliases #${attempt}`); } - const rulesData = this.readRulesFile(rules) - const ruleCount = rulesData.rules.length + const rulesData = this.readRulesFile(rules); + const ruleCount = rulesData.rules.length; const res = await this._fetch(`/now/aliases`, { - method: 'POST', - body: {alias, rules: rulesData.rules} - }) + method: "POST", + body: { alias, rules: rulesData.rules } + }); - const body = await res.json() - body.ruleCount = ruleCount + const body = await res.json(); + body.ruleCount = ruleCount; if (this._debug) { - console.timeEnd(`> [debug] /now/aliases #${attempt}`) + console.timeEnd(`> [debug] /now/aliases #${attempt}`); } // 409 conflict is returned if it already exists if (res.status === 409) { - return {uid: body.error.uid} + return { uid: body.error.uid }; } if (res.status === 422) { - return body + return body; } // no retry on authorization problems if (res.status === 403) { - const code = body.error.code - - if (code === 'custom_domain_needs_upgrade') { - const err = new Error(`Custom domains are only enabled for premium accounts. Please upgrade by running ${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}.`) - err.userError = true - return bail(err) + const code = body.error.code; + + if (code === "custom_domain_needs_upgrade") { + const err = new Error( + `Custom domains are only enabled for premium accounts. Please upgrade by running ${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")}.` + ); + err.userError = true; + return bail(err); } - if (code === 'alias_in_use') { - const err = new Error(`The alias you are trying to configure (${chalk.underline(chalk.bold(alias))}) is already in use by a different account.`) - err.userError = true - return bail(err) + if (code === "alias_in_use") { + const err = new Error( + `The alias you are trying to configure (${chalk.underline(chalk.bold(alias))}) is already in use by a different account.` + ); + err.userError = true; + return bail(err); } - if (code === 'forbidden') { - const err = new Error('The domain you are trying to use as an alias is already in use by a different account.') - err.userError = true - return bail(err) + if (code === "forbidden") { + const err = new Error( + "The domain you are trying to use as an alias is already in use by a different account." + ); + err.userError = true; + return bail(err); } - return bail(new Error('Authorization error')) + return bail(new Error("Authorization error")); } // all other errors if (body.error) { - const code = body.error.code + const code = body.error.code; - if (code === 'cert_missing') { - console.log(`> Provisioning certificate for ${chalk.underline(chalk.bold(alias))}`) + if (code === "cert_missing") { + console.log( + `> Provisioning certificate for ${chalk.underline(chalk.bold(alias))}` + ); try { - await this.createCert(alias) + await this.createCert(alias); } catch (err) { // we bail to avoid retrying the whole process // of aliasing which would involve too many // retries on certificate provisioning - return bail(err) + return bail(err); } // try again, but now having provisioned the certificate - return this.upsertPathAlias(alias, rules) + return this.upsertPathAlias(alias, rules); } - if (code === 'cert_expired') { - console.log(`> Renewing certificate for ${chalk.underline(chalk.bold(alias))}`) + if (code === "cert_expired") { + console.log( + `> Renewing certificate for ${chalk.underline(chalk.bold(alias))}` + ); try { - await this.createCert(alias, {renew: true}) + await this.createCert(alias, { renew: true }); } catch (err) { - return bail(err) + return bail(err); } } - return bail(new Error(body.error.message)) + return bail(new Error(body.error.message)); } // the two expected succesful cods are 200 and 304 if (res.status !== 200 && res.status !== 304) { - throw new Error('Unhandled error') + throw new Error("Unhandled error"); } - return body - }) + return body; + }); } readRulesFile(rules) { try { - const rulesJson = readFileSync(rules, 'utf8') - return JSON.parse(rulesJson) + const rulesJson = readFileSync(rules, "utf8"); + return JSON.parse(rulesJson); } catch (err) { - console.error(`Reading rules file ${rules} failed: ${err}`) + console.error(`Reading rules file ${rules} failed: ${err}`); } } async set(deployment, alias) { - const depl = await this.findDeployment(deployment) + const depl = await this.findDeployment(deployment); if (!depl) { - const err = new Error(`Deployment not found by "${deployment}". Run ${chalk.dim('`now ls`')} to see your deployments.`) - err.userError = true - throw err + const err = new Error( + `Deployment not found by "${deployment}". Run ${chalk.dim("`now ls`")} to see your deployments.` + ); + err.userError = true; + throw err; } - const aliasDepl = (await this.listAliases()).find(e => e.alias === alias) + const aliasDepl = (await this.listAliases()).find(e => e.alias === alias); if (aliasDepl && aliasDepl.rules) { if (isTTY) { try { - const msg = `> Path alias excists with ${aliasDepl.rules.length} rule${aliasDepl.rules.length > 1 ? 's' : ''}.\n` + - `> Are you sure you want to update ${alias} to be a normal alias?\n` + const msg = `> Path alias excists with ${aliasDepl.rules.length} rule${aliasDepl.rules.length > 1 ? "s" : ""}.\n` + + `> Are you sure you want to update ${alias} to be a normal alias?\n`; - const confirmation = await promptBool(msg) + const confirmation = await promptBool(msg); if (!confirmation) { - console.log('\n> Aborted') - return exit(1) + console.log("\n> Aborted"); + return exit(1); } } catch (err) { - console.log(err) + console.log(err); } } else { - console.log(`Overwriting path alias with ${aliasDepl.rules.length} rule${aliasDepl.rules.length > 1 ? 's' : ''} to be a normal alias.`) + console.log( + `Overwriting path alias with ${aliasDepl.rules.length} rule${aliasDepl.rules.length > 1 ? "s" : ""} to be a normal alias.` + ); } } - alias = await this.maybeSetUpDomain(alias) + alias = await this.maybeSetUpDomain(alias); - const newAlias = await this.createAlias(depl, alias) + const newAlias = await this.createAlias(depl, alias); if (!newAlias) { - throw new Error(`Unexpected error occurred while setting up alias: ${JSON.stringify(newAlias)}`) + throw new Error( + `Unexpected error occurred while setting up alias: ${JSON.stringify(newAlias)}` + ); } - const {created, uid} = newAlias + const { created, uid } = newAlias; if (created) { - const pretty = `https://${alias}` - const output = `${chalk.cyan('> Success!')} Alias created ${chalk.dim(`(${uid})`)}:\n${chalk.bold(chalk.underline(pretty))} now points to ${chalk.bold(`https://${depl.url}`)} ${chalk.dim(`(${depl.uid})`)}` + const pretty = `https://${alias}`; + const output = `${chalk.cyan("> Success!")} Alias created ${chalk.dim(`(${uid})`)}:\n${chalk.bold(chalk.underline(pretty))} now points to ${chalk.bold(`https://${depl.url}`)} ${chalk.dim(`(${depl.uid})`)}`; if (isTTY && clipboard) { - let append + let append; try { - await copy(pretty) - append = '[copied to clipboard]' + await copy(pretty); + append = "[copied to clipboard]"; } catch (err) { - append = '' + append = ""; } finally { - console.log(`${output} ${append}`) + console.log(`${output} ${append}`); } } else { - console.log(output) + console.log(output); } } else { - console.log(`${chalk.cyan('> Success!')} Alias already exists ${chalk.dim(`(${uid})`)}.`) + console.log( + `${chalk.cyan("> Success!")} Alias already exists ${chalk.dim(`(${uid})`)}.` + ); } } createAlias(depl, alias) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] /now/deployments/${depl.uid}/aliases #${attempt}`) + console.time( + `> [debug] /now/deployments/${depl.uid}/aliases #${attempt}` + ); } const res = await this._fetch(`/now/deployments/${depl.uid}/aliases`, { - method: 'POST', - body: {alias} - }) + method: "POST", + body: { alias } + }); - const body = await res.json() + const body = await res.json(); if (this._debug) { - console.timeEnd(`> [debug] /now/deployments/${depl.uid}/aliases #${attempt}`) + console.timeEnd( + `> [debug] /now/deployments/${depl.uid}/aliases #${attempt}` + ); } // 409 conflict is returned if it already exists if (res.status === 409) { - return {uid: body.error.uid} + return { uid: body.error.uid }; } // no retry on authorization problems if (res.status === 403) { - const code = body.error.code - - if (code === 'custom_domain_needs_upgrade') { - const err = new Error(`Custom domains are only enabled for premium accounts. Please upgrade by running ${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}.`) - err.userError = true - return bail(err) + const code = body.error.code; + + if (code === "custom_domain_needs_upgrade") { + const err = new Error( + `Custom domains are only enabled for premium accounts. Please upgrade by running ${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")}.` + ); + err.userError = true; + return bail(err); } - if (code === 'alias_in_use') { - const err = new Error(`The alias you are trying to configure (${chalk.underline(chalk.bold(alias))}) is already in use by a different account.`) - err.userError = true - return bail(err) + if (code === "alias_in_use") { + const err = new Error( + `The alias you are trying to configure (${chalk.underline(chalk.bold(alias))}) is already in use by a different account.` + ); + err.userError = true; + return bail(err); } - if (code === 'forbidden') { - const err = new Error('The domain you are trying to use as an alias is already in use by a different account.') - err.userError = true - return bail(err) + if (code === "forbidden") { + const err = new Error( + "The domain you are trying to use as an alias is already in use by a different account." + ); + err.userError = true; + return bail(err); } - return bail(new Error('Authorization error')) + return bail(new Error("Authorization error")); } // all other errors if (body.error) { - const code = body.error.code + const code = body.error.code; - if (code === 'deployment_not_found') { - return bail(new Error('Deployment not found')) + if (code === "deployment_not_found") { + return bail(new Error("Deployment not found")); } - if (code === 'cert_missing') { - console.log(`> Provisioning certificate for ${chalk.underline(chalk.bold(alias))}`) + if (code === "cert_missing") { + console.log( + `> Provisioning certificate for ${chalk.underline(chalk.bold(alias))}` + ); try { - await this.createCert(alias) + await this.createCert(alias); } catch (err) { // we bail to avoid retrying the whole process // of aliasing which would involve too many // retries on certificate provisioning - return bail(err) + return bail(err); } // try again, but now having provisioned the certificate - return this.createAlias(depl, alias) + return this.createAlias(depl, alias); } - if (code === 'cert_expired') { - console.log(`> Renewing certificate for ${chalk.underline(chalk.bold(alias))}`) + if (code === "cert_expired") { + console.log( + `> Renewing certificate for ${chalk.underline(chalk.bold(alias))}` + ); try { - await this.createCert(alias, {renew: true}) + await this.createCert(alias, { renew: true }); } catch (err) { - return bail(err) + return bail(err); } } - return bail(new Error(body.error.message)) + return bail(new Error(body.error.message)); } // the two expected succesful cods are 200 and 304 if (res.status !== 200 && res.status !== 304) { - throw new Error('Unhandled error') + throw new Error("Unhandled error"); } - return body - }) + return body; + }); } async setupRecord(domain, name) { - await this.setupDomain(domain) + await this.setupDomain(domain); if (this._debug) { - console.log(`> [debug] Setting up record "${name}" for "${domain}"`) + console.log(`> [debug] Setting up record "${name}" for "${domain}"`); } - const type = name === '' ? 'ALIAS' : 'CNAME' + const type = name === "" ? "ALIAS" : "CNAME"; return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] /domains/${domain}/records #${attempt}`) + console.time(`> [debug] /domains/${domain}/records #${attempt}`); } const res = await this._fetch(`/domains/${domain}/records`, { - method: 'POST', + method: "POST", body: { type, - name: name === '' ? name : '*', - value: 'alias.zeit.co' + name: name === "" ? name : "*", + value: "alias.zeit.co" } - }) + }); if (this._debug) { - console.timeEnd(`> [debug] /domains/${domain}/records #${attempt}`) + console.timeEnd(`> [debug] /domains/${domain}/records #${attempt}`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } - const body = await res.json() + const body = await res.json(); if (res.status !== 200) { - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } async maybeSetUpDomain(alias) { // make alias lowercase - alias = alias.toLowerCase() + alias = alias.toLowerCase(); // trim leading and trailing dots // for example: `google.com.` => `google.com` - alias = alias - .replace(/^\.+/, '') - .replace(/\.+$/, '') + alias = alias.replace(/^\.+/, "").replace(/\.+$/, ""); // evaluate the alias if (/\./.test(alias)) { - alias = toHost(alias) + alias = toHost(alias); } else { if (this._debug) { - console.log(`> [debug] suffixing \`.now.sh\` to alias ${alias}`) + console.log(`> [debug] suffixing \`.now.sh\` to alias ${alias}`); } - alias = `${alias}.now.sh` + alias = `${alias}.now.sh`; } if (!domainRegex.test(alias)) { - const err = new Error(`Invalid alias "${alias}"`) - err.userError = true - throw err + const err = new Error(`Invalid alias "${alias}"`); + err.userError = true; + throw err; } if (!/\.now\.sh$/.test(alias)) { - console.log(`> ${chalk.bold(chalk.underline(alias))} is a custom domain.`) - console.log(`> Verifying the DNS settings for ${chalk.bold(chalk.underline(alias))} (see ${chalk.underline('https://zeit.world')} for help)`) - - const _domain = publicSuffixList.parse(alias).domain - const _domainInfo = await this.getDomain(_domain) - const domainInfo = _domainInfo && !_domainInfo.error ? _domainInfo : undefined - const {domain, nameservers} = domainInfo ? {domain: _domain} : await this.getNameservers(alias) - const usingZeitWorld = domainInfo ? !domainInfo.isExternal : isZeitWorld(nameservers) - let skipDNSVerification = false + console.log( + `> ${chalk.bold(chalk.underline(alias))} is a custom domain.` + ); + console.log( + `> Verifying the DNS settings for ${chalk.bold(chalk.underline(alias))} (see ${chalk.underline("https://zeit.world")} for help)` + ); + + const _domain = publicSuffixList.parse(alias).domain; + const _domainInfo = await this.getDomain(_domain); + const domainInfo = _domainInfo && !_domainInfo.error + ? _domainInfo + : undefined; + const { domain, nameservers } = domainInfo + ? { domain: _domain } + : await this.getNameservers(alias); + const usingZeitWorld = domainInfo + ? !domainInfo.isExternal + : isZeitWorld(nameservers); + let skipDNSVerification = false; if (this._debug) { if (domainInfo) { - console.log(`> [debug] Found domain ${domain} with verified:${domainInfo.verified}`) + console.log( + `> [debug] Found domain ${domain} with verified:${domainInfo.verified}` + ); } else { - console.log(`> [debug] Found domain ${domain} and nameservers ${nameservers}`) + console.log( + `> [debug] Found domain ${domain} and nameservers ${nameservers}` + ); } } if (!usingZeitWorld && domainInfo) { if (domainInfo.verified) { - skipDNSVerification = true + skipDNSVerification = true; } else if (domainInfo.uid) { - const {verified, created} = await this.setupDomain(domain, {isExternal: true}) + const { verified, created } = await this.setupDomain(domain, { + isExternal: true + }); if (!(created && verified)) { - const e = new Error(`> Failed to verify the ownership of ${domain}, please refer to 'now domain --help'.`) - e.userError = true - throw e + const e = new Error( + `> Failed to verify the ownership of ${domain}, please refer to 'now domain --help'.` + ); + e.userError = true; + throw e; } - console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(domain))} verified`) + console.log( + `${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(domain))} verified` + ); } } try { if (!skipDNSVerification) { - await this.verifyOwnership(alias) + await this.verifyOwnership(alias); } } catch (err) { if (err.userError) { @@ -465,110 +516,134 @@ module.exports = class Alias extends Now { // configuration (if we can!) try { if (usingZeitWorld) { - console.log(`> Detected ${chalk.bold(chalk.underline('zeit.world'))} nameservers! Configuring records.`) - const record = alias.substr(0, alias.length - domain.length) + console.log( + `> Detected ${chalk.bold(chalk.underline("zeit.world"))} nameservers! Configuring records.` + ); + const record = alias.substr(0, alias.length - domain.length); // lean up trailing and leading dots - const _record = record - .replace(/^\./, '') - .replace(/\.$/, '') - const _domain = domain - .replace(/^\./, '') - .replace(/\.$/, '') - - if (_record === '') { - await this.setupRecord(_domain, '*') + const _record = record.replace(/^\./, "").replace(/\.$/, ""); + const _domain = domain.replace(/^\./, "").replace(/\.$/, ""); + + if (_record === "") { + await this.setupRecord(_domain, "*"); } - await this.setupRecord(_domain, _record) + await this.setupRecord(_domain, _record); - this.recordSetup = true - console.log('> DNS Configured! Verifying propagation…') + this.recordSetup = true; + console.log("> DNS Configured! Verifying propagation…"); try { - await this.retry(() => this.verifyOwnership(alias), {retries: 10, maxTimeout: 8000}) + await this.retry(() => this.verifyOwnership(alias), { + retries: 10, + maxTimeout: 8000 + }); } catch (err2) { - const e = new Error('> We configured the DNS settings for your alias, but we were unable to ' + - 'verify that they\'ve propagated. Please try the alias again later.') - e.userError = true - throw e + const e = new Error( + "> We configured the DNS settings for your alias, but we were unable to " + + "verify that they've propagated. Please try the alias again later." + ); + e.userError = true; + throw e; } } else { - console.log(`> Resolved IP: ${err.ip ? `${chalk.underline(err.ip)} (unknown)` : chalk.dim('none')}`) - console.log(`> Nameservers: ${nameservers && nameservers.length ? nameservers.map(ns => chalk.underline(ns)).join(', ') : chalk.dim('none')}`) - throw err + console.log( + `> Resolved IP: ${err.ip ? `${chalk.underline(err.ip)} (unknown)` : chalk.dim("none")}` + ); + console.log( + `> Nameservers: ${nameservers && nameservers.length ? nameservers + .map(ns => chalk.underline(ns)) + .join(", ") : chalk.dim("none")}` + ); + throw err; } } catch (e) { if (e.userError) { - throw e + throw e; } - throw err + throw err; } } else { - throw err + throw err; } } if (!usingZeitWorld && !skipDNSVerification) { if (this._debug) { - console.log(`> [debug] Trying to register a non-ZeitWorld domain ${domain} for the current user`) + console.log( + `> [debug] Trying to register a non-ZeitWorld domain ${domain} for the current user` + ); } - const {uid, verified, created} = await this.setupDomain(domain, {isExternal: true}) + const { uid, verified, created } = await this.setupDomain(domain, { + isExternal: true + }); if (!(created && verified)) { - const e = new Error(`> Failed to verify the ownership of ${domain}, please refer to 'now domain --help'.`) - e.userError = true - throw e + const e = new Error( + `> Failed to verify the ownership of ${domain}, please refer to 'now domain --help'.` + ); + e.userError = true; + throw e; } - console.log(`${chalk.cyan('> Success!')} Domain ${chalk.bold(chalk.underline(domain))} ${chalk.dim(`(${uid})`)} added`) + console.log( + `${chalk.cyan("> Success!")} Domain ${chalk.bold(chalk.underline(domain))} ${chalk.dim(`(${uid})`)} added` + ); } - console.log(`> Verification ${chalk.bold('OK')}!`) + console.log(`> Verification ${chalk.bold("OK")}!`); } - return alias + return alias; } verifyOwnership(domain) { return this.retry(async bail => { - const targets = await resolve4('alias.zeit.co') + const targets = await resolve4("alias.zeit.co"); if (targets.length <= 0) { - return bail(new Error('Unable to resolve alias.zeit.co')) + return bail(new Error("Unable to resolve alias.zeit.co")); } - let ips = [] + let ips = []; try { - ips = await resolve4(domain) + ips = await resolve4(domain); } catch (err) { - if (err.code === 'ENODATA' || err.code === 'ESERVFAIL' || err.code === 'ENOTFOUND') { + if ( + err.code === "ENODATA" || + err.code === "ESERVFAIL" || + err.code === "ENOTFOUND" + ) { // not errors per se, just absence of records if (this._debug) { - console.log(`> [debug] No records found for "${domain}"`) + console.log(`> [debug] No records found for "${domain}"`); } - const err = new Error(DOMAIN_VERIFICATION_ERROR) - err.userError = true - return bail(err) + const err = new Error(DOMAIN_VERIFICATION_ERROR); + err.userError = true; + return bail(err); } - throw err + throw err; } if (ips.length <= 0) { - const err = new Error(DOMAIN_VERIFICATION_ERROR) - err.userError = true - return bail(err) + const err = new Error(DOMAIN_VERIFICATION_ERROR); + err.userError = true; + return bail(err); } for (const ip of ips) { if (targets.indexOf(ip) === -1) { - const err = new Error(`The domain ${domain} has an A record ${chalk.bold(ip)} that doesn't resolve to ${chalk.bold(chalk.underline('alias.zeit.co'))}.\n> ` + DOMAIN_VERIFICATION_ERROR) - err.ip = ip - err.userError = true - return bail(err) + const err = new Error( + `The domain ${domain} has an A record ${chalk.bold(ip)} that doesn't resolve to ${chalk.bold(chalk.underline("alias.zeit.co"))}.\n> ` + + DOMAIN_VERIFICATION_ERROR + ); + err.ip = ip; + err.userError = true; + return bail(err); } } - }) + }); } -} +}; diff --git a/lib/build-logger.js b/lib/build-logger.js index e3e81fc..43e01ea 100644 --- a/lib/build-logger.js +++ b/lib/build-logger.js @@ -1,153 +1,156 @@ // Native -const EventEmitter = require('events') +const EventEmitter = require("events"); // Packages -const ansi = require('ansi-escapes') -const io = require('socket.io-client') -const chalk = require('chalk') +const ansi = require("ansi-escapes"); +const io = require("socket.io-client"); +const chalk = require("chalk"); -const {compare, deserialize} = require('./logs') +const { compare, deserialize } = require("./logs"); class Lines { constructor(maxLines = 100) { - this.max = maxLines - this.buf = [] + this.max = maxLines; + this.buf = []; } write(str) { - const {max, buf} = this + const { max, buf } = this; if (buf.length === max) { - process.stdout.write(ansi.eraseLines(max + 1)) - buf.shift() - buf.forEach(line => console.log(line)) + process.stdout.write(ansi.eraseLines(max + 1)); + buf.shift(); + buf.forEach(line => console.log(line)); } - buf.push(str) - console.log(str) + buf.push(str); + console.log(str); } reset() { - this.buf = [] + this.buf = []; } } module.exports = class Logger extends EventEmitter { - constructor(host, {debug = false, quiet = false} = {}) { - super() - this.host = host - this.debug = debug - this.quiet = quiet + constructor(host, { debug = false, quiet = false } = {}) { + super(); + this.host = host; + this.debug = debug; + this.quiet = quiet; // readyState - this.building = false + this.building = false; - this.socket = io(`https://io.now.sh/states?host=${host}&v=2`) - this.socket.once('error', this.onSocketError.bind(this)) - this.socket.on('state', this.onState.bind(this)) - this.socket.on('logs', this.onLog.bind(this)) - this.socket.on('backend', this.onComplete.bind(this)) + this.socket = io(`https://io.now.sh/states?host=${host}&v=2`); + this.socket.once("error", this.onSocketError.bind(this)); + this.socket.on("state", this.onState.bind(this)); + this.socket.on("logs", this.onLog.bind(this)); + this.socket.on("backend", this.onComplete.bind(this)); - this.lines = new Lines(10) + this.lines = new Lines(10); // log buffer - this.buf = [] + this.buf = []; } onState(state) { // console.log(state) if (!state.id) { - console.error('> Deployment not found') - this.emit('error') - return + console.error("> Deployment not found"); + this.emit("error"); + return; } if (state.error) { - this.emit('error', state) - return + this.emit("error", state); + return; } if (state.backend) { - this.onComplete() - return + this.onComplete(); + return; } if (state.logs) { - state.logs.forEach(this.onLog, this) + state.logs.forEach(this.onLog, this); } } onLog(log) { if (!this.building) { if (!this.quiet) { - console.log('> Building') + console.log("> Building"); } - this.building = true + this.building = true; } if (this.quiet) { - return + return; } - log = deserialize(log) + log = deserialize(log); - const timer = setTimeout(() => { - this.buf.sort((a, b) => compare(a.log, b.log)) - const idx = this.buf.findIndex(b => b.log.id === log.id) + 1 - for (const b of this.buf.slice(0, idx)) { - clearTimeout(b.timer) - this.printLog(b.log) - } - this.buf = this.buf.slice(idx) - }, 300) + const timer = setTimeout( + () => { + this.buf.sort((a, b) => compare(a.log, b.log)); + const idx = this.buf.findIndex(b => b.log.id === log.id) + 1; + for (const b of this.buf.slice(0, idx)) { + clearTimeout(b.timer); + this.printLog(b.log); + } + this.buf = this.buf.slice(idx); + }, + 300 + ); - this.buf.push({log, timer}) + this.buf.push({ log, timer }); } onComplete() { - this.socket.disconnect() + this.socket.disconnect(); if (this.building) { - this.building = false + this.building = false; } - this.buf.sort((a, b) => compare(a.log, b.log)) + this.buf.sort((a, b) => compare(a.log, b.log)); // flush all buffer for (const b of this.buf) { - clearTimeout(b.timer) - this.printLog(b.log) + clearTimeout(b.timer); + this.printLog(b.log); } - this.buf = [] + this.buf = []; - this.emit('close') + this.emit("close"); } onSocketError(err) { if (this.debug) { - console.log(`> [debug] Socket error ${err}\n${err.stack}`) + console.log(`> [debug] Socket error ${err}\n${err.stack}`); } } printLog(log) { - const data = log.object ? JSON.stringify(log.object) : log.text + const data = log.object ? JSON.stringify(log.object) : log.text; - if (log.type === 'command') { - console.log(`${chalk.gray('>')} ▲ ${data}`) - this.lines.reset() - } else if (log.type === 'stderr') { - data.split('\n').forEach(v => { + if (log.type === "command") { + console.log(`${chalk.gray(">")} ▲ ${data}`); + this.lines.reset(); + } else if (log.type === "stderr") { + data.split("\n").forEach(v => { if (v.length > 0) { - console.error(chalk.gray(`> ${v}`)) + console.error(chalk.gray(`> ${v}`)); } - }) - this.lines.reset() - } else if (log.type === 'stdout') { - data.split('\n').forEach(v => { + }); + this.lines.reset(); + } else if (log.type === "stdout") { + data.split("\n").forEach(v => { if (v.length > 0) { - this.lines.write(`${chalk.gray('>')} ${v}`) + this.lines.write(`${chalk.gray(">")} ${v}`); } - }) + }); } } -} +}; diff --git a/lib/certs.js b/lib/certs.js index 46d78ed..f6a9a94 100644 --- a/lib/certs.js +++ b/lib/certs.js @@ -1,102 +1,101 @@ // Ours -const Now = require('../lib') +const Now = require("../lib"); module.exports = class Certs extends Now { - ls() { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} GET now/certs`) + console.time(`> [debug] #${attempt} GET now/certs`); } - const res = await this._fetch('/now/certs') + const res = await this._fetch("/now/certs"); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} GET now/certs`) + console.timeEnd(`> [debug] #${attempt} GET now/certs`); } - const body = await res.json() - return body.certs - }) + const body = await res.json(); + return body.certs; + }); } create(cn) { - return this.createCert(cn) + return this.createCert(cn); } renew(cn) { - return this.createCert(cn, {renew: true}) + return this.createCert(cn, { renew: true }); } put(cn, crt, key, ca) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} PUT now/certs`) + console.time(`> [debug] #${attempt} PUT now/certs`); } - const res = await this._fetch('/now/certs', { - method: 'PUT', + const res = await this._fetch("/now/certs", { + method: "PUT", body: { domains: [cn], ca, cert: crt, key } - }) + }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} PUT now/certs`) + console.timeEnd(`> [debug] #${attempt} PUT now/certs`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } - const body = await res.json() + const body = await res.json(); if (res.status !== 200) { if (res.status === 404 || res.status === 400) { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } delete(cn) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} DELETE now/certs/${cn}`) + console.time(`> [debug] #${attempt} DELETE now/certs/${cn}`); } - const res = await this._fetch(`/now/certs/${cn}`, {method: 'DELETE'}) + const res = await this._fetch(`/now/certs/${cn}`, { method: "DELETE" }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} DELETE now/certs/${cn}`) + console.timeEnd(`> [debug] #${attempt} DELETE now/certs/${cn}`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } - const body = await res.json() + const body = await res.json(); if (res.status !== 200) { if (res.status === 404 || res.status === 400) { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } -} +}; diff --git a/lib/cfg.js b/lib/cfg.js index 781be23..82de468 100644 --- a/lib/cfg.js +++ b/lib/cfg.js @@ -1,23 +1,25 @@ // Native -const {homedir} = require('os') -const path = require('path') +const { homedir } = require("os"); +const path = require("path"); // Packages -const fs = require('fs-promise') +const fs = require("fs-promise"); -let file = process.env.NOW_JSON ? path.resolve(process.env.NOW_JSON) : path.resolve(homedir(), '.now.json') +let file = process.env.NOW_JSON + ? path.resolve(process.env.NOW_JSON) + : path.resolve(homedir(), ".now.json"); function setConfigFile(nowjson) { - file = path.resolve(nowjson) + file = path.resolve(nowjson); } function read() { - let existing = null + let existing = null; try { - existing = fs.readFileSync(file, 'utf8') - existing = JSON.parse(existing) + existing = fs.readFileSync(file, "utf8"); + existing = JSON.parse(existing); } catch (err) {} - return existing || {} + return existing || {}; } /** @@ -29,12 +31,12 @@ function read() { */ function merge(data) { - const cfg = Object.assign({}, read(), data) - fs.writeFileSync(file, JSON.stringify(cfg, null, 2)) + const cfg = Object.assign({}, read(), data); + fs.writeFileSync(file, JSON.stringify(cfg, null, 2)); } module.exports = { setConfigFile, read, merge -} +}; diff --git a/lib/copy.js b/lib/copy.js index 63f3b22..4859669 100644 --- a/lib/copy.js +++ b/lib/copy.js @@ -1,16 +1,16 @@ // Packages -const {copy: _copy} = require('copy-paste') +const { copy: _copy } = require("copy-paste"); function copy(text) { return new Promise((resolve, reject) => { _copy(text, err => { if (err) { - return reject(err) + return reject(err); } - resolve() - }) - }) + resolve(); + }); + }); } -module.exports = copy +module.exports = copy; diff --git a/lib/credit-cards.js b/lib/credit-cards.js index cb1ce0e..8034636 100644 --- a/lib/credit-cards.js +++ b/lib/credit-cards.js @@ -1,33 +1,34 @@ -const stripe = require('stripe')('pk_live_alyEi3lN0kSwbdevK0nrGwTw') +const stripe = require("stripe")("pk_live_alyEi3lN0kSwbdevK0nrGwTw"); -const Now = require('../lib') +const Now = require("../lib"); module.exports = class CreditCards extends Now { - async ls() { - const res = await this._fetch('/www/user/cards') - const body = await res.json() + const res = await this._fetch("/www/user/cards"); + const body = await res.json(); - return body + return body; } async setDefault(cardId) { - await this._fetch('/www/user/cards/default', { - method: 'PUT', - body: {cardId} - }) - return true + await this._fetch("/www/user/cards/default", { + method: "PUT", + body: { cardId } + }); + return true; } async rm(cardId) { - await this._fetch(`/www/user/cards/${encodeURIComponent(cardId)}`, {method: 'DELEtE'}) - return true + await this._fetch(`/www/user/cards/${encodeURIComponent(cardId)}`, { + method: "DELEtE" + }); + return true; } /* eslint-disable camelcase */ add(card) { return new Promise(async (resolve, reject) => { - const expDateParts = card.expDate.split(' / ') + const expDateParts = card.expDate.split(" / "); card = { name: card.name, number: card.cardNumber, @@ -37,34 +38,34 @@ module.exports = class CreditCards extends Now { address_state: card.state, address_city: card.city, address_line1: card.address1 - } + }; - card.exp_month = expDateParts[0] - card.exp_year = expDateParts[1] + card.exp_month = expDateParts[0]; + card.exp_year = expDateParts[1]; try { - const stripeToken = (await stripe.tokens.create({card})).id - const res = await this._fetch('/www/user/cards', { - method: 'POST', - body: {stripeToken} - }) + const stripeToken = (await stripe.tokens.create({ card })).id; + const res = await this._fetch("/www/user/cards", { + method: "POST", + body: { stripeToken } + }); - const body = await res.json() + const body = await res.json(); if (body.card && body.card.id) { resolve({ last4: body.card.last4 - }) + }); } else if (body.error && body.error.message) { - reject({message: body.error.message}) + reject({ message: body.error.message }); } else { - reject('Unknown error') + reject("Unknown error"); } } catch (err) { reject({ - message: err.message || 'Unknown error' - }) + message: err.message || "Unknown error" + }); } - }) + }); } -} +}; diff --git a/lib/dns.js b/lib/dns.js index 124840a..bb99726 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -1,15 +1,15 @@ // Packages -const dns = require('dns') +const dns = require("dns"); function resolve4(host) { return new Promise((resolve, reject) => { return dns.resolve4(host, (err, answer) => { if (err) { - return reject(err) + return reject(err); } - resolve(answer) - }) - }) + resolve(answer); + }); + }); } -module.exports = resolve4 +module.exports = resolve4; diff --git a/lib/domain-records.js b/lib/domain-records.js index 5465e65..018167c 100644 --- a/lib/domain-records.js +++ b/lib/domain-records.js @@ -1,130 +1,139 @@ // Ours -const Now = require('../lib') +const Now = require("../lib"); module.exports = class DomainRecords extends Now { - async getRecord(id) { - const all = (await this.ls()).entries() + const all = (await this.ls()).entries(); for (const [domain, records] of all) { for (const record of records) { if (record.id === id) { - record.domain = domain - return record + record.domain = domain; + return record; } } } - return null + return null; } async ls(dom) { - let domains + let domains; if (dom) { - domains = [dom] + domains = [dom]; } else { - const ret = await this.listDomains() - domains = ret.filter(x => !x.isExternal).map(x => x.name).sort((a, b) => a.localeCompare(b)) + const ret = await this.listDomains(); + domains = ret + .filter(x => !x.isExternal) + .map(x => x.name) + .sort((a, b) => a.localeCompare(b)); } - const records = new Map() + const records = new Map(); for (const domain of domains) { const body = await this.retry(async (bail, attempt) => { - const url = `/domains/${domain}/records` + const url = `/domains/${domain}/records`; if (this._debug) { - console.time(`> [debug] #${attempt} GET ${url}`) + console.time(`> [debug] #${attempt} GET ${url}`); } - const res = await this._fetch(url) + const res = await this._fetch(url); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} GET ${url}`) + console.timeEnd(`> [debug] #${attempt} GET ${url}`); } - const body = await res.json() + const body = await res.json(); - if (res.status === 404 && body.code === 'not_found') { - return bail(new Error(body.message)) + if (res.status === 404 && body.code === "not_found") { + return bail(new Error(body.message)); } else if (res.status !== 200) { - throw new Error(`Failed to get DNS records for domain "${domain}"`) + throw new Error(`Failed to get DNS records for domain "${domain}"`); } - return body - }) - records.set(domain, body.records.sort((a, b) => a.slug.localeCompare(b.slug))) + return body; + }); + records.set( + domain, + body.records.sort((a, b) => a.slug.localeCompare(b.slug)) + ); } - return records + return records; } create(domain, data) { - const url = `/domains/${domain}/records` + const url = `/domains/${domain}/records`; return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} POST ${url}`) + console.time(`> [debug] #${attempt} POST ${url}`); } const res = await this._fetch(url, { - method: 'POST', + method: "POST", body: data - }) + }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} POST ${url}`) + console.timeEnd(`> [debug] #${attempt} POST ${url}`); } - const body = await res.json() + const body = await res.json(); if (res.status === 400) { - return bail(new Error(body.error ? body.error.message : 'Unknown error')) + return bail( + new Error(body.error ? body.error.message : "Unknown error") + ); } else if (res.status === 403) { - const err = new Error(`Not authorized to access the domain "${domain}"`) - err.userError = true - return bail(err) + const err = new Error( + `Not authorized to access the domain "${domain}"` + ); + err.userError = true; + return bail(err); } else if (res.status === 404) { - let err + let err; - if (body.error.code === 'not_found') { - err = new Error(`The domain "${domain}" was not found`) - err.userError = true - return bail(err) + if (body.error.code === "not_found") { + err = new Error(`The domain "${domain}" was not found`); + err.userError = true; + return bail(err); } } if (res.status !== 200) { - throw new Error(body.error ? body.error.message : 'Unknown error') + throw new Error(body.error ? body.error.message : "Unknown error"); } - return body - }) + return body; + }); } delete(domain, recordId) { - const url = `/domains/${domain}/records/${recordId}` + const url = `/domains/${domain}/records/${recordId}`; return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} DELETE ${url}`) + console.time(`> [debug] #${attempt} DELETE ${url}`); } - const res = await this._fetch(url, {method: 'DELETE'}) + const res = await this._fetch(url, { method: "DELETE" }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} DELETE ${url}`) + console.timeEnd(`> [debug] #${attempt} DELETE ${url}`); } - const body = await res.json() + const body = await res.json(); if (res.status === 403) { - const err = new Error(`Not authorized to access domain ${domain}`) - err.userError = true - return bail(err) + const err = new Error(`Not authorized to access domain ${domain}`); + err.userError = true; + return bail(err); } else if (res.status === 404) { - let err + let err; - if (body.error.code === 'not_found') { - err = new Error(body.error.message) - err.userError = true - return bail(err) + if (body.error.code === "not_found") { + err = new Error(body.error.message); + err.userError = true; + return bail(err); } } if (res.status !== 200) { - throw new Error(body.error ? body.error.message : 'Unkown error') + throw new Error(body.error ? body.error.message : "Unkown error"); } - return body - }) + return body; + }); } -} +}; diff --git a/lib/domains.js b/lib/domains.js index 921da36..2fd02d7 100644 --- a/lib/domains.js +++ b/lib/domains.js @@ -1,77 +1,81 @@ // Packages -const chalk = require('chalk') +const chalk = require("chalk"); // Ours -const Now = require('../lib') -const isZeitWorld = require('./is-zeit-world') -const {DNS_VERIFICATION_ERROR} = require('./errors') +const Now = require("../lib"); +const isZeitWorld = require("./is-zeit-world"); +const { DNS_VERIFICATION_ERROR } = require("./errors"); -const domainRegex = /^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/ +const domainRegex = /^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/; module.exports = class Domains extends Now { - async ls() { - return await this.listDomains() + return await this.listDomains(); } async rm(name) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} DELETE /domains/${name}`) + console.time(`> [debug] #${attempt} DELETE /domains/${name}`); } - const res = await this._fetch(`/domains/${name}`, {method: 'DELETE'}) + const res = await this._fetch(`/domains/${name}`, { method: "DELETE" }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} DELETE /domains/${name}`) + console.timeEnd(`> [debug] #${attempt} DELETE /domains/${name}`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } if (res.status !== 200) { - const body = await res.json() - throw new Error(body.error.message) + const body = await res.json(); + throw new Error(body.error.message); } - }) + }); } async add(domain, skipVerification, isExternal) { if (!domainRegex.test(domain)) { - const err = new Error(`The supplied value ${chalk.bold(`"${domain}"`)} is not a valid domain.`) - err.userError = true - throw err + const err = new Error( + `The supplied value ${chalk.bold(`"${domain}"`)} is not a valid domain.` + ); + err.userError = true; + throw err; } if (skipVerification || isExternal) { - return this.setupDomain(domain, {isExternal}) + return this.setupDomain(domain, { isExternal }); } - let ns + let ns; try { - console.log('> Verifying nameservers…') - const res = await this.getNameservers(domain) - ns = res.nameservers + console.log("> Verifying nameservers…"); + const res = await this.getNameservers(domain); + ns = res.nameservers; } catch (err) { - const err2 = new Error(`Unable to fetch nameservers for ${chalk.underline(chalk.bold(domain))}.`) - err2.userError = true - throw err2 + const err2 = new Error( + `Unable to fetch nameservers for ${chalk.underline(chalk.bold(domain))}.` + ); + err2.userError = true; + throw err2; } if (isZeitWorld(ns)) { - console.log(`> Verification ${chalk.bold('OK')}!`) - return this.setupDomain(domain) + console.log(`> Verification ${chalk.bold("OK")}!`); + return this.setupDomain(domain); } if (this._debug) { - console.log(`> [debug] Supplied domain "${domain}" has non-zeit nameservers`) + console.log( + `> [debug] Supplied domain "${domain}" has non-zeit nameservers` + ); } - const err3 = new Error(DNS_VERIFICATION_ERROR) - err3.userError = true - throw err3 + const err3 = new Error(DNS_VERIFICATION_ERROR); + err3.userError = true; + throw err3; } - -} +}; diff --git a/lib/error.js b/lib/error.js index 7a94510..635e3d0 100644 --- a/lib/error.js +++ b/lib/error.js @@ -1,33 +1,37 @@ // Packages -const ms = require('ms') -const chalk = require('chalk') +const ms = require("ms"); +const chalk = require("chalk"); -const error = require('./utils/output/error') +const error = require("./utils/output/error"); function handleError(err) { if (err.status === 403) { - error('Authentication error. Run `now -L` or `now --login` to log-in again.') + 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) + if (err.retryAfter === "never") { + error(err.message); } else if (err.retryAfter === null) { - error('Rate limit exceeded error. Please try later.') + error("Rate limit exceeded error. Please try later."); } else { - error('Rate limit exceeded error. Try again in ' + - ms(err.retryAfter * 1000, {long: true}) + - ', or upgrade your account by runnung ' + - `${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')}`) + error( + "Rate limit exceeded error. Try again in " + + ms(err.retryAfter * 1000, { long: true }) + + ", or upgrade your account by runnung " + + `${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")}` + ); } } else if (err.userError) { - error(err.message) + error(err.message); } else if (err.status === 500) { - error('Unexpected server error. Please retry.') + error("Unexpected server error. Please retry."); } else { - error(`Unexpected error. Please try later. (${err.message})`) + error(`Unexpected error. Please try later. (${err.message})`); } } module.exports = { handleError, error -} +}; diff --git a/lib/errors.js b/lib/errors.js index 78ef0f2..0bb2d93 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -1,17 +1,17 @@ // Packages -const chalk = require('chalk') +const chalk = require("chalk"); -const DNS_VERIFICATION_ERROR = `Please make sure that your nameservers point to ${chalk.underline('zeit.world')}. -> Examples: (full list at ${chalk.underline('https://zeit.world')}) -> ${chalk.gray('-')} ${chalk.underline('california.zeit.world')} ${chalk.dim('173.255.215.107')} -> ${chalk.gray('-')} ${chalk.underline('newark.zeit.world')} ${chalk.dim('173.255.231.87')} -> ${chalk.gray('-')} ${chalk.underline('london.zeit.world')} ${chalk.dim('178.62.47.76')} -> ${chalk.gray('-')} ${chalk.underline('singapore.zeit.world')} ${chalk.dim('119.81.97.170')}` +const DNS_VERIFICATION_ERROR = `Please make sure that your nameservers point to ${chalk.underline("zeit.world")}. +> Examples: (full list at ${chalk.underline("https://zeit.world")}) +> ${chalk.gray("-")} ${chalk.underline("california.zeit.world")} ${chalk.dim("173.255.215.107")} +> ${chalk.gray("-")} ${chalk.underline("newark.zeit.world")} ${chalk.dim("173.255.231.87")} +> ${chalk.gray("-")} ${chalk.underline("london.zeit.world")} ${chalk.dim("178.62.47.76")} +> ${chalk.gray("-")} ${chalk.underline("singapore.zeit.world")} ${chalk.dim("119.81.97.170")}`; const DOMAIN_VERIFICATION_ERROR = DNS_VERIFICATION_ERROR + -`\n> Alternatively, ensure it resolves to ${chalk.underline('alias.zeit.co')} via ${chalk.dim('CNAME')} / ${chalk.dim('ALIAS')}.` + `\n> Alternatively, ensure it resolves to ${chalk.underline("alias.zeit.co")} via ${chalk.dim("CNAME")} / ${chalk.dim("ALIAS")}.`; module.exports = { DNS_VERIFICATION_ERROR, DOMAIN_VERIFICATION_ERROR -} +}; diff --git a/lib/get-files.js b/lib/get-files.js index 14b896f..b4fef08 100644 --- a/lib/get-files.js +++ b/lib/get-files.js @@ -1,15 +1,15 @@ // Native -const {resolve} = require('path') +const { resolve } = require("path"); // Packages -const flatten = require('arr-flatten') -const unique = require('array-unique') -const ignore = require('ignore') -const _glob = require('glob') -const {stat, readdir, readFile} = require('fs-promise') +const flatten = require("arr-flatten"); +const unique = require("array-unique"); +const ignore = require("ignore"); +const _glob = require("glob"); +const { stat, readdir, readFile } = require("fs-promise"); // Ours -const IGNORED = require('./ignored') +const IGNORED = require("./ignored"); /** * Returns a list of files in the given @@ -24,84 +24,95 @@ const IGNORED = require('./ignored') * @return {Array} comprehensive list of paths to sync */ -async function npm(path, pkg, nowConfig = null, { - limit = null, - hasNowJson = false, - debug = false -} = {}) { - const whitelist = (nowConfig && nowConfig.files) || pkg.files +async function npm( + path, + pkg, + nowConfig = null, + { + limit = null, + hasNowJson = false, + debug = false + } = {} +) { + const whitelist = (nowConfig && nowConfig.files) || pkg.files; // the package.json `files` whitelist still // honors ignores: https://docs.npmjs.com/files/package.json#files - const search_ = whitelist || ['.'] + const search_ = whitelist || ["."]; // convert all filenames into absolute paths - const search = Array.prototype.concat.apply([], (await Promise.all(search_.map(file => glob(file, {cwd: path, absolute: true, dot: true}))))) + const search = Array.prototype.concat.apply( + [], + await Promise.all( + search_.map(file => glob(file, { cwd: path, absolute: true, dot: true })) + ) + ); // always include the "main" file if (pkg.main) { - search.push(require.resolve(resolve(path, pkg.main), 'may-exclude')) // pkg: may-exclude suppresses warnings + search.push(require.resolve(resolve(path, pkg.main), "may-exclude")); // pkg: may-exclude suppresses warnings } // compile list of ignored patterns and files - const npmIgnore = await maybeRead(resolve(path, '.npmignore'), null) - const gitIgnore = npmIgnore === null ? - await maybeRead(resolve(path, '.gitignore')) : - null + const npmIgnore = await maybeRead(resolve(path, ".npmignore"), null); + const gitIgnore = npmIgnore === null + ? await maybeRead(resolve(path, ".gitignore")) + : null; - const filter = ignore().add( - IGNORED + - '\n' + - clearRelative(npmIgnore === null ? gitIgnore : npmIgnore) - ).createFilter() + const filter = ignore() + .add( + IGNORED + "\n" + clearRelative(npmIgnore === null ? gitIgnore : npmIgnore) + ) + .createFilter(); - const prefixLength = path.length + 1 + const prefixLength = path.length + 1; // the package.json `files` whitelist still // honors npmignores: https://docs.npmjs.com/files/package.json#files // but we don't ignore if the user is explicitly listing files // under the now namespace, or using files in combination with gitignore - const overrideIgnores = (pkg.now && pkg.now.files) || (gitIgnore !== null && pkg.files) - const accepts = overrideIgnores ? - () => true : - file => { - const relativePath = file.substr(prefixLength) - - if (relativePath === '') { - return true - } + const overrideIgnores = (pkg.now && pkg.now.files) || + (gitIgnore !== null && pkg.files); + const accepts = overrideIgnores + ? () => true + : file => { + const relativePath = file.substr(prefixLength); + + if (relativePath === "") { + return true; + } - const accepted = filter(relativePath) - if (!accepted && debug) { - console.log('> [debug] ignoring "%s"', file) - } - return accepted - } + const accepted = filter(relativePath); + if (!accepted && debug) { + console.log('> [debug] ignoring "%s"', file); + } + return accepted; + }; // locate files if (debug) { - console.time(`> [debug] locating files ${path}`) + console.time(`> [debug] locating files ${path}`); } const files = await explode(search, { accepts, limit, debug - }) + }); if (debug) { - console.timeEnd(`> [debug] locating files ${path}`) + console.timeEnd(`> [debug] locating files ${path}`); } // always include manifest as npm does not allow ignoring it // source: https://docs.npmjs.com/files/package.json#files - files.push(asAbsolute('package.json', path)) + files.push(asAbsolute("package.json", path)); if (hasNowJson) { - files.push(asAbsolute('now.json', path)) + files.push(asAbsolute("now.json", path)); } // get files - return unique(files) + return unique(files); } /** @@ -112,13 +123,13 @@ async function npm(path, pkg, nowConfig = null, { * @param {String} parent full path */ -const asAbsolute = function (path, parent) { - if (path[0] === '/') { - return path +const asAbsolute = function(path, parent) { + if (path[0] === "/") { + return path; } - return resolve(parent, path) -} + return resolve(parent, path); +}; /** * Returns a list of files in the given @@ -133,81 +144,89 @@ const asAbsolute = function (path, parent) { * @return {Array} comprehensive list of paths to sync */ -async function docker(path, nowConfig = null, { - limit = null, - hasNowJson = false, - debug = false -} = {}) { - const whitelist = nowConfig && nowConfig.files +async function docker( + path, + nowConfig = null, + { + limit = null, + hasNowJson = false, + debug = false + } = {} +) { + const whitelist = nowConfig && nowConfig.files; // base search path // the now.json `files` whitelist still // honors ignores: https://docs.npmjs.com/files/package.json#files - const search_ = whitelist || ['.'] + const search_ = whitelist || ["."]; // convert all filenames into absolute paths - const search = search_.map(file => asAbsolute(file, path)) + const search = search_.map(file => asAbsolute(file, path)); // compile list of ignored patterns and files - const dockerIgnore = await maybeRead(resolve(path, '.dockerignore'), null) - - const filter = ignore().add( - IGNORED + - '\n' + - clearRelative(dockerIgnore === null ? - await maybeRead(resolve(path, '.gitignore')) : - dockerIgnore) - ).createFilter() - - const prefixLength = path.length + 1 - const accepts = function (file) { - const relativePath = file.substr(prefixLength) - - if (relativePath === '') { - return true + const dockerIgnore = await maybeRead(resolve(path, ".dockerignore"), null); + + const filter = ignore() + .add( + IGNORED + + "\n" + + clearRelative( + dockerIgnore === null + ? await maybeRead(resolve(path, ".gitignore")) + : dockerIgnore + ) + ) + .createFilter(); + + const prefixLength = path.length + 1; + const accepts = function(file) { + const relativePath = file.substr(prefixLength); + + if (relativePath === "") { + return true; } - const accepted = filter(relativePath) + const accepted = filter(relativePath); if (!accepted && debug) { - console.log('> [debug] ignoring "%s"', file) + console.log('> [debug] ignoring "%s"', file); } - return accepted - } + return accepted; + }; // locate files if (debug) { - console.time(`> [debug] locating files ${path}`) + console.time(`> [debug] locating files ${path}`); } - const files = await explode(search, {accepts, limit, debug}) + const files = await explode(search, { accepts, limit, debug }); if (debug) { - console.timeEnd(`> [debug] locating files ${path}`) + console.timeEnd(`> [debug] locating files ${path}`); } // always include manifest as npm does not allow ignoring it // source: https://docs.npmjs.com/files/package.json#files - files.push(asAbsolute('Dockerfile', path)) + files.push(asAbsolute("Dockerfile", path)); if (hasNowJson) { - files.push(asAbsolute('now.json', path)) + files.push(asAbsolute("now.json", path)); } // get files - return unique(files) + return unique(files); } -const glob = async function (pattern, options) { +const glob = async function(pattern, options) { return new Promise((resolve, reject) => { _glob(pattern, options, (error, files) => { if (error) { - reject(error) + reject(error); } else { - resolve(files) + resolve(files); } - }) - }) -} + }); + }); +}; /** * Explodes directories into a full list of files. @@ -223,52 +242,54 @@ const glob = async function (pattern, options) { * @return {Array} of {String}s of full paths */ -async function explode(paths, {accepts, debug}) { +async function explode(paths, { accepts, debug }) { const list = async file => { - let path = file - let s + let path = file; + let s; if (!accepts(file)) { - return null + return null; } try { - s = await stat(path) + s = await stat(path); } catch (e) { // in case the file comes from `files` or `main` // and it wasn't specified with `.js` by the user - path = file + '.js' + path = file + ".js"; try { - s = await stat(path) + s = await stat(path); } catch (e2) { if (debug) { - console.log('> [debug] ignoring invalid file "%s"', file) + console.log('> [debug] ignoring invalid file "%s"', file); } - return null + return null; } } if (s.isDirectory()) { - const all = await readdir(file) - return many(all.map(subdir => asAbsolute(subdir, file))) + const all = await readdir(file); + return many(all.map(subdir => asAbsolute(subdir, file))); } else if (!s.isFile()) { if (debug) { - console.log('> [debug] ignoring special file "%s"', file) + console.log('> [debug] ignoring special file "%s"', file); } - return null + return null; } - return path - } + return path; + }; const many = async all => { - return await Promise.all(all.map(async file => { - return await list(file) - })) - } - - return flatten((await many(paths))).filter(v => v !== null) + return await Promise.all( + all.map(async file => { + return await list(file); + }) + ); + }; + + return flatten(await many(paths)).filter(v => v !== null); } /** @@ -277,24 +298,24 @@ async function explode(paths, {accepts, debug}) { * @return {String} results or `''` */ -const maybeRead = async function (path, default_ = '') { +const maybeRead = async function(path, default_ = "") { try { - return await readFile(path, 'utf8') + return await readFile(path, "utf8"); } catch (err) { - return default_ + return default_; } -} +}; /** * Remove leading `./` from the beginning of ignores * because our parser doesn't like them :| */ -const clearRelative = function (str) { - return str.replace(/(\n|^)\.\//g, '$1') -} +const clearRelative = function(str) { + return str.replace(/(\n|^)\.\//g, "$1"); +}; module.exports = { npm, docker -} +}; diff --git a/lib/git.js b/lib/git.js index 4ef6337..1377947 100644 --- a/lib/git.js +++ b/lib/git.js @@ -1,211 +1,209 @@ // Native -const path = require('path') -const url = require('url') -const childProcess = require('child_process') +const path = require("path"); +const url = require("url"); +const childProcess = require("child_process"); // Packages -const fs = require('fs-promise') -const download = require('download') -const tmp = require('tmp-promise') -const isURL = require('is-url') - -const cloneRepo = (parts, tmpDir) => new Promise((resolve, reject) => { - let host - - switch (parts.type) { - case 'GitLab': - host = `gitlab.com` - break - case 'Bitbucket': - host = `bitbucket.org` - break - default: - host = `github.com` - } +const fs = require("fs-promise"); +const download = require("download"); +const tmp = require("tmp-promise"); +const isURL = require("is-url"); + +const cloneRepo = (parts, tmpDir) => + new Promise((resolve, reject) => { + let host; + + switch (parts.type) { + case "GitLab": + host = `gitlab.com`; + break; + case "Bitbucket": + host = `bitbucket.org`; + break; + default: + host = `github.com`; + } - const url = `https://${host}/${parts.main}` - const ref = parts.ref || (parts.type === 'Bitbucket' ? 'default' : 'master') - const cmd = `git clone ${url} --single-branch ${ref}` + const url = `https://${host}/${parts.main}`; + const ref = parts.ref || + (parts.type === "Bitbucket" ? "default" : "master"); + const cmd = `git clone ${url} --single-branch ${ref}`; - childProcess.exec(cmd, {cwd: tmpDir.path}, (err, stdout) => { - if (err) { - reject(err) - } + childProcess.exec(cmd, { cwd: tmpDir.path }, (err, stdout) => { + if (err) { + reject(err); + } - resolve(stdout) - }) -}) + resolve(stdout); + }); + }); const renameRepoDir = async (pathParts, tmpDir) => { - const tmpContents = await fs.readdir(tmpDir.path) + const tmpContents = await fs.readdir(tmpDir.path); - const oldTemp = path.join(tmpDir.path, tmpContents[0]) - const newTemp = path.join(tmpDir.path, pathParts.main.replace('/', '-')) + const oldTemp = path.join(tmpDir.path, tmpContents[0]); + const newTemp = path.join(tmpDir.path, pathParts.main.replace("/", "-")); - await fs.rename(oldTemp, newTemp) - tmpDir.path = newTemp + await fs.rename(oldTemp, newTemp); + tmpDir.path = newTemp; - return tmpDir -} + return tmpDir; +}; const downloadRepo = async repoPath => { - const pathParts = gitPathParts(repoPath) + const pathParts = gitPathParts(repoPath); const tmpDir = await tmp.dir({ // We'll remove it manually once deployment is done keep: true, // Recursively remove directory when calling respective method unsafeCleanup: true - }) + }); - let gitInstalled = true + let gitInstalled = true; try { - await cloneRepo(pathParts, tmpDir) + await cloneRepo(pathParts, tmpDir); } catch (err) { - gitInstalled = false + gitInstalled = false; } if (gitInstalled) { - return await renameRepoDir(pathParts, tmpDir) + return await renameRepoDir(pathParts, tmpDir); } - let url + let url; switch (pathParts.type) { - case 'GitLab': { - const ref = pathParts.ref ? `?ref=${pathParts.ref}` : '' - url = `https://gitlab.com/${pathParts.main}/repository/archive.tar` + ref - break + case "GitLab": { + const ref = pathParts.ref ? `?ref=${pathParts.ref}` : ""; + url = `https://gitlab.com/${pathParts.main}/repository/archive.tar` + ref; + break; } - case 'Bitbucket': - url = `https://bitbucket.org/${pathParts.main}/get/${pathParts.ref || 'default'}.zip` - break + case "Bitbucket": + url = `https://bitbucket.org/${pathParts.main}/get/${pathParts.ref || "default"}.zip`; + break; default: - url = `https://api.github.com/repos/${pathParts.main}/tarball/${pathParts.ref}` + url = `https://api.github.com/repos/${pathParts.main}/tarball/${pathParts.ref}`; } try { await download(url, tmpDir.path, { extract: true - }) + }); } catch (err) { - tmpDir.cleanup() - return false + tmpDir.cleanup(); + return false; } - return await renameRepoDir(pathParts, tmpDir) -} + return await renameRepoDir(pathParts, tmpDir); +}; const capitalizePlatform = name => { const names = { - github: 'GitHub', - gitlab: 'GitLab', - bitbucket: 'Bitbucket' - } + github: "GitHub", + gitlab: "GitLab", + bitbucket: "Bitbucket" + }; - return names[name] -} + return names[name]; +}; const splittedURL = fullURL => { - const parsedURL = url.parse(fullURL) - const pathParts = parsedURL.path.split('/') + const parsedURL = url.parse(fullURL); + const pathParts = parsedURL.path.split("/"); - pathParts.shift() + pathParts.shift(); // Set path to repo... - const main = pathParts[0] + '/' + pathParts[1] + const main = pathParts[0] + "/" + pathParts[1]; // ...and then remove it from the parts - pathParts.splice(0, 2) + pathParts.splice(0, 2); // Assign Git reference - let ref = pathParts.length >= 2 ? pathParts[1] : '' + let ref = pathParts.length >= 2 ? pathParts[1] : ""; // Firstly be sure that we haven know the ref type if (pathParts[0]) { // Then shorten the SHA of the commit - if (pathParts[0] === 'commit' || pathParts[0] === 'commits') { - ref = ref.substring(0, 7) + if (pathParts[0] === "commit" || pathParts[0] === "commits") { + ref = ref.substring(0, 7); } } // We're deploying master by default, // so there's no need to indicate it explicitly - if (ref === 'master') { - ref = '' + if (ref === "master") { + ref = ""; } return { main, ref, - type: capitalizePlatform(parsedURL.host.split('.')[0]) - } -} + type: capitalizePlatform(parsedURL.host.split(".")[0]) + }; +}; const gitPathParts = main => { - let ref = '' + let ref = ""; if (isURL(main)) { - return splittedURL(main) + return splittedURL(main); } - if (main.split('/')[1].includes('#')) { - const parts = main.split('#') + if (main.split("/")[1].includes("#")) { + const parts = main.split("#"); - ref = parts[1] - main = parts[0] + ref = parts[1]; + main = parts[0]; } return { main, ref, - type: capitalizePlatform('github') - } -} + type: capitalizePlatform("github") + }; +}; const isRepoPath = path => { if (!path) { - return false + return false; } - const allowedHosts = [ - 'github.com', - 'gitlab.com', - 'bitbucket.org' - ] + const allowedHosts = ["github.com", "gitlab.com", "bitbucket.org"]; if (isURL(path)) { - const urlParts = url.parse(path) - const slashSplitted = urlParts.path.split('/').filter(n => n) - const notBare = slashSplitted.length >= 2 + const urlParts = url.parse(path); + const slashSplitted = urlParts.path.split("/").filter(n => n); + const notBare = slashSplitted.length >= 2; if (allowedHosts.includes(urlParts.host) && notBare) { - return true + return true; } - return 'no-valid-url' + return "no-valid-url"; } - return /[^\s\\]\/[^\s\\]/g.test(path) -} + return /[^\s\\]\/[^\s\\]/g.test(path); +}; const fromGit = async (path, debug) => { - let tmpDir = false + let tmpDir = false; try { - tmpDir = await downloadRepo(path) + tmpDir = await downloadRepo(path); } catch (err) { if (debug) { - console.log(`Could not download "${path}" repo from GitHub`) + console.log(`Could not download "${path}" repo from GitHub`); } } - return tmpDir -} + return tmpDir; +}; module.exports = { gitPathParts, isRepoPath, fromGit -} +}; diff --git a/lib/hash.js b/lib/hash.js index 9cd1cab..cec9072 100644 --- a/lib/hash.js +++ b/lib/hash.js @@ -1,11 +1,11 @@ // Native -const {createHash} = require('crypto') -const path = require('path') +const { createHash } = require("crypto"); +const path = require("path"); // Packages -const {readFile} = require('fs-promise') +const { readFile } = require("fs-promise"); - /** +/** * Computes hashes for the contents of each file given. * * @param {Array} of {String} full paths @@ -13,28 +13,30 @@ const {readFile} = require('fs-promise') */ async function hashes(files, isStatic, pkg) { - const map = new Map() - - await Promise.all(files.map(async name => { - const filename = path.basename(name) - let data - - if (isStatic && filename === 'package.json') { - const packageString = JSON.stringify(pkg, null, 2) - data = Buffer.from(packageString) - } else { - data = await readFile(name) - } - - const h = hash(data) - const entry = map.get(h) - if (entry) { - entry.names.push(name) - } else { - map.set(hash(data), {names: [name], data}) - } - })) - return map + const map = new Map(); + + await Promise.all( + files.map(async name => { + const filename = path.basename(name); + let data; + + if (isStatic && filename === "package.json") { + const packageString = JSON.stringify(pkg, null, 2); + data = Buffer.from(packageString); + } else { + data = await readFile(name); + } + + const h = hash(data); + const entry = map.get(h); + if (entry) { + entry.names.push(name); + } else { + map.set(hash(data), { names: [name], data }); + } + }) + ); + return map; } /** @@ -45,9 +47,7 @@ async function hashes(files, isStatic, pkg) { */ function hash(buf) { - return createHash('sha1') - .update(buf) - .digest('hex') + return createHash("sha1").update(buf).digest("hex"); } -module.exports = hashes +module.exports = hashes; diff --git a/lib/ignored.js b/lib/ignored.js index b623513..253a807 100644 --- a/lib/ignored.js +++ b/lib/ignored.js @@ -14,4 +14,4 @@ module.exports = `.hg npm-debug.log config.gypi node_modules -CVS` +CVS`; diff --git a/lib/indent.js b/lib/indent.js index f934288..a4c6071 100644 --- a/lib/indent.js +++ b/lib/indent.js @@ -1,5 +1,5 @@ function indent(text, n) { - return text.split('\n').map(l => ' '.repeat(n) + l).join('\n') + return text.split("\n").map(l => " ".repeat(n) + l).join("\n"); } -module.exports = indent +module.exports = indent; diff --git a/lib/index.js b/lib/index.js index 5c9bb94..9e99a11 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,101 +1,104 @@ // Native -const {homedir} = require('os') -const {resolve: resolvePath, join: joinPaths} = require('path') -const EventEmitter = require('events') +const { homedir } = require("os"); +const { resolve: resolvePath, join: joinPaths } = require("path"); +const EventEmitter = require("events"); // Packages -const bytes = require('bytes') -const chalk = require('chalk') -const resumer = require('resumer') -const retry = require('async-retry') -const splitArray = require('split-array') -const {parse: parseIni} = require('ini') -const {readFile, stat, lstat} = require('fs-promise') +const bytes = require("bytes"); +const chalk = require("chalk"); +const resumer = require("resumer"); +const retry = require("async-retry"); +const splitArray = require("split-array"); +const { parse: parseIni } = require("ini"); +const { readFile, stat, lstat } = require("fs-promise"); // Ours -const {npm: getNpmFiles, docker: getDockerFiles} = require('./get-files') -const ua = require('./ua') -const hash = require('./hash') -const Agent = require('./agent') -const readMetaData = require('./read-metadata') +const { npm: getNpmFiles, docker: getDockerFiles } = require("./get-files"); +const ua = require("./ua"); +const hash = require("./hash"); +const Agent = require("./agent"); +const readMetaData = require("./read-metadata"); // how many concurrent HTTP/2 stream uploads -const MAX_CONCURRENT = 10 +const MAX_CONCURRENT = 10; // check if running windows -const IS_WIN = /^win/.test(process.platform) -const SEP = IS_WIN ? '\\' : '/' +const IS_WIN = /^win/.test(process.platform); +const SEP = IS_WIN ? "\\" : "/"; module.exports = class Now extends EventEmitter { - constructor(url, token, {forceNew = false, debug = false}) { - super() - this._token = token - this._debug = debug - this._forceNew = forceNew - this._agent = new Agent(url, {debug}) - this._onRetry = this._onRetry.bind(this) + constructor(url, token, { forceNew = false, debug = false }) { + super(); + this._token = token; + this._debug = debug; + this._forceNew = forceNew; + this._agent = new Agent(url, { debug }); + this._onRetry = this._onRetry.bind(this); } - async create(path, { - wantsPublic, - quiet = false, - env = {}, - followSymlinks = true, - forceNew = false, - forceSync = false, - forwardNpm = false, - deploymentType = 'npm', - deploymentName, - isStatic = false - }) { - this._path = path - this._static = isStatic - - let files + async create( + path, + { + wantsPublic, + quiet = false, + env = {}, + followSymlinks = true, + forceNew = false, + forceSync = false, + forwardNpm = false, + deploymentType = "npm", + deploymentName, + isStatic = false + } + ) { + this._path = path; + this._static = isStatic; + + let files; const meta = await readMetaData(path, { deploymentType, deploymentName, quiet, isStatic - }) - const {pkg, name, description, nowConfig, hasNowJson} = meta - deploymentType = meta.deploymentType + }); + const { pkg, name, description, nowConfig, hasNowJson } = meta; + deploymentType = meta.deploymentType; if (this._debug) { - console.time('> [debug] Getting files') + console.time("> [debug] Getting files"); } - const opts = {debug: this._debug, hasNowJson} - if (deploymentType === 'npm') { - files = await getNpmFiles(path, pkg, nowConfig, opts) + const opts = { debug: this._debug, hasNowJson }; + if (deploymentType === "npm") { + files = await getNpmFiles(path, pkg, nowConfig, opts); } else { - files = await getDockerFiles(path, nowConfig, opts) + files = await getDockerFiles(path, nowConfig, opts); } if (this._debug) { - console.timeEnd('> [debug] Getting files') + console.timeEnd("> [debug] Getting files"); } - forwardNpm = forwardNpm || (nowConfig && nowConfig.forwardNpm) + forwardNpm = forwardNpm || (nowConfig && nowConfig.forwardNpm); // Read .npmrc - let npmrc = {} - let authToken - if (deploymentType === 'npm' && forwardNpm) { + let npmrc = {}; + let authToken; + if (deploymentType === "npm" && forwardNpm) { try { - npmrc = await readFile(resolvePath(path, '.npmrc'), 'utf8') - npmrc = parseIni(npmrc) - authToken = npmrc['//registry.npmjs.org/:_authToken'] + npmrc = await readFile(resolvePath(path, ".npmrc"), "utf8"); + npmrc = parseIni(npmrc); + authToken = npmrc["//registry.npmjs.org/:_authToken"]; } catch (err) { // Do nothing } if (!authToken) { try { - npmrc = await readFile(resolvePath(homedir(), '.npmrc'), 'utf8') - npmrc = parseIni(npmrc) - authToken = npmrc['//registry.npmjs.org/:_authToken'] + npmrc = await readFile(resolvePath(homedir(), ".npmrc"), "utf8"); + npmrc = parseIni(npmrc); + authToken = npmrc["//registry.npmjs.org/:_authToken"]; } catch (err) { // Do nothing } @@ -103,64 +106,71 @@ module.exports = class Now extends EventEmitter { } if (this._debug) { - console.time('> [debug] Computing hashes') + console.time("> [debug] Computing hashes"); } - const pkgDetails = {} - pkgDetails.name = name + const pkgDetails = {}; + pkgDetails.name = name; - Object.assign(pkgDetails, pkg) + Object.assign(pkgDetails, pkg); - const hashes = await hash(files, isStatic, pkgDetails) + const hashes = await hash(files, isStatic, pkgDetails); if (this._debug) { - console.timeEnd('> [debug] Computing hashes') + console.timeEnd("> [debug] Computing hashes"); } - this._files = hashes + this._files = hashes; - const engines = (nowConfig && nowConfig.engines) || pkg.engines + const engines = (nowConfig && nowConfig.engines) || pkg.engines; const deployment = await this.retry(async bail => { if (this._debug) { - console.time('> [debug] /now/create') + console.time("> [debug] /now/create"); } // Flatten the array to contain files to sync where each nested input // array has a group of files with the same sha but different path - const files = await Promise.all(Array.prototype.concat.apply([], await Promise.all((Array.from(this._files)).map(async ([sha, {data, names}]) => { - const statFn = followSymlinks ? stat : lstat - - return await names.map(async name => { - let mode - - const getMode = async () => { - const st = await statFn(name) - return st.mode - } - - if (this._static) { - if (toRelative(name, this._path) === 'package.json') { - mode = 33261 - } else { - mode = await getMode() - name = this.pathInsideContent(name) - } - } else { - mode = await getMode() - } - - return { - sha, - size: data.length, - file: toRelative(name, this._path), - mode - } - }) - })))) + const files = await Promise.all( + Array.prototype.concat.apply( + [], + await Promise.all( + Array.from(this._files).map(async ([sha, { data, names }]) => { + const statFn = followSymlinks ? stat : lstat; + + return await names.map(async name => { + let mode; + + const getMode = async () => { + const st = await statFn(name); + return st.mode; + }; + + if (this._static) { + if (toRelative(name, this._path) === "package.json") { + mode = 33261; + } else { + mode = await getMode(); + name = this.pathInsideContent(name); + } + } else { + mode = await getMode(); + } + + return { + sha, + size: data.length, + file: toRelative(name, this._path), + mode + }; + }); + }) + ) + ) + ); - const res = await this._fetch('/now/create', { - method: 'POST', + const res = await this._fetch("/now/create", { + method: "POST", body: { env, public: wantsPublic, @@ -173,533 +183,591 @@ module.exports = class Now extends EventEmitter { files, engines } - }) + }); if (this._debug) { - console.timeEnd('> [debug] /now/create') + console.timeEnd("> [debug] /now/create"); } // no retry on 4xx - let body + let body; try { - body = await res.json() + body = await res.json(); } catch (err) { - throw new Error('Unexpected response') + throw new Error("Unexpected response"); } if (res.status === 429) { - let msg = `You reached your 20 deployments limit in the OSS plan.\n` - msg += `${chalk.gray('>')} Please run ${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')} to proceed` - const err = new Error(msg) - err.status = res.status - err.retryAfter = 'never' - return bail(err) + let msg = `You reached your 20 deployments limit in the OSS plan.\n`; + msg += `${chalk.gray(">")} Please run ${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")} to proceed`; + const err = new Error(msg); + err.status = res.status; + err.retryAfter = "never"; + return bail(err); } else if (res.status >= 400 && res.status < 500) { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } else if (res.status !== 200) { - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); // we report about files whose sizes are too big - let missingVersion = false + let missingVersion = false; if (deployment.warnings) { - let sizeExceeded = 0 + let sizeExceeded = 0; deployment.warnings.forEach(warning => { - if (warning.reason === 'size_limit_exceeded') { - const {sha, limit} = warning - const n = hashes.get(sha).names.pop() - console.error('> \u001b[31mWarning!\u001b[39m Skipping file %s (size exceeded %s)', + if (warning.reason === "size_limit_exceeded") { + const { sha, limit } = warning; + const n = hashes.get(sha).names.pop(); + console.error( + "> \u001b[31mWarning!\u001b[39m Skipping file %s (size exceeded %s)", n, bytes(limit) - ) - hashes.get(sha).names.unshift(n) // move name (hack, if duplicate matches we report them in order) - sizeExceeded++ - } else if (warning.reason === 'node_version_not_found') { - const {wanted, used} = warning - console.error('> \u001b[31mWarning!\u001b[39m Requested node version %s is not available', + ); + hashes.get(sha).names.unshift(n); // move name (hack, if duplicate matches we report them in order) + sizeExceeded++; + } else if (warning.reason === "node_version_not_found") { + const { wanted, used } = warning; + console.error( + "> \u001b[31mWarning!\u001b[39m Requested node version %s is not available", wanted, used - ) - missingVersion = true + ); + missingVersion = true; } - }) + }); if (sizeExceeded) { - console.error(`> \u001b[31mWarning!\u001b[39m ${sizeExceeded} of the files ` + - 'exceeded the limit for your plan.\n' + - `> Please run ${chalk.gray('`')}${chalk.cyan('now upgrade')}${chalk.gray('`')} to upgrade.`) + console.error( + `> \u001b[31mWarning!\u001b[39m ${sizeExceeded} of the files ` + + "exceeded the limit for your plan.\n" + + `> Please run ${chalk.gray("`")}${chalk.cyan("now upgrade")}${chalk.gray("`")} to upgrade.` + ); } } - if (!quiet && deploymentType === 'npm' && deployment.nodeVersion) { + if (!quiet && deploymentType === "npm" && deployment.nodeVersion) { if (engines && engines.node) { if (missingVersion) { - console.log(`> Using Node.js ${chalk.bold(deployment.nodeVersion)} (default)`) + console.log( + `> Using Node.js ${chalk.bold(deployment.nodeVersion)} (default)` + ); } else { - console.log(`> Using Node.js ${chalk.bold(deployment.nodeVersion)} (requested: ${chalk.dim(`\`${engines.node}\``)})`) + console.log( + `> Using Node.js ${chalk.bold(deployment.nodeVersion)} (requested: ${chalk.dim(`\`${engines.node}\``)})` + ); } } else { - console.log(`> Using Node.js ${chalk.bold(deployment.nodeVersion)} (default)`) + console.log( + `> Using Node.js ${chalk.bold(deployment.nodeVersion)} (default)` + ); } } - this._id = deployment.deploymentId - this._host = deployment.url - this._missing = deployment.missing || [] + this._id = deployment.deploymentId; + this._host = deployment.url; + this._missing = deployment.missing || []; - return this._url + return this._url; } pathInsideContent(position) { - const relativePath = toRelative(position, this._path) - const contentDir = joinPaths(this._path, 'content') - const newPath = joinPaths(contentDir, relativePath) + const relativePath = toRelative(position, this._path); + const contentDir = joinPaths(this._path, "content"); + const newPath = joinPaths(contentDir, relativePath); - return newPath + return newPath; } upload() { - const parts = splitArray(this._missing, MAX_CONCURRENT) + const parts = splitArray(this._missing, MAX_CONCURRENT); if (this._debug) { - console.log('> [debug] Will upload ' + - `${this._missing.length} files in ${parts.length} ` + - `steps of ${MAX_CONCURRENT} uploads.`) + console.log( + "> [debug] Will upload " + + `${this._missing.length} files in ${parts.length} ` + + `steps of ${MAX_CONCURRENT} uploads.` + ); } const uploadChunk = () => { - Promise.all(parts.shift().map(sha => retry(async (bail, attempt) => { - const file = this._files.get(sha) - const {data, names} = file - - if (this._debug) { - console.time(`> [debug] /sync #${attempt} ${names.join(' ')}`) - } - - const stream = resumer().queue(data).end() - const res = await this._fetch('/now/sync', { - method: 'POST', - headers: { - 'Content-Type': 'application/octet-stream', - 'Content-Length': data.length, - 'x-now-deployment-id': this._id, - 'x-now-sha': sha, - 'x-now-file': names.map(name => { - if (this._static) { - name = this.pathInsideContent(name) + Promise.all( + parts.shift().map(sha => + retry( + async (bail, attempt) => { + const file = this._files.get(sha); + const { data, names } = file; + + if (this._debug) { + console.time(`> [debug] /sync #${attempt} ${names.join(" ")}`); } - return toRelative(encodeURIComponent(name), this._path) - }).join(','), - 'x-now-size': data.length - }, - body: stream - }) - - if (this._debug) { - console.timeEnd(`> [debug] /sync #${attempt} ${names.join(' ')}`) - } - - // no retry on 4xx - if (res.status !== 200 && (res.status >= 400 || res.status < 500)) { - if (this._debug) { - console.log('> [debug] bailing on creating due to %s', res.status) - } + const stream = resumer().queue(data).end(); + const res = await this._fetch("/now/sync", { + method: "POST", + headers: { + "Content-Type": "application/octet-stream", + "Content-Length": data.length, + "x-now-deployment-id": this._id, + "x-now-sha": sha, + "x-now-file": names + .map(name => { + if (this._static) { + name = this.pathInsideContent(name); + } + + return toRelative(encodeURIComponent(name), this._path); + }) + .join(","), + "x-now-size": data.length + }, + body: stream + }); + + if (this._debug) { + console.timeEnd( + `> [debug] /sync #${attempt} ${names.join(" ")}` + ); + } - return bail(responseError(res)) - } + // no retry on 4xx + if ( + res.status !== 200 && (res.status >= 400 || res.status < 500) + ) { + if (this._debug) { + console.log( + "> [debug] bailing on creating due to %s", + res.status + ); + } + + return bail(responseError(res)); + } - this.emit('upload', file) - }, {retries: 3, randomize: true, onRetry: this._onRetry}))) - .then(() => parts.length ? uploadChunk() : this.emit('complete')) - .catch(err => this.emit('error', err)) - } + this.emit("upload", file); + }, + { retries: 3, randomize: true, onRetry: this._onRetry } + )) + ) + .then(() => parts.length ? uploadChunk() : this.emit("complete")) + .catch(err => this.emit("error", err)); + }; - uploadChunk() + uploadChunk(); } async listSecrets() { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} GET /secrets`) + console.time(`> [debug] #${attempt} GET /secrets`); } - const res = await this._fetch('/now/secrets') + const res = await this._fetch("/now/secrets"); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} GET /secrets`) + console.timeEnd(`> [debug] #${attempt} GET /secrets`); } - const body = await res.json() - return body.secrets - }) + const body = await res.json(); + return body.secrets; + }); } async list(app) { - const query = app ? `?app=${encodeURIComponent(app)}` : '' + const query = app ? `?app=${encodeURIComponent(app)}` : ""; - const {deployments} = await this.retry(async bail => { - if (this._debug) { - console.time('> [debug] /list') - } - - const res = await this._fetch('/now/list' + query) + const { deployments } = await this.retry( + async bail => { + if (this._debug) { + console.time("> [debug] /list"); + } - if (this._debug) { - console.timeEnd('> [debug] /list') - } + const res = await this._fetch("/now/list" + query); - // no retry on 4xx - if (res.status >= 400 && res.status < 500) { if (this._debug) { - console.log('> [debug] bailing on listing due to %s', res.status) + console.timeEnd("> [debug] /list"); } - return bail(responseError(res)) - } - if (res.status !== 200) { - throw new Error('Fetching deployment list failed') - } + // no retry on 4xx + if (res.status >= 400 && res.status < 500) { + if (this._debug) { + console.log("> [debug] bailing on listing due to %s", res.status); + } + return bail(responseError(res)); + } - return res.json() - }, {retries: 3, minTimeout: 2500, onRetry: this._onRetry}) + if (res.status !== 200) { + throw new Error("Fetching deployment list failed"); + } + + return res.json(); + }, + { retries: 3, minTimeout: 2500, onRetry: this._onRetry } + ); - return deployments + return deployments; } async listAliases(deploymentId) { return this.retry(async () => { - const res = await this._fetch(deploymentId ? `/now/deployments/${deploymentId}/aliases` : '/now/aliases') - const body = await res.json() - return body.aliases - }) + const res = await this._fetch( + deploymentId + ? `/now/deployments/${deploymentId}/aliases` + : "/now/aliases" + ); + const body = await res.json(); + return body.aliases; + }); } async last(app) { - const deployments = await this.list(app) + const deployments = await this.list(app); - const last = deployments.sort((a, b) => { - return b.created - a.created - }).shift() + const last = deployments + .sort((a, b) => { + return b.created - a.created; + }) + .shift(); if (!last) { - const e = Error(`No deployments found for "${app}"`) - e.userError = true - throw e + const e = Error(`No deployments found for "${app}"`); + e.userError = true; + throw e; } - return last + return last; } async listDomains() { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} GET /domains`) + console.time(`> [debug] #${attempt} GET /domains`); } - const res = await this._fetch('/domains') + const res = await this._fetch("/domains"); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} GET /domains`) + console.timeEnd(`> [debug] #${attempt} GET /domains`); } - const body = await res.json() - return body.domains - }) + const body = await res.json(); + return body.domains; + }); } async getDomain(domain) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} GET /domains/${domain}`) + console.time(`> [debug] #${attempt} GET /domains/${domain}`); } - const res = await this._fetch(`/domains/${domain}`) + const res = await this._fetch(`/domains/${domain}`); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} GET /domains/${domain}`) + console.timeEnd(`> [debug] #${attempt} GET /domains/${domain}`); } - return await res.json() - }) + return await res.json(); + }); } getNameservers(domain) { return new Promise((resolve, reject) => { - let fallback = false + let fallback = false; this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} GET /whois-ns${fallback ? '-fallback' : ''}`) + console.time( + `> [debug] #${attempt} GET /whois-ns${fallback ? "-fallback" : ""}` + ); } - const res = await this._fetch(`/whois-ns${fallback ? '-fallback' : ''}?domain=${encodeURIComponent(domain)}`) + const res = await this._fetch( + `/whois-ns${fallback ? "-fallback" : ""}?domain=${encodeURIComponent(domain)}` + ); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} GET /whois-ns${fallback ? '-fallback' : ''}`) + console.timeEnd( + `> [debug] #${attempt} GET /whois-ns${fallback ? "-fallback" : ""}` + ); } - const body = await res.json() + const body = await res.json(); if (res.status === 200) { - if ((!body.nameservers || body.nameservers.length === 0) && !fallback) { + if ( + (!body.nameservers || body.nameservers.length === 0) && !fallback + ) { // if the nameservers are `null` it's likely // that our whois service failed to parse it - fallback = true - throw new Error('Invalid whois response') + fallback = true; + throw new Error("Invalid whois response"); } - return body + return body; } if (attempt > 1) { - fallback = true + fallback = true; } - throw new Error(`Whois error (${res.status}): ${body.error.message}`) - }).then(body => { - body.nameservers = body.nameservers.filter(ns => { - // temporary hack: - // sometimes we get a response that looks like: - // ['ns', 'ns', '', ''] - // so we filter the empty ones - return ns.length - }) - resolve(body) - }).catch(err => { - reject(err) + throw new Error(`Whois error (${res.status}): ${body.error.message}`); }) - }) + .then(body => { + body.nameservers = body.nameservers.filter(ns => { + // temporary hack: + // sometimes we get a response that looks like: + // ['ns', 'ns', '', ''] + // so we filter the empty ones + return ns.length; + }); + resolve(body); + }) + .catch(err => { + reject(err); + }); + }); } // _ensures_ the domain is setup (idempotent) - setupDomain(name, {isExternal} = {}) { + setupDomain(name, { isExternal } = {}) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} POST /domains`) + console.time(`> [debug] #${attempt} POST /domains`); } - const res = await this._fetch('/domains', { - method: 'POST', - body: {name, isExternal: Boolean(isExternal)} - }) + const res = await this._fetch("/domains", { + method: "POST", + body: { name, isExternal: Boolean(isExternal) } + }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} POST /domains`) + console.timeEnd(`> [debug] #${attempt} POST /domains`); } - const body = await res.json() + const body = await res.json(); if (res.status === 403) { - const code = body.error.code - let err + const code = body.error.code; + let err; - if (code === 'custom_domain_needs_upgrade') { - err = new Error(`Custom domains are only enabled for premium accounts. Please upgrade at ${chalk.underline('https://zeit.co/account')}.`) + if (code === "custom_domain_needs_upgrade") { + err = new Error( + `Custom domains are only enabled for premium accounts. Please upgrade at ${chalk.underline("https://zeit.co/account")}.` + ); } else { - err = new Error(`Not authorized to access domain ${name}`) + err = new Error(`Not authorized to access domain ${name}`); } - err.userError = true - return bail(err) - } else if (res.status === 409) { // domain already exists + err.userError = true; + return bail(err); + } else if (res.status === 409) { + // domain already exists if (this._debug) { - console.log('> [debug] Domain already exists (noop)') + console.log("> [debug] Domain already exists (noop)"); } - return {uid: body.error.uid, code: body.error.code} - } else if (res.status === 401 && body.error && body.error.code === 'verification_failed') { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + return { uid: body.error.uid, code: body.error.code }; + } else if ( + res.status === 401 && + body.error && + body.error.code === "verification_failed" + ) { + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } else if (res.status !== 200) { - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } - createCert(domain, {renew} = {}) { - return this.retry(async (bail, attempt) => { - if (this._debug) { - console.time(`> [debug] /now/certs #${attempt}`) - } - - const res = await this._fetch('/now/certs', { - method: 'POST', - body: { - domains: [domain], - renew + createCert(domain, { renew } = {}) { + return this.retry( + async (bail, attempt) => { + if (this._debug) { + console.time(`> [debug] /now/certs #${attempt}`); } - }) - if (res.status === 304) { - console.log('> Certificate already issued.') - return - } + const res = await this._fetch("/now/certs", { + method: "POST", + body: { + domains: [domain], + renew + } + }); - const body = await res.json() + if (res.status === 304) { + console.log("> Certificate already issued."); + return; + } - if (this._debug) { - console.timeEnd(`> [debug] /now/certs #${attempt}`) - } + const body = await res.json(); - if (body.error) { - const {code} = body.error - - if (code === 'verification_failed') { - const err = new Error('The certificate issuer failed to verify ownership of the domain. ' + - 'This likely has to do with DNS propagation and caching issues. Please retry later!') - err.userError = true - // retry - throw err - } else if (code === 'rate_limited') { - const err = new Error(body.error.message) - err.userError = true - // dont retry - return bail(err) + if (this._debug) { + console.timeEnd(`> [debug] /now/certs #${attempt}`); } - throw new Error(body.error.message) - } + if (body.error) { + const { code } = body.error; + + if (code === "verification_failed") { + const err = new Error( + "The certificate issuer failed to verify ownership of the domain. " + + "This likely has to do with DNS propagation and caching issues. Please retry later!" + ); + err.userError = true; + // retry + throw err; + } else if (code === "rate_limited") { + const err = new Error(body.error.message); + err.userError = true; + // dont retry + return bail(err); + } - if (res.status !== 200 && res.status !== 304) { - throw new Error('Unhandled error') - } - return body - }, {retries: 5, minTimeout: 30000, maxTimeout: 90000}) + throw new Error(body.error.message); + } + + if (res.status !== 200 && res.status !== 304) { + throw new Error("Unhandled error"); + } + return body; + }, + { retries: 5, minTimeout: 30000, maxTimeout: 90000 } + ); } deleteCert(domain) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] /now/certs #${attempt}`) + console.time(`> [debug] /now/certs #${attempt}`); } const res = await this._fetch(`/now/certs/${domain}`, { - method: 'DELETE' - }) + method: "DELETE" + }); if (res.status !== 200) { - const err = new Error(res.body.error.message) - err.userError = false + const err = new Error(res.body.error.message); + err.userError = false; if (res.status === 400 || res.status === 404) { - return bail(err) + return bail(err); } - throw err + throw err; } - }) + }); } - async remove(deploymentId, {hard}) { - const data = {deploymentId, hard} + async remove(deploymentId, { hard }) { + const data = { deploymentId, hard }; await this.retry(async bail => { if (this._debug) { - console.time('> [debug] /remove') + console.time("> [debug] /remove"); } - const res = await this._fetch('/now/remove', { - method: 'DELETE', + const res = await this._fetch("/now/remove", { + method: "DELETE", body: data - }) + }); if (this._debug) { - console.timeEnd('> [debug] /remove') + console.timeEnd("> [debug] /remove"); } // no retry on 4xx if (res.status >= 400 && res.status < 500) { if (this._debug) { - console.log('> [debug] bailing on removal due to %s', res.status) + console.log("> [debug] bailing on removal due to %s", res.status); } - return bail(responseError(res)) + return bail(responseError(res)); } if (res.status !== 200) { - throw new Error('Removing deployment failed') + throw new Error("Removing deployment failed"); } - }) + }); - return true + return true; } - retry(fn, {retries = 3, maxTimeout = Infinity} = {}) { + retry(fn, { retries = 3, maxTimeout = Infinity } = {}) { return retry(fn, { retries, maxTimeout, onRetry: this._onRetry - }) + }); } _onRetry(err) { if (this._debug) { - console.log(`> [debug] Retrying: ${err}\n${err.stack}`) + console.log(`> [debug] Retrying: ${err}\n${err.stack}`); } } close() { - this._agent.close() + this._agent.close(); } get id() { - return this._id + return this._id; } get url() { - return `https://${this._host}` + return `https://${this._host}`; } get host() { - return this._host + return this._host; } get syncAmount() { if (!this._syncAmount) { this._syncAmount = this._missing - .map(sha => this._files.get(sha).data.length) - .reduce((a, b) => a + b, 0) + .map(sha => this._files.get(sha).data.length) + .reduce((a, b) => a + b, 0); } - return this._syncAmount + return this._syncAmount; } _fetch(_url, opts = {}) { - opts.headers = opts.headers || {} - opts.headers.authorization = `Bearer ${this._token}` - opts.headers['user-agent'] = ua - return this._agent.fetch(_url, opts) + opts.headers = opts.headers || {}; + opts.headers.authorization = `Bearer ${this._token}`; + opts.headers["user-agent"] = ua; + return this._agent.fetch(_url, opts); } -} +}; function toRelative(path, base) { - const fullBase = base.endsWith(SEP) ? base : base + SEP - let relative = path.substr(fullBase.length) + const fullBase = base.endsWith(SEP) ? base : base + SEP; + let relative = path.substr(fullBase.length); if (relative.startsWith(SEP)) { - relative = relative.substr(1) + relative = relative.substr(1); } - return relative.replace(/\\/g, '/') + return relative.replace(/\\/g, "/"); } function responseError(res) { - const err = new Error('Response error') - err.status = res.status + const err = new Error("Response error"); + err.status = res.status; if (res.status === 429) { - const retryAfter = res.headers.get('Retry-After') + const retryAfter = res.headers.get("Retry-After"); if (retryAfter) { - err.retryAfter = parseInt(retryAfter, 10) + err.retryAfter = parseInt(retryAfter, 10); } } - return err + return err; } diff --git a/lib/is-zeit-world.js b/lib/is-zeit-world.js index d40ef43..6edca26 100644 --- a/lib/is-zeit-world.js +++ b/lib/is-zeit-world.js @@ -3,17 +3,17 @@ */ const nameservers = new Set([ - 'california.zeit.world', - 'london.zeit.world', - 'newark.zeit.world', - 'sydney.zeit.world', - 'iowa.zeit.world', - 'dallas.zeit.world', - 'amsterdam.zeit.world', - 'paris.zeit.world', - 'frankfurt.zeit.world', - 'singapore.zeit.world' -]) + "california.zeit.world", + "london.zeit.world", + "newark.zeit.world", + "sydney.zeit.world", + "iowa.zeit.world", + "dallas.zeit.world", + "amsterdam.zeit.world", + "paris.zeit.world", + "frankfurt.zeit.world", + "singapore.zeit.world" +]); /** * Given an array of nameservers (ie: as returned @@ -21,9 +21,10 @@ const nameservers = new Set([ * zeit.world's. */ function isZeitWorld(ns) { - return ns.length > 1 && ns.every(host => { - return nameservers.has(host) - }) + return ns.length > 1 && + ns.every(host => { + return nameservers.has(host); + }); } -module.exports = isZeitWorld +module.exports = isZeitWorld; diff --git a/lib/login.js b/lib/login.js index 3180e24..b99b778 100644 --- a/lib/login.js +++ b/lib/login.js @@ -1,116 +1,125 @@ // Native -const os = require('os') +const os = require("os"); // Packages -const {stringify: stringifyQuery} = require('querystring') -const chalk = require('chalk') -const fetch = require('node-fetch') -const {validate} = require('email-validator') -const readEmail = require('email-prompt') -const ora = require('ora') +const { stringify: stringifyQuery } = require("querystring"); +const chalk = require("chalk"); +const fetch = require("node-fetch"); +const { validate } = require("email-validator"); +const readEmail = require("email-prompt"); +const ora = require("ora"); // Ours -const pkg = require('./pkg') -const ua = require('./ua') -const cfg = require('./cfg') -const info = require('./utils/output/info') -const promptBool = require('./utils/input/prompt-bool') +const pkg = require("./pkg"); +const ua = require("./ua"); +const cfg = require("./cfg"); +const info = require("./utils/output/info"); +const promptBool = require("./utils/input/prompt-bool"); async function getVerificationData(url, email) { - const tokenName = `Now CLI ${os.platform()}-${os.arch()} ${pkg.version} (${os.hostname()})` - const data = JSON.stringify({email, tokenName}) + const tokenName = `Now CLI ${os.platform()}-${os.arch()} ${pkg.version} (${os.hostname()})`; + const data = JSON.stringify({ email, tokenName }); const res = await fetch(`${url}/now/registration`, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data), - 'User-Agent': ua + "Content-Type": "application/json", + "Content-Length": Buffer.byteLength(data), + "User-Agent": ua }, body: data - }) + }); if (res.status !== 200) { - throw new Error('Verification error') + throw new Error("Verification error"); } - const body = await res.json() - return body + const body = await res.json(); + return body; } async function verify(url, email, verificationToken) { const query = { email, token: verificationToken - } - - const res = await fetch(`${url}/now/registration/verify?${stringifyQuery(query)}`, { - headers: {'User-Agent': ua} - }) - const body = await res.json() - return body.token + }; + + const res = await fetch( + `${url}/now/registration/verify?${stringifyQuery(query)}`, + { + headers: { "User-Agent": ua } + } + ); + const body = await res.json(); + return body.token; } function sleep(ms) { return new Promise(resolve => { - setTimeout(resolve, ms) - }) + setTimeout(resolve, ms); + }); } -async function register(url, {retryEmail = false} = {}) { - let email +async function register(url, { retryEmail = false } = {}) { + let email; try { - email = await readEmail({invalid: retryEmail}) + email = await readEmail({ invalid: retryEmail }); } catch (err) { - process.stdout.write('\n') - throw err + process.stdout.write("\n"); + throw err; } - process.stdout.write('\n') + process.stdout.write("\n"); - info(`By continuing, you declare that you agree with ${chalk.bold('https://zeit.co/terms')} and ${chalk.bold('https://zeit.co/privacy.')}`) - if (!await promptBool('Continue?')) { - info('Aborted.') - process.exit() // eslint-disable-line unicorn/no-process-exit + info( + `By continuing, you declare that you agree with ${chalk.bold("https://zeit.co/terms")} and ${chalk.bold("https://zeit.co/privacy.")}` + ); + if (!await promptBool("Continue?")) { + info("Aborted."); + process.exit(); // eslint-disable-line unicorn/no-process-exit } if (!validate(email)) { - return register(url, {retryEmail: true}) + return register(url, { retryEmail: true }); } - const {token, securityCode} = await getVerificationData(url, email) - console.log(`> Please follow the link sent to ${chalk.bold(email)} to log in.`) + const { token, securityCode } = await getVerificationData(url, email); + console.log( + `> Please follow the link sent to ${chalk.bold(email)} to log in.` + ); if (securityCode) { - console.log(`> Verify that the provided security code in the email matches ${chalk.cyan(chalk.bold(securityCode))}.`) + console.log( + `> Verify that the provided security code in the email matches ${chalk.cyan(chalk.bold(securityCode))}.` + ); } - process.stdout.write('\n') + process.stdout.write("\n"); const spinner = ora({ - text: 'Waiting for confirmation...', - color: 'black' - }).start() + text: "Waiting for confirmation...", + color: "black" + }).start(); - let final + let final; do { - await sleep(2500) + await sleep(2500); try { - final = await verify(url, email, token) + final = await verify(url, email, token); } catch (err) {} - } while (!final) + } while (!final); - spinner.text = 'Confirmed email address!' - spinner.stopAndPersist('✔') + spinner.text = "Confirmed email address!"; + spinner.stopAndPersist("✔"); - process.stdout.write('\n') + process.stdout.write("\n"); - return {email, token: final} + return { email, token: final }; } -module.exports = async function (url) { - const loginData = await register(url) - cfg.merge(loginData) - return loginData.token -} +module.exports = async function(url) { + const loginData = await register(url); + cfg.merge(loginData); + return loginData.token; +}; diff --git a/lib/logs.js b/lib/logs.js index 464b2ff..b58dbc1 100644 --- a/lib/logs.js +++ b/lib/logs.js @@ -1,12 +1,12 @@ -exports.compare = function (a, b) { +exports.compare = function(a, b) { return a.serial.localeCompare(b.serial) || // for the case serials are a same value on old logs - a.created.getTime() - b.created.getTime() -} + a.created.getTime() - b.created.getTime(); +}; -exports.deserialize = function (log) { +exports.deserialize = function(log) { return Object.assign({}, log, { data: new Date(log.date), created: new Date(log.created) - }) -} + }); +}; diff --git a/lib/pkg.js b/lib/pkg.js index fce3a8a..468a01d 100644 --- a/lib/pkg.js +++ b/lib/pkg.js @@ -1,8 +1,8 @@ -let pkg +let pkg; try { - pkg = require('../package.json') + pkg = require("../package.json"); } catch (err) { - pkg = require('../../package.json') + pkg = require("../../package.json"); } -module.exports = pkg +module.exports = pkg; diff --git a/lib/plans.js b/lib/plans.js index c884c7e..7cf6f44 100644 --- a/lib/plans.js +++ b/lib/plans.js @@ -1,48 +1,47 @@ -const ms = require('ms') +const ms = require("ms"); -const Now = require('../lib') +const Now = require("../lib"); async function parsePlan(res) { - let id - let until + let id; + let until; - const {subscription} = await res.json() + const { subscription } = await res.json(); if (subscription) { - id = subscription.plan.id + id = subscription.plan.id; if (subscription.cancel_at_period_end) { until = ms( new Date(subscription.current_period_end * 1000) - new Date(), - {long: true} - ) + { long: true } + ); } } else { - id = 'oss' + id = "oss"; } - return {id, until} + return { id, until }; } module.exports = class Plans extends Now { - async getCurrent() { - const res = await this._fetch('/www/user/plan') + const res = await this._fetch("/www/user/plan"); - return await parsePlan(res) + return await parsePlan(res); } async set(plan) { - const res = await this._fetch('/www/user/plan', { - method: 'PUT', - body: {plan} - }) + const res = await this._fetch("/www/user/plan", { + method: "PUT", + body: { plan } + }); if (res.ok) { - return await parsePlan(res) + return await parsePlan(res); } - const err = new Error(res.statusText) - err.res = res - throw err + const err = new Error(res.statusText); + err.res = res; + throw err; } -} +}; diff --git a/lib/re-alias.js b/lib/re-alias.js index 3cb26f0..c2d4bc8 100644 --- a/lib/re-alias.js +++ b/lib/re-alias.js @@ -1,80 +1,86 @@ // Native -const {join} = require('path') +const { join } = require("path"); // Packages -const fs = require('fs-promise') -const chalk = require('chalk') +const fs = require("fs-promise"); +const chalk = require("chalk"); // Ours -const {error} = require('./error') -const readMetaData = require('./read-metadata') -const NowAlias = require('./alias') +const { error } = require("./error"); +const readMetaData = require("./read-metadata"); +const NowAlias = require("./alias"); exports.assignAlias = async (autoAlias, token, deployment, apiUrl, debug) => { - const aliases = new NowAlias(apiUrl, token, {debug}) - console.log(`> Assigning alias ${chalk.bold.underline(autoAlias)} to deployment...`) + const aliases = new NowAlias(apiUrl, token, { debug }); + console.log( + `> Assigning alias ${chalk.bold.underline(autoAlias)} to deployment...` + ); // Assign alias - await aliases.set(String(deployment), String(autoAlias)) -} + await aliases.set(String(deployment), String(autoAlias)); +}; exports.reAlias = async (token, host, help, exit, apiUrl, debug, alias) => { - const path = process.cwd() + const path = process.cwd(); const configFiles = { - pkg: join(path, 'package.json'), - nowJSON: join(path, 'now.json') - } + pkg: join(path, "package.json"), + nowJSON: join(path, "now.json") + }; if (!fs.existsSync(configFiles.pkg) && !fs.existsSync(configFiles.nowJSON)) { - error(`Couldn't find a now.json or package.json file with an alias list in it`) - return + error( + `Couldn't find a now.json or package.json file with an alias list in it` + ); + return; } - const {nowConfig, name} = await readMetaData(path, { - deploymentType: 'npm', // hard coding settings… + const { nowConfig, name } = await readMetaData(path, { + deploymentType: "npm", // hard coding settings… quiet: true // `quiet` - }) + }); if (!host) { - const lastAlias = await alias.last(name) - host = lastAlias.url + const lastAlias = await alias.last(name); + host = lastAlias.url; } if (!nowConfig) { - help() - return exit(0) + help(); + return exit(0); } - let pointers = [] + let pointers = []; if (nowConfig.alias) { - const value = nowConfig.alias + const value = nowConfig.alias; - if (typeof value === 'string') { - pointers.push(value) + if (typeof value === "string") { + pointers.push(value); } else if (Array.isArray(value)) { - pointers = pointers.concat(nowConfig.alias) + pointers = pointers.concat(nowConfig.alias); } else { - error(`Property ${chalk.grey('aliases')} is not a valid array or string`) - return exit(1) + error(`Property ${chalk.grey("aliases")} is not a valid array or string`); + return exit(1); } } if (nowConfig.aliases && Array.isArray(nowConfig.aliases)) { - console.log(`${chalk.red('Deprecated!')} The property ${chalk.grey('aliases')} will be ` + - `removed from the config file soon.`) - console.log('Read more about the new way here: http://bit.ly/2l2v5Fg\n') + console.log( + `${chalk.red("Deprecated!")} The property ${chalk.grey("aliases")} will be ` + + `removed from the config file soon.` + ); + console.log("Read more about the new way here: http://bit.ly/2l2v5Fg\n"); - pointers = pointers.concat(nowConfig.aliases) + pointers = pointers.concat(nowConfig.aliases); } if (pointers.length === 0) { - help() - return exit(0) + help(); + return exit(0); } for (const pointer of pointers) { - await exports.assignAlias(pointer, token, host, apiUrl, debug) + await exports.assignAlias(pointer, token, host, apiUrl, debug); } -} +}; diff --git a/lib/read-metadata.js b/lib/read-metadata.js index 9860ca9..71593dc 100644 --- a/lib/read-metadata.js +++ b/lib/read-metadata.js @@ -1,42 +1,45 @@ -const {basename, resolve: resolvePath} = require('path') -const chalk = require('chalk') -const {readFile, exists} = require('fs-promise') -const {parse: parseDockerfile} = require('docker-file-parser') +const { basename, resolve: resolvePath } = require("path"); +const chalk = require("chalk"); +const { readFile, exists } = require("fs-promise"); +const { parse: parseDockerfile } = require("docker-file-parser"); const listPackage = { scripts: { start: `NODE_ENV='production' serve ./content` }, dependencies: { - serve: '4.0.1' + serve: "4.0.1" } -} - -module.exports = readMetaData - -async function readMetaData(path, { - deploymentType = 'npm', - deploymentName, - quiet = false, - strict = true, - isStatic = false -}) { - let pkg = {} - let nowConfig = null - let hasNowJson = false +}; + +module.exports = readMetaData; + +async function readMetaData( + path, + { + deploymentType = "npm", + deploymentName, + quiet = false, + strict = true, + isStatic = false + } +) { + let pkg = {}; + let nowConfig = null; + let hasNowJson = false; - let name - let description + let name; + let description; try { - nowConfig = JSON.parse(await readFile(resolvePath(path, 'now.json'))) - hasNowJson = true + nowConfig = JSON.parse(await readFile(resolvePath(path, "now.json"))); + hasNowJson = true; } catch (err) { // if the file doesn't exist then that's fine; any other error bubbles up - if (err.code !== 'ENOENT') { - const e = Error(`Failed to read JSON in "${path}/now.json"`) - e.userError = true - throw e + if (err.code !== "ENOENT") { + const e = Error(`Failed to read JSON in "${path}/now.json"`); + e.userError = true; + throw e; } } @@ -44,113 +47,130 @@ async function readMetaData(path, { // user can specify the type of deployment explicitly in the `now.json` file // when both a package.json and Dockerfile exist if (nowConfig.type) { - deploymentType = nowConfig.type - } else if (nowConfig.type === undefined && !await exists(resolvePath(path, 'package.json'))) { - deploymentType = 'static' + deploymentType = nowConfig.type; + } else if ( + nowConfig.type === undefined && + !await exists(resolvePath(path, "package.json")) + ) { + deploymentType = "static"; } if (nowConfig.name) { - deploymentName = nowConfig.name + deploymentName = nowConfig.name; } } - if (deploymentType === 'static') { - isStatic = true - deploymentType = 'npm' + if (deploymentType === "static") { + isStatic = true; + deploymentType = "npm"; } - if (deploymentType === 'npm') { + if (deploymentType === "npm") { if (isStatic) { - pkg = listPackage + pkg = listPackage; } else { try { - pkg = JSON.parse(await readFile(resolvePath(path, 'package.json'))) + pkg = JSON.parse(await readFile(resolvePath(path, "package.json"))); } catch (err) { - const e = Error(`Failed to read JSON in "${path}/package.json"`) - e.userError = true - throw e + const e = Error(`Failed to read JSON in "${path}/package.json"`); + e.userError = true; + throw e; } } - if (strict && (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start']))) { - const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' + - 'See: https://docs.npmjs.com/cli/start.') - e.userError = true - throw e + if ( + strict && + (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts["now-start"])) + ) { + const e = Error( + "Missing `start` (or `now-start`) script in `package.json`. " + + "See: https://docs.npmjs.com/cli/start." + ); + e.userError = true; + throw e; } if (!deploymentName) { - if (typeof pkg.name === 'string') { - name = pkg.name + if (typeof pkg.name === "string") { + name = pkg.name; } else { - name = basename(path) + name = basename(path); if (!quiet && !isStatic) { - console.log(`> No \`name\` in \`package.json\`, using ${chalk.bold(name)}`) + console.log( + `> No \`name\` in \`package.json\`, using ${chalk.bold(name)}` + ); } } } - description = pkg.description - } else if (deploymentType === 'docker') { - let docker + description = pkg.description; + } else if (deploymentType === "docker") { + let docker; try { - const dockerfile = await readFile(resolvePath(path, 'Dockerfile'), 'utf8') - docker = parseDockerfile(dockerfile, {includeComments: true}) + const dockerfile = await readFile( + resolvePath(path, "Dockerfile"), + "utf8" + ); + docker = parseDockerfile(dockerfile, { includeComments: true }); } catch (err) { - const e = Error(`Failed to parse "${path}/Dockerfile"`) - e.userError = true - throw e + const e = Error(`Failed to parse "${path}/Dockerfile"`); + e.userError = true; + throw e; } if (strict && docker.length <= 0) { - const e = Error('No commands found in `Dockerfile`') - e.userError = true - throw e + const e = Error("No commands found in `Dockerfile`"); + e.userError = true; + throw e; } - const labels = {} - docker - .filter(cmd => cmd.name === 'LABEL') - .forEach(({args}) => { + const labels = {}; + docker.filter(cmd => cmd.name === "LABEL").forEach(({ args }) => { for (const key in args) { if (!{}.hasOwnProperty.call(args, key)) { - continue + continue; } // unescape and convert into string try { - labels[key] = JSON.parse(args[key]) + labels[key] = JSON.parse(args[key]); } catch (err) { - const e = Error(`Error parsing value for LABEL ${key} in \`Dockerfile\``) - e.userError = true - throw e + const e = Error( + `Error parsing value for LABEL ${key} in \`Dockerfile\`` + ); + e.userError = true; + throw e; } } - }) + }); if (!deploymentName) { if (labels.name) { - name = labels.name + name = labels.name; } else { - name = basename(path) + name = basename(path); if (!quiet) { if (hasNowJson) { - console.log(`> No \`name\` LABEL in \`Dockerfile\` or \`name\` field in \`now.json\`, using ${chalk.bold(name)}`) + console.log( + `> No \`name\` LABEL in \`Dockerfile\` or \`name\` field in \`now.json\`, using ${chalk.bold(name)}` + ); } else { - console.log(`> No \`name\` LABEL in \`Dockerfile\`, using ${chalk.bold(name)}`) + console.log( + `> No \`name\` LABEL in \`Dockerfile\`, using ${chalk.bold(name)}` + ); } } } } - description = labels.description + description = labels.description; } else { - throw new TypeError(`Unsupported "deploymentType": ${deploymentType}`) + throw new TypeError(`Unsupported "deploymentType": ${deploymentType}`); } if (deploymentName) { - name = deploymentName + name = deploymentName; } if (pkg.now) { @@ -158,13 +178,15 @@ async function readMetaData(path, { // file, then fail hard and let the user know that they need to pick one or the // other if (hasNowJson) { - const e = new Error('You have a `now` configuration field' + - 'inside `package.json`, but configuration is also present' + - 'in `now.json`! Please ensure there\'s a single source of configuration by removing one') - e.userError = true - throw e + const e = new Error( + "You have a `now` configuration field" + + "inside `package.json`, but configuration is also present" + + "in `now.json`! Please ensure there's a single source of configuration by removing one" + ); + e.userError = true; + throw e; } else { - nowConfig = pkg.now + nowConfig = pkg.now; } } @@ -175,5 +197,5 @@ async function readMetaData(path, { pkg, nowConfig, hasNowJson - } + }; } diff --git a/lib/secrets.js b/lib/secrets.js index 6b0451d..0af647f 100644 --- a/lib/secrets.js +++ b/lib/secrets.js @@ -1,115 +1,117 @@ // Ours -const Now = require('../lib') +const Now = require("../lib"); module.exports = class Secrets extends Now { ls() { - return this.listSecrets() + return this.listSecrets(); } rm(nameOrId) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`) + console.time(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`); } - const res = await this._fetch(`/now/secrets/${nameOrId}`, {method: 'DELETE'}) + const res = await this._fetch(`/now/secrets/${nameOrId}`, { + method: "DELETE" + }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`) + console.timeEnd(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } - const body = await res.json() + const body = await res.json(); if (res.status !== 200) { if (res.status === 404 || res.status === 400) { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } add(name, value) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} POST /secrets`) + console.time(`> [debug] #${attempt} POST /secrets`); } - const res = await this._fetch('/now/secrets', { - method: 'POST', + const res = await this._fetch("/now/secrets", { + method: "POST", body: { name, value: value.toString() } - }) + }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} POST /secrets`) + console.timeEnd(`> [debug] #${attempt} POST /secrets`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } - const body = await res.json() + const body = await res.json(); if (res.status !== 200) { if (res.status === 404 || res.status === 400) { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } rename(nameOrId, newName) { return this.retry(async (bail, attempt) => { if (this._debug) { - console.time(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`) + console.time(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`); } const res = await this._fetch(`/now/secrets/${nameOrId}`, { - method: 'PATCH', + method: "PATCH", body: { name: newName } - }) + }); if (this._debug) { - console.timeEnd(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`) + console.timeEnd(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`); } if (res.status === 403) { - return bail(new Error('Unauthorized')) + return bail(new Error("Unauthorized")); } - const body = await res.json() + const body = await res.json(); if (res.status !== 200) { if (res.status === 404 || res.status === 400) { - const err = new Error(body.error.message) - err.userError = true - return bail(err) + const err = new Error(body.error.message); + err.userError = true; + return bail(err); } - throw new Error(body.error.message) + throw new Error(body.error.message); } - return body - }) + return body; + }); } -} +}; diff --git a/lib/strlen.js b/lib/strlen.js index 1080706..23a0c7a 100644 --- a/lib/strlen.js +++ b/lib/strlen.js @@ -1,5 +1,5 @@ function strlen(str) { - return str.replace(/\x1b[^m]*m/g, '').length + return str.replace(/\x1b[^m]*m/g, "").length; } -module.exports = strlen +module.exports = strlen; diff --git a/lib/test.js b/lib/test.js index fae66c5..73dcf0d 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,22 +1,22 @@ // Native -const {resolve} = require('path') +const { resolve } = require("path"); // Ours -const {npm: getFiles} = require('./get-files') +const { npm: getFiles } = require("./get-files"); -getFiles(resolve('../mng-test/files-in-package')) -.then(files => { - console.log(files) +getFiles(resolve("../mng-test/files-in-package")) + .then(files => { + console.log(files); - getFiles(resolve('../mng-test/files-in-package-ignore')) - .then(files2 => { - console.log('ignored: ') - console.log(files2) + getFiles(resolve("../mng-test/files-in-package-ignore")) + .then(files2 => { + console.log("ignored: "); + console.log(files2); + }) + .catch(err => { + console.log(err.stack); + }); }) .catch(err => { - console.log(err.stack) - }) -}) -.catch(err => { - console.log(err.stack) -}) + console.log(err.stack); + }); diff --git a/lib/to-host.js b/lib/to-host.js index daae30a..32bf004 100644 --- a/lib/to-host.js +++ b/lib/to-host.js @@ -1,5 +1,5 @@ // Native -const {parse} = require('url') +const { parse } = require("url"); /** * Converts a valid deployment lookup parameter to a hostname. @@ -9,12 +9,12 @@ const {parse} = require('url') function toHost(url) { if (/^https?:\/\//.test(url)) { - return parse(url).host + return parse(url).host; } // remove any path if present // `a.b.c/` => `a.b.c` - return url.replace(/(\/\/)?([^/]+)(.*)/, '$2') + return url.replace(/(\/\/)?([^/]+)(.*)/, "$2"); } -module.exports = toHost +module.exports = toHost; diff --git a/lib/ua.js b/lib/ua.js index 82468e7..df70711 100644 --- a/lib/ua.js +++ b/lib/ua.js @@ -1,7 +1,7 @@ // Native -const os = require('os') +const os = require("os"); // Ours -const {version} = require('./pkg') +const { version } = require("./pkg"); -module.exports = `now ${version} node-${process.version} ${os.platform()} (${os.arch()})` +module.exports = `now ${version} node-${process.version} ${os.platform()} (${os.arch()})`; diff --git a/lib/utils/billing/geocode.js b/lib/utils/billing/geocode.js index 4c6d5fe..0c3e659 100644 --- a/lib/utils/billing/geocode.js +++ b/lib/utils/billing/geocode.js @@ -1,27 +1,33 @@ // Packages -const gMaps = require('@google/maps') +const gMaps = require("@google/maps"); -const MAPS_API_KEY = 'AIzaSyALfKTQ6AiIoJ8WGDXR3E7IBOwlHoTPfYY' +const MAPS_API_KEY = "AIzaSyALfKTQ6AiIoJ8WGDXR3E7IBOwlHoTPfYY"; // eslint-disable-next-line camelcase -module.exports = function ({country, zipCode: postal_code}) { +module.exports = function({ country, zipCode: postal_code }) { return new Promise(resolve => { - const maps = gMaps.createClient({key: MAPS_API_KEY}) - maps.geocode({ - address: `${postal_code} ${country}` // eslint-disable-line camelcase - }, (err, res) => { - if (err || res.json.results.length === 0) { - resolve() - } + const maps = gMaps.createClient({ key: MAPS_API_KEY }); + maps.geocode( + { + address: `${postal_code} ${country}` // eslint-disable-line camelcase + }, + (err, res) => { + if (err || res.json.results.length === 0) { + resolve(); + } - const data = res.json.results[0] - const components = {} - data.address_components.forEach(c => { - components[c.types[0]] = c - }) - const state = components.administrative_area_level_1 - const city = components.locality - resolve({state: state && state.long_name, city: city && city.long_name}) - }) - }) -} + const data = res.json.results[0]; + const components = {}; + data.address_components.forEach(c => { + components[c.types[0]] = c; + }); + const state = components.administrative_area_level_1; + const city = components.locality; + resolve({ + state: state && state.long_name, + city: city && city.long_name + }); + } + ); + }); +}; diff --git a/lib/utils/check-path.js b/lib/utils/check-path.js index 8d71267..b8fa87d 100644 --- a/lib/utils/check-path.js +++ b/lib/utils/check-path.js @@ -1,49 +1,49 @@ // Native -const os = require('os') -const path = require('path') +const os = require("os"); +const path = require("path"); const checkPath = async dir => { if (!dir) { - return + return; } - const home = os.homedir() - let location + const home = os.homedir(); + let location; const paths = { home, - desktop: path.join(home, 'Desktop'), - downloads: path.join(home, 'Downloads') - } + desktop: path.join(home, "Desktop"), + downloads: path.join(home, "Downloads") + }; for (const locationPath in paths) { if (!{}.hasOwnProperty.call(paths, locationPath)) { - continue + continue; } if (dir === paths[locationPath]) { - location = locationPath + location = locationPath; } } if (!location) { - return + return; } - let locationName + let locationName; switch (location) { - case 'home': - locationName = 'user directory' - break - case 'downloads': - locationName = 'downloads directory' - break + case "home": + locationName = "user directory"; + break; + case "downloads": + locationName = "downloads directory"; + break; default: - locationName = location + locationName = location; } - throw new Error(`You're trying to deploy your ${locationName}.`) -} + throw new Error(`You're trying to deploy your ${locationName}.`); +}; -module.exports = checkPath +module.exports = checkPath; diff --git a/lib/utils/exit.js b/lib/utils/exit.js index c2fb1a8..022fadb 100644 --- a/lib/utils/exit.js +++ b/lib/utils/exit.js @@ -4,5 +4,5 @@ module.exports = code => { // stdout writes are asynchronous // https://github.com/nodejs/node/issues/6456 /* eslint-disable unicorn/no-process-exit */ - setTimeout(() => process.exit(code || 0), 100) -} + setTimeout(() => process.exit(code || 0), 100); +}; diff --git a/lib/utils/input/list.js b/lib/utils/input/list.js index 47dc582..dfa6d14 100644 --- a/lib/utils/input/list.js +++ b/lib/utils/input/list.js @@ -1,86 +1,92 @@ -const chalk = require('chalk') -const inquirer = require('inquirer') -const stripAnsi = require('strip-ansi') +const chalk = require("chalk"); +const inquirer = require("inquirer"); +const stripAnsi = require("strip-ansi"); /* eslint-disable no-multiple-empty-lines, no-var, no-undef, no-eq-null, eqeqeq, semi */ -inquirer.prompt.prompts.list.prototype.getQuestion = function () { - var message = chalk.bold('> ' + this.opt.message) + ' ' +inquirer.prompt.prompts.list.prototype.getQuestion = function() { + var message = chalk.bold("> " + this.opt.message) + " "; // Append the default if available, and if question isn't answered - if (this.opt.default != null && this.status !== 'answered') { - message += chalk.dim('(' + this.opt.default + ') ') + if (this.opt.default != null && this.status !== "answered") { + message += chalk.dim("(" + this.opt.default + ") "); } - return message + return message; }; /* eslint-enable */ function getLength(string) { - let biggestLength = 0 - string.split('\n').map(str => { - str = stripAnsi(str) + let biggestLength = 0; + string.split("\n").map(str => { + str = stripAnsi(str); if (str.length > biggestLength) { - biggestLength = str.length + biggestLength = str.length; } - return undefined - }) - return biggestLength + return undefined; + }); + return biggestLength; } -module.exports = async function ({ - message = 'the question', - choices = [{ // eslint-disable-line no-unused-vars - name: 'something\ndescription\ndetails\netc', - value: 'something unique', - short: 'generally the first line of `name`' - }], - pageSize = 15, // Show 15 lines without scrolling (~4 credit cards) - separator = true, // puts a blank separator between each choice - abort = 'end' // wether the `abort` option will be at the `start` or the `end` -}) { - let biggestLength = 0 +module.exports = async function( + { + message = "the question", + // eslint-disable-line no-unused-vars + choices = [ + { + name: "something\ndescription\ndetails\netc", + value: "something unique", + short: "generally the first line of `name`" + } + ], + pageSize = 15, // Show 15 lines without scrolling (~4 credit cards) + separator = true, // puts a blank separator between each choice + abort = "end" // wether the `abort` option will be at the `start` or the `end` + } +) { + let biggestLength = 0; choices = choices.map(choice => { if (choice.name) { - const length = getLength(choice.name) + const length = getLength(choice.name); if (length > biggestLength) { - biggestLength = length + biggestLength = length; } - return choice + return choice; } - throw new Error('Invalid choice') - }) + throw new Error("Invalid choice"); + }); if (separator === true) { - choices = choices.reduce((prev, curr) => ( - prev.concat(new inquirer.Separator(' '), curr) - ), []) + choices = choices.reduce( + (prev, curr) => prev.concat(new inquirer.Separator(" "), curr), + [] + ); } - const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength)) + const abortSeparator = new inquirer.Separator("─".repeat(biggestLength)); const _abort = { - name: 'Abort', + name: "Abort", value: undefined - } + }; - if (abort === 'start') { - const blankSep = choices.shift() - choices.unshift(abortSeparator) - choices.unshift(_abort) - choices.unshift(blankSep) + if (abort === "start") { + const blankSep = choices.shift(); + choices.unshift(abortSeparator); + choices.unshift(_abort); + choices.unshift(blankSep); } else { - choices.push(abortSeparator) - choices.push(_abort) + choices.push(abortSeparator); + choices.push(_abort); } - const nonce = Date.now() + const nonce = Date.now(); const answer = await inquirer.prompt({ name: nonce, - type: 'list', + type: "list", message, choices, pageSize - }) + }); - return answer[nonce] -} + return answer[nonce]; +}; diff --git a/lib/utils/input/prompt-bool.js b/lib/utils/input/prompt-bool.js index 486a5e5..36e7c60 100644 --- a/lib/utils/input/prompt-bool.js +++ b/lib/utils/input/prompt-bool.js @@ -1,56 +1,59 @@ -const chalk = require('chalk') +const chalk = require("chalk"); -module.exports = (label, { - defaultValue = false, - abortSequences = new Set(['\x03']), - resolveChars = new Set(['\r']), - yesChar = 'y', - noChar = 'n', - stdin = process.stdin, - stdout = process.stdout, - trailing = '\n' -} = {}) => { +module.exports = ( + label, + { + defaultValue = false, + abortSequences = new Set(["\x03"]), + resolveChars = new Set(["\r"]), + yesChar = "y", + noChar = "n", + stdin = process.stdin, + stdout = process.stdout, + trailing = "\n" + } = {} +) => { return new Promise((resolve, reject) => { - const isRaw = process.stdin.isRaw + const isRaw = process.stdin.isRaw; - stdin.setRawMode(true) - stdin.resume() + stdin.setRawMode(true); + stdin.resume(); function restore() { - console.log(trailing) - stdin.setRawMode(isRaw) - stdin.pause() - stdin.removeListener('data', onData) + console.log(trailing); + stdin.setRawMode(isRaw); + stdin.pause(); + stdin.removeListener("data", onData); } function onData(buffer) { - const data = buffer.toString() + const data = buffer.toString(); if (abortSequences.has(data)) { - restore() - return reject(new Error('USER_ABORT')) + restore(); + return reject(new Error("USER_ABORT")); } if (resolveChars.has(data[0])) { - restore() - resolve(defaultValue) + restore(); + resolve(defaultValue); } else if (data[0].toLowerCase() === yesChar) { - restore() - resolve(true) + restore(); + resolve(true); } else if (data[0].toLowerCase() === noChar) { - restore() - resolve(false) + restore(); + resolve(false); } else { // ignore extraneous input } } - const defaultText = defaultValue === null ? - `[${yesChar}|${noChar}]` : - defaultValue ? - `[${chalk.bold(yesChar.toUpperCase())}|${noChar}]` : - `[${yesChar}|${chalk.bold(noChar.toUpperCase())}]` - stdout.write(`${chalk.gray('-')} ${label} ${chalk.gray(defaultText)} `) - stdin.on('data', onData) - }) -} + const defaultText = defaultValue === null + ? `[${yesChar}|${noChar}]` + : defaultValue + ? `[${chalk.bold(yesChar.toUpperCase())}|${noChar}]` + : `[${yesChar}|${chalk.bold(noChar.toUpperCase())}]`; + stdout.write(`${chalk.gray("-")} ${label} ${chalk.gray(defaultText)} `); + stdin.on("data", onData); + }); +}; diff --git a/lib/utils/input/text.js b/lib/utils/input/text.js index 9cfd4a1..e963858 100644 --- a/lib/utils/input/text.js +++ b/lib/utils/input/text.js @@ -1,207 +1,223 @@ -const ansiEscapes = require('ansi-escapes') -const ansiRegex = require('ansi-regex') -const chalk = require('chalk') -const stripAnsi = require('strip-ansi') +const ansiEscapes = require("ansi-escapes"); +const ansiRegex = require("ansi-regex"); +const chalk = require("chalk"); +const stripAnsi = require("strip-ansi"); const ESCAPES = { - LEFT: '\x1b[D', - RIGHT: '\x1b[C', - CTRL_C: '\x03', - BACKSPACE: '\x08', - CTRL_H: '\x7f', - CARRIAGE: '\r' -} + LEFT: "\x1b[D", + RIGHT: "\x1b[C", + CTRL_C: "\x03", + BACKSPACE: "\x08", + CTRL_H: "\x7f", + CARRIAGE: "\r" +}; -module.exports = function ({ - label = '', - initialValue = '', - // 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 - autoCompleteChars = new Set([ - '\t', // tab - '\x1b[C' // right arrow - ]) -} = {}) { +module.exports = function( + { + label = "", + initialValue = "", + // 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"]) + } = {} +) { return new Promise((resolve, reject) => { - const isRaw = process.stdin.isRaw + const isRaw = process.stdin.isRaw; - let value - let caretOffset = 0 - let regex - let suggestion = '' + let value; + let caretOffset = 0; + let regex; + let suggestion = ""; - stdout.write(label) - value = initialValue - stdout.write(initialValue) + stdout.write(label); + value = initialValue; + stdout.write(initialValue); if (mask) { if (!value) { - value = chalk.gray(placeholder) - caretOffset = 0 - stripAnsi(value).length - stdout.write(value) - stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) + value = chalk.gray(placeholder); + caretOffset = 0 - stripAnsi(value).length; + stdout.write(value); + stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))); } - 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') + 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() + stdin.setRawMode(true); + stdin.resume(); function restore() { - stdin.setRawMode(isRaw) - stdin.pause() - stdin.removeListener('data', onData) + stdin.setRawMode(isRaw); + stdin.pause(); + stdin.removeListener("data", onData); if (trailing) { - stdout.write(trailing) + stdout.write(trailing); } } async function onData(buffer) { - const data = buffer.toString() - value = stripAnsi(value) + const data = buffer.toString(); + value = stripAnsi(value); if (abortSequences.has(data)) { - restore() - return reject(new Error('USER_ABORT')) + restore(); + return reject(new Error("USER_ABORT")); } - if (suggestion !== '' && !caretOffset && autoCompleteChars.has(data)) { - value += stripAnsi(suggestion) - suggestion = '' + 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) { if (caretOffset < 0) { - caretOffset++ + caretOffset++; } } 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 + 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-- + 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) + char = placeholder[value.length + caretOffset - 1]; + value = value.substr(0, value.length + caretOffset - 1) + + char + + value.substr(value.length + caretOffset); } - caretOffset-- + caretOffset--; } else { value = value.substr(0, value.length + caretOffset - 1) + - value.substr(value.length + caretOffset) + value.substr(value.length + caretOffset); } - suggestion = '' + suggestion = ""; } else if (resolveChars.has(data)) { if (validateValue(value)) { - restore() - resolve(value) + restore(); + resolve(value); } else { - if (mask === 'cc' || mask === 'ccv') { - value = value.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim() - value = value.replace(regex, chalk.gray('$1')) - } else if (mask === 'expDate') { - value = value.replace(regex, chalk.gray('$1')) + if (mask === "cc" || mask === "ccv") { + value = value.replace(/\s/g, "").replace(/(.{4})/g, "$1 ").trim(); + value = value.replace(regex, chalk.gray("$1")); + } else if (mask === "expDate") { + value = value.replace(regex, chalk.gray("$1")); } - const l = chalk.red(label.replace('-', '✖')) - stdout.write(ansiEscapes.eraseLines(1) + l + value + ansiEscapes.beep) + const l = chalk.red(label.replace("-", "✖")); + stdout.write( + ansiEscapes.eraseLines(1) + l + value + ansiEscapes.beep + ); if (caretOffset) { - process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) + process.stdout.write( + ansiEscapes.cursorBackward(Math.abs(caretOffset)) + ); } } - return + return; } else if (!ansiRegex().test(data)) { - let tmp = value.substr(0, value.length + caretOffset) + data + - value.substr(value.length + caretOffset) + let tmp = value.substr(0, value.length + caretOffset) + + data + + value.substr(value.length + caretOffset); if (mask) { if (/\d/.test(data) && caretOffset !== 0) { - if (value[value.length + caretOffset + 1] === ' ') { - tmp = value.substr(0, value.length + caretOffset) + data + - value.substr(value.length + caretOffset + 1) - caretOffset += 2 - if (value[value.length + caretOffset] === '/') { - caretOffset += 2 + if (value[value.length + caretOffset + 1] === " ") { + tmp = value.substr(0, value.length + caretOffset) + + data + + value.substr(value.length + caretOffset + 1); + caretOffset += 2; + if (value[value.length + caretOffset] === "/") { + caretOffset += 2; } } else { - tmp = value.substr(0, value.length + caretOffset) + data + - value.substr(value.length + caretOffset + 1) - caretOffset++ + tmp = value.substr(0, value.length + caretOffset) + + data + + value.substr(value.length + caretOffset + 1); + caretOffset++; } } else if (/\s/.test(data) && caretOffset < 0) { - caretOffset++ - tmp = value + caretOffset++; + tmp = value; } else { - return stdout.write(ansiEscapes.beep) + return stdout.write(ansiEscapes.beep); } - value = tmp + value = tmp; } else if (validateKeypress(data, value)) { - value = tmp + value = tmp; if (caretOffset === 0) { - const completion = await autoComplete(value) + const completion = await autoComplete(value); if (completion) { - suggestion = chalk.gray(completion) - suggestion += ansiEscapes.cursorBackward(completion.length) + suggestion = chalk.gray(completion); + suggestion += ansiEscapes.cursorBackward(completion.length); } else { - suggestion = '' + suggestion = ""; } } } else { - return stdout.write(ansiEscapes.beep) + return stdout.write(ansiEscapes.beep); } } - if (mask === 'cc' || mask === 'ccv') { - value = value.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim() - value = value.replace(regex, chalk.gray('$1')) - } else if (mask === 'expDate') { - value = value.replace(regex, chalk.gray('$1')) + if (mask === "cc" || mask === "ccv") { + value = value.replace(/\s/g, "").replace(/(.{4})/g, "$1 ").trim(); + value = value.replace(regex, chalk.gray("$1")); + } else if (mask === "expDate") { + value = value.replace(regex, chalk.gray("$1")); } - stdout.write(ansiEscapes.eraseLines(1) + label + value + suggestion) + stdout.write(ansiEscapes.eraseLines(1) + label + value + suggestion); if (caretOffset) { - process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))) + process.stdout.write(ansiEscapes.cursorBackward(Math.abs(caretOffset))); } } - stdin.on('data', onData) - }) -} + stdin.on("data", onData); + }); +}; diff --git a/lib/utils/output/cmd.js b/lib/utils/output/cmd.js index 1553f4e..c19508f 100644 --- a/lib/utils/output/cmd.js +++ b/lib/utils/output/cmd.js @@ -1,8 +1,7 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // the equivalent of , for embedding a cmd // eg: Please run ${cmd(woot)} -module.exports = cmd => ( - `${chalk.gray('`')}${chalk.cyan(cmd)}${chalk.gray('`')}` -) +module.exports = cmd => + `${chalk.gray("`")}${chalk.cyan(cmd)}${chalk.gray("`")}`; diff --git a/lib/utils/output/code.js b/lib/utils/output/code.js index 6f26948..a437665 100644 --- a/lib/utils/output/code.js +++ b/lib/utils/output/code.js @@ -1,8 +1,7 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // the equivalent of , for embedding anything // you may want to take a look at ./cmd.js -module.exports = cmd => ( - `${chalk.gray('`')}${chalk.bold(cmd)}${chalk.gray('`')}` -) +module.exports = cmd => + `${chalk.gray("`")}${chalk.bold(cmd)}${chalk.gray("`")}`; diff --git a/lib/utils/output/error.js b/lib/utils/output/error.js index 01a7c01..d936347 100644 --- a/lib/utils/output/error.js +++ b/lib/utils/output/error.js @@ -1,7 +1,6 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // prints an error message module.exports = msg => { - console.error(`${chalk.red('> Error!')} ${msg}`) -} - + console.error(`${chalk.red("> Error!")} ${msg}`); +}; diff --git a/lib/utils/output/info.js b/lib/utils/output/info.js index 9041204..ea8092e 100644 --- a/lib/utils/output/info.js +++ b/lib/utils/output/info.js @@ -1,7 +1,6 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // prints an informational message module.exports = msg => { - console.log(`${chalk.gray('>')} ${msg}`) -} - + console.log(`${chalk.gray(">")} ${msg}`); +}; diff --git a/lib/utils/output/logo.js b/lib/utils/output/logo.js index 1b18411..3c3f49b 100644 --- a/lib/utils/output/logo.js +++ b/lib/utils/output/logo.js @@ -1 +1 @@ -module.exports = process.platform === 'win32' ? 'Δ' : '𝚫' +module.exports = process.platform === "win32" ? "Δ" : "𝚫"; diff --git a/lib/utils/output/param.js b/lib/utils/output/param.js index 7c1445f..9e43c2b 100644 --- a/lib/utils/output/param.js +++ b/lib/utils/output/param.js @@ -1,8 +1,7 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // returns a user param in a nice formatting // e.g.: google.com -> "google.com" (in bold) -module.exports = param => ( - chalk.bold(`${chalk.gray('"')}${chalk.bold(param)}${chalk.gray('"')}`) -) +module.exports = param => + chalk.bold(`${chalk.gray('"')}${chalk.bold(param)}${chalk.gray('"')}`); diff --git a/lib/utils/output/stamp.js b/lib/utils/output/stamp.js index ace9951..f646c04 100644 --- a/lib/utils/output/stamp.js +++ b/lib/utils/output/stamp.js @@ -1,12 +1,10 @@ -const ms = require('ms') -const chalk = require('chalk') +const ms = require("ms"); +const chalk = require("chalk"); // returns a time delta with the right color // example: `[103ms]` module.exports = () => { - const start = new Date() - return () => ( - chalk.gray(`[${ms(new Date() - start)}]`) - ) -} + const start = new Date(); + return () => chalk.gray(`[${ms(new Date() - start)}]`); +}; diff --git a/lib/utils/output/success.js b/lib/utils/output/success.js index dbb7db5..738e6b2 100644 --- a/lib/utils/output/success.js +++ b/lib/utils/output/success.js @@ -1,6 +1,6 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // prints a success message module.exports = msg => { - console.log(`${chalk.cyan('> Success!')} ${msg}`) -} + console.log(`${chalk.cyan("> Success!")} ${msg}`); +}; diff --git a/lib/utils/output/uid.js b/lib/utils/output/uid.js index d36b947..60b9b13 100644 --- a/lib/utils/output/uid.js +++ b/lib/utils/output/uid.js @@ -1,7 +1,5 @@ -const chalk = require('chalk') +const chalk = require("chalk"); // used for including uids in the output // example: `(dom_ji13dj2fih4fi2hf)` -module.exports = id => ( - chalk.gray(`(${id})`) -) +module.exports = id => chalk.gray(`(${id})`); diff --git a/lib/utils/output/wait.js b/lib/utils/output/wait.js index 4b5c37c..7c8b192 100644 --- a/lib/utils/output/wait.js +++ b/lib/utils/output/wait.js @@ -1,15 +1,15 @@ -const ora = require('ora') -const chalk = require('chalk') -const {eraseLine} = require('ansi-escapes') +const ora = require("ora"); +const chalk = require("chalk"); +const { eraseLine } = require("ansi-escapes"); // prints a spinner followed by the given text module.exports = msg => { - const spinner = ora(chalk.gray(msg)) - spinner.color = 'gray' - spinner.start() + const spinner = ora(chalk.gray(msg)); + spinner.color = "gray"; + spinner.start(); return () => { - spinner.stop() - process.stdout.write(eraseLine) - } -} + spinner.stop(); + process.stdout.write(eraseLine); + }; +}; diff --git a/lib/utils/prompt-options.js b/lib/utils/prompt-options.js index b9db67e..4623d95 100644 --- a/lib/utils/prompt-options.js +++ b/lib/utils/prompt-options.js @@ -1,35 +1,35 @@ // Packages -const chalk = require('chalk') +const chalk = require("chalk"); -module.exports = function (opts) { +module.exports = function(opts) { return new Promise((resolve, reject) => { opts.forEach(([, text], i) => { - console.log(`${chalk.gray('>')} [${chalk.bold(i + 1)}] ${text}`) - }) + console.log(`${chalk.gray(">")} [${chalk.bold(i + 1)}] ${text}`); + }); const ondata = v => { - const s = v.toString() + const s = v.toString(); const cleanup = () => { - process.stdin.setRawMode(false) - process.stdin.removeListener('data', ondata) - } + process.stdin.setRawMode(false); + process.stdin.removeListener("data", ondata); + }; - if (s === '\u0003') { - cleanup() - reject(new Error('Aborted')) - return + if (s === "\u0003") { + cleanup(); + reject(new Error("Aborted")); + return; } - const n = Number(s) + const n = Number(s); if (opts[n - 1]) { - cleanup() - resolve(opts[n - 1][0]) + cleanup(); + resolve(opts[n - 1][0]); } - } + }; - process.stdin.setRawMode(true) - process.stdin.resume() - process.stdin.on('data', ondata) - }) -} + process.stdin.setRawMode(true); + process.stdin.resume(); + process.stdin.on("data", ondata); + }); +}; diff --git a/lib/utils/to-human-path.js b/lib/utils/to-human-path.js index 282ec5f..f62cbb2 100644 --- a/lib/utils/to-human-path.js +++ b/lib/utils/to-human-path.js @@ -1,9 +1,9 @@ // Native -const {resolve} = require('path') -const {homedir} = require('os') +const { resolve } = require("path"); +const { homedir } = require("os"); // cleaned-up `$HOME` (i.e.: no trailing slash) -const HOME = resolve(homedir()) +const HOME = resolve(homedir()); /** * Attempts to show the given path in @@ -12,7 +12,7 @@ const HOME = resolve(homedir()) */ function toHumanPath(path) { - return path.replace(HOME, '~') + return path.replace(HOME, "~"); } -module.exports = toHumanPath +module.exports = toHumanPath;