/** * Upgrade a dependencies collection based on latest available versions * @param currentDependencies current dependencies collection object * @param latestVersions latest available versions collection object * @returns {{}} upgraded dependency collection object */ function upgradeDependencies(currentDependencies, latestVersions) { // filter out dependencies with empty values currentDependencies = cint.filterObject(currentDependencies, function (key, value) { return value; }); // get the preferred wildcard and bind it to upgradeDependencyDeclaration var wildcard = getPreferredWildcard(currentDependencies) || DEFAULT_WILDCARD; var upgradeDep = _.partialRight(upgradeDependencyDeclaration, { wildcard: wildcard }); return _(currentDependencies) // only include packages for which a latest version was fetched .pickBy(function (current, packageName) { return packageName in latestVersions; }) // combine the current and latest dependency objects into a single object keyed by packageName and containing // both versions in an array: [current, latest] .mapValues(function (current, packageName) { var latest = latestVersions[packageName]; return [current, latest]; }) // pick the packages that are upgradeable // we can use spread because isUpgradeable and upgradeDependencyDeclaration both take current and latest as // arguments .pickBy(_.spread(isUpgradeable)) .mapValues(_.spread(upgradeDep)) .value(); }
return vm.upgradePackageDefinitions(current, options).spread(async (upgraded, latest) => { const {newPkgData, selectedNewDependencies} = await vm.upgradePackageData(pkgData, current, upgraded, latest, options); const output = options.jsonAll ? jph.parse(newPkgData) : options.jsonDeps ? _.pick(jph.parse(newPkgData), 'dependencies', 'devDependencies', 'optionalDependencies') : selectedNewDependencies; // print if (options.json) { // use the selectedNewDependencies dependencies data to generate new package data // INVARIANT: we don't need try-catch here because pkgData has already been parsed as valid JSON, and vm.upgradePackageData simply does a find-and-replace on that printJson(options, output); } else { printUpgrades(options, { current, upgraded: selectedNewDependencies, latest }); } // write // TODO: All this is just to get numUpgraded here. This is repeated in printUpgrades. const deps = Object.keys(upgraded); const satisfied = cint.toObject(deps, dep => cint.keyValue(dep, vm.isSatisfied(latest[dep], current[dep])) ); const isSatisfied = _.propertyOf(satisfied); const filteredUpgraded = options.minimal ? cint.filterObject(upgraded, cint.not(isSatisfied)) : upgraded; const numUpgraded = Object.keys(filteredUpgraded).length; if (numUpgraded > 0) { // if error-level is 2, immediately exit with error code if (options.errorLevel === 2) { programError(options, '\nDependencies not up-to-date'); } // if there is a package file, write the new package data // otherwise, suggest ncu -u if (pkgFile) { if (options.upgrade) { // short-circuit return in order to wait for write operation, but still return the same output return writePackageFile(pkgFile, newPkgData) .then(() => { print(options, `\nRun ${chalk.cyan('npm install')} to install new versions.\n`); return output; }); } else { print(options, `\nRun ${chalk.cyan('ncu -u')} to upgrade ${getPackageFileName(options)}`); } } } return output; });
/** * Get the current dependencies from the package file * @param pkg Object with dependencies and/or devDependencies properties * @param options.filter List or regex of package names to search * @param options.prod * @param options.dev * @returns Promised {packageName: version} collection */ function getCurrentDependencies(pkg, options) { if (!options.prod && !options.dev) { options.prod = options.dev = true; } var allDependencies = cint.filterObject(_.merge({}, options.prod && pkg.dependencies, options.dev && pkg.devDependencies ), packageNameFilter(options.filter)); return allDependencies; }
/** * Get the current dependencies from the package file * @param pkg Object with dependencies, devDependencies, and/or optionalDependencies properties * @param options.dev * @param options.filter * @param options.prod * @param options.reject * @returns Promised {packageName: version} collection */ function getCurrentDependencies(pkg, options) { pkg = pkg || {}; options = options || {}; if (!options.prod && !options.dev && !options.optional) { options.prod = options.dev = options.optional = true; } var allDependencies = cint.filterObject(_.merge({}, options.prod && pkg.dependencies, options.dev && pkg.devDependencies, options.optional && pkg.optionalDependencies ), filterAndReject(options.filter, options.reject)); return allDependencies; }
return selectedPackageManager.list([], {cwd: options.cwd}).then(function (results) { if (!results || !results.dependencies) { throw new Error('Unable to retrieve NPM package list'); } // filter out undefined packages or those with a wildcard var filterFunction = filterAndReject(options.filter, options.reject); var validPackages = cint.filterObject(results.dependencies, function (dep, pkgInfo) { return pkgInfo && pkgInfo.name && pkgInfo.version && !versionUtil.isWildPart(pkgInfo.version) && filterFunction(dep); }); // convert the dependency object from npm into a simple object that maps the package name to its version var simpleDependencies = cint.mapObject(validPackages, function (dep, pkgInfo) { return cint.keyValue(dep, pkgInfo.version); }); return simpleDependencies; });
/** * Get the current dependencies from the package file * @param pkg Object with dependencies, devDependencies, and/or optionalDependencies properties * @param options.dev * @param options.filter * @param options.prod * @param options.reject * @returns Promised {packageName: version} collection */ function getCurrentDependencies(pkg, options) { pkg = pkg || {}; options = options || {}; if (!options.prod && !options.dev && !options.optional) { options.prod = options.dev = options.optional = true; } var allDependencies = cint.filterObject(_.merge({}, options.prod && pkg.dependencies, options.dev && pkg.devDependencies, options.optional && pkg.optionalDependencies ), and( options.filter ? packageNameFilter(options.filter) : _.identity, options.reject ? _.negate(packageNameFilter(options.reject)) : _.identity )); return allDependencies; }
/** * @param args.current * @param args.upgraded * @param args.latest (optional) */ function printUpgrades(options, args) { // split the deps into satisfied and unsatisfied to display in two separate tables const deps = Object.keys(args.upgraded); const satisfied = cint.toObject(deps, dep => cint.keyValue(dep, vm.isSatisfied(args.latest[dep], args.current[dep])) ); const isSatisfied = _.propertyOf(satisfied); const upgraded = options.minimal ? cint.filterObject(args.upgraded, cint.not(isSatisfied)) : args.upgraded; const numUpgraded = Object.keys(upgraded).length; print(options, ''); // print everything is up-to-date if (numUpgraded === 0) { const smiley = chalk.green.bold(':)'); if (Object.keys(args.current).length === 0) { print(options, 'No dependencies.'); } else if (options.global) { print(options, `All global packages are up-to-date ${smiley}`); } else { print(options, `All dependencies match the ${vm.getVersionTarget(options)} package versions ${smiley}`); } } // print table if (numUpgraded > 0) { // ToDo this code seems wrong, there is parameter mismatch const table = toDependencyTable({ from: args.current, to: upgraded }); print(options, table.toString()); } }
// TODO: printUpgrades and analyzeProjectDependencies need to be refactored. They are tightly coupled and monolithic. /** * @param args.current * @param args.upgraded * @param args.installed (optional) * @param args.latest (optional) * @param args.pkgData * @param args.pkgFile (optional) * @param args.isUpgrade (optional) */ function printUpgrades(args) { // split the deps into satisfied and unsatisfied to display in two separate tables var deps = Object.keys(args.upgraded); var satisfied = cint.toObject(deps, function (dep) { return cint.keyValue(dep, vm.isSatisfied(args.latest[dep], args.current[dep])); }); var isSatisfied = _.propertyOf(satisfied); var satisfiedUpgraded = cint.filterObject(args.upgraded, function (dep) { return isSatisfied(dep) && (!args.latest || !args.installed || args.latest[dep] !== args.installed[dep]); }); var unsatisfiedUpgraded = cint.filterObject(args.upgraded, cint.not(isSatisfied)); var numSatisfied = Object.keys(satisfiedUpgraded).length; var numUnsatisfied = Object.keys(unsatisfiedUpgraded).length; print(''); // print everything is up-to-date if (numSatisfied === 0 && numUnsatisfied === 0) { var smiley = chalk.yellow(':)'); if (options.global) { print('All global packages are up-to-date ' + smiley); } else { print('All dependencies match the ' + getVersionTarget(options) + ' package versions ' + smiley); } } // print unsatisfied table if (numUnsatisfied > 0) { var unsatisfiedTable = toDependencyTable({ from: args.current, to: unsatisfiedUpgraded }, { greatest: options.greatest, newest: options.newest }); print(unsatisfiedTable.toString()); } // print satisfied table if (numSatisfied > 0) { var satisfiedTable = toDependencyTable({ from: args.current, to: satisfiedUpgraded }, { greatest: options.greatest, newest: options.newest }); print((numUnsatisfied > 0 ? '\n' : '') + 'The following dependenc' + (numSatisfied === 1 ? 'y is' : 'ies are') + ' satisfied by ' + (numSatisfied === 1 ? 'its' : 'their') + ' declared version range, but the installed version' + (numSatisfied === 1 ? ' is' : 's are') + ' behind. You can install the latest version' + (numSatisfied === 1 ? '' : 's') + ' without modifying your package file by using ' + chalk.blue(options.packageManager + ' update') + '. If you want to update the dependenc' + (numSatisfied === 1 ? 'y' : 'ies') + ' in your package file anyway, use ' + chalk.cyan('ncu ') + chalk.blue('-a/--upgradeAll') + '.\n'); print(satisfiedTable.toString()); } var numToUpgrade = numUnsatisfied + (options.upgradeAll ? numSatisfied : 0); if (args.pkgFile && numToUpgrade > 0) { if (options.errorLevel >= 2) { programError('Dependencies not up-to-date'); } else if (args.isUpgrade) { var newPkgData = vm.updatePackageData(args.pkgData, args.current, args.upgraded, args.latest, options); writePackageFile(args.pkgFile, newPkgData) .then(function () { print('Upgraded ' + args.pkgFile + '\n'); }); } else { print('\nRun ' + chalk.cyan('ncu') + ' with ' + chalk.blue(numUnsatisfied > 0 ? '-u' : '-a') + ' to upgrade ' + getPackageFileName()); } } print(''); }