Browse Source

Make binaries comply to linting rules

master
Leo Lamprecht 8 years ago
parent
commit
e32bd818e4
No known key found for this signature in database GPG Key ID: EF804E3FF4BBA8AB
  1. 277
      bin/now-alias.js
  2. 273
      bin/now-certs.js
  3. 428
      bin/now-deploy.js
  4. 253
      bin/now-domains.js
  5. 145
      bin/now-list.js
  6. 160
      bin/now-remove.js
  7. 234
      bin/now-secrets.js
  8. 4
      package.json

277
bin/now-alias.js

@ -1,18 +1,18 @@
#!/usr/bin/env node
// Packages
import chalk from 'chalk';
import minimist from 'minimist';
import table from 'text-table';
import ms from 'ms';
import chalk from 'chalk'
import minimist from 'minimist'
import table from 'text-table'
import ms from 'ms'
// Ours
import strlen from '../lib/strlen';
import NowAlias from '../lib/alias';
import login from '../lib/login';
import * as cfg from '../lib/cfg';
import { error } from '../lib/error';
import toHost from '../lib/to-host';
import strlen from '../lib/strlen'
import NowAlias from '../lib/alias'
import login from '../lib/login'
import * as cfg from '../lib/cfg'
import {error} from '../lib/error'
import toHost from '../lib/to-host'
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -23,8 +23,8 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd',
token: 't'
}
});
const subcommand = argv._[0];
})
const subcommand = argv._[0]
// options
const help = () => {
@ -70,209 +70,218 @@ const help = () => {
To get the list of alias ids, use ${chalk.dim('`now alias ls`')}.
${chalk.dim('Alias:')} ln
`);
};
`)
}
// options
const debug = argv.debug;
const apiUrl = argv.url || 'https://api.zeit.co';
if (argv.config) cfg.setConfigFile(argv.config);
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = (code) => {
const exit = code => {
// we give stdout some time to flush out
// 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) => {
.then(async token => {
try {
await run(token);
await run(token)
} catch (err) {
if (err.userError) {
error(err.message);
error(err.message)
} else {
error(`Unknown error: ${err.stack}`);
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)
})
}
async function run (token) {
const alias = new NowAlias(apiUrl, token, { debug });
const args = argv._.slice(1);
async function run(token) {
const alias = new NowAlias(apiUrl, token, {debug})
const args = argv._.slice(1)
switch (subcommand) {
case 'list':
case 'ls':
if (0 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias ls`')}`);
return exit(1);
if (args.length !== 0) {
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 = 0 === list.length ? null : table(header.concat(aliases.map((_alias) => {
const _url = chalk.underline(`https://${_alias.alias}`);
const target = _alias.deploymentId;
const _sourceUrl = urls.get(target)
? chalk.underline(`https://${urls.get(target)}`)
: chalk.gray('<null>');
const time = chalk.gray(ms(current - new Date(_alias.created)) + ' ago');
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
const _sourceUrl = urls.get(target) ? chalk.underline(`https://${urls.get(target)}`) : chalk.gray('<null>')
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
null == _alias.uid ? '' : _alias.uid,
_alias.uid === null ? '' : _alias.uid,
_sourceUrl,
_url,
time
];
})), { align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen });
]
})), {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 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');
break;
if (text) {
console.log('\n' + text + '\n')
}
break
case 'remove':
case 'rm':
const _target = String(args[0]);
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 (1 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias rm <id>`')}`);
return exit(1);
if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias rm <id>`')}`)
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 readConfirmation(alias, _alias, _aliases)).toLowerCase();
if ('y' !== confirmation && 'yes' !== confirmation) {
console.log('\n> Aborted');
process.exit(0);
const confirmation = (await readConfirmation(alias, _alias, _aliases)).toLowerCase()
if (confirmation !== 'y' && confirmation !== 'yes') {
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':
if (2 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias set <id> <domain>`')}`);
return exit(1);
if (args.length !== 2) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now alias set <id> <domain>`')}`)
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 (2 === argv._.length) {
await alias.set(String(argv._[0]), String(argv._[1]));
if (argv._.length === 2) {
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()
}
function indent (text, n) {
return text.split('\n').map((l) => ' '.repeat(n) + l).join('\n');
}
async function readConfirmation(alias, _alias) {
const deploymentsList = await alias.list()
const urls = new Map(deploymentsList.map(l => [l.uid, l.url]))
async function readConfirmation (alias, _alias, list) {
const deploymentsList = await alias.list();
const urls = new Map(deploymentsList.map(l => [l.uid, l.url]));
return new Promise((resolve, reject) => {
const time = chalk.gray(ms(new Date() - new Date(_alias.created)) + ' ago');
const _sourceUrl = chalk.underline(`https://${urls.get(_alias.deploymentId)}`);
return new Promise(resolve => {
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) }
);
process.stdout.write('> The following alias will be removed permanently\n');
process.stdout.write(' ' + tbl + '\n');
process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`);
process.stdin.on('data', (d) => {
process.stdin.pause();
resolve(d.toString().trim());
}).resume();
});
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
)
process.stdout.write('> The following alias will be removed permanently\n')
process.stdout.write(' ' + tbl + '\n')
process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', d => {
process.stdin.pause()
resolve(d.toString().trim())
}).resume()
})
}
function findAlias (alias, list) {
let key, val;
function findAlias(alias, list) {
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) => {
const _alias = list.find(d => {
if (d[key] === val) {
if (debug) console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`);
return true;
if (debug) {
console.log(`> [debug] matched alias ${d.uid} by ${key} ${val}`)
}
return true
}
// match prefix
if (`${val}.now.sh` === d.alias) {
if (debug) console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`);
return true;
if (debug) {
console.log(`> [debug] matched alias ${d.uid} by url ${d.host}`)
}
return true
}
return false;
});
return false
})
return _alias;
return _alias
}

273
bin/now-certs.js

@ -1,20 +1,21 @@
#!/usr/bin/env node
// Native
import path from 'path';
import path from 'path'
// Packages
import chalk from 'chalk';
import table from 'text-table';
import minimist from 'minimist';
import fs from 'fs-promise';
import ms from 'ms';
import chalk from 'chalk'
import table from 'text-table'
import minimist from 'minimist'
import fs from 'fs-promise'
import ms from 'ms'
// Ours
import strlen from '../lib/strlen';
import * as cfg from '../lib/cfg';
import {handleError, error} from '../lib/error';
import NowCerts from '../lib/certs';
import strlen from '../lib/strlen'
import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'
import NowCerts from '../lib/certs'
import login from '../lib/login'
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token', 'crt', 'key', 'ca'],
@ -25,8 +26,9 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd',
token: 't'
}
});
const subcommand = argv._[0];
})
const subcommand = argv._[0]
// options
const help = () => {
@ -64,190 +66,199 @@ const help = () => {
${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')}
`);
};
`)
}
// options
const debug = argv.debug;
const apiUrl = argv.url || 'https://api.zeit.co';
if (argv.config) cfg.setConfigFile(argv.config);
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = (code) => {
const exit = code => {
// we give stdout some time to flush out
// 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) => {
.then(async token => {
try {
await run(token);
await run(token)
} catch (err) {
handleError(err);
exit(1);
handleError(err)
exit(1)
}
})
.catch((e) => {
error(`Authentication error – ${e.message}`);
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(new Date(-diff)) + ' ago') : chalk.gray('in ' + ms(new Date(diff)));
function formatExpirationDate(date) {
const diff = date - Date.now()
return diff < 0 ? chalk.gray(ms(new Date(-diff)) + ' ago') : chalk.gray('in ' + ms(new Date(diff)))
}
async function run (token) {
const certs = new NowCerts(apiUrl, token, { debug });
const args = argv._.slice(1);
const start = Date.now();
async function run(token) {
const certs = new NowCerts(apiUrl, token, {debug})
const args = argv._.slice(1)
const start = Date.now()
if ('ls' === subcommand || 'list' === subcommand) {
if (0 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs ls`')}`);
return exit(1);
if (subcommand === 'ls' || subcommand === 'list') {
if (args.length !== 0) {
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);
console.log(`> ${list.length} certificate${list.length !== 1 ? 's' : ''} found ${chalk.gray(`[${elapsed}]`)}`);
if (0 < list.length) {
const cur = Date.now();
const list = await certs.ls()
const elapsed = ms(new Date() - start)
console.log(`> ${list.length} certificate${list.length === 1 ? '' : 's'} found ${chalk.gray(`[${elapsed}]`)}`)
if (list.length > 0) {
const cur = Date.now()
list.sort((a, b) => {
return a.cn.localeCompare(b.cn);
});
const header = [['', 'id', 'cn', 'created', 'expiration'].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 a.cn.localeCompare(b.cn)
})
const header = [['', 'id', 'cn', 'created', 'expiration'].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
];
})), { align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen });
if (out) console.log('\n' + out + '\n');
]
})), {align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen})
if (out) {
console.log('\n' + out + '\n')
}
}
} else if ('create' === subcommand) {
if (1 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs create <cn>`')}`);
return exit(1);
} else if (subcommand === 'create') {
if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs create <cn>`')}`)
return exit(1)
}
const cn = args[0];
const cert = await certs.create(cn);
const elapsed = ms(new Date() - start);
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} issued ${chalk.gray(`[${elapsed}]`)}`);
} else if ('renew' === subcommand) {
if (1 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs renew <id | cn>`')}`);
return exit(1);
const cn = args[0]
const cert = await certs.create(cn)
const elapsed = ms(new Date() - start)
console.log(`${chalk.cyan('> Success!')} Certificate ${chalk.bold(cn)} ${chalk.gray(`(${cert.uid})`)} issued ${chalk.gray(`[${elapsed}]`)}`)
} else if (subcommand === 'renew') {
if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs renew <id | cn>`')}`)
return exit(1)
}
const cert = await getCertIdCn(certs, args[0]);
const yes = await readConfirmation(cert, 'The following certificate will be renewed\n');
const cert = await getCertIdCn(certs, args[0])
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 ('replace' === subcommand) {
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] <id | cn>`')}`);
return exit(1);
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs replace --crt DOMAIN.CRT --key DOMAIN.KEY [--ca CA.CRT] <id | cn>`')}`)
return exit(1)
}
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 yes = await readConfirmation(cert, 'The following certificate will be replaced permanently\n');
const cert = await getCertIdCn(certs, args[0])
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.replace(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 ('rm' === subcommand || 'remove' === subcommand) {
if (1 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now certs rm <id | cn>`')}`);
return exit(1);
await certs.replace(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 <id | cn>`')}`)
return exit(1)
}
const cert = await getCertIdCn(certs, args[0]);
const yes = await readConfirmation(cert, 'The following certificate will be removed permanently\n');
const cert = await getCertIdCn(certs, args[0])
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, reject) => {
const time = chalk.gray(ms(new Date() - new Date(cert.created)) + ' ago');
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) }
);
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
)
process.stdout.write(`> ${msg}`);
process.stdout.write(' ' + tbl + '\n');
process.stdout.write(`> ${msg}`)
process.stdout.write(' ' + tbl + '\n')
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`);
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => {
process.stdin.pause();
resolve('y' === d.toString().trim().toLowerCase());
}).resume();
});
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');
function readX509File(file) {
return fs.readFileSync(path.resolve(file), 'utf8')
}
async function getCertIdCn (certs, idOrCn) {
const list = await certs.ls();
const thecert = list.filter((cert) => {
return cert.uid === idOrCn || cert.cn === idOrCn;
})[0];
async function getCertIdCn(certs, idOrCn) {
const list = await certs.ls()
const thecert = list.filter(cert => {
return cert.uid === idOrCn || cert.cn === idOrCn
})[0]
if (!thecert) {
error(`No certificate found by id or cn "${idOrCn}"`);
return exit(1);
error(`No certificate found by id or cn "${idOrCn}"`)
return exit(1)
}
return thecert;
return thecert
}

428
bin/now-deploy.js

@ -1,26 +1,26 @@
#!/usr/bin/env node
// Native
import {resolve} from 'path';
import {resolve} from 'path'
// Packages
import Progress from 'progress';
import {stat} from 'fs-promise';
import bytes from 'bytes';
import chalk from 'chalk';
import minimist from 'minimist';
import ms from 'ms';
import Progress from 'progress'
import {stat} from 'fs-promise'
import bytes from 'bytes'
import chalk from 'chalk'
import minimist from 'minimist'
import ms from 'ms'
// Ours
import copy from '../lib/copy';
import login from '../lib/login';
import * as cfg from '../lib/cfg';
import {version} from '../../package';
import Logger from '../lib/build-logger';
import Now from '../lib';
import toHumanPath from '../lib/utils/to-human-path';
import promptOptions from '../lib/utils/prompt-options';
import {handleError, error} from '../lib/error';
import copy from '../lib/copy'
import login from '../lib/login'
import * as cfg from '../lib/cfg'
import {version} from '../../package'
import Logger from '../lib/build-logger'
import Now from '../lib'
import toHumanPath from '../lib/utils/to-human-path'
import promptOptions from '../lib/utils/prompt-options'
import {handleError, error} from '../lib/error'
const argv = minimist(process.argv.slice(2), {
string: [
@ -52,7 +52,7 @@ const argv = minimist(process.argv.slice(2), {
'no-clipboard': 'C',
'forward-npm': 'N'
}
});
})
const help = () => {
console.log(`
@ -115,227 +115,243 @@ const help = () => {
${chalk.gray('–')} Displays comprehensive help for the subcommand ${chalk.dim('`list`')}
${chalk.cyan('$ now help list')}
`);
};
`)
}
let path = argv._[0];
let path = argv._[0]
if (null != path) {
if (path === null) {
path = process.cwd()
} else {
// if path is relative: resolve
// if path is absolute: clear up strange `/` etc
path = resolve(process.cwd(), path);
} else {
path = process.cwd();
path = resolve(process.cwd(), path)
}
const exit = (code) => {
const exit = code => {
// we give stdout some time to flush out
// 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)
}
// options
const debug = argv.debug;
const clipboard = !argv['no-clipboard'];
const forwardNpm = argv['forward-npm'];
const forceNew = argv.force;
const forceSync = argv.forceSync;
const shouldLogin = argv.login;
const wantsPublic = argv.public;
const apiUrl = argv.url || 'https://api.zeit.co';
const isTTY = process.stdout.isTTY;
const quiet = !isTTY;
if (argv.config) cfg.setConfigFile(argv.config);
const config = cfg.read();
const alwaysForwardNpm = config.forwardNpm;
const debug = argv.debug
const clipboard = !argv['no-clipboard']
const forwardNpm = argv['forward-npm']
const forceNew = argv.force
const forceSync = argv.forceSync
const shouldLogin = argv.login
const wantsPublic = argv.public
const apiUrl = argv.url || 'https://api.zeit.co'
const isTTY = process.stdout.isTTY
const quiet = !isTTY
if (argv.config) {
cfg.setConfigFile(argv.config)
}
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(chalk.bold('𝚫 now'), version);
process.exit(0);
console.log(chalk.bold('𝚫 now'), version)
process.exit(0)
} else if (!(argv.token || config.token) || shouldLogin) {
login(apiUrl)
.then((token) => {
.then(token => {
if (shouldLogin) {
console.log('> Logged in successfully. Token saved in ~/.now.json');
process.exit(0);
console.log('> Logged in successfully. Token saved in ~/.now.json')
process.exit(0)
} else {
sync(token).catch((err) => {
error(`Unknown error: ${err.stack}`);
process.exit(1);
});
sync(token).catch(err => {
error(`Unknown error: ${err.stack}`)
process.exit(1)
})
}
})
.catch((e) => {
error(`Authentication error – ${e.message}`);
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.stack}`);
process.exit(1);
});
sync(argv.token || config.token).catch(err => {
error(`Unknown error: ${err.stack}`)
process.exit(1)
})
}
async function sync (token) {
const start = Date.now();
async function sync(token) {
const start = Date.now()
if (!quiet) {
console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`);
console.log(`> Deploying ${chalk.bold(toHumanPath(path))}`)
}
try {
await stat(path);
await stat(path)
} catch (err) {
error(`Could not read directory ${chalk.bold(path)}`);
process.exit(1);
error(`Could not read directory ${chalk.bold(path)}`)
process.exit(1)
}
let deploymentType, hasPackage, hasDockerfile;
let deploymentType
let hasPackage
let hasDockerfile
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'
} else {
if (argv.npm) {
deploymentType = 'npm';
} else {
try {
await stat(resolve(path, 'package.json'));
} catch (err) {
hasPackage = true;
try {
await stat(resolve(path, 'package.json'))
} catch (err) {
hasPackage = true
}
[hasPackage, hasDockerfile] = await Promise.all([
await (async () => {
try {
await stat(resolve(path, 'package.json'))
} catch (err) {
return false
}
return true
})(),
await (async () => {
try {
await stat(resolve(path, 'Dockerfile'))
} catch (err) {
return false
}
return true
})()
])
if (hasPackage && hasDockerfile) {
if (debug) {
console.log('[debug] multiple manifests found, disambiguating')
}
[hasPackage, hasDockerfile] = await Promise.all([
await (async () => {
try {
await stat(resolve(path, 'package.json'));
} catch (err) {
return false;
}
return true;
})(),
await (async () => {
try {
await stat(resolve(path, 'Dockerfile'));
} catch (err) {
return false;
}
return true;
})()
]);
if (hasPackage && hasDockerfile) {
if (debug) 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`);
deploymentType = await promptOptions([
if (isTTY) {
try {
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')} `]
]);
} catch (err) {
error(err.message);
process.exit(1);
}
} else {
error('Ambiguous deployment (`package.json` and `Dockerfile` found). ' +
'Please supply `--npm` or `--docker` to disambiguate.');
])
} catch (err) {
error(err.message)
process.exit(1)
}
} else if (hasPackage) {
if (debug) console.log('[debug] `package.json` found, assuming `deploymentType` = `npm`');
deploymentType = 'npm';
} else if (hasDockerfile) {
if (debug) console.log('[debug] `Dockerfile` found, assuming `deploymentType` = `docker`');
deploymentType = 'docker';
} else {
error(`Could not access a ${chalk.dim('`package.json`')} or ${chalk.dim('`Dockerfile`')} in ${chalk.bold(path)}`);
console.log(`> To deploy statically, try: ${chalk.dim(chalk.underline('https://zeit.co/blog/serve-it-now'))}.`);
process.exit(1);
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`')
}
deploymentType = 'npm'
} else if (hasDockerfile) {
if (debug) {
console.log('[debug] `Dockerfile` found, assuming `deploymentType` = `docker`')
}
deploymentType = 'docker'
} else {
error(`Could not access a ${chalk.dim('`package.json`')} or ${chalk.dim('`Dockerfile`')} in ${chalk.bold(path)}`)
console.log(`> To deploy statically, try: ${chalk.dim(chalk.underline('https://zeit.co/blog/serve-it-now'))}.`)
process.exit(1)
}
}
const now = new Now(apiUrl, token, { debug });
const envs = [].concat(argv.env || []);
const now = new Now(apiUrl, token, {debug})
const envs = [].concat(argv.env || [])
let secrets;
const findSecret = async (uidOrName) => {
if (!secrets) secrets = await now.listSecrets();
return secrets.filter((secret) => {
return secret.name === uidOrName || secret.uid === uidOrName;
});
};
let secrets
const findSecret = async uidOrName => {
if (!secrets) {
secrets = await now.listSecrets()
}
const env_ = await Promise.all(envs.map(async (kv) => {
const [key, ...rest] = kv.split('=');
let val;
return secrets.filter(secret => {
return secret.name === uidOrName || secret.uid === uidOrName
})
}
if (rest.length) {
val = rest.join('=');
const env_ = await Promise.all(envs.map(async kv => {
const [key, ...rest] = kv.split('=')
let val
if (rest.length > 0) {
val = rest.join('=')
}
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);
error(`Invalid ${chalk.dim('-e')} key ${chalk.bold(`"${chalk.bold(key)}"`)}. Only letters, digits and underscores are allowed.`)
return process.exit(1)
}
if ('' === key || null == key) {
error(`Invalid env option ${chalk.bold(`"${kv}"`)}`);
return process.exit(1);
if (key === '' || key === null) {
error(`Invalid env option ${chalk.bold(`"${kv}"`)}`)
return process.exit(1)
}
if (val == null) {
if (!(key in process.env)) {
error(`No value specified for env ${chalk.bold(`"${chalk.bold(key)}"`)} and it was not found in your env.`);
return process.exit(1);
} else {
console.log(`> Reading ${chalk.bold(`"${chalk.bold(key)}"`)} from your env (as no value was specified)`);
if (val === null) {
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(/^\@/, '\\@');
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 ('@' === val[0]) {
const uidOrName = val.substr(1);
const secrets = await findSecret(uidOrName);
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 (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}"`)}`);
error(`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`)
}
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);
} else {
val = { uid: secrets[0].uid };
error(`Ambiguous secret ${chalk.bold(`"${uidOrName}"`)} (matches ${chalk.bold(secrets.length)} secrets)`)
return process.exit(1)
}
val = {uid: secrets[0].uid}
}
return [
key,
typeof val === 'string'
// add support for escaping the @ as \@
? val.replace(/^\\@/, '@')
: val
];
}));
let env = {};
typeof val === 'string' ? val.replace(/^\\@/, '@') : val
]
}))
const env = {}
env_
.filter(v => !!v)
.filter(v => Boolean(v))
.forEach(([key, val]) => {
if (key in env) console.log(`> ${chalk.yellow('NOTE:')} Overriding duplicate env key ${chalk.bold(`"${key}"`)}`);
if (key in env) {
console.log(`> ${chalk.yellow('NOTE:')} Overriding duplicate env key ${chalk.bold(`"${key}"`)}`)
}
env[key] = val
});
})
try {
await now.create(path, {
@ -346,45 +362,49 @@ async function sync (token) {
forwardNpm: alwaysForwardNpm || forwardNpm,
quiet,
wantsPublic
});
})
} catch (err) {
if (debug) console.log(`> [debug] error: ${err.stack}`);
handleError(err);
process.exit(1);
if (debug) {
console.log(`> [debug] error: ${err.stack}`)
}
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 start_u = new Date();
const startU = new Date()
const complete = () => {
if (!quiet) {
const elapsed_u = ms(new Date() - start_u);
console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsed_u}] `);
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);
};
printLogs(now.host)
}
if (now.syncAmount) {
const bar = new Progress('> Upload [:bar] :percent :etas', {
@ -392,45 +412,45 @@ async function sync (token) {
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);
printLogs(now.host)
}
}
function printLogs (host) {
function printLogs(host) {
// log build
const logger = new Logger(host, { debug, quiet });
const logger = new Logger(host, {debug, quiet})
logger.on('close', () => {
if (!quiet) {
console.log(`${chalk.cyan('> Deployment complete!')}`);
console.log(`${chalk.cyan('> Deployment complete!')}`)
}
process.exit(0);
});
process.exit(0)
})
}

