UNPKG

@snyk/composer-lockfile-parser

Version:

Generate a dep tree given a composer.lock file

116 lines 5.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const _findKey = require("lodash.findkey"); const _get = require("lodash.get"); const _invert = require("lodash.invert"); const _isEmpty = require("lodash.isempty"); const types_1 = require("../types"); const _ = { get: _get, isEmpty: _isEmpty, invert: _invert, findKey: _findKey, }; class ComposerParser { static getVersion(depObj) { // check for `version` property. may not exist const versionFound = _.get(depObj, 'version', ''); // even if found, may be an alias, so check const availableAliases = _.get(depObj, "extra['branch-alias']", []); // if the version matches the alias (either as is, or without 'dev-'), use the aliases version. // otherwise, use the version as is, and if not, the first found alias return _.get(availableAliases, versionFound) || _.get(_.invert(availableAliases), versionFound.replace('dev-', '')) && versionFound.replace('dev-', '') || versionFound || _.findKey(_.invert(availableAliases), '0'); // first available alias } static buildDependencies(composerJsonObj, composerLockObj, depObj, systemPackages, includeDev = false, isDevTree = false, depRecursiveArray = [], packageRefCount = {}) { const result = {}; // find depObj properties const depName = _.get(depObj, 'name'); const require = _.get(depObj, 'require', {}); const requireDev = includeDev ? _.get(depObj, 'require-dev', {}) : {}; // recursion tests const inRecursiveArray = depRecursiveArray.indexOf(depName) > -1; const exceedsMaxRepeats = packageRefCount[depName] >= this.MAX_PACKAGE_REPEATS; const hasNoDependencies = _.isEmpty(require) && _.isEmpty(requireDev); // break recursion when if (inRecursiveArray || exceedsMaxRepeats || hasNoDependencies) { return result; } // prevent circular dependencies depRecursiveArray.push(depName); // get locked packages const packages = _.get(composerLockObj, 'packages', []); const packagesDev = includeDev ? _.get(composerLockObj, 'packages-dev', []) : []; const allPackages = [ ...packages, ...packagesDev, ]; // parse require dependencies for (const name of Object.keys(require)) { let version = ''; // lets find if this dependency has an object in composer.lock const lockedPackage = allPackages.find((dep) => dep.name === name); if (lockedPackage) { version = this.getVersion(lockedPackage); } else { // here we use the system version or composer json - not a locked version version = _.get(systemPackages, name) || _.get(require, name); } // remove any starting 'v' from version numbers version = version.replace(/^v(\d)/, '$1'); // bump package reference count (or assign to 1 if we haven't seen this before) packageRefCount[name] = (packageRefCount[name] || 0) + 1; result[name] = { name, version, dependencies: this.buildDependencies(composerJsonObj, composerLockObj, lockedPackage, // undefined if transitive dependency systemPackages, includeDev, false, depRecursiveArray, packageRefCount), labels: { scope: isDevTree ? types_1.Scope.dev : types_1.Scope.prod, }, }; } // parse require-dev dependencies for (const name of Object.keys(requireDev)) { let version = ''; // lets find if this dependency has an object in composer.lock const lockedPackage = allPackages.find((dep) => dep.name === name); if (lockedPackage) { version = this.getVersion(lockedPackage); } else { // here we use the system version or composer json - not a locked version version = _.get(systemPackages, name) || _.get(requireDev, name); } // remove any starting 'v' from version numbers version = version.replace(/^v(\d)/, '$1'); // bump package reference count (or assign to 1 if we haven't seen this before) packageRefCount[name] = (packageRefCount[name] || 0) + 1; result[name] = { name, version, dependencies: this.buildDependencies(composerJsonObj, composerLockObj, lockedPackage, // undefined if transitive dependency systemPackages, includeDev, true, depRecursiveArray, packageRefCount), labels: { scope: types_1.Scope.dev, }, }; } // remove from recursive check depRecursiveArray.pop(); // return dep tree return result; } } exports.ComposerParser = ComposerParser; // After this threshold, a package node in the dep tree won't have expanded dependencies. // This is a cheap protection against combinatorial explosion when there's N packages // that depend on each other (producing N! branches of the dep tree). // The value of 150 was chosen as a lowest one that doesn't break existing tests. // Switching to dependency graph would render this trick obsolete. ComposerParser.MAX_PACKAGE_REPEATS = 150; //# sourceMappingURL=composer-parser.js.map