npm-check-updates
Version:
Find newer versions of dependencies than what your package.json allows
239 lines • 11.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOwnerPerDependency = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const json_parse_helpfulerror_1 = __importDefault(require("json-parse-helpfulerror"));
const get_1 = __importDefault(require("lodash/get"));
const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
const pick_1 = __importDefault(require("lodash/pick"));
const prompts_ncu_1 = __importDefault(require("prompts-ncu"));
const semver_1 = require("semver");
const chalk_1 = __importDefault(require("./chalk"));
const getCurrentDependencies_1 = __importDefault(require("./getCurrentDependencies"));
const getIgnoredUpgrades_1 = __importDefault(require("./getIgnoredUpgrades"));
const getPackageManager_1 = __importDefault(require("./getPackageManager"));
const getPeerDependencies_1 = __importDefault(require("./getPeerDependencies"));
const keyValueBy_1 = __importDefault(require("./keyValueBy"));
const logging_1 = require("./logging");
const programError_1 = __importDefault(require("./programError"));
const resolveDepSections_1 = __importDefault(require("./resolveDepSections"));
const upgradePackageData_1 = __importDefault(require("./upgradePackageData"));
const upgradePackageDefinitions_1 = __importDefault(require("./upgradePackageDefinitions"));
const version_util_1 = require("./version-util");
const INTERACTIVE_HINT = `
↑/↓: Select a package
Space: Toggle selection
a: Toggle all
Enter: Upgrade`;
/**
* 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.
* @param options
* @returns
*/
async function getOwnerPerDependency(fromVersion, toVersion, options) {
const packageManager = (0, getPackageManager_1.default)(options, options.packageManager);
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, options);
return {
...(await accum),
[dep]: ownerChanged,
};
}, {});
}
exports.getOwnerPerDependency = getOwnerPerDependency;
/** Prompts the user to choose which upgrades to upgrade. */
const chooseUpgrades = async (oldDependencies, newDependencies, pkgFile, options) => {
var _a;
let chosenDeps = [];
// use toDependencyTable to create choices that are properly padded to align vertically
const table = await (0, logging_1.toDependencyTable)({
from: oldDependencies,
to: newDependencies,
format: options.format,
pkgFile: pkgFile || undefined,
});
const formattedLines = (0, keyValueBy_1.default)(table.toString().split('\n'), line => {
const dep = line.trim().split(' ')[0];
return {
[dep]: line.trim(),
};
});
// do not prompt if there are no dependencies
// prompts will crash if passed an empty list of choices
if (Object.keys(newDependencies).length > 0) {
(0, logging_1.print)(options, '');
if ((_a = options.format) === null || _a === void 0 ? void 0 : _a.includes('group')) {
const groups = (0, version_util_1.getDependencyGroups)(newDependencies, oldDependencies, options);
const choices = groups.flatMap(({ heading, groupName, packages }) => {
return [
{ title: '\n' + heading, heading: true },
// eslint-disable-next-line fp/no-mutating-methods
...Object.keys(packages)
.sort()
.map(dep => ({
title: formattedLines[dep],
value: dep,
selected: ['patch', 'minor'].includes(groupName),
})),
];
});
const response = await (0, prompts_ncu_1.default)({
choices: [...choices, { title: ' ', heading: true }],
hint: INTERACTIVE_HINT,
instructions: false,
message: 'Choose which packages to update',
name: 'value',
optionsPerPage: 50,
type: 'multiselect',
onState: (state) => {
if (state.aborted) {
process.nextTick(() => process.exit(1));
}
},
});
chosenDeps = response.value;
}
else {
// eslint-disable-next-line fp/no-mutating-methods
const choices = Object.keys(newDependencies)
.sort()
.map(dep => ({
title: formattedLines[dep],
value: dep,
selected: true,
}));
const response = await (0, prompts_ncu_1.default)({
choices: [...choices, { title: ' ', heading: true }],
hint: INTERACTIVE_HINT + '\n',
instructions: false,
message: 'Choose which packages to update',
name: 'value',
optionsPerPage: 50,
type: 'multiselect',
onState: (state) => {
if (state.aborted) {
process.nextTick(() => process.exit(1));
}
},
});
chosenDeps = response.value;
}
}
return (0, keyValueBy_1.default)(chosenDeps, dep => ({ [dep]: newDependencies[dep] }));
};
/** Checks local project dependencies for upgrades. */
async function runLocal(options, pkgData, pkgFile) {
(0, logging_1.print)(options, '\nOptions:', 'verbose');
(0, logging_1.printSorted)(options, options, 'verbose');
let pkg;
try {
if (!pkgData) {
(0, programError_1.default)(options, 'Missing package data');
}
else {
// strip comments from jsonc files
const pkgDataStripped = (pkgFile === null || pkgFile === void 0 ? void 0 : pkgFile.endsWith('.jsonc')) && pkgData ? (await import('strip-json-comments')).default(pkgData) : pkgData;
pkg = json_parse_helpfulerror_1.default.parse(pkgDataStripped);
}
}
catch (e) {
(0, programError_1.default)(options, `Invalid package file${pkgFile ? `: ${pkgFile}` : ' from stdin'}. Error details:\n${e.message}`);
}
const current = (0, getCurrentDependencies_1.default)(pkg, options);
(0, logging_1.print)(options, '\nCurrent versions:', 'verbose');
(0, logging_1.print)(options, current, 'verbose');
if (options.enginesNode) {
options.nodeEngineVersion = (0, get_1.default)(pkg, 'engines.node');
}
if (options.peer) {
options.peerDependencies = await (0, getPeerDependencies_1.default)(current, options);
}
const [upgraded, latestResults, upgradedPeerDependencies] = await (0, upgradePackageDefinitions_1.default)(current, options);
const latest = (0, keyValueBy_1.default)(latestResults, (key, result) => (result.version ? { [key]: result.version } : null));
const errors = (0, keyValueBy_1.default)(latestResults, (key, result) => (result.error ? { [key]: result.error } : null));
const time = (0, keyValueBy_1.default)(latestResults, (key, result) => (result.time ? { [key]: result.time } : null));
if (options.peer) {
(0, logging_1.print)(options, '\nupgradedPeerDependencies:', 'verbose');
(0, logging_1.print)(options, upgradedPeerDependencies, 'verbose');
}
(0, logging_1.print)(options, `\n${typeof options.target === 'string' ? `${options.target[0].toUpperCase()}${options.target.slice(1)}` : 'Fetched'} versions:`, 'verbose');
(0, logging_1.print)(options, latest, 'verbose');
(0, logging_1.print)(options, '\nUpgraded versions:', 'verbose');
(0, logging_1.print)(options, upgraded, 'verbose');
// filter out satisfied deps when using --minimal
const filteredUpgraded = options.minimal
? (0, keyValueBy_1.default)(upgraded, (dep, version) => (!(0, semver_1.satisfies)(latest[dep], current[dep]) ? { [dep]: version } : null))
: upgraded;
const ownersChangedDeps = (options.format || []).includes('ownerChanged')
? await getOwnerPerDependency(current, filteredUpgraded, options)
: undefined;
const chosenUpgraded = options.interactive
? await chooseUpgrades(current, filteredUpgraded, pkgFile, options)
: filteredUpgraded;
if (!options.json || options.deep) {
await (0, logging_1.printUpgrades)(
// in interactive mode, do not group upgrades afterwards since the prompts are grouped
options.interactive
? { ...options, format: (options.format || []).filter(formatType => formatType !== 'group') }
: options, {
current,
upgraded: chosenUpgraded,
total: Object.keys(upgraded).length,
latest: latestResults,
ownersChangedDeps,
pkgFile: pkgFile || undefined,
errors,
time,
});
if (options.peer) {
const ignoredUpdates = await (0, getIgnoredUpgrades_1.default)(current, upgraded, upgradedPeerDependencies, options);
if (!(0, isEmpty_1.default)(ignoredUpdates)) {
(0, logging_1.printIgnoredUpdates)(options, ignoredUpdates);
}
}
}
const newPkgData = await (0, upgradePackageData_1.default)(pkgData, current, chosenUpgraded, options);
const output = options.jsonAll
? json_parse_helpfulerror_1.default.parse(newPkgData)
: options.jsonDeps
? (0, pick_1.default)(json_parse_helpfulerror_1.default.parse(newPkgData), (0, resolveDepSections_1.default)(options.dep))
: chosenUpgraded;
// will be overwritten with the result of fs.writeFile so that the return promise waits for the package file to be written
let writePromise = Promise.resolve();
if (options.json && !options.deep) {
(0, logging_1.printJson)(options, output);
}
if (Object.keys(filteredUpgraded).length > 0) {
// if there is a package file, write the new package data
// otherwise, suggest ncu -u
if (pkgFile) {
if (options.upgrade) {
// do not await until the end
writePromise = promises_1.default.writeFile(pkgFile, newPkgData);
}
else {
const ncuCmd = process.env.npm_lifecycle_event === 'npx' ? 'npx npm-check-updates' : 'ncu';
// quote arguments with spaces
const argv = process.argv
.slice(2)
.map(arg => (arg.includes(' ') ? `"${arg}"` : arg))
.join(' ');
const ncuOptions = argv ? ' ' + argv : argv;
const upgradeHint = `\nRun ${chalk_1.default.cyan(`${ncuCmd}${ncuOptions} -u`)} to upgrade ${options.packageFile || 'package.json'}`;
(0, logging_1.print)(options, upgradeHint);
}
}
}
await writePromise;
return output;
}
exports.default = runLocal;
//# sourceMappingURL=runLocal.js.map