npm-consider
Version:
Check npm package dependencies, stats and impact on your package before installing it
149 lines (138 loc) • 3.77 kB
JavaScript
/**
* @file main file
*/
const program = require('commander');
const packageJson = require('./package.json');
const moment = require('moment');
const inquirer = require('inquirer');
const getPackageDetails = require('./lib/getPackageDetails');
const walkDependencies = require('./lib/walkDependencies');
const showImpact = require('./lib/showImpact');
const showDetails = require('./lib/showDetails');
const showQuickStats = require('./lib/showQuickStats');
const colors = require('colors/safe');
const install = require('./lib/install');
const exec = require('./lib/exec');
const getInstallCommand = require(
'./lib/getInstallCommand'
);
/**
* @param {string} nameVersion
* @return {object} name and version loose
*/
function parseName(nameVersion) {
// TODO: check urls
let nameVersionStr = String(nameVersion).trim();
let scope = false;
if (nameVersionStr[0] === `@`) {
scope = true;
nameVersionStr = nameVersionStr.slice(1);
}
let [name, versionLoose] = nameVersionStr.split(`@`);
if (!versionLoose) {
versionLoose = `latest`;
}
if (scope) {
name = `@${name}`;
}
return { name, versionLoose };
}
/**
* @param {string} command npm or yarn
* @param {string[]} args
* @return {string[]} prompt choices
*/
function getChoices(command, args) {
return [
`Install (${colors.bold(`${command} ${args.join(' ')}`)})`,
`Impact`,
`Details`,
`Skip`
];
}
/**
* @param {string} name
* @param {string} versionLoose
* @param {Object} packages
*/
function promptNextAction(name, versionLoose, packages) {
return getInstallCommand().then(({ command, args }) => {
const choices = getChoices(command, args);
return inquirer.prompt({
type: `list`,
name: `next`,
message: `What is next?`,
choices
}).then(({ next }) => {
switch (choices.indexOf(next)) {
case 0:
exec(command, args);
return Promise.reject();
case 1:
return showImpact(name, versionLoose, packages);
case 2:
return showDetails(packages);
default:
process.exit(0);
}
});
}).then(() => {
return promptNextAction(name, versionLoose, packages);
}, (e) => {
if (e) {
throw e;
}
});
}
/**
* install action
* @param {string} nameVersion package considering to install
*/
function installPackage(nameVersion) {
const { name, versionLoose } = parseName(nameVersion);
getPackageDetails(name, versionLoose)
.then((packageStats) => {
process.stdout.cursorTo(0);
process.stdout.clearLine(1);
process.stdout.write(`${
colors.bold(
`${packageStats.name}@${packageStats.version}`
)
} (updated ${
moment(packageStats.modified).fromNow()
})\n`);
return walkDependencies(
{ [name]: versionLoose }
);
})
.then((packages) => {
showQuickStats(packages);
return promptNextAction(
name, versionLoose, packages
);
})
.catch((e) => {
console.error(e);
process.exit(1);
});
}
program.version(packageJson.version);
program.description(packageJson.description);
program.usage('npm-consider install <pkg>');
if (!process.argv.slice(2).length) {
program.outputHelp();
}
program.command(`install [pkg]`)
.alias(`i`)
.action((pkg, options) => {
if (pkg) {
installPackage(pkg);
} else {
install(options);
}
})
.option(`-S, --save`, `Save to dependencies`)
.option(`-D, --save-dev`, `Save to devDependencies`)
.option(`--production`, `Will not install modules listed in devDependencies`)
.option(`--test`, `Exit with code 1 if package limits like maxPackagesNumber or maxSizeBites exceeded`);
program.parse(process.argv);