From a5d7163e0d0d698c6c5d7f91aeeab84f40c06bd0 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Thu, 16 Jan 2020 23:16:05 -0500 Subject: [PATCH 01/10] Add owner changed opt-in flag --- bin/ncu | 1 + lib/npm-check-updates.js | 55 ++++++++++++++++++++++++++++++------- lib/package-managers/npm.js | 27 +++++++++++++++++- lib/versionmanager.js | 5 ++-- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/bin/ncu b/bin/ncu index 3c42db96..cfa19dd6 100755 --- a/bin/ncu +++ b/bin/ncu @@ -26,6 +26,7 @@ program .option('--engines-node', 'upgrade to version which satisfies engines.node range') .option('-f, --filter ', 'include only package names matching the given string, comma-or-space-delimited list, or /regex/') .option('-g, --global', 'check global packages instead of in the current project') + .option('-o, --ownerChanged', 'check if package owner changed and show as additional column') // program.json is set to true in programInit if any options that begin with 'json' are true .option('-i, --interactive', 'Enable interactive prompts for each dependency; implies -u unless one of the json options are set') .option('-j, --jsonAll', 'output new package file instead of human-readable message') diff --git a/lib/npm-check-updates.js b/lib/npm-check-updates.js index c8408c98..9a45d6aa 100644 --- a/lib/npm-check-updates.js +++ b/lib/npm-check-updates.js @@ -77,8 +77,8 @@ function getPackageFileName(options) { } function createDependencyTable() { - return new Table({ - colAligns: ['left', 'right', 'right', 'right'], + const baseTable = new Table({ + colAligns: ['left', 'right', 'right', 'right', 'right'], chars: { top: '', 'top-mid': '', @@ -97,20 +97,54 @@ function createDependencyTable() { middle: '' } }); + return baseTable; +} + +/** + * @param {object} args + * @param {object} options + * @returns {Promise} + */ +async function getOwnerPerDependency(args, options) { + const packageManager = vm.getPackageManager(options.packageManager); + args.owner = {}; + await Promise.all( + Object.keys(args.to).map(async (dep) => { + const from = args.from[dep] || null; + const to = args.to[dep] || null; + const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to); + // eslint-disable-next-line require-atomic-updates + args.owner[dep] = ownerChanged; + }) + ); + return args; } /** * @param {object} args * @param {object} args.from * @param {object} args.to - * @returns {Table} + * @param {object} args.owner + * @param {object} options + * @returns {Promise} */ -function toDependencyTable(args) { +async function toDependencyTable(args, options) { const table = createDependencyTable(); - const rows = Object.keys(args.to).map(dep => { - const from = args.from[dep] || ''; - const to = versionUtil.colorizeDiff(args.from[dep], args.to[dep] || ''); - return [dep, from, '→', to]; + let mappedArgs = []; + if (options.ownerChanged) { + const newArgs = await getOwnerPerDependency(args, options); + mappedArgs = _.cloneDeep(newArgs); + } else { + mappedArgs = _.cloneDeep(args); + } + const rows = Object.keys(mappedArgs.to).map(dep => { + const from = mappedArgs.from[dep] || ''; + const to = versionUtil.colorizeDiff(mappedArgs.from[dep], mappedArgs.to[dep] || ''); + let owner = ''; + if (options.ownerChanged) { + owner = mappedArgs.owner && mappedArgs.owner[dep] ? '*owner changed*' : ''; + } + return [dep, from, '→', to, owner]; }); rows.forEach(row => table.push(row)); return table; @@ -296,11 +330,12 @@ function printUpgrades(options, {current, upgraded, numUpgraded, total}) { // print table if (numUpgraded > 0) { - const table = toDependencyTable({ + toDependencyTable({ from: current, to: upgraded + }, options).then(table => { + print(options, table.toString()); }); - print(options, table.toString()); } } diff --git a/lib/package-managers/npm.js b/lib/package-managers/npm.js index b7b1e7b6..96dcd47b 100644 --- a/lib/package-managers/npm.js +++ b/lib/package-managers/npm.js @@ -44,6 +44,30 @@ function parseJson(result, data) { return json; } +/** + * Check if package author changed between current and next version. + * @param {string} packageName Name of the package + * @param {string} currentVersion + * @param {string} latestVersion + * @returns {Promise} A promise that fullfills with boolean value. + */ +async function packageAuthorChanged(packageName, currentVersion, latestVersion) { + npmConfig.fullMetadata = true; + + const current = versionUtil.isWildCard(currentVersion.charAt(0)) ? currentVersion.substr(1) : currentVersion; + const latest = versionUtil.isWildCard(latestVersion.charAt(0)) ? latestVersion.substr(1) : latestVersion; + + const result = await pacote.packument(packageName, npmConfig); + if (result.versions) { + const currentData = result.versions[current]; + const latestData = result.versions[latest]; + const currentAuthor = currentData && currentData.author ? currentData.author : null; + const latestAuthor = latestData && latestData.author ? latestData.author : null; + return !_.isEqual(currentAuthor, latestAuthor); + } + return false; +} + /** * Returns the value of one of the properties retrieved by npm view. * @param {string} packageName Name of the package @@ -283,5 +307,6 @@ module.exports = { ); }, - defaultPrefix + defaultPrefix, + packageAuthorChanged }; diff --git a/lib/versionmanager.js b/lib/versionmanager.js index 0d790c61..ef8b49ee 100644 --- a/lib/versionmanager.js +++ b/lib/versionmanager.js @@ -419,10 +419,10 @@ function queryVersions(packageMap, options = {}) { /** * Ignore 404 errors from getPackageVersion by having them return `null` * instead of rejecting. - * @param dep + * @param {string} dep * @returns {Promise} */ - function getPackageVersionProtected(dep) { + async function getPackageVersionProtected(dep) { return getPackageVersion(dep, packageMap[dep], Object.assign( {}, options, @@ -554,6 +554,7 @@ module.exports = { isSatisfied, upgradePackageData, upgradePackageDefinitions, + getPackageManager, // exposed for testing getPreferredWildcard, From 4e46c1836735771211cf01b698114c4d218f0390 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 17 Jan 2020 21:57:00 -0500 Subject: [PATCH 02/10] Refactoring of owner data per dependency --- lib/npm-check-updates.js | 66 +++++++++++++--------------------------- lib/versionmanager.js | 24 +++++++++++++++ 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/lib/npm-check-updates.js b/lib/npm-check-updates.js index 9a45d6aa..224d1019 100644 --- a/lib/npm-check-updates.js +++ b/lib/npm-check-updates.js @@ -77,7 +77,7 @@ function getPackageFileName(options) { } function createDependencyTable() { - const baseTable = new Table({ + return new Table({ colAligns: ['left', 'right', 'right', 'right', 'right'], chars: { top: '', @@ -97,27 +97,6 @@ function createDependencyTable() { middle: '' } }); - return baseTable; -} - -/** - * @param {object} args - * @param {object} options - * @returns {Promise} - */ -async function getOwnerPerDependency(args, options) { - const packageManager = vm.getPackageManager(options.packageManager); - args.owner = {}; - await Promise.all( - Object.keys(args.to).map(async (dep) => { - const from = args.from[dep] || null; - const to = args.to[dep] || null; - const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to); - // eslint-disable-next-line require-atomic-updates - args.owner[dep] = ownerChanged; - }) - ); - return args; } /** @@ -126,24 +105,14 @@ async function getOwnerPerDependency(args, options) { * @param {object} args.to * @param {object} args.owner * @param {object} options - * @returns {Promise
} + * @returns {Table} */ -async function toDependencyTable(args, options) { +function toDependencyTable(args, options) { const table = createDependencyTable(); - let mappedArgs = []; - if (options.ownerChanged) { - const newArgs = await getOwnerPerDependency(args, options); - mappedArgs = _.cloneDeep(newArgs); - } else { - mappedArgs = _.cloneDeep(args); - } - const rows = Object.keys(mappedArgs.to).map(dep => { - const from = mappedArgs.from[dep] || ''; - const to = versionUtil.colorizeDiff(mappedArgs.from[dep], mappedArgs.to[dep] || ''); - let owner = ''; - if (options.ownerChanged) { - owner = mappedArgs.owner && mappedArgs.owner[dep] ? '*owner changed*' : ''; - } + const rows = Object.keys(args.to).map(dep => { + const from = args.from[dep] || ''; + const to = versionUtil.colorizeDiff(args.from[dep], args.to[dep] || ''); + const owner = options.ownerChanged && args.owner && args.owner[dep] ? '*owner changed*' : ''; return [dep, from, '→', to, owner]; }); rows.forEach(row => table.push(row)); @@ -260,6 +229,11 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) { const filteredUpgraded = options.minimal ? cint.filterObject(selectedNewDependencies, cint.not(isSatisfied)) : selectedNewDependencies; const numUpgraded = Object.keys(filteredUpgraded).length; + let owners = {}; + if (options.ownerChanged) { + owners = await vm.getOwnerPerDependency(current, filteredUpgraded, options); + } + // print if (options.json) { // use the selectedNewDependencies dependencies data to generate new package data @@ -271,7 +245,8 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) { upgraded: filteredUpgraded, latest, numUpgraded, - total: Object.keys(upgraded).length + total: Object.keys(upgraded).length, + owners }); } @@ -309,9 +284,10 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) { * @param {Object} args.upgraded - The packages that should be upgraded. * @param {number} args.numUpgraded - The number of upgraded packages * @param {number} args.total - The total number of all possible upgrades + * @param {Object} args.owners - Boolean flag per dependency which announces if package owner changed. * @returns {void} */ -function printUpgrades(options, {current, upgraded, numUpgraded, total}) { +function printUpgrades(options, {current, upgraded, numUpgraded, total, owners}) { print(options, ''); // print everything is up-to-date @@ -330,12 +306,12 @@ function printUpgrades(options, {current, upgraded, numUpgraded, total}) { // print table if (numUpgraded > 0) { - toDependencyTable({ + const table = toDependencyTable({ from: current, - to: upgraded - }, options).then(table => { - print(options, table.toString()); - }); + to: upgraded, + owners + }, options); + print(options, table.toString()); } } diff --git a/lib/versionmanager.js b/lib/versionmanager.js index ef8b49ee..c94a11f4 100644 --- a/lib/versionmanager.js +++ b/lib/versionmanager.js @@ -238,6 +238,29 @@ function filterAndReject(filter, reject) { ); } +/** + * Return a promise which resolves to object storing package owners per dependency. + * @param {object} fromVersion current packages version. + * @param {object} toVersion target packages version. + * @param {object} options + * @returns {Promise} + */ +async function getOwnerPerDependency(fromVersion, toVersion, options) { + const packageManager = getPackageManager(options.packageManager); + const owner = {}; + await Promise.all( + Object.keys(toVersion).map(async (dep) => { + const from = fromVersion[dep] || null; + const to = toVersion[dep] || null; + const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to); + return Object.assign({}, owner, { + [dep]: ownerChanged + }); + }) + ); + return owner; +} + /** * Returns an 2-tuple of upgradedDependencies and their latest versions. * @param {Object} currentDependencies @@ -555,6 +578,7 @@ module.exports = { upgradePackageData, upgradePackageDefinitions, getPackageManager, + getOwnerPerDependency, // exposed for testing getPreferredWildcard, From 17ff16b153b29233b5615dd8d4a4c546ca7830ba Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Sun, 10 May 2020 18:00:23 -0500 Subject: [PATCH 03/10] Check max version match for the passed versions --- lib/package-managers/npm.js | 16 ++++++++-------- lib/versionmanager.js | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/package-managers/npm.js b/lib/package-managers/npm.js index 3a20def4..7ed542fb 100644 --- a/lib/package-managers/npm.js +++ b/lib/package-managers/npm.js @@ -54,16 +54,16 @@ function parseJson(result, data) { async function packageAuthorChanged(packageName, currentVersion, latestVersion) { npmConfig.fullMetadata = true; - const current = versionUtil.isWildCard(currentVersion.charAt(0)) ? currentVersion.substr(1) : currentVersion; - const latest = versionUtil.isWildCard(latestVersion.charAt(0)) ? latestVersion.substr(1) : latestVersion; - const result = await pacote.packument(packageName, npmConfig); if (result.versions) { - const currentData = result.versions[current]; - const latestData = result.versions[latest]; - const currentAuthor = currentData && currentData.author ? currentData.author : null; - const latestAuthor = latestData && latestData.author ? latestData.author : null; - return !_.isEqual(currentAuthor, latestAuthor); + let pkgVersions = Object.keys(result.versions); + const current = semver.maxSatisfying(pkgVersions, currentVersion); + const latest = semver.maxSatisfying(pkgVersions, latestVersion); + if (current && latest) { + const currentAuthor = result.versions[current].author; + const latestAuthor = result.versions[latest].author; + return !_.isEqual(currentAuthor, latestAuthor); + } } return false; } diff --git a/lib/versionmanager.js b/lib/versionmanager.js index f48adc72..8a7164f7 100644 --- a/lib/versionmanager.js +++ b/lib/versionmanager.js @@ -594,6 +594,5 @@ module.exports = { isUpgradeable, queryVersions, upgradeDependencies, - upgradeDependencyDeclaration, - getPackageManager + upgradeDependencyDeclaration }; From 6995040e8db84e45cb2aab2b3b88a629868499a3 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 12 Jun 2020 20:27:01 -0500 Subject: [PATCH 04/10] Add ownerChanged cli option to readme --- README.md | 2 ++ bin/ncu | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1cad1af0..be8b5571 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ Options satisfied by the existing version range (v2 behavior). -n, --newest find the newest published versions available instead of the latest stable versions + -o, --ownerChanged check if the package owner changed between current + and next version. -p, --packageManager npm or bower (default: npm) --packageData include stringified package file (use stdin instead) --packageFile package file location (default: ./package.json) diff --git a/bin/ncu b/bin/ncu index 8babf011..3606be53 100755 --- a/bin/ncu +++ b/bin/ncu @@ -27,7 +27,6 @@ program .option('--engines-node', 'upgrade to version which satisfies engines.node range') .option('-f, --filter ', 'include only package names matching the given string, comma-or-space-delimited list, or /regex/') .option('-g, --global', 'check global packages instead of in the current project') - .option('-o, --ownerChanged', 'check if package owner changed and show as additional column') // program.json is set to true in programInit if any options that begin with 'json' are true .option('-i, --interactive', 'Enable interactive prompts for each dependency; implies -u unless one of the json options are set') .option('-j, --jsonAll', 'output new package file instead of human-readable message') @@ -36,6 +35,7 @@ program .option('-l, --loglevel ', 'what level of logs to report: silent, error, minimal, warn, info, verbose, silly', 'warn') .option('-m, --minimal', 'do not upgrade newer versions that are already satisfied by the version range according to semver') .option('-n, --newest', 'find the newest versions available instead of the latest stable versions') + .option('-o, --ownerChanged', 'check if package owner changed and show as additional column') .option('-p, --packageManager ', 'npm (default) or bower', 'npm') .option('--packageData', 'include stringified package file (use stdin instead)') .option('--packageFile ', 'package file location (default: ./package.json)') From dcfc1b0ac40c45deba326d8f457e562f64c63f49 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 12 Jun 2020 20:27:32 -0500 Subject: [PATCH 05/10] Avoid passing options to dependency table function --- lib/npm-check-updates.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/npm-check-updates.js b/lib/npm-check-updates.js index 7dfdfe31..7a8e3151 100644 --- a/lib/npm-check-updates.js +++ b/lib/npm-check-updates.js @@ -107,12 +107,12 @@ function createDependencyTable() { * @param {object} options * @returns {Table} */ -function toDependencyTable(args, options) { +function toDependencyTable(args) { const table = createDependencyTable(); const rows = Object.keys(args.to).map(dep => { const from = args.from[dep] || ''; const to = versionUtil.colorizeDiff(args.from[dep], args.to[dep] || ''); - const owner = options.ownerChanged && args.owner && args.owner[dep] ? '*owner changed*' : ''; + const owner = args.owner && args.owner[dep] ? '*owner changed*' : ''; return [dep, from, '→', to, owner]; }); rows.forEach(row => table.push(row)); @@ -313,7 +313,7 @@ function printUpgrades(options, {current, upgraded, numUpgraded, total, owners}) from: current, to: upgraded, owners - }, options); + }); print(options, table.toString()); } } From 1c373683b059ba098d7150f295f3b3ac2dec78d5 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 12 Jun 2020 20:27:49 -0500 Subject: [PATCH 06/10] use npmUser rather than author on semver check --- lib/package-managers/npm.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/package-managers/npm.js b/lib/package-managers/npm.js index 7ed542fb..2b49ef76 100644 --- a/lib/package-managers/npm.js +++ b/lib/package-managers/npm.js @@ -57,11 +57,11 @@ async function packageAuthorChanged(packageName, currentVersion, latestVersion) const result = await pacote.packument(packageName, npmConfig); if (result.versions) { let pkgVersions = Object.keys(result.versions); - const current = semver.maxSatisfying(pkgVersions, currentVersion); + const current = currentVersion; const latest = semver.maxSatisfying(pkgVersions, latestVersion); if (current && latest) { - const currentAuthor = result.versions[current].author; - const latestAuthor = result.versions[latest].author; + const currentAuthor = result.versions[current]._npmUser.name; + const latestAuthor = result.versions[latest]._npmUser.name; return !_.isEqual(currentAuthor, latestAuthor); } } From 25e39503d3bc43d57c0824e6484524a2fb16a7b8 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Fri, 12 Jun 2020 21:12:50 -0500 Subject: [PATCH 07/10] Fix linter errors due to merge --- bin/ncu.js | 1 + lib/npm-check-updates.js | 2 +- lib/package-managers/npm.js | 33 +++++++++++---------- lib/versionmanager.js | 59 ++++++++++--------------------------- 4 files changed, 35 insertions(+), 60 deletions(-) diff --git a/bin/ncu.js b/bin/ncu.js index 233c9c33..92c525b7 100755 --- a/bin/ncu.js +++ b/bin/ncu.js @@ -35,6 +35,7 @@ program .option('-l, --loglevel ', 'what level of logs to report: silent, error, minimal, warn, info, verbose, silly', 'warn') .option('-m, --minimal', 'do not upgrade newer versions that are already satisfied by the version range according to semver') .option('-n, --newest', 'find the newest versions available instead of the latest stable versions') + .option('-o, --ownerChanged', 'check if package owner changed and show as additional column') .option('-p, --packageManager ', 'npm (default)', 'npm') .option('--packageData', 'include stringified package file (use stdin instead)') .option('--packageFile ', 'package file location (default: ./package.json)') diff --git a/lib/npm-check-updates.js b/lib/npm-check-updates.js index 7d701f9b..446ad7aa 100644 --- a/lib/npm-check-updates.js +++ b/lib/npm-check-updates.js @@ -236,7 +236,7 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) { let owners = {} if (options.ownerChanged) { - owners = await vm.getOwnerPerDependency(current, filteredUpgraded, options) + owners = await vm.getOwnerPerDependency(current, filteredUpgraded, options) } // print diff --git a/lib/package-managers/npm.js b/lib/package-managers/npm.js index caa17e1b..d58d3d5b 100644 --- a/lib/package-managers/npm.js +++ b/lib/package-managers/npm.js @@ -50,26 +50,27 @@ function parseJson(result, data) { /** * Check if package author changed between current and next version. - * @param {string} packageName Name of the package - * @param {string} currentVersion - * @param {string} latestVersion - * @returns {Promise} A promise that fullfills with boolean value. + * + * @param packageName Name of the package + * @param currentVersion + * @param latestVersion + * @returns A promise that fullfills with boolean value. */ async function packageAuthorChanged(packageName, currentVersion, latestVersion) { - npmConfig.fullMetadata = true; + npmConfig.fullMetadata = true - const result = await pacote.packument(packageName, npmConfig); - if (result.versions) { - let pkgVersions = Object.keys(result.versions); - const current = currentVersion; - const latest = semver.maxSatisfying(pkgVersions, latestVersion); - if (current && latest) { - const currentAuthor = result.versions[current]._npmUser.name; - const latestAuthor = result.versions[latest]._npmUser.name; - return !_.isEqual(currentAuthor, latestAuthor); - } + const result = await pacote.packument(packageName, npmConfig) + if (result.versions) { + const pkgVersions = Object.keys(result.versions) + const current = currentVersion + const latest = semver.maxSatisfying(pkgVersions, latestVersion) + if (current && latest) { + const currentAuthor = result.versions[current]._npmUser.name + const latestAuthor = result.versions[latest]._npmUser.name + return !_.isEqual(currentAuthor, latestAuthor) } - return false; + } + return false } /** diff --git a/lib/versionmanager.js b/lib/versionmanager.js index 9265b5d7..3ff5e6e0 100644 --- a/lib/versionmanager.js +++ b/lib/versionmanager.js @@ -258,25 +258,26 @@ function filterAndReject(filter, reject) { /** * Return a promise which resolves to object storing package owners per dependency. - * @param {object} fromVersion current packages version. - * @param {object} toVersion target packages version. - * @param {object} options - * @returns {Promise} + * + * @param fromVersion current packages version. + * @param toVersion target packages version. + * @param options + * @returns */ async function getOwnerPerDependency(fromVersion, toVersion, options) { - const packageManager = getPackageManager(options.packageManager); - const owner = {}; + const packageManager = getPackageManager(options.packageManager) + const owner = {} await Promise.all( - Object.keys(toVersion).map(async (dep) => { - const from = fromVersion[dep] || null; - const to = toVersion[dep] || null; - const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to); - return Object.assign({}, owner, { - [dep]: ownerChanged - }); + Object.keys(toVersion).map(async dep => { + const from = fromVersion[dep] || null + const to = toVersion[dep] || null + const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to) + return Object.assign({}, owner, { + [dep]: ownerChanged }) - ); - return owner; + }) + ) + return owner } /** @@ -623,31 +624,3 @@ module.exports = { upgradeDependencyDeclaration, getPackageManager, } -for use in `new RegExp()` - */ -function escapeRegexp(s) { - return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); // Thanks Stack Overflow! -} - -// -// API -// - -module.exports = { - // used directly by npm-check-updates.js - getCurrentDependencies, - getInstalledPackages, - getVersionTarget, - isSatisfied, - upgradePackageData, - upgradePackageDefinitions, - getPackageManager, - getOwnerPerDependency, - - // exposed for testing - getPreferredWildcard, - isUpgradeable, - queryVersions, - upgradeDependencies, - upgradeDependencyDeclaration -}; From 947430cb5c18145014d627fe30e201c15bda7ccd Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Sat, 22 Aug 2020 11:36:48 -0500 Subject: [PATCH 08/10] tests: add packageAuthorChanged tests --- lib/logging.js | 2 +- lib/package-managers/npm.js | 14 ++++++++------ test/package-managers/npm/index.js | 6 ++++++ test/test-bin.js | 4 ++-- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/logging.js b/lib/logging.js index a94d9642..af8e1767 100644 --- a/lib/logging.js +++ b/lib/logging.js @@ -78,7 +78,7 @@ function toDependencyTable(args) { const to = isGithubUrl(toRaw) ? getGithubUrlTag(toRaw) : isNpmAlias(toRaw) ? parseNpmAlias(toRaw)[1] : toRaw - const owner = args.owner && args.owner[dep] ? '*owner changed*' : '' + const owner = args.owner && args.owner[dep] ? args.owner[dep] ? '*owner changed*' : '' : '*unknown*' const toColorized = colorizeDiff(from, to) return [dep, from, '→', toColorized, owner] }) diff --git a/lib/package-managers/npm.js b/lib/package-managers/npm.js index 1621162e..a23da6c4 100644 --- a/lib/package-managers/npm.js +++ b/lib/package-managers/npm.js @@ -91,15 +91,17 @@ async function packageAuthorChanged(packageName, currentVersion, latestVersion) const result = await pacote.packument(packageName, npmConfig) if (result.versions) { const pkgVersions = Object.keys(result.versions) - const current = currentVersion - const latest = semver.maxSatisfying(pkgVersions, latestVersion) + const current = semver.minSatisfying(pkgVersions, currentVersion) + const latest = semver.minSatisfying(pkgVersions, latestVersion) if (current && latest) { - const currentAuthor = result.versions[current]._npmUser.name - const latestAuthor = result.versions[latest]._npmUser.name - return !_.isEqual(currentAuthor, latestAuthor) + if (result.versions[current]._npmUser && result.versions[latest]._npmUser) { + const currentAuthor = result.versions[current]._npmUser.name + const latestAuthor = result.versions[latest]._npmUser.name + return !_.isEqual(currentAuthor, latestAuthor) + } } } - return false + return null } /** diff --git a/test/package-managers/npm/index.js b/test/package-managers/npm/index.js index 9b4ca77a..11a8ecfb 100644 --- a/test/package-managers/npm/index.js +++ b/test/package-managers/npm/index.js @@ -26,4 +26,10 @@ describe('npm', function () { await packageManagers.npm.greatest('ncu-test-greatest-not-newest', null, { cwd: __dirname }) .should.eventually.equal('2.0.0-beta') ) + + it('ownerChanged', async () => { + await packageManagers.npm.packageAuthorChanged('mocha', '^7.1.0', '8.0.1').should.eventually.equal(true) + await packageManagers.npm.packageAuthorChanged('htmlparser2', '^3.10.1', '^4.0.0').should.eventually.equal(false) + await packageManagers.npm.packageAuthorChanged('ncu-test-v2', '^1.0.0', '2.2.0').should.eventually.be.null + }) }) diff --git a/test/test-bin.js b/test/test-bin.js index 71326a94..ffe31bbc 100644 --- a/test/test-bin.js +++ b/test/test-bin.js @@ -251,7 +251,7 @@ describe('bin', function () { 'ncu-test-v2': 'https://github.com/raineorshine/ncu-test-v2.git#v1.0.0' } const output = await spawn('node', ['bin/cli.js'], JSON.stringify({ dependencies })) - output.trim().should.equal('ncu-test-v2 https://github.com/raineorshine/ncu-test-v2.git#v1.0.0 → v2.0.0') + output.trim().should.equal('ncu-test-v2 https://github.com/raineorshine/ncu-test-v2.git#v1.0.0 → v2.0.0 *unknown*') }) it('should strip prefix from npm alias in "to" output', async () => { @@ -259,7 +259,7 @@ describe('bin', function () { request: 'npm:ncu-test-v2@1.0.0' } const output = await spawn('node', ['bin/cli.js'], JSON.stringify({ dependencies })) - output.trim().should.equal('request npm:ncu-test-v2@1.0.0 → 2.0.0') + output.trim().should.equal('request npm:ncu-test-v2@1.0.0 → 2.0.0 *unknown*') }) }) From 3c4db9f26c98ec30718752f9e2b001cf79ff73fd Mon Sep 17 00:00:00 2001 From: Raine Revere Date: Fri, 28 Aug 2020 16:31:25 -0600 Subject: [PATCH 09/10] Add --ownerChanged to CLI options. --- README.md | 4 ++-- lib/constants.js | 4 ++++ lib/npm-check-updates.d.ts | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e7bcdaa..6fdb482f 100644 --- a/README.md +++ b/README.md @@ -116,9 +116,9 @@ $ ncu "/^(?!gulp-).*$/" # windows satisfied by the version range according to semver. -n, --newest DEPRECATED. Renamed to "--target newest". --o, --ownerChanged Check if the package owner changed between current - and next version. -p, --packageManager npm, yarn (default: "npm") +-o, --ownerChanged Check if the package owner changed between + current and upgraded version. --packageData Include stringified package file (you can also send to stdin). --packageFile Package file location (default: ./package.json). diff --git a/lib/constants.js b/lib/constants.js index 783b981c..79bdbf99 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -80,6 +80,10 @@ const cliOptions = [ description: 'npm, yarn', default: 'npm' }, + { + name: '-o, --ownerChanged', + description: 'Check if the package owner changed between current and upgraded version.', + }, { name: '--packageData', description: 'Include stringified package file (you can also send to stdin).' diff --git a/lib/npm-check-updates.d.ts b/lib/npm-check-updates.d.ts index b1daf0d1..f445aae7 100644 --- a/lib/npm-check-updates.d.ts +++ b/lib/npm-check-updates.d.ts @@ -56,6 +56,9 @@ declare namespace ncu { /** npm, yarn (default: "npm") */ packageManager?: string; + /** Check if the package owner changed between current and upgraded version. */ + ownerChanged?: boolean; + /** Include stringified package file (you can also send to stdin). */ packageData?: boolean; From 7f08648147c2e8cff7a9d360aed7e7f0cf70dcfc Mon Sep 17 00:00:00 2001 From: Raine Revere Date: Fri, 28 Aug 2020 19:44:45 -0600 Subject: [PATCH 10/10] ownerChanged: Only print with -o, fix getOwnerPerDependency, refactor. --- lib/logging.js | 16 ++++++++++------ lib/npm-check-updates.js | 9 ++++----- lib/package-managers/npm.js | 21 ++++++++++----------- lib/versionmanager.js | 23 ++++++++++------------- test/test-bin.js | 4 ++-- 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/lib/logging.js b/lib/logging.js index af8e1767..ff3f2b22 100644 --- a/lib/logging.js +++ b/lib/logging.js @@ -67,7 +67,7 @@ function createDependencyTable() { * @param args * @param args.from * @param args.to - * @param args.owner + * @param args.ownersChangedDeps * @returns */ function toDependencyTable(args) { @@ -78,9 +78,13 @@ function toDependencyTable(args) { const to = isGithubUrl(toRaw) ? getGithubUrlTag(toRaw) : isNpmAlias(toRaw) ? parseNpmAlias(toRaw)[1] : toRaw - const owner = args.owner && args.owner[dep] ? args.owner[dep] ? '*owner changed*' : '' : '*unknown*' + const ownerChanged = args.ownersChangedDeps + ? dep in args.ownersChangedDeps + ? args.ownersChangedDeps[dep] ? '*owner changed*' : '' + : '*unknown*' + : '' const toColorized = colorizeDiff(from, to) - return [dep, from, '→', toColorized, owner] + return [dep, from, '→', toColorized, ownerChanged] }) rows.forEach(row => table.push(row)) // eslint-disable-line fp/no-mutating-methods return table @@ -93,9 +97,9 @@ function toDependencyTable(args) { * @param args.upgraded - The packages that should be upgraded. * @param args.numUpgraded - The number of upgraded packages * @param args.total - The total number of all possible upgrades - * @param args.owners - Boolean flag per dependency which announces if package owner changed. + * @param args.ownersChangedDeps - Boolean flag per dependency which announces if package owner changed. */ -function printUpgrades(options, { current, upgraded, numUpgraded, total, owners }) { +function printUpgrades(options, { current, upgraded, numUpgraded, total, ownersChangedDeps }) { print(options, '') // print everything is up-to-date @@ -120,7 +124,7 @@ function printUpgrades(options, { current, upgraded, numUpgraded, total, owners const table = toDependencyTable({ from: current, to: upgraded, - owner: owners + ownersChangedDeps }) print(options, table.toString()) } diff --git a/lib/npm-check-updates.js b/lib/npm-check-updates.js index b3320dd0..8548bb0d 100644 --- a/lib/npm-check-updates.js +++ b/lib/npm-check-updates.js @@ -167,10 +167,9 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) { const filteredUpgraded = options.minimal ? cint.filterObject(selectedNewDependencies, dep => !isSatisfied(dep)) : selectedNewDependencies const numUpgraded = Object.keys(filteredUpgraded).length - let owners = {} - if (options.ownerChanged) { - owners = await vm.getOwnerPerDependency(current, filteredUpgraded, options) - } + const ownersChangedDeps = options.ownerChanged + ? await vm.getOwnerPerDependency(current, filteredUpgraded, options) + : null // print if (options.json) { @@ -185,7 +184,7 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) { latest, numUpgraded, total: Object.keys(upgraded).length, - owners + ownersChangedDeps }) } diff --git a/lib/package-managers/npm.js b/lib/package-managers/npm.js index a23da6c4..34c9909b 100644 --- a/lib/package-managers/npm.js +++ b/lib/package-managers/npm.js @@ -78,29 +78,28 @@ function parseJson(result, data) { } /** - * Check if package author changed between current and next version. + * Check if package author changed between current and upgraded version. * * @param packageName Name of the package - * @param currentVersion - * @param latestVersion + * @param currentVersion Current version declaration (may be range) + * @param upgradedVersion Upgraded version declaration (may be range) * @returns A promise that fullfills with boolean value. */ -async function packageAuthorChanged(packageName, currentVersion, latestVersion) { +async function packageAuthorChanged(packageName, currentVersion, upgradedVersion) { npmConfig.fullMetadata = true const result = await pacote.packument(packageName, npmConfig) if (result.versions) { const pkgVersions = Object.keys(result.versions) const current = semver.minSatisfying(pkgVersions, currentVersion) - const latest = semver.minSatisfying(pkgVersions, latestVersion) - if (current && latest) { - if (result.versions[current]._npmUser && result.versions[latest]._npmUser) { - const currentAuthor = result.versions[current]._npmUser.name - const latestAuthor = result.versions[latest]._npmUser.name - return !_.isEqual(currentAuthor, latestAuthor) - } + const upgraded = semver.maxSatisfying(pkgVersions, upgradedVersion) + if (current && upgraded && result.versions[current]._npmUser && result.versions[upgraded]._npmUser) { + const currentAuthor = result.versions[current]._npmUser.name + const latestAuthor = result.versions[upgraded]._npmUser.name + return !_.isEqual(currentAuthor, latestAuthor) } } + return null } diff --git a/lib/versionmanager.js b/lib/versionmanager.js index f0fddde4..8ad6f36a 100644 --- a/lib/versionmanager.js +++ b/lib/versionmanager.js @@ -273,7 +273,7 @@ function filterAndReject(filter, reject) { } /** - * Return a promise which resolves to object storing package owners per dependency. + * Return a promise which resolves to object storing package owner changed status for each dependency. * * @param fromVersion current packages version. * @param toVersion target packages version. @@ -282,18 +282,15 @@ function filterAndReject(filter, reject) { */ async function getOwnerPerDependency(fromVersion, toVersion, options) { const packageManager = getPackageManager(options.packageManager) - const owner = {} - await Promise.all( - Object.keys(toVersion).map(async dep => { - const from = fromVersion[dep] || null - const to = toVersion[dep] || null - const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to) - return Object.assign({}, owner, { - [dep]: ownerChanged - }) - }) - ) - return owner + return await Object.keys(toVersion).reduce(async (accum, dep) => { + const from = fromVersion[dep] || null + const to = toVersion[dep] || null + const ownerChanged = await packageManager.packageAuthorChanged(dep, from, to) + return { + ...accum, + [dep]: ownerChanged, + } + }, {}) } /** diff --git a/test/test-bin.js b/test/test-bin.js index ffe31bbc..71326a94 100644 --- a/test/test-bin.js +++ b/test/test-bin.js @@ -251,7 +251,7 @@ describe('bin', function () { 'ncu-test-v2': 'https://github.com/raineorshine/ncu-test-v2.git#v1.0.0' } const output = await spawn('node', ['bin/cli.js'], JSON.stringify({ dependencies })) - output.trim().should.equal('ncu-test-v2 https://github.com/raineorshine/ncu-test-v2.git#v1.0.0 → v2.0.0 *unknown*') + output.trim().should.equal('ncu-test-v2 https://github.com/raineorshine/ncu-test-v2.git#v1.0.0 → v2.0.0') }) it('should strip prefix from npm alias in "to" output', async () => { @@ -259,7 +259,7 @@ describe('bin', function () { request: 'npm:ncu-test-v2@1.0.0' } const output = await spawn('node', ['bin/cli.js'], JSON.stringify({ dependencies })) - output.trim().should.equal('request npm:ncu-test-v2@1.0.0 → 2.0.0 *unknown*') + output.trim().should.equal('request npm:ncu-test-v2@1.0.0 → 2.0.0') }) })