UNPKG

snyk-nodejs-lockfile-parser

Version:
180 lines 7.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getYarnLockV2ChildNode = exports.yarnLockFileKeyNormalizer = void 0; const core_1 = require("@yarnpkg/core"); const _flatMap = require("lodash.flatmap"); const errors_1 = require("../../errors"); const parsers_1 = require("../../parsers"); const util_1 = require("../util"); const semver = require("semver"); const debugModule = require("debug"); const debug = debugModule('snyk-nodejs-plugin'); const BUILTIN_PLACEHOLDER = 'builtin'; const MULTIPLE_KEYS_REGEXP = / *, */g; const keyNormalizer = (parseDescriptor, parseRange) => (rawDescriptor) => { // See https://yarnpkg.com/features/protocols const descriptors = [rawDescriptor]; const descriptor = parseDescriptor(rawDescriptor); const name = `${descriptor.scope ? '@' + descriptor.scope + '/' : ''}${descriptor.name}`; const range = parseRange(descriptor.range); const protocol = range.protocol; switch (protocol) { case 'npm:': case 'file:': // This is space inneficient but will be kept for now, // Due to how we wish to index using the dependencies map // we want the keys to match name@version but this is handled different // for npm alias and normal install. descriptors.push(`${name}@${range.selector}`); descriptors.push(`${name}@${protocol}${range.selector}`); break; case 'git:': case 'git+ssh:': case 'git+http:': case 'git+https:': case 'github:': if (range.source) { descriptors.push(`${name}@${protocol}${range.source}${range.selector ? '#' + range.selector : ''}`); } else { descriptors.push(`${name}@${protocol}${range.selector}`); } break; case 'patch:': if (range.source && range.selector.indexOf(BUILTIN_PLACEHOLDER) === 0) { descriptors.push(range.source); } else { descriptors.push(`${name}@${protocol}${range.source}${range.selector ? '#' + range.selector : ''}`); } break; case null: case undefined: if (range.source) { descriptors.push(`${name}@${range.source}#${range.selector}`); } else { descriptors.push(`${name}@${range.selector}`); } break; case 'http:': case 'https:': case 'link:': case 'portal:': case 'exec:': case 'workspace:': case 'virtual:': default: // For user defined plugins descriptors.push(`${name}@${protocol}${range.selector}`); break; } return descriptors; }; const yarnLockFileKeyNormalizer = (parseDescriptor, parseRange) => (fullDescriptor) => { const allKeys = fullDescriptor .split(MULTIPLE_KEYS_REGEXP) .map(keyNormalizer(parseDescriptor, parseRange)); return new Set(_flatMap(allKeys)); }; exports.yarnLockFileKeyNormalizer = yarnLockFileKeyNormalizer; const getYarnLockV2ChildNode = (name, depInfo, pkgs, strictOutOfSync, includeOptionalDeps, resolutions, parentNode) => { // First, check if a resolution would be used const resolvedVersionFromResolution = (() => { // Check for scoped resolution (e.g., "parentPackageName/dependencyName") const scopedKey = `${parentNode.name}/${name}`; if (resolutions[scopedKey]) { return resolutions[scopedKey]; } // Check for resolutions matching "packageName@versionOrRangeToOverride" for (const resKey in resolutions) { if (Object.prototype.hasOwnProperty.call(resolutions, resKey)) { try { const descriptor = core_1.structUtils.parseDescriptor(resKey); const resKeyPkgName = core_1.structUtils.stringifyIdent(descriptor); // Check if the resolution key targets the current package name if (resKeyPkgName === name) { if (descriptor.range && descriptor.range !== 'unknown') { // Check if the current dependency's version satisfies the // version/range specified in the resolution key. if (semver.satisfies(depInfo.version, descriptor.range)) { return resolutions[resKey]; } } } } catch (e) { debug(`Error parsing resolution key(${resKey}): ${e}$`); } } } // Check for global resolution by package name (e.g., "packageName": "version") if (resolutions[name]) { return resolutions[name]; } return ''; // No resolution applies })(); if (resolvedVersionFromResolution) { const childNodeKeyFromResolution = `${name}@${resolvedVersionFromResolution}`; if (!pkgs[childNodeKeyFromResolution]) { if (strictOutOfSync && !/^file:/.test(resolvedVersionFromResolution)) { throw new errors_1.OutOfSyncError(childNodeKeyFromResolution, parsers_1.LockfileType.yarn2); } else { return { id: childNodeKeyFromResolution, name: name, version: resolvedVersionFromResolution, dependencies: {}, isDev: depInfo.isDev, missingLockFileEntry: true, }; } } const { version: versionFromResolution, dependencies, optionalDependencies, } = pkgs[childNodeKeyFromResolution]; const formattedDependencies = (0, util_1.getGraphDependencies)(dependencies || {}, depInfo.isDev); const formattedOptionalDependencies = includeOptionalDeps ? (0, util_1.getGraphDependencies)(optionalDependencies || {}, depInfo.isDev) : {}; return { id: childNodeKeyFromResolution, name: name, version: versionFromResolution, dependencies: Object.assign(Object.assign({}, formattedOptionalDependencies), formattedDependencies), isDev: depInfo.isDev, }; } // No resolutions const childNodeKey = `${name}@${depInfo.version}`; if (!pkgs[childNodeKey]) { if (strictOutOfSync && !/^file:/.test(depInfo.version)) { throw new errors_1.OutOfSyncError(childNodeKey, parsers_1.LockfileType.yarn2); } else { return { id: childNodeKey, name: name, version: depInfo.version, dependencies: {}, isDev: depInfo.isDev, missingLockFileEntry: true, }; } } else { const depData = pkgs[childNodeKey]; const dependencies = (0, util_1.getGraphDependencies)(depData.dependencies || {}, depInfo.isDev); const optionalDependencies = includeOptionalDeps ? (0, util_1.getGraphDependencies)(depData.optionalDependencies || {}, depInfo.isDev) : {}; return { id: `${name}@${depData.version}`, name: name, version: depData.version, dependencies: Object.assign(Object.assign({}, dependencies), optionalDependencies), isDev: depInfo.isDev, }; } }; exports.getYarnLockV2ChildNode = getYarnLockV2ChildNode; //# sourceMappingURL=utils.js.map