1
0
Fork 0
mirror of https://github.com/shimataro/ssh-key-action.git synced 2025-06-19 22:52:10 +10:00

* first action! (#1)

This commit is contained in:
shimataro 2019-09-18 20:39:54 +09:00 committed by GitHub
parent 8deacc95b1
commit ace1e6a69a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3750 changed files with 1155519 additions and 0 deletions

438
node_modules/npm-check-updates/lib/npm-check-updates.js generated vendored Normal file
View file

@ -0,0 +1,438 @@
//
// Dependencies
//
'use strict';
const cint = require('cint');
const path = require('path');
const findUp = require('find-up');
const _ = require('lodash');
const getstdin = require('get-stdin');
const Table = require('cli-table');
const chalk = require('chalk');
const {promisify} = require('util');
const fs = require('fs');
const vm = require('./versionmanager');
const packageManagers = require('./package-managers');
const versionUtil = require('./version-util');
const jph = require('json-parse-helpfulerror');
// maps package managers to package file names
const packageFileNames = {
npm: 'package.json',
bower: 'bower.json'
};
// maps string levels to numeric levels
const logLevels = {
silent: 0,
error: 1,
minimal: 2,
warn: 3,
info: 4,
verbose: 5,
silly: 6
};
// time to wait for stdin before printing a warning
const stdinWarningTime = 5000;
const stdinWarningMessage = `Hmmmmm... this is taking a long time. Your console is telling me to wait for input \non stdin, but maybe that is not what you want.\nTry ${chalk.cyan('winpty ncu.cmd')}, or specify a package file explicitly with ${chalk.cyan('--packageFile package.json')}. \nSee https://github.com/tjunnone/npm-check-updates/issues/136#issuecomment-155721102`;
//
// Helper functions
//
function print(options, message, loglevel, method = 'log') {
// not in json mode
// not silent
// not at a loglevel under minimum specified
if (!options.json && options.loglevel !== 'silent' && (loglevel == null || logLevels[options.loglevel] >= logLevels[loglevel])) {
console[method](message);
}
}
function programError(options, message) {
if (options.cli) {
print(options, message, null, 'error');
process.exit(1);
} else {
throw new Error(message);
}
}
function printJson(options, object) {
if (options.loglevel !== 'silent') {
console.log(JSON.stringify(object, null, 2));
}
}
/**
* Gets the name of the package file based on --packageFile or --packageManager.
*/
function getPackageFileName(options) {
return options.packageFile ? options.packageFile :
packageFileNames[options.packageManager];
}
function createDependencyTable() {
return new Table({
colAligns: ['left', 'right', 'right', 'right'],
chars: {
top: '',
'top-mid': '',
'top-left': '',
'top-right': '',
bottom: '',
'bottom-mid': '',
'bottom-left': '',
'bottom-right': '',
left: '',
'left-mid': '',
mid: '',
'mid-mid': '',
right: '',
'right-mid': '',
middle: ''
}
});
}
/**
* @param args.from
* @param args.to
*/
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] || '');
return [dep, from, '→', to];
});
rows.forEach(row => table.push(row));
return table;
}
const readPackageFile = cint.partialAt(promisify(fs.readFile), 1, 'utf8');
const writePackageFile = promisify(fs.writeFile);
//
// Main functions
//
function analyzeGlobalPackages(options) {
print(options, 'Getting installed packages...', 'verbose');
return vm.getInstalledPackages({
cwd: options.cwd,
filter: options.filter,
global: options.global,
packageManager: options.packageManager,
prefix: options.prefix,
reject: options.reject
})
.then(globalPackages => {
print(options, 'globalPackages', 'silly');
print(options, globalPackages, 'silly');
print(options, '', 'silly');
print(options, `Fetching ${vm.getVersionTarget(options)} versions...`, 'verbose');
return vm.upgradePackageDefinitions(globalPackages, options)
.then(([upgraded, latest]) => {
print(options, latest, 'silly');
const upgradedPackageNames = Object.keys(upgraded);
const upgradePromise = printUpgrades(options, {
current: globalPackages,
upgraded,
latest,
// since an interactive upgrade of globals is not available, the numUpgraded is always all
numUpgraded: upgradedPackageNames.length,
total: upgradedPackageNames.length
});
let instruction = '[package]';
if (upgraded) {
instruction = upgradedPackageNames.map(pkg => pkg + '@' + upgraded[pkg]).join(' ');
}
if (options.json) {
// since global packages do not have a package.json, return the upgraded deps directly (no version range replacements)
printJson(options, upgraded);
} else if (instruction.length) {
print(options, '\n' + chalk.cyan('ncu') + ' itself cannot upgrade global packages. Run the following to upgrade all global packages: \n\n' + chalk.cyan('npm -g install ' + instruction) + '\n');
}
return upgradePromise;
});
});
}
function analyzeProjectDependencies(options, pkgData, pkgFile) {
let pkg;
try {
if (!pkgData) {
throw new Error('pkgData: ' + pkgData);
} else {
pkg = jph.parse(pkgData);
}
} catch (e) {
programError(options, chalk.red(`Invalid package file${pkgFile ? `: ${pkgFile}` : ' from stdin'}. Error details:\n${e.message}`));
}
const current = vm.getCurrentDependencies(pkg, options);
print(options, `Fetching ${vm.getVersionTarget(options)} versions...`, 'verbose');
return vm.upgradePackageDefinitions(current, options).then(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;
// split the deps into satisfied and unsatisfied to display in two separate tables
const deps = Object.keys(selectedNewDependencies);
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(selectedNewDependencies, cint.not(isSatisfied)) : selectedNewDependencies;
const numUpgraded = Object.keys(filteredUpgraded).length;
// 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: filteredUpgraded,
latest,
numUpgraded,
total: Object.keys(upgraded).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;
});
}
/**
* @param {Object} options - Options from the configuration
* @param {Object} args - The arguments passed to the function.
* @param {Object} args.current - The current packages.
* @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
*/
function printUpgrades(options, {current, upgraded, numUpgraded, total}) {
print(options, '');
// print everything is up-to-date
const smiley = chalk.green.bold(':)');
if (numUpgraded === 0 && total === 0) {
if (Object.keys(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}`);
}
} else if (numUpgraded === 0 && total > 0) {
print(options, `All dependencies match the desired package versions ${smiley}`);
}
// print table
if (numUpgraded > 0) {
const table = toDependencyTable({
from: current,
to: upgraded
});
print(options, table.toString());
}
}
//
// Program
//
/** Initializes and consolidates options from the cli. */
function initOptions(options) {
return _.assign({}, options, {
filter: options.args.join(' ') || options.filter,
// convert silent option to loglevel silent
loglevel: options.silent ? 'silent' : options.loglevel,
minimal: options.minimal === undefined ? false : options.minimal,
// default to 0, except when newest or greatest are set
pre: options.pre ? Boolean(Number(options.pre)) : options.newest || options.greatest,
// add shortcut for any keys that start with 'json'
json: _(options)
.keys()
.filter(_.partial(_.startsWith, _, 'json', 0))
.some(_.propertyOf(options))
});
}
/** Finds the package file and data.
@returns Promise [pkgFile, pkgData]
Searches as follows:
--packageData flag
--packageFile flag
--stdin
--findUp
*/
async function findPackage(options) {
let pkgData;
let pkgFile;
let stdinTimer;
print(options, 'Running in local mode...', 'verbose');
print(options, 'Finding package file data...', 'verbose');
const pkgFileName = getPackageFileName(options);
// returns: string
function getPackageDataFromFile(pkgFile, pkgFileName) {
// exit if no pkgFile to read from fs
if (pkgFile != null) {
// print a message if we are using a descendant package file
const relPathToPackage = path.resolve(pkgFile);
if (relPathToPackage !== pkgFileName) {
print(options, `${options.upgrade ? 'Upgrading' : 'Checking'} ${relPathToPackage}`);
}
} else {
programError(options, `${chalk.red(`No ${pkgFileName}`)}\n\nPlease add a ${pkgFileName} to the current directory, specify the ${chalk.cyan('--packageFile')} or ${chalk.cyan('--packageData')} options, or pipe a ${pkgFileName} to stdin.`);
}
return readPackageFile(pkgFile);
}
// get the package data from the various input possibilities
if (options.packageData) {
pkgFile = null;
pkgData = Promise.resolve(options.packageData);
} else if (options.packageFile) {
pkgFile = options.packageFile;
pkgData = getPackageDataFromFile(pkgFile, pkgFileName);
} else if (!process.stdin.isTTY) {
print(options, 'Waiting for package data on stdin...', 'verbose');
// warn the user after a while if still waiting for stdin
// this is a way to mitigate #136 where Windows unexpectedly waits for stdin
stdinTimer = setTimeout(() => {
console.log(stdinWarningMessage);
}, stdinWarningTime);
// get data from stdin
// trim stdin to account for \r\n
// clear the warning timer once stdin returns
const stdinData = await getstdin();
const data = stdinData.trim().length > 0 ? stdinData : null;
clearTimeout(stdinTimer);
// if no stdin content fall back to searching for package.json from pwd and up to root
pkgFile = data || !pkgFileName ? null : findUp.sync(pkgFileName);
pkgData = data || getPackageDataFromFile(await pkgFile, pkgFileName);
} else {
// find the closest package starting from the current working directory and going up to the root
pkgFile = pkgFileName ? findUp.sync(pkgFileName) : null;
pkgData = getPackageDataFromFile(pkgFile, pkgFileName);
}
return Promise.all([pkgData, pkgFile]);
}
/** main entry point */
async function run(options={}) {
// exit with non-zero error code when there is an unhandled promise rejection
process.on('unhandledRejection', err => {
throw err;
});
// if not executed on the command-line (i.e. executed as a node module), set some defaults
if (!options.cli) {
options = _.defaults({}, options, {
jsonUpgraded: true,
loglevel: 'silent',
packageManager: 'npm',
args: []
});
}
options = initOptions(options);
print(options, 'Initializing...', 'verbose');
if (options.packageManager === 'npm' && !options.prefix) {
options.prefix = await packageManagers.npm.defaultPrefix(options); // eslint-disable-line require-atomic-updates
}
let timeout;
let timeoutPromise = new Promise(r => r);
if (options.timeout) {
const timeoutMs = _.isString(options.timeout) ? parseInt(options.timeout, 10) : options.timeout;
timeoutPromise = new Promise((resolve, reject) => {
timeout = setTimeout(() => {
// must catch the error and reject explicitly since we are in a setTimeout
const error = `Exceeded global timeout of ${timeoutMs}ms`;
reject(error);
try {
programError(options, chalk.red(error));
} catch (e) {/* noop */}
}, timeoutMs);
});
}
async function getAnalysis() {
if (options.global) {
const analysis = await analyzeGlobalPackages(options);
clearTimeout(timeout);
return analysis;
} else {
const [pkgData, pkgFile] = await findPackage(options);
const analysis = await analyzeProjectDependencies(options, pkgData, pkgFile);
clearTimeout(timeout);
return analysis;
}
}
return await Promise.race([timeoutPromise, getAnalysis()]);
}
module.exports = _.assign({
run
}, vm);

View file

@ -0,0 +1,14 @@
To add support for another package manager, drop in a module with the following interface:
```
{
list: (npmOptions) => Promise<{ NAME: VERSION, ... }>
latest: (String pkgName) => Promise<String> version
newest: (String pkgName) => Promise<String> version
greatest: (String pkgName) => Promise<String> version
greatestMajor: (String pkgName, String currentVersion) => Promise<String> version
greatestMinor: (String pkgName, String currentVersion) => Promise<String> version
}
```
* latest and greatest are expected to reject with `'404 Not Found'` if the package is not found

View file

@ -0,0 +1,76 @@
'use strict';
const cint = require('cint');
const chalk = require('chalk');
const requireg = require('requireg');
/**
* @param args.global
* @param args.registry
* @param args.loglevel
*/
// see if the bower dependency has been installed
const bower = ({loglevel}) => {
try {
requireg.resolve('bower'); // throws an error if not installed
return requireg('bower');
} catch (e) {
if (loglevel !== 'silent') {
console.error(`Bower not installed. Please install bower using: ${chalk.cyan('npm install -g bower')}`);
}
process.exit(1);
}
};
module.exports = {
list({prefix, loglevel} = {}) {
return new Promise((resolve, reject) => {
bower({loglevel}).commands.list(null, {cwd: prefix})
.on('end', results => {
resolve(cint.mapObject(results.dependencies, (key, value) => {
return cint.keyValue(key, value.pkgMeta);
}));
})
.on('error', reject);
});
},
latest(packageName, _, {prefix, loglevel} = {}) {
return new Promise((resolve, reject) => {
bower({loglevel}).commands.info(packageName, null, {cwd: prefix})
.on('end', results => {
resolve(results.latest.version);
})
.on('error', err => {
// normalize 404
reject(/Package \S* not found|Repository not found/.test(err.message) ? '404 Not Found' : err);
});
});
},
greatest(packageName, _, {prefix, loglevel} = {}) {
return new Promise((resolve, reject) => {
bower({loglevel}).commands.info(packageName, null, {cwd: prefix})
.on('end', results => {
resolve(results.versions[0]); // bower versions returned in highest-to-lowest order.
})
.on('error', reject);
});
},
newest() {
throw new Error('Semantic versioning level "newest" is not supported for Bower');
},
greatestMajor() {
throw new Error('Semantic versioning level "major" is not supported for Bower');
},
greatestMinor() {
throw new Error('Semantic versioning level "minor" is not supported for Bower');
}
};

View file

@ -0,0 +1,5 @@
'use strict';
module.exports = {
bower: require('./bower'),
npm: require('./npm')
};

View file

@ -0,0 +1,188 @@
'use strict';
const _ = require('lodash');
const cint = require('cint');
const semver = require('semver');
const versionUtil = require('../version-util.js');
const spawn = require('spawn-please');
const pacote = require('pacote');
// needed until pacote supports full npm config compatibility
// See: https://github.com/zkat/pacote/issues/156
const npmConfig = {};
require('libnpmconfig').read().forEach((value, key) => {
// replace env ${VARS} in strings with the process.env value
npmConfig[key] = typeof value !== 'string' ?
value :
value.replace(/\${([^}]+)}/, (_, envVar) =>
process.env[envVar]
);
});
npmConfig.cache = false;
/** Parse JSON and throw an informative error on failure.
* @param result Data to be parsed
* @param data { command, packageName }
*/
function parseJson(result, data) {
let json;
// use a try-catch instead of .catch to avoid re-catching upstream errors
try {
json = JSON.parse(result);
} catch (err) {
throw new Error(`Expected JSON from "${data.command}". This could be due to npm instability${data.packageName ? ` or problems with the ${data.packageName} package` : ''}.\n\n${result}`);
}
return json;
}
/**
* @param packageName Name of the package
* @param field Field such as "versions" or "dist-tags.latest" are parsed from the pacote result (https://www.npmjs.com/package/pacote#packument)
* @Returns Promised result
*/
function view(packageName, field, currentVersion) {
if (currentVersion && (!semver.validRange(currentVersion) || versionUtil.isWildCard(currentVersion))) {
return Promise.resolve();
}
npmConfig['full-metadata'] = field === 'time';
return pacote.packument(packageName, npmConfig).then(result => {
if (field.startsWith('dist-tags.')) {
const split = field.split('.');
if (result[split[0]]) {
return result[split[0]][split[1]];
}
} else if (field === 'versions') {
return Object.keys(result[field]);
} else {
return result[field];
}
});
}
/**
* @param versions Array of all available versions
* @Returns An array of versions with the release versions filtered out
*/
function filterOutPrereleaseVersions(versions) {
return _.filter(versions, _.negate(isPre));
}
/**
* @param version
* @Returns True if the version is any kind of prerelease: alpha, beta, rc, pre
*/
function isPre(version) {
return versionUtil.getPrecision(version) === 'release';
}
/** Spawn npm requires a different command on Windows. */
function spawnNpm(args, npmOptions={}, spawnOptions={}) {
const cmd = process.platform === 'win32'? 'npm.cmd' : 'npm';
const fullArgs = [].concat(
args,
npmOptions.global ? '--global' : [],
npmOptions.prefix ? `--prefix=${npmOptions.prefix}` : [],
'--depth=0',
'--json'
);
return spawn(cmd, fullArgs, spawnOptions);
}
/** Get platform-specific default prefix to pass on to npm.
* @param options.global
* @param options.prefix
*/
function defaultPrefix(options) {
if (options && options.prefix) {
return Promise.resolve(options.prefix);
}
const cmd = process.platform === 'win32'? 'npm.cmd' : 'npm';
return spawn(cmd, ['config', 'get', 'prefix']).then(prefix => {
// FIX: for ncu -g doesn't work on homebrew or windows #146
// https://github.com/tjunnone/npm-check-updates/issues/146
return options.global && prefix.match('Cellar') ? '/usr/local' :
// Workaround: get prefix on windows for global packages
// Only needed when using npm api directly
process.platform === 'win32' && options.global && !process.env.prefix ?
`${process.env.AppData}\\npm` :
null;
});
}
module.exports = {
/**
* @options.cwd (optional)
* @options.global (optional)
* @options.prefix (optional)
*/
list(options={}) {
return spawnNpm('ls', options, options.cwd ? {cwd: options.cwd, rejectOnError: false} : {rejectOnError: false})
.then(result => {
const json = parseJson(result, {
command: 'npm ls'
});
return cint.mapObject(json.dependencies, (name, info) =>
// unmet peer dependencies have a different structure
cint.keyValue(name, info.version || (info.required && info.required.version))
);
});
},
latest(packageName, currentVersion, pre) {
return view(packageName, 'dist-tags.latest', currentVersion)
.then(version => {
// if latest is not a prerelease version, return it
// if latest is a prerelease version and --pre is specified, return it
if (!isPre(version) || pre) {
return version;
// if latest is a prerelease version and --pre is not specified, find the next
// version that is not a prerelease
} else {
return view(packageName, 'versions', currentVersion)
.then(filterOutPrereleaseVersions)
.then(_.last);
}
});
},
newest(packageName, currentVersion, pre) {
return view(packageName, 'time', currentVersion)
.then(_.keys)
.then(_.partialRight(_.pullAll, ['modified', 'created']))
.then(versions => {
return _.last(pre ? versions : filterOutPrereleaseVersions(versions));
});
},
greatest(packageName, currentVersion, pre) {
return view(packageName, 'versions', currentVersion)
.then(versions => {
return _.last(pre ? versions : filterOutPrereleaseVersions(versions));
});
},
greatestMajor(packageName, currentVersion, pre) {
return view(packageName, 'versions', currentVersion).then(versions => {
const resultVersions = pre ? versions : filterOutPrereleaseVersions(versions);
return versionUtil.findGreatestByLevel(resultVersions, currentVersion, 'major');
});
},
greatestMinor(packageName, currentVersion, pre) {
return view(packageName, 'versions', currentVersion).then(versions => {
const resultVersions = pre ? versions : filterOutPrereleaseVersions(versions);
return versionUtil.findGreatestByLevel(resultVersions, currentVersion, 'minor');
});
},
defaultPrefix
};

28
node_modules/npm-check-updates/lib/raw-promisify.js generated vendored Normal file
View file

@ -0,0 +1,28 @@
'use strict';
const _ = require('lodash');
/**
* For some reason, Promise.promisifyAll does not work on npm.commands :(
* Promise.promisifyAll(npm.commands);
* So we have to do it manually.
*/
function rawPromisify(obj) {
_.each(obj, (method, name) => {
obj[`${name}Async`] = () => {
const args = [].slice.call(arguments);
const that = this;
return new Promise((resolve, reject) => {
args.push((err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
return method.apply(that, args);
});
};
});
}
module.exports = rawPromisify;

199
node_modules/npm-check-updates/lib/version-util.js generated vendored Normal file
View file

@ -0,0 +1,199 @@
'use strict';
const semverutils = require('semver-utils');
const _ = require('lodash');
const chalk = require('chalk');
const util = require('util');
const VERSION_BASE_PARTS = ['major', 'minor', 'patch'];
const VERSION_ADDED_PARTS = ['release', 'build'];
const VERSION_PARTS = [].concat(VERSION_BASE_PARTS, VERSION_ADDED_PARTS);
const VERSION_PART_DELIM = {
major: '',
minor: '.',
patch: '.',
release: '-',
build: '+'
};
const WILDCARDS = ['^', '~', '.*', '.x'];
const WILDCARDS_PURE = ['^', '~', '^*', '*', 'x', 'x.x', 'x.x.x'];
const WILDCARD_PURE_REGEX = new RegExp(`^(${WILDCARDS_PURE.join('|')
.replace(/\^/g, '\\^')
.replace(/\*/g, '\\*')})$`);
const SEMANTIC_DIRECT = new RegExp('^\\d+\\.\\d+\\.\\d+([-|+].*)*$');
/**
* Returns the number of parts in the version
*/
function numParts(version) {
const semver = semverutils.parseRange(version)[0];
if (!semver) {
throw new Error(util.format('semverutils.parseRange returned null when trying to parse "%s". This is probably a problem with the "semver-utils" dependency. Please report an issue at https://github.com/tjunnone/npm-check-updates/issues.', version));
}
return _.intersection(VERSION_PARTS, Object.keys(semver)).length;
}
/**
* Increases or decreases the given precision by the given amount, e.g. major+1 -> minor
*/
function precisionAdd(precision, n) {
if (n === 0) {
return precision;
}
const index = n === 0 ? precision :
_.includes(VERSION_BASE_PARTS, precision) ? VERSION_BASE_PARTS.indexOf(precision) + n :
_.includes(VERSION_ADDED_PARTS, precision) ? VERSION_BASE_PARTS.length + n :
null;
if (index === null) {
throw new Error(`Invalid precision: ${precision}`);
} else if (!VERSION_PARTS[index]) {
throw new Error(`Invalid precision math${arguments}`);
}
return VERSION_PARTS[index];
}
/** Joins the major, minor, patch, release, and build parts (controlled by an optional precision arg) of a semver object
* into a dot-delimited string. */
function stringify(semver, precision) {
// get a list of the parts up until (and including) the given precision
// or all of them, if no precision is specified
const parts = precision ? VERSION_PARTS.slice(0, VERSION_PARTS.indexOf(precision)+1) : VERSION_PARTS;
// pair each part with its delimiter and join together
return parts
.filter(part => {
return _.includes(VERSION_BASE_PARTS, precision) || semver[part];
})
.map(part => {
return VERSION_PART_DELIM[part] + (semver[part] || '0');
})
.join('');
}
/**
* Gets how precise this version number is (major, minor, patch, release, or build)
*/
function getPrecision(version) {
const semver = semverutils.parseRange(version)[0];
// expects VERSION_PARTS to be in correct order
return _.find(VERSION_PARTS.slice().reverse(), _.propertyOf(semver));
}
/**
* Sets the precision of a (loose) semver to the specified level: major, minor, etc.
*/
function setPrecision(version, precision) {
const semver = semverutils.parseRange(version)[0];
return stringify(semver, precision);
}
/** Adds a given wildcard (^,~,.*,.x) to a version number. Adds ^ and ~ to the beginning. Replaces everything after the
* major version number with .* or .x */
function addWildCard(version, wildcard) {
return wildcard === '^' || wildcard === '~' ?
wildcard + version :
setPrecision(version, 'major') + wildcard;
}
/** Returns true if the given string is one of the wild cards. */
function isWildCard(version) {
return WILDCARD_PURE_REGEX.test(version);
}
/** Returns true if the given digit is a wildcard for a part of a version. */
function isWildPart(versionPart) {
return versionPart === '*' || versionPart === 'x';
}
/**
* Colorize the parts of a version string (to) that are different than another (from). Assumes that the two verson strings are in the same format.
*/
function colorizeDiff(from, to) {
let leadingWildcard = '';
// separate out leading ^ or ~
if (/^[~^]/.test(to) && to[0] === from[0]) {
leadingWildcard = to[0];
to = to.slice(1);
from = from.slice(1);
}
// split into parts
const partsToColor = to.split('.');
const partsToCompare = from.split('.');
let i = _.findIndex(partsToColor, (part, i) => part !== partsToCompare[i]);
i = i >= 0 ? i : partsToColor.length;
// major = red (or any change before 1.0.0)
// minor = cyan
// patch = green
const color = i === 0 || partsToColor[0] === '0' ? 'red' :
i === 1 ? 'cyan' :
'green';
// if we are colorizing only part of the word, add a dot in the middle
const middot = i > 0 && i < partsToColor.length ? '.' : '';
return leadingWildcard +
partsToColor.slice(0,i).join('.') +
middot +
chalk[color](partsToColor.slice(i).join('.'));
}
/**
* @param versions Array of all available versions
* @param current Current version
* @param level major/minor
* @Returns String representation of the suggested version. If the current version
* is not direct then returns null
*/
function findGreatestByLevel(versions, current, level) {
if (!SEMANTIC_DIRECT.test(current)) {
return null;
}
const cur = semverutils.parse(current);
return _.chain(versions)
.map(v => {
return {
string: v,
parsed: semverutils.parse(v)
};
})
.filter(o => {
if (level === 'minor' && o.parsed.major !== cur.major) {
return false;
}
return o.parsed[level] === cur[level];
})
.map(o => {
return o.string;
})
.last()
.value();
}
module.exports = {
numParts,
stringify,
precisionAdd,
getPrecision,
setPrecision,
addWildCard,
isWildCard,
isWildPart,
colorizeDiff,
findGreatestByLevel,
VERSION_BASE_PARTS,
VERSION_ADDED_PARTS,
VERSION_PARTS,
WILDCARDS
};

496
node_modules/npm-check-updates/lib/versionmanager.js generated vendored Normal file
View file

@ -0,0 +1,496 @@
'use strict';
const semver = require('semver');
const _ = require('lodash');
const cint = require('cint');
const semverutils = require('semver-utils');
const ProgressBar = require('progress');
const versionUtil = require('./version-util.js');
const packageManagers = require('./package-managers');
const prompts = require('prompts');
// keep order for setPrecision
const DEFAULT_WILDCARD = '^';
/** Returns 'v' if the string starts with a v, otherwise returns empty string. */
function v(str) {
return str && (str[0] === 'v' || str[1] === 'v') ? 'v' : '';
}
/** Returns a new function that AND's the two functions over the provided arguments. */
function and(f, g) {
return function () {
return f.apply(this, arguments) && g.apply(this, arguments);
};
}
/**
* Upgrade an existing dependency declaration to satisfy the latest version
* @param declaration Current version declaration (e.g. "1.2.x")
* @param latestVersion Latest version (e.g "1.3.2")
* @param {Object} [options={}]
* @returns {string} The upgraded dependency declaration (e.g. "1.3.x")
*/
function upgradeDependencyDeclaration(declaration, latestVersion, options = {}) {
options.wildcard = options.wildcard || DEFAULT_WILDCARD;
// parse the latestVersion
// return original declaration if latestSemver is invalid
const latestSemver = semverutils.parseRange(latestVersion)[0];
if (!latestSemver) {
return declaration;
}
// return global versionUtil.wildcards immediately
if (options.removeRange) {
return latestVersion;
} else if (versionUtil.isWildCard(declaration)) {
return declaration;
}
// parse the declaration
// if multiple ranges, use the semver with the least number of parts
const parsedRange = _(semverutils.parseRange(declaration))
// semver-utils includes empty entries for the || and - operators. We can remove them completely
.reject({operator: '||'})
.reject({operator: '-'})
.sortBy(_.ary(_.flow(versionUtil.stringify, versionUtil.numParts), 1))
.value();
const declaredSemver = parsedRange[0];
/**
* Chooses version parts between the declared version and the latest.
* Base parts (major, minor, patch) are only included if they are in the original declaration.
* Added parts (release, build) are always included. They are only present if we are checking --greatest versions
* anyway.
*/
function chooseVersion(part) {
return versionUtil.isWildPart(declaredSemver[part]) ? declaredSemver[part] :
_.includes(versionUtil.VERSION_BASE_PARTS, part) && declaredSemver[part] ? latestSemver[part] :
_.includes(versionUtil.VERSION_ADDED_PARTS, part) ? latestSemver[part] :
undefined;
}
// create a new semver object with major, minor, patch, build, and release parts
const newSemver = cint.toObject(versionUtil.VERSION_PARTS, part =>
cint.keyValue(part, chooseVersion(part))
);
const newSemverString = versionUtil.stringify(newSemver);
const version = v(declaredSemver.semver) + newSemverString;
// determine the operator
// do not compact, because [undefined, '<'] must be differentiated from ['<']
const uniqueOperators = _(parsedRange)
.map(range => range.operator)
.uniq()
.value();
const operator = uniqueOperators[0] || '';
const hasWildCard = versionUtil.WILDCARDS.some(_.partial(_.includes, newSemverString, _, 0));
const isLessThan = uniqueOperators[0] === '<' || uniqueOperators[0] === '<=';
const isMixed = uniqueOperators.length > 1;
// convert versions with </<= or mixed operators into the preferred wildcard
// only do so if the new version does not already contain a wildcard
return !hasWildCard && (isLessThan || isMixed) ?
versionUtil.addWildCard(version, options.wildcard) :
operator + version;
}
/**
* Upgrade a dependencies collection based on latest available versions
* @param currentDependencies current dependencies collection object
* @param latestVersions latest available versions collection object
* @param {Object} [options={}]
* @returns {{}} upgraded dependency collection object
*/
function upgradeDependencies(currentDependencies, latestVersions, options = {}) {
// filter out dependencies with empty values
currentDependencies = cint.filterObject(currentDependencies, (key, value) => {
return value;
});
// get the preferred wildcard and bind it to upgradeDependencyDeclaration
const wildcard = getPreferredWildcard(currentDependencies) || DEFAULT_WILDCARD;
const upgradeDep = _.partialRight(upgradeDependencyDeclaration, {
wildcard,
removeRange: options.removeRange
});
return _(currentDependencies)
// only include packages for which a latest version was fetched
.pickBy((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((current, packageName) => {
const 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();
}
// Determines if the given version (range) should be upgraded to the latest (i.e. it is valid, it does not currently
// Return true if the version satisfies the range
const isSatisfied = semver.satisfies;
// satisfy the latest, and it is not beyond the latest)
function isUpgradeable(current, latest) {
// do not upgrade non-npm version declarations (such as git tags)
// do not upgrade versionUtil.wildcards
if (!semver.validRange(current) || versionUtil.isWildCard(current)) {
return false;
}
// remove the constraint (e.g. ^1.0.1 -> 1.0.1) to allow upgrades that satisfy the range, but are out of date
const range = semverutils.parseRange(current)[0];
if (!range) {
throw new Error(`"${current}" could not be parsed by semver-utils. This is probably a bug. Please file an issue at https://github.com/tjunnone/npm-check-updates.`);
}
const version = versionUtil.stringify(range);
// make sure it is a valid range
// not upgradeable if the latest version satisfies the current range
// not upgradeable if the specified version is newer than the latest (indicating a prerelease version)
return Boolean(semver.validRange(version)) &&
!isSatisfied(latest, version) &&
!semver.ltr(latest, version);
}
/**
* Creates a filter function from a given filter string. Supports strings, comma-or-space-delimited lists, and regexes.
*/
function packageNameFilter(filter) {
let filterPackages;
// no filter
if (!filter) {
filterPackages = _.identity;
} else if (typeof filter === 'string') {
// RegExp filter
if (filter[0] === '/' && cint.index(filter,-1) === '/') {
const regexp = new RegExp(filter.slice(1, filter.length-1));
filterPackages = regexp.test.bind(regexp);
} else {
// string filter
const packages = filter.split(/[\s,]+/);
filterPackages = _.includes.bind(_, packages);
}
} else if (Array.isArray(filter)) {
// array filter
filterPackages = _.includes.bind(_, filter);
} else if (filter instanceof RegExp) {
// raw RegExp
filterPackages = filter.test.bind(filter);
} else {
throw new Error('Invalid packages filter. Must be a RegExp, array, or comma-or-space-delimited list.');
}
// (limit the arity to 1 to avoid passing the value)
return cint.aritize(filterPackages, 1);
}
/** Creates a single filter function from an optional filter and optional reject. */
function filterAndReject(filter, reject) {
return and(
filter ? packageNameFilter(filter) : _.identity,
reject ? _.negate(packageNameFilter(reject)) : _.identity
);
}
/** Returns an 2-tuple of upgradedDependencies and their latest versions */
function upgradePackageDefinitions(currentDependencies, options) {
const versionTarget = getVersionTarget(options);
return queryVersions(currentDependencies, {
versionTarget,
registry: options.registry ? options.registry : null,
pre: options.pre,
packageManager: options.packageManager,
json: options.json,
loglevel: options.loglevel
}).then(latestVersions => {
const upgradedDependencies = upgradeDependencies(currentDependencies, latestVersions, {
removeRange: options.removeRange
});
const filteredUpgradedDependencies = _.pickBy(upgradedDependencies, (v, dep) => {
return !options.jsonUpgraded || !options.minimal || !isSatisfied(latestVersions[dep], currentDependencies[dep]);
});
return [filteredUpgradedDependencies, latestVersions];
});
}
/**
* Upgrade the dependency declarations in the package data
* @param pkgData The package.json data, as utf8 text
* @param oldDependencies Object of old dependencies {package: range}
* @param newDependencies Object of new dependencies {package: range}
* @param newVersions Object of new versions {package: version}
* @param {Object} [options={}]
* @returns {string} The updated package data, as utf8 text
* @sideeffect prompts
*/
async function upgradePackageData(pkgData, oldDependencies, newDependencies, newVersions, options = {}) {
// copy newDependencies for mutation via interactive mode
const selectedNewDependencies = Object.assign({}, newDependencies);
let newPkgData = pkgData;
for (const dependency in newDependencies) {
if (!options.minimal || !isSatisfied(newVersions[dependency], oldDependencies[dependency])) {
if (options.interactive) {
const to = versionUtil.colorizeDiff(oldDependencies[dependency], newDependencies[dependency] || '');
const response = await prompts({
type: 'confirm',
name: 'value',
message: `Do you want to upgrade: ${dependency} ${oldDependencies[dependency]}${to}?`,
initial: true
});
if (!response.value) {
// continue loop to next dependency and skip updating newPkgData
delete selectedNewDependencies[dependency];
continue;
}
}
const expression = `"${dependency}"\\s*:\\s*"${escapeRegexp(`${oldDependencies[dependency]}"`)}`;
const regExp = new RegExp(expression, 'g');
newPkgData = newPkgData.replace(regExp, `"${dependency}": "${newDependencies[dependency]}"`);
}
}
return {newPkgData, selectedNewDependencies};
}
/**
* Get the current dependencies from the package file
* @param {Object} [pkgData={}] Object with dependencies, devDependencies, peerDependencies, optionalDependencies, and/or bundleDependencies properties
* @param {Object} [options={}]
* @param options.dep
* @param options.filter
* @param options.reject
* @returns Promised {packageName: version} collection
*/
function getCurrentDependencies(pkgData = {}, options = {}) {
if (options.dep) {
const deps = (options.dep || '').split(',');
options.prod = _.includes(deps, 'prod');
options.dev = _.includes(deps, 'dev');
options.peer = _.includes(deps, 'peer');
options.optional = _.includes(deps, 'optional');
options.bundle = _.includes(deps, 'bundle');
} else {
options.prod = options.dev = options.peer = options.optional = options.bundle = true;
}
const allDependencies = cint.filterObject(_.merge({},
options.prod && pkgData.dependencies,
options.dev && pkgData.devDependencies,
options.peer && pkgData.peerDependencies,
options.optional && pkgData.optionalDependencies,
options.bundle && pkgData.bundleDependencies
), filterAndReject(options.filter, options.reject));
return allDependencies;
}
/**
* @options.cwd
* @options.filter
* @options.global
* @options.packageManager
* @options.prefix
* @options.reject
*/
function getInstalledPackages(options = {}) {
return getPackageManager(options.packageManager).list({cwd: options.cwd, prefix: options.prefix, global: options.global}).then(pkgInfoObj => {
if (!pkgInfoObj) {
throw new Error('Unable to retrieve NPM package list');
}
// filter out undefined packages or those with a wildcard
const filterFunction = filterAndReject(options.filter, options.reject);
return cint.filterObject(pkgInfoObj, (dep, version) =>
version && !versionUtil.isWildPart(version) && filterFunction(dep)
);
});
}
/**
* Get the latest or greatest versions from the NPM repository based on the version target
* @param packageMap an object whose keys are package name and values are current versions
* @param {Object} [options={}] Options. Default: { versionTarget: 'latest' }. You may also specify { versionTarget: 'greatest' }
* @returns Promised {packageName: version} collection
*/
function queryVersions(packageMap, options = {}) {
let getPackageVersion;
const packageList = Object.keys(packageMap);
const packageManager = getPackageManager(options.packageManager);
// validate options.versionTarget
options.versionTarget = options.versionTarget || 'latest';
let bar;
if (!options.json && options.loglevel !== 'silent' && packageList.length > 0) {
bar = new ProgressBar('[:bar] :current/:total :percent', {total: packageList.length, width: 20});
bar.render();
}
// determine the getPackageVersion function from options.versionTarget
switch (options.versionTarget) {
case 'latest': {
getPackageVersion = packageManager.latest;
break;
}
case 'greatest': {
getPackageVersion = packageManager.greatest;
break;
}
case 'newest': {
getPackageVersion = packageManager.newest;
break;
}
case 'major': {
getPackageVersion = packageManager.greatestMajor;
break;
}
case 'minor': {
getPackageVersion = packageManager.greatestMinor;
break;
}
default: {
const supportedVersionTargets = ['latest', 'newest', 'greatest', 'major', 'minor'];
return Promise.reject(new Error(`Unsupported versionTarget: ${options.versionTarget}. Supported version targets are: ${supportedVersionTargets.join(', ')}`));
}
}
// ignore 404 errors from getPackageVersion by having them return null instead of rejecting
function getPackageVersionProtected(dep) {
return getPackageVersion(dep, packageMap[dep], options.pre).catch(err => {
if (err && (err.message || err).toString().match(/E404|ENOTFOUND|404 Not Found/i)) {
return null;
} else {
throw err;
}
}).then(result => {
if (bar) {
bar.tick();
}
return result;
});
}
// zip up the array of versions into to a nicer object keyed by package name
function zipVersions(versionList) {
return cint.toObject(versionList, (version, i) => {
return cint.keyValue(packageList[i], version);
});
}
return Promise.all(packageList.map(getPackageVersionProtected))
.then(zipVersions)
.then(_.partialRight(_.pickBy, _.identity));
}
/**
* Given a dependencies collection, returns whether the user prefers ^, ~, .*, or .x (simply counts the greatest number
* of occurrences). Returns null if given no dependencies.
*/
function getPreferredWildcard(dependencies) {
// if there are no dependencies, return null.
if (Object.keys(dependencies).length === 0) {
return null;
}
// group the dependencies by wildcard
const groups = _.groupBy(_.values(dependencies), dep =>
_.find(versionUtil.WILDCARDS, wildcard =>
dep && dep.indexOf(wildcard) > -1
)
);
delete groups.undefined;
// convert to an array of objects that can be sorted
const arrOfGroups = cint.toArray(groups, (wildcard, instances) => ({
wildcard,
instances
}));
// reverse sort the groups so that the wildcard with the most appearances is at the head, then return it.
const sorted = _.sortBy(arrOfGroups, wildcardObject => -wildcardObject.instances.length);
return sorted.length > 0 ? sorted[0].wildcard : null;
}
function getVersionTarget(options) {
return options.semverLevel ? options.semverLevel :
options.newest ? 'newest' :
options.greatest ? 'greatest' :
'latest';
}
/**
* Initialize the version manager with the given package manager.
* @param args.global
* @param args.packageManager
*/
function getPackageManager(packageManagerNameOrObject) {
/** Get one of the preset package managers or throw an error if there is no match. */
function getPresetPackageManager(packageManagerName) {
if (!(packageManagerName in packageManagers)) {
throw new Error(`Invalid package manager: ${packageManagerName}`);
}
return packageManagers[packageManagerName];
}
return !packageManagerNameOrObject ? packageManagers.npm : // default to npm
// use present package manager if name is specified
typeof packageManagerNameOrObject === 'string' ? getPresetPackageManager(packageManagerNameOrObject) :
// use provided package manager object otherwise
packageManagerNameOrObject;
}
//
// Helper functions
//
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,
// exposed for testing
getPreferredWildcard,
isUpgradeable,
queryVersions,
upgradeDependencies,
upgradeDependencyDeclaration
};