UNPKG

snyk-paket-parser

Version:

Generate a dep tree given a collection of manifests

172 lines 7.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildDepTreeFromFiles = exports.DepType = exports.OutOfSyncError = exports.InvalidUserInputError = void 0; const tslib_1 = require("tslib"); const lock_parser_1 = require("./lock-parser"); const dependencies_parser_1 = require("./dependencies-parser"); const path = require("path"); const fs = require("fs"); const errors_1 = require("./errors"); Object.defineProperty(exports, "InvalidUserInputError", { enumerable: true, get: function () { return errors_1.InvalidUserInputError; } }); Object.defineProperty(exports, "OutOfSyncError", { enumerable: true, get: function () { return errors_1.OutOfSyncError; } }); const DEV_GROUPS = ['build', 'test', 'tests']; const SUPPORTED_SOURCES = ['nuget']; const FREQUENCY_THRESHOLD = 100; var DepType; (function (DepType) { DepType["prod"] = "prod"; DepType["dev"] = "dev"; })(DepType = exports.DepType || (exports.DepType = {})); function buildDepTreeFromFiles(root, manifestFilePath, lockFilePath, includeDev = false, strict = true) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const manifestFileFullPath = path.resolve(root, manifestFilePath); const lockFileFullPath = path.resolve(root, lockFilePath); if (!fs.existsSync(manifestFileFullPath)) { throw new errors_1.InvalidUserInputError('Target file paket.dependencies not found at ' + `location: ${manifestFileFullPath}`); } if (!fs.existsSync(lockFileFullPath)) { throw new errors_1.InvalidUserInputError('Lockfile not found at location: ' + lockFileFullPath); } const manifestFileContents = fs.readFileSync(manifestFileFullPath, 'utf-8'); const lockFileContents = fs.readFileSync(lockFileFullPath, 'utf-8'); return { dependencies: yield buildDepTree(manifestFileContents, lockFileContents, includeDev, strict), name: path.basename(path.dirname(manifestFileFullPath)), version: '', }; }); } exports.buildDepTreeFromFiles = buildDepTreeFromFiles; function buildDepTree(manifestFileContents, lockFileContents, includeDev = false, strict = true) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const manifestFile = dependencies_parser_1.parseDependenciesFile(manifestFileContents); const lockFile = lock_parser_1.parseLockFile(lockFileContents); const dependenciesMap = new Map(); collectRootDeps(manifestFile, dependenciesMap); collectResolvedDeps(lockFile, dependenciesMap); for (const dep of dependenciesMap.values()) { if (dep.root) { calculateReferences(dep, dependenciesMap); } } const dependencies = {}; for (const dep of dependenciesMap.values()) { if (dep.root && (includeDev || dep.depType === DepType.prod)) { if (strict && !dep.resolved) { throw new errors_1.OutOfSyncError(dep.name); } dependencies[dep.name] = buildTreeFromList(dep, dependenciesMap); if (!dep.resolved) { dependencies[dep.name].missingLockFileEntry = true; } } } const frequentSubTree = buildFrequentDepsSubtree(dependenciesMap); if (Object.keys(frequentSubTree.dependencies).length) { dependencies[frequentSubTree.name] = frequentSubTree; } return dependencies; }); } function collectRootDeps(manifestFile, dependenciesMap) { for (const group of manifestFile) { const isDev = DEV_GROUPS.indexOf((group.name || '').toLowerCase()) !== -1; for (const dep of group.dependencies) { if (SUPPORTED_SOURCES.indexOf(dep.source.toLowerCase()) === -1) { continue; } const nugetDep = dep; if (!dependenciesMap.has(nugetDep.name.toLowerCase())) { dependenciesMap.set(nugetDep.name.toLowerCase(), { name: nugetDep.name, // Will be overwritten in `collectResolvedDeps`. version: nugetDep.versionRange, // Will be overwritten in `collectResolvedDeps`. dependencies: [], depType: isDev ? DepType.dev : DepType.prod, root: true, refs: 1, // Will be overwritten in `collectResolvedDeps`. resolved: false, }); } } } } function collectResolvedDeps(lockFile, dependenciesMap) { for (const group of lockFile.groups) { const isDev = DEV_GROUPS.indexOf((group.name || '').toLowerCase()) !== -1; for (const dep of group.dependencies) { if (SUPPORTED_SOURCES.indexOf(dep.repository.toLowerCase()) === -1) { continue; } if (dependenciesMap.has(dep.name.toLowerCase())) { const rootDep = dependenciesMap.get(dep.name.toLowerCase()); rootDep.version = dep.version; rootDep.dependencies = dep.dependencies.map((d) => d.name.toLowerCase()); rootDep.resolved = true; } else { dependenciesMap.set(dep.name.toLowerCase(), { name: dep.name, version: dep.version, dependencies: dep.dependencies.map((d) => d.name.toLowerCase()), depType: isDev ? DepType.dev : DepType.prod, root: false, refs: 0, resolved: true, }); } } } } function calculateReferences(node, dependenciesMap) { for (const subName of node.dependencies) { const sub = dependenciesMap.get(subName); sub.refs += node.refs; // Do not propagate calculations if we already reach threshold for the node. if (sub.refs < FREQUENCY_THRESHOLD) { calculateReferences(sub, dependenciesMap); } } } function buildFrequentDepsSubtree(dependenciesMap) { const tree = { name: 'meta-common-packages', version: 'meta', dependencies: {}, }; getFrequentDependencies(dependenciesMap).forEach((listItem) => { const treeNode = buildTreeFromList(listItem, dependenciesMap); tree.dependencies[treeNode.name] = treeNode; }); return tree; } function getFrequentDependencies(dependenciesMap) { const frequentDeps = []; for (const dep of dependenciesMap.values()) { if (!dep.root && dep.refs >= FREQUENCY_THRESHOLD) { frequentDeps.push(dep); } } return frequentDeps; } function buildTreeFromList(listItem, dependenciesMap) { const tree = { name: listItem.name, version: listItem.version, dependencies: {}, depType: listItem.depType, }; for (const name of listItem.dependencies) { const subListItem = dependenciesMap.get(name); if (!(subListItem.refs >= FREQUENCY_THRESHOLD)) { const subtree = buildTreeFromList(subListItem, dependenciesMap); tree.dependencies[subtree.name] = subtree; } } return tree; } //# sourceMappingURL=index.js.map