UNPKG

npm-check-updates

Version:

Find newer versions of dependencies than what your package.json allows

239 lines 11.5 kB
"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