253
bin/now-domains.js

@ -1,18 +1,18 @@
#!/usr/bin/env node
// Packages
import chalk from 'chalk';
import minimist from 'minimist';
import table from 'text-table';
import ms from 'ms';
import chalk from 'chalk'
import minimist from 'minimist'
import table from 'text-table'
import ms from 'ms'
// Ours
import login from '../lib/login';
import * as cfg from '../lib/cfg';
import { error } from '../lib/error';
import toHost from '../lib/to-host';
import strlen from '../lib/strlen';
import NowDomains from '../lib/domains';
import login from '../lib/login'
import * as cfg from '../lib/cfg'
import {error} from '../lib/error'
import toHost from '../lib/to-host'
import strlen from '../lib/strlen'
import NowDomains from '../lib/domains'
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -23,8 +23,8 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd',
token: 't'
}
});
const subcommand = argv._[0];
})
const subcommand = argv._[0]
// options
const help = () => {
@ -75,192 +75,201 @@ const help = () => {
${chalk.cyan('$ now domain rm domainId')}
To get the list of domain ids, use ${chalk.dim('`now domains ls`')}.
`);
};
`)
}
// options
const debug = argv.debug;
const apiUrl = argv.url || 'https://api.zeit.co';
if (argv.config) cfg.setConfigFile(argv.config);
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = (code) => {
const exit = code => {
// we give stdout some time to flush out
// 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) => {
.then(async token => {
try {
await run(token);
await run(token)
} catch (err) {
if (err.userError) {
error(err.message);
error(err.message)
} else {
error(`Unknown error: ${err.stack}`);
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)
})
}
async function run (token) {
const domain = new NowDomains(apiUrl, token, { debug });
const args = argv._.slice(1);
async function run(token) {
const domain = new NowDomains(apiUrl, token, {debug})
const args = argv._.slice(1)
switch (subcommand) {
case 'ls':
case 'list':
if (0 !== args.length) {
error('Invalid number of arguments');
return exit(1);
if (args.length !== 0) {
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', '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');
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', '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,
time
];
})), { align: ['l', 'r', 'l', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen });
]
})), {align: ['l', 'r', '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');
break;
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')
}
break
case 'rm':
case 'remove':
if (1 !== args.length) {
error('Invalid number of arguments');
return exit(1);
if (args.length !== 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 ('y' !== confirmation && 'yes' !== confirmation) {
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':
if (1 !== args.length) {
error('Invalid number of arguments');
return exit(1);
if (args.length !== 1) {
error('Invalid number of arguments')
return exit(1)
}
const start = new Date();
const name = String(args[0]);
const { uid, created } = await domain.add(name);
const elapsed = ms(new Date() - start);
const start = new Date()
const name = String(args[0])
const {uid, created} = await domain.add(name)
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 {
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})`)} already exists [${elapsed}]`)
}
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();
}
function indent (text, n) {
return text.split('\n').map((l) => ' '.repeat(n) + l).join('\n');
domain.close()
}
async function readConfirmation (domain, _domain, list) {
const urls = new Map(list.map(l => [l.uid, l.url]));
return new Promise((resolve, reject) => {
const time = chalk.gray(ms(new Date() - new Date(_domain.created)) + ' ago');
async function readConfirmation(domain, _domain) {
return new Promise(resolve => {
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) }
);
process.stdout.write('> The following domain will be removed permanently\n');
process.stdout.write(' ' + tbl + '\n');
if (_domain.aliases.length) {
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`);
{align: ['l', 'r', 'l'], hsep: ' '.repeat(6)}
)
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.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`);
process.stdin.on('data', (d) => {
process.stdin.pause();
resolve(d.toString().trim());
}).resume();
});
process.stdout.write(` ${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', d => {
process.stdin.pause()
resolve(d.toString().trim())
}).resume()
})
}
function findDomain (val, list) {
return list.find((d) => {
function findDomain(val, list) {
return list.find(d => {
if (d.uid === val) {
if (debug) console.log(`> [debug] matched domain ${d.uid} by uid`);
return true;
if (debug) {
console.log(`> [debug] matched domain ${d.uid} by uid`)
}
return true
}
// match prefix
if (d.name === toHost(val)) {
if (debug) console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`);
return true;
if (debug) {
console.log(`> [debug] matched domain ${d.uid} by name ${d.name}`)
}
return true
}
return false;
});
return false
})
}

