npm-check-updates
Version:
Find newer versions of dependencies than what your package.json allows
248 lines • 11.9 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.semver = exports.patch = exports.newest = exports.minor = exports.latest = exports.greatest = exports.distTag = exports.list = exports.defaultPrefix = exports.getPathToLookForYarnrc = exports.npmAuthTokenKeyValue = void 0;
const fast_memoize_1 = __importDefault(require("fast-memoize"));
const promises_1 = __importDefault(require("fs/promises"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const jsonlines_1 = __importDefault(require("jsonlines"));
const curry_1 = __importDefault(require("lodash/curry"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const spawn_please_1 = __importDefault(require("spawn-please"));
const exists_1 = __importDefault(require("../lib/exists"));
const findLockfile_1 = __importDefault(require("../lib/findLockfile"));
const keyValueBy_1 = require("../lib/keyValueBy");
const logging_1 = require("../lib/logging");
const npm = __importStar(require("./npm"));
/** Safely interpolates a string as a template string. */
const interpolate = (s, data) => s.replace(/\$\{([^:-]+)(?:(:)?-([^}]*))?\}/g, (match, key, name, fallbackOnEmpty, fallback) => data[key] || (fallbackOnEmpty ? fallback : ''));
/** Reads an auth token from a yarn config, interpolates it, and returns it as an npm config key-value pair. */
exports.npmAuthTokenKeyValue = (0, curry_1.default)((npmConfig, dep, scopedConfig) => {
if (scopedConfig.npmAuthToken) {
// get registry server from this config or a previous config (assumes setNpmRegistry has already been called on all npm scopes)
const registryServer = scopedConfig.npmRegistryServer || npmConfig[`@${dep}:registry`];
// interpolate environment variable fallback
// https://yarnpkg.com/configuration/yarnrc
if (registryServer) {
let trimmedRegistryServer = registryServer.replace(/^https?:/, '');
if (trimmedRegistryServer.endsWith('/')) {
trimmedRegistryServer = trimmedRegistryServer.slice(0, -1);
}
return {
[`${trimmedRegistryServer}/:_authToken`]: interpolate(scopedConfig.npmAuthToken, process.env),
};
}
}
return null;
});
/** Reads a registry from a yarn config. interpolates it, and returns it as an npm config key-value pair. */
const npmRegistryKeyValue = (dep, scopedConfig) => scopedConfig.npmRegistryServer
? { [`@${dep}:registry`]: interpolate(scopedConfig.npmRegistryServer, process.env) }
: null;
/**
* Returns the path to the local .yarnrc.yml, or undefined. This doesn't
* actually check that the .yarnrc.yml file exists.
*
* Exported for test purposes only.
*
* @param readdirSync This is only a parameter so that it can be used in tests.
*/
async function getPathToLookForYarnrc(options, readdir = promises_1.default.readdir) {
var _a;
if (options.global)
return undefined;
const directoryPath = (_a = (await (0, findLockfile_1.default)(options, readdir))) === null || _a === void 0 ? void 0 : _a.directoryPath;
if (!directoryPath)
return undefined;
return path_1.default.join(directoryPath, '.yarnrc.yml');
}
exports.getPathToLookForYarnrc = getPathToLookForYarnrc;
// If private registry auth is specified in npmScopes in .yarnrc.yml, read them in and convert them to npm config variables.
// Define as a memoized function to efficiently call existsSync and readFileSync only once, and only if yarn is being used.
// https://github.com/raineorshine/npm-check-updates/issues/1036
const npmConfigFromYarn = (0, fast_memoize_1.default)(async (options) => {
const yarnrcLocalPath = await getPathToLookForYarnrc(options);
const yarnrcUserPath = path_1.default.join(os_1.default.homedir(), '.yarnrc.yml');
const yarnrcLocalExists = typeof yarnrcLocalPath === 'string' && (await (0, exists_1.default)(yarnrcLocalPath));
const yarnrcUserExists = await (0, exists_1.default)(yarnrcUserPath);
const yarnrcLocal = yarnrcLocalExists ? await promises_1.default.readFile(yarnrcLocalPath, 'utf-8') : '';
const yarnrcUser = yarnrcUserExists ? await promises_1.default.readFile(yarnrcUserPath, 'utf-8') : '';
const yarnConfigLocal = js_yaml_1.default.load(yarnrcLocal);
const yarnConfigUser = js_yaml_1.default.load(yarnrcUser);
let npmConfig = {
...(0, keyValueBy_1.keyValueBy)((yarnConfigUser === null || yarnConfigUser === void 0 ? void 0 : yarnConfigUser.npmScopes) || {}, npmRegistryKeyValue),
...(0, keyValueBy_1.keyValueBy)((yarnConfigLocal === null || yarnConfigLocal === void 0 ? void 0 : yarnConfigLocal.npmScopes) || {}, npmRegistryKeyValue),
};
// npmAuthTokenKeyValue uses scoped npmRegistryServer, so must come after npmRegistryKeyValue
npmConfig = {
...npmConfig,
...(0, keyValueBy_1.keyValueBy)((yarnConfigUser === null || yarnConfigUser === void 0 ? void 0 : yarnConfigUser.npmScopes) || {}, (0, exports.npmAuthTokenKeyValue)(npmConfig)),
...(0, keyValueBy_1.keyValueBy)((yarnConfigLocal === null || yarnConfigLocal === void 0 ? void 0 : yarnConfigLocal.npmScopes) || {}, (0, exports.npmAuthTokenKeyValue)(npmConfig)),
};
// set auth token after npm registry, since auth token syntax uses regitry
if (yarnrcLocalExists) {
(0, logging_1.print)(options, `\nUsing local yarn config at ${yarnrcLocalPath}:`, 'verbose');
(0, logging_1.print)(options, yarnConfigLocal, 'verbose');
}
if (yarnrcUserExists) {
(0, logging_1.print)(options, `\nUsing user yarn config at ${yarnrcUserPath}:`, 'verbose');
(0, logging_1.print)(options, yarnConfigLocal, 'verbose');
}
if (Object.keys(npmConfig)) {
(0, logging_1.print)(options, '\nMerged yarn config in npm format:', 'verbose');
(0, logging_1.print)(options, npmConfig, 'verbose');
}
return npmConfig;
});
/**
* Parse JSON lines and throw an informative error on failure.
*
* Note: although this is similar to the NPM parseJson() function we always return the
* same concrete-type here, for now.
*
* @param result Output from `yarn list --json` to be parsed
*/
function parseJsonLines(result) {
return new Promise((resolve, reject) => {
const dependencies = {};
const parser = jsonlines_1.default.parse();
parser.on('data', d => {
// only parse info data
// ignore error info, e.g. "Visit https://yarnpkg.com/en/docs/cli/list for documentation about this command."
if (d.type === 'info' && !d.data.match(/^Visit/)) {
// parse package name and version number from info data, e.g. "nodemon@2.0.4" has binaries
const [, pkgName, pkgVersion] = d.data.match(/"(@?.*)@(.*)"/) || [];
dependencies[pkgName] = {
version: pkgVersion,
from: pkgName,
};
}
else if (d.type === 'error') {
reject(new Error(d.data));
}
});
parser.on('end', () => {
resolve({ dependencies });
});
parser.on('error', reject);
parser.write(result);
parser.end();
});
}
/**
* Spawn yarn requires a different command on Windows.
*
* @param args
* @param [yarnOptions={}]
* @param [spawnOptions={}]
* @returns
*/
async function spawnYarn(args, yarnOptions = {}, spawnOptions) {
const cmd = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';
const fullArgs = [
...(yarnOptions.location === 'global' ? ['global'] : []),
...(yarnOptions.prefix ? [`--prefix=${yarnOptions.prefix}`] : []),
'--depth=0',
'--json',
'--no-progress',
// args must go after yarn options, otherwise they are passed through to npm scripts
// https://github.com/raineorshine/npm-check-updates/issues/1362
...(Array.isArray(args) ? args : [args]),
];
return (0, spawn_please_1.default)(cmd, fullArgs, spawnOptions);
}
/**
* Get platform-specific default prefix to pass on to yarn.
*
* @param options
* @param [options.global]
* @param [options.prefix]
* @returns
*/
async function defaultPrefix(options) {
if (options.prefix) {
return Promise.resolve(options.prefix);
}
const cmd = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';
const prefix = await (0, spawn_please_1.default)(cmd, ['global', 'dir'])
// yarn 2.0 does not support yarn global
// catch error to prevent process from crashing
// https://github.com/raineorshine/npm-check-updates/issues/873
.catch(() => {
/* empty */
});
// FIX: for ncu -g doesn't work on homebrew or windows #146
// https://github.com/raineorshine/npm-check-updates/issues/146
return options.global && prefix && 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
? prefix
? prefix.trim()
: `${process.env.LOCALAPPDATA}\\Yarn\\Data\\global`
: null;
}
exports.defaultPrefix = defaultPrefix;
/**
* Fetches the list of all installed packages.
*
* @param [options]
* @param [options.cwd]
* @param [options.global]
* @param [options.prefix]
* @returns
*/
const list = async (options = {}, spawnOptions) => {
const jsonLines = await spawnYarn('list', options, {
...(options.cwd ? { cwd: options.cwd } : {}),
...spawnOptions,
});
const json = await parseJsonLines(jsonLines);
const keyValues = (0, keyValueBy_1.keyValueBy)(json.dependencies, (name, info) => {
var _a;
return ({
// unmet peer dependencies have a different structure
[name]: info.version || ((_a = info.required) === null || _a === void 0 ? void 0 : _a.version),
});
});
return keyValues;
};
exports.list = list;
/** Wraps a GetVersion function and passes the yarn config. */
const withNpmConfigFromYarn = (getVersion) => async (packageName, currentVersion, options = {}) => getVersion(packageName, currentVersion, options, await npmConfigFromYarn(options));
exports.distTag = withNpmConfigFromYarn(npm.distTag);
exports.greatest = withNpmConfigFromYarn(npm.greatest);
exports.latest = withNpmConfigFromYarn(npm.latest);
exports.minor = withNpmConfigFromYarn(npm.minor);
exports.newest = withNpmConfigFromYarn(npm.newest);
exports.patch = withNpmConfigFromYarn(npm.patch);
exports.semver = withNpmConfigFromYarn(npm.semver);
exports.default = spawnYarn;
//# sourceMappingURL=yarn.js.map
;