145
bin/now-list.js

@ -1,18 +1,18 @@
#!/usr/bin/env node
// Packages
import fs from 'fs-promise';
import minimist from 'minimist';
import chalk from 'chalk';
import table from 'text-table';
import ms from 'ms';
import fs from 'fs-promise'
import minimist from 'minimist'
import chalk from 'chalk'
import table from 'text-table'
import ms from 'ms'
// Ours
import strlen from '../lib/strlen';
import Now from '../lib';
import login from '../lib/login';
import * as cfg from '../lib/cfg';
import {handleError, error} from '../lib/error';
import strlen from '../lib/strlen'
import Now from '../lib'
import login from '../lib/login'
import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -23,7 +23,7 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd',
token: 't'
}
});
})
const help = () => {
console.log(`
@ -47,95 +47,110 @@ const help = () => {
${chalk.cyan('$ now ls my-app')}
${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';
if (argv.config) cfg.setConfigFile(argv.config);
const config = cfg.read();
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const config = cfg.read()
Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => {
.then(async token => {
try {
await list(token);
await list(token)
} catch (err) {
error(`Unknown error: ${err.stack}`);
process.exit(1);
error(`Unknown error: ${err.stack}`)
process.exit(1)
}
})
.catch((e) => {
error(`Authentication error – ${e.message}`);
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();
async function list(token) {
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 sorted = await sort([...apps])
const current = Date.now()
const current = Date.now();
const text = sorted.map(([name, deps]) => {
const t = table(deps.map(({ uid, url, created }) => {
const _url = chalk.underline(`https://${url}`);
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}]`)}`);
if (text) console.log('\n' + text + '\n');
const t = table(deps.map(({uid, url, created}) => {
const _url = chalk.underline(`https://${url}`)
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}]`)}`)
if (text) {
console.log('\n' + text + '\n')
}
}
async function sort (apps) {
let pkg;
async function sort(apps) {
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;
});
return [name, deps];
return b.created - a.created
})
return [name, deps]
})
.sort(([nameA, depsA], [nameB, depsB]) => {
if (pkg.name === nameA) return -1;
if (pkg.name === nameB) return 1;
return depsB[0].created - depsA[0].created;
});
if (pkg.name === nameA) {
return -1
}
if (pkg.name === nameB) {
return 1
}
return depsB[0].created - depsA[0].created
})
}
function indent (text, n) {
return text.split('\n').map((l) => ' '.repeat(n) + l).join('\n');
function indent(text, n) {
return text.split('\n').map(l => ' '.repeat(n) + l).join('\n')
}

160
bin/now-remove.js

@ -1,16 +1,16 @@
#!/usr/bin/env node
// Packages
import minimist from 'minimist';
import chalk from 'chalk';
import ms from 'ms';
import table from 'text-table';
import minimist from 'minimist'
import chalk from 'chalk'
import ms from 'ms'
import table from 'text-table'
// Ours
import Now from '../lib';
import login from '../lib/login';
import * as cfg from '../lib/cfg';
import {handleError, error} from '../lib/error';
import Now from '../lib'
import login from '../lib/login'
import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -21,9 +21,9 @@ const argv = minimist(process.argv.slice(2), {
debug: 'd',
token: 't'
}
});
})
const ids = argv._;
const ids = argv._
// options
const help = () => {
@ -52,118 +52,120 @@ const help = () => {
${chalk.cyan('$ now rm eyWt6zuSdeus uWHoA9RQ1d1o')}
${chalk.dim('Alias:')} rm
`);
};
`)
}
if (argv.help || 0 === ids.length) {
help();
process.exit(0);
if (argv.help || ids.length === 0) {
help()
process.exit(0)
}
// options
const debug = argv.debug;
const apiUrl = argv.url || 'https://api.zeit.co';
const hard = argv.hard || false;
if (argv.config) cfg.setConfigFile(argv.config);
const config = cfg.read();
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
const hard = argv.hard || false
if (argv.config) {
cfg.setConfigFile(argv.config)
}
function readConfirmation (matches) {
return new Promise((resolve, reject) => {
const config = cfg.read()
process.stdout.write(`> The following deployment${matches.length === 1 ? '' : 's'} will be removed permanently:\n`);
function readConfirmation(matches) {
return new Promise(resolve => {
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 = chalk.underline(`https://${depl.url}`);
return [depl.uid, url, time];
matches.map(depl => {
const time = chalk.gray(ms(new Date() - depl.created) + ' ago')
const 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 (let depl of matches) {
for (let alias of depl.aliases) {
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`
);
)
}
}
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`);
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => {
process.stdin.pause();
resolve(d.toString().trim());
}).resume();
});
process.stdin.on('data', d => {
process.stdin.pause()
resolve(d.toString().trim())
}).resume()
})
}
Promise.resolve(argv.token || config.token || login(apiUrl))
.then(async (token) => {
.then(async token => {
try {
await remove(token);
await remove(token)
} catch (err) {
error(`Unknown error: ${err.stack}`);
process.exit(1);
error(`Unknown error: ${err.stack}`)
process.exit(1)
}
})
.catch((e) => {
error(`Authentication error – ${e.message}`);
process.exit(1);
});
.catch(e => {
error(`Authentication error – ${e.message}`)
process.exit(1)
})
async function remove (token) {
const now = new Now(apiUrl, token, { debug });
async function remove(token) {
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) => {
const matches = deployments.filter(d => {
return ids.find(id => {
// `url` should match the hostname of the deployment
let u = id.replace(/^https\:\/\//i, '');
if (-1 === u.indexOf('.')) {
let u = id.replace(/^https\:\/\//i, '')
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 (0 === matches.length) {
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);
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)
}
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 {
const confirmation = (await readConfirmation(matches)).toLowerCase();
if ('y' !== confirmation && 'yes' !== confirmation) {
console.log('\n> Aborted');
process.exit(0);
const confirmation = (await readConfirmation(matches)).toLowerCase()
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()
}

234
bin/now-secrets.js

@ -1,17 +1,17 @@
#!/usr/bin/env node
// Packages
import chalk from 'chalk';
import table from 'text-table';
import minimist from 'minimist';
import ms from 'ms';
import chalk from 'chalk'
import table from 'text-table'
import minimist from 'minimist'
import ms from 'ms'
// Ours
import strlen from '../lib/strlen';
import * as cfg from '../lib/cfg';
import {handleError, error} from '../lib/error';
import NowSecrets from '../lib/secrets';
import login from '../lib/login';
import strlen from '../lib/strlen'
import * as cfg from '../lib/cfg'
import {handleError, error} from '../lib/error'
import NowSecrets from '../lib/secrets'
import login from '../lib/login'
const argv = minimist(process.argv.slice(2), {
string: ['config', 'token'],
@ -23,8 +23,9 @@ const argv = minimist(process.argv.slice(2), {
base64: 'b',
token: 't'
}
});
const subcommand = argv._[0];
})
const subcommand = argv._[0]
// options
const help = () => {
@ -66,160 +67,173 @@ const help = () => {
${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';
if (argv.config) cfg.setConfigFile(argv.config);
const debug = argv.debug
const apiUrl = argv.url || 'https://api.zeit.co'
const exit = (code) => {
if (argv.config) {
cfg.setConfigFile(argv.config)
}
const exit = code => {
// we give stdout some time to flush out
// 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) => {
.then(async token => {
try {
await run(token);
await run(token)
} catch (err) {
handleError(err);
exit(1);
handleError(err)
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 secrets = new NowSecrets(apiUrl, token, { debug });
const args = argv._.slice(1);
const start = Date.now();
async function run(token) {
const secrets = new NowSecrets(apiUrl, token, {debug})
const args = argv._.slice(1)
const start = Date.now()
if ('ls' === subcommand || 'list' === subcommand) {
if (0 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret ls`')}`);
return exit(1);
if (subcommand === 'ls' || subcommand === 'list') {
if (args.length !== 0) {
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);
console.log(`> ${list.length} secret${list.length !== 1 ? 's' : ''} found ${chalk.gray(`[${elapsed}]`)}`);
if (0 < list.length) {
const cur = Date.now();
const header = [['', 'id', 'name', 'created'].map(s => chalk.dim(s))];
const out = table(header.concat(list.map((secret) => {
const list = await secrets.ls()
const elapsed = ms(new Date() - start)
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 });
if (out) console.log('\n' + out + '\n');
]
})), {align: ['l', 'r', 'l', 'l'], hsep: ' '.repeat(2), stringLength: strlen})
if (out) {
console.log('\n' + out + '\n')
}
}
return secrets.close();
return secrets.close()
}
if ('rm' === subcommand || 'remove' === subcommand) {
if (1 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm <id | name>`')}`);
return exit(1);
if (subcommand === 'rm' || subcommand === 'remove') {
if (args.length !== 1) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rm <id | name>`')}`)
return exit(1)
}
const list = await secrets.ls();
const theSecret = list.filter((secret) => {
return secret.uid === args[0]
|| secret.name === args[0];
})[0];
const list = await secrets.ls()
const theSecret = list.filter(secret => {
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 ('rename' === subcommand) {
if (2 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename <old-name> <new-name>`')}`);
return exit(1);
if (subcommand === 'rename') {
if (args.length !== 2) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret rename <old-name> <new-name>`')}`)
return exit(1)
}
const secret = await secrets.rename(args[0], args[1]);
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 ('add' === subcommand || 'set' === subcommand) {
if (2 !== args.length) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret add <name> <value>`')}`);
if (subcommand === 'add' || subcommand === 'set') {
if (args.length !== 2) {
error(`Invalid number of arguments. Usage: ${chalk.cyan('`now secret add <name> <value>`')}`)
if (args.length > 2) {
const [, ...rest] = args;
console.log('> If your secret has spaces, make sure to wrap it in quotes. Example: \n'
+ ` ${chalk.cyan('$ now secret add ${args[0]} "${escaped}"')} `);
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);
console.log(`${chalk.cyan('> Success!')} Secret ${chalk.bold(name.toLowerCase())} ${chalk.gray(`(${secret.uid})`)} added ${chalk.gray(`[${elapsed}]`)}`);
return secrets.close();
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()
}
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, reject) => {
const time = chalk.gray(ms(new Date() - new Date(secret.created)) + ' ago');
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) }
);
{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('> The following secret will be removed permanently\n')
process.stdout.write(' ' + tbl + '\n')
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`);
process.stdout.write(`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[yN] ')}`)
process.stdin.on('data', (d) => {
process.stdin.pause();
resolve('y' === d.toString().trim().toLowerCase());
}).resume();
});
process.stdin.on('data', d => {
process.stdin.pause()
resolve(d.toString().trim().toLowerCase() === 'y')
}).resume()
})
}

4
package.json

@ -57,7 +57,9 @@
"no-use-before-define": 0,
"complexity": 0,
"unicorn/no-process-exit": 0,
"no-control-regex": 0
"no-control-regex": 0,
"no-case-declarations": 0,
"no-useless-escape": 0
}
},
"bin": {

Loading…
Cancel
